Agent¶
Agent is the high-level interface for running model interactions with optional tools, files, sessions, and structured output.
Responsibilities¶
- Detect provider from model name (
_detect_provider). - Load tools from multiple formats via
ToolLoader. - Transform media inputs with
MediaTransformer. - Route execution between function-calling and structured-output code paths.
- Normalize provider responses to
AgentResult. - Optionally track detailed per-run costs.
Initialization model¶
Primary constructor parameters include:
- Core:
model,instructions,max_iterations,max_tokens,temperature(sent only to models whose registry row lists it — see Sampling knobs).max_iterationsbounds the tool rounds of a run.result.errorreports "Maximum completed tool calling loop iterations (N) reached" when the rounds ran out before a finished answer: the model was still calling tools, or, on Gemini, its last turn was cut off (atmax_tokens, say) or came back empty or thought-only after a tool round, with no round left to ask again. A run that answers on its last allowed round succeeds, on every provider (Gemini fixed in 0.5.1) - Tooling:
tools,tool_choice,strict_tools(Anthropic; each request stays within Anthropic's per-request strict limits, see Strict tool schemas on Anthropic),built_in_tools,defer_tools(see Tool search and deferred tools),max_tool_result_chars(see Tool-result size cap),tool_result_preview_chars(default500: how much of each tool result thetool_call_resultstream and hook events carry;0= the whole result plusresult_data. It never changes what the model is sent; see Tool results in events),truncated_result_read_chars(defaultNone, off: how many more characters of a truncated tool result the model may read back with the built-inread_truncated_resulttool; see Letting the model read past the cut) - Reasoning:
thinking, plus provider-specific kwargs - Steering:
hooks— an awaited control channel that can veto a tool call, inject a message, pause, or abort (see Hooks and Steering);emit_request_start(defaultFalse) adds the opt-inrequest_startevent on both channels - Prompt-part policy:
on_unsupported_part—"drop"(default, unchanged behaviour) or"raise"(see Unsupported parts are now loud) - Pre-flight estimate:
estimate_input_tokens—"lazy"(the new default),TrueorFalse(see The pre-flight estimate no longer calls the provider) - Files/media:
code_execution_output_dir(default: a fresh per-run folder; a name the run already wrote is written asname (2).ext, a file an earlier run left there is overwritten — see Output files), per-runfiles,tool_files,media_resolution_list - Persistence:
media_policy,media_store,media_max_inline_bytes— what a saved session does with media and where externalised bytes live (see Media in a checkpoint) - Anthropic code execution container reuse (per-run or session-scoped):
anthropic_container_id,reuse_anthropic_container(session-only). See Built-in tools recipe for examples. - Session/cost:
auto_inject_memory_instructions,auto_inject_skills,detailed_costs,cost_output_dir - Context management:
compaction,compaction_config,session_history_config(see Compaction and Sessions) - Per-token cost levers:
prompt_caching(Anthropic opt-in — see Prompt Caching),service_tier("priority"or"flex"on OpenAI and Google,"ultrafast"ongpt-6-astra— see Service Tiers) - Transport:
openai_transport="websocket"runs an OpenAI agent's tool loop over one WebSocket instead of one HTTP request per step (opt-in, same results and billing — see OpenAI WebSocket Transport) - Anthropic thinking-prefix controls:
anthropic_block_binding,strict_prefix_binding,prefix_audit,prefix_strip_fallback(see Thinking) - Credentials:
api_key(optional per-Agent override of the provider's environment variable) - Logging:
logger,log_level,log_file, andlog_to_file(defaultTrue). With nologger=the Agent builds its own logger that also writes./logs/<logger-name>_<timestamp>.log;log_to_file=False— orDPT_AGENT_LOG_TO_FILE=0for the whole process — makes it console-only, never creates./logs/, and ignoreslog_file=with a warning. Alogger=you pass is used as-is either way. In a YAML config,logging.to_file: false(or alog_to_fileoverride toAgent.from_config) does the same. - Recipe:
to_config()returns the data half of the constructor as the YAML shapefrom_configreads (model, instructions, constraints, the tool and media policies, thinking, compaction, the history mode, logging, provider overrides, every tool given as data); Python callables, hooks, a custom media store, a logger and the api key are listed underunserialisable, never dropped silently. So are a pre-built{"session": ...}entry and the{"type": "fastmcp", ...}dict form, by name, since 0.5.1 (they hold a live object), and since 0.5.3 theenvvalues of anmcp_command/mcp_scriptentry (an API key may be among them): the entry is emitted withoutenvand the variable names go underunserialisable.tool_env; a path-likecwdis emitted as a string.session_history_configis a YAML key too. See Best practices.
Provider-specific keywords are forwarded as given — file_active_timeout_s,
file_poll_interval_s and file_poll_backoff reach the Google entry points this way, for example.
A keyword the target provider's function does not declare is dropped at request time, so check
Providers for the spelling. Construction now
catches the mistake early: a keyword that is neither an Agent parameter nor one the provider's
parameter mapping or entry points accept — or one the Agent supplies itself, such as
system_prompt — logs one WARNING naming every such keyword, with the Agent parameter to use
where there is an obvious one (system_prompt= → instructions=). It never raises, and the
keywords are still forwarded unchanged, so code that passes provider keywords deliberately keeps
working.
Reasoning in a schema field¶
When your output_schema has a field named internal_reasoning, thoughts or reasoning
(case-insensitive), every provider appends the model's reasoning text to that field of output,
after whatever the model wrote there, under a ***Internal Reasoning:*** heading. The same text is
always on result.reasoning_content, which is the canonical location — so such a run carries the
reasoning twice.
The merge stays the default for clients that read it from output.
DPT_MERGE_REASONING_INTO_SCHEMA=off stops
the copy on all four providers: the field then holds exactly what the model wrote, and the reasoning
is on reasoning_content only.
Scheduled change in 0.5
The merge is scheduled to be retired in 0.5: the default will become off, as a breaking change
with its own release note. Read reasoning from reasoning_content now, and set
DPT_MERGE_REASONING_INTO_SCHEMA=off to see today what your schema field will hold then.
Client lifecycle¶
By default an Agent borrows its provider client from a process-level cache, so many agents on the
same provider share one SDK client and one connection pool. await agent.close() disconnects local
MCP servers and drops the Agent's references, but deliberately leaves a shared client open — closing
it would break every other live Agent using it. The MCP server configs survive close(), so the
Agent reconnects them on its next run (see Tools).
Passing api_key=... changes that: the Agent builds its own client outside the shared cache, and
its close() closes that client. Use it when one process must talk to a provider under more than
one credential.
To close the shared clients at process exit, call doc_processing_toolkit.utils.shutdown_clients(),
or await shutdown_clients_async() from code already running on an event loop. Full details,
including event-loop affinity for async clients and the loop-teardown hook that makes an explicit
call rarely necessary, are in Async Clients.
The pre-flight estimate no longer calls the provider¶
Agent(estimate_input_tokens=...) controls how the pre-flight input-token figure is produced. That
figure is read by exactly one thing — the cost record a provider writes when a stream fails
mid-flight — and it used to be a call to the provider's own token-counting API before every
request. On Google that call uploads every media part to the Files API, polls it and deletes it
again, so a run carrying one image uploaded those bytes twice and the counted copy could never be
reused.
| Value | What happens |
|---|---|
"lazy" |
The new default. A purely local approximation: roughly four characters per token for text, plus a fixed figure per non-text part (agent.core.LAZY_MEDIA_TOKEN_ESTIMATES). No API call, and on Google no Files API upload. |
True |
The previous behaviour verbatim — the provider's exact count, upload included. |
False |
No estimate at all; the figure is 0, which is what the old code already did whenever the count raised. |
This is a default change
On "lazy" the failure-path cost estimate becomes a local approximation instead of an exact
provider count. It feeds a record that was already an estimate and never a billed total, and the
media figures are deliberately coarse order-of-magnitude numbers rather than vendor-published
ones. Pass estimate_input_tokens=True if you want the old exactness back on that one path.
Anything other than True, False or "lazy" raises at construction
(agent.core.ESTIMATE_INPUT_TOKENS_MODES is the vocabulary).
Google Files API uploads on the request path are reused as well: identical bytes sent under the same
credential upload once and are reused while the handle is ACTIVE and not about to expire. Reuse is
keyed on credential and content and MIME type, so a handle can never cross API keys, and one
the cleanup path deletes is dropped from the cache immediately
(providers.google.GOOGLE_UPLOAD_REUSE_MIN_TTL_S, 60 seconds of remaining lifetime).
A run's cleanup deletes only the handles that run uploaded itself. A handle it reused belongs to
whoever uploaded it and stays in the cache. Before this release a no-tools run that reused a handle
an AgentSession had uploaded on an earlier turn deleted it on the way out, and the session's stored
file_uri then failed on every later turn (403 / "File is not in an ACTIVE state"). The cache is
also bounded: at most providers.google.GOOGLE_UPLOAD_CACHE_MAX_ENTRIES (256) entries, evicting the
least recently used, so a long-lived process such as an MCP server or a worker no longer grows it for
its whole life. There is no age-based sweep: a cache hit is re-validated with the Files API before it
is reused, which is what catches a handle Google has already expired.
Run loop behavior¶
run(...)is async and is the canonical execution path.run_sync(...)wraps the async path and handles nested event loop scenarios; MCP servers are connected and released inside the loop it creates.- Local MCP, client-side remote MCP, and local tools route to function-calling APIs.
- Provider-native remote MCP-only setups are handled provider-natively via mapped params.
- MCP sessions are opened once per event loop and reused across runs and session turns, so an Agent with local or client-side remote MCP tools serves many runs.
Typical run call:
result = await agent.run(
"Summarize this document",
files=["./sample.pdf"],
output_schema=MySchema,
)
Result fields commonly used:
outputtools_usedbuilt_in_tools_used— the built-in tools configured on the request (a config echo, not proof of use) /built_in_tools_invoked— the subset with observed evidence they actually ran. A provider-executed remote MCP call adds the neutral"remote_mcp"on every provider that has one (Anthropic also keeps the MCP tool's own name beside it), so"remote_mcp" in result.built_in_tools_invokedis one check everywhereinput_tokens/output_tokens/total_tokens(their sum)reasoning_tokens/reasoning_content— the thinking share ofoutput_tokens, and the thinking text or summary, on every provider and every run shape.reasoning_contentis the canonical place to read reasoning; see Reasoning in a schema fielditeration_metrics— one entry per provider request, each with apurpose(see Cost Tracking)cached_input_tokens/cache_write_tokens— prompt-cache accountingrequest_cost_records— per-request cost breakdown for the runtier_downgraded— some request in the run was not served at the tier it asked for;service_tier_served/priority_request_countgive the run-level answerprefix_binding_mismatches/model_binding_mismatches/prefix_binding_retry— replayed Anthropic thinking blocks the API dropped, and whether a binding 400 was recovered from;0/0/Falseeverywhere elsedistinct_urls— grounding URLs, now populated on the Google tool-calling path too; on OpenAI the URLs each web search visited as well as the ones the answer cites, so JSON and structured answers carry them too (details)dropped_parts— every prompt part this run's model could not accept, withkind,index,reason,providerandmodel; populated under bothon_unsupported_partpolicies. A restored session reports the same onsession.dropped_partsfor the history it loadedmedia_uploads— every Google Files API upload this run made (part_index,uri,name,mime_type,size,sha256,uploaded_at,expiration_time);[]elsewheredeferred_tools— the client tool names actually hidden behind the provider's tool search this run, in wire order;[]whenever deferral did not applycost/cost_estimatedtool_reported_cost/tool_reported_costs— spend the run's tools reported through the_dpt_spendmarker, already incost(see Cost Tracking)retry_count/retry_cost/retry_input_tokens/retry_output_tokens— spend on failed-and-retried attempts, already included in the totals aboveerror
A stream the caller closes before its complete event returns no AgentResult. Its spend is kept
on the Agent instead: agent.last_abandoned_stream holds that run's result dict with
"abandoned": True, and is reset to None when the next stream starts (see
Stopping a stream early).
Builder pattern¶
AgentBuilder provides a fluent interface for constructing agents step-by-step:
agent = (
Agent.builder()
.model("claude-sonnet-4-5")
.instructions("You are helpful.")
.add_tool(my_tool)
.max_iterations(10)
.build()
)
This is equivalent to passing all parameters directly to the Agent constructor but can be more readable for complex configurations.
New/advanced features reflected in code¶
- Flexible tool orchestration across local tools, local MCP sessions, and remote MCP configs.
- Session-aware execution with multi-turn memory and metadata accumulation.
- Optional structured-output mode with schema-validated responses.
- Optional detailed per-run telemetry for cost, tokens, iterations, and tool usage.
- Provider-aware parameter mapping that keeps one high-level Agent API.