Two tools at once
A model can ask for several tools in one message. The agent runs them all before calling the model again, and return_direct can end the run with their results.
Lesson 8's question about A17 and C40 produced two tool calls in one AI message. Each call carries its own id, and each result comes back tagged with it, so the model can tell which answer belongs to which request.
from langchain.agents import create_agent
from shop_model import ShopModel
from tools import lookup_order
agent = create_agent(ShopModel(), tools=[lookup_order])result = agent.invoke({"messages": [{"role": "user", "content": "Where are A17, B22 and C40?"}]})
for message in result["messages"][1:-1]:
if message.type == "ai":
for call in message.tool_calls:
print("asked ", call["id"])
else:
print("result", message.tool_call_id, "->", message.text)Three calls went out together and three results came back, all before the model was called a second time. Most hosted models make parallel calls by default; OpenAI and Anthropic let you turn it off with parallel_tool_calls=False when binding tools.
Skipping the last model call
The final answer here only repeats what the tools said. A tool with return_direct=True ends the run as soon as it returns, and its result is the answer.
@tool(return_direct=True)
def lookup_order(order_id: str) -> str:
"""Look up an order's shipping status by its id, such as A17."""
status = ORDERS.get(order_id)
return f"{order_id} {status}." if status else f"{order_id} is not an order we have."from langchain.agents import create_agent
from shop_model import ShopModel
from tools import lookup_order
agent = create_agent(ShopModel(), tools=[lookup_order])
result = agent.invoke({"messages": [{"role": "user", "content": "Where are A17 and C40?"}]})
for message in result["messages"]:
print(f"{message.type:<5} {message.text or len(message.tool_calls)}")The run ends on the two tool messages, with no AI message after them: one model call instead of two. When the model calls several tools in one step, the run stops only if every one of them has return_direct; otherwise all results go back to the model as usual.
- Ask about one order with
return_directon and count the messages. - Stream the
return_directagent and check which step comes last. - Print each
message.idfor the tool messages and compare them with the tool call ids.
You understood something today that you didn't yesterday.