AgentRunner
AgentRunner is the driver behind Rig’s high-level agent prompt APIs. Most applications can use
agent.prompt("...").await?, but a runner gives you an explicit value for one run: one prompt,
one set of per-run options, and one call to run() or stream().
Think of the layers like this:
Agentstores reusable configuration: model, preamble, tools, RAG context, memory, and default hooks.AgentRunnerdrives one prompt through the agent loop. It owns per-run options such as turn limits, memory behavior, tool concurrency, tool extensions, and hooks.AgentRunis the lower-level, sans-IO state machine. Use it only when you need to persist and resume the loop yourself, for example durable out-of-process approvals.
Basic runner usage
Section titled “Basic runner usage”agent.runner(prompt) builds a runner seeded from the agent’s configuration. run() drives the loop to
completion and returns a PromptResponse, including the final output, aggregate usage, per-call usage,
and message history.
use rig::client::{CompletionClient, ProviderClient};use rig::providers::openai;
#[tokio::main]async fn main() -> Result<(), anyhow::Error> { let agent = openai::Client::from_env()? .agent(openai::GPT_4O) .preamble("You are a helpful assistant.") .build();
let response = agent .runner("Find the answer and show your work.") .max_turns(5) .run() .await?;
println!("answer: {}", response.output); println!("model calls: {}", response.completion_calls.len()); println!("tokens: {}", response.usage.total_tokens);
Ok(())}This is equivalent in spirit to:
let response = agent .prompt("Find the answer and show your work.") .max_turns(5) .extended_details() .await?;Use whichever shape reads better in your code. AgentRunner is especially useful when you build a run
in stages or share configuration between the blocking and streaming surfaces.
Per-run controls
Section titled “Per-run controls”| Method | What it changes |
|---|---|
max_turns(n) | Maximum multi-turn depth before PromptError::MaxTurnsError. |
max_invalid_tool_call_retries(n) | Retry budget for invalid tool-call recovery. Retries also consume normal turns. |
history(messages) | Explicit chat history. This bypasses conversation memory for the run. |
conversation(id) | Conversation id used to load/save configured memory. |
without_memory() | Disable memory load and save for this run. |
tool_concurrency(n) | Execute up to n tool calls from one model turn at once; 0 is clamped to 1. |
tool_extensions(ext) | Pass runtime-only values to tools via Tool::call_with_extensions, without exposing them to the model. |
add_hook(hook) | Append a hook after any default hooks on the agent. See Hooks. |
Turns and invalid-tool retries
Section titled “Turns and invalid-tool retries”max_turns is the agent loop’s safety bound: it limits how many follow-up model calls Rig may make
after tool results. If the model keeps calling tools past the limit, Rig returns
PromptError::MaxTurnsError with the history accumulated so far.
max_invalid_tool_call_retries is separate. It only applies when a hook recovers from an invalid tool
call with Flow::retry(...); each retry asks the model to try the turn again and also consumes normal
turn budget. For the hook side of that recovery, see Hooks.
Conversation memory
Section titled “Conversation memory”If the agent has a memory backend and a conversation id, the runner loads history before the first model call and saves the completed turn after the run finishes:
let response = agent .runner("What did I ask earlier?") .conversation("user-42") .run() .await?;Passing explicit history(...) bypasses memory completely for that run: Rig neither loads from nor
saves to the memory backend. Use without_memory() when you want the same bypass without providing
manual history.
Tool concurrency
Section titled “Tool concurrency”When a model emits multiple tool calls in a single turn, the runner can execute them concurrently:
let response = agent .runner("Check inventory, shipping, and pricing.") .max_turns(3) .tool_concurrency(3) .run() .await?;Final message history is still persisted in tool-call order. With tool_concurrency > 1, per-tool side
effects such as logs, spans, and hook callbacks may interleave in completion order, so make those side
effects concurrency-safe.
Tool extensions
Section titled “Tool extensions”tool_extensions lets application code pass runtime-only values to tools: auth tokens, tenant IDs,
request metadata, session state, or other values the model should not see. Tools read these values from
Tool::call_with_extensions.
let response = agent .runner("Update my account preferences.") .tool_extensions(extensions) .run() .await?;Use extensions for trusted application context. Do not put secrets in the prompt just so a tool can read them.
Blocking and streaming runs
Section titled “Blocking and streaming runs”run() drives the blocking path and returns one PromptResponse. stream() drives the same runner
through the streaming path, yielding assistant deltas, tool activity, and a final response.
Both surfaces share the same run construction, tool execution, memory behavior, tracing span shape, and hook handling. The streaming path adds streaming-specific items and hook events, such as text deltas and tool-call argument deltas.
let stream = agent .runner("Explain the result as you work.") .max_turns(3) .stream() .await;See Streaming for consuming streaming items and Hooks for streaming hook events.
AgentRunner vs. AgentRun
Section titled “AgentRunner vs. AgentRun”Use AgentRunner when you still want Rig to perform IO: send model requests, execute tools, apply
memory, emit tracing spans, and run hooks. Use AgentRun when you want a serializable state machine and
will provide the IO yourself.
AgentRun is the right tool for durable human-in-the-loop systems: serialize the run while tools are
pending, wait for an approval in another service, then deserialize and feed tool results back. It does
not contain hooks because hooks are async, side-effecting driver code; hooks live at the AgentRunner
layer.
See also
Section titled “See also”- Hooks — observe and steer runner events.
- Agents — the high-level agent abstraction.
- Tools — define tools that the runner executes.
- Memory — conversation memory used by
conversation(...). - Streaming — stream responses from the runner.
AgentRunnersource on GitHub — exact main-branch type and builder methods.
