--- 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. 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. ## How it works Agent config is a typed object the frontend owns and publishes to the agent as runtime context. There are two pieces: the UI side, which owns the React state and publishes every change with `useAgentContext`, and the backend node, which reads that context entry and turns it into a system prompt. The UI side stays simple. Hold the typed config in React state, then mirror every change into the agent through `useAgentContext`: ```tsx title="frontend/src/app/page.tsx — UI publishes the typed config" function ConfigContextRelay({ config }: { config: AgentConfig }) { useAgentContext({ description: "Agent response preferences", value: { tone: config.tone, expertise: config.expertise, responseLength: config.responseLength, }, }); return null; } ``` The backend half is also a single node. Read the latest config context 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" import json CONFIG_KEYS = ("tone", "expertise", "responseLength") def read_config_value(entry): value = entry.get("value") if isinstance(value, str): try: value = json.loads(value) except json.JSONDecodeError: return None if not isinstance(value, dict): return None if any(key in value for key in CONFIG_KEYS): return value return None async def my_agent_node(state: AgentState, config: RunnableConfig): context_entries = state.get("copilotkit", {}).get("context", []) cfg = next( ( value for entry in reversed(context_entries) if (value := read_config_value(entry)) is not None ), {}, ) tone = cfg.get("tone", "professional") expertise = cfg.get("expertise", "intermediate") response_length = cfg.get("responseLength", "concise") 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. ## 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 `` 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" ``` 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 /* ... */ }); }; ```