Agent Configuration
A complete reference for configuring agents using YAML files. Learn about all available properties and best practices.
The type field determines how your agent executes. Connic supports three agent types:
- LLM: For tasks requiring AI reasoning, conversation, or complex decision-making
- Sequential: For multi-step workflows where agents need to work in sequence
- Tool: For deterministic operations that don't need AI reasoning
LLM Agent
Standard AI agents powered by language models
LLM agents use AI models to process requests. They can reason about inputs, use tools, and generate natural language responses. This is the default type. Use when you need conversational AI, reasoning, or intelligent tool selection.
version: "1.0"
name: assistant
type: llm # Default type, can be omitted
model: gemini/gemini-2.5-pro # Provider prefix required
description: "A helpful general-purpose assistant"
system_prompt: "You are a helpful assistant. Be concise and accurate."Required: model and system_prompt
Sequential Agent
Chain multiple agents together in a pipeline
Sequential agents execute a chain of other agents in order. Each agent in the chain receives the previous agent's output as its input. Use for multi-step workflows or data pipelines.
version: "1.0"
name: document-pipeline
type: sequential
description: "Processes documents through extraction and validation"
# Agents execute in order, each receiving the previous agent's output
agents:
- assistant # First: extracts key information
- invoice-processor # Then: validates and processes the dataRequired: agents list. Each must be defined in its own YAML file
Tool Agent
Execute tools directly without AI reasoning
Tool agents execute a single tool directly with the incoming payload. No AI model is involved: faster and more deterministic. Use for calculations, data transforms, or API calls.
version: "1.0"
name: tax-calculator
type: tool
description: "Calculates tax directly using the calculator tool"
# Executes this tool directly with the incoming payload
tool_name: calculator.calculate_taxRequired: tool_name. Payload is passed as keyword arguments
Type Comparison
| Feature | LLM | Sequential | Tool |
|---|---|---|---|
| AI Model | Yes | Depends on chain | No |
| Uses Tools | Multiple tools | Via sub-agents | Single tool |
| Speed | Model-dependent | Sum of chain | Fastest |
| Cost | Per-token | Sum of chain | No model cost |
Configuration Fields
| Field | Type | Status | Description |
|---|---|---|---|
| version | string | Required | Configuration schema version. Currently only '1.0' is supported. |
| name | string | Required | Unique identifier for the agent. Use lowercase letters, numbers, and hyphens. |
| type | string | Optional | Agent type: 'llm' (default), 'sequential', or 'tool'.Default: llm |
| description | string | Required | Human-readable description of what the agent does. |
| model | string | Optional | The AI model to use. Required for LLM agents. |
| fallback_model | string | Optional | Fallback AI model to use when the primary model's provider is unavailable. When the main model fails due to provider issues (outages, rate limits, auth errors, timeouts), retries automatically switch to this model. |
| system_prompt | string | Optional | Instructions for LLM agents. Use YAML's pipe (|) for multi-line. |
| tools | list | Optional | List of tools for LLM agents. Each entry is either a string (always available) or a mapping with a condition expression. See Conditional Tools. Max 100 per agent.Default: [] |
| agents | string[] | Optional | List of agent names to execute in sequence. Required for sequential agents.Default: [] |
| tool_name | string | Optional | Tool to execute directly. Required for tool agents. Use the exact module path under tools/, for example calculator.add or billing.calculator.add. |
| max_concurrent_runs | integer | Optional | Maximum simultaneous runs allowed. Capped by your subscription plan.Default: 1 |
| temperature | number | Optional | Controls randomness in LLM output. Lower = more deterministic.Default: 1 |
| reasoning | boolean | Optional | Include the model's reasoning in run traces. When enabled, the model's internal thinking process is captured and displayed separately in run details. Supported by models with reasoning capabilities (e.g. Gemini 2.5, Claude).Default: true |
| reasoning_budget | integer | Optional | Maximum number of tokens the model may use for reasoning. Use 0 to disable reasoning or -1 to let the model decide automatically. Only applies when reasoning is enabled. |
| retry_options | object | Optional | Configuration for automatic retries. Enables two retry mechanisms: (1) Agent-level retries with exponential backoff for transient failures (network, API limits), and (2) Tool-level retries where the model reflects on errors and tries different approaches. |
| attempts | integer | Optional | Maximum retry attempts for both agent and tool-level retries. Max 10.Default: 3 |
| max_delay | integer | Optional | Maximum seconds between agent-level retries (uses exponential backoff: 1s, 2s, 4s... capped at max_delay). Max 300s.Default: 30 |
| rerun_middleware | boolean | Optional | Re-execute the 'before' middleware on each retry attempt. When enabled, middleware can refresh dynamic context (e.g., current external state) between retries. The 'after' middleware still runs once after all retries complete.Default: false |
| timeout | integer | Optional | Maximum execution time in seconds. Minimum is 5 seconds. The actual timeout is always capped by your subscription's limit. |
| max_iterations | integer | Optional | Maximum number of agent loop iterations per run. Each iteration is one LLM call (e.g. agent output, tool call, next output). Prevents infinite loops and excessive resource consumption. LLM agents only.Default: 100 |
| guardrails | object | Optional | Input and output safety guardrails. LLM agents only. See Guardrails docs. |
| input | object[] | Optional | Guardrail rules applied to raw input before middleware and agent execution. |
| output | object[] | Optional | Guardrail rules applied to agent response before returning. |
| type | string | Required | Guardrail type: prompt_injection, pii, moderation, topic_restriction, regex, pii_leakage, system_prompt_leakage, relevance, data_exfiltration, or custom. |
| mode | string | Optional | block (stop and return message), warn (log and continue), or redact (replace detected content). Redact only for pii/pii_leakage.Default: block |
| name | string | Optional | Required for type: custom. Name of the guardrail module in guardrails/. |
| config | object | Optional | Per-guardrail options. Includes rejection_message, off_topic_message, fail_run, entities, patterns, provider, sensitivity, model, etc. |
| rejection_message | string | Optional | Message returned to user when guardrail blocks. Overrides default. Fallback: off_topic_message. |
| off_topic_message | string | Optional | Message for topic_restriction blocks. Fallback for rejection_message when not set. |
| fail_run | boolean | Optional | When true, block marks the run as failed. When false (default), block returns the rejection message as a completed response.Default: false |
| output_schema | string | Optional | Name of a JSON Schema file from schemas/ directory. Forces structured JSON output. LLM agents only. See Output Schema docs. |
| mcp_servers | object[] | Optional | List of MCP servers to connect to for external tools. Max 50 per agent. See MCP Integration docs. |
| name | string | Required | Identifier for the MCP server |
| url | string | Required | URL of the MCP server endpoint |
| tools | string[] | Optional | Filter to specific tools (omit to use all available) |
| headers | object | Optional | HTTP headers for authentication (supports ${VAR} syntax) |
| concurrency | object | Optional | Key-based concurrency control. Ensures only one run per unique key value is active at a time. Not supported on sequential agents. See Concurrency Rules. |
| key | string | Required | Dot-notation path to extract the concurrency key from the trigger payload (e.g., 'process_id', 'data.customer_id'). |
| on_conflict | string | Optional | Behavior when a run with the same key is already active: 'queue' waits for the active run to finish, 'drop' cancels the new run immediately.Default: queue |
| approval | object | Optional | Human-in-the-loop approval for specific tools. When set, the listed tools require human approval before execution. See Approvals docs. |
| tools | list | Required | Tool references that require approval before execution. Each entry is either a plain string (always requires approval) or a mapping with a condition expression using param.* and context.*. See Conditional Approvals. |
| timeout | integer | Optional | Seconds to wait for a decision before the approval expires. With on_rejection: fail (default) the run terminates; with on_rejection: continue the agent adapts. Range: 30–604800 (1 week).Default: 3600 |
| message | string | Optional | Custom message shown to the reviewer in the dashboard and email notification. |
| on_rejection | string | Optional | Behavior when an approval is rejected: "fail" terminates the run (default), "continue" resumes the run with a rejection message returned to the LLM so it can adapt. See Rejection Behavior.Default: "fail" |
| session | object | Optional | Persistent session configuration. When set, the agent maintains conversation history across requests, keyed by a resolved value. See Persistent Sessions. |
| key | string | Required | Dot-path expression to resolve the session ID. Must start with context. (read from middleware context) or input. (read from the raw payload). Example: context.chat_id. |
| ttl | integer | Optional | Session time-to-live in seconds. Sessions not updated within this period are considered expired and automatically cleaned up. Minimum 60 seconds. When not set, sessions never expire. |
Required Fields by Type
All agents: version, name, description
LLM: + model, system_prompt · Sequential: + agents · Tool: + tool_name
Writing System Prompts
system_prompt: |
This is a multi-line system prompt.
You can write multiple paragraphs here.
The pipe character (|) preserves newlines.
Use this for complex instructions.Best practices: Be specific about role and responsibilities, include examples, specify output formats for structured responses, mention available tools.
Referencing Tools
# Reference tools by exact module path under tools/
tools:
- search.web_search # tools/search.py -> web_search()
- billing.calculator.add # tools/billing/calculator.py -> add()
- email.send_notification # tools/email.py -> send_notification()Tools are Python functions in your tools/ directory. Top-level modules use refs like calculator.add, while nested modules use the full dotted path like billing.calculator.add. See the Write Tools guide.
Conditional Tools
Tools can be made conditionally available per request. Instead of a plain string, use a mapping where the key is the tool reference and the value is a condition expression. If the condition evaluates to false, the tool is completely removed from the agent for that request.
# Conditional tools: only available when the expression evaluates to true.
# Uses Python expression syntax (and, or, not, ==, !=, >, <, >=, <=).
tools:
- calculator.add # always available
- calculator.multiply: context.multiply_allowed # available when middleware sets context.multiply_allowed
- web_search: input.search_enabled # available when input JSON has search_enabled=true
- admin.dangerous_tool: input.role == 'admin' or context.admin # equality check with or
- premium.tool: input.tier == 'pro' and context.feature_on # and with equality
- optional.tool: not context.disabled # negationData Sources
context.<key>- Values from the middleware context dict. Set in yourbeforemiddleware. Supports nested paths likecontext.user.role.input.<key>- Values from the connector's JSON payload. Only works when the incoming payload is valid JSON. Supports nested paths likeinput.metadata.tier. If the payload is not valid JSON, allinput.*checks evaluate to false.
Operators
Conditions use Python expression syntax: and, or, not for logic, ==, !=, >, <, >=, <= for comparisons, and parentheses for grouping. String literals use single quotes.
Setting Context in Middleware
async def before(content: dict, context: dict) -> dict:
# Set context values that tool conditions can check
context["multiply_allowed"] = True
context["admin"] = content["parts"][0]["text"].startswith("/admin")
return contentValidation at Deploy Time
Condition expressions are validated when your agent is loaded. Invalid syntax will fail the deployment, not at runtime. A bare accessor like context.foo is a truthy check: it passes if the value is set and not empty/zero/false.
Concurrency Rules
Concurrency rules let you ensure only one run per unique key value is active at a time. This is useful when an agent processes events for specific entities (e.g., a support process, a customer, an order) and you need to prevent parallel processing of events belonging to the same entity.
Queue Mode (default)
When a second event arrives for the same key while a run is active, it waits in a queue until the first run finishes, then processes automatically.
version: "1.0"
name: support-processor
type: llm
model: gemini/gemini-2.5-pro
description: "Processes support tickets one at a time per process"
system_prompt: "You are a support agent. Process the incoming ticket."
# Only one run per process_id at a time. Additional runs wait in queue.
concurrency:
key: "process_id"
on_conflict: queueDrop Mode
When a second event arrives for the same key while a run is active, it is immediately cancelled. Use this when only the latest state matters and processing stale events would be wasteful.
version: "1.0"
name: notification-handler
type: tool
description: "Handles notifications, skipping duplicates"
tool_name: notifications.handle
# Drop duplicate runs for the same customer while one is active.
concurrency:
key: "data.customer_id"
on_conflict: dropKey Extraction
The key field uses dot-notation to extract a value from the trigger payload. For example, if your Kafka message payload is {"data": {"customer_id": "abc"}}, use data.customer_id as the key. If the key path is not found in the payload, concurrency enforcement is skipped for that run.
Interaction with max_concurrent_runs
Both constraints apply simultaneously. For example, with max_concurrent_runs: 5 and concurrency.key: "process_id", up to 5 different process IDs can run in parallel, but only 1 run per process ID at a time.
Limitations
Concurrency rules are not supported on sequential agents. The key is extracted from the raw trigger payload, so it applies to all trigger types (Kafka, HTTP, Cron, API, trigger_agent) uniformly.
Persistent Sessions
By default, every request creates a new, isolated session. With persistent sessions enabled, the agent maintains conversation history across multiple requests, enabling multi-turn conversations that survive restarts and redeployments.
version: "1.0"
name: support-bot
type: llm
model: gemini/gemini-2.5-pro
description: "A support chatbot that remembers conversation history"
system_prompt: |
You are a helpful support agent. Use the conversation history
to provide contextual responses.
# Maintain persistent sessions keyed by chat ID from middleware
session:
key: context.chat_id
ttl: 86400 # Sessions expire after 24 hours of inactivitySession Key
The key field determines how the agent identifies which session to use. It must start with one of two prefixes:
context.<path>- Resolves from the middleware context dictionary. Set values in yourbeforemiddleware. This is the recommended approach as it lets you derive a stable session key from any part of the request.input.<path>- Resolves from the raw connector payload (before middleware). Only works when the incoming payload is valid JSON. Supports nested paths likeinput.data.user_id.
Setting the Key via Middleware
async def before(content: dict, context: dict) -> dict:
# Extract a unique identifier for this conversation
context["chat_id"] = content["parts"][0]["text"].split(":")[0]
return contentUsing Input Keys
# When the connector payload is JSON like {"user_id": "u123", "message": "..."}
session:
key: input.user_idTTL (Time-to-Live)
The optional ttl field sets a session expiration in seconds. Sessions that have not been updated within the TTL period are considered expired and will be cleaned up automatically. If ttl is not set, sessions never expire. The minimum value is 60 seconds.
Managing Sessions
Active sessions can be viewed and deleted from the dashboard under Storage > Sessions. Sessions are scoped per environment.
Important
If the session key resolves to an empty or missing value, the run will fail with an error. Make sure your middleware or payload always provides the expected key.
LLM Agent Examples
Simple LLM Agent
A minimal LLM agent configuration:
version: "1.0"
name: assistant
type: llm # Default type, can be omitted
model: gemini/gemini-2.5-pro # Provider prefix required
description: "A helpful general-purpose assistant"
system_prompt: "You are a helpful assistant. Be concise and accurate."Full-Featured LLM Agent
An invoice processing agent with multiple tools and detailed instructions:
version: "1.0"
name: invoice-processor
type: llm
model: openai/gpt-5.2 # Or: gemini/gemini-2.5-pro, ...
fallback_model: gemini/gemini-2.5-pro # Used when primary model is unavailable
description: "Extracts data from invoices and validates totals"
system_prompt: |
You are an expert accountant specializing in invoice processing.
Your responsibilities:
1. Extract all relevant fields from invoices (vendor, date, line items, totals)
2. Use the calculator tool to verify mathematical accuracy
3. Flag any discrepancies between line items and totals
4. Format extracted data in a structured JSON format
Always double-check calculations before confirming totals are correct.
max_concurrent_runs: 10
max_iterations: 50 # Stop after 50 iterations to limit cost
temperature: 0.7
reasoning: true # Capture model reasoning in traces
reasoning_budget: 8192 # Max tokens for reasoning
retry_options:
attempts: 5
max_delay: 60
# rerun_middleware: true
tools:
- calculator.add
- calculator.multiply
- pdf.extract_text
- validation.check_totals
guardrails:
input:
- type: prompt_injection
mode: block
- type: pii
mode: redact
output:
- type: moderation
mode: block
- type: system_prompt_leakage
mode: blockSequential Agent Example
Document Processing Pipeline
A sequential agent that chains multiple agents together:
version: "1.0"
name: document-pipeline
type: sequential
description: "Processes documents through extraction and validation"
# Agents execute in order, each receiving the previous agent's output
agents:
- assistant # First: extracts key information
- invoice-processor # Then: validates and processes the dataCustomer Inquiry Pipeline
A three-step pipeline for validation, database lookup, and response formatting:
version: "1.0"
name: customer-inquiry
type: sequential
description: "Validate input → fetch account → format response"
# Each step receives the previous agent's output
agents:
- validate-inquiry
- fetch-account
- format-responseDefine each step as its own agent:
version: "1.0"
name: validate-inquiry
type: tool
description: "Validate and normalize customer inquiry payload"
tool_name: validation.validate_inquiryversion: "1.0"
name: fetch-account
type: tool
description: "Lookup account details from Postgres"
tool_name: postgres.fetch_user_accountversion: "1.0"
name: format-response
type: llm
model: gemini/gemini-2.5-pro
description: "Format a helpful customer response"
system_prompt: |
You receive validated inquiry data plus account details.
Respond concisely and include next steps when relevant.Middleware linking: createmiddleware/validate-inquiry.py to attach hooks toagents/validate-inquiry.yaml. No YAML config required.
Tool Agent Example
Direct Tool Execution
A tool agent that executes a calculator function directly:
version: "1.0"
name: tax-calculator
type: tool
description: "Calculates tax directly using the calculator tool"
# Executes this tool directly with the incoming payload
tool_name: calculator.calculate_taxPersistent Session Example
Chatbot with Session Memory
An agent that maintains conversation history per chat, with sessions expiring after 24 hours of inactivity:
version: "1.0"
name: support-bot
type: llm
model: gemini/gemini-2.5-pro
description: "A support chatbot that remembers conversation history"
system_prompt: |
You are a helpful support agent. Use the conversation history
to provide contextual responses.
# Maintain persistent sessions keyed by chat ID from middleware
session:
key: context.chat_id
ttl: 86400 # Sessions expire after 24 hours of inactivitySetting the Session Key in Middleware
Use a before middleware to extract a stable identifier and set it in the context:
async def before(content: dict, context: dict) -> dict:
# Extract a unique identifier for this conversation
context["chat_id"] = content["parts"][0]["text"].split(":")[0]
return contentModel Format
Models must include a provider prefix: provider/model-name. Configure API keys in Project Settings before using a provider.
Connic supports multiple LLM providers. Configure your API keys in Project Settings, then use the provider prefix in your agent configuration.
| Provider | Prefix | Configuration |
|---|---|---|
| OpenAI | openai/ | API Key only |
| Azure OpenAI | azure/ | API Key + Base URL + API Version |
| Anthropic | anthropic/ | API Key only |
| Google Gemini | gemini/ | API Key only |
| OpenRouter | openrouter/ | API Key only |
| AWS Bedrock | bedrock/ | Access Key ID + Secret Access Key + Region |
| Google Vertex AI | vertex_ai/ | GCP Project ID + Location + Service Account JSON |
Example Usage
# Using OpenAI
model: openai/gpt-5.2
# Using Anthropic
model: anthropic/claude-sonnet-4-5-20250929
# Using Google Gemini
model: gemini/gemini-2.5-pro
# Using Azure OpenAI (use your deployment name)
model: azure/my-gpt5-deployment
# Using OpenRouter (provider/model format)
model: openrouter/anthropic/claude-sonnet-4.5
# Using AWS Bedrock
model: bedrock/us.anthropic.claude-sonnet-4-5-20250929-v1:0
# Using Google Vertex AI
model: vertex_ai/gemini-2.5-proIterate Faster with Local Testing
Use connic test to test your agent configurations with hot-reload. Changes are reflected in 2-5 seconds without pushing to git.
Write Custom Tools
Create Python functions your agents can call
Output Schema
Force structured JSON output with JSON Schema
Predefined Tools
Use built-in knowledge and trigger tools
Guardrails
Add safety guardrails for input validation and output filtering
Local Testing
Test agents locally with hot-reload
Variables
Inject environment-specific configuration into your agents