Agent Loop docs
agentloop.agent
The high-level assembly layer for running an agent.
An Agent is a configured conversation runner. It keeps together the parts that should stay
the same across calls:
- the model
clientand system prompt, - the tools the model may call,
- the application dependencies and approval policy used to execute those tools, and
- limits, observation, and tracing settings.
This is why Agent, tool, and Deps live in the same module. The tool decorator defines
a capability that can be offered to the model. Deps holds runtime-only state and policy
that the capability needs when it executes. Agent registers the tools and passes its deps
bundle to each tool call.
The model sees a tool's name, description, and JSON Schema, but it never sees Deps.
Values chosen by the model belong in the schema; application data belongs in Deps.
agentloop supports Anthropic, OpenAI, OpenRouter, and Modal/vLLM. This example uses OpenAI.
Install OpenAI support and set the provider credentials:
python -m pip install 'based-models-agentloop[openai-compat]'
export LLM_PROVIDER=openai
export OPENAI_API_KEY='your-key'
The model chooses a note name. The application supplies the notes through Deps:
import os
from agentloop import Agent, Deps, tool
class NoteDeps(Deps):
notes: dict[str, str]
@tool(
parameters={
"type": "object",
"properties": {"name": {"type": "string"}},
"required": ["name"],
"additionalProperties": False,
}
)
def read_note(name: str, *, deps: NoteDeps) -> str:
"""Read a note by name."""
return deps.notes.get(name, "Note not found.")
deps = NoteDeps(notes={"welcome": "Hello from the agent loop."})
with Agent.from_env(os.environ, tools=[read_note], deps=deps) as agent:
result = agent.run_result(
"Read the welcome note with read_note. Reply with its text only."
)
print(result.text)
print(result.summary.tool_calls)
Output:
Hello from the agent loop.
{'read_note': 1}
tool_calls confirms the agent ran the tool.
When to use Deps
Omit deps when the agent has no tools, or when every tool can do its work using only its
model-provided arguments. Agent supplies an empty default Deps bundle in that case.
Provide Deps when tools need either:
- shared application state or configuration, such as a tenant ID, repository, API client, database handle, or request context; or
- a
before_toolhook that approves or denies tool calls before execution.
The read_note example shows the two kinds of input:
nameis in the tool's JSON Schema. The model chooses it when callingread_note.notesis declared onNoteDepsand passed to the agent by the application. The model can ask for a note by name, but cannot supply or replace the notes dictionary.
Define a Deps subclass for the application values a tool needs. Add *, deps: NoteDeps
to a handler that reads those values, as read_note does. A handler that needs only its
model-chosen arguments can leave out deps. Keep those arguments in the JSON Schema so
the model knows what it can pass. For example, read_note lists name under properties
and required, so the model knows to supply a note name when it calls the tool.
The agent keeps the same deps object for every run and every tool call. For example, two
calls to agent.run_result(...) would use the same notes dictionary. If the notes differ
by request, create an agent with the appropriate NoteDeps for each request. If a tool
changes shared data, account for the fact that multiple tool calls can run concurrently;
the same applies to a custom before_tool approval hook.
Deps
Deps(*, before_tool=allow_all)
A frozen Pydantic model implementing ToolDeps. It is the default
runtime dependency bundle used by Agent.
before_tool is called before every tool execution. It receives the requested
ToolCallPart; return None to allow the call or a reason string to deny it. The default,
allow_all, permits every call. Subclass Deps to add typed fields needed by your tool
handlers.
tool
tool(parameters, *, name=None, description=None, timeout_seconds=None, parallel_safe=True)
Decorator that converts a callable into a ToolSpec.
parameters: required, handwritten JSON Schema for model-visible arguments.name: model-visible name; defaults to the function name.description: defaults to the first paragraph of the docstring. If neither exists, decoration raisesValueError.timeout_seconds: optional positive limit for a local tool call.parallel_safe: set toFalseto run local tool calls in a turn sequentially.- A keyword-only parameter named
depsreceives the configured dependency bundle and is hidden from the model-visible handler signature and JSON Schema. The same bundle is available to every tool registered with the agent, though a handler can omit the parameter when it does not need it.
Agent
Agent(
tools,
*,
client,
system=None,
deps=None,
observer=None,
name="agentloop",
trace=False,
max_tokens=16_000,
max_iterations=50,
deadline_seconds=600.0,
summary_options=None,
metrics=False,
)
Public attribute: name, the application-level agent name. Other constructor values are
kept as internal state.
client is borrowed: an agent constructed directly never closes it. tools accepts local
ToolSpec objects and provider-run HostedTool objects. Values produced by @tool can be
passed directly. Construction fails for duplicate local tool names, hosted tools the client
does not support, or a local tool whose name matches a hosted tool passed to the same agent
(for example, both named web_search).
deps is the one runtime dependency bundle passed to local tool handlers, defaulting to
Deps(). trace=True combines an OTelObserver with the supplied observer. metrics=True
adds a MetricsObserver. summary_options configures the optional timeline, repeated-call
detection, and limit warnings described in
Conversation summaries.
Agent.from_env
Agent.from_env(env, tools, *, http_client=None, **agent_options) -> Agent
Builds Settings, creates a retrying provider client, and returns an agent that owns that
client. env is an explicit mapping such as os.environ. The method does not load dotenv
files. observer and trace are connected to both retries and loop events.
http_client is passed to build_client: an httpx2.Client for
Anthropic, an httpx.Client for the other providers, or ConfigurationError. The agent's
close() closes the provider client it built but leaves your http_client open.
Agent.run
agent.run(
prompt,
*,
conversation_id=None,
correlation_id=None,
parent_id=None,
) -> str
Runs one new conversation and returns text from the final assistant message. Its transcript is discarded. The optional identifiers are copied to the conversation summary and trace.
Agent.run_result
Accepts the same arguments as run() and returns a ConversationResult containing the
final transcript, text, and summary.
Agent.converse
agent.converse(
transcript,
*,
conversation_id=None,
correlation_id=None,
parent_id=None,
) -> Transcript
Updates and returns the supplied transcript. The optional identifiers have the
same meaning as in run().
Agent.converse_result
Accepts the same arguments as converse() and returns a ConversationResult containing the
transcript, text, and summary.
Agent.close
agent.close() -> None
Closes only a client created by from_env(). It is safe to use Agent as a context manager;
__enter__() returns the agent and __exit__() calls close().
A traced Agent supports one active conversation at a time. Concurrent traced runs require
separate agent instances.