Yesterday extract_action_items turned a raw meeting transcript into a structured list. What happens when your triage queue is support tickets — and you need both a condensed version and a validated urgency label from each one?
Running extract_action_items got me a list. But for urgency I'd want a single label — not a list of strings I have to interpret myself. Something I can route on directly.
That's exactly the chain. First agent summarises the ticket in one or two sentences, second agent classifies the summary with result_type=Literal. The Literal constraint replaces the .strip().lower() you'd use with a plain system prompt:
from typing import Literal
summarizer = Agent(model, system_prompt="Summarize in 2 sentences.")
classifier = Agent(model, result_type=Literal["high", "medium", "low"])
summary = summarizer.run_sync(text).output
urgency = classifier.run_sync(summary).outputWhy not just send the full ticket straight to the classifier? Wouldn't one agent be faster?
Classification accuracy drops on noisy input. A five-paragraph complaint has timestamps, pleasantries, repeated context. The summarizer strips the noise so the classifier sees a clean one-sentence version of the problem. Both agents do less work and the Literal output is more reliable:
def summarize_and_classify(text: str) -> str:
summarizer = Agent(model, system_prompt="Summarize in 2 sentences.")
classifier = Agent(model, result_type=Literal["high", "medium", "low"])
summary = summarizer.run_sync(text).output
urgency = classifier.run_sync(summary).output
print(f"Summary: {summary}")
print(f"Urgency: {urgency}")
return urgencySo the Literal type is doing what the system prompt did before — except it's a hard constraint the model literally cannot violate. No .strip().lower() anywhere.
Correct. Type-level enforcement replaces runtime cleaning. The model sees the enum and knows its only valid replies are "high", "medium", or "low". You return the output directly with no normalization pass.
I've been copy-pasting tickets into ChatGPT and manually marking priority on a spreadsheet. Two agents and a Literal and that's gone.
Build summarize_and_classify(text) now — the summary flows from the first agent into the second, and you return the validated urgency string.
Why chain? Classification accuracy drops on noisy input. A multi-paragraph ticket contains timestamps, pleasantries, and repetition. Summarising first gives the classifier a clean, scoped prompt — fewer tokens, more reliable label.
Why Literal over system_prompt? system_prompt="Return one word: high, medium, or low" is a suggestion. result_type=Literal["high","medium","low"] is enforced at the type level — no .strip().lower() needed, no defensive normalization.
Summary as plain variable: summary is just a Python string. The second agent treats it like any other prompt. No framework glue — pure function composition.
Yesterday extract_action_items turned a raw meeting transcript into a structured list. What happens when your triage queue is support tickets — and you need both a condensed version and a validated urgency label from each one?
Running extract_action_items got me a list. But for urgency I'd want a single label — not a list of strings I have to interpret myself. Something I can route on directly.
That's exactly the chain. First agent summarises the ticket in one or two sentences, second agent classifies the summary with result_type=Literal. The Literal constraint replaces the .strip().lower() you'd use with a plain system prompt:
from typing import Literal
summarizer = Agent(model, system_prompt="Summarize in 2 sentences.")
classifier = Agent(model, result_type=Literal["high", "medium", "low"])
summary = summarizer.run_sync(text).output
urgency = classifier.run_sync(summary).outputWhy not just send the full ticket straight to the classifier? Wouldn't one agent be faster?
Classification accuracy drops on noisy input. A five-paragraph complaint has timestamps, pleasantries, repeated context. The summarizer strips the noise so the classifier sees a clean one-sentence version of the problem. Both agents do less work and the Literal output is more reliable:
def summarize_and_classify(text: str) -> str:
summarizer = Agent(model, system_prompt="Summarize in 2 sentences.")
classifier = Agent(model, result_type=Literal["high", "medium", "low"])
summary = summarizer.run_sync(text).output
urgency = classifier.run_sync(summary).output
print(f"Summary: {summary}")
print(f"Urgency: {urgency}")
return urgencySo the Literal type is doing what the system prompt did before — except it's a hard constraint the model literally cannot violate. No .strip().lower() anywhere.
Correct. Type-level enforcement replaces runtime cleaning. The model sees the enum and knows its only valid replies are "high", "medium", or "low". You return the output directly with no normalization pass.
I've been copy-pasting tickets into ChatGPT and manually marking priority on a spreadsheet. Two agents and a Literal and that's gone.
Build summarize_and_classify(text) now — the summary flows from the first agent into the second, and you return the validated urgency string.
Why chain? Classification accuracy drops on noisy input. A multi-paragraph ticket contains timestamps, pleasantries, and repetition. Summarising first gives the classifier a clean, scoped prompt — fewer tokens, more reliable label.
Why Literal over system_prompt? system_prompt="Return one word: high, medium, or low" is a suggestion. result_type=Literal["high","medium","low"] is enforced at the type level — no .strip().lower() needed, no defensive normalization.
Summary as plain variable: summary is just a Python string. The second agent treats it like any other prompt. No framework glue — pure function composition.
Create a free account to get started. Paid plans unlock all tracks.