You have a list of five URLs with titles and you want the agent to pick the most informative one. How do you make sure it returns a URL from the list, not a paraphrase?
Add a system_prompt saying "reply only with one of these URLs"? That still lets the model invent something.
A prompt is a hint. A type is a contract. Build Literal[url1, url2, ...] dynamically from the input list and pass it as result_type. Now the only valid replies are the exact URLs you handed in:
from typing import Literal
urls = [r["url"] for r in results]
agent = Agent(model, result_type=Literal[tuple(urls)])The type is generated from the data? I pass the exact URL tuple as the Literal members so the agent cannot drift?
Exactly — a runtime-built type. The agent sees the schema, picks one, and PydanticAI validates the answer. Wrapped with a short ranking prompt:
def select_best_url(results: list) -> str:
urls = [r["url"] for r in results]
agent = Agent(
model,
result_type=Literal[tuple(urls)],
system_prompt="Pick the single most informative URL from the list."
)
listing = "\n".join(f"{r['title']}: {r['url']}" for r in results)
result = agent.run_sync(listing)
print(f"Agent pick: {result.output}")
return result.outputAnd if someone passes a list of one URL? The Literal still validates, right?
It does — a one-member Literal is still valid. For larger lists you feed every title plus URL into the prompt so the model has context to choose, but the output type guarantees only a URL from the input comes out. Invention is impossible.
So this pattern fits any ranking task — best candidate, highest-priority ticket, top reply — whenever the answer is one of a known set?
That is the generalization. Any selection over a finite input set is a Literal type built from the set itself. Typed ranking, no string matching, no fallback logic.
TL;DR: build Literal[tuple(options)] from your input so the agent must pick a real member.
Literal[tuple(urls)] — closed set derived from the call argumentssystem_prompt — guides selection criteria ("most informative", "highest priority")| Set known at code time | Set depends on input |
|---|---|
Literal["a","b","c"] | Literal[tuple(options)] |
| Static enum | Dynamic ranking |
A Literal built from the input is the simplest safe way to ask the agent to select.
You have a list of five URLs with titles and you want the agent to pick the most informative one. How do you make sure it returns a URL from the list, not a paraphrase?
Add a system_prompt saying "reply only with one of these URLs"? That still lets the model invent something.
A prompt is a hint. A type is a contract. Build Literal[url1, url2, ...] dynamically from the input list and pass it as result_type. Now the only valid replies are the exact URLs you handed in:
from typing import Literal
urls = [r["url"] for r in results]
agent = Agent(model, result_type=Literal[tuple(urls)])The type is generated from the data? I pass the exact URL tuple as the Literal members so the agent cannot drift?
Exactly — a runtime-built type. The agent sees the schema, picks one, and PydanticAI validates the answer. Wrapped with a short ranking prompt:
def select_best_url(results: list) -> str:
urls = [r["url"] for r in results]
agent = Agent(
model,
result_type=Literal[tuple(urls)],
system_prompt="Pick the single most informative URL from the list."
)
listing = "\n".join(f"{r['title']}: {r['url']}" for r in results)
result = agent.run_sync(listing)
print(f"Agent pick: {result.output}")
return result.outputAnd if someone passes a list of one URL? The Literal still validates, right?
It does — a one-member Literal is still valid. For larger lists you feed every title plus URL into the prompt so the model has context to choose, but the output type guarantees only a URL from the input comes out. Invention is impossible.
So this pattern fits any ranking task — best candidate, highest-priority ticket, top reply — whenever the answer is one of a known set?
That is the generalization. Any selection over a finite input set is a Literal type built from the set itself. Typed ranking, no string matching, no fallback logic.
TL;DR: build Literal[tuple(options)] from your input so the agent must pick a real member.
Literal[tuple(urls)] — closed set derived from the call argumentssystem_prompt — guides selection criteria ("most informative", "highest priority")| Set known at code time | Set depends on input |
|---|---|
Literal["a","b","c"] | Literal[tuple(options)] |
| Static enum | Dynamic ranking |
A Literal built from the input is the simplest safe way to ask the agent to select.
Create a free account to get started. Paid plans unlock all tracks.