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_knowledge retrieval 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.

A supervisor agent (Atlas) at the top delegates via tool_use arrows to three specialist agents below: a Researcher with its own search_knowledge retrieval tool, a Budget analyst with cost-lookup tools, and an Itinerary writer that drafts the day-by-day plan. A dashed arrow shows each specialist's result returning to the supervisor as a tool_result. Each specialist is itself a full agent running its own run_agent loop.
The supervisor calls each specialist like a tool; behind every tool is a full agent running its own loop with its own tools, returning just its final answer.

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:

  1. Atlas delegates. The supervisor emits a tool_use for research_destination with {"question": "When should I visit Arashiyama?"}.
  2. The sub-agent runs its own loop. Inside that one tool call, the researcher runs two steps: a thought plus a search_knowledge tool_use, which really retrieves the kyoto-guide passage; then a grounded answer — “Visit Arashiyama early morning to avoid crowds [kyoto-guide].” (example — exact wording varies).
  3. 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.