Why your agent's tool call latency budget needs to be 10x your prompt latency
A 10x tool-call budget is useful as initial capacity planning, not as permission for every tool to run ten times slower than the model. Remote tools cross networks, queues, gateways, and databases; agent runs also compose several calls and retries. The ratio gives that variance somewhere to go while you collect the percentile data needed for a real budget.
The model and the tool travel different paths
Suppose a model produces its first tool decision 150 ms after prompt processing begins. A 150 ms tool timeout looks symmetrical and is usually wrong.
The model provider controls most of the path that produced the decision. A remote tool may cross:
- DNS and connection setup.
- TLS and an API gateway.
- Authentication and rate-limit checks.
- A provider queue.
- A database, search index, or another upstream API.
- Serialization and response transfer.
- Local validation, tracing, and shape checks.
A healthy call can spend 35 ms on the network, 45 ms in a gateway, 280 ms in a query, 70 ms serializing a large response, and 40 ms returning to the agent. That is 470 ms before normal tail variance. A three-region deployment or cold connection can add more without any component being broken.
Start with a ratio that reflects that difference. If the model reaches a tool decision in 200 ms, a 2-second hard ceiling is a defensible first estimate for a normal remote read. It is not the final SLO. It is enough room to observe reality without converting ordinary p95 behavior into timeouts.
Budget the critical path, not the average tool
The user waits for the critical path through the agent, not the mean latency displayed on one tool's dashboard.
Represent the run explicitly:
T_run = T_plan
+ sum(T_serial_tools)
+ max(T_parallel_tools)
+ T_final_model
+ T_retry_branches
+ T_application_overhead
Consider an 8-second user-facing deadline:
| Stage | Budget | |---|---:| | Initial model decision | 450 ms | | Two serial tools | 1,400 ms each | | Three parallel tools | 1,100 ms for the slowest branch | | Final model response | 2,000 ms | | Application overhead | 350 ms | | Remaining variance and retry reserve | 1,300 ms |
The planned path consumes 6.7 seconds. The remaining 1.3 seconds is not spare capacity for every component. It is shared insurance against queueing, one retry, scheduling pauses, and network variance.
This is why local targets can all look green while the agent misses its SLO. Two serial tools at their individual p95 values can place the complete run near its p99. A retry in the wrong location exhausts the reserve.
Treat 10x as headroom, not a target
A latency budget has three separate numbers:
- Expected range: where normal calls should land, usually tracked with p50 and p95.
- Degradation threshold: where the agent should prefer a cache, fallback, partial result, or alternate tool.
- Hard timeout: where continued waiting can no longer produce a response inside the end-to-end deadline.
Do not collapse them into one threshold. A search tool might have a 450 ms p50, an 850 ms p95 alert threshold, a 1.4-second fallback threshold, and a 2-second hard timeout.
If the only configured number is 2 seconds, the team learns about degradation when requests already fail. If the only configured number is 850 ms, normal tail behavior becomes an outage.
The initial 10x ratio should usually apply to the hard ceiling. Expected and degradation thresholds should be lower and should come from measured distributions.
Enforce a deadline that follows the request
A fixed timeout per tool can violate the total deadline late in an agent run. A tool called with 900 ms remaining should not receive the same 2-second timeout it would receive at the beginning.
Pass an absolute deadline through the request and derive each tool timeout from the remaining budget:
import asyncio
import time
from collections.abc import Awaitable, Callable
from typing import TypeVar
T = TypeVar("T")
async def call_with_deadline(
tool_name: str,
operation: Callable[[], Awaitable[T]],
*,
deadline: float,
configured_timeout_s: float,
reserve_for_final_answer_s: float = 0.8,
) -> T:
remaining = deadline - time.monotonic() - reserve_for_final_answer_s
timeout = min(configured_timeout_s, remaining)
if timeout <= 0:
raise TimeoutError(f"no budget left for {tool_name}")
started = time.perf_counter()
outcome = "ok"
try:
return await asyncio.wait_for(operation(), timeout=timeout)
except TimeoutError:
outcome = "timeout"
raise
except Exception:
outcome = "error"
raise
finally:
latency_ms = (time.perf_counter() - started) * 1000
record_tool_call(
tool_name=tool_name,
latency_ms=latency_ms,
outcome=outcome,
allocated_timeout_ms=timeout * 1000,
)
Two details matter.
First, reserve time for the final model response. An agent that finishes its last tool exactly at the user deadline still failed.
Second, record timeouts and cancellations. A telemetry pipeline that records only successful completions will report lower latency during the worst incident because the slowest calls disappear from the sample.
Retries are latency multipliers
A retry policy spends from the same deadline. It also adds load to a dependency that may already be saturated.
This policy cannot meet a 5-second SLO:
first attempt timeout: 3.0 s
backoff: 0.5 s
second attempt: 3.0 s
final model response: 1.2 s
The worst-case path is 7.7 seconds before application overhead. No amount of dashboard tuning fixes arithmetic.
A bounded policy might allocate:
- First attempt: 1.2 seconds.
- Jittered backoff: 80 to 180 ms.
- Second attempt: 800 ms.
- Fallback lookup: 350 ms.
- Final model reserve: 1.2 seconds.
Retry only failures likely to change on another attempt, such as a connection reset or short provider timeout. Do not retry a schema mismatch. The same malformed contract will return faster than your incident response. Do not automatically retry side-effecting tools unless they accept and honor an idempotency key.
Hedged requests can reduce tail latency for safe, idempotent reads, but they trade latency for load and cost. Sending a second request after 700 ms may improve p99 while doubling pressure during a provider slowdown. Apply concurrency limits and measure the amplification.
Alert on distribution movement before the timeout
Hard timeouts are user-visible failures. The monitor should detect movement earlier.
A useful regression rule combines volume, an absolute threshold, a relative threshold, and persistence:
- At least 100 calls in both the recent and baseline windows.
- Recent p95 above 900 ms.
- Recent p95 at least 35% above the seven-day baseline.
- Condition present for three consecutive five-minute windows.
The sample floor avoids overreacting to five calls. The absolute threshold avoids paging because 20 ms became 30 ms. The relative threshold catches a meaningful shift before it reaches the hard timeout. Persistence filters one bad batch or short network event.
Segment before drawing conclusions. Group by tool operation, region, provider, response size, and success state. A global p95 can move because traffic shifted toward a slower operation, not because the dependency regressed.
Latency also needs a validity signal. A provider may return an error-shaped object in 90 ms with status 200. That is fast, but it is not successful. Track duration, outcome, and response shape together.
Where the 10x rule does not apply
Local deterministic tools deserve tighter limits. An in-process calculator taking 500 ms is slow even if the model needed 100 ms to select it. A database query on the same private network may have a 75 ms p95 and a 250 ms hard timeout.
Long-running work should not receive a larger synchronous budget. Video generation, large exports, code execution, and human approval should become asynchronous jobs with progress, cancellation, and resumable state. A 90-second HTTP timeout is not a latency strategy.
The ratio also fails when the model itself is slow. A 5-second planning turn does not justify a 50-second tool timeout. Start with the user's end-to-end deadline, reserve the final response, map the call graph, and divide the remainder across the critical path.
ToolPulse can measure latency, failures, and response shape at each tool boundary. It cannot choose the deadline. The budget becomes defensible only when every tool timeout proves that the complete agent can still answer on time.