Yesterday's summarize_text returned a full sentence. What if the next step in your pipeline needs a label — not a sentence — so a conditional can branch on it?
Right, summarizing a client email and then routing it to the right team. A label like negative is what the router needs, not a paragraph.
Exactly. A system prompt is how you constrain the agent to that one label. The instruction goes in at construction time, and every run_sync call inherits it:
agent = Agent(model, system_prompt="Return one word: positive, neutral, or negative.")
result = agent.run_sync(text)
return result.output.strip().lower()The .strip().lower() at the end matters — the model will occasionally hand back "Positive." with a capital and a period, or " negative" with a leading space. Neither of those compares equal to "negative" in a downstream if statement.
So the system prompt isn't a guarantee? I told it to return one word and it still might not?
The system prompt is a strong suggestion, not a contract. Temperature, phrasing, and model version all introduce variation. .strip() removes whitespace at either end. .lower() folds capitalization. Together they collapse "Positive", " positive ", and "POSITIVE" into the same string your router expects.
So I'm writing defensive code around LLM output the same way I'd write defensive code around a user input field?
Same instinct, yes. Any value from outside your control — a user form field, an API response, an LLM output — needs normalization before you compare it. The three-character chain .strip().lower() is the minimum for text labels. Here's the full function:
def classify_sentiment(text: str) -> str:
agent = Agent(model, system_prompt="Return one word: positive, neutral, or negative.")
result = agent.run_sync(text)
return result.output.strip().lower()And once that label is clean, I can pipe it straight into a routing dict or an if-else without any extra parsing. That's the part that was always fragile when I tried to do this in ChatGPT.
A ChatGPT reply is for reading. A Python string is for routing. Write classify_sentiment(text) — set the system prompt, call run_sync, normalize with .strip().lower(), and return.
TL;DR: system_prompt constrains the shape; .strip().lower() enforces it in code.
system_prompt= — fixed instruction the model reads before every input.strip() — removes leading and trailing whitespace.lower() — folds capitalization so comparisons are case-insensitive| Raw output | After .strip() | After .strip().lower() |
|---|---|---|
"Positive" | "Positive" | "positive" |
" negative " | "negative" | "negative" |
"NEUTRAL" | "NEUTRAL" | "neutral" |
A trailing period still leaks through — Day 11 shows result_type=Literal[...] for strict enum guarantees.
Yesterday's summarize_text returned a full sentence. What if the next step in your pipeline needs a label — not a sentence — so a conditional can branch on it?
Right, summarizing a client email and then routing it to the right team. A label like negative is what the router needs, not a paragraph.
Exactly. A system prompt is how you constrain the agent to that one label. The instruction goes in at construction time, and every run_sync call inherits it:
agent = Agent(model, system_prompt="Return one word: positive, neutral, or negative.")
result = agent.run_sync(text)
return result.output.strip().lower()The .strip().lower() at the end matters — the model will occasionally hand back "Positive." with a capital and a period, or " negative" with a leading space. Neither of those compares equal to "negative" in a downstream if statement.
So the system prompt isn't a guarantee? I told it to return one word and it still might not?
The system prompt is a strong suggestion, not a contract. Temperature, phrasing, and model version all introduce variation. .strip() removes whitespace at either end. .lower() folds capitalization. Together they collapse "Positive", " positive ", and "POSITIVE" into the same string your router expects.
So I'm writing defensive code around LLM output the same way I'd write defensive code around a user input field?
Same instinct, yes. Any value from outside your control — a user form field, an API response, an LLM output — needs normalization before you compare it. The three-character chain .strip().lower() is the minimum for text labels. Here's the full function:
def classify_sentiment(text: str) -> str:
agent = Agent(model, system_prompt="Return one word: positive, neutral, or negative.")
result = agent.run_sync(text)
return result.output.strip().lower()And once that label is clean, I can pipe it straight into a routing dict or an if-else without any extra parsing. That's the part that was always fragile when I tried to do this in ChatGPT.
A ChatGPT reply is for reading. A Python string is for routing. Write classify_sentiment(text) — set the system prompt, call run_sync, normalize with .strip().lower(), and return.
TL;DR: system_prompt constrains the shape; .strip().lower() enforces it in code.
system_prompt= — fixed instruction the model reads before every input.strip() — removes leading and trailing whitespace.lower() — folds capitalization so comparisons are case-insensitive| Raw output | After .strip() | After .strip().lower() |
|---|---|---|
"Positive" | "Positive" | "positive" |
" negative " | "negative" | "negative" |
"NEUTRAL" | "NEUTRAL" | "neutral" |
A trailing period still leaks through — Day 11 shows result_type=Literal[...] for strict enum guarantees.
Create a free account to get started. Paid plans unlock all tracks.