What this is
No new concepts in this lesson. This is a checkpoint: a small, real agent built entirely out of ideas from Lessons 2 through 9, combined into one thing. If you can read the code and explain why every tool, validator, and dependency is there, you've mastered the Beginner tier. If any piece feels unfamiliar, revisit the lesson it came from before continuing to Intermediate.
What it does
An agent that researches a topic, using a fake "search" tool, so this runs with no external API, then returns a validated ResearchNote, and saves it into an in-memory notes store via a second tool. The model is instructed to call fake_search first to gather "evidence," then save_note to persist its findings, then produce the final ResearchNote. You don't write that sequencing logic, the model decides the order based on the system prompt and the tool descriptions, same as any multi-tool LangChain agent.
Where each piece came from
class ResearchNote(BaseModel): title: str summary: str key_points: list[str] confidence: float
@dataclassclass ResearchDeps: notes: dict[str, ResearchNote] = field(default_factory=dict)
agent = Agent( "google:gemini-3.5-flash-lite", deps_type=ResearchDeps, output_type=ResearchNote, system_prompt=( "You are a research assistant. For the given topic: call " "fake_search to gather evidence, call save_note to persist your " "findings, then produce a final ResearchNote with at least two " "key_points and a confidence between 0.0 and 1.0." ),)Lesson 3: the final answer is a ResearchNote model, not a string. Lesson 5, 7: the notes store is injected as a dependency, not a global.
@agent.tool_plaindef fake_search(topic: str) -> str: """Search a small internal knowledge base for a topic.
Args: topic: The topic to search for, lowercase. """ return FAKE_KNOWLEDGE_BASE.get( topic.lower(), f"No entries found for '{topic}'." )
@agent.tooldef save_note(ctx: RunContext[ResearchDeps], note: ResearchNote) -> str: """Save a finished research note.
Args: note: The research note to persist. """ ctx.deps.notes[note.title] = note return f"Saved note '{note.title}'."Lesson 6, 7: fake_search and save_note are both tools, one plain, one reading ctx.deps.
@agent.output_validatordef validate_note(ctx: RunContext[ResearchDeps], output: ResearchNote) -> ResearchNote: if not output.key_points: raise ModelRetry("key_points must not be empty. Try again.") if not (0.0 <= output.confidence <= 1.0): raise ModelRetry("confidence must be between 0.0 and 1.0. Try again.") return outputLesson 8: rejects a note with no key points or an out-of-range confidence score, raising ModelRetry to send the model back for another attempt rather than accepting a shape-valid but meaning-invalid ResearchNote.
Checkpoint
- An agent combining a validated
output_type, injecteddeps, two tools, and a custom output validator, all in one place. - The model decides the order of tool calls; you never write that sequencing logic yourself.
- A validator failure sends the model back for another attempt, the same retry mechanism as a type-validation failure.
Try this yourself, without looking anything up: add a second fake knowledge base entry and confirm the agent's ResearchNote reflects it. Then change validate_note to also require at least three key_points and watch the retry happen.
If this ran cleanly and made sense without looking anything up, you're ready for the Intermediate tier, starting at Lesson 11, where a single run stops being the whole story and conversations start carrying memory across turns.