One Tool Call at a Time

UC Berkeley's LLMCompiler found that executing independent tool calls concurrently instead of one at a time cuts latency up to 3.7x and cost up to 6.7x. Published at ICML 2024, it also found roughly 9% higher accuracy, all against ReAct-style sequential execution. Anthropic's and OpenAI's APIs have batched proposals of multiple tool calls into a single model turn by default for two years, but neither API decides how those calls get executed. Anthropic's own docs say plainly that execution order is the caller's choice, not the model's. Most agent code still awaits each tool call in a loop anyway, which keeps the token savings from batching and throws away the latency win concurrency was supposed to buy.

Published 2026-07-13 by Dor Amir on the Nadir blog.

Filed under Agents.

Two numbers hiding behind one feature name

In December 2023, a team of UC Berkeley researchers published LLMCompiler, a framework for scheduling an LLM agent's function calls as a dependency graph instead of a strict sequence. Benchmarked against ReAct-style sequential execution, the paper reports a consistent latency speedup of up to 3.7x, cost savings of up to 6.7x, and an accuracy improvement of roughly 9%. Source: Kim et al., "An LLM Compiler for Parallel Function Calling," ICML 2024. Those numbers get quoted constantly as evidence that parallel tool calling is a free win. They are real, and they are also two different mechanisms wearing one name, which is why so many teams ship the first one and never notice they left the second one on the table.

Both Anthropic's and OpenAI's APIs have supported parallel tool calling for about two years. By default, Claude may return several tool_use blocks in a single response, and OpenAI's parallel_tool_calls parameter defaults to true for the same reason. Source: Anthropic, "Parallel tool use," Claude Platform Docs. Source: OpenAI, "Function calling," API documentation. What neither provider's docs claim is that this makes your agent faster. Anthropic's own page is explicit about it: "The API doesn't prescribe an execution order: you can run the calls concurrently, sequentially in the order they appear, or in any combination that suits your tools." The model batching its proposal into one turn and your code running those calls at the same time are two separate decisions, and only one of them is the model's.

The two levers, and what each one actually moves

The confusion is worth untangling before touching a chart, because the fix for each lever is different and neither one substitutes for the other.

Turn-count reduction. When a model proposes four tool calls in one response instead of across four separate turns, your agent pays the fixed cost of a model turn, the system prompt, the tool schemas, the accumulated history, once instead of four times. That is a token and dollar saving. It happens the moment the model batches the proposal, and it happens whether your code then runs those four calls one after another or all at once.

Execution concurrency. When your code runs those four calls with asyncio.gather or Promise.all instead of awaiting them in a loop, the wall-clock time collapses from the sum of four round trips to roughly the slowest one. That is a latency saving, and it depends entirely on how the calling code is written. The API returning four tool-call proposals in one turn does not make your for-loop concurrent. It just gives the for-loop four things to iterate over instead of one.

ArchitectureModel turns for 4 callsHow the 4 calls executeWhat it saves
Sequential (ReAct-style)4 decision turns + 1 synthesisOne at a time, each gated by a model turnNeither
Batched proposal, sequential execution1 decision turn + 1 synthesisOne at a time, in a loopTokens and cost, not latency
Batched proposal, parallel execution1 decision turn + 1 synthesisConcurrentlyTokens, cost, and latency
Optimized pipeline1 decision turn + 1 synthesisConcurrently, with selective reads and cache hitsAll three, plus reduces what gets read

The second row is the one worth checking your own codebase against. It is the easiest state to end up in by accident: you adopt an SDK that already batches tool-call proposals, you feel the token bill drop, and you conclude the parallel tool calling box is checked. The loop that executes those calls is still a for loop.

Concurrency, not batching, is what collapses wait time

Here is what the two levers do to wall-clock time on an illustrative agent step: a plan-then-act loop that needs four independent lookups, each a separate tool call, before it can synthesize an answer. The model below assumes a 0.6 second average tool round trip and a 1.2 second average model turn, both illustrative figures, not measurements from a specific vendor or benchmark.

Bar chart comparing wall-clock latency across four tool-calling architectures: sequential ReAct at 8.4 seconds, batched proposal with sequential execution at 4.8 seconds, batched proposal with parallel execution at 3.1 seconds, and an optimized pipeline with parallel execution plus selective reads and caching at 2.4 seconds. An illustrative model, not measured production data.
Bar chart comparing wall-clock latency across four tool-calling architectures: sequential ReAct at 8.4 seconds, batched proposal with sequential execution at 4.8 seconds, batched proposal with parallel execution at 3.1 seconds, and an optimized pipeline with parallel execution plus selective reads and caching at 2.4 seconds. An illustrative model, not measured production data.

Going from sequential to batched cuts latency by cutting model turns, about 43% in this model. Going from batched-sequential to batched-parallel cuts it again, about 35% further, for a completely different reason: the tool round trips stop queuing behind each other. Anthropic's own guidance on when to take that second step is a useful filter: "Independent, read-only operations are usually safe to run in parallel for lower latency. Tools with side effects, shared state, or ordering requirements might be better run sequentially." Source: Anthropic, "Parallel tool use," Claude Platform Docs. Four independent web searches clear that bar easily. Four sequential writes to the same record do not, and running them concurrently anyway is how agents end up racing themselves.

What batching alone saves, and does not

Turn-count reduction is a real saving, and it is worth separating from concurrency because it shows up in a different place: the token bill, not the clock. The same illustrative step, broken down by where the tokens actually go:

Grouped bar chart of tokens per agent step by workflow stage, comparing sequential execution (one tool call per model turn) against batched execution (model proposes all four tool calls in one turn). Reasoning and structural overhead drops from 8,000 to 3,200 tokens, and result reading drops from 7,000 to 2,800 tokens, both from fewer repeated turns. Retrieved context stays flat at 2,800 tokens in both, and search query generation and final synthesis are unchanged. Total: 18,340 tokens sequential versus 9,340 batched, an illustrative model, not measured production data.
Grouped bar chart of tokens per agent step by workflow stage, comparing sequential execution (one tool call per model turn) against batched execution (model proposes all four tool calls in one turn). Reasoning and structural overhead drops from 8,000 to 3,200 tokens, and result reading drops from 7,000 to 2,800 tokens, both from fewer repeated turns. Retrieved context stays flat at 2,800 tokens in both, and search query generation and final synthesis are unchanged. Total: 18,340 tokens sequential versus 9,340 batched, an illustrative model, not measured production data.

Two of the five categories shrink when the proposal is batched: the fixed system-prompt and tool-schema overhead that gets resent on every model turn, and the accumulated history that grows every time a prior tool result has to be restated for the next turn to see it. Both of those costs are purely a function of how many turns the loop takes. This blog has already measured the first one directly: a ReAct-pattern research agent spends 82% of its token budget before writing a word of the actual answer, and tool schemas alone can burn well over 100,000 tokens of context before an agent with a few MCP servers connected does any work. Batching the proposal does not touch either of the other two categories: retrieved context is identical in both rows, because the same four calls fetch the same four results either way. Batching changes how many times the loop pays the toll booth. It does not change how much cargo crosses the bridge.

Where the dollar cost actually drops

Extending the same model to cost, at blended premium-model rates against a 10x cheaper model for delegated sub-work, makes the two-lever split even sharper:

Bar chart of cost per 1,000 agent steps across four configurations: sequential ReAct always-premium at $100, batched proposal always-premium at $55, batched proposal with parallel execution and sub-calls routed to a cheap model at $35, and the same configuration with cache-aware handling of repeat sub-queries at $30. An illustrative model, not measured production data.
Bar chart of cost per 1,000 agent steps across four configurations: sequential ReAct always-premium at $100, batched proposal always-premium at $55, batched proposal with parallel execution and sub-calls routed to a cheap model at $35, and the same configuration with cache-aware handling of repeat sub-queries at $30. An illustrative model, not measured production data.

Notice what does not move: going from batched-sequential to batched-parallel execution, the step that collapsed latency in the earlier chart, leaves the cost bar almost untouched, because concurrent execution changes when the tokens get spent, not how many get spent. The real cost drop between those two configurations comes from a decision this chart bundles in alongside it: routing the interior sub-calls, the per-result summarization and formatting work inside the batch, to a cheaper model instead of the one doing the planning. The same principle that makes multi-agent orchestration expensive applies here at a smaller scale: pricing every sub-step at the coordinator's rate is where the multiplier comes from, and routing the sub-steps individually is what removes it. Cache-aware handling of repeated sub-queries adds a further, smaller cut, useful specifically when the same lookups recur across steps or across users, not a fixed percentage you can assume applies to a one-off task.

The failure modes nobody draws in the diagram

None of the above is a reason to parallelize and route everything by default. Four ways this goes wrong in production:

Horizontal bar chart showing the share of non-productive tokens in a naive sequential agent loop, broken into four categories: duplicate context from history re-sent every turn at 47%, repeated reasoning and structural overhead at 43%, verbose unformatted tool output at 6%, and over-read or irrelevant retrieved chunks at 4%. An illustrative breakdown, not measured production data.
Horizontal bar chart showing the share of non-productive tokens in a naive sequential agent loop, broken into four categories: duplicate context from history re-sent every turn at 47%, repeated reasoning and structural overhead at 43%, verbose unformatted tool output at 6%, and over-read or irrelevant retrieved chunks at 4%. An illustrative breakdown, not measured production data.
Scatter plot of cost versus quality retained across four strategies: premium-only at $100 per 1,000 steps and 100% quality, small-model-first with no verification at $18 and 78% quality, hybrid routing with sub-calls routed and a verified floor at $33 and 97% quality, and aggressive compression at $40 and 85% quality. A shaded band marks a 95% quality floor. Illustrative positions, not measured production data.
Scatter plot of cost versus quality retained across four strategies: premium-only at $100 per 1,000 steps and 100% quality, small-model-first with no verification at $18 and 78% quality, hybrid routing with sub-calls routed and a verified floor at $33 and 97% quality, and aggressive compression at $40 and 85% quality. A shaded band marks a 95% quality floor. Illustrative positions, not measured production data.

Hybrid routing lands closest to the quality floor most teams actually target, not because routing is magic, but because it keeps the planning and synthesis turns, the steps that need real reasoning, on the model that can do it, and only delegates the interior sub-calls a verification step can catch if they go wrong. The same logic underlies routing without a verifier being closer to a coin flip than a decision: a router that doesn't check its own cheap-model output is a small-model-first strategy with better branding.

What actually closes the gap

What that looks like in a request

None of this requires a new framework, just running the batch concurrently and routing what runs inside it:

import asyncio
import openai

client = openai.OpenAI(
    base_url="https://api.getnadir.com/v1",
    api_key="ndr_...",
)

# The planning turn that proposes the batch, and the final synthesis
# turn, are the two steps that plausibly need frontier reasoning.
async def run_batch(tool_calls, execute_tool):
    # Concurrency is the caller's decision, not the model's. This is
    # what turns a batched proposal into collapsed wait time instead
    # of a for-loop shaped exactly like ReAct.
    return await asyncio.gather(*(execute_tool(tc) for tc in tool_calls))

# Every per-result summarization call inside the batch is scored
# independently against the cheapest model that can handle it, not
# against the model that planned the batch.
response = client.chat.completions.create(
    model="auto",
    messages=[{"role": "user", "content": summarize_result_prompt}],
)

Nadir can shadow each interior summarization call and return model, effort, cache, context, and policy evidence without making a provider call. Join each receipt to actual batch usage and outcomes before enforcement. Complete non-streaming responses can optionally enter the verifier; streaming calls bypass post-generation verification.

Conclusion

Parallel tool calling earned its reputation honestly. LLMCompiler's 3.7x latency and 6.7x cost numbers are real results, not marketing rounding. The part that gets lost in the citation is that they come from two different changes bundled into one paper: fewer model turns, which both major APIs now hand you by default, and concurrent execution of the resulting batch, which is still a decision your code has to make on its own. Most teams got the first one for free when they upgraded SDKs and never checked whether their tool-execution loop actually runs concurrently. The second one is a few lines of asyncio.gather, and pairing it with routing the batch's interior calls is what turns a latency fix and a cost fix into the same commit instead of two separate ones nobody gets around to shipping.

Related reading


Sources: [Kim, Moon, Tabrizi, Lee, Mahoney, Keutzer, Gholami, "An LLM Compiler for Parallel Function Calling," ICML 2024, arXiv:2312.04511](https://arxiv.org/abs/2312.04511). [Anthropic, "Parallel tool use," Claude Platform Docs](https://platform.claude.com/docs/en/agents-and-tools/tool-use/parallel-tool-use). [Anthropic, "Building Effective AI Agents," December 19, 2024](https://www.anthropic.com/engineering/building-effective-agents). [OpenAI, "Function calling," API documentation](https://developers.openai.com/api/docs/guides/function-calling). [Lin, Liew, Savarese, Li, "W&D: Scaling Parallel Tool Calling for Efficient Deep Research Agents," February 2026, arXiv:2602.07359](https://arxiv.org/abs/2602.07359). All latency, token, and cost figures presented as an "illustrative model" are back-of-envelope calculations from the stated assumptions, not measured production traces or a specific vendor benchmark.


More on agents

What Nadir is

Nadir is an LLM router. Nadir sizes every prompt and routes it to the cheapest model that still clears your quality bar. A small decision model reads each prompt against your model menu and picks the lowest-cost model it expects to handle it. When the decision model is uncertain or unavailable, the request goes to the strongest model on the menu. The routing step makes no call to your model provider.

Nadir runs two ways. The decision API returns a model, reasoning-effort, cache, context, and policy recommendation without calling a model provider, beside the gateway you already run. That is how a shadow-mode evaluation works, and its projected savings stay advisory. The OpenAI compatible managed proxy executes the route, and migration is a two-line change: point the base URL at api.getnadir.com and set model to auto. On that path an optional verifier can score a complete non-streaming answer and escalate to a stronger model when it misses the configured bar. Streaming bypasses post-generation verification. BYOK is supported on every tier.

For coding agents, Nadir Auto is a terminal launcher for Claude Code or Codex that asks Nadir for the main-session model before each turn, with your configured model as the fallback and cost ceiling. Delegation integrations for Claude Code, Codex, or Cursor instead recommend a model tier for subagent work, and the agent decides. Either way inference runs on your own provider account, so Nadir holds no provider keys on that path.

What the numbers are, and what they are not

Nadir publishes each evaluation with its scope. On checkable code, run-check-escalate solved 392 of 395 common HumanEval and MBPP problems (99.2%), graded by running the canonical tests; that applies only to tasks with runnable deterministic tests. Nadir-Tumbler posts an arena_score of 72.3 on RouterArena's public scorer, 5th of 23 routers, which measures the routing decision on RouterArena's own model pool. A reference-assisted RouterBench evaluation over 11,420 held-out triples produced a 60% lower projected cost than always-Opus with about 98% retained quality and a 1.7% catastrophic-route rate. That experiment gave the verifier the expensive-model reference answer, which production does not have, so it is a research ceiling and not the deployed path.

None of these is a production guarantee, a universal savings rate, or a forecast for any particular workload. Customer savings are reported from measured execution against a declared baseline, and customer quality only from outcome-labelled traffic. Projected savings and realized savings are separate artifacts and are never blended.

Design-partner program

Three rungs, picked by risk appetite. Rung 0 Shadow runs advisory decision calls alongside live traffic and returns a projected receipt, with nothing in the request path changed. Rung 1 Hosted is the two-line swap on a production slice and returns a realized receipt. Rung 2 On-prem is a supervised six-week proof of concept inside the partner's VPC, where no prompt, response, or usage reaches Nadir. The commitments are the same at every rung. Apply for a rung directly: Rung 0 Shadow, Rung 1 Hosted, or Rung 2 On-prem. Not sure which fits? Start at getnadir.com/contact/?reason=design-partner.

Licensing

NadirClaw is the self-hosted core, source-available under the PolyForm Noncommercial License. Source-available is the correct label; NadirClaw is not open source. Nadir Route's hosted plan has no base fee and charges a variable fee only on measured savings from requests Nadir executed.

Pages on this site

Machine-readable summaries of this site: llms.txt and llms-full.txt.