Introduction
When building agents, you will often find yourself repeating the same configuration: setting up an LLM provider, registering tools, attaching guard hooks, and wiring a message compiler. TheAgentBuilder class provides a clean composition layer that lets you assemble fully configured AgentLoop instances from reusable, self-contained capabilities.
Instead of manually constructing each dependency and passing them to AgentLoop, you describe what your agent should be able to do by stacking capabilities. Each capability encapsulates a single concern — configuring the LLM provider, adding a tool, attaching a lifecycle hook, or enabling subagent delegation. The builder composes them all into a working agent in a single build() call.
This approach makes agent configuration declarative, testable, and easy to share across your application.
Quick Start
The following example creates an agent that can execute bash commands, uses the Anthropic provider, and enforces step and token limits:AgentBuilder::base() factory creates a builder pre-configured with sensible defaults: a ToolCallingDriver backed by the default LLM provider, the ConversationWithCurrentToolTrace message compiler, and an empty hook stack. Every withCapability() call returns a new builder instance — the builder is immutable, so you can safely branch configurations from a shared base.
Immutability
AgentBuilder is a final readonly class. Every withCapability() call returns a new builder instance with the capability appended, leaving the original unchanged. This means you can safely branch from a shared base without worrying about mutation side effects:
The Build Pipeline
When you callbuild(), the builder delegates to an internal AgentConfigurator that resolves all components in a specific order:
- Message compiler — determines how
AgentStatemessages are compiled into the LLM prompt. The default isConversationWithCurrentToolTrace, which includes all non-trace messages plus the current execution’s tool traces. - Tool-use driver — the driver responsible for calling the LLM and parsing tool calls from the response. The default is
ToolCallingDriver. The resolved message compiler is injected into the driver at this stage if the driver implementsCanAcceptMessageCompiler. - Concrete tools — all tools registered via capabilities, including deferred tools that need access to the finalized driver or event system. Deferred tools are resolved last because they may depend on the final driver and tool set.
- Interceptor — the hook stack is compiled into an interceptor that wraps every lifecycle phase of the agent loop. If no hooks have been registered, a lightweight
PassThroughInterceptoris used instead.
UseToolFactory, UseSubagents, or UsePlanningSubagent) receive the finalized driver, tool set, and event handler at resolution time. If you need a tool that references the driver, use UseToolFactory rather than UseTools.
Core Capabilities
Core capabilities modify fundamental aspects of the agent’s behavior: which LLM it talks to, how it handles tool calls, what guards protect execution, and how the conversation context is compiled.UseLLMConfig
Configures the LLM provider and creates aToolCallingDriver for the agent. If omitted, the builder uses the default provider from LLMProvider::new().
| Parameter | Type | Default | Description |
|---|---|---|---|
llm | LLMProvider|null | null (uses default) | The LLM provider to use |
maxRetries | int | 1 | Maximum inference attempts on transient failure |
maxRetries is greater than 1, an InferenceRetryPolicy is created and passed to the driver, enabling automatic retries on transient LLM errors.
UseGuards
Installs safety guards that stop execution when resource limits are reached. All parameters are optional; set a parameter tonull to disable that specific guard entirely.
| Parameter | Type | Default | Description |
|---|---|---|---|
maxSteps | int|null | 20 | Maximum number of steps before stopping |
maxTokens | int|null | 32768 | Total token budget across all steps |
maxExecutionTime | float|null | 300.0 | Maximum wall-clock seconds |
finishReasons | array | [] | Stop when the LLM returns one of these finish reasons |
BeforeStep with priority 200 (early in the lifecycle). The finish-reason guard runs at AfterStep with priority -200 (late in the lifecycle, after the LLM response has been processed).
UseTools
Adds one or more tool instances to the agent. Tools are merged with any previously registered tools, never replaced.UseTools multiple times across different capabilities. Each invocation merges additional tools into the existing set.
UseHook
Attaches a single hook to the agent’s lifecycle. Hooks intercept execution at defined trigger points and can modify the agent state or halt execution entirely.| Parameter | Type | Default | Description |
|---|---|---|---|
hook | HookInterface | (required) | The hook implementation |
triggers | HookTriggers | (required) | When the hook fires (e.g., beforeStep(), afterStep(), beforeExecution()) |
priority | int | 0 | Higher priority hooks run earlier within the same trigger phase |
name | string|null | null | Optional name for debugging and logging |
UseDriver
Replaces the default tool-use driver entirely. Use this when you need a completely custom driver implementation rather than the defaultToolCallingDriver.
UseDriverDecorator
Wraps the current driver with a decorator function. The decorator receives the existing driver and must return a newCanUseTools implementation. This is useful for adding cross-cutting concerns like logging, caching, or rate limiting around the driver without replacing it.
UseContextCompiler
Replaces the message compiler that prepares the conversation history for the LLM. The default compiler isConversationWithCurrentToolTrace.
UseContextCompilerDecorator
Wraps the current message compiler with a decorator. This is the recommended approach for adding token-limit trimming, context windowing, or injecting additional context without replacing the entire compilation pipeline.UseContextConfig
Sets a system prompt and optional response format that are injected before each step via aBeforeStep hook at priority 100. The system prompt is always present regardless of how messages are compiled.
string system prompt and a ResponseFormat object are accepted. If both are empty, the capability is a no-op.
UseReActConfig
Replaces the driver with aReActDriver that implements the Reasoning and Acting (ReAct) pattern. The agent alternates between explicit thinking steps and tool-use actions, producing structured reasoning traces that make the decision process transparent.
UseToolFactory
Registers a deferred tool factory. The factory callback is not invoked immediately — it runs duringbuild() after the driver and tool set have been finalized. This gives the factory access to the complete agent context.
Tools collection, the finalized CanUseTools driver, and the CanHandleEvents event dispatcher. It must return a single ToolInterface instance.
Domain Capabilities
Domain capabilities bundle tools, hooks, and configuration for specific workflows. They are built on top of the core capabilities and provide higher-level abstractions.| Capability | Location | Description |
|---|---|---|
UseBash | Capability\Bash | Adds a bash command execution tool |
UseFileTools | Capability\File | Adds file read/write/edit tools scoped to a directory |
UseSubagents | Capability\Subagent | Enables spawning child agents from definitions |
UsePlanningSubagent | Capability\PlanningSubagent | Adds a planning subagent that creates execution plans |
UseStructuredOutputs | Capability\StructuredOutput | Configures structured (typed) output extraction |
UseSummarization | Capability\Summarization | Adds conversation summarization hooks |
UseSelfCritique | Capability\SelfCritique | Adds self-critique evaluation after responses |
UseSkills | Capability\Skills | Injects skill instructions into agent context |
UseTaskPlanning | Capability\Tasks | Adds task planning and decomposition tools |
UseMetadataTools | Capability\Metadata | Adds tools for reading/writing agent metadata |
UseToolRegistry | Capability\Tools | Resolves tools from a named registry at build time |
UseExecutionHistory | Capability\ExecutionHistory | Tracks and exposes execution history |
UseExecutionRetrospective | Capability\Retrospective | Adds retrospective analysis of past executions |
Writing Custom Capabilities
Every capability implements theCanProvideAgentCapability interface, which defines two methods: capabilityName() for registry lookups, and configure() for applying the capability’s configuration to the agent.
CanConfigureAgent interface provides read and write access to all configurable components:
| Method | Returns | Purpose |
|---|---|---|
tools() / withTools() | Tools | Registered tool instances |
contextCompiler() / withContextCompiler() | CanCompileMessages | Message compilation strategy |
toolUseDriver() / withToolUseDriver() | CanUseTools | LLM driver for tool calling |
hooks() / withHooks() | HookStack | Execution lifecycle hooks |
deferredTools() / withDeferredTools() | DeferredToolProviders | Tools resolved at build time |
events() | CanHandleEvents | Event dispatcher (read-only) |
capabilityName() method returns a string identifier used by AgentCapabilityRegistry to look up capabilities by name. This is how agent templates reference capabilities in their definition files (see Agent Templates).
AgentBuilder vs AgentLoop
UseAgentLoop::default() or construct AgentLoop directly when you have a small, one-off setup where the wiring is straightforward. Use AgentBuilder when:
- The configuration is complex enough to benefit from decomposition into capabilities.
- You want to share the same setup across multiple agents via branching.
- You need deferred tool resolution (tools that depend on the final driver).
- You want testable, reusable configuration units that can be independently verified.
Event Propagation
TheAgentBuilder::base() method accepts an optional CanHandleEvents parent. Events dispatched by the built agent propagate upward to this parent handler, allowing you to collect events from multiple agents in a single place: