f1: Hello, and the two inputs
Your first agent call needs two things to get a reply out of a model: a system prompt for the agent, and the thing you want to ask it right now.
Quick path
Section titled “Quick path”In a hurry? These three steps are the whole challenge. Everything below is the why and the how.
- Run
make f1and watch it print two pitches: both invent details, and both are shaped by the same sharedinstructions. - Edit
start/agent.py: do TODO 1 (add a rule toinstructionsand watch both pitches change), TODO 2 (add a one-off rule to the first prompt only), TODO 3 (swap bothagent.run(...)calls foragent.run_stream(...)blocks), TODO 4 (optional: setmodel_settingstemperature to 0 then 1.2). - Done when a rule in
instructionschanges both answers, a one-off rule in the first prompt affects only pitch[1], both pitches type themselves out token by token, and you can point at why the price was fabricated.
Pydantic AI calls these instructions and the prompt, and learning to tell them apart is
most of this first challenge. The last move changes how the reply arrives: streamed
token by token, the way every shipped AI app delivers text.
Mental model
Section titled “Mental model”Change the instructions and every answer shifts; change the prompt and only this one does.
Open start/agent.py. We build an agent once with Agent(...), hand
it a model and its instructions, then call it with a prompt:
The instructions are the agent’s system prompt: its role, its tone, its rules. You set
them once and they apply to every call the agent makes.
The prompt is the one thing you’re asking this time.
Change the instructions and you change how it answers everything. Change the prompt and you change what it answers just this once.
Run it:
You’ll get two pitches from the same agent.
Read the invented price and hotel back from pitch [1]. The agent has no way to know either
one. It has no tools and no data, so it filled the gaps with text that reads right.
Now look across both outputs. They are different asks, but they share one thing: the same
instructions. That is the setup for the whole exercise.
That’s fabrication, and it’s the reason the rest of the workshop exists. Tools start fixing it in f4.
Build it
Section titled “Build it”-
Run it and spot the invention. Run
make f1and read the pitch. Pick out the price and the hotel, and notice you have no way to check either. The model made them up. -
Add a rule to the instructions (TODO 1). Write a rule of your own into
instructions, keep both prompts exactly the same, and run again. Pick a direction, predict what it’ll do, then check:Try a length limit, a format (“answer as three bullets”), or a persona (“talk like a pirate”). Both pitches change, even though you only wrote the rule once. That’s the instructions doing their job on every call.
-
Add a rule to one prompt (TODO 2). Leave your instructions rule in place, and add a one-off rule (say “Answer in one sentence.”) to the first prompt only:
Run it again. Pitch
[1]obeys it and pitch[2]does not, while both still obey the instructions rule. A standing rule lives ininstructions; a one-off ask lives in the prompt. Same kind of words, different reach. -
Make it stream (TODO 3). So far each pitch lands all at once after a silent wait. No shipped AI app works that way: the reply types itself out as the model writes it, and the silent wait reads as hung. The swap is
agent.run(...)foragent.run_stream(...), a context manager whosestream_text(delta=True)yields the reply a few characters at a time, so youasync forand print each chunk as it arrives. On a different agent, the shape is:Convert both calls. There is no
.outputto print at the end any more; the chunks are the text, so print the[1]label before the block and a newline after it. Run it and watch the pitch type itself. Same agent, same work, same wait; it just stops feeling like one. -
Turn the temperature up (TODO 4, optional stretch).
temperaturecontrols how much the model varies its wording. Add amodel_settingsline and run the same prompt a few times at each value, predicting before each run:At
0the pitch barely moves. At1.2it swings around. Watch the one thing that never improves, though: a higher temperature gives you a different invented price, not a truer one. Wording is all temperature touches. -
Check you’ve got it. You can add a rule to
instructionsand watch both answers change, add a one-off rule to the first prompt and see[1]obey it while[2]does not (with the instructions rule still on both), make both pitches stream token by token, and explain why the invented price or hotel was made up.
Stuck? finish/agent.py is the canonical version. Read it after you’ve had a real go.
A couple of things worth knowing
Section titled “A couple of things worth knowing”Why async def main() from lesson one?
Because streaming is async, and so is almost everything after f1: tools, MCP, and the web
handler all want an event loop. So the file starts the way the rest of the workshop
continues: await agent.run(...) inside async def main(), run with asyncio.run(main()).
Pydantic AI also has agent.run_sync(...), a blocking call for quick scripts with no
event loop; it hands back the same result, it just can’t stream.
When do I stream, and when do I run()?
Stream when a human is watching the reply arrive; run() when code consumes it.
Streaming is the industry default for anything user-facing, which is why it’s in lesson
one. But several later challenges feed one run’s reply into more code: a typed output to
validate (f3), a one-character verdict gate (f5), a chain of edits (p1). Code can’t act
on half a sentence, so those use await agent.run(...) and wait for the whole text. Same
agent either way; you pick the delivery per run.
Is instructions just a system prompt?
Same idea, current name. Some SDKs call the system prompt the “system prompt”; Pydantic
AI calls it instructions, and it lines up with the TypeScript path’s Agent. Pydantic AI
also has a system_prompt argument, but instructions is the idiom we use: it’s the text
the model treats as its role on every run, separate from the user’s message.
Why is f1 the only challenge with no trace?
From f2 on, each file calls enable_tracing() and logfire prints the run to your console
as a span tree: what went out, what came back, how long it took, how many tokens it used.
f1 leaves it off so your first contact is just the agent and its answer, nothing else on
screen. The observability spine switches on in f2.
That’s the foundation the whole workshop sits on. Next up is f2, where we open the hood: you’ll see the message loop underneath this single call, count its tokens, and read the run as a trace in your console instead of guessing at what happened.