LangChainLangChain 1.4 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
46 small wins to finish your pathNext lesson →

Two tools at once

Parallel tool calls are several tool requests the model makes in one message, which the agent runs together before it calls the model again.

Last updated: 27 Sep, 2026 · LangChain 1.4

An earlier 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.

The return_direct and parallel_tool_calls options

python
@tool(return_direct=True)          # end the run as soon as this tool returns
def my_tool(...): ...

# turn parallel calls off when binding (OpenAI, Anthropic)
model.bind_tools([t], parallel_tool_calls=False)

Building the agent

Build the agent with the deterministic stand-in shop model and one lookup tool.

python
from langchain.agents import create_agent
from shop_model import ShopModel      # a deterministic stand-in model
from tools import lookup_order        # looks up one order id

agent = create_agent(ShopModel(), tools=[lookup_order])

Asking about three orders

Ask about three orders in one message. The model puts all three requests in one AI message.

python
result = agent.invoke({"messages": [
    {"role": "user", "content": "Where are A17, B22 and C40?"}
]})

Matching results to calls by id

Walk the messages between the question and the final answer. Print each tool call's id, then each result tagged with the id it answers.

python
for message in result["messages"][1:-1]:
    if message.type == "ai":
        for call in message.tool_calls:
            print("asked ", call["id"])          # one line per request
    else:
        print("result", message.tool_call_id, "->", message.text)  # tagged result

Three calls in one run

The same pieces in one file. Three calls go out together and three results come back, all before the model is called a second time.

Example
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)

Skipping the last model call

The final answer above only repeats what the tools said. A tool marked return_direct=True ends the run the moment it returns, and its result becomes the answer.

python
@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."

Run the same question and print every message with its type. The run ends on the tool results, with no AI message after them.

Example
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)}")

What the ids matched

  • Three calls, three results. All three ran before the model was called a second time, and each result is tagged with the id of the call it answers.
  • Most hosted models make parallel calls by default; OpenAI and Anthropic let you switch it off with parallel_tool_calls=False when binding tools.
  • return_direct ends the run on the tool result: one model call instead of two, and its text is the answer.
  • With 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.

A normal tool vs return_direct

Normal toolreturn_direct
Result goesBack to the modelStraight out as the answer
Model callsTwo: ask, then answerOne: ask only
Run endsAfter the model's replyAs soon as the tool returns
Several tools in a stepAll results go backStops only if all are return_direct

When to run tools in parallel

  • Looking up several items in one request instead of a round trip to the model per item.
  • A tool whose result is the finished answer, such as a lookup that needs no rewording.
Watch out. When the model calls several tools in one step, return_direct ends the run only if every one of them has it. Mix in one plain tool and all the results go back to the model as usual.
Try it yourself
  • Ask about one order with return_direct on and count the messages.
  • Stream the return_direct agent and check which step comes last.
  • Print each message.id for the tool messages and compare them with the tool call ids.

Slow is fine. Stopping is the only problem.