Text Generation Applications › Code Implementation
Language AI HandbookAll chapters Text Generation Applications › Code ImplementationOne section · Sections in this chapter6 / 11 ↓
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Let's build practical implementations of the main generation use cases. We use the transformers library to demonstrate generation locally, and show patterns that apply when calling hosted APIs.
Setup and Imports
# For tokenization and quality measurement# Download NLTK data if needed
import nltk
nltk.download("punkt", quiet=True)
nltk.download("punkt_tab", quiet=True)Simulating Generation Outputs
Rather than downloading a multi-gigabyte model, we demonstrate quality analysis and evaluation patterns using pre-written outputs that represent realistic generation results. This is standard practice in educational settings: analyze real representative examples to understand the patterns.
# Representative product description generation examples
# These represent the range of outputs from instruction-tuned models
product_examples = {
"keyboard": {
"attributes": {
"name": "KeyPro X1 Wireless Keyboard",
"connectivity": "Bluetooth 5.0",
"battery_life": "6 months",
"layout": "Tenkeyless",
"switches": "Silent tactile",
"weight": "380g",
"compatibility": "Windows, macOS, Linux",
},
"generated_outputs": [
"The KeyPro X1 Wireless Keyboard delivers a premium typing experience for remote workers. Featuring Bluetooth 5.0 connectivity and silent tactile switches, it keeps your workspace quiet without sacrificing feedback. The 6-month battery life means you can focus on work rather than charging cables. At 380g, this tenkeyless layout keyboard is light enough to carry between locations while remaining solid enough to anchor your workstation. Compatible with Windows, macOS, and Linux, it fits naturally into any workflow.",
"Work from anywhere without the noise. The KeyPro X1 combines Bluetooth 5.0 reliability with silent tactile switches to deliver a distraction-free typing experience. Six months of battery life, tenkeyless portability, and universal compatibility across Windows, macOS, and Linux make this keyboard the obvious choice for the modern remote worker.",
"Introducing the KeyPro X1 Wireless Keyboard: engineered for professionals who demand silence, portability, and lasting power. With Bluetooth 5.0 multi-device connectivity, silent tactile switches, and a remarkable 6-month battery life, the X1 redefines what a wireless keyboard can be. Its lightweight tenkeyless form factor fits every desk, every bag, and every operating system.",
],
}
}# Quality measurement functions
def count_words(text: str) -> int:
"""Count words in text using simple whitespace tokenization."""
return len(text.split())
def count_sentences(text: str) -> int:
"""Count sentences using NLTK sentence tokenizer."""
sentences = sent_tokenize(text)
return len(sentences)
def avg_sentence_length(text: str) -> float:
"""Average words per sentence."""
sentences = sent_tokenize(text)
if not sentences:
return 0.0
word_counts = [len(s.split()) for s in sentences]
return sum(word_counts) / len(word_counts)
def attribute_coverage(text: str, attributes: dict) -> float:
"""Fraction of attribute values mentioned in the text."""
text_lower = text.lower()
mentioned = 0
total = 0
for key, value in attributes.items():
if key == "name":
continue # Skip product name
total += 1
# Check if the value or key term appears
val_str = str(value).lower()
key_words = key.replace("_", " ").lower()
if val_str in text_lower or key_words in text_lower:
mentioned += 1
return mentioned / total if total > 0 else 0.0
def compute_quality_metrics(text: str, attributes: dict) -> dict:
"""Compute quality metrics for a generated product description."""
return {
"word_count": count_words(text),
"sentence_count": count_sentences(text),
"avg_sentence_length": round(avg_sentence_length(text), 1),
"attribute_coverage": round(attribute_coverage(text, attributes), 2),
}Product Description Quality Analysis ================================================== Variant 1: Word count: 76 Sentences: 5 Avg sentence length: 15.2 words Attribute coverage: 83% Preview: The KeyPro X1 Wireless Keyboard delivers a premium typing experience for remote ... Variant 2: Word count: 49 Sentences: 3 Avg sentence length: 16.3 words Attribute coverage: 83% Preview: Work from anywhere without the noise. The KeyPro X1 combines Bluetooth 5.0 relia... Variant 3: Word count: 53 Sentences: 3 Avg sentence length: 17.7 words Attribute coverage: 83% Preview: Introducing the KeyPro X1 Wireless Keyboard: engineered for professionals who de...
The three variants differ in tone and length while covering similar attribute ground. Variant 1 is more informational, Variant 2 is punchier, and Variant 3 takes a formal product-launch register. A practitioner would select among these based on the target channel: product page (Variant 1), social media (Variant 2), press release (Variant 3). This multi-variant generation pattern, which costs only marginally more than single-output generation, dramatically increases the probability that at least one output will be suitable for the intended use.
Measuring Lexical Diversity
One measure of generation quality is lexical diversity: how much of the vocabulary is unique versus repeated. Repetitive outputs suggest the model is stuck in a pattern, often a sign of decoding issues or insufficient temperature. The most common lexical diversity metric is the Type-Token Ratio (TTR):
where:
- : the ordered sequence of words in text (the token list)
- : the number of distinct word types (unique vocabulary)
- : the total number of word tokens (including repetitions)
TTR ranges from 0 (every token is identical) to 1 (every token is unique). For typical natural language, values above 0.70 indicate good lexical variety. TTR is sensitive to document length: longer texts accumulate more repeats, so TTR tends to decrease as documents grow. The Moving-Average TTR (MATTR) addresses this by computing TTR over a sliding window of fixed size and averaging across all window positions, making it comparable across texts of different lengths.
Why does lexical diversity matter for generation quality? A model generating repetitive text is either stuck in a local probability maximum (a phrase that keeps predicting itself), or it has settled into a high-probability pattern from training data that happens to repeat a phrase or sentence. Both situations indicate that the generation is not fully exploring the model's distribution. Repetition penalties, which we covered in the Repetition Penalties chapter, directly address this by reducing the probability of recently generated tokens. Low TTR in an output is a signal that either repetition penalties were not applied, or that the applied penalties were insufficient for the task.
def type_token_ratio(text: str) -> float:
"""
Type-Token Ratio (TTR): unique words / total words.
Higher values indicate greater lexical diversity.
Values above 0.7 are generally considered diverse.
"""
tokens = text.lower().split()
if not tokens:
return 0.0
types = set(tokens)
return len(types) / len(tokens)
def moving_average_ttr(text: str, window: int = 50) -> float:
"""
Moving-Average TTR (MATTR): average TTR over sliding windows.
Less sensitive to document length than raw TTR.
"""
tokens = text.lower().split()
if len(tokens) < window:
return type_token_ratio(text)
ttrs = []
for i in range(len(tokens) - window + 1):
window_tokens = tokens[i : i + window]
ttrs.append(len(set(window_tokens)) / window)
return sum(ttrs) / len(ttrs)
def top_n_repeated_words(text: str, n: int = 5) -> List[tuple]:
"""Find the most frequently repeated content words."""
stopwords = {
"the",
"a",
"an",
"is",
"it",
"in",
"on",
"for",
"of",
"and",
"to",
"with",
"you",
"your",
"its",
"this",
"that",
"from",
"are",
"be",
"as",
"at",
"or",
"by",
"can",
"will",
"not",
}
tokens = text.lower().split()
# Remove punctuation and stopwords
clean_tokens = [re.sub(r"[^a-z]", "", t) for t in tokens]
content_words = [t for t in clean_tokens if t and t not in stopwords]
counts = Counter(content_words)
return counts.most_common(n)Lexical Diversity Analysis
==================================================
Variant 1:
TTR: 0.908 (unique words / total words)
MATTR: 0.979 (moving-window average)
Top repeated content words: [('keyboard', 2), ('enough', 2), ('keypro', 1)]
Variant 2:
TTR: 0.918 (unique words / total words)
MATTR: 0.963 (moving-window average)
Top repeated content words: [('work', 1), ('anywhere', 1), ('without', 1)]
Variant 3:
TTR: 0.849 (unique words / total words)
MATTR: 0.957 (moving-window average)
Top repeated content words: [('every', 3), ('x', 2), ('wireless', 2)]High TTR values (above 0.70) confirm the variants avoid repetition. For production use, you would flag outputs with TTR below 0.50 as likely containing repetitive patterns and trigger a retry. The most repeated content words are product-specific nouns like "keyboard" and "wireless," which is expected and does not indicate a generation problem.
Constraint Verification
Many generation tasks have hard constraints. Let's implement a constraint checker that validates output against a specification.
@dataclass
class GenerationConstraints:
"""Specification of constraints for a generation task."""
min_words: Optional[int] = None
max_words: Optional[int] = None
required_phrases: List[str] = None
forbidden_phrases: List[str] = None
min_sentences: Optional[int] = None
max_sentences: Optional[int] = None
def __post_init__(self):
if self.required_phrases is None:
self.required_phrases = []
if self.forbidden_phrases is None:
self.forbidden_phrases = []
def check_constraints(text: str, constraints: GenerationConstraints) -> dict:
"""
Verify that a generated text satisfies all constraints.
Returns a dict of constraint name -> (passed, details).
"""
results = {}
word_count = count_words(text)
sentence_count = count_sentences(text)
text_lower = text.lower()
if constraints.min_words is not None:
passed = word_count >= constraints.min_words
results["min_words"] = (
passed,
f"got {word_count}, need >= {constraints.min_words}",
)
if constraints.max_words is not None:
passed = word_count <= constraints.max_words
results["max_words"] = (
passed,
f"got {word_count}, need <= {constraints.max_words}",
)
if constraints.min_sentences is not None:
passed = sentence_count >= constraints.min_sentences
results["min_sentences"] = (passed, f"got {sentence_count}")
if constraints.max_sentences is not None:
passed = sentence_count <= constraints.max_sentences
results["max_sentences"] = (passed, f"got {sentence_count}")
for phrase in constraints.required_phrases:
present = phrase.lower() in text_lower
results[f"required:'{phrase}'"] = (
present,
"found" if present else "MISSING",
)
for phrase in constraints.forbidden_phrases:
absent = phrase.lower() not in text_lower
results[f"forbidden:'{phrase}'"] = (
absent,
"absent" if absent else "PRESENT (violation)",
)
return results
def constraint_pass_rate(results: dict) -> float:
"""Fraction of constraints that passed."""
passed = sum(1 for v, _ in results.values() if v)
return passed / len(results) if results else 1.0# Define constraints for the product description task
constraints = GenerationConstraints(
min_words=50,
max_words=120,
required_phrases=["bluetooth", "battery"],
forbidden_phrases=["inferior", "cheap"],
max_sentences=6,
)Constraint Verification Results
==================================================
Variant 1: PASS (100% constraints met)
OK min_words: got 76, need >= 50
OK max_words: got 76, need <= 120
OK max_sentences: got 5
OK required:'bluetooth': found
OK required:'battery': found
OK forbidden:'inferior': absent
OK forbidden:'cheap': absent
Variant 2: FAIL (86% constraints met)
!! min_words: got 49, need >= 50
OK max_words: got 49, need <= 120
OK max_sentences: got 3
OK required:'bluetooth': found
OK required:'battery': found
OK forbidden:'inferior': absent
OK forbidden:'cheap': absent
Variant 3: PASS (100% constraints met)
OK min_words: got 53, need >= 50
OK max_words: got 53, need <= 120
OK max_sentences: got 3
OK required:'bluetooth': found
OK required:'battery': found
OK forbidden:'inferior': absent
OK forbidden:'cheap': absentWhen a variant fails a constraint, the production pattern is to retry generation with a more explicit prompt, reduce the response length, or apply post-processing (e.g., truncating at the word limit). A retry-until-valid loop with a maximum of three attempts handles most cases. Structuring constraint failures as typed error codes, rather than just boolean pass/fail, allows the retry prompt to target the specific violation: "Your previous response was 135 words; please rewrite it to stay under 120 words."
Temperature Effects on Generation Diversity
Temperature is the primary control for balancing output quality against diversity. Recall from the Decoding Temperature chapter that temperature rescales the model's logits before the softmax. Given logits over a vocabulary of tokens, the probability assigned to token is:
where:
- : the raw logit for token , produced by the final linear layer of the model
- : the temperature parameter; leaves the distribution unchanged, sharpens it, flattens it
- : dividing by before exponentiation amplifies (for ) or compresses (for ) differences between logits
At , the distribution collapses to a point mass on the highest-logit token (greedy decoding). At , all tokens become equally probable (uniform random sampling). Let's visualize this relationship concretely using a simulation based on real vocabulary statistics.


The left panel shows the fundamental trade-off: higher temperature increases lexical diversity (more varied vocabulary) but reduces coherence (more surprising continuations). The practical operating range for most content generation tasks is 0.7 to 1.2. The right panel makes the mechanism concrete: at temperature 0.5, the model is nearly deterministic, concentrating most probability on the top token; at temperature 2.0, the distribution flattens, giving lower-probability tokens a meaningful share. For A/B testing applications that need clearly different variants, a temperature between 1.0 and 1.5 is a reasonable starting point, with human filtering used to validate quality.
Evaluating Multiple Variants
A common production pattern generates multiple outputs and selects the best by scoring. Let's implement a simple scoring pipeline.
def score_output(
text: str, attributes: dict, constraints: GenerationConstraints
) -> dict:
"""
Composite quality score for a generated text.
Components: constraint compliance, attribute coverage, lexical diversity.
"""
constraint_results = check_constraints(text, constraints)
compliance = constraint_pass_rate(constraint_results)
coverage = attribute_coverage(text, attributes)
diversity = type_token_ratio(text)
# Weighted composite: compliance is most critical
composite = 0.50 * compliance + 0.30 * coverage + 0.20 * diversity
return {
"compliance": round(compliance, 3),
"coverage": round(coverage, 3),
"diversity": round(diversity, 3),
"composite": round(composite, 3),
}
def select_best_output(
outputs: List[str], attributes: dict, constraints: GenerationConstraints
) -> tuple:
"""Score all outputs and return (best_output, best_score, all_scores)."""
scores = [score_output(o, attributes, constraints) for o in outputs]
best_idx = max(range(len(scores)), key=lambda i: scores[i]["composite"])
return outputs[best_idx], scores[best_idx], scoresOutput Scoring and Selection ================================================== Variant 1 <-- SELECTED: Compliance: 100% Coverage: 83% Diversity: 0.91 Composite: 0.932 Variant 2: Compliance: 86% Coverage: 83% Diversity: 0.92 Composite: 0.862 Variant 3: Compliance: 100% Coverage: 83% Diversity: 0.85 Composite: 0.920 Selected output preview: The KeyPro X1 Wireless Keyboard delivers a premium typing experience for remote workers. Featuring B...
The composite score makes the selection process transparent and auditable. Each weight in the scoring formula encodes a design decision: 50% to compliance reflects the judgment that constraint violations are the most damaging failure mode; 30% to coverage reflects the judgment that attribute completeness matters more than stylistic variety. Teams often tune these weights against human preference judgments collected on a validation set, treating scoring as a machine learning problem in its own right.

The visualization makes the selection rationale clear. Variants 1 and 3 satisfy every constraint, whereas Variant 2 misses the 50-word minimum by one word. Attribute coverage is tied at five of six attributes, so Variant 1 wins because its lexical-diversity score is higher than Variant 3's. Making this reasoning explicit in a dashboard or audit log is valuable for production systems where humans may need to review or override automated selections.
Prompt Template Engineering
Prompt quality drives output quality more than any other single factor. A well-structured prompt reduces constraint violations, improves factual grounding, and produces more consistent outputs. Let's examine the components of effective generation prompts.
A generation prompt for a structured task has four components that each play a distinct role. The role specification tells the model what perspective to adopt and activates the relevant subset of its training distribution. The task description precisely states what output is expected, preventing ambiguity about format or scope. The constraint section makes explicit requirements unambiguous and separates them from the task description, so they do not get buried in narrative prose. The input data section provides the grounding information the model needs to produce factually accurate output.
def build_product_description_prompt(
attributes: dict,
constraints: GenerationConstraints,
style: str = "professional",
) -> str:
"""
Build a structured prompt for product description generation.
Good prompts have four components:
1. Role/persona specification
2. Task description with explicit output format
3. Constraints stated precisely
4. Input data
"""
# Format attributes as a structured list
attr_lines = []
for key, value in attributes.items():
label = key.replace("_", " ").title()
attr_lines.append(f" - {label}: {value}")
attrs_str = "\n".join(attr_lines)
# Translate constraints to natural language
constraint_parts = []
if constraints.min_words and constraints.max_words:
constraint_parts.append(
f"- Length: {constraints.min_words} to {constraints.max_words} words"
)
if constraints.required_phrases:
constraint_parts.append(
f"- Must mention: {', '.join(constraints.required_phrases)}"
)
if constraints.forbidden_phrases:
constraint_parts.append(
f"- Never use: {', '.join(constraints.forbidden_phrases)}"
)
if constraints.max_sentences:
constraint_parts.append(
f"- Maximum {constraints.max_sentences} sentences"
)
constraints_str = "\n".join(constraint_parts)
prompt = f"""You are an expert copywriter specializing in {style} product descriptions.
Write a product description for an e-commerce listing based on the attributes below.
Constraints:
{constraints_str}
Product attributes:
{attrs_str}
Write only the description. Do not include a title or headings."""
return prompt
def analyze_prompt_structure(prompt: str) -> dict:
"""Count structural components in a prompt."""
lines = prompt.strip().split("\n")
return {
"total_lines": len(lines),
"word_count": count_words(prompt),
"has_role": any("you are" in l.lower() for l in lines),
"has_task": any("write" in l.lower() for l in lines),
"has_constraints": "constraints" in prompt.lower(),
"has_data": "attributes" in prompt.lower(),
"has_output_format": "only the" in prompt.lower(),
}Prompt Structure Analysis ================================================== OK Total Lines: 20 OK Word Count: 87 OK Has Role: True OK Has Task: True OK Has Constraints: True OK Has Data: True OK Has Output Format: True Full prompt (87 words): ---------------------------------------- You are an expert copywriter specializing in e-commerce product descriptions. Write a product description for an e-commerce listing based on the attributes below. Constraints: - Length: 50 to 120 words - Must mention: bluetooth, battery - Never use: inferior, cheap - Maximum 6 sentences Product attributes: - Name: KeyPro X1 Wireless Keyboard - Connectivity: Bluetooth 5.0 - Battery Life: 6 months - Layout: Tenkeyless - Switches: Silent tactile - Weight: 380g - Compatibility: Windows, macOS, Linux Write only the description. Do not include a title or headings.
The prompt includes all four structural components and keeps them visually separated, which reduces the likelihood of the model confusing constraints with task description. Notice that the constraint section explicitly lists the forbidden phrases: including them explicitly, rather than relying on the model to infer what not to say, reliably reduces forbidden content violations.
Summarizing Attribute Coverage Across Output Lengths
A final visualization shows how attribute coverage varies with output length. This pattern is useful when deciding whether to use a word-count constraint: very short descriptions cannot cover all key attributes, while very long ones may add fabricated details.

The saturation curve shows that coverage rises quickly from 30 to 80 words, then flattens. The three actual variants all cover five of six attributes, but they remain below the 80-word reference: Variant 1 contains 76 words, Variant 2 contains 49, and Variant 3 contains 53. The simulation therefore supports a 70- to 80-word minimum as a conservative production heuristic: shorter outputs can still cover most attributes, but they are less likely to cover all six.
Key Parameters
The key parameters for generation quality control are:
- temperature: Controls the diversity-coherence trade-off. Values from 0.7 to 1.2 work well for most content generation tasks. Use 0.3 to 0.7 for structured, factual outputs; use 1.0 to 1.5 for creative, varied outputs.
- min_words / max_words: Hard word-count bounds. Set min to ensure full attribute coverage; set max to prevent verbose, padded outputs.
- required_phrases: Must-include terms for SEO or brand compliance. Check with case-insensitive substring matching.
- forbidden_phrases: Must-exclude terms for brand safety or regulatory compliance. Any match is a hard failure.
- compliance weight (0.50): The fraction of the composite score assigned to constraint compliance. Keeping this high (above 0.40) ensures constraint violations are always penalized.
- retry_limit (3): Maximum number of generation attempts before reporting failure. Three retries resolve the vast majority of constraint violations.