A subagent as a tool
One agent can call others. Wrap each specialist agent in a tool, and a main agent decides which to ask, what to ask, and how to put the answers together.
The shop now has two agents: lesson 7's, which knows orders, and lesson 28's, which knows policies. A customer asks one question about both. In the subagents pattern a main agent, often called a supervisor, calls the specialists as tools; they answer it, never the customer.
from langchain.agents import create_agent
from langchain.tools import tool
from help_model import HelpModel
from search import search_policies
from shop_model import ShopModel
from tools import lookup_order
orders_agent = create_agent(ShopModel(), tools=[lookup_order])
policies_agent = create_agent(HelpModel(), tools=[search_policies])
def last_reply(agent, question):
return agent.invoke({"messages": [{"role": "user", "content": question}]})["messages"][-1].textEach specialist is a whole agent of its own, with its own model and tools. last_reply sends it one question and hands back the text of its final message.
@tool
def ask_orders(question: str) -> str:
"""Ask the orders specialist where an order is."""
return last_reply(orders_agent, question)
@tool
def ask_policies(question: str) -> str:
"""Ask the policies specialist about refunds, shipping or accounts."""
return last_reply(policies_agent, question)Wrapped in @tool, each specialist looks like any other tool to the main agent: a name, a description and one string argument. The main agent sees a short answer, not the specialist's whole conversation.
A main agent that delegates
import re
from langchain.messages import AIMessage
from shop_model import ShopModel
class Supervisor(ShopModel):
def decide(self, messages):
if messages[-1].type == "tool":
return AIMessage("\n".join(m.text for m in messages if m.type == "tool"))
text = messages[-1].text
calls = [{"name": "ask_orders", "args": {"question": f"Where is {o}?"}, "id": f"call_{o}"}
for o in re.findall(r"\b[A-Z]\d+\b", text)]
if any(word in text.lower() for word in ("refund", "shipping", "password")):
calls.append({"name": "ask_policies", "args": {"question": text}, "id": "call_policies"})
if not calls:
return AIMessage("Which order or policy is this about?")
return AIMessage("", tool_calls=calls)The supervisor asks the orders specialist about each order it finds, writing a question of its own, and asks the policies specialist when the text mentions a policy topic. When both apply, it asks both in one message. When results come back, it joins them.
from langchain.agents import create_agent
from specialists import ask_orders, ask_policies
from supervisor import Supervisor
agent = create_agent(Supervisor(), tools=[ask_orders, ask_policies])question = "Where is A17, and how long does a refund take?"
result = agent.invoke({"messages": [{"role": "user", "content": question}]})
for message in result["messages"][1:]:
print(f"{message.type:<4}", message.text or [c["name"] for c in message.tool_calls])Both specialists ran from one message, and the final answer combines their replies. The orders specialist got "Where is A17?", not the customer's whole sentence, because the supervisor decides what each subagent is asked. By default a subagent keeps no memory between calls; each question starts it fresh.
- Ask a question with two order ids and count the calls to
ask_orders. - Ask "How do I reset my password?" and check which specialist answers.
- Make
ask_ordersreturn the specialist's whole message list and print what the supervisor gets.
Every expert started right here.