# How to Configure the Runner for Agent Execution in AISuite: A Complete Guide

> Master AISuite agent execution by configuring the Runner. Learn how to set max turns, state store, and tool policy for robust multi-turn tool loops and persistent conversations.

- Repository: [Andrew Ng/aisuite](https://github.com/andrewyng/aisuite)
- Tags: how-to-guide
- Published: 2026-06-15

---

**The `Runner` class is the central orchestration component that drives agent execution through configurable parameters like `max_turns`, `state_store`, and `tool_policy`, enabling multi-turn tool loops, persistent conversations, and detailed observability.**

The `Runner` in the AISuite framework manages the complete lifecycle of an agent interaction, from initial message construction through optional multi-turn tool execution and state persistence. When configuring the Runner for agent execution, you control conversation limits, tool permissions, tracing behavior, and storage backends. This guide examines the configuration options defined in [`aisuite/agents/runner.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/agents/runner.py) and demonstrates how to implement production-ready agent workflows.

## Core Runner Configuration Parameters

The `Runner.run_sync()` and `Runner.run()` methods accept a comprehensive set of keyword arguments that define execution behavior. These parameters are processed at the start of the run to initialize the execution context.

### Connection and Identity

- **`client`**: Supplies a pre-configured `Client` instance. If omitted, the Runner instantiates a new `Client()` automatically via [`aisuite/client.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/client.py).
- **`run_name`**, **`parent_run_id`**, **`group_id`**: Enable hierarchical organization of runs for complex workflow orchestration. These identifiers appear in every trace event emitted during execution.

### Execution Control

- **`max_turns`**: Enables multi-turn tool loops by specifying the maximum number of model-tool interaction cycles. The Runner repeatedly calls the model while tool calls are present, up to this limit.
- **`tool_policy`**: A callable or `ToolPolicy` object that decides whether a specific tool may execute. The Runner forwards this policy and a context dictionary to the underlying tool runner.

### Observability and Metadata

- **`tags`** / **`metadata`**: Attach arbitrary string tags or key-value dictionaries that propagate to every trace event, enabling filtering and correlation in downstream observability platforms.
- **`trace_sinks`**: List of `TraceSink` objects receiving trace events; defaults to globally configured sinks from [`aisuite/tracing/sinks.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/tracing/sinks.py).
- **`tracing_disabled`**: Boolean flag to skip all tracing initialization, eliminating the overhead of trace ID generation and event emission.

### State Persistence

- **`state_store`** / **`thread_id`**: Persist the run state for later continuation. Both must be supplied together; the store queries for existing thread state before starting execution via `state_store.load_state()`.
- **`artifact_store`**: Handles large artifacts such as files and images by dehydrating them when persisting state and rehydrating upon retrieval.

### Model Overrides

- **`**kwargs`**: Any additional model-specific arguments (e.g., `temperature`, `top_p`) are merged with the agent’s `model_settings` and passed to `Client.chat.completions.create()`.

## The Agent Execution Flow

The execution logic in [`aisuite/agents/runner.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/agents/runner.py) follows a strict seven-step pipeline:

1. **Input Normalisation**: If the input is a `RunState`, the Runner copies stored messages and merges run-level overrides; otherwise, it builds a new message list via `Runner._build_messages` (lines 97-105).
2. **Trace Initialisation**: Allocates a new trace ID unless tracing is disabled; resolves trace sinks via `get_configured_sinks` (lines 101-106).
3. **Emit `run.started` Event**: Records the run start, including the full input payload (lines 163-173).
4. **Model Call**: Invokes `Client.chat.completions.create` with merged model settings. If `agent.tools` is defined, the request includes tool schemas and respects `max_turns` (lines 134-140).
5. **Error Handling**: Catches exceptions, emits a `run.failed` trace event, and re-raises the original error.
6. **Response Processing**: Extracts final output, assembles response steps via `_build_response_steps` and tool steps via `_build_tool_steps`, then constructs a `RunResult` object (lines 260-284).
7. **Final Trace Events**: Emits `model.response` (if not already emitted by the client) and `run.completed` events, optionally persisting state via `state_store.save_state` (lines 286-306).

## Practical Configuration Examples

### Basic Synchronous Execution with Tracing

```python
from aisuite import Agent, Runner, Client

agent = Agent(
    name="simple-agent",
    model="openai:gpt-4o-mini",
    instructions="You are a helpful assistant.",
)

result = Runner.run_sync(
    agent,
    "Explain the difference between a list and a tuple.",
    max_turns=3,
    tags=["demo"],
    metadata={"request_id": "12345"},
)
print(result.final_output)

```

This call creates a trace ID, emits `run.started`, calls the model, and finally emits `run.completed`.

### Multi-Turn Tool Execution

```python
from aisuite import Agent, Runner, Tools

def list_files(path: str) -> str:
    import os; return "\n".join(os.listdir(path))

agent = Agent(
    name="file-explorer",
    model="openai:gpt-4o-mini",
    tools=Tools([list_files]).tools(),
)

result = Runner.run_sync(
    agent,
    "List the files in the current directory.",
    max_turns=5,                 # Enable tool loop

    tool_policy=lambda ctx, tool: True,  # Allow all tools

)
print(result.final_output)

```

The Runner calls `list_files` when the model produces a `tool_calls` entry and continues for up to 5 turns.

### Persisted Conversations with State Store

```python
from aisuite import Agent, Runner, InMemoryStateStore

store = InMemoryStateStore()
thread = "demo-thread"

# First run – creates a persisted thread

first = Runner.run_sync(
    agent,
    "Tell me a short story about a robot.",
    state_store=store,
    thread_id=thread,
)

# Continue the same thread later

second = Runner.continue_sync(
    first,
    "Add a twist where the robot meets a cat.",
    state_store=store,
    thread_id=thread,
)

print(second.final_output)

```

Both calls share the same `thread_id`; the state store automatically saves and loads the conversation via `state_store.save_state` and `state_store.load_state`.

### Disabling Tracing for Performance

```python
result = Runner.run_sync(
    agent,
    "Compute the 20th Fibonacci number.",
    tracing_disabled=True,   # No trace events emitted

)

```

## Continuing a Persisted Agent Run

To resume a persisted conversation, use `Runner.continue_sync(target, input, ...)`. This method, implemented in [`aisuite/agents/runner.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/agents/runner.py) (lines 374-394), loads the stored `RunState`, appends the new user message, and re-executes the run with the same configuration. It updates the stored state atomically, preserving the revision token to prevent conflicts in concurrent environments.

## Key Integration Points

**Client**: The `Client` class provides the underlying provider implementation and handles tool execution through `_tool_runner`. See [`aisuite/client.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/client.py) (lines 57-66) for the pipeline details.

**TraceSink**: Any object implementing `emit_event` can receive trace payloads. The default sinks are configured in [`aisuite/tracing/sinks.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/tracing/sinks.py).

**StateStore**: Concrete implementations like `InMemoryStateStore` or `PostgresStateStore` provide `load_state`, `save_state`, and version handling interfaces defined in [`aisuite/agents/state_store.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/agents/state_store.py).

## Summary

- The `Runner` class in [`aisuite/agents/runner.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/agents/runner.py) is the central orchestration component for agent execution.
- Configure multi-turn tool loops using the `max_turns` parameter and control tool permissions with `tool_policy`.
- Persist conversation state across sessions by providing both `state_store` and `thread_id` arguments.
- Implement observability by configuring `trace_sinks`, `tags`, and `metadata`, or disable tracing entirely with `tracing_disabled`.
- Resume existing conversations using `Runner.continue_sync()`, which atomically updates the stored `RunState`.

## Frequently Asked Questions

### What is the difference between `run_sync` and `run` in AISuite?

The `run_sync()` method provides a synchronous interface for agent execution, blocking until the full conversation or tool loop completes. The `run()` method is the asynchronous counterpart that returns a coroutine, suitable for async/await patterns in high-concurrency applications. Both methods accept identical configuration parameters and execute the same underlying logic in [`aisuite/agents/runner.py`](https://github.com/andrewyng/aisuite/blob/main/aisuite/agents/runner.py).

### How do I enable multi-turn tool execution in the Runner?

Set the `max_turns` parameter to an integer greater than 1 when calling `Runner.run_sync()`. The Runner will then enter a loop where it calls the model, executes any returned tool calls, and feeds the results back to the model, repeating this process up to the specified limit. You must also define `tools` in your `Agent` instance and optionally configure a `tool_policy` to control which tools may execute.

### How does state persistence work in AISuite?

State persistence requires a `StateStore` implementation (such as `InMemoryStateStore`) and a unique `thread_id` string. When both are provided to `Runner.run_sync()`, the Runner calls `state_store.save_state()` after completion, storing the `RunState` object containing message history and metadata. To resume, call `Runner.continue_sync()` with the same `thread_id` and store instance; the Runner loads the previous state via `state_store.load_state()` before appending new messages.

### Can I disable tracing for specific runs in production?

Yes. Pass `tracing_disabled=True` to `Runner.run_sync()` to suppress all trace events for that specific execution. This eliminates the overhead of trace ID generation and event emission, making it ideal for high-throughput benchmarks or sensitive operations where observability data should not be recorded. Note that this only affects the specific run; global trace sink configuration remains unchanged.