Skip to content

Agents

Auto-generated from source docstrings (mkdocstrings). The complete public contract is agentmaker.__all__ plus each subpackage __all__.

agentmaker.agents: Agent base + execution primitive + the strategies.

BaseAgent (base.py): the unified run / resume template (guardrails + persistence + HITL resume) plus the three sub-agent delegation methods. Single-loop Agent (agent.py): the one "model calls tools in a loop" execution primitive (native function-calling); the spec menu's "chat" maps to it and "react" is its preset. Orchestration recipes (workflows subpackage): PlanAgent / ReflectionAgent, strategies whose control flow is in code, driving an internal Agent for each step's work. Multi-agent orchestration (multi_agent subpackage): AgentTool, wrapping one Agent as a Tool (orchestrator-worker).

Agent is the single-loop class (agent.py); to write a new strategy, inherit BaseAgent (base.py).

BaseAgent

Shared base for all agents. Subclasses must implement at least one of _arun (async-native) or _run (synchronous).

__init__(name, llm, system_prompt=None, *, session_store=None, scope=None, checkpoint_store=None, input_guardrails=None, output_guardrails=None, hooks=None, run_policy=None, harness_config=None, prompts=None, on_pending='error', as_child=False)

Store the agent's core dependencies and initialize conversation history (loading and resuming from session_store if one is attached).

Parameters:

Name Type Description Default
name str

Agent name, used for identification / debugging.

required
llm LLMClient

LLM client the agent uses to call the model.

required
system_prompt Optional[str]

System prompt defining the agent's persona and behavior; optional.

None
session_store Optional[SessionStore]

Optional session persistence store. When attached, history is persisted per scope and loaded per scope at each run to resume (the store is the single source of truth and supports cross-process access), so a long-running daemon does not lose conversations on restart. When absent, history is per-scope and in-process (lost on restart).

None
scope Optional[Scope]

The default scope the session belongs to (identifies session / user via retrieval.Scope); run / resume can override it per call, so one Agent instance can serve multiple sessions (history is isolated per scope). Defaults to an empty scope.

None
checkpoint_store Optional[CheckpointStore]

Optional execution-state checkpoint store (CheckpointStore, agentmaker.runtime.execution). Attaching one enables execution-state persistence, shared across three use cases: (1) HITL async approval (high-risk tools suspend, run returns an Interrupt, resume(decision) continues); (2) crash recovery / (3) long-task resume (a checkpoint is saved each step, resume() continues after a process restart). Strategies that support resume implement _adrive.

None
input_guardrails Optional[list[Guardrail]]

Optional list of input guardrails. Checked against user input before run; a tripped guardrail raises GuardrailTripwireError.

None
output_guardrails Optional[list[Guardrail]]

Optional list of output guardrails. Checked against the final output before persisting history; a trip raises (the blocked turn is not recorded). The rules are app business logic.

None
hooks Optional[list[Hook]]

Optional list of lifecycle hooks (Hook, agentmaker.runtime.hooks); observe-only. Run-level events (run start / end, guardrail, suspend, error, etc.) are fired by this base; model/tool-level events (calling the model / executing tools) are fired by each strategy's Harness. Zero overhead when none are attached.

None
run_policy Optional[RunPolicy]

Optional run governance (RunPolicy, agentmaker.runtime.execution). When attached, a single run is bounded by wall-time / LLM-call count / tool-call count / token limits plus cooperative cancellation (gated at each call site in the Harness); exceeding a limit / cancellation raises RunLimitExceeded / RunCancelled to abort the turn. Counted independently per run (resume is a new turn). Unbounded when absent. Warning: nested runs (delegated via AgentTool, or a Plan / Reflection sub-executor) inherit the outermost policy's global counters; a run_policy attached to a sub-agent does not take effect within the parent run (a warning is emitted). To bound sub-tasks, set the global limits on the parent Agent's run_policy.

None
prompts

Optional prompt registry (PromptRegistry). If not passed, a copy of DEFAULT_PROMPTS is made at construction (isolated per instance, so update_prompts only affects this agent and its harness / sub-agents, without mutating the global registry / other agents).

None
on_pending str

Policy for running again when the same scope already has a pending-approval checkpoint (requires checkpoint_store): "error" (default) raises SessionError to prevent a new run silently overwriting the pending action (making the approval request vanish); "discard" drops the old pending action and continues the new run.

'error'
as_child bool

Set True when acting as an internal sub-agent of an orchestration strategy (Plan / Reflection, etc.). It declares two semantics at once: (1) run-level hooks do not fire at the child layer (the parent strategy fires them once, avoiding repetition on every sub-step); the passed hooks are still forwarded via _harness_hooks to the inner Harness to observe model/tool-level events; (2) checkpoints are not self-cleared (_defer_checkpoint_clear); the parent strategy clears them explicitly via clear_checkpoint after committing its own progress, guaranteeing "parent commits before child cleanup" and eliminating the unrecoverable state where the parent is awaiting but the child's checkpoint has been cleared.

False

__init_subclass__(**kwargs)

Definition-time check: a subclass (including multi-level inheritance) must override at least one of _run or _arun (resolved via MRO, so intermediate bases are unconstrained).

arun(input_text, *, scope=None, trace_carrier=None, **kwargs) async

Process one input and return a reply (template method: the run boundary shared by all strategies): input guardrails -> strategy logic _arun -> output guardrails -> persist history.

Subclasses only implement _arun (pure generation); this method funnels the "run boundary" cross-cutting concerns (guardrails and history persistence) so every strategy gets them for free, mirroring the OpenAI Agents SDK where guardrails are declared on the agent and enforced by a single execution layer (rather than reimplemented in each strategy). The return type matches _arun (usually str; a validated model instance when output_schema is passed; an Interrupt on HITL suspend, in which case history is not persisted, the state has already been saved into the CheckpointStore by the strategy, awaiting resume).

Parameters:

Name Type Description Default
input_text str

The user's raw input text.

required
scope

The session identifier for this call; defaults to self.scope. History is loaded / saved by it (one instance can serve multiple sessions).

None
trace_carrier

Optional upstream W3C trace carrier (e.g. {"traceparent": request-header}). Useful only when an OTelExporter(carrier_provider=...) is attached, letting this run's spans join the app's cross-service trace. When absent, spans are attributed by the OTel current context (no regression).

None
**kwargs

Passed through to _arun (e.g. verbose, output_schema, temperature).

{}

run(input_text, *, scope=None, trace_carrier=None, **kwargs)

Synchronous facade over arun (driven by aio.run_sync). In environments with a running event loop (async / Jupyter / FastAPI), await arun instead.

aresume(decision=None, *, scope=None, trace_carrier=None, **kwargs) async

Resume an unfinished run from a checkpoint: reload the execution state -> (for HITL) inject the decision -> continue from the breakpoint.

Two kinds of resume share this entry: - HITL: decision is a bool. Approve (True) executes the suspended high-risk action / reject (False) skips it and feeds the result back so the model reroutes. - Crash recovery / long-task resume: decision is omitted (None). No decision is injected; it simply continues from the last checkpoint (a suspended high-risk action re-suspends and returns an Interrupt awaiting approval again, rather than being mistaken for a rejection).

Funneled in the base: reload ExecutionState -> (as needed) inject the decision -> call the strategy's _adrive to continue -> teardown (on completion: output guardrails + persist history + clear checkpoint; on re-suspend: return a new Interrupt). Input guardrails are not re-checked (the first run already checked them); it does not go through _scaffold, so resume continues the run_id / step from before the suspend and does not re-fire on_run_start.

Parameters:

Name Type Description Default
decision Optional[bool]

The HITL decision. A bool (True approve / False reject) decides all pending actions of this turn uniformly; a dict {call_id: bool} decides per call_id (used when one suspend has multiple pending actions, see Interrupt.pendings); None is a crash-recovery resume (no decision injected).

None
scope

The session identifier; defaults to self.scope.

None
trace_carrier

Optional upstream W3C trace carrier (the traceparent of this resume request); not persisted with the checkpoint, passed by the app on each resume.

None
**kwargs

Passed through to the strategy's _adrive.

{}

Returns:

Type Description

str (the final reply, and history persisted) or Interrupt (a high-risk action is still

awaiting approval during resume, re-suspended).

resume(decision=None, *, scope=None, trace_carrier=None, **kwargs)

Synchronous facade over aresume (driven by aio.run_sync).

clear_checkpoint(scope=None) async

Explicitly clear a scope's checkpoint (ignoring the defer flag), and cascade-clear the internal sub-agents' derived child-scope checkpoints.

For a parent strategy to clean up sub-agent checkpoints after committing its own progress, and for a user to manually clear a stuck suspended task: cascading ensures it does not clear only the parent and leave child checkpoints orphaned (the next sub-agent arun / delegation hitting _guard_pending permanently jams). A sub-agent without a checkpoint_store is a no-op each.

Parameters:

Name Type Description Default
scope

The session identifier to clear; defaults to self.scope.

None

add_message(message, scope=None) async

Append one message to the conversation history by scope (a single-message convenience over add_messages).

Parameters:

Name Type Description Default
message Message

The message to append.

required
scope

The session identifier; defaults to self.scope.

None

add_messages(messages, scope=None) async

Append multiple messages to the conversation history by scope atomically; with a session_store it goes through its aappend_many (single transaction, avoiding a half-turn), otherwise the in-process dict[scope] is extended once. This is the single funnel point for session persistence (the async real body, using a* so as not to block the event loop).

Parameters:

Name Type Description Default
messages list[Message]

The list of messages to append.

required
scope

The session identifier; defaults to self.scope.

None

clear_history(scope=None)

Clear a scope's conversation history; with a session_store, clear its persisted record, otherwise clear the in-process dict[scope].

Parameters:

Name Type Description Default
scope

The session identifier; defaults to self.scope.

None

get_history(scope=None)

Return a deep copy of a scope's conversation history, so external mutations (including changing a Message's content / metadata) do not affect internal state.

Parameters:

Name Type Description Default
scope

The session identifier; defaults to self.scope.

None

Returns:

Type Description
list[Message]

list[Message]: A deep copy of the history message list (Message is a mutable dataclass, so

list[Message]

a shallow copy would not prevent field mutation).

The synchronous public facade (funneling to the async real body _history_for via run_sync): in environments with a running event loop (async / Jupyter / FastAPI), use await agent._history_for(scope) instead (same limitation as the run/resume synchronous facades).

get_prompts()

List all built-in prompts this agent currently uses, as {key: text} (from the shared self.prompts).

For inspection / export: first see which prompts the framework builds in, what their keys are called, and what they look like, then decide which to override (see agentmaker/doc/prompts.md).

update_prompts(updates)

Override built-in prompts: updates may be {key: new-text} (per key) or another PromptRegistry (a whole swap, e.g. changing language).

Modifies self.prompts in place: this agent's harness and internal sub-agents share the same copy, so one change takes effect across the whole chain. Each override is validated to ensure "the placeholders + protocol tokens are still present", raising PromptError if any is missing. It is isolated per instance by default (a copy of DEFAULT_PROMPTS is made at construction when prompts= is not passed), so this method only changes this agent's chain and does not affect other agents / independently constructed tools / the process-wide global. To change language / wording process-wide, explicitly call DEFAULT_PROMPTS.override(pack) before creating any component.

Boundaries: (1) an already-created tool's overall description is a construction-time snapshot and does not change with this override (the parameter descriptions do change live), so changing language / tool-related prompts should be done before creating tools (use packs' chinese_registry(), or override first then build tools), otherwise the same tool's schema shows a half-mixed state of "overall intro in the old language, parameter descriptions in the new". (2) After the default isolation, a tool constructed separately (not passed the same prompts) has its wording determined by the tool's own prompts and does not change with this agent's update_prompts: to keep the tool and agent in sync, pass the same PromptRegistry to both, or use a global override.

__str__()

Let print(agent) show "Agent(name, provider)" for debugging.

Returns:

Name Type Description
str str

Of the form "Agent(name=assistant, provider=deepseek)".

Agent

Bases: BaseAgent

Single-loop Agent: one input -> model-tool loop -> reply; with no tools it is plain question-answering. Automatically maintains multi-turn history per scope.

__init__(name, llm, system_prompt=None, *, tools=None, tool_registry=None, max_turns=10, compactor=None, confirm=None, permissions=None, reducer=None, tool_retriever=None, context_builder=None, sources=None, window_budget=None, token_counter=count_tokens, tracer=None, hooks=None, run_policy=None, session_store=None, scope=None, checkpoint_store=None, input_guardrails=None, output_guardrails=None, prompts=None, on_pending='error', as_child=False)

Parameters:

Name Type Description Default
name str

Agent name.

required
llm LLMClient

LLM client.

required
system_prompt Optional[str]

System prompt defining the persona; optional (if omitted, no system message is sent).

None
tools

Convenience entry point: a list[Tool] (including @tool-decorated function objects) or a ToolRegistry, normalized into a registry internally via ToolRegistry.from_tools. Use it for a one-line start in simple cases; mutually exclusive with tool_registry (passing both raises ValueError).

None
tool_registry Optional[ToolRegistry]

Tool registry (advanced case: reuse the same registry or customize prompts). Passing it enables the tool loop; omitting it means plain question-answering.

None
max_turns int

The maximum number of model turns in the loop, to prevent repeated calls from spinning into an infinite loop (must be a positive integer, default 10, since tool tasks often need multiple turns).

10
compactor Optional[HistoryCompactor]

Optional history compactor (HistoryCompactor); once attached, cross-turn session history is automatically summarized and compacted when it exceeds a threshold.

None
confirm Optional[ConfirmCallback]

Confirmation callback for high-risk tools (tool, params) -> bool; if omitted, it safely refuses (returns a readable error fed back to the model, without blocking on stdin). In CLI / teaching scenarios pass agentmaker.runtime.cli_confirm explicitly for command-line y/n; in server scenarios configure HITL (checkpoint_store).

None
permissions

Optional tool permissions (ToolPermissions, allow/deny lists); denied tools are rejected directly at the execution gate.

None
reducer Optional[ReducerConfig]

Optional trajectory-trimming knob (ReducerConfig); when this turn's tool trajectory overflows the window, keep recent units and summarize the earlier ones.

None
tool_retriever Optional[ToolRetriever]

Optional Tool-RAG (ToolRetriever); selects only the relevant subset of tools for this turn's input (saves tokens when there are many tools).

None
context_builder + sources

Optional memory/RAG injection; each turn retrieves by input and assembles a guardrailed system block for injection.

required
window_budget

Optional window-budgeting knob (WindowBudgetConfig); unifies budgeting across retrieval blocks, trajectory, and output reservation.

None
token_counter TokenCounter

Pluggable token counter (default count_tokens); the harness uses it to estimate tool overhead and to trim trajectories. For consistent accounting throughout, pass the same token_counter to your ContextBuilder / HistoryCompactor as well (the Agent does not silently override the counters they already carry).

count_tokens
session_store / scope

Session persistence (see BaseAgent); once attached, history is resumed per scope and persisted automatically.

required
checkpoint_store

Optional checkpoint store; once attached, enables HITL suspend/resume plus crash recovery (saved at every step).

None
input_guardrails / output_guardrails / hooks / run_policy / prompts / on_pending

see BaseAgent.

required
as_child bool

Set True when acting as an orchestration recipe's internal sub-agent (see BaseAgent: run-level hooks do not fire and checkpoint cleanup is left to the parent).

False

astream_run(input_text, *, scope=None, buffer_output=False, trace_carrier=None, **kwargs) async

Streaming question-answering (the real async implementation): yields reply text piece by piece.

Shares the base _scaffold (run context with scope + run-level hooks + exception routing). The output-side duties run inside the generator body only after the stream drains naturally: output guardrails -> one atomic history save -> on_run_end. If the consumer breaks early (break / aclose), none of the three happen (GeneratorExit is a BaseException and does not reach on_error), but the harness-level finally accounting always runs.

Tool loop: when the agent has registered tools, this runs the same model-tool loop as arun but with every model turn streamed -- text deltas are yielded as they arrive (including text preceding a tool call, matching common chat UIs), tools execute between turns (before_tool/after_tool hooks fire as usual), and the final no-tool-call turn's text is the answer. Unlike arun, the streaming loop does NOT support HITL suspend/resume or checkpoints: a tool that requires confirmation uses its synchronous confirm (ApprovalRequired propagates as an error in persistent setups) -- use arun when you need suspend semantics. History save matches arun: one atomic user + final-assistant turn (tool trajectories are not persisted to session history).

Output guardrail vs. streaming trade-off (buffer_output): - Default buffer_output=False: yield as it generates, check the guardrail on the final answer only after the stream drains. Content already emitted cannot be recalled (a tripped guardrail can only raise after the fact, it cannot stop text that already reached the consumer). - buffer_output=True: accumulate the full output first, run the output guardrail, and only after it passes release it piece by piece. With a tool loop this buffers ALL streamed text across turns and releases it only after the final answer passes the guardrail (tools still execute live in between).

Parameters:

Name Type Description Default
input_text str

User input (str, or a multimodal content-part list).

required
scope

This session's identifier; defaults to self.scope.

None
buffer_output bool

See above; default False (immediate streaming, guardrail checked after the fact).

False
trace_carrier

Optional upstream W3C trace carrier (see the same-named arg on arun); only useful when an OTelExporter(carrier_provider=...) is attached.

None
**kwargs

Passed through to the LLM streaming interface.

{}

stream_run(input_text, *, scope=None, **kwargs)

Synchronous facade for astream_run (pumped via aio.iter_sync): returns a sync generator that yields piece by piece. Drain it explicitly or close it (e.g. contextlib.closing); in an async environment use astream_run instead.

RunResult dataclass

Unified return envelope for run / arun / resume / aresume: bundles final output + whether a human-approval interrupt occurred + usage + new messages this turn into one object (rather than returning a bare string), turning a suspension from a silent accident into an explicitly checkable state.

Consumption template

r = agent.run("...") if r.interrupted: handle(r.interrupt) # HITL: take the suspended state to resume else: use(r.final_output) # completed: take the final output (str or structured instance)

Attributes:

Name Type Description
final_output Any

the completed state's final output (str, or a structured instance when output_schema was passed); None when suspended.

status RunStatus

"completed" or "interrupted".

interrupt Optional[Interrupt]

the suspended state's Interrupt (the pending action + the resume scope); None when completed.

usage RunUsage

usage snapshot of this run (RunUsage).

new_messages tuple[Message, ...]

messages newly added and stored in history this turn (user + assistant); empty when suspended.

run_id Optional[str]

this run's trace correlation id.

interrupted property

Whether this is a HITL suspended state (status == "interrupted").

__str__()

Let print(result) / f"{result}" show the final output text directly (eases migration: old code that used it as a string still gets the answer); a suspended state shows a readable note rather than a bare None.

RunUsage dataclass

Usage snapshot of a single run (for cost accounting / limit observability).

Attributes:

Name Type Description
llm_calls int

total LLM calls accumulated over this run.

tool_calls int

tool calls actually executed during this run.

total_tokens int

total tokens accumulated over this run (sum of usage.total_tokens across each LLM response).

PlanAgent

Bases: BaseAgent

Plan-and-Solve orchestration recipe: plan (structured step breakdown) -> delegate step-by-step execution to an internal Agent -> synthesize the answer.

__init__(name, llm, system_prompt=None, *, tool_registry=None, max_turns=3, confirm=None, tracer=None, permissions=None, hooks=None, run_policy=None, session_store=None, scope=None, checkpoint_store=None, input_guardrails=None, output_guardrails=None, tool_retriever=None, context_builder=None, sources=None, reducer=None, window_budget=None, token_counter=count_tokens, prompts=None)

Parameters:

Name Type Description Default
system_prompt Optional[str]

Optional extra persona for the planning stage (third positional arg, aligned with Agent / ReflectionAgent).

None
tool_registry Optional[ToolRegistry]

If passed, every execution step can call tools; without it, execution is pure reasoning (keyword-only).

None
max_turns int

Upper bound on the tool-loop turns for each sub-step executor (the internal single-loop Agent), default 3 (not the number of plan steps).

3
tool_retriever / context_builder / sources

Context engineering, passed through to each step's executor; the Plan's own planning and synthesis calls also inject a memory/RAG block.

required
confirm / permissions / checkpoint_store

Passed through to the executor; with a checkpoint_store attached, high-risk tools during a step suspend/resume via HITL (the Plan main loop catches and propagates the Interrupt upward).

required
reducer / window_budget

Window-governance knobs, given to Plan's own Harness (when history overflows the window it is trimmed against the window budget) and also passed through to each step's executor (whose tool-trajectory trimming is likewise bound by these two knobs).

required

ReflectionAgent

Bases: BaseAgent

Reflection orchestration recipe: draft -> (reflect -> refine) xN, until the critique signals "best reached" or max_turns is hit. Optionally verifies with tools in the critique step.

__init__(name, llm, system_prompt=None, *, max_turns=3, tracer=None, hooks=None, run_policy=None, session_store=None, scope=None, input_guardrails=None, output_guardrails=None, tool_registry=None, confirm=None, permissions=None, checkpoint_store=None, tool_retriever=None, context_builder=None, sources=None, reducer=None, window_budget=None, token_counter=count_tokens, prompts=None)

Parameters:

Name Type Description Default
max_turns int

Maximum number of "reflect-refine" rounds (must be a positive integer; the same name across Agent / PlanAgent / AgentSpec denotes the loop cap).

3
tool_registry Optional[ToolRegistry]

Optional tool table; if passed, the critique step can call tools to verify facts / arithmetic. Without it, critique is pure self-reflection.

None
confirm / permissions / checkpoint_store

Passed through to the critique-executor; with a checkpoint_store attached, critique's high-risk tools suspend/resume via HITL (the Reflection main loop propagates the Interrupt upward).

required
tool_retriever

Passed through to the critique-executor (selects a relevant subset when critique has many tools).

None
context_builder + sources

memory/RAG injection; splices the retrieval block into the prompt in the draft / refine steps (the generation step benefits most).

required
reducer / window_budget

Window-governance knobs, given to Reflection's own Harness (when the reflection trajectory overflows the window it is trimmed against the window budget, keeping the latest answer plus the critique's key points) and also passed through to the critique-executor (whose verification trajectory is likewise bound).

required

AgentSpec dataclass

Declaratively aggregates every configurable point of an agent; strategy picks the paradigm, build_agent constructs from it.

The fields are a superset across strategies: common fields (including the memory/RAG injection context_builder+sources and the Tool-RAG tool_retriever) apply to the relevant strategies, while strategy-specific fields (compactor is chat-only, the single-loop Agent) only take effect on strategies that support them. Setting one on an unsupported strategy is rejected by build_agent (fail loud).

AgentTool

Bases: Tool

Adapt an agent into a Tool: the main agent calls it to delegate one subtask and gets the result back (the main agent keeps control).

__init__(agent, *, name=None, description=None, scope=None, prompts=None)

Parameters:

Name Type Description Default
agent BaseAgent

The wrapped sub-agent (any strategy).

required
name Optional[str]

Tool name, defaults to agent.name; set it explicitly to disambiguate sub-agents that share a name.

None
description Optional[str]

Tool description telling the orchestrating LLM what this sub-agent is good at and when to delegate to it; defaults to a value generated from agent.name.

None
scope Optional[Scope]

The session ownership used when delegating to the sub-agent (advanced override: pin the sub-agent to a fixed scope). Defaults to None, in which case the parent agent's current run scope is picked up automatically (propagated through the run context, see _effective_scope), so that even when "one AgentTool instance serves multiple parent sessions" the sub-agent's history / memory stays isolated per parent session and never bleeds across them; if the parent has no run context (rare, calling the tool bare) it falls back to the sub-agent's own default scope. Pass this explicitly only when you genuinely need to pin the sub-agent to a specific scope.

None

get_parameters()

Declare parameters: a single task string (the self-contained subtask handed to the sub-agent).

run(parameters)

Synchronous delegation: hand the task to the sub-agent, run it, and return its reply (normalized into a ToolResponse via _textualize).

arun(parameters) async

Native async delegation: await the sub-agent's arun (a truly async strategy runs async, otherwise its arun auto-dispatches to a thread pool).

build_agent(spec)

Construct the matching strategy agent based on spec.strategy.

str -> "provider:model" (e.g. "deepseek:deepseek-v4-flash", split into

LLMClient(provider, model=right_half)); a bare provider name (no colon, e.g. "deepseek") is still treated as a provider (using the .env key + the provider's default model); an LLMClient instance -> used directly (lets you pin model / key / base_url); None -> LLMClient() default.

tools: list[Tool] -> build a new ToolRegistry and register them / ToolRegistry -> used as-is / None -> no tools. A field unsupported by the strategy that is set to a non-default value raises ValueError (avoids the silent "set but had no effect" trap).

Returns:

Name Type Description
BaseAgent BaseAgent

the constructed strategy instance (single-loop Agent (chat/react) / ReflectionAgent / PlanAgent).