---
title: Agent Config
description: Forward typed configuration from your UI into the agent's reasoning loop.
icon: "lucide/SlidersHorizontal"
snippet_cell: agent-config
---

You have a working agent and want the user to be able to tune how it behaves: tone, expertise level, response length, language, persona. By the end of this guide, your UI will own a typed config object that the agent reads on every run and rebuilds its system prompt from.

## When to use this

Reach for agent config whenever the agent's behaviour depends on user-controllable settings that don't fit naturally as chat input:

- **Tone, voice, persona**: "playful", "formal", "casual"
- **Expertise level**: "beginner", "intermediate", "expert"
- **Response shape**: short / medium / long, structured / prose, language
- **Domain switches**: which knowledge base to consult, which tool subset to enable

If the values are a *channel* the user occasionally tunes (a settings panel, a toolbar of selects), agent config is the right shape. If the values are *content* the agent should write back to (notes, a document, a plan), use [Shared State](/shared-state) instead.

<InlineDemo demo="agent-config" />

How agent config flows from the UI into the agent's reasoning loop depends on your runtime architecture. Agents living behind a runtime read it from agent state on every run, while in-process agents receive the same object as forwarded properties on the provider — same UX, slightly different wiring on each side.

<WhenFrameworkHas flag="agent_config_pattern" equals="shared-state">

## How it works

Agent config is a typed object the frontend owns and keeps in sync with the agent. There are two pieces: the UI side, which owns the React state and pushes every change into agent state, and the backend node, which reads those fields out of state and turns them into a system prompt.

The UI side stays simple. Hold the typed config in React state, then mirror every change into the agent through `agent.setState({...})`:

```tsx title="frontend/src/app/page.tsx — UI owns the typed config"
function ConfigStateSync({ config }: { config: AgentConfig }) {
  const { agent } = useAgent({ agentId: "agent-config" });
  useEffect(() => {
    agent.setState({ ...config });
  }, [agent, config]);
  return null;
}
```

The backend half is also a single node. Read the config out of `state` at the top of every run and use it to build the system prompt for that turn:

```python title="backend/agent.py — agent reads config and rebuilds the system prompt"
async def my_agent_node(state: AgentState, config: RunnableConfig):
    cfg = state.get("config", {})
    tone = cfg.get("tone", "casual")
    expertise = cfg.get("expertise", "intermediate")
    response_length = cfg.get("response_length", "medium")
    system_prompt = build_system_prompt(tone, expertise, response_length)
    # ...
```

The agent reads the latest typed config at the start of every turn, rebuilds the system prompt, runs the turn. This is the same shape as the [shared-state write-side pattern](/shared-state#writing-to-agent-state); agent config is just a specific use of that pattern with a UI-owned typed object on top.

</WhenFrameworkHas>

<WhenFrameworkHas flag="agent_config_pattern" equals="runtime-properties">

## How it works

The runtime owns the agent in-process, so config travels through the provider rather than agent state. There's no separate backend service to push state into, and no extra plumbing — the typed object you set on `<CopilotKit>` becomes the input to the agent factory directly.

The UI side passes the typed object as `properties` on the provider:

```tsx title="frontend/src/app/page.tsx — config flows through the provider"
<CopilotKit
  runtimeUrl="/api/copilotkit"
  properties={{ tone, expertise, responseLength }}
  useSingleEndpoint
>
  <Demo />
</CopilotKit>
```

The runtime hands the same object to the agent factory on every call as `input.forwardedProps`. The factory uses those fields to build a system prompt before returning the agent for that turn:

```ts title="backend/agent factory — synthesise the system prompt per turn"
export const agentConfigFactory = async (input: AgentFactoryInput) => {
  const { tone, expertise, responseLength } = input.forwardedProps ?? {};
  const systemPrompt = buildSystemPrompt(tone, expertise, responseLength);
  return makeAgent({ systemPrompt /* ... */ });
};
```

</WhenFrameworkHas>

<FeatureIntegrations feature="agent-config" />

<IntegrationGrid path="agent-config" />
