defineAgent is the single function you need to build an agent route. It returns a route handler that runs the model↔tool loop and streams the Vercel AI SDK data stream protocol back to the client. Export it as the default from an agent.ts file (or any post.ts) and Anvil mounts it automatically.
Quick start
The example below creates a fully working agent route atPOST /chat that uses Anthropic’s Claude, answers questions, and can call a weather tool:
Configuration reference
defineAgent(config: DefineAgentConfig) accepts the following options:
The
LlmClient instance to use for model calls. If omitted, the runtime falls back to resolveClient or the client attached by a withLlm() middleware.A per-request factory for the
LlmClient. Useful when the client depends on request state (e.g. a per-tenant API key stored in ctx.state). Takes precedence over a middleware-attached client but not over an inline client.Override the default model for this specific agent route. The value is passed through to the
LlmClient and must be supported by one of its drivers.The system prompt. Pass a plain string for a static prompt, or an async function to build the prompt from request context — for example, to inject the authenticated user’s name.
The tools available to the model during this run. Pass an array for a fixed set or a function to assemble tools dynamically from request context (e.g. scoped by user permissions).
Hard cap on the number of model↔tool turns. Defaults to
10. When the cap is reached the stream ends with a final event — the model’s last text (if any) is still returned. Use this to bound cost and latency on open-ended agent tasks.A
Tracer instance for trace instrumentation. Each request opens a named trace; model calls and tool executions are recorded as child spans. Traces appear in the /_anvil dashboard.The name given to the trace opened for each request. Defaults to
"agent <path>". Pass a string for a static name, or a function to derive it from the request context (e.g. to include the user ID).Per-request cost cap. The runtime checks the accumulated cost before each model call and aborts the loop if the budget is exceeded.
An ordered array of guardrail policies applied to model output text and tool calls. Use the built-in helpers (
contentFilter, toolPolicy, injectionGuard) or implement the Guardrail interface directly.A context assembly pipeline that runs once before the loop starts. Steps can inject retrieved documents as system addenda (RAG), trim the message history to a token budget, or add static context fragments. Built-in steps:
retrievalContext, tokenBudget, systemContext.Custom message extractor. By default
defineAgent reads body.messages — the standard AI SDK useChat request shape. Override this when your request body has a different structure.Attaching a client with middleware
UsewithLlm when you want to share one LlmClient instance across many routes without importing it into every file:
Dynamic system prompt
Pass an async function tosystem to build the prompt from the incoming request:
Custom message extraction
OverridegetMessages when your frontend sends a request body that doesn’t match the AI SDK useChat shape: