Lesson 2 - Agents as Tools
Welcome to Agents as Tools
Last lesson ended on a promise: multi-agent systems need no new engine because a specialist agent is exposed to the supervisor as an ordinary tool. This lesson makes that concrete. You’ll build a supervisor (Atlas) that delegates a research question by calling a tool named research_destination(question) — exactly the way it calls any tool. The twist is what lives behind that tool: not a simple function, but a whole sub-agent running its own run_agent loop with its own retrieval tool over a knowledge base. The supervisor never learns that its “tool” is really an agent. It sends a tool_use, gets a tool_result, and continues. That’s the entire pattern — delegation built from the loop and tool mechanics you already have.
By the end of this lesson, you will be able to:
- Build a specialist sub-agent (a destination researcher) with its own
search_knowledgeretrieval tool - Wrap that whole sub-agent in a plain function and expose it to a supervisor as a single tool
- Trace how a fact retrieved deep inside the sub-agent propagates up into the supervisor’s final answer
- Explain context isolation — why the supervisor sees only the answer, not the sub-agent’s internal steps
Let’s build the specialist first, then hand it to the supervisor.
The Specialist Sub-Agent
Start at the bottom: a destination researcher. It’s nothing exotic — just a run_agent (the canonical loop from Modules 2-6) with one tool, search_knowledge, that retrieves from a tiny knowledge base (the retrieval idea from Module 6). Its system prompt is tight and single-purpose: search the KB, cite sources.
kb = KB()
kb.add("kyoto-guide",
"Arashiyama and the bamboo grove are best visited early morning to avoid crowds.")
def search_knowledge(query):
hits = kb.search(query, k=1)
return hits[0][0] + ": " + hits[0][1] if hits else "No match."
researcher_tools = [{"name": "search_knowledge", "description": "Search travel KB.",
"input_schema": {"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"]}}]That’s a complete, focused agent’s toolbox. Given a question, this researcher will emit a tool_use for search_knowledge, get the retrieved passage back as a tool_result, and then write a grounded, cited answer. Nothing here is aware that a supervisor exists — and that’s the point. The specialist stays sharp on one job with one tool and one clean context.
Wrapping the Agent as a Tool
Here’s the move that turns a sub-agent into a delegation target: wrap the entire researcher in a plain Python function that runs the sub-agent and returns its answer string.
def research_destination(question):
"""Expose the whole researcher agent to the supervisor as ONE tool."""
out = run_agent(researcher_client, question,
system="You are a destination researcher. Search the KB and cite sources.",
tools=researcher_tools,
tool_functions={"search_knowledge": search_knowledge})
return out["answer"]From the outside, research_destination looks like any other tool function: it takes a string, returns a string. From the inside, calling it runs a whole agent — its own multi-step run_agent loop, its own model calls, its own retrieval. This is the composition Lesson 1 described: a tool whose body is another agent. The supervisor’s loop doesn’t change one bit; it just calls a function whose result happens to be the distilled output of a nested agent.
The Supervisor Delegates
Now the top: Atlas, the supervisor. It’s also just a run_agent. Its single tool is research_destination — described to it as “ask the destination-researcher agent about a place.” Atlas has no idea this tool is an agent; it only sees a name, a description, and an input schema, like every tool it has ever used.
supervisor_tools = [{"name": "research_destination",
"description": "Ask the destination-researcher agent about a place.",
"input_schema": {"type": "object",
"properties": {"question": {"type": "string"}},
"required": ["question"]}}]
out = run_agent(supervisor_client, "Plan a morning in Kyoto near Arashiyama.",
system="You are Atlas. Delegate research to specialists, then plan.",
tools=supervisor_tools,
tool_functions={"research_destination": research_destination})
print("supervisor answer:", out["answer"])When you run this, both loops turn. The orchestration below is verified against a mock that mirrors the Anthropic SDK surface (this environment has no ANTHROPIC_API_KEY, so the nested loops, tool dispatch, and real retrieval are exercised for real; the Claude API code is correct as written, and the model’s exact wording is illustrative). Here is what happens:
- Atlas delegates. The supervisor emits a
tool_useforresearch_destinationwith{"question": "When should I visit Arashiyama?"}. - The sub-agent runs its own loop. Inside that one tool call, the researcher runs two steps: a thought plus a
search_knowledgetool_use, which really retrieves thekyoto-guidepassage; then a grounded answer — “Visit Arashiyama early morning to avoid crowds [kyoto-guide].” (example — exact wording varies). - The fact propagates up. That answer returns to Atlas as a plain
tool_result, and Atlas folds it into its final plan: “go to Arashiyama early morning to beat the crowds [kyoto-guide].”
The verification confirms the mechanics: the researcher ran 2 model calls, the supervisor ran 2 model calls, and the [kyoto-guide] source and the “early morning” fact are real — they came from actual retrieval inside the sub-agent, not from the supervisor guessing. A fact discovered two loops deep surfaced, cited, in the top-level answer.
Context isolation is the quiet win
Notice what Atlas didn’t see. The researcher’s thought, its search_knowledge call, the raw retrieved passage, the intermediate tool_result — none of that entered the supervisor’s context. Atlas received only the researcher’s final one-line answer. This is context isolation: each agent keeps a tight prompt and a small tool set, and the supervisor’s transcript stays clean because sub-agents hide their internal work. It’s what lets you nest specialists arbitrarily — the pattern is just the loop composed, so a researcher could itself delegate to a deeper specialist without any of them cluttering each other’s context.
The trade-off is real, though: nesting multiplies model calls — the supervisor loops and each sub-agent loops, so a single top-level request here cost four model calls (2 + 2). Delegate a sub-problem only when it genuinely deserves its own focused agent (like retrieval-grounded research), not for jobs a plain function or a single tool could handle.
Practice Exercises
Exercise 1: What does the supervisor actually see?
The researcher runs a 2-step loop internally — a thought, a search_knowledge call, a retrieved passage, then its answer. When research_destination returns, which of those does the supervisor’s context receive?
Hint
Only the researcher’s final answer string. The function returns out["answer"], and that string becomes the tool_result fed back to Atlas. The thought, the search_knowledge tool_use, and the raw retrieved passage all stay inside the sub-agent’s own messages list — the supervisor never sees them. That’s context isolation: the sub-agent does the messy work and hands back a clean result.
Exercise 2: Count the model calls
For the single request “Plan a morning in Kyoto near Arashiyama,” how many model calls happen in total, and where?
Hint
Four. The supervisor makes 2 (one to emit the research_destination tool_use, one to write the final plan after the result comes back). The researcher sub-agent makes 2 more inside that single tool call (one to emit the search_knowledge tool_use, one to write its grounded answer). Nesting multiplies calls — supervisor calls times whatever each delegated sub-agent spends — which is exactly the coordination cost to weigh before splitting.
Exercise 3: Add a second specialist
Suppose you want Atlas to also estimate costs. Sketch how you’d add a budget_analyst specialist without touching the run_agent loop.
Hint
Build a second sub-agent — a run_agent with its own cost-lookup tools and a tight budget-focused system prompt — then wrap it in a function estimate_budget(question) that returns out["answer"]. Add one more entry to supervisor_tools (name, description, input_schema) and one more key to the supervisor’s tool_functions. The loop is unchanged; you’re just giving Atlas another tool that happens to be an agent.
Summary
A specialist agent becomes a delegation target the moment you wrap it in a plain function and expose that function as a tool. Here, a destination-researcher sub-agent — a run_agent with its own search_knowledge retrieval tool — is wrapped by research_destination(question), which the supervisor (Atlas) calls like any ordinary tool. When Atlas delegates, the sub-agent runs its own loop (thought → real retrieval → grounded answer), and the retrieved fact propagates all the way up, cited, into the supervisor’s final plan. The supervisor sees only that final answer, never the sub-agent’s internal steps — context isolation — which keeps every agent’s prompt tight and context clean, and lets you nest specialists arbitrarily. The cost is that nesting multiplies model calls, so you delegate only sub-problems that deserve their own agent.
Key Concepts
- Agent as a tool — wrap a whole sub-agent in a function that returns its
.answer, and register it as an ordinary tool; the supervisor’s loop is unchanged. - Nested loops — one top-level tool call runs a full sub-agent loop; the supervisor and sub-agent each run their own
run_agent. - Fact propagation — a fact retrieved deep in the sub-agent surfaces, cited, in the supervisor’s final answer (verified: real
[kyoto-guide]retrieval, model wording illustrative). - Context isolation — the supervisor receives only the sub-agent’s final answer, keeping prompts tight and contexts clean, at the cost of extra model calls.
Why This Matters
Agents-as-tools is the foundational multi-agent pattern precisely because it adds nothing to the mental model you already carry — it’s the run_agent loop composed with itself. That means you can grow a system incrementally: turn one overloaded responsibility into a focused sub-agent, expose it as a tool, and your supervisor keeps working unchanged. Context isolation is what makes this scale — because each specialist hides its internal churn behind a clean answer, you can stack researchers, analysts, and writers without any of them drowning the others in context. Get this pattern in your hands and the rest of the module is variations on it: next you’ll put a lightweight classifier in front of your specialists so each request reaches exactly the right one.
Next Steps
Continue to Lesson 3 - Routing
Add a lightweight classifier that reads each request and dispatches it to exactly one specialist.
Back to Module Overview
Return to the Multi-Agent Systems module overview
Continue Building Your Skills
You just built the foundational multi-agent pattern: a specialist sub-agent wrapped as a tool, a supervisor that delegates to it, and a grounded fact flowing cleanly from deep inside the sub-agent up into the final answer — all from the loop you already know. Next you’ll add routing: a classifier that reads each incoming request and sends it to exactly the right specialist, so the correct expert handles each job.