Agent Configuration
Complete reference for configuring agents using YAML files. Covers 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 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 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 agents execute a single tool directly with the incoming payload. No AI model is involved, so they're 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. Supports wildcard patterns (e.g., billing.*) to include multiple functions from a module. See Conditional Tools. Max 100 per agent.Default: [] |
| discoverable_tools | list | Optional | Tools indexed for on-demand discovery instead of being loaded into the LLM context upfront. Same syntax as tools (strings, conditions, wildcards). The agent finds them at runtime with a natural-language query. See Discoverable Tools.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_effort | string | Optional | How hard the model should think. One of auto (use the provider's own default), off (disable where the model allows it), or minimal / low / medium / high / xhigh. Each provider maps these to its own native parameter (Claude adaptive effort, Gemini thinking_level/budget, OpenAI reasoning_effort). Captured reasoning is shown automatically in run traces whenever the provider returns it.Default: auto |
| reasoning_budget | integer | Optional | Explicit token budget for reasoning. Only honored by providers that accept a raw budget (Anthropic Claude 3.7 and Sonnet 4, Gemini 2.5). Providers that only accept effort levels — Claude Opus 4.7, Gemini 3, OpenAI — will reject this; use reasoning_effort instead. |
| 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 variable interpolation via ${VAR} syntax and per-run context interpolation via ${context.*} syntax. |
| discoverable | boolean | Optional | When true, tools from this MCP server are indexed for on-demand discovery instead of being loaded into the LLM context upfront. See MCP docs.Default: false |
| 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. |
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()
- billing.* # all public functions in tools/billing.py
- support.search_* # functions starting with search_ in tools/support.pyTools 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.
Use wildcards to include multiple functions from a module without listing each one. Patterns use standard wildcard syntax: * matches any sequence of characters. For example, billing.* includes all public functions in tools/billing.py, and support.search_* includes only functions starting with search_. Wildcards also work with API spec tools (e.g., api:stripe.*). The deployment fails if a wildcard matches zero tools.
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 # negationPython-like syntax: and, or, not; comparisons == != > < >= <=; membership in, not in; parentheses for grouping; string literals in single or double quotes. Reach into nested objects with dot-paths like context.user.role. A bare path like context.active is a truthy check: it passes when the value is set and not empty, zero, or false. Missing fields make the surrounding predicate fail rather than raising.
context.<key>run_id, agent_name, etc.). Supports nested paths like context.user.role.input.<key>input.metadata.tier. If the payload is not valid JSON, all input.* checks evaluate to false.Setting Context in Middleware
async def before(content: dict, context: dict) -> dict:
# Set context values that tool conditions can check.
payload = context.get("payload", {})
context["multiply_allowed"] = True
context["admin"] = payload.get("role") == "admin"
return contentCondition 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.
Discoverable Tools
When an agent has many tools, loading all of them into the LLM context increases token usage and can reduce accuracy. Discoverable tools solve this by keeping rarely-used tools out of context until the agent actively searches for them.
List tools under discoverable_tools instead of tools. They use the same syntax (strings, wildcards, conditions). At runtime, the framework indexes these tools and lets the agent find and call them on demand with a natural-language query.
version: "1.0"
name: multi-purpose-assistant
type: llm
model: gemini/gemini-2.5-pro
description: "Assistant that discovers tools on demand to keep context lean"
system_prompt: |
You are a multi-purpose assistant with access to many tools.
You always have basic math tools available. For anything else,
the framework will find the right tool for you automatically.
# Always available in the LLM context
tools:
- math.calculator.add
- math.calculator.multiply
# Indexed for on-demand discovery at runtime
discoverable_tools:
- math.calculator.calculate_tax
- orders.*
- social.twitter.post_tweet
- assistant_tools.*Discoverable MCP Servers
You can also mark an entire MCP server as discoverable. All tools from that server are indexed for search instead of loaded upfront.
mcp_servers:
- name: large-toolset
url: https://mcp.example.com/tools
discoverable: true # tools indexed for search, not loaded upfrontA tool cannot appear in both tools and discoverable_tools. The deployment will fail if any tool reference resolves to the same function in both lists.
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.
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 stable identifier for this conversation from the connector payload.
payload = context.get("payload", {})
context["chat_id"] = payload.get("chat_id")
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.
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_effort: medium # minimal | low | medium | high | xhigh | off | auto
reasoning_budget: 8192 # explicit token budget (provider-dependent)
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 stable identifier for this conversation from the connector payload.
payload = context.get("payload", {})
context["chat_id"] = payload.get("chat_id")
return contentModels 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 |
| Custom (OpenAI-compatible) | your_prefix/ | API Base URL + optional API Key. Configure in Project Settings. |
Custom OpenAI-Compatible Providers
You can connect any OpenAI-compatible endpoint (self-hosted models, inference proxies, etc.) as a custom provider. In Project Settings > LLM Provider, click Add Custom Provider and configure:
- Model Prefix - a unique lowercase prefix for this provider (e.g.
ollama) - API Base URL - the endpoint URL (e.g.
https://my-llm.example.com/v1) - API Key - optional, leave empty if the endpoint has no authentication
Then use your prefix in agent YAML just like any built-in provider:
# Custom provider configured with prefix "ollama"
model: ollama/llama3
# Custom provider configured with prefix "vllm"
model: vllm/mistral-7bAt runtime, the model string is routed to your endpoint via LiteLLM's OpenAI-compatible handler. The prefix must not collide with built-in provider names.
Example Usage
# Using OpenAI
model: openai/gpt-5.2
# Using Anthropic
model: anthropic/claude-opus-4-7
# 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-opus-4-7-v1:0
# Using Google Vertex AI
model: vertex_ai/gemini-2.5-pro
# Using a custom OpenAI-compatible provider (configured in Project Settings)
model: ollama/llama3Drop a _defaults.yaml into any directory under agents/ to share configuration with every agent at that directory level and below. Use it to factor out the same model, guardrails, common tools, or database.collections across families of agents instead of repeating them per file.
The loader merges defaults shallowest-first (root → deepest) and then applies the agent's own file last, so the agent always wins on conflict.
What can live in a defaults file
The same fields as a normal agent YAML, but partial — only set what you want to share. Two exceptions:
nameanddescriptionare forbidden in defaults — they identify a specific agent and sharing them is either useless or causes name collisions.versionis allowed so you can pin the schema version once project-wide. The agent file must still set it (along withnameanddescription).
Merge rules
- Scalars (
model,temperature,system_prompt,timeout, …) — deeper layer replaces shallower; the agent file replaces all. - Dicts (
database,knowledge,retry_options,approval,session,guardrails,database.collections, …) — recursive deep merge, per-key. Defaults can supply some collections; an agent can add more without losing the inherited ones. - Lists — concat with dedup, so children add to rather than replace inherited entries:
tools,discoverable_tools,approval.tools— dedup by tool ref. An agent re-declaring an inherited tool (e.g. with a different condition) overrides the inherited entry.mcp_servers— dedup by servername; the agent's full server config replaces the inherited one on collision.guardrails.input,guardrails.output— dedup by rulenameif present; otherwise appended.- Sequential
agents:— dedup by string.
Example
version: "1.0"
type: llm
model: anthropic/claude-sonnet-4-6
temperature: 0
guardrails:
input:
- type: prompt_injection
mode: block
output:
- type: system_prompt_leakage
mode: block
tools:
- audit.log_eventversion: "1.0"
name: refund-agent
description: "Issues refunds against the billing system."
system_prompt: "Refund only valid charges. Use the tools."
tools:
- billing.lookup_charge
- billing.issue_refund
approval:
tools:
- billing.issue_refund: param.amount > 50
timeout: 3600Effective config for refund-agent: model, temperature, and both guardrails inherited from the root defaults; tools = [audit.log_event, billing.lookup_charge, billing.issue_refund].
Run connic lint to validate the merged config; load errors point at the file whose values caused the rejection. A/B test variants (<base>-test-<name>.yaml) inherit the same defaults as their base.
Use connic dev for a cloud dev runner with hot-reload. Edits land in 2-5 seconds, no commits needed. Pair with tests/ to lock in behaviour before shipping.
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
Testing
Declarative test suites that gate every deploy
Variables
Inject environment-specific configuration into your agents