From bd35d6859a3df3be9570317ef350c797b8a82637 Mon Sep 17 00:00:00 2001 From: Djordje Lukic Date: Fri, 28 Nov 2025 10:21:17 +0100 Subject: [PATCH 1/4] Cagent docs Signed-off-by: Djordje Lukic --- content/manuals/ai/cagent/_index.md | 2 - content/manuals/ai/cagent/guides/_index.md | 11 + content/manuals/ai/cagent/mcp.md | 16 ++ content/manuals/ai/cagent/tools/_index.md | 195 ++++++++++++++++++ .../manuals/ai/cagent/tools/builtin/fetch.md | 62 ++++++ .../ai/cagent/tools/builtin/filesystem.md | 147 +++++++++++++ .../manuals/ai/cagent/tools/builtin/memory.md | 82 ++++++++ .../ai/cagent/tools/builtin/script_shell.md | 141 +++++++++++++ .../manuals/ai/cagent/tools/builtin/shell.md | 93 +++++++++ .../manuals/ai/cagent/tools/builtin/think.md | 68 ++++++ .../manuals/ai/cagent/tools/builtin/todo.md | 116 +++++++++++ 11 files changed, 931 insertions(+), 2 deletions(-) create mode 100644 content/manuals/ai/cagent/guides/_index.md create mode 100644 content/manuals/ai/cagent/mcp.md create mode 100644 content/manuals/ai/cagent/tools/_index.md create mode 100644 content/manuals/ai/cagent/tools/builtin/fetch.md create mode 100644 content/manuals/ai/cagent/tools/builtin/filesystem.md create mode 100644 content/manuals/ai/cagent/tools/builtin/memory.md create mode 100644 content/manuals/ai/cagent/tools/builtin/script_shell.md create mode 100644 content/manuals/ai/cagent/tools/builtin/shell.md create mode 100644 content/manuals/ai/cagent/tools/builtin/think.md create mode 100644 content/manuals/ai/cagent/tools/builtin/todo.md diff --git a/content/manuals/ai/cagent/_index.md b/content/manuals/ai/cagent/_index.md index 77ea20c69b29..bc0c4eea3b54 100644 --- a/content/manuals/ai/cagent/_index.md +++ b/content/manuals/ai/cagent/_index.md @@ -26,12 +26,10 @@ they don't share knowledge. ## Key features -- ️Multi-tenant architecture with client isolation and session management. - Rich tool ecosystem via Model Context Protocol (MCP) integration. - Hierarchical agent system with intelligent task delegation. - Multiple interfaces including CLI, TUI, API server, and MCP server. - Agent distribution via Docker registry integration. -- Security-first design with proper client scoping and resource isolation. - Event-driven streaming for real-time interactions. - Multi-model support (OpenAI, Anthropic, Gemini, DMR, Docker AI Gateway). diff --git a/content/manuals/ai/cagent/guides/_index.md b/content/manuals/ai/cagent/guides/_index.md new file mode 100644 index 000000000000..19ba2235b895 --- /dev/null +++ b/content/manuals/ai/cagent/guides/_index.md @@ -0,0 +1,11 @@ +--- +title: Guides +grid: + - title: "Coding agent" + description: Understand the process for building and publishing an extension. + icon: "checklist" + link: "/extensions/extensions-sdk/process/" +--- + + +{{< grid >}} diff --git a/content/manuals/ai/cagent/mcp.md b/content/manuals/ai/cagent/mcp.md new file mode 100644 index 000000000000..c35da9625b80 --- /dev/null +++ b/content/manuals/ai/cagent/mcp.md @@ -0,0 +1,16 @@ +--- +title: MCP +description: Get inspiration from agent examples +keywords: [ai, agent, cagent] +weight: 10 +--- + +**MCP toolsets:** +- `command`: Command to run for local MCP servers +- `args`: Arguments for the command +- `ref`: Reference name for the MCP server +- `remote`: Configuration for remote MCP servers + - `url`: Remote server URL + - `transport_type`: Transport type (`sse` or `http`) + - `headers`: HTTP headers for authentication + diff --git a/content/manuals/ai/cagent/tools/_index.md b/content/manuals/ai/cagent/tools/_index.md new file mode 100644 index 000000000000..f867d57c1178 --- /dev/null +++ b/content/manuals/ai/cagent/tools/_index.md @@ -0,0 +1,195 @@ +--- +title: Builtin tools +description: cagent's builtin tools +keywords: [ai, agent, cagent] +weight: 10 +--- + +Tools are what make agents useful. They give your agent the ability to interact +with external systems, execute commands, access files, fetch web content, and +much more. Without tools, an agent can only generate text-with tools, it can +take action. + +## What are Toolsets? + +`cagent` organizes tools into **toolsets** - logical groups of related tools +that work together to accomplish specific tasks. For example: + +- The **filesystem** toolset provides tools for reading, writing, searching, and + managing files +- The **shell** toolset enables command execution in your terminal +- The **memory** toolset allows agents to remember information across + conversations +- An **MCP server** is a toolset that can provide any number of custom tools + +Toolsets are configured in your agent YAML file and determine what capabilities +your agent has access to. + +## Types of Toolsets + +`cagent` supports several types of toolsets: + +### Builtin Toolsets + +Built directly into cagent, these toolsets provide core functionality: + +| Toolset | Description | +| ---------------- | ------------------------------------------------------------- | +| `filesystem` | Read, write, search, and manage files and directories | +| `shell` | Execute shell commands in your environment | +| `memory` | Store and retrieve persistent information about users | +| `fetch` | Retrieve content from HTTP/HTTPS URLs | +| `think` | Reasoning scratchpad for complex planning | +| `todo` | Task tracking for multi-step operations | +| `script_shell` | Define custom parameterized shell commands as tools | + +[Learn more about builtin toolsets →](/docs/tools/builtin/filesystem) + +### MCP Servers + +The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) is an open +standard for connecting AI assistants to external systems. MCP servers act as +toolsets in cagent, providing standardized access to a wide ecosystem of tools. + +**Local MCP Servers (stdio):** Run as local processes that communicate via +standard input/output. Great for accessing local resources and services. + +**Remote MCP Servers (SSE/HTTP):** Connect to remote servers over HTTP, enabling +access to web APIs, databases, and cloud services. + +[Learn more about MCP servers →](/docs/mcp) + +### Custom Shell Scripts + +Using the `script_shell` toolset, you can define your own custom tools that +execute shell commands with typed parameters: + +```yaml +toolsets: + - type: script_shell + tools: + deploy: + cmd: "./deploy.sh" + description: "Deploy the application" + args: + environment: + type: string + description: "Target environment" + required: + - environment +``` + +[Learn more about custom shell tools →](/docs/tools/builtin/script_shell) + +## Configuring Toolsets + +Toolsets are configured in your agent's YAML file under the `toolsets` array: + +```yaml +agents: + my_agent: + model: gpt-4o + description: "A helpful coding assistant" + toolsets: + # Builtin toolset - simple type reference + - type: filesystem + + # Builtin toolset with configuration + - type: memory + path: "./memories.db" + + # Local MCP server (stdio) + - type: mcp + command: npx + args: + - "-y" + - "@modelcontextprotocol/server-filesystem" + - "/path/to/directory" + + # Remote MCP server (SSE) + - type: mcp + remote: + url: "https://api.example.com/mcp" + transport_type: sse + headers: + Authorization: "Bearer ${API_TOKEN}" + + # Custom shell tools + - type: script_shell + tools: + build: + cmd: "npm run build" + description: "Build the project" +``` + +## Toolset Configuration Options + +Each toolset type may have specific configuration options. Common options are: + +- `instruction`: Additional instructions for using the toolset (optional) +- `tools`: Array of specific tool names to enable (optional, defaults to all) +- `env`: Environment variables for the toolset (optional) + +## Tool Selection + +By default, agents have access to all tools provided by their configured +toolsets. You can restrict this using the `tools` option: + +```yaml +toolsets: + - type: filesystem + tools: + - read_file + - write_file + - list_directory + # Agent only gets these three filesystem tools +``` + +This is useful for: +- Limiting agent capabilities for security +- Reducing context size for smaller models +- Creating specialized agents with focused tool access + +## Best Practices + +### Performance + +- **Choose appropriate toolsets**: Don't load toolsets the agent won't use +- **Limit tool selection**: Use the `tools` array to restrict available tools +- **Consider model capabilities**: Smaller models may struggle with too many + tools + +## Multi-Agent Systems + +Different agents in a multi-agent system can have different toolsets: + +```yaml +agents: + coordinator: + model: gpt-4o + sub_agents: + - code_writer + - code_reviewer + toolsets: + - type: transfer_task + + code_writer: + model: gpt-4o + toolsets: + - type: filesystem + - type: shell + + code_reviewer: + model: gpt-4o + toolsets: + - type: filesystem + tools: + - read_file + - read_multiple_files +``` + +This allows you to: +- Create specialized agents with focused capabilities +- Implement security boundaries between agents +- Optimize performance by limiting each agent's toolset + diff --git a/content/manuals/ai/cagent/tools/builtin/fetch.md b/content/manuals/ai/cagent/tools/builtin/fetch.md new file mode 100644 index 000000000000..193cebebe067 --- /dev/null +++ b/content/manuals/ai/cagent/tools/builtin/fetch.md @@ -0,0 +1,62 @@ +--- +title: Fetch +description: Fetch content from remote URLs +--- + +This toolset allows your agent to fetch content from HTTP and HTTPS URLs. It +supports multiple URLs in a single call, respects robots.txt restrictions, and +can return content in different formats (text, markdown, or HTML). + +## Usage + +```yaml +toolsets: + - type: fetch + timeout: 30 # Optionally set a default timeout for HTTP requests +``` + +## Tools + +| Name | Description | +| ------- | ---------------------------------------------------------------- | +| `fetch` | Fetch content from one or more HTTP/HTTPS URLs with metadata | + +### fetch + +Fetches content from one or more HTTP/HTTPS URLs and returns the response body +along with metadata such as status code, content type, and content length. + +Args: + +- `urls`: Array of URLs to fetch (required) +- `format`: The format to return the content in - `text`, `markdown`, or `html` + (required) +- `timeout`: Request timeout in seconds, default is 30, maximum is 300 + (optional) + +**Features:** + +- Support for multiple URLs in a single call +- Returns response body and metadata (status code, content type, length) +- Automatic HTML to markdown or text conversion based on format parameter +- Respects robots.txt restrictions +- Configurable timeout per request + +**Example:** + +Single URL: +```json +{ + "urls": ["https://example.com"], + "format": "markdown", + "timeout": 60 +} +``` + +Multiple URLs: +```json +{ + "urls": ["https://example.com", "https://another.com"], + "format": "text" +} +``` diff --git a/content/manuals/ai/cagent/tools/builtin/filesystem.md b/content/manuals/ai/cagent/tools/builtin/filesystem.md new file mode 100644 index 000000000000..c5bba41839c4 --- /dev/null +++ b/content/manuals/ai/cagent/tools/builtin/filesystem.md @@ -0,0 +1,147 @@ +--- +title: Filesystem +description: Access and manage the filesystem +--- + +This toolset gives your agent access to your filesystem. By default the +filesystem tool has access to the current working directory, this can be +modified in your agent file, an agent can also decide, thanks to +`add_allowed_directory`, to ask for permission for a directory access. + +## Usage + +```yaml title="agent.yaml" +toolsets: + - type: filesystem +``` + +## Tools + +| Name | Description | +| --------------------------- | --------------------------------------------------- | +| `add_allowed_directory` | Adds a directory to the list of allowed directories | +| `create_directory` | Creates a directory | +| `directory_tree` | Returns a recursive view of the directory tree | +| `edit_file` | Modifies a file | +| `get_file_info` | Get stat info about a file/directory | +| `list_allowed_directories` | Returns the list of currently allowed directories | +| `list_directory` | Lists the contents of a directory | +| `list_directory_with_sizes` | Desc | +| `move_file` | Moves a file | +| `read_file` | Reads a file | +| `read_multiple_files` | Reads multiple files | +| `search_files` | Search for files by filename/pattern | +| `search_files_content` | Grep-like search | +| `write_file` | Writes a file | + +### add_allowed_directory + +By default the filesystem tool only has access to the current working directory. +This tool allows the agent to request access to other directories. + +Args: + +- `path`: The directory path to add to the list of allowed directories. + +### create_directory + +Creates a directory at the specified path. + +Args: + +- `path`: The directory path to create. + +### directory_tree + +Returns a recursive view of the directory tree starting from the specified path. + +Args: + +- `path`: The directory path to view. +- `max_depth`: The maximum depth to recurse into the directory tree. + +### edit_file + +Modifies a file at the specified path using a series of edit operations. + +Args: + +- `path`: The file path to edit. +- `edits`: Array of edit operations. + +### get_file_info + +Gets stat info about a file or directory. + +Args: + +- `path`: The file or directory path to get info about. + +### list_allowed_directories + +Returns the list of currently allowed directories. + +### list_directory + +Lists the contents of a directory. + +Args: +- `path`: The directory path to list. + +### list_directory_with_sizes + +Lists the contents of a directory along with the sizes of each item. + +Args: +- `path`: The directory path to list. + +### move_file + +Moves a file from one location to another. + +Args: +- `source`: The source file path. +- `destination`: The destination file path. + +### read_file + +Reads a file at the specified path. + +Args: +- `path`: The file path to read. + +### read_multiple_files + +Reads multiple files at the specified paths. + +Args: +- `paths`: An array of file paths to read. +- `json` (optional): If true, returns the contents as a JSON object. + +### search_files + +Search for files by filename or pattern. + +Args: +- `pattern`: The filename or pattern to search for. +- `path`: The starting directory path. +- `excludePatterns`: Patterns to exclude from search. + +### search_files_content + +Grep-like search for file contents. + +Args: +- `pattern`: The content pattern to search for. +- `path`: The starting directory path. +- `query`: The text or regex pattern to search for. +- `isRegex`: If true, treat query as regex; otherwise literal text. +- `excludePatterns`: Patterns to exclude from search. + +### write_file + +Writes a file at the specified path. + +Args: +- `path`: The file path to write. +- `content`: The content to write to the file. diff --git a/content/manuals/ai/cagent/tools/builtin/memory.md b/content/manuals/ai/cagent/tools/builtin/memory.md new file mode 100644 index 000000000000..fda1ec5468c7 --- /dev/null +++ b/content/manuals/ai/cagent/tools/builtin/memory.md @@ -0,0 +1,82 @@ +--- +title: Memory +description: Agentic memory for your agent +--- + +The memory toolset gives your agent the ability to store, retrieve, and delete +persistent memories about the user. This allows the agent to remember important +information across conversations. + +## Usage + +```yaml +toolsets: + - type: memory + path: "./memories.db" +``` + +## Tools + +| Name | Description | +| ---------------- | -------------------------------------- | +| `add_memory` | Add a new memory to the database | +| `get_memories` | Retrieve all stored memories | +| `delete_memory` | Delete a specific memory by ID | + +### add_memory + +Adds a new memory to the database with an auto-generated ID and timestamp. + +Args: + +- `memory`: The memory content to store (required) + +**Example:** + +```json +{ + "memory": "User prefers dark mode for all interfaces" +} +``` + +### get_memories + +Retrieves all stored memories from the database. Returns an array of memory +objects, each containing an ID, creation timestamp, and the memory content. + +No arguments required. + +**Response format:** + +```json +[ + { + "id": "1234567890", + "createdAt": "2024-01-15T10:30:00Z", + "memory": "User prefers dark mode for all interfaces" + } +] +``` + +### delete_memory + +Deletes a specific memory from the database by its ID. + +Args: + +- `id`: The ID of the memory to delete (required) + +**Example:** + +```json +{ + "id": "1234567890" +} +``` + +## Best Practices + +- Use `get_memories` at the start of conversations to retrieve relevant context +- Store important user preferences, facts, and context +- Delete outdated or incorrect memories when necessary +- Create specific, actionable memories rather than vague observations diff --git a/content/manuals/ai/cagent/tools/builtin/script_shell.md b/content/manuals/ai/cagent/tools/builtin/script_shell.md new file mode 100644 index 000000000000..bad18239f4bf --- /dev/null +++ b/content/manuals/ai/cagent/tools/builtin/script_shell.md @@ -0,0 +1,141 @@ +--- +title: Script Shell +description: Define custom shell command as tools +--- + +This toolset allows you to define custom shell command tools with typed +parameters in your agent configuration. This enables you to create reusable, +parameterized shell commands as first-class tools for your agent. + +## Usage + +```yaml +toolsets: + - type: script_shell + tools: + deploy: + cmd: "./deploy.sh" + description: "Deploy the application to production" + workingDir: "./scripts" + args: + environment: + type: string + description: "Target environment (staging/production)" + version: + type: string + description: "Version to deploy" + required: + - environment + - version + + run_tests: + cmd: "go test -v -race ./..." + description: "Run Go tests with race detection" + workingDir: "." + args: + package: + type: string + description: "Specific package to test (optional)" +``` + +## Configuration + +Each custom tool is defined with the following properties: + +- `cmd`: The shell command to execute (required) +- `description`: Human-readable description of what the tool does (optional, + defaults to showing the command) +- `workingDir`: Working directory to execute the command in (optional) +- `args`: Object defining typed parameters that can be passed to the command + (optional) +- `required`: Array of required parameter names (optional, defaults to all args + being required) + +## Parameters + +Parameters defined in `args` are passed to the command as environment variables. +Each parameter can specify: + +- `type`: The parameter type (string, number, boolean, etc.) +- `description`: Description of what the parameter is for + +## Examples + +### Simple command without parameters + +```yaml +toolsets: + - type: script_shell + tools: + build: + cmd: "npm run build" + description: "Build the frontend application" + workingDir: "./frontend" +``` + +### Command with required parameters + +```yaml +toolsets: + - type: script_shell + tools: + create_migration: + cmd: "migrate create -ext sql -dir ./migrations -seq $name" + description: "Create a new database migration" + args: + name: + type: string + description: "Name of the migration" + required: + - name +``` + +### Command with optional parameters + +```yaml +toolsets: + - type: script_shell + tools: + run_benchmark: + cmd: | + if [ -n "$package" ]; then + go test -bench=. -benchmem $package + else + go test -bench=. -benchmem ./... + fi + description: "Run Go benchmarks" + args: + package: + type: string + description: "Specific package to benchmark (optional)" + required: [] +``` + +## How It Works + +1. Parameters are passed as environment variables to the shell command +2. Commands execute in the specified `workingDir` or current directory +3. The command runs in the user's default shell (`$SHELL` on Unix, or `/bin/sh`) +4. stdout and stderr are combined and returned as the tool result + +## Best Practices + +- **Use descriptive names** - Tool names should clearly indicate their purpose +- **Document parameters** - Provide clear descriptions for all parameters +- **Set working directories** - Use `workingDir` to ensure commands run in the + correct context +- **Handle optional parameters** - Use shell conditionals when parameters are + optional +- **Keep commands focused** - Each tool should do one thing well +- **Use shell scripts for complex logic** - For multi-step operations, call a + shell script rather than inlining complex commands + +## Use Cases + +- Deployment automation +- Running tests with specific configurations +- Database migrations +- Code generation +- Build system integration +- CI/CD operations +- Custom project-specific workflows diff --git a/content/manuals/ai/cagent/tools/builtin/shell.md b/content/manuals/ai/cagent/tools/builtin/shell.md new file mode 100644 index 000000000000..2574e2e34c82 --- /dev/null +++ b/content/manuals/ai/cagent/tools/builtin/shell.md @@ -0,0 +1,93 @@ +--- +title: Shell +description: Execute shell commands in the user's environment +--- + +This toolset allows your agent to execute shell commands in the user's default +shell environment. Commands run with full access to environment variables and +can be executed in any working directory. + +## Usage + +```yaml +toolsets: + - type: shell +``` + +## Tools + +| Name | Description | +| ------- | ------------------------------------------------ | +| `shell` | Execute shell commands in the user's environment | + +### shell + +Executes shell commands in the user's default shell. On Windows, PowerShell +(pwsh/powershell) is used when available; otherwise, cmd.exe is used. On +Unix-like systems, the `$SHELL` environment variable is used, or `/bin/sh` as a +fallback. + +Args: + +- `cmd`: The shell command to execute (required) +- `cwd`: Working directory to execute the command in (required, use "." for + current directory) +- `timeout`: Command execution timeout in seconds, default is 30 (optional) + +**Features:** + +- Supports pipes, redirections, and complex shell operations +- Each command runs in a fresh shell session (no state persists) +- Automatic timeout protection to prevent hanging commands +- Full access to environment variables +- Support for heredocs and multi-line scripts + +**Examples:** + +Basic command: +```json +{ + "cmd": "ls -la", + "cwd": "." +} +``` + +Long-running command with custom timeout: +```json +{ + "cmd": "npm run build", + "cwd": ".", + "timeout": 120 +} +``` + +Using pipes: +```json +{ + "cmd": "cat package.json | jq '.dependencies'", + "cwd": "frontend" +} +``` + +Complex script with heredoc: +```json +{ + "cmd": "cat << 'EOF' | ${SHELL}\necho 'Hello'\necho 'World'\nEOF", + "cwd": "." +} +``` + +## Best Practices + +- Use the `cwd` parameter for directory-specific commands +- Quote arguments containing spaces or special characters +- Use the `timeout` parameter for long-running operations (builds, tests, etc.) +- Prefer heredocs over writing temporary script files +- Leverage this tool for batch file operations + +## Error Handling + +- Commands with non-zero exit codes return error information along with any + output +- Commands that exceed their timeout are automatically terminated +- Output includes both stdout and stderr combined diff --git a/content/manuals/ai/cagent/tools/builtin/think.md b/content/manuals/ai/cagent/tools/builtin/think.md new file mode 100644 index 000000000000..c4a2d6f74512 --- /dev/null +++ b/content/manuals/ai/cagent/tools/builtin/think.md @@ -0,0 +1,68 @@ +--- +title: Think +description: Thought recording and reasoning tool +--- + +This toolset provides your agent with a scratchpad for reasoning and planning. +It allows the agent to record thoughts without performing actions or changing +data, making it useful for complex reasoning tasks. + +## Usage + +```yaml +toolsets: + - type: think +``` + +## Tools + +| Name | Description | +| ------- | ---------------------------------------------- | +| `think` | Record thoughts for reasoning and planning | + +### think + +Use this tool to think about something. It will not obtain new information or +change the database, but will append the thought to a log. This is useful when +complex reasoning or cache memory is needed. + +Args: + +- `thought`: The thought to think about (required) + +**Use Cases:** + +- List specific rules that apply to the current request +- Check if all required information has been collected +- Verify that planned actions comply with policies +- Iterate over tool results for correctness +- Break down complex problems into steps +- Record intermediate reasoning steps + +**Example:** + +```json +{ + "thought": "The user wants to create a new feature. I need to: 1) Check existing code structure, 2) Identify dependencies, 3) Create new files, 4) Update configuration" +} +``` + +## Best Practices + +- Use the think tool generously before taking actions +- Record your reasoning process step-by-step +- Use it to verify tool results and plan next steps +- Helpful for maintaining context during multi-step tasks +- Use it to check compliance with rules and policies before proceeding + +## Output + +The tool returns all accumulated thoughts in the session, allowing you to review +your reasoning process: + +``` +Thoughts: +First thought here +Second thought here +Third thought here +``` diff --git a/content/manuals/ai/cagent/tools/builtin/todo.md b/content/manuals/ai/cagent/tools/builtin/todo.md new file mode 100644 index 000000000000..56f2e5bf0609 --- /dev/null +++ b/content/manuals/ai/cagent/tools/builtin/todo.md @@ -0,0 +1,116 @@ +--- +title: Todo +description: Task tracking for your agent +--- + +This toolset provides your agent with task tracking capabilities. It allows the +agent to create, update, and list todo items to maintain progress through +complex multi-step tasks. + +## Usage + +```yaml +toolsets: + - type: todo +``` + +## Tools + +| Name | Description | +| --------------- | ---------------------------------------------- | +| `create_todo` | Create a new todo item with a description | +| `create_todos` | Create multiple todo items at once | +| `update_todo` | Update the status of a todo item | +| `list_todos` | List all current todos with their status | + +### create_todo + +Creates a single new todo item with a unique ID and sets its initial status to +"pending". + +Args: + +- `description`: Description of the todo item (required) + +**Example:** + +```json +{ + "description": "Implement user authentication module" +} +``` + +### create_todos + +Creates multiple todo items at once. Useful for breaking down a complex task +into multiple steps at the beginning. + +Args: + +- `descriptions`: Array of todo item descriptions (required) + +**Example:** + +```json +{ + "descriptions": [ + "Read existing code structure", + "Design new feature architecture", + "Implement core functionality", + "Write tests", + "Update documentation" + ] +} +``` + +### update_todo + +Updates the status of an existing todo item. Valid statuses are: `pending`, +`in-progress`, and `completed`. + +Args: + +- `id`: ID of the todo item (required) +- `status`: New status - `pending`, `in-progress`, or `completed` (required) + +**Example:** + +```json +{ + "id": "todo_1", + "status": "completed" +} +``` + +### list_todos + +Lists all current todos with their ID, description, and status. No arguments +required. + +**Response format:** + +``` +Current todos: +- [todo_1] Implement user authentication module (Status: completed) +- [todo_2] Design new feature architecture (Status: in-progress) +- [todo_3] Write tests (Status: pending) +``` + +## Best Practices + +- **Always create todos before starting complex tasks** - Break down work into + manageable steps +- **Use list_todos frequently** - Check remaining work before responding to + users +- **Update status regularly** - Mark todos as completed to track progress +- **Never skip steps** - Ensure all todos are addressed before considering a + task complete +- **Use create_todos for batch creation** - More efficient than multiple + create_todo calls + +## Workflow + +1. **Before starting:** Use `create_todos` to break down the task into steps +2. **While working:** Use `list_todos` to check what remains +3. **After each step:** Use `update_todo` to mark completed items +4. **Before finishing:** Use `list_todos` to verify all steps are done From 8c03e9d0738d3a0109b3bec7cf3136b16938f384 Mon Sep 17 00:00:00 2001 From: Djordje Lukic Date: Fri, 28 Nov 2025 10:21:27 +0100 Subject: [PATCH 2/4] Add one guide, not great but... Signed-off-by: Djordje Lukic --- content/manuals/ai/cagent/examples.md | 4 +- .../ai/cagent/guides/coding-agent/_index.md | 256 ++++++++++++++++++ 2 files changed, 258 insertions(+), 2 deletions(-) create mode 100644 content/manuals/ai/cagent/guides/coding-agent/_index.md diff --git a/content/manuals/ai/cagent/examples.md b/content/manuals/ai/cagent/examples.md index 8f0388852a2a..a69d3f5d8c69 100644 --- a/content/manuals/ai/cagent/examples.md +++ b/content/manuals/ai/cagent/examples.md @@ -1,8 +1,8 @@ --- -title: cagent examples +title: Examples description: Get inspiration from agent examples keywords: [ai, agent, cagent] -weight: 10 +weight: 100 --- Get inspiration from the following agent examples. diff --git a/content/manuals/ai/cagent/guides/coding-agent/_index.md b/content/manuals/ai/cagent/guides/coding-agent/_index.md new file mode 100644 index 000000000000..da20fd3dbe5c --- /dev/null +++ b/content/manuals/ai/cagent/guides/coding-agent/_index.md @@ -0,0 +1,256 @@ +--- +title: "Building a Coding Agent" +linkTitle: "Building a Coding Agent" +weight: 10 +description: "Learn how to create a powerful, customizable coding agent using cagent, starting from a simple base and evolving into a full-fledged developer assistant." +--- + +This guide walks you through creating a coding agent using `cagent`. You will start with a minimal configuration and progressively add features until you have a robust "daily driver" agent similar to the one used by the Docker Engineering team. + +## Prerequisites + +Before you begin, ensure you have: + +1. **Installed `cagent`**: Follow the [installation instructions](../../#installation). +2. **API Keys**: Export your API keys (e.g., `ANTHROPIC_API_KEY` or `OPENAI_API_KEY`) in your environment. + +## Step 1: The Simplest Agent + +At its core, a `cagent` agent is defined in a YAML file. The simplest agent needs just a model and some instructions. + +Create a file named `my_coder.yaml`: + +```yaml +agents: + root: + model: openai/gpt-4o + description: A basic coding assistant + instruction: | + You are a helpful coding assistant. + Help me write and understand code. +``` + +Run it: + +```bash +cagent run my_coder.yaml +``` + +This agent can answer questions about code, but it cannot see your files or run commands yet. It lives in isolation. + +## Step 2: Giving the Agent Hands (Toolsets) + +To be a true *coding* agent, it needs to interact with your project. We do this by adding **toolsets**. The most critical ones for a developer are `filesystem` (to read/write code) and `shell` (to run tests and builds). + +Update `my_coder.yaml`: + +```yaml +agents: + root: + model: openai/gpt-4o + description: A coding assistant with filesystem access + instruction: | + You are a helpful coding assistant. + You can read and write files to help me develop software. + Always check if code works before finishing a task. + toolsets: + - type: filesystem + - type: shell +``` + +Now, when you run this agent, it can actually edit your code and explore your project directories. + +## Step 3: Defining the Expert Persona + +A generic "helpful assistant" is okay, but for daily work, you want a specialist. Let's refine the instructions to define a specific workflow, constraints, and responsibilities. This is where prompt engineering shines. + +This configuration defines a "Golang Developer" persona. Notice the structured instructions using XML-like tags and clear headers. + +```yaml +agents: + root: + model: anthropic/claude-3-5-sonnet-latest + description: Expert Golang developer + instruction: | + Your goal is to help users with code-related tasks by examining, modifying, and validating code changes. + + + # **Workflow:** + # 1. **Analyze**: Understand requirements and identify relevant code. + # 2. **Examine**: Search for files, analyze structure and dependencies. + # 3. **Modify**: Make changes following best practices. + # 4. **Validate**: Run linters/tests. If issues found, loop back to Modify. + + + **Constraints:** + * Be thorough in examination before making changes. + * Always validate changes (tests/lint) before considering the task complete. + * Don't show the generated code in the chat; just write it to the files. + + ## Development Workflow + - `go build ./...` - Build the application + - `go test ./...` - Run tests + - `golangci-lint run` - Check code quality + + add_date: true + add_environment_info: true + toolsets: + - type: filesystem + - type: shell + - type: todo +``` + +**Key Additions:** +- **Structured Workflow**: Tells the agent *how* to work (Analyze -> Examine -> Modify -> Validate). +- **Specific Commands**: Gives the agent the exact commands to run for your project. +- **Constraints**: Prevents common pitfalls (like hallucinating code without saving it). +- **`add_environment_info: true`**: Lets the agent know about your OS and shell environment. + +## Step 4: Adding Multi-Agent Capabilities + +Complex tasks often require different types of thinking. You can split responsibilities by adding **sub-agents**. For example, a "Planner" agent to break down big tasks, and a "Librarian" agent to look up documentation. + +This mirrors the structure of the `golang_developer.yaml` used by Docker's team. + +```yaml +agents: + # The main worker + root: + model: anthropic/claude-3-5-sonnet-latest + description: Expert Golang developer + instruction: | + (Instructions from Step 3...) + toolsets: + - type: filesystem + - type: shell + - type: todo + sub_agents: + - librarian # Can delegate research tasks to the librarian + + # The researcher + librarian: + model: anthropic/claude-3-5-haiku-latest + description: Documentation researcher + instruction: | + You are the librarian. Your job is to look for relevant documentation to help the developer agent. + Search the internet for documentation, articles, or resources. + toolsets: + - type: mcp + ref: docker:duckduckgo # Uses Docker MCP for web search + - type: fetch # Can fetch web pages +``` + +In this setup, the `root` agent can call the `librarian` tool when it needs to check external documentation, keeping its own context focused on coding. + +## The "Daily Driver" Agent Reference + +Here is the complete `golang_developer.yaml` agent configuration used by the `cagent` team to build `cagent` itself. You can adapt this for your own language and project. + +```yaml +#!/usr/bin/env cagent run + +agents: + root: + model: anthropic/claude-3-5-sonnet-latest + description: Expert Golang developer specializing in the cagent multi-agent AI system architecture + instruction: | + Your main goal is to help users with code-related tasks by examining, modifying, and validating code changes. + Always use conversation context/state or tools to get information. Prefer tools over your own internal knowledge. + + + # **Workflow:** + + # 1. **Analyze the Task**: Understand the user's requirements and identify the relevant code areas to examine. + + # 2. **Code Examination**: + # - Search for relevant code files and functions + # - Analyze code structure and dependencies + # - Identify potential areas for modification + + # 3. **Code Modification**: + # - Make necessary code changes + # - Ensure changes follow best practices + # - Maintain code style consistency + + # 4. **Validation Loop**: + # - Run linters or tests to check code quality + # - Verify changes meet requirements + # - If issues found, return to step 3 + # - Continue until all requirements are met + + + **Constraints:** + + * Be thorough in code examination before making changes + * Always validate changes before considering the task complete + * Follow best practices and maintain code quality + * Be proactive in identifying potential issues + * Only ask for clarification if necessary, try your best to use all the tools to get the info you need + * Don't show the code that you generated + * Never write summary documents, only code changes + + ## Core Responsibilities + - Develop, maintain, and enhance Go applications following best practices + - Debug and optimize Go code with proper error handling and logging + + ## Development Workflow + Use these commands for development tasks: + - `task build` - Build the application binary + - `task test` - Run Go tests + - `task lint` - Run golangci-lint for code quality + + ## Development Guidelines + - Tests located alongside source files (`*_test.go`) + - Always run `task test` to execute full test suite + - Follow existing patterns in `pkg/` directories + - Implement proper interfaces for providers and tools + - Add configuration support when adding new features + + ## Tests + - Use Go's testing package for unit tests + - Mock external dependencies for isolated tests + - Use t.Context() when needed + - Always use github.com/stretchr/testify/assert and github.com/stretchr/testify/require for assertions + + Always provide practical, actionable advice based on the cagent architecture and follow Go best practices. When helping with code, consider the multi-tenant security model, proper error handling, and the event-driven streaming architecture. + add_date: true + add_environment_info: true + toolsets: + - type: filesystem + - type: shell + - type: todo + sub_agents: + - librarian + + planner: + model: anthropic/claude-3-5-sonnet-latest + instruction: | + You are a planning agent responsible for gathering user requirements and creating a development plan. + Always ask clarifying questions to ensure you fully understand the user's needs before creating the plan. + Once you have a clear understanding, analyze the existing code and create a detailed development plan in a markdown file. Do not write any code yourself. + Once the plan is created, you will delegate tasks to the root agent. Make sure to provide the file name of the plan when delegating. Write the plan in the current directory. + toolsets: + - type: filesystem + sub_agents: + - root + + librarian: + model: anthropic/claude-3-5-haiku-latest + instruction: | + You are the librarian, your job is to look for relevant documentation to help the golang developer agent. + When given a query, search the internet for relevant documentation, articles, or resources that can assist in completing the task. + Use context7 for searching documentation and duckduckgo for general web searches. + toolsets: + - type: mcp + ref: docker:context7 + - type: mcp + ref: docker:duckduckgo + - type: fetch +``` + +## Tips for Your Own Agent + +1. **Iterate on Instructions**: If the agent keeps making a mistake (e.g., forgetting to run tests), add a specific constraint or workflow step to the YAML. +2. **Project Context**: Hardcode project-specific commands (like `make test` or `npm test`) in the instructions so the agent knows exactly how to validate its work. +3. **Use the Right Model**: For coding logic, use high-reasoning models like `claude-3-5-sonnet` or `gpt-4o`. For simple searches or summaries, smaller models like `haiku` can save costs. +4. **Dogfooding**: The best way to improve your agent is to use it to improve itself! Ask it to "Add a new constraint to your configuration to prevent X". From 8c6513f8d7b57559517d74da26522b28e61c360e Mon Sep 17 00:00:00 2001 From: David Karlsson <35727626+dvdksn@users.noreply.github.com> Date: Mon, 1 Dec 2025 16:36:05 +0100 Subject: [PATCH 3/4] vale: add toolsets, reranking to vocabulary Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com> --- _vale/config/vocabularies/Docker/accept.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/_vale/config/vocabularies/Docker/accept.txt b/_vale/config/vocabularies/Docker/accept.txt index 0370dcd7c16e..8f99fceffc9a 100644 --- a/_vale/config/vocabularies/Docker/accept.txt +++ b/_vale/config/vocabularies/Docker/accept.txt @@ -290,4 +290,6 @@ Zsh [Vv]irtiofs [Vv]irtualize [Ww]alkthrough +[Tt]oolsets? +[Rr]erank(ing|ed)? From 78f5efd3f84d21c70392265eaf8b1c3958aac8c0 Mon Sep 17 00:00:00 2001 From: David Karlsson <35727626+dvdksn@users.noreply.github.com> Date: Mon, 1 Dec 2025 16:27:42 +0100 Subject: [PATCH 4/4] cagent: big rewrite Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com> --- content/manuals/ai/cagent/_index.md | 288 ++++----- content/manuals/ai/cagent/best-practices.md | 259 ++++++++ content/manuals/ai/cagent/examples.md | 166 ------ content/manuals/ai/cagent/guides/_index.md | 11 - .../ai/cagent/guides/coding-agent/_index.md | 256 -------- .../ai/cagent/images/cagent-acp-zed.avif | Bin 0 -> 42442 bytes .../manuals/ai/cagent/integrations/_index.md | 6 + content/manuals/ai/cagent/integrations/acp.md | 251 ++++++++ content/manuals/ai/cagent/integrations/mcp.md | 278 +++++++++ content/manuals/ai/cagent/mcp.md | 16 - content/manuals/ai/cagent/rag.md | 436 ++++++++++++++ content/manuals/ai/cagent/reference/_index.md | 6 + content/manuals/ai/cagent/reference/cli.md | 482 +++++++++++++++ content/manuals/ai/cagent/reference/config.md | 557 ++++++++++++++++++ .../manuals/ai/cagent/reference/examples.md | 33 ++ .../manuals/ai/cagent/reference/toolsets.md | 493 ++++++++++++++++ content/manuals/ai/cagent/sharing-agents.md | 96 +++ content/manuals/ai/cagent/tools/_index.md | 195 ------ .../manuals/ai/cagent/tools/builtin/fetch.md | 62 -- .../ai/cagent/tools/builtin/filesystem.md | 147 ----- .../manuals/ai/cagent/tools/builtin/memory.md | 82 --- .../ai/cagent/tools/builtin/script_shell.md | 141 ----- .../manuals/ai/cagent/tools/builtin/shell.md | 93 --- .../manuals/ai/cagent/tools/builtin/think.md | 68 --- .../manuals/ai/cagent/tools/builtin/todo.md | 116 ---- content/manuals/ai/cagent/tutorial.md | 288 +++++++++ 26 files changed, 3286 insertions(+), 1540 deletions(-) create mode 100644 content/manuals/ai/cagent/best-practices.md delete mode 100644 content/manuals/ai/cagent/examples.md delete mode 100644 content/manuals/ai/cagent/guides/_index.md delete mode 100644 content/manuals/ai/cagent/guides/coding-agent/_index.md create mode 100644 content/manuals/ai/cagent/images/cagent-acp-zed.avif create mode 100644 content/manuals/ai/cagent/integrations/_index.md create mode 100644 content/manuals/ai/cagent/integrations/acp.md create mode 100644 content/manuals/ai/cagent/integrations/mcp.md delete mode 100644 content/manuals/ai/cagent/mcp.md create mode 100644 content/manuals/ai/cagent/rag.md create mode 100644 content/manuals/ai/cagent/reference/_index.md create mode 100644 content/manuals/ai/cagent/reference/cli.md create mode 100644 content/manuals/ai/cagent/reference/config.md create mode 100644 content/manuals/ai/cagent/reference/examples.md create mode 100644 content/manuals/ai/cagent/reference/toolsets.md create mode 100644 content/manuals/ai/cagent/sharing-agents.md delete mode 100644 content/manuals/ai/cagent/tools/_index.md delete mode 100644 content/manuals/ai/cagent/tools/builtin/fetch.md delete mode 100644 content/manuals/ai/cagent/tools/builtin/filesystem.md delete mode 100644 content/manuals/ai/cagent/tools/builtin/memory.md delete mode 100644 content/manuals/ai/cagent/tools/builtin/script_shell.md delete mode 100644 content/manuals/ai/cagent/tools/builtin/shell.md delete mode 100644 content/manuals/ai/cagent/tools/builtin/think.md delete mode 100644 content/manuals/ai/cagent/tools/builtin/todo.md create mode 100644 content/manuals/ai/cagent/tutorial.md diff --git a/content/manuals/ai/cagent/_index.md b/content/manuals/ai/cagent/_index.md index bc0c4eea3b54..bb7576f3197b 100644 --- a/content/manuals/ai/cagent/_index.md +++ b/content/manuals/ai/cagent/_index.md @@ -5,234 +5,148 @@ weight: 60 params: sidebar: group: Open source + badge: + color: violet + text: Experimental keywords: [ai, agent, cagent] --- {{< summary-bar feature_name="cagent" >}} -[cagent](https://github.com/docker/cagent) lets you build, orchestrate, and share -AI agents. You can use it to define AI agents that work as a team. +[cagent](https://github.com/docker/cagent) is an open source tool for building +teams of specialized AI agents. Instead of prompting one generalist model, you +define agents with specific roles and instructions that collaborate to solve +problems. Run these agent teams from your terminal using any LLM provider. -cagent relies on the concept of a _root agent_ that acts as a team lead and -delegates tasks to the sub-agents you define. -Each agent: -- uses the model of your choice, with the parameters of your choice. -- has access to the [built-in tools](#built-in-tools) and MCP servers - configured in the [Docker MCP gateway](/manuals/ai/mcp-catalog-and-toolkit/mcp-gateway.md). -- works in its own context. They do not share knowledge. +## Why agent teams -The root agent is your main contact point. Each agent has its own context, -they don't share knowledge. +One agent handling complex work means constant context-switching. Split the +work across focused agents instead - each handles what it's best at. cagent +manages the coordination. -## Key features +Here's a two-agent team that debugs problems: -- Rich tool ecosystem via Model Context Protocol (MCP) integration. -- Hierarchical agent system with intelligent task delegation. -- Multiple interfaces including CLI, TUI, API server, and MCP server. -- Agent distribution via Docker registry integration. -- Event-driven streaming for real-time interactions. -- Multi-model support (OpenAI, Anthropic, Gemini, DMR, Docker AI Gateway). - -## Get started with cagent - -1. The easiest way to get cagent is to [install Docker Desktop version 4.49 or later](/manuals/desktop/release-notes.md) for your operating system. - - > [!NOTE] - > You can also build cagent from the source. For more information, see the [cagent GitHub repository](https://github.com/docker/cagent?tab=readme-ov-file#build-from-source). - -1. Set the following environment variables: - - ```bash - export OPENAI_API_KEY= # For OpenAI models - export ANTHROPIC_API_KEY= # For Anthropic models - export GOOGLE_API_KEY= # For Gemini models - ``` - -1. Create an agent by saving this sample as `assistant.yaml`: - - ```yaml {title="assistant.yaml"} - agents: - root: - model: openai/gpt-5-mini - description: A helpful AI assistant - instruction: | - You are a knowledgeable assistant that helps users with various tasks. - Be helpful, accurate, and concise in your responses. - ``` - -1. Start your prompt with your agent: - - ```bash - cagent run assistant.yaml - ``` - -## Create an agentic team - -You can use AI prompting to generate a team of agents with the `cagent new` -command: - -```console -$ cagent new - -For any feedback, visit: https://docker.qualtrics.com/jfe/form/SV_cNsCIg92nQemlfw - -Welcome to cagent! (Ctrl+C to exit) - -What should your agent/agent team do? (describe its purpose): - -> I need a cross-functional feature team. The team owns a specific product - feature end-to-end. Include the key responsibilities of each of the roles - involved (engineers, designer, product manager, QA). Keep the description - short, clear, and focused on how this team delivers value to users and the business. -``` - -Alternatively, you can write your configuration file manually. For example: - -```yaml {title="agentic-team.yaml"} +```yaml agents: root: - model: claude - description: "Main coordinator agent that delegates tasks and manages workflow" + model: openai/gpt-5-mini # Change to the model that you want to use + description: Bug investigator instruction: | - You are the root coordinator agent. Your job is to: - 1. Understand user requests and break them down into manageable tasks. - 2. Delegate appropriate tasks to your helper agent. - 3. Coordinate responses and ensure tasks are completed properly. - 4. Provide final responses to the user. - When you receive a request, analyze what needs to be done and decide whether to: - - Handle it yourself if it's simple. - - Delegate to the helper agent if it requires specific assistance. - - Break complex requests into multiple sub-tasks. - sub_agents: ["helper"] + Analyze error messages, stack traces, and code to find bug root causes. + Explain what's wrong and why it's happening. + Delegate fix implementation to the fixer agent. + sub_agents: [fixer] + toolsets: + - type: filesystem + - type: mcp + ref: docker:duckduckgo - helper: - model: claude - description: "Assistant agent that helps with various tasks as directed by the root agent" + fixer: + model: anthropic/claude-sonnet-4-5 # Change to the model that you want to use + description: Fix implementer instruction: | - You are a helpful assistant agent. Your role is to: - 1. Complete specific tasks assigned by the root agent. - 2. Provide detailed and accurate responses. - 3. Ask for clarification if tasks are unclear. - 4. Report back to the root agent with your results. - - Focus on being thorough and helpful in whatever task you're given. - -models: - claude: - provider: anthropic - model: claude-sonnet-4-0 - max_tokens: 64000 -``` - -[See the reference documentation](https://github.com/docker/cagent?tab=readme-ov-file#-configuration-reference). - -## Built-in tools - -cagent includes a set of built-in tools that enhance your agents' capabilities. -You don't need to configure any external MCP tools to use them. - -```yaml -agents: - root: - # ... other config + Write fixes for bugs diagnosed by the investigator. + Make minimal, targeted changes and add tests to prevent regression. toolsets: - - type: todo - - type: transfer_task + - type: filesystem + - type: shell ``` -### Think tool +The root agent investigates and explains the problem. When it understands the +issue, it hands off to `fixer` for implementation. Each agent stays focused on +its specialty. -The think tool allows agents to reason through problems step by step: +## Installation -```yaml -agents: - root: - # ... other config - toolsets: - - type: think -``` +cagent is included in Docker Desktop 4.49 and later. -### Todo tool +For Docker Engine users or custom installations: -The todo tool helps agents manage task lists: +- **Homebrew**: `brew install cagent` +- **Pre-built binaries**: [GitHub releases](https://github.com/docker/cagent/releases) +- **From source**: See the [cagent repository](https://github.com/docker/cagent?tab=readme-ov-file#build-from-source) -```yaml -agents: - root: - # ... other config - toolsets: - - type: todo -``` +## Get started -### Memory tool +Try the bug analyzer team: -The memory tool provides persistent storage: +1. Set your API key for the model provider you want to use: -```yaml -agents: - root: - # ... other config - toolsets: - - type: memory - path: "./agent_memory.db" -``` - -### Task transfer tool + ```console + $ export ANTHROPIC_API_KEY= # For Claude models + $ export OPENAI_API_KEY= # For OpenAI models + $ export GOOGLE_API_KEY= # For Gemini models + ``` -The task transfer tool is an internal tool that allows an agent to delegate a task -to sub-agents. To prevent an agent from delegating work, make sure it doesn't have -sub-agents defined in its configuration. +2. Save the [example configuration](#why-agent-teams) as `debugger.yaml`. -### Using tools via the Docker MCP Gateway +3. Run your agent team: -If you use the [Docker MCP gateway](/manuals/ai/mcp-catalog-and-toolkit/mcp-gateway.md), -you can configure your agent to interact with the -gateway and use the MCP servers configured in it. See [docker mcp -gateway run](/reference/cli/docker/mcp/gateway/gateway_run.md). + ```console + $ cagent run debugger.yaml + ``` -For example, to enable an agent to use Duckduckgo via the MCP Gateway: +You'll see a prompt where you can describe bugs or paste error messages. The +investigator analyzes the problem, then hands off to the fixer for +implementation. -```yaml -toolsets: - - type: mcp - command: docker - args: ["mcp", "gateway", "run", "--servers=duckduckgo"] -``` +## How it works -## CLI interactive commands +You interact with the _root agent_, which can delegate work to sub-agents you +define. Each agent: -You can use the following CLI commands, during -CLI sessions with your agents: +- Uses its own model and parameters +- Has its own context (agents don't share knowledge) +- Can access built-in tools like todo lists, memory, and task delegation +- Can use external tools via [MCP servers](/manuals/ai/mcp-catalog-and-toolkit/mcp-gateway.md) -| Command | Description | -|----------|------------------------------------------| -| /exit | Exit the program | -| /reset | Clear conversation history | -| /eval | Save current conversation for evaluation | -| /compact | Compact the current session | +The root agent delegates tasks to agents listed under `sub_agents`. Sub-agents +can have their own sub-agents for deeper hierarchies. -## Share your agents +## Configuration options -Agent configurations can be packaged and shared via Docker Hub. -Before you start, make sure you have a [Docker repository](/manuals/docker-hub/repos/create.md). +Agent configurations are YAML files. A basic structure looks like this: -To push an agent: +```yaml +agents: + root: + model: claude-sonnet-4-0 + description: Brief role summary + instruction: | + Detailed instructions for this agent... + sub_agents: [helper] -```bash -cagent push ./.yaml / + helper: + model: gpt-5-mini + description: Specialist agent role + instruction: | + Instructions for the helper agent... ``` -To pull an agent to the current directory: +You can also configure model settings (like context limits), tools (including +MCP servers), and more. See the [configuration reference](https://github.com/docker/cagent?tab=readme-ov-file#-configuration-reference) +for complete details. + +## Share agent teams -```bash -cagent pull / +Agent configurations are packaged as OCI artifacts. Push and pull them like +container images: + +```console +$ cagent push ./debugger.yaml myusername/debugger +$ cagent pull myusername/debugger ``` -The agent's configuration file is named `_.yaml`. Run -it with the `cagent run ` command. +Use Docker Hub or any OCI-compatible registry. Pushing creates the repository +if it doesn't exist yet. -## Related pages +## What's next -- For more information about cagent, see the -[GitHub repository](https://github.com/docker/cagent). -- [Docker MCP Gateway](/manuals/ai/mcp-catalog-and-toolkit/mcp-gateway.md) +- Follow the [tutorial](./tutorial.md) to build your first coding agent +- Learn [best practices](./best-practices.md) for building effective agents +- Integrate cagent with your [editor](./integrations/acp.md) or use agents as + [tools in MCP clients](./integrations/mcp.md) +- Browse example agent configurations in the [cagent repository](https://github.com/docker/cagent/tree/main/examples) +- Use `cagent new` to generate agent teams with AI +- Connect agents to external tools via the [Docker MCP Gateway](/manuals/ai/mcp-catalog-and-toolkit/mcp-gateway.md) +- Read the full [configuration reference](https://github.com/docker/cagent?tab=readme-ov-file#-configuration-reference) diff --git a/content/manuals/ai/cagent/best-practices.md b/content/manuals/ai/cagent/best-practices.md new file mode 100644 index 000000000000..3015aee0d72d --- /dev/null +++ b/content/manuals/ai/cagent/best-practices.md @@ -0,0 +1,259 @@ +--- +title: Best practices +description: Patterns and techniques for building effective cagent agents +keywords: [cagent, best practices, patterns, agent design, optimization] +weight: 20 +--- + +Patterns you learn from building and running cagent agents. These aren't +features or configuration options - they're approaches that work well in +practice. + +## Handling large command outputs + +Shell commands that produce large output can overflow your agent's context +window. Validation tools, test suites, and build logs often generate thousands +of lines. If you capture this output directly, it consumes all available +context and the agent fails. + +The solution: redirect output to a file, then read the file. The Read tool +automatically truncates large files to 2000 lines, and your agent can navigate +through it if needed. + +**Don't do this:** + +```yaml +reviewer: + instruction: | + Run validation: `docker buildx bake validate` + Check the output for errors. + toolsets: + - type: shell +``` + +The validation output goes directly into context. If it's large, the agent +fails with a context overflow error. + +**Do this:** + +```yaml +reviewer: + instruction: | + Run validation and save output: + `docker buildx bake validate > validation.log 2>&1` + + Read validation.log to check for errors. + The file can be large - read the first 2000 lines. + Errors usually appear at the beginning. + toolsets: + - type: filesystem + - type: shell +``` + +The output goes to a file, not context. The agent reads what it needs using +the filesystem toolset. + +**Important details:** + +- Use `>` to redirect, not `tee`. The `tee` command writes to both the file + and stdout, defeating the purpose. +- Redirect both stdout and stderr with `2>&1` +- Write to the working directory, not `/tmp` (permission issues) +- Tell your agent that errors usually appear early in logs so it knows to read + from the beginning + +This pattern works for any command with potentially large output: test runs, +build logs, linting tools, search results, database dumps. + +## Structuring agent teams + +A single agent handling multiple responsibilities makes instructions complex +and behavior unpredictable. Breaking work across specialized agents produces +better results. + +The coordinator pattern works well: a root agent understands the overall task +and delegates to specialists. Each specialist focuses on one thing. + +**Example: Documentation writing team** + +```yaml +agents: + root: + description: Technical writing coordinator + instruction: | + Coordinate documentation work: + 1. Delegate to writer for content creation + 2. Delegate to editor for formatting polish + 3. Delegate to reviewer for validation + 4. Loop back through editor if reviewer finds issues + sub_agents: [writer, editor, reviewer] + toolsets: [filesystem, todo] + + writer: + description: Creates and edits documentation content + instruction: | + Write clear, practical documentation. + Focus on content quality - the editor handles formatting. + toolsets: [filesystem, think] + + editor: + description: Polishes formatting and style + instruction: | + Fix formatting issues, wrap lines, run prettier. + Remove AI-isms and polish style. + Don't change meaning or add content. + toolsets: [filesystem, shell] + + reviewer: + description: Runs validation tools + instruction: | + Run validation suite, report failures. + toolsets: [filesystem, shell] +``` + +Each agent has clear responsibilities. The writer doesn't worry about line +wrapping. The editor doesn't generate content. The reviewer just runs tools. + +**When to use teams:** + +- Multiple distinct steps in your workflow +- Different skills required (writing ↔ editing ↔ testing) +- One step might need to retry based on later feedback + +**When to use a single agent:** + +- Simple, focused tasks +- All work happens in one step +- Adding coordination overhead doesn't help + +## Optimizing RAG performance + +RAG indexing takes time when you have many files. A configuration that indexes +your entire codebase might take minutes to start. Optimize for what your agent +actually needs. + +**Narrow the scope:** + +Don't index everything. Index what's relevant for the agent's work. + +```yaml +# Too broad - indexes entire codebase +rag: + codebase: + docs: [./] + +# Better - indexes only relevant directories +rag: + codebase: + docs: [./src/api, ./docs, ./examples] +``` + +If your agent only works with API code, don't index tests, vendor directories, +or generated files. + +**Increase batching and concurrency:** + +Process more chunks per API call and make parallel requests. + +```yaml +strategies: + - type: chunked-embeddings + embedding_model: openai/text-embedding-3-small + batch_size: 50 # More chunks per API call + max_embedding_concurrency: 10 # Parallel requests + chunking: + size: 2000 # Larger chunks = fewer total chunks + overlap: 150 +``` + +This reduces both API calls and indexing time. + +**Consider BM25 for fast local search:** + +If you need exact term matching (function names, error messages, identifiers), +BM25 is fast and runs locally without API calls. + +```yaml +strategies: + - type: bm25 + database: ./bm25.db + chunking: + size: 1500 +``` + +Combine with embeddings using hybrid retrieval when you need both semantic +understanding and exact matching. + +## Preserving document scope + +When building agents that update documentation, a common problem: the agent +transforms minimal guides into comprehensive tutorials. It adds prerequisites, +troubleshooting, best practices, examples, and detailed explanations to +everything. + +These additions might individually be good, but they change the document's +character. A focused 90-line how-to becomes a 200-line reference. + +**Build this into instructions:** + +```yaml +writer: + instruction: | + When updating documentation: + + 1. Understand the current document's scope and length + 2. Match that character - don't transform minimal guides into tutorials + 3. Add only what's genuinely missing + 4. Value brevity - not every topic needs comprehensive coverage + + Good additions fill gaps. Bad additions change the document's character. + When in doubt, add less rather than more. +``` + +Tell your agents explicitly to preserve the existing document's scope. Without +this guidance, they default to being comprehensive. + +## Model selection + +Choose models based on the agent's role and complexity. + +**Use larger models (Sonnet, GPT-5) for:** + +- Complex reasoning and planning +- Writing and editing content +- Coordinating multiple agents +- Tasks requiring judgment and creativity + +**Use smaller models (Haiku, GPT-5 Mini) for:** + +- Running validation tools +- Simple structured tasks +- Reading logs and reporting errors +- High-volume, low-complexity work + +Example from the documentation writing team: + +```yaml +agents: + root: + model: anthropic/claude-sonnet-4-5 # Complex coordination + writer: + model: anthropic/claude-sonnet-4-5 # Creative content work + editor: + model: anthropic/claude-sonnet-4-5 # Judgment about style + reviewer: + model: anthropic/claude-haiku-4-5 # Just runs validation +``` + +The reviewer uses Haiku because it runs commands and checks for errors. No +complex reasoning needed, and Haiku is faster and cheaper. + +## What's next + +- Review [configuration reference](./reference/config.md) for all available + options +- Check [toolsets reference](./reference/toolsets.md) to understand what tools + agents can use +- See [example configurations](https://github.com/docker/cagent/tree/main/examples) + for complete working agents +- Read the [RAG guide](./rag.md) for detailed retrieval optimization diff --git a/content/manuals/ai/cagent/examples.md b/content/manuals/ai/cagent/examples.md deleted file mode 100644 index a69d3f5d8c69..000000000000 --- a/content/manuals/ai/cagent/examples.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -title: Examples -description: Get inspiration from agent examples -keywords: [ai, agent, cagent] -weight: 100 ---- - -Get inspiration from the following agent examples. - -## Agentic development team - -```yaml {title="dev-team.yaml"} -agents: - root: - model: claude - description: Technical lead coordinating development - instruction: | - You are a technical lead managing a development team. - Coordinate tasks between developers and ensure quality. - sub_agents: [developer, reviewer, tester] - - developer: - model: claude - description: Expert software developer - instruction: | - You are an expert developer. Write clean, efficient code - and follow best practices. - toolsets: - - type: filesystem - - type: shell - - type: think - - reviewer: - model: gpt4 - description: Code review specialist - instruction: | - You are a code review expert. Focus on code quality, - security, and maintainability. - toolsets: - - type: filesystem - - tester: - model: gpt4 - description: Quality assurance engineer - instruction: | - You are a QA engineer. Write tests and ensure - software quality. - toolsets: - - type: shell - - type: todo - -models: - gpt4: - provider: openai - model: gpt-4o - - claude: - provider: anthropic - model: claude-sonnet-4-0 - max_tokens: 64000 -``` - -## Research assistant - -```yaml {title="research-assistant.yaml"} -agents: - root: - model: claude - description: Research assistant with web access - instruction: | - You are a research assistant. Help users find information, - analyze data, and provide insights. - toolsets: - - type: mcp - command: mcp-web-search - args: ["--provider", "duckduckgo"] - - type: todo - - type: memory - path: "./research_memory.db" - -models: - claude: - provider: anthropic - model: claude-sonnet-4-0 - max_tokens: 64000 -``` - -## Technical blog writer - -```yaml {title="tech-blog-writer.yaml"} -#!/usr/bin/env cagent run -version: "1" - -agents: - root: - model: anthropic - description: Writes technical blog posts - instruction: | - You are the leader of a team of AI agents for a technical blog writing workflow. - - Here are the members in your team: - - - web_search_agent: Searches the web - - writer: Writes a 750-word technical blog post based on the chosen prompt - - - - 1. Call the `web_search_agent` agent to search the web to get - important information about the task that is asked - - 2. Call the `writer` agent to write a 750-word technical blog - post based on the research done by the web_search_agent - - - - Use the transfer_to_agent tool to call the right agent at the right - time to complete the workflow. - - DO NOT transfer to multiple members at once - - ONLY CALL ONE AGENT AT A TIME - - When using the `transfer_to_agent` tool, make exactly one call - and wait for the result before making another. Do not batch or - parallelize tool calls. - sub_agents: - - web_search_agent - - writer - toolsets: - - type: think - - web_search_agent: - model: anthropic - add_date: true - description: Search the web for information - instruction: | - Search the web for information - - Always include sources - toolsets: - - type: mcp - command: uvx - args: ["duckduckgo-mcp-server"] - - writer: - model: anthropic - description: Writes a 750-word technical blog post based on the chosen prompt. - instruction: | - You are an agent that receives a single technical writing prompt - and generates a detailed, informative, and well-structured technical blog post. - - - Ensure the content is technically accurate and includes relevant - code examples, diagrams, or technical explanations where appropriate. - - Structure the blog post with clear sections, including an introduction, - main content, and conclusion. - - Use technical terminology appropriately and explain complex concepts clearly. - - Include practical examples and real-world applications where relevant. - - Make sure the content is engaging for a technical audience while - maintaining professional standards. - - Constraints: - - DO NOT use lists - -models: - anthropic: - provider: anthropic - model: claude-3-5-sonnet-latest -``` - -See more examples in the [repository](https://github.com/docker/cagent/tree/main/examples). \ No newline at end of file diff --git a/content/manuals/ai/cagent/guides/_index.md b/content/manuals/ai/cagent/guides/_index.md deleted file mode 100644 index 19ba2235b895..000000000000 --- a/content/manuals/ai/cagent/guides/_index.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: Guides -grid: - - title: "Coding agent" - description: Understand the process for building and publishing an extension. - icon: "checklist" - link: "/extensions/extensions-sdk/process/" ---- - - -{{< grid >}} diff --git a/content/manuals/ai/cagent/guides/coding-agent/_index.md b/content/manuals/ai/cagent/guides/coding-agent/_index.md deleted file mode 100644 index da20fd3dbe5c..000000000000 --- a/content/manuals/ai/cagent/guides/coding-agent/_index.md +++ /dev/null @@ -1,256 +0,0 @@ ---- -title: "Building a Coding Agent" -linkTitle: "Building a Coding Agent" -weight: 10 -description: "Learn how to create a powerful, customizable coding agent using cagent, starting from a simple base and evolving into a full-fledged developer assistant." ---- - -This guide walks you through creating a coding agent using `cagent`. You will start with a minimal configuration and progressively add features until you have a robust "daily driver" agent similar to the one used by the Docker Engineering team. - -## Prerequisites - -Before you begin, ensure you have: - -1. **Installed `cagent`**: Follow the [installation instructions](../../#installation). -2. **API Keys**: Export your API keys (e.g., `ANTHROPIC_API_KEY` or `OPENAI_API_KEY`) in your environment. - -## Step 1: The Simplest Agent - -At its core, a `cagent` agent is defined in a YAML file. The simplest agent needs just a model and some instructions. - -Create a file named `my_coder.yaml`: - -```yaml -agents: - root: - model: openai/gpt-4o - description: A basic coding assistant - instruction: | - You are a helpful coding assistant. - Help me write and understand code. -``` - -Run it: - -```bash -cagent run my_coder.yaml -``` - -This agent can answer questions about code, but it cannot see your files or run commands yet. It lives in isolation. - -## Step 2: Giving the Agent Hands (Toolsets) - -To be a true *coding* agent, it needs to interact with your project. We do this by adding **toolsets**. The most critical ones for a developer are `filesystem` (to read/write code) and `shell` (to run tests and builds). - -Update `my_coder.yaml`: - -```yaml -agents: - root: - model: openai/gpt-4o - description: A coding assistant with filesystem access - instruction: | - You are a helpful coding assistant. - You can read and write files to help me develop software. - Always check if code works before finishing a task. - toolsets: - - type: filesystem - - type: shell -``` - -Now, when you run this agent, it can actually edit your code and explore your project directories. - -## Step 3: Defining the Expert Persona - -A generic "helpful assistant" is okay, but for daily work, you want a specialist. Let's refine the instructions to define a specific workflow, constraints, and responsibilities. This is where prompt engineering shines. - -This configuration defines a "Golang Developer" persona. Notice the structured instructions using XML-like tags and clear headers. - -```yaml -agents: - root: - model: anthropic/claude-3-5-sonnet-latest - description: Expert Golang developer - instruction: | - Your goal is to help users with code-related tasks by examining, modifying, and validating code changes. - - - # **Workflow:** - # 1. **Analyze**: Understand requirements and identify relevant code. - # 2. **Examine**: Search for files, analyze structure and dependencies. - # 3. **Modify**: Make changes following best practices. - # 4. **Validate**: Run linters/tests. If issues found, loop back to Modify. - - - **Constraints:** - * Be thorough in examination before making changes. - * Always validate changes (tests/lint) before considering the task complete. - * Don't show the generated code in the chat; just write it to the files. - - ## Development Workflow - - `go build ./...` - Build the application - - `go test ./...` - Run tests - - `golangci-lint run` - Check code quality - - add_date: true - add_environment_info: true - toolsets: - - type: filesystem - - type: shell - - type: todo -``` - -**Key Additions:** -- **Structured Workflow**: Tells the agent *how* to work (Analyze -> Examine -> Modify -> Validate). -- **Specific Commands**: Gives the agent the exact commands to run for your project. -- **Constraints**: Prevents common pitfalls (like hallucinating code without saving it). -- **`add_environment_info: true`**: Lets the agent know about your OS and shell environment. - -## Step 4: Adding Multi-Agent Capabilities - -Complex tasks often require different types of thinking. You can split responsibilities by adding **sub-agents**. For example, a "Planner" agent to break down big tasks, and a "Librarian" agent to look up documentation. - -This mirrors the structure of the `golang_developer.yaml` used by Docker's team. - -```yaml -agents: - # The main worker - root: - model: anthropic/claude-3-5-sonnet-latest - description: Expert Golang developer - instruction: | - (Instructions from Step 3...) - toolsets: - - type: filesystem - - type: shell - - type: todo - sub_agents: - - librarian # Can delegate research tasks to the librarian - - # The researcher - librarian: - model: anthropic/claude-3-5-haiku-latest - description: Documentation researcher - instruction: | - You are the librarian. Your job is to look for relevant documentation to help the developer agent. - Search the internet for documentation, articles, or resources. - toolsets: - - type: mcp - ref: docker:duckduckgo # Uses Docker MCP for web search - - type: fetch # Can fetch web pages -``` - -In this setup, the `root` agent can call the `librarian` tool when it needs to check external documentation, keeping its own context focused on coding. - -## The "Daily Driver" Agent Reference - -Here is the complete `golang_developer.yaml` agent configuration used by the `cagent` team to build `cagent` itself. You can adapt this for your own language and project. - -```yaml -#!/usr/bin/env cagent run - -agents: - root: - model: anthropic/claude-3-5-sonnet-latest - description: Expert Golang developer specializing in the cagent multi-agent AI system architecture - instruction: | - Your main goal is to help users with code-related tasks by examining, modifying, and validating code changes. - Always use conversation context/state or tools to get information. Prefer tools over your own internal knowledge. - - - # **Workflow:** - - # 1. **Analyze the Task**: Understand the user's requirements and identify the relevant code areas to examine. - - # 2. **Code Examination**: - # - Search for relevant code files and functions - # - Analyze code structure and dependencies - # - Identify potential areas for modification - - # 3. **Code Modification**: - # - Make necessary code changes - # - Ensure changes follow best practices - # - Maintain code style consistency - - # 4. **Validation Loop**: - # - Run linters or tests to check code quality - # - Verify changes meet requirements - # - If issues found, return to step 3 - # - Continue until all requirements are met - - - **Constraints:** - - * Be thorough in code examination before making changes - * Always validate changes before considering the task complete - * Follow best practices and maintain code quality - * Be proactive in identifying potential issues - * Only ask for clarification if necessary, try your best to use all the tools to get the info you need - * Don't show the code that you generated - * Never write summary documents, only code changes - - ## Core Responsibilities - - Develop, maintain, and enhance Go applications following best practices - - Debug and optimize Go code with proper error handling and logging - - ## Development Workflow - Use these commands for development tasks: - - `task build` - Build the application binary - - `task test` - Run Go tests - - `task lint` - Run golangci-lint for code quality - - ## Development Guidelines - - Tests located alongside source files (`*_test.go`) - - Always run `task test` to execute full test suite - - Follow existing patterns in `pkg/` directories - - Implement proper interfaces for providers and tools - - Add configuration support when adding new features - - ## Tests - - Use Go's testing package for unit tests - - Mock external dependencies for isolated tests - - Use t.Context() when needed - - Always use github.com/stretchr/testify/assert and github.com/stretchr/testify/require for assertions - - Always provide practical, actionable advice based on the cagent architecture and follow Go best practices. When helping with code, consider the multi-tenant security model, proper error handling, and the event-driven streaming architecture. - add_date: true - add_environment_info: true - toolsets: - - type: filesystem - - type: shell - - type: todo - sub_agents: - - librarian - - planner: - model: anthropic/claude-3-5-sonnet-latest - instruction: | - You are a planning agent responsible for gathering user requirements and creating a development plan. - Always ask clarifying questions to ensure you fully understand the user's needs before creating the plan. - Once you have a clear understanding, analyze the existing code and create a detailed development plan in a markdown file. Do not write any code yourself. - Once the plan is created, you will delegate tasks to the root agent. Make sure to provide the file name of the plan when delegating. Write the plan in the current directory. - toolsets: - - type: filesystem - sub_agents: - - root - - librarian: - model: anthropic/claude-3-5-haiku-latest - instruction: | - You are the librarian, your job is to look for relevant documentation to help the golang developer agent. - When given a query, search the internet for relevant documentation, articles, or resources that can assist in completing the task. - Use context7 for searching documentation and duckduckgo for general web searches. - toolsets: - - type: mcp - ref: docker:context7 - - type: mcp - ref: docker:duckduckgo - - type: fetch -``` - -## Tips for Your Own Agent - -1. **Iterate on Instructions**: If the agent keeps making a mistake (e.g., forgetting to run tests), add a specific constraint or workflow step to the YAML. -2. **Project Context**: Hardcode project-specific commands (like `make test` or `npm test`) in the instructions so the agent knows exactly how to validate its work. -3. **Use the Right Model**: For coding logic, use high-reasoning models like `claude-3-5-sonnet` or `gpt-4o`. For simple searches or summaries, smaller models like `haiku` can save costs. -4. **Dogfooding**: The best way to improve your agent is to use it to improve itself! Ask it to "Add a new constraint to your configuration to prevent X". diff --git a/content/manuals/ai/cagent/images/cagent-acp-zed.avif b/content/manuals/ai/cagent/images/cagent-acp-zed.avif new file mode 100644 index 0000000000000000000000000000000000000000..258e751effaf74ad0f8abffbc90f58099a1c4656 GIT binary patch literal 42442 zcmXuKQ;;ys&NVu=ZQHhO+qP}nwr$(CZQFdtGw0pksXwXeBx|KQx#^3p?f?J)5SY1m zIvBcHngRSL|FgE1W{kF$hGw#Yj6(lGPqwBmhX3RK6AB9x8>j!@1psicGUQ51y&A5MzNo9+U@>2O)%%-3+Ml34o1 zZp@qVC7{>jAY{UxPOyP}@f@5XfjUWo1ODcw8>dcI4J;d&5X0G<*>$DTWKZFe`=&Z_ z4y5&2^yS#PsrTHDnkN+UsG&c5Y@^hwbelvr`;gz|4M@F)h=>VlOmk$?P)g|eGNO)$ z2Se(X5pI6{(kFaudspJ9ORJd#dYefA%*M5bI3gm+`o$2(g!?_O$Pgs#Pagl&YbvQV zab&0{v3(R8QMyXZ6KF2SRmxDaicUPf7vr!F1J$VYr+l@7qRb;A1gMRM+402y_W(Vw zoo7eqmp#p30hdNV<^Z_cV|{>lciwLWeZpgIy#Bn_b5gC~XdIG1tkw%N*F?Xxj^K0l zNp~j-WnqX<0#*10f1^qY3*I^{J2DpQyeHkv^nr^zWeEpgfR zcHqJUGL=i&A7v^-v}7$}O20Qnb*>2Ix-z2ZCP!8a7tQKJdaouvC|d}UfA#YSsyEF_ zw7n;VPNp9BbsNZ5)9wl`p56^eEZvn#<7$cN-kqYkT>1(5NM>Z7f7QMqTg|_zzjw~8 z%nan)5@i3GM{G2u_o?#xl9q$fO{zO#(?m+oOOb^?lkQ0UMoIhgGcjn(^x@}xC9+{t z^e);d-P&3=EUUweZwXaSs8VyrO`H1{H}^fUpXxt}ne($1tm5_O?t<)wL9F&!WN4ig zJSLlfQM%@?N%S?i_tk-D7cz{?m!TiihpZkoVxBaA`F23#x>yj% z>D*#A@Ia@=?@z00-~e?nnieprY|g8lY^MFLdP%8sLHRYWTHV-XH4MzrRRW#{zxCwF z-6p*Z{S*~7PP!h4XcNM#)^GtNP6#t8-S_m-wF-pLl~`6rG>fNut@^)J21Ig<1V)?5 z2vr-CFEy@tYe|`Df_H3kC(;w=C<~JmsC1rhK6(Bf-l#X~sk{i{#aH42$J&0PHj zoof=uH-=2lTpR9RMmH|yBpuWockVpH+us&{q-T$H+t&+QIzRx6b(K3=kI za|N7&%hZSCkAKBY;GGsUa^0x0zTwr`g7)O{F0iZ3S(%aN%So1zV9WX8KbX7IM3b%d z!2d)U88tWf?7HH-0flToc9&uf&E*60Pvu3$uZXD$h<51D%A{p2%q}PqD9kLb=#dzl zK*JHPU)j&Vi;ZMoKS!pl-kGmIb&(!qur)!qX1j3?{W#&a3A_0XL#izN`b#DqNZZj^ zcFa7ZaR$cYSt1$z{EdQaPKzl3%s<0djRMZ6sA3FP(2^n+Sd18>6I5#Q4{3nHago1~ zi0Zek+a>lcJg*@fkMj`#OKuC?$;vb0hM%o)*G=)w#fJqX!iOp1&xR>*O6*~utWe9r zXt#ii1d_Lb@UgpVe`vBx0Y}nppk1K2&jV3mg7Z0AcWi?&4?O9bKPUWvT3PgLXLZpU z#r2|2mrJ7uGt8QR&6RN>MI>1>>3bC273K~P+$#>329I_fjG={NePV2~LagGH(3 z26pUt%LDM_9{n~F zf%3pwJV@;U-}=W3BRlE}^%y~&KsgyCSpSJB2mt%BGVk>i_^1o)nmJc^q^F=@N#0G< z`BO;r7RhO1BHp(JEa2T-XGj0pT3cR7YBKsv+XX9turr!pB6o(az~49&U>5JK8RK1% zIHzY;;@Hzptz3;e&f%om8<*E)x&>1?RZ+F{`_?_{xLe0By@Ob^CM44~AOGBxT>3ph zgd$T}Y(}5eqwPSK!6F-lzqi+FmC4mLqCWBaZDs!RxQc?dfX9+=Je}qR7I#aHecm*Md zC0!>k%^cC9#-AeSP@U_K1S-`!#?NnK#}N^Y7#`iy1=9ZZVEsTpMKoUHjG0H#ewoOi z#g4W3+OlE68K0!BQ1Zz_bVaSGwe(h_J|{WA$E*wTkJV_CBd!^$HRq+WEqfxB6g3hE z4b6Dg&?{OpEqB>9BR*gj^XVhpDio?u8;h%S>kEz$1QzSltft?R>Wy(Yv%5?d?sz!ydXEDlfjj`zlu)B!2x>os{WZt%YdMfoC2>-s zMigQ3`SFeM#z5!g6JNHZG6LI2eoTF|Y^V^r{L5L#)5EOjrrb}*(#msqkTL=@`aBGR zE%wSg7J0B%hEL&bb>HNwsNAVZAqXzuqgm1D$1viHX>yDnD-lb+UN%g?CTt~Lyy@79 zP7Bh-(y4^`kWhM3Uib_V0HU2TMQqlL61KP_^QJbXzZdJJaXn1)2jQ+tuT zn=wwA(SHV;G!<=E*IvX>{Qh-^xHt4WhG0WeT3}_}-*vzKjufq~7UynS6yViaN-l!= zB8^qoCSof-5ZhrmOruC5?A9lOy`qDI<~i@&ZKwBsz8Mum0`eB#=qf+4Y4g*6{Qa*n zzG5&%AykXo{xM*e3E&9~lFKH&#y7ApCav{$BT+TPL8Y>`>yCko$PT6lGUHY6ls~0DVKO6AcXxg1@^PSuo?bB^W2%zM^kD+4=avE z2@>1k!J$kenmwoc=RxXo`NH^^$W+CvwuxaAhci(hTa-R5c~=T0>20;_)l6Uk*8A+iL_j&RB1HF-CE|cbQ+Eh1Eh~mDyl0eAADUYfNv6?;(Qxq84h#L2nCxnOE>uSZISKuE&|WpfTy zwT2~TJoWG|!G!Lt5Qvc{VlBqAN&rfd{Mkx!Rnf_#$qAn7+Js426B0zNA%C>AKCnjZ zy_hT{mV7NzNZ5TJ=x5Z)Ktr{m!43j>h&q_A(LTK@plD$E@zE+|3z&4O_t3;E!MZOU zZ$i;R3DMneXPVEj0Mpw#_ZC&or zhS!86kN`DVgW~k}WG@;DpU;FNGHJ8|b&f3whr}`KV`KExAQrh{*agy1JD*9KGyAiV z9G~4epxBd6cLnouLzc(_bZj>%YFG)+S8IPM3olz2C7)B!6me`;D)(>;Lr4R@tZJ|4 zKB-tUa)#cI-E_}52Ds|gi?i+Htn$DrhI{c)!%0Hm)2+LygZ*ekYPB*3XkaG`dJgK9 z*o6fe7gn%Ea~yV$pJCC+&&(+KZWd3H#jkd@*CA*;xHmHvA%D0=mA~Wv6r2?o#S1Wa zX7K;x({4!A+G&$$xU~@9x@hFP54hebvLMbp=3XE@e~)~&sc3jyy^Fh^4wjd}5FhA$ zo1a0g7A~MVrFk;in`45i5)W1$C(&T5#hOc#z?b+X51dyY?FuT-4t~kZ?AB8>E81{k z{2a55+^Q`af+$0PHwOfJZF*6!#TAyR%~rN#G?h0D^Lm8d{d*WX2KPloVisjGW<@%2 zsIf<2;k?KgpK(#)u;L+dr236@0W!eP5CBLcU-ML)QAKY8?I*;70|%EpVsc+q-x%9a znp+j70t63S6fc$}9X3UbZLBa8x0 zZ_lf>eiyUw2naI5954J;f2=Z(0w!vgERITMpGa4`?0DT6j?6m?oy2FyH?19nm6^Q{ zWBR61H8E|2VNWMyZOPNGhD^tIsZM@RlGdpn|4x!SFoAPWR^Na!-FuVyq%oQv(H zydn-G3#zAE8CLcFU3G4RPAQ~YNB*95-<|8mS;e!&&;MgJ5)oIN+eh5>*DjxuQb((c z!GC$$S=Z4LnaA=6@$Vj4)!z<8LKK1ejb_MJ4Qbsi=Y>x$LMV>8$a>#EsPv&Z~le zm%3@iQ=EGjYy<@XV!|HIL{k;y1|Tia5tw&V00&Vs-J1C-1W71E0q-++0Y8yRr{{Qe zk%0!C-?YB*I&cI8J?ML60_B{pluYO3Q-Z^Dqd=e8ybDe)ven*3j+j~*)Kd$#-W%H6 zQWpd3NK8rQC`G%BJg_s#MB>D~jSW|VTpUgOYi3!oKH#hiQEPeNYT4||JN9_>xOnf= zvVM06A(~5Wf=L0mutl#eFTM*Ej4m9L*aUe$YY7#90cGZs z2IxKo{2X9k2mkIQt%ja;;7*xZzm_(>gl|WuvbufUC?QJq;BU-f-I>f{nh52`kM`JL7R1C9ipwny@`aIDjD~5*&2amw;z>nlcWG< zsAfv*q{>&GZ(9Q!cX-R;bV`@wjr^f>@8gf=mkqQ00|;qw9f~q4Os|WF%jPj6Egiv+ zf85ghN?pYAEKNCdRB5_D2;zicXlL5+QEl7>{hQl55wYfY51rsauA`*?I^vbrUK%xq z`P9Y&Tkd%5{)&d~U@(g;EbIqwwuS#EB-F;>O$rOHY1KJsj^J+$NsH=NHcHnWkY)K# zl`k)AaIaws6Rd+tFmnZ1u5r&%6OI9s@y9cF*k5h-K)L}y-DU1*E5C3r1bHRp3=_R? zZ_@mLa7-8k_LeFzg>0jym%yG~?QWa3n+{6GFQHI@b_PR0YMpkTLh>!;wTghm^5>uS zE~+ECA5K&W*g&;48Tm2>b9O-YWNVuc_#+>3Rlcw^Pe_yQKp~cT2&&g`Lm09`1d@iu zGNlB2_EW?>2&e^-RHBRubS!m>S4J&v+uWYMdIE>oxx{`#l8O8(2hY+0Z!;nDfCL4r_Do?eU>#m38fDp#=U5IK0`1Dq$M_`IV}`=b3Ml^M6CGOnw9mPn~Ns{}i5SC7&=>%%ikkl(l0V-Lo} z>Hg$db1~JOna?G#tUxYjI3!lnv)Va456D327Y@9H5sNu( z^1?5+@Db@tUsbb+G*TVO4GTp8=MA#wiqV!F(3HTw1LQR3xyN=LTY+@-l0Q5PwbSP`xzI>J1L0_HbGB>RjG$?9Ox?93Go#A46%Fy|m9%*~xlc5O4 zzV}MvMViiA*+^Qlp+zd1`Z!om?ckuKOoA1=?Bf=Pu8#iJ{B*pSEjoh~6xuZP3Vndanu-^svovRS zoPMK7;;nArep&LzBS1Vi-e4_0cJh|o4Gh8TC=<)k(S&7DxHbeN0~8T37g%@a`+(7-49ALXtXU+Qvd^({~<|*ABeAAji1l zu(27sb1{9PeZeqRsg_Z6see4Z|0p9k)GA)Q0YFi&p8r~4wt`9BNsE#^(r$fs!E_w! zBeLp}?5niNq&|^eh=9RtA4mR`%Oc5ZOLP+HBEaMEq&VvRleCKn z*39;UlSc0~3jc|`5oZFl6l(@|!#41d`(k1ijO6{A0F4gmMCanqvX)0@$5rhI59#Rh4ylnfu2KMqT zq%27O$Lt0xYD8KosY2>HAL78ivmUGz&pcE0w+NW6gEV~lEDF`%@LtwUWnkx=$PTI_ zXrlolkz6op(iMXkNM^12e9Ij*Aa1i=3p)r~?0WkGk_u^|DHyc?O1<1w{-I+FGEj7R z+ZXQ4qFRoarRE7=E@$~Xo-N>{rbOk*9@A8Mq@4Wq^^ZPpzoz-aI)H6`a|?yK+{5#| zFnugKtnefsKZwaP9|PD^=;)7TA`WWXXVgjzD43+p0W3B-Q~hMs4xw*drUZu)JXeS! zo!fe5iT!hIK<;cYtU28C*`~>&qKN02sAz2yWA5yi6z?36WDlC%#}oy|wJSBGZZrf{4iJJ$m^1IjpI7NwJSr@ro8Dr zs5F{>w`OB!GH+pZL#gg(W11l(=C$ccz99`(ni^QIl6ufEvB5*PE;+G09)gs8+--5k z?#NtgpLbU)EL73Jmf(zYnoZL(;0G|>IVV!7ws9?Db% zL3WL>%ag#`bCwfi>g+H0lw5SBJ(7)j5tJ`uy-SLtFIHE=s`Vv82|aK>)O-DQt499E z-%nm(p!Un4d_V;jn}hVQdsOHFcvl{>HE!J=rnk_$({x>9pFShBP8V!f%vV+A;?kPt%^XJRU)6z z=IV?q3VA^5pyWZ+vxl$)M7fTwX_NYf46V-uC>+lKB5|9LX}onmGzjwS570Q^F{720 z?OB>z~E`bn*7H z2FefryNvCl{p*qYD(qmrivX6~XT^8n9g0JDeBcW}YYR<0EBL}2bvKk~CBzI>)+I^z ztI>#KU|6V95vrjBxJ1gr@jcMceP-WiTUvrdt@pbT3~Mi>*70B&7$5hCEb*AAcGRyf z=cq4#;aY>(^NFCM!=Z(Uz1`rU2b2XjF}Q>Rw&r^L@W!f8#k^vmM_IY`=sj3V!gUGCUlsuI23<+&7KN z$4Sin{K{gCH8m1fJy2T=!bX%~c$aV$fGFTNP5?%qFznq0RAh&$1{Q?APqK8DKS90*WbeWot08MBTPw&hb|b)qT2kUIepAC(c>dnVd&nu z5803OYoHee0{ck-@N=bI_BGc$9s`b;WMbB8JTNo`V~f@3F@`s^u{v$G`wuhm(?Mgm zDc;@urEu_3fezBl2?YbY7k!smj_*6SEe3dh*b25@*j*~G(YMs<6TaYEQXcXC=8DDY z7U)jAD+I+u}2>CAO55we^cXlakD~2uexIw~iiofehBCTejA51Bvi$v3LO$e(hXs~BWUm=%R0ZRIsjvVU{|8Z zs{(j|s0=CyKy?~mrqm+{TfN~+i35B4FsDmv#0BuzV7abLQ5_d`H{aM@(=#>?ub}X- z>z0W13PF!Ta6MXvMt4a5MmYu!5(F?2AS4(oj`!v9`61tXKIjPbdk@uq#%_elgtq{e zUxWNaab(2l{jWR)su0{#y+yj@-*^>lMJAIuYQci`vX`KRsWfW) z12%9)u$e6C1;;|#W@2ZG5h*v3bIUnXqvF&WijOP@uGYUx4gjH{OB9MMfVlZQZXWPI zPK@FCpU@`MkgHuzWM<=aT|)!DWvo+r^nbUcoCZrHsadKtwv?E(Ab&F2oe5ho33}^3tf+&OzvoBHAqgNXx1)*;GyogJ z0DyOI#p)0j(Lgv10y!GHXV*Wo*-~2WZ_xMS!u5S8oxWUXV86*li&OMxa-g^d1GXE$#$$s;S9y&2v>q7hx!J5SY2EibHX>!PfX$)-Mu>EghWW$% zia#bP+I{>5g|WSs(2kVVFd)QrzIQJW-L?dRh6Oy`rOL1umn!<|1WG(n206K{d#slEc?!i6Qk1YX=tWke~p2@#BK_m>37`wCxex_RBhF zZ?hb>4A`IVWLJffP@IM2uSZJs8K|0noCztM4MSk^o&=;|!l^eIY=dBMZ-5o{MZ2Me z0)pb^9FxjAs6VmH|PcC9gdzdPcuL} zZ#?hT8p^vXI6C4 z>#nlPgSAJp2Dx8a8*c8qwz`m>ZrH;r_JZ;K>~7_&g93ttrIiyD*oRh@ zZVbb>B>vTAo<_?Tl)|ZUpTUvaXTF*}=_(kV*`IWw1R($EwP`f?#d}=%Z*#ayfkAXk zBJeSIDv)Ts?<8-K}o8U_}4mP1~Q*b0D)s94D^sz`OGA~I?s5qO6 z-7kb0XaJDTFTJ$bLZ!m;xht1qtfC7n^j@b*uS35Xc_)V_7;(5g_H^oVY*5sy8 z;f_$@*%)}JJRQ&xXK?L(=`DhfC3+e7QO0(q`^BFkX2#gNV^3EP1*K~u-}cwD$jV*+ z8u%6U+u!ok2QmKTXb#VyN`=IRhAInSp$X zU7DEbT$bMusH=$RYBlY7Re|R_Y<%m8s8clzisS$f@rsW3uBj~34w+MNAA*y>&VI*5 zo|)Z(S4Adv`GHzsI;Q-iV)`O}j?FC^fmvgUYl^(zZ#4RsEf}K!4cN~l8c24k$d0Vn zQ7&U0+zjtz(hmIK?E!)bT6ml=e-@*dWS#D2+^hG5dutD29ZB>)wBx=1unb~0D#Vv#SsFQ{Qtn}M z{NZ{$m|nY2Z9Y^|AmIOlx%$^OeJURczLx+=o1jAw_c%)lbm>L}?lHJ!4XThb|1sJk zVPHQ-vTwap2qas7E!Zkl8gm1OJUVM4qQpRRlNM(|yW}L8@#xe{ykxKCP5yAc*9l85MdjOhT z3O0|rK^JG6zPRNOadqie3aRuENj^rbW)6Efio-;F(!fG(_HX9m>@kb1wGWGW3r`-K zoRH~TD#!r!kidjej#QVf&6dNz{1djAWpY=8b)z3;QA#slzv?G(WF#{jpOlNZstRRu zq9zFH5%(nHuL@Ru9e6xw)=%N+L5U8l?Myo}X8vrf_D$HOG`ilz$O68lF82<{leV}X!^mWE(JYD;)&uR>DMeXb`8 zh8RH7;xHU@(IzWDuMA6fw4!V2m*5C5xX|9Zq-+gaT|G*2!nQg3(AI{4LA+Vpms>D{C zpkk-ptKP+=X0FPgl3@i%F^udVavTO5;vx@3IG0Mzf$RV^;Y%ni zmVM2&2;&}r`?%D|JJGT}<()x`nN18BLBAk+>d9;0CyQD4j_I**S5+Q-d&84ma`5Rs z7eB}*Zo%QP?V&r_MEEFhsuOP=EF{s>h;(sq=DW)a+g$eSMrJ@EZGxF6KnGIf^wt&n zByf9P^&wSGL9VF7a{QE(Gq;&T{y9+7ujVcQa0-R@Z$?T6J z+*X5#y8;34t8jP&Vp3bEadiT@JmO2AyHFpEwWM!F67=<~Mf03p*WTzj?k)9*hp{f__qLth3IHdV;FLeU`EsRJsY>3K4CiH1QJB9+u zrG%_k4-u-E(%ne_WRL6-B$+iY6kQ!^h7hX-@Trq+W708Ae7Y|ClYHpaC^~&q-I=Ky zjN0BIDS~)>e5*RR%z1VuS8M_mszf!g#`la56uS3R=*itZ;7VCRr6bnqqLV4R^q2Rq ziXh?KEQIY*88Bk<&WyAk$~1LR1=KD2ZauC7I!X(dF{AKp_-sn6!m_JdKpx_{kseYu zW4$g2&|u~98O`vdZ#M&o3}akgtdTr=nV zS@f~HZ>wr3L6%Sz*v0hH7<24!)gM)LZfP)_o8Sd#b6I2L5nb)nvPBefvX4e)hXm85 z*sjjy$h<+iJW$$dOrP5zF1JYtu=9*g3(!(+7RsfVRhUWwA}R@rG#Z^2zW8e6-P*|* zKJ)_3r#$aotF&{VFa)q;D4~KX7ZZ#`s>s*CR2!xuszDlv8QL*}t8mPr_1E0P^Qtgb2&Fhg(A9}UasZ&a0{M!=`2?ntQ( z6o52e0nQ_eOElVK8PiPTdcrkuALz`}n13$6S~*c@J4HQz=Gx+1hvm@HiWro z?$kq(HXo?zIXO_zjO4f}eqC(}{^P1MkZD=}!XK+Htq@UrLh5%nydVaPe&=H-d5HD- z`TUwrk0+s>IeO;NPw8}o{#x9MXEOIM5`9H0ChX*%9yNI_v9cMX;_|KO(G{9h66U~L zh9c8W=wz&1GKhm4|LDiQHdA?3P}(|7Ue6OxrDwq-Jg)}$Bn(A2pKP{Sq6W=kYf^5l zI#)}U$K}n%Pi*^U=Vyegj*_w=K|5E|Bx$rkjK7AJP{zE4cDeEKPSw8&4Ebh?$s_bY zAX`lY@HxnlVTCV{jW!U5qr83eNnOjf{APSYTuP07z8ovF-$a_y#*>QRD7sarX26<5 zL*c*uwTWpSJbOY|Nq7iD-qYi^A2ZIixxz!d{jA{x%ONoklQt*vQFg#eI-1s(r~)T< z@!YxAX6oPEObxwqaw(Lx=zjz)aSWly2-zsvfuGK!VY%Q8yHp5a8Nl7Xx^*{nnulzA z8)lRs2UU*TJ67ubj&;&?8JK6Wozs3`R(=g>Zl#gu^6z%Q&sECI-~$0e(N*@t)ANaUDcI07?%qqkkMaqQjmgOW}TEGxsJkFJLVSHh_s^+F?`O$4-n-*);g3$)}-!rDpN33=@45ti&&|*^XzZO zz!CpyMM3Oqy3lk8m)iw)DdPCek?Do5#yq97G4dSf#vh->0X1@D{p{K|Qy$I>j?>6S zPe$Fp6A3qRqR&3VsmjWQ9MB<-FJeG$*muCmXi_b47wW``KYCe2V7h0OP_AdrLml{- z2_s0WpnaH};8fZS3;dVd&8$8_HUnyY&r;On4T~?pD*_y6^f*#_*6Ai|z?-WL@>}B$ zpkN9c@qS`9)42_{_=ja4i|;s3S5`eE{I45L=Uc~Y@n~1WpTF7A>Yv61LKA)Wzdf5? z3)xS|xiLisyb5+4qmu*3qV_J9?A73GYjz-2^4Ky2@43(92To z_~#6e!1MyH7UqpQce@O}bvNyP4f^u7XoXFWY_ zVrVw3d-E_PcB`Y~8Z;SG>#mEunjSc|1{OQ7T~bw@6y8ao(9%Pm%I6=|jP=pG3Ghkv zsm>JO@(xv+9fhb0Ur(eUHxHGoLHzv&+^Iq<7l}(FF^Lox24$(Dik_G^yk8Q}B-`$o zetjU(w|^IrV>Z44Reen;lfx>L5LGqkJX)rwj2D3i1st`fX82}{PXj+wRn7S~W`lp_ zzhLT&Fu?DbY?@(}6F#|0b-1&>`R7U{ND4P&fh;i>vvZM;eE;IuOlF4wN0msi5u**l zYU5cq{1#xj7^5l9M@T(IKO{|lO$jaEhk?6|cIdaLQD=wXFGCiG@VOc_!IC(!u0pOP z_2BaHmyPe+TAQi>kw5ReU^qE4?zV?D!4%0 z(@_S2vr1Z7e}7hQub0TBqWTzjM$0FW4JMBnkSL=U}Cvq zdXOA?-q^L4k*-J?5NdwJ&ib~@LH_%%YAtE8`1hEWVMP>JY~`|CD5IL16AGw-FgAC_ zA*Mssoc9Hgt26I=xOeWl^^ChuY>62?M*@)lQ-pjnPtWGAbDuXtVZtFRRyuRN!y=v7MI2gN;zkMNLNP4l6~kV<$!#*MblpxNNL? z@Wo4I<4F4!QC=}CuA4b#h%F$^$??`ySFT3AVqq}c$!SJU4vCNQV|GfGK}BFvc>P9{ z@hwG)RsjfaQLV>(rFvWCzDWCaV%#c+Ku5fgWp&7yq|)xNg~~JQ{O(0z-{sk%tHzI~ zeJ&{A-KDD7Kq+#mcK2c@_p(PdC7>mdoF1O)hUFRCbhe1l)QqMxhdpwP1jl`e^>mv$u9DN|1Ta=TKto zQ;4%$h8#h@YSr~oR97bcUk5isfn~~b-h?{%k17VvR}oqf(SAQLOCaYr$X>-v(SlHj z*c;@SJ>Ibg1Y_^@+}Ph338EMW|4^`50~KVw4mN7g)C^uQZ#yCv7*UUK_ z*$`C&guCIv2LU7C8@T3kcf|o~F2Ojn7XI*zE3^2TSn&RE+_~yi-0Zm_5oE+nppeet zcr9+7F);yZS$wV-HoNE3BS*I6M<&M#SjNTGq4Z!)L0}U8hZs=C%^*Z^Td?JC9$Vx2 zqb=J*-!h|7Xqsaf?|MGM>*Yj$a!?AsR*4|$1J&C@Sy;kf#~h zh!HUXLN>)h@2dR(+k8%tM_jl*{EX*5G@P=MP(}~v5@7aO3nV$hEKf+X9h_KeEuO?x zC5Z%@J2!C^4h6Q_P!w>fSqzNmM-4Y^)yMmRkf-3H%o8jS9=4;++@O?;Nqyi<%c^wO*CA< z!ONTX61oM*@C!&#`OKLBop!Qp-{%WXj(!J)N=icib&N6?S6k*Bv`HE>TxEDGi@MLX zETaS;K!n^ReT0m+$;ef@za3jA`0@1&PWR>oiNkTVu<=Z0UCNT+3>M^D@qv?Y^t;coNMv|Da)VViwDg4%=!bDI zHl62)xJKW61a1-IwNkam8teMh#t^Y`MY<0;1OEKJh;A-!Z>x-?-K{11d>sS9>)6 zLvuBBEDQ?GE%uw8O$N(XN-2cC@hW1(g&){?zUDKu%&_ArQdK3Hg1p026}#wrbn5*n za2%ierASQX_*HMIt{>;jE3!^Krnn*ZGuvsC=NIw$Pgu452FO|Z{S_CDPx`61!9c7aA-K3o+sB z^;#N1frvtjPSjcahkrNv>&UHC^-sXF;5=g6mFfDGs2d&fOHe{090l~It(H$j9%+YD zaz^-8`6V@5{wj3OayU7bU9av%I0lY7Vhv+yxt0vsW07Ir1kHu5KayZJ8)IziLR`nj zIG(pD-11UYN`+fYobX`oaK3@T#F+pp&OYLP;Mctgj#r^#E5Ju%V($!i(wKH<;PWg+ znnfJ7uFjHBB$Q=;^TYPTj*vOHuKhnlkKfoJp&`N;RpP#kl&6^lcLZ-hF?1m4#%gCp zvt`LbuN}aO;6WipoQIU7+*oEjL7r{D>&URjz?X{F;}Yl6DY#3fXEbI)q($-~Zu=%6 zvmzL8(J+M(!jPvEl0+TT{^@TUyf0pm-n;_*h0_UY1V1(k`t=uUhz&Bx(ZeTs1Zf` zhRh8Zd+o89vztk<+>%nT+@C6r(9RJk3|qd1%6o?iwi1pgq#2|pNSBe)MN`H{{<^4Q z3DcZvn`&$X6C%-_|C}GAX$Zv$9OiZVN|>=bZ@;cep$1Daog7F@xZM8m;;@?;z$&tv zKH@_Sjn1@U*JTT}ms2n15Xqu-1%Hd5dwRVCtsvc&mi7vi=!lXweJj+ZPOzN}DC+yz zy}ovq<)Wd&0RPo7yY!F{=&j{dq~}{rdHzP-m}c&KNt{klS`@anZ=P|*GQO_1F|d!F z!jqg*fV#tYqu`n~)b7DJswnxqX8k+8_!bI(iInp`Ry_o<^UBKL9Ov2VoMC|(-#rD< zQxz?}G*fCt53_Gr{6Qo{psq235n=Brm#*P2i2+xJk|+))Sqm2ed@`Vz*;`7n>;c$? zw(jS7rl3jVkd)K`c^CTxLyT2+RG}-F5uh!WLbcdV(WMT z%)lGA$%3&AZA}(Pu0mD=TJ%}%32*^vC_M>Hy*Rb(YAGz=BjdPU^A}XU;`+S1nnrW_RZ=?$Dj}oQ+vCB50k?j(%he+A0}#) zC>(BZlVXSsoUl}{K8>e0ngW@4gtI3he7+;GEsw)8CzmoF9c*<36ac6C9mUKR--I`& z*hu!$$oM@o(tM@Jh_WF2_zxD|%OQdr#?bajlr{TlV3i)7P0veAu$MNqOldyo3!m7d zZnH(a^L!4U*OiLTnNP1NZ9WQawcJRtZ*HY@f*RL{grT);g-zu8J(aaO$|WXAorLyHB6mDB=irN^PKq zqgj^~Kq@Y^a*vE(VV?iXF0sEw=`+Zy@K+|Wq@fwJ6zXV|;-v)9@`f=cn9}qWCuMxR z^Fc-?=?J8VGpctdS-^pjKORDQ5>BxD#p%Owm_9Azc=Hjx9Y6T`11_@AMh%Ohk-!NC znLBC_^w&Wk1XoQl*s`4vQQyvF8_-_s_f+jC2&#d&I%pE}0$m;7#vQ^;&bm^Q55cP! zKe5j;9j=1`T%IT2*i~N09Xj2M3fNU;3)ielX&7J)=N9qj3|=GC7So=Lf<@ViV8sug z(gvG3G8qI~96genk=y&L8c>?Ci7NZ8GwGlEi}5T4f!5<*bR|cEsZ211S#r6-1G6EK za#%p^gbZwjmg!|ehYhUQ7@XuZeJS;K{;RPLB*vf=d-F3x6uUIK{QG}1sNSwO2$+(>&*V`Gw}8cMBV>7Ef5J=El~ZY+Q( z=yFmRNF6f*o*1%~86ts0-s{$R&M4xR4v>%D|IgNCB92jK({!&mJpd!J$Bd6(%%~^n+16x>AQm}O|4S~?+@>&;kXFY6-(d<51s&S4T$(nos;sAJ4vQ<(!Zz)92t}H2 z>a1Kp&A-|*D%u7uq2hHykgx7&ba)TFpYW9^?$>v~Cyz{}0Q|yNjDQ%Gi{0Sa{>a`Y zYGZM&sq_o#3z(q4v;g4fOBZ%Tq*=@kF^43kXC}=^FlIBZP{gmwNS7ypTqu|{R&aroq++-rhe(DRTJ8@n962B2Vyp8Q%)B$9A_#d+eC_JtFi@I8! zY5{yGG~=qth>N*8<`X@UFeg8l!ZYdXDDf1m&v&Eh7)07ah9{t>+nNJC)Y!tf$~YG& z>Y}lADcWCdN}#H%%Vhd&2|21lDN4OWrVR&vh0Zh1D$Y;o%Ybw4T7kqagJAoKGc5WdfAF>Q%2HyHvYJHwIx+jvM0Yvgz7=?F*}|2 z5bG%#L#yiX6Rz3cnet=Z@P40^EHCmi)+Jud}uSR_0?0`L>fdGByXi~{qDQ* zTZ7oH%vwu4r=z?wFhvcIB9{K!(?33!G*;~g`DFIf0cAVg$ko|{jTaaDFENUvr@UTS zz$;eC+6tEP8hFZrdQM8<0>4h+h|LrA@IAXsDv?T*TY;miI1*K~LT+%L;(vE!Eifhw z2pSN8)IPAim`43y0cj+%GA0Tvc|iIi;pdC_nVmvkj;0qN z%wmP>|B}!#rsRIk%yvEd%J-e6)b(iP@&dzlqUb`hUjvc$n@=pG%$_-YZVvVQQ^t^M zY6HEvX^&LJ3=xwMR^unGX2fiuaAy>`6$HWB;$3s3tS2w+VjXEuGxb{w3riL8I zMYsfv1Dz5Wry^|MH9|&$l))E zLUZ9KY9uBt6w+ViLV>*6li@dKL<%9S@I2kKL%qEo)US9p*<4H|qwny!66h5iXWPF; zP3}f&Pvkc)WT(r>HU6ZU+nxgboREmPSt%{Tjk*UM7?;6(gGE-%PK%EVx7{f`;fI$F zp2eyJqf3wwrr0753n78g4>F`8+;*j?d`yk^?c?Z!m+dx5w;C}zl=j;(t8WpR99okS z>ULk9h!u&V9qxUXmOuXF%0a?zEHJhZWe3BI4K$cV2R8QHZQIres|R~eE2TMFTlhz! z@3(BE8{!TWg2v>YhZr#p%<8jROF?*RsjTwmD208u$7~{UfSbX7Z=7aBk1s*xT0tW6 zHpSp>l{Xb2Y1w7?iD@hHumNru5sEzZNvou=w zo06Iwau$s`B3OfTf$?NFele6`Wz8Te3MWVXv7+>2kZV`}uUd2BGV<%nR+-cVJd&%% zngn%c1HUJ}+9VZ=5$10)QgxmhF4!Soey$*8`MdTG9<$xKB|frL7mAXLGFQ63+9=!@ zVw$jKZ$kna@HLVpOY`%;HH6Vh|JRnB?KDYx4&ai#Bz-Ybko98zVyFmCU58R_b==++ z&fMqLo@aYwm2oYOYm{p=6Lv5(Dn2$a=Xbcd$woSL5p*-Z=uVH<8-xz%t}S zGE`7(3tL)jqhzOZLR0!X`euUCW?!zXD-xf`j0+p z&V~b{cw(}J4CQw%kJie?LwBH{=+K}<@|S0?KQP?zQ9yT(dyexJxE+Rfe}VuXKX)~{ zLm*7F3Mh3&Bs~Y>10aDz#~r+lzJbk=T7@ElWB-e}m8FNQcnBCV;dFmZI<2GW!xB(z;Sx8s2s(+e#j_y$RJ3da#CJh<*kecXh? z?V|R7D|I3Hfd{B0d5eV1zE7JBY+Iv?`R0;(Wk;zNtXrnU3th$oQT#jDQ2E>ipV zyU&EgF-1N-&gLCHhz^`-k*`w@-S99L1b?H!VpNom&Cl;^mfxcLnXYCca9BHYSji@~S zk3yzN!i3h_hmXqa+V_@F5}<|l2yPky=kKGMDq0|0LvRpd=w|Z$yugE-u2W|*_cW0>n=pAdE%Cu!M8&c93b2<99v#D`&Vq$NP92;_ z)spwPas^aq43+yKg0-8g&dR>Ug|e~9MiU?3!cNRZA1q4DK(}`TumL=0piuGZuaz1} z1_90%);e^DBAfAYjN%sq&T|@xb~5y9UKn;YBypTGN4?MkYrjI66k3)p|hblv<{o}Cb6C= zL`k$GZs?85pH~D2V2gNLFpdeft^pXaH?h@d2>S+vq)fA%Fbhhjc~l5ghFL_E*Vs9CnJ5b`KX z%DRF)UL1C&b9X(JTO_f^8KiObt2yL19}Yd_e{9~2%{t*mp+9WL8fy|LC|X2T|bV4PFRZxL>O*=olbd-x#|$ zR6`9FHPgkVBqVnM3Z5rC*vIdbY?b+14#qW`uk7&^3*o_k8)}&+zwW!+An=k+p+ZsO zoJ<^Fv5!3O^a^fZ%7@||!gf1#jeH1;^S}I)hhN_dD_7N~l+o1|F59akHz-S|>ywm# zpQ*ct0w;g6+)W)%sa9d1x!Z1H!zNj5@u?EQ#U}fmL;KsKsq;(7{Y#R7!%zG{xtrA! z^9ifq@re2g#xc$hrtwg3`vm7SCzWe5=5W6D4y(}z4$9ffMj2F)8lwcoI;$XAB=eA{ z%A!3fB>Vc`ul`iXbW6|}S520*qO9E{-q%8Ajn(-K|HI1=83XaEDMgI|xc5~!CD{Sb z3y0=!}apAohGQQgqRLOjr?F2+U>- zng90*83{HM$<;OU&>V}69Y@}*Xc$KYVh+mhj=S!ci&D@G8Tk2?{?PCI9wWeIV{C|; zTK4aO0dvsM;}KWm!7toV-y5%P?C1xP$AUY1-+J}xVN7SW($O69lPVmN3APVKWb57R zb5dQ>uW9O_p?k=CSj)SW6c*55+ry)wR0MxDgy=;REfkEoAJsAvc{sk2>4DB+fpKX; z3=z=gheZEX-vkrnW#x>5u4H7&Vib)PE!Hid>mS&tCIw4ivr;e9o!GMd~m77kfT zRln2gHB+(h5rvR$ezmU!Mas>}Csn);vCR_l-&&k_q^p>xQl_cyB;R>9qInOSJRXZx zRE}FV4l!-};uiaSV{Rb%u&v|xr_~C%A}`%S?j*&3C(ZKy)Sl5-ptl67tktCO&L=Nf z>X3GR#gXpRpB!60kqmaFFepIl2EERiTzymMW#^|3%mVz4Z3;0qh0-V;?s<1W%}rfH zAYjeo8nF73>B7DVo2@U|4Y43tNE{vcCjyn=+vXRG>+nshXM5(J3grWB#7$Z7Dh zbb;6HKdo1OA3&zVDeJJj< zU@{1#mkdu72c3bkhorBhe@0VZb$T-W!9cBO%!t3(0`=p#I(YL!sG53*zXtyy+t-(k|76frkvP`_3MG$A@nT-*$6S zY1WiQKekrkB<0;>;%NoW<=0WOgAYXIr8v>`G*3TDEs?+dye$N`{3aZwd@L(>57KVG z%?Q-@B{zu-7q-lEsgjQVFxR$*sRI(_ovmO@uq(=V{M70I1*;Tt$KL}9;2DoBg7aFL z6%aBL2(USR_vs>{mDF*PIVF z+bMy&snYLI)|gXD92w;Oou8+(vG$bO08EVZe-iX3kZ`zNTLdUO7+qVezOvtV>j%M41eXsnvB+>UsAY7B0x+O}gE8wS^fS^j%;woQ22ja%2ts zniF`nW#@9GnAYq!nTDq0Fp)8p@*~jSq%}u>m5)56JXnvJ^Q9;|GFcv7ri5uDWTeUB zBJJq~-nLtLI9|XrKpgd3*!nFH*{-Qlv+}ys-~BVo90u`EK03~uw(9x+QP0kZq2Ft- zb{Xd7R@G#ZP{n#cbg~(9QXy|%p;hPbu# zTpfn8LztA2sNTu3F{=BtgT8Iklj!mxoP|;A-Nsp z=`KM(-|JH95JtAKxJFDdL{Om)2nc=I^eThm?`+*u@8D@gKug`s1Fu1j0tn}N_sZ^R z&+;{`cehFp*Rp4jfyygB>}39zZqA9;0wIynloie$5UuL?OZ?-eQKa|B+3cEp%ya#AY$yDc2&%5PB5X?kC=C(9;zYHMD_Y z@wo&@l&rqRGh6WB8my^eAs|_Sm;?Wk^lf4m@UFc_5mN1zC5B=ARrF?6FPRfqIoyOM zjb1A^GqaX3=V~j$O7|A1xn6K|v9?ruiSM}=R)84Cru)6ojU;pC!u*)E{1wCVzX;6! zJrE%mH63R$WHI8P;r1K&)=sJ~J)s>)rb~DLvB?HeIjn1XZ5{;Hr^E0CbENabMeo>D zJ{zjs?}ne6MwK=c$H&*Rw7Pkh99Mp% zdLm_UTRmoP2|3Pk@lBWZA@bRt*N3KGjjEIM->hLJZ(IiK;*xY?>Nv_mv z?5((ocGTmWE54bf3t8bU1)oQK-4j)b4&oHKE7?wc&6u@mx9TW_n29Q?`;`Q9m)}56 z4_f?oiovBB*^^rGXqM!$a#QWrmrD7QxfrMB`$Ah7H`z{hR}hH+x|$rjf=8aFQk8Mo zMYKu0kgzCyIQm}W2&rBOJZ(oks?3TjR@@0Lzm#-09PzJ9l?GusGy14Sor!yB3FM8wCMGI=tTz$Y}}SVk>5C=%z75i4&&^1 zdcDO+mWJO#W`EbjC1X6MRYFsh4ME;_%DX>#0|j543_`OEYtA|U=|&x(z^^9Ugi=R+ z7#nGRvo9M}RQt&u7~-rhU|e7hil~v}wfaID9S4cJ-{W~-RrlT*PZE5uxjnzv10t4o zx1Pb>j%Gxj@~S8iVF|$deO+k089aCH7eG5#m{vpb!UFrnbUcOu4}xI5BSY)RtRM<% zyC4H$Ku#XUgCd!i)q{}qKKo;(*PSIyv>PvmzJsV!1Gcf8fA`?g_^Pj^CRBM?%wi|8 zi=~8(0468+#S^PBtc}DmGA4SdF08@B4~nO8*^ssu!_L%Gn{KbS?#VFwAuazbi=^#g zMEnec*Jae_RN6N<4br2QkchZh7ARdtHwyk&JAyVoMHH9v?I`n`m`|}qOc7H?!xpkx zaM!|Kj@blJ0}@m88+8mh@fAxB8iW^*CI{^^7*U|uIisWQ^%~c*LtKQFMTe{>Q>Bar zYVh_jv4z1n9;|@0J{0ESbQ+>bUNj5H?LR+;PZB5a82rSmn|OuR6Fy`=ymh{Eu@^p~GKS7NlLzj5;(okAa<9Ng#taHKz(x#Z&|8?>s(lA1z{x&Q%v^C&3CO!OID5i9S9-FutVa> zqzGVkaLo_Fi6M2T!JX7s3EaiO9d6JKa;(y&D=BElTi7A(gxE6Qa1D+tToux5&(zSR zColVCZ>0a$*=VH7h`3!!ek60Xoml4QvGHs{kyRq#!#T#IA?$Mlm~{7u%oPt?wDFMt^oI6J;IwYkb@MFPc3|vAXfRt! z3EkX8U?V`JjV!}aVt#Y1SAIn1#|E;Qfwvir^K9ek{>trbf)z+>iI<`J_yJlQ0K0|Z zj_$dbK8`$KT&g6pr6-QEHjMQc1hk+Hme z1(OJa=7I$v%9VUu*7j)u)0}8klj`&&JMc%i0BNh}HLpgNt%hsJwh2*nR*H?w9iPWM zc5h~O0o8?*vBCY2*Vlcq9C1@xNKQ^v$2>_2y$9oGVv1dt_gN25a;Zduv56(>GZ z^29gU<=ZeJbKK{%{yH%;8Z6aM#LP%4Na(!lXt&UdM632$2yrEtdaVIb;PkXkwRBuO zRuPQ+h3q}1_4W;E1;!XZ(J{dAg^_VI__L>-Kz8cynTt{D)Csb4!5&x`Y8P`JJV>im z8QO@dnT-SCP|d3PpGBS?A?SV-5pWw>K;7692zN=#v7qzqP|;w}+dXoRy4YL>dWR)h zZ0_{DkXq9%;AgZ%TAYrcjBKz(p8a_~U~0El8TofU)gh0&^o7L! z@U3-0Kg2X$kRgJ=(V8s$8sFlpPHrba89{kZ?$D^$nZ(Wd!GxL4k4E*B{|(rGgvx%Nk5ug)*9(-&%-J_Uz^ znVlFdg)%z5Q@slQ$8Ik)N<#|{%Q{_s;YatVBHPpiQYY#K4*4i8oWB8dj5#q&q3@TJ zA9ZFn#Z`d4M9$Qh6YE?N;Xng2-Eo)-gq#$4(vVtVvSpgNG-HL@R8 zD{_cU$M@tre`BQbK4Z`w`nXL!;WY2V>&P)z-GVslb+o-s?t7lLGIF#;(bru+{`AZR z8jW2}?n^Gae@f>A&N)XlHoEO2%S6Kuw z9ZM2iq7(}0XNy)sLzTqAl40~vj)+Fj@Ff#p@MXfR1<0%TR*v$;o~|&^vH*i|`s-v_ zbXi~e1@k_}#n{^4uFBC$gNq2Ei90Cdv&&rDOH=HdvYSSaLe{>gsS2J&+El1>W6;V7 zxx;}AcM!}KaGxn-!RR{SMM())vx3%~Je4N$Y$i-RUkKGJLlqR|he7x1$=c}37;5}n zoBX|j=3T?O=&Rfzzfo~8dqqXHY{4JQnMUdM(6>isD^@rIfUQ67B9xseAknf%QZA{@ zwQG0Ss~T%%QWczj=VFTLKZ}NCsNxnc2j#$eE%=9M0b~?S_?m3Nfe;blL41+D78gnq zo;vRSezSd(XSnhmvX_Bvnae{|Bh8&)Ba zO&1wGpK%&a$a^=iT;ERQUZ{w`5gGwdms9z1-x`WW`*xnkJO8qO}l5#oMVr4Q%Q9} zrab9FGG~~<)(Zw}t_I7RymnAUI-Y}#nx#;=0yj35@{{!)5V1uFMM%p~LY4!tI8QW8 zSr(|&pBA7S=Zz$hJ<&*_cjn_t&u!gROF1MpMrxU5)qEe54>DDX{M8Gb_ssMBb16BS zdz=)vEE@9StYgQU_U|^4d*SX?|8AJG8lX9AxI#D?!Efza6i4*U62I4R_4KBwlIXW2 zh|Cjvb9~P1y0fE}xZVu9-sJlWwHIueSoPR$a;=VX^Zz8 z>rR7wqY-$1KG;Pf$FRE5daotQYJ03WRw?oslDNk(%FSEPHr+!HJlms;DI{zF3` zb4S+Z0C~Pv%6%nHd^ohL7f6j7bbSt8g2EuZVZQ6`(Z+thN|*FgKKswTWl=h0DhVBy zbRs^bx9qc<@U`02k$30Z*2nsK^sAJKYOlR{{RRno{tb-%atIU!t&;{jDd`niX@?8Ua~#Iv|AahSaV(<^6G z*@_#yaB&_&ZAf;!^f>Ni^yFeUi+dpqp47I!yg^Hk=4Q{2%E&5%Qct@Fe( z1efGSwcH|M0wX|s0#6^~7A)~vN#2>rGo^z%=&LBcz&=Kq^#b*Y?;+RL)rw`K`z<0? znUcuJdsD508Sw)4DZC?L6%O&ufv5A&|A2Gw-Md)Pp*ZX25n}EPm~&BfbCIObqZiqD zQUWSTagQxc>K%G(zF}Xi)bXh!-}sG4Fj^jzB^3|9naaN(Vhv{8MDVj$KEBXL8vzSs zlel`AT6zc#R3z?h`Csj;G~7H^L06J$1pB)g02E&8N_c8V>8bk z!3wPgUVZ5W-d#05>j(N%b`k*V5(PXt1j!RLJ0jpUOAsP$+CL5zAh`l$4mI6$-=4YE zf#Wx2oGY2$4vcSKBJMv#T_Yu@86@GI$KS0Ba`GE$`q7qgPXDR8yzep>KDq?0C%=vi zg(L!^%qpWj?!*h~tzNslmbaa71aylc5T5vKwHcY6Q(_SGJ4!35R2{MSxb$*124dul z4uD$ZdMLx@GjdyFa7rfa-nrkfyc%rtP4~1fwTlAy@q_dg(t7=j32=gS%9961zLLI9;w=ltuDYq>VX(Jw zwsg){GuE4^8qOk6dP+}4oNUvURjxRm`$97M;by??Joh3PH-`V+Dpo3CBYNc`!k}|C zOaNP}P-yIVA3o4X3fKO>Rq~8mvanFfM-f@o0RL4gpPHb(TLyrH0%B9aAs)m zhcW&Hux)Q@tCfu~%UFP{M>UBX+_*`wkoi+_A7_~k;l^24iWGenBVMM1t;qx7zXv^x z@;|oXnElgrjh4xaBK`F_fWFF`!O4XZN{_Vmnp7)0VR$qsf}c9+z{?=jN`t|QS zIZeK(>cQ2t3I6&1{w0qv)ix1lUW%IhY<5IR)s@yjljwdefe`Z<#d15p`7Y%i6k|ou zv10^5V}unFOE_4ZF=N|)>xdS}Krx%bW|g3L2ZLw%)1vZUO}v@y_~B^r|JEkJIm0?3 z5VFrJ-_q&{_38H@`)8w&2u{>1-L^1Lw$avOSf63|64>lHb?9(u-;xLdW+r2#53em8dNxP9}Wvnu%V=vu9g^1H4FK`ErPm;tLv~#&qukN z;4RKU8$WZGi#~k9O5MYDJO3#jiBn984-mOC!cI^E$eaoNXmje#if_?w@P z)y7w*aqL%8=1v467d9Z2K8gR!LU`N4_Lm_$dM)%?&Xh{6buGOTTOox7J zX~%)U%?ro|tbj$@s!i!K_p*IT+CGIdz|N_c)tFC5SO&{*eS@=JSHJ!gw<1Ym0}f^)#wD!fsZCb(bxr`U4A7C#Jw5dr0^PGF4gdr5KE z3x}(iQe~uCIs6yBo>{)d6{l1x^;!rOCouH&?uk6(;lcD_!EN(> znm5?W4m%r81Hmvw=`34m9>wANMpHZ>$-i%WqQ|h127(1X%X|dpy5G`|pl7&eeXQxB zwd(K)dd-$#McafL)(*Em+R#WIg#$y94jp_>RcSf1q?4B5Y8wCi1M%0ss`AP}h1nYA zs@S=y5`Hx>#(KS%*GQGMJ7leh{TK?e`i zpWwg{35L;fF4BsyaDE5NQK8DP`FXewy$PExp7CiG1VZrb>37p3)t-{&lxr_RMoc#3 zN{FZo`6R!!p!cd%<-pnFe*Y{s0dIO(prVX*DzQ$dgZ~dclgxU$S6uEji;$IlDt*U< z!Wxu}E#Ny*wOw}p@fc>4NH0M};HZ^F_B{)K^3&>D>${ix^F<6@?@#xpGOmakv>8Lq z?w;FP_*Z#6KeuhLVARt;8vgu~4JrXoI?zA$YybOf3Kfo5)@7&;NPm+NSCRsHt6 z@l<`xeEYQBAsBlua)47<5k{_|zF>FMOX%wnhvP^paGAD}+=6pQv<9l0&6sa@>)kbc zimOHTeL=|^1~(6Wzpk}C3Z}U?{y)IJK+tZ+po?hzb-)(5G6x&}9?+3CreoU+t=@F50~0?6GCx z3noZ@h_v*=>$4iWt?DLbndY-D=oUlXfi;Ps`^CNH7B)`D zpV$8*p-b$=dZA$9?YF$7BZuwGtTuJW!Y;s=iOKvuC;$cvidaDGzIrUg*+(2KK>Gtg zDxV!xW!Gayt;HtR{Yb0&Cl2f}04W!I!6WO&g-PBVt9WZ8c*nbmzYfn75ETHmBY(i3th-YkOw`G4|<086;DB7OrMO7EyWlVpuqit zt;9jt-}Q^&q4U-CV%Rw_yK8#ogLBKF39UaH4^~L=x&_G@9eMr#Nr{GHE?ZN!5uLcl zb*c${eE>yu5y+qUWFvW5D`>Z<`T-67wpeF#u*|P6X{zxQe^xl0^Nc~UDaw|V^*`cO zJ*Vnf1%)&_Uv&b*%QK6sc3fUV^?6R@Mc-LSo6OR(v39)+yTvvOeAtxIWyZX`QZgLt zM(Wu7efUp{x(Z}|+h`O8O$2u_nAA(n+|Dv6?*VEiZl&fu%MI|FAgwrZ6qQOYVYoeJ zLu%2fIqZV{M`r`%-B+L#el%&m#;pLK602DQ?%@al$vtAkwiqA2&n)nW8>qO1r-~hv zQZHHZ)g5{gStYr{l1sNXQPZqoyG+T zm^5bqWQvPS76~#HwDrj+KWF`FNPHce83e%u!6rMAIsHDS0TAQA#%UDO5ybiVNGJo# z%aB;a((iRIm~xQxl)<{~dn71anQoI_Nbot*0jWhQHhS)0Bi=c+CV^3ZfNP0{Tqs;* zbo%W|JId^G^`?X^=|ex%7T!#g+?BOan6#p_t^G7_$ZQPN-x%y8K;M+*cE_+MV!SA` zc^G-TS6mS41740Ki>kELeaKwXiq!0gD6&?_2$t-|!e0-&GXyT3eGd6+RLn5{f z+&r#-R5T|EUFga1%vxko{bDx=F!QO<#KSO9dkDs7>81o~U0(jCit-gSpjSKGm8tc5 zOVX~};nnW4%c5TyR-o+7Gk2?h_4Wy~5t6H|^_&{fp}__)8<&c8YRXUc{K# z;7EKxL=3vye;8<8NtyF4XNbtH*DtaT4ZDPU1BkB(G+zIV`ZAA*vZZ8;marK_tyq7l zt2+FKM5g-Zm>%aELm}f{Ll9qz7i(b8-ZLK8Rolx`uv@{ZXqm@|e}q%TqC|NHB_k#o zQ~^5v7GU=Q=;BsO+Mezi0HO6)&?;o%h@cy>L3%2_9zJkP9p=f-+n-}>;s8F9FIYdm;ZmHzvp}fUxKPWA|9F zS~=&+s8&v}k%<35LN0|@rYTQa{4J5L+$$&)t=Z9;JD#HlK6N?ihgGs)vOp1s-<$7LO$pFEw^8!p`#XI?WMM3UYgr%D~ZIwJBT-&)9n;8 zAq+M{X;`(de^tWxVgr`P2`yG0h@V{)rhYVgk-@YNYnuo96 zVvqgj4CnXABzWw(kUw&0Dq5-+tid0VdC-kO?`sm#O(?Bk)SUnGWWAIwm7O!l&xGKa*6gkkDd!Fr8 zimYo(6CDro@K2htE6DE8U|>@aCfh)28r5N(nwTAdg9FQfW1@4YP?B>%sv@-*>Dd$K z$D)-8N~}5@s7-q79=-gR4w?t-bZkV8=gv`1Y~n_KJ|_0@ww0213=ths5Qr;C&vS`8 z?9jTIuaNDfk})r=%x_naITcy~XJ@0Pxkd?GKkae(;p)VNfWUg?4@4J(y5oNZlI$kZ zQi-XdtZ`;z=LHgQAFiW~RKKK4SiNa(8spJ*$v~oTxXEV=NapKyaBcbRAKngpUV7v0 zl>>2yzMG9g#zF+THn;Kf$R3#s3}ZEHD_9?U0PA>wyk|cUPGc=B@gp~|+xuMdY*&GDU>@v|6Fkjy{*k5qY8RbZ zFKd)vp8;tU%qx!%r?D#`TYE~u+2U8~=mr8|Nb1lR+FBl$!`#M&1eA! zjhblONiAFP3b%URDqJI;bOOL*AhEA;xWtus4~9Eo>C1k^^;kO%*AZ;2EAw2j!VCj% zFMvt*zxiG-robKrMAQ$B15ATisIY`ardI7Kd77`SotrKrx5y&T$2H+b1^A9uM~F-v^&-qrC^)N7 zf_+l#@hRAioF}X}${iHd&Rx;eBf1j){m{59*|*ef{l76}A))`TIipt2qXS~a(ITdJ zprZrP?+CMGC{iL=VmEO8rjXiWBy`k-jqC?;o!?k01tQQ3&TyFH2Zhins@(j^NA7iD z6n+3rP`^n7o{X*JbzFB$(Glv|iZRs+hzw-H`IDZf?iaEwt9+i{S1@c~zu)TdW9cTQ zW?amOe^& z5#_;(ATBvSW_j|PY+)oFvAPasXo(f6n=%^qSJtM5>5^)q^WSrOS*LjAs4F_>x#$IH z!BJdQ|Z1dD0MqZTF;It^wNFeqa^zWC*%rs3jx&lzp(t9SB zdhT8gn1UJ|fUoT_FJ!1=@g=gj`OloLArvDO(;99KRh*>3q={NpOngKW=q`WEJU5r03=O_5AK|3VJ1rQJ)43>Iq?TcPiJAB;>Zff^Oz8o*^Et=n@4X<=%+T)IMLRagKu(mlZ`&R!o#*R5vh{jw2|- zbxF!2kpH_hyX8XjS@e@Rac<_uUNSKi;+yo3s+>F)S}fPnm1Umqr?y1Hoojz0wp0sp zSu7?LSlu!8eG5yer|7claOS0cxEu1$ZB#W(1Uw8B3NzKr9mM0=V9mdT$H8M|&(}46 z1WDtpE04LCwh|%+t0i?z?y&Rp!E>(tGYL zlsk(^hL`?5vaA*PCzbp}D3DXj)h-%98Lx^04q89&lAL{EM8yY*w-{^bdDr-G`({lj z(x9a~znhuRCRFIxSr$pB$xN%xN8WiuY9qroc9N$Mh3fO+(~Y2_Nc7J6hVVbqB_;)x8R2jqj8bIBKRShhrTGL87%4IFU2(i z7hXWTvs?}FTgkGAhBz}}RXjBtW}>mu%plw{V7-eEa-EP{XaMQO#Y=;Y;|W{KMLo_$ z*K?0pZ@Ula6X(&g?cbsaJ7^lx8uz6eV=e2PGNdw%+ZaWIP2hq5kL!!}jsjxYmGS&6 zg-Q$sP|pk@DB91g^E!Eu{x|zdG?2#jm=NzMGX?;%y51az+|pIdF8Q?p9+siOV_t9?~_4??^0zVGSA1q zWTUddSr0-_p7oELLr*dg*foAjMRcjr<&}33nbl>iB1=XQTH_TRH1>nnCcE}=j!n8u7t>vzMc&=Q~PtUee;b(F=KZRA4b!wL41Ag*UB;G z5o2m;mbsOa|9znF?hDA8;C5M>T0zUX%cLT_9dMG)W(VC$s_G}ljV)Zc=;23oidlKM zUdot!C3f_;`$XgP%mQB0pGR)h2`MA%KI%03!|y8UA02N{$2O zZsv`#hYion=C4&OJ!nqoi@k|Mok)Xig%t%SM}2$h1k3I+d9AD11)SFmQ>Mfci|gOcFmv#6;S92TEDGAZN7)y%^Q`ic=x;m zHOmY~i>|Cf{PC&Jd47Tz#SO0cOr%dkd_?6t`NeJbGRGFGk5%_|B63@0meea?0jpZZ z;aDja7?LnP*OEOy)4C}sk)7Z0nJu$bz=;;`U@hf_QRJ+|U4_>`Px1d828WWbKD$yU zWH54=X?&g6tCH$2u{TtQ>9JV*-9kxZ9&}(e;C3osw%HCUh@v>>%_HAHpc|?EH(z1f z7ogmX=T65md0X)L!mot$DTg4w(W-Z-0S=n`EYZ0<#f+x%!X$pJ=F4}B$@KO9>W6l9 z!auVV0q4ily>qzkfnc_E&<~!!Qh`J=OvHT0EEzQL*vX@eoY$}+kAiRK(N4V%;o>zu zF!ttK4-iIrn>|6mkrvZ~?gC%!AlFO-7%1e4^!)PCMJ~B@S+NfY)v1m5M4@v!QL0Hr zF^=kdf~iijjl>!s6dDweMaRg+?K$*DiT)e1T#R8l>CFgo(y-lPpsf&c*t9BI+tY#T z>4-GO>Jtl$Q5_Ioq^LUHm3#`PBC8Vo8j*f?k?3O(!V$%ic1Xj2h|;=1T{ek!7FYC9U$fD@`nbK+upk*>T^Y zC`+^J8m*~V(Xf~4Gu)$%Y9N2X+A+c1WiA-jr+X^XXwrnp7S|e*d9hr9OxnO++nZ$_ zB!QNWs|mc@{z%gIFRL6v9R#pTTZEj|T-=J>GFQ?ZRW2S2URE_g?d4So7&FY%@DvDY zC7DKUuEowf?1$g##+%b3)RUh>kf47A$(;_~<0_tFT=4L3JiHcrRLw;fedHU?=w95c*;i;&(hkt6XW%52o7qj==CS2Y-j;>1uzI^RDPl-Cj_jnK+3DaaDkBATE|IWJUSA}AQ9&J*<=nT&SpPYoN->8t2HTM?I{ zOh7_Tkqnt&RUe>hXHab++9#?h#>3C>Pr0Wevv%#r!trE(m-5_?4Ib)elhU~ zQu9s4m=Z4SjU-0RpQcTPwx*asY;*O$eyyMYo=7N|0L4O42XEOs0U#b-jF|t(p*p*^ zg9$CPrfEu7c$xNJO%io1e%fEkPp-Zui5`@M#Ny&9``KcUMGEzeIur5QhMv+BBG@$+ zhcCiMW_fu&jQh@Q@egm1mqGeT?jyX_jPgQKD-O-gx5_y^Bn-{g!zqz7ZK|pWdf78S zN#p*L{rBQ-f5XRRYL0ccbRdGcEanhA9pB}ueC6tM9-QQ}0yO^&NKp)9n*_=y13pCl zGPWJNcY6St5MUG_Oepd^^rh(XEZt4Rl$CgWH_0Eq!;`19(DoG(C;4_G?9OcD!NhYa z-F6i~6vOx9exT+zMCXUI2py`2ce<=xSK&#=(n8?>%-KD>$$mVxuUS8q3o<4$=#nKp zv8he8hZcjB6Unm`ud*piZe=d%#-4LH$L>t(9`^q$MIxfKXwX4n;3_>7Mzo_vDKbr= z_g}UvD=eZbd+|8czF&^0lg6V*M z5z3;9H^q_5mvc4SsazN_Qm8?|gKx!>XlcS5(48yTSXUc&1pbNevbZD5%K?CQevFHu z3+R>#+Nxcj@(6_u04n6n=d*eKuZ_=Oy)5O0*T!i~&3)!`eOD z55Y}WM+VjXNAE1J7t{%V(@EdU>Nn{r+90@7Wk5OOZ}8l zrpaj9dv`eWtkjq%%@*#PIg-4os?$hjV7>N$15+0+hp6TM+P zjHpG-@BiQ>0$yn>6*duhSHxUTkSDRSb2Bf1gg@>Kh{txNZ*HTf!D-Z!eyV>^`JC4Q zo67Q?&>V275hu%hv$eB^5yw$Qvv-|*?^tP*cLtpyUgL7)?ypC+@(D6s;HkcUV*A7j z?UAhGG?7Eh~%vwe=#$6`i zMdyt33?sIP#E2%)=Q53|Q&Ht?hcMBGU%?^STc{WHT5gh!i;MySx#*%AzEYfJYE7cz zZhH!?F5Ol8X&(W^V@;YqY2w~fI3WoEI_Vi#5EjY|BJaOo-HU_buU}dqcZljO8muy? z&$$)LCj6+=UDQIdKX#JV50zpl(+oAcRK4D@_n;%CkJP4fmTz zNHKAit?QUZr2ATtA_;pn!S^K6Z%bP^E4WV|c-H-gwM(;DG)PKSKRjODtngHVq)GJS z*JEW##(YuWIG``bSn9>l@MY>7K2=*3jl~9}buI)!{*qbn92saI8yU5X&(1@&^}3wD zKl&|=h|N;ayTgS@Q*YFR%!&{&%`!^^j(UH@UOX8;kVD-GGQ4#E$O{h=ZCN|}I%p_5 z$QP3ay4cUMvjMH6#XB3l{2ceOshAwy*tP>zBfD!((WfbWWHToFfT5W9d5%=2l>_T} z(RxKKsi#bSS*9KSg;a8^Yzq8vOPF+7?pCU4&j3LKCfh?Vjte^?+^6CaxlhO=#5;uw^@j1#ZIUXBf>fN9Wbt-?o^f` zGFGfFUQg!*U9~Oj=VewmfN5Y(#mJ@efNH$lN+V> zqI|KytEsh7Gxakhj^b6n&%?fD@?Y54S_E~v*XH{CQKjJ#B|Xd>l1$VgAb)M!D!*?z8KW@*6-PAq2E~D3 z`@PD0i)uudf~QVu07=GI`&bXDESdz_)RW#FNLee0#F~Lv{}ihC1ksJj)9Z*^h;Voft>!U?RAuj>`EE8SoW6jiV42ZWVSyFi>vv4bn!khv8-O|0xu;+&>=_ z@Gd%7ZK#H;<^kEeEHCgS?6wkMp{ z_EU9^BotZd_wL4>Y(zI7hb|vAVcI*2Bh0O^mOJVN#9p%`uo3FRMHbS#q}UtPY*GRb zt?(gqIv_zQ2>t$qMt87v(-G@KtI|7xdeSn4sB(%hsY(#73XU)x4y=ZK(;qdK-SqR!tNR93y7j*mQGOFw@OL54D)W5!r4p6Bm7YaV>xXLKtqK?6D+ znN|8})EDgBwvQ`mYk84$0a>ZZqoXhXb=@bICVkR$Y}lWTHk580L!Mo*gQisA;PTm4LC8KR>06>Lv!(Sz0zi^7`5EqXGT;;_Kg;;;bR0+% zh=UZnLz5^JZ*PSkKa#?#Z*K*r7Jmko@7Nk__0-R_xh;Re>llG<>1r!pzfCJMmoOd+ zdjlb-EzfO`{6*zs%vI`}g*LR(W8CB^Fe8!Xg-aW`UXm1CMjl=v>)3>-w5{e;8x^up zSV(%81>ScplRMiWrg0!j-W#AencP=&00U0 zVW`b2cmu|$o*Pjz*Zwkm|59~GV@Mz@`tdn~xS14H;jwZqQMD_RrxWQfk!3&+1j1%{ zx;SPMZhd2fQGrBWgvQ&kR!R;tQrZ^NLxtA+vv9Gb8*rSaDJbUI%2QVekvTAlXt?NQ8}~;H(tPLbrM8#mA#)%=QDG zG%6a#{o@ea7TTnYKGx^2PokNtCYthL6oD-wE# z06}exL7KXmXqx`Q+8zx-#>Ei3{vEWdboL(?V|fpIA-ta2{Gwn_Bp~W!OHbI-rv+1E z&f<9l&o<(X+>&PEgi3*I_HZXFR5afrp~P~skQ3|M%74adU3&k4u2&zBn-jGbQ7%LL zw0AA6MD&PfnyTQWG}dR@*JfxWSFn7}{3Ca<#K1%ne&i%94{A7BH6NZSG zB%LdwPN?0)1aLf35w2<`#ulRWg4&OH?V+YPXLM#BLE}0>NkC-CGw18SWqN_NUUC5p z?H9(oteVYxq3<)7m-%8hv)2r@IKNEX#A-K|cKYio^Vks-B)Jim<0@#_1w0yZE6(s3 z*-KwHIw8hC6ADaSy+>ctQ3hn63u^kOvY79=G+OLud)uBHzWW#=1dg<{l;cvf)E;jg zbPyHr27(}H%qZKTqw5fT{$4`cN)tq58H`NDfJ?{aNp?R=;RB8D4xy7s3tf460_mva z`MV8Hh;p#DIA!y@|1VM=JWJeLq_UFxPo`4sziSh@ykWWzK<3hFZlqd+a7Q@5orm$9 ztd3rtK_j2Nz2_mfhcV0^eG-h<2WOD+o<=IsI>YH>WNe1V;UR>~*l@I*wzJOtHMpP7 zf2Ix}D*6VDTX+GotRO;-jvr9rszC2H%!CCP9i_ZsdQV8BHkjKAX%xB1z2+DT!BH=I z0zYN@Qc5D5AG;UR-w*HHcvfO4uTHfA4!J$^61dXXSg8Ozf2?OF`%nQ~>e%TGd`_(V zPTzf3FWv{h(h=<{q?8|BwrRHaTJ@jA=t;pf4SVi&$uO|K|9&bbjM^KX<7+A}2e3*} z80DJI{!KG1--0to5yP;D+2W>l`Lu}3^-(JN4s8{#Zgx{aj6vb%bZRkRrLRi%q;#t| z@S-Enu!EKBf}p(XtBWV*-lIV*6p{IA$$Yd_a%L%>8F*Srb~si5>33G$ zW)K>8H@1@GzWCGL;1s?vYW62bJJxyyjx{*K|FxTcGnH1>V_PUno%kWo(Ka~(8yL#* z0l@F&1{n!<-Y2FzBFWisAO3u|?H zuG<&~x=*`0r3jlcZn4h;H2@p0<{E^=9;pWenxh%F@-X~?XtN^NCN)RPVbr~)1M|mo z`(pj=s1}a?T4yrZR2Pw-20XUKs*-7@<_@Q+=hH4|1QT_mouA};lk532wLHT5gW_u; z&+~zJmVUIuBi`f_Me~y=u^z;k#NTTHF|0)p+C-1$h9bDw-*bH;D>bc0A|m`_QoQ^0 zys}CEX8*}rKSTxLoi$$yfvFYF9`~pmjlWrFB$hkApy8%1uM1_*p13%VuN5X}LueOb z{gP>&z=Ke*pP&ttIR*2M-=9~>w-d*U!15*$-yrUvOrXa;@23Z0hQe-1Ncu*lf3t%k zT#4(Y{IEvduT*jCww;SX>n4BuBHzN-XlI+r>kRv$@UE$mD7R8r6zBR%>#2Zs?@*Kb zbQyht<^hIvVRwl0_kb!8rf?Ty|CMDawYFLq;;KGfHvmmc;r;-?ra@YeB3 z9}p>nV@^owNv7dyKizKhw{Ca4+igamufkeA3OPLXE*%=Tk%mb%BqaLLt)?#JXErS`He6HRcYRT5XM) zz15}KYUhUtdJVEQtbOd3CCU(g*31&lZ1C^j`;W*F$w4=F^P*;ajs{d%Ms#(nhuoTW zS>*2P<4fWaE~%+#B6VnO0gD6G9kfrnd%@gzP^XDs0?#R;-zW3DP@FZO zzg&9CX1JRumgy|!fDEiC2aqC%WA}mh1$b9ff2!$Pc*ut(rf_pf#s)tl?Myu)X0dZ% z<=cj{O$KbYs@^c|Cwj57m6_;k2P-qO%>VYmXdSa@pJ*L41x8rt-Ol)@qX4?^)h-;| zwR78SN&EB+P0fb|@UnJIY!H(6(E(++?4!JTt@G3hdx2Sr057M|b>4A!nmbn>E{|y0 zm?@9?el;U;&(Hwu)a@h8WWIgeQ!!L4g*K-dWfx|9P5_rgr@YCmJx01V#Rj$bM>L~F7oyx>ZA0|fl)8vssIOc9fkV&msxn+r z7WRZssf|U*0;ZU_McQr$RzO5`xyXbQafp@iPNQmw=f5Cx)Fx}hAe?Zw5jBE7?>2i~ z(pY2S!Eaq>_SW|a#uf@qU7Q?vl5Vu5ER8_F4gdpZLsU%v9@+_thuD3rULOF536^{^u$<^4Vh-0Z z4+{6tFw%NnuPHaNs;@0dX6>s?-GMSJPJd|d<@ny=9UO$&l_`7{wm)-0oML-*)CDnB zUDsty#^CGjuRe#Hi7^uHQIqsQS0Mu@4fdOdV?wmV>b0}v*h9Id1%GH)Jl$0og30W! zZRJRLyUFRlBHGaxf(65F+UkF%w8eXZ^de|o?Gh`ZDafM4%C>ydtbXbK3RL0;rH_8- zeS^9Ik_!Z&Z`yU+45#kwnL+QddBrOr&q;ywJR#-e>4ogX(|iOuuB; z==|28SuKi)%k+pvv>Zy(2@4T>W7MbSs<-Ndqwb4OANT-JCu6bJl=ia@BzcW;@#O!v z+~SSZ%9@_%l(_PKLOLOb7|?h~6zBZZ1rS^={qXWNUd*;NWn@&=M(cuPJ3H8gp^$Vf zYo*A=-9KC3hwV+A?w+Glpe_7%BKPu%c1}G$>d!@_N$msxk*8fuDxO`X+|vkr0Uu#YJ$|Cy(uQiqsPrF>gDZkZhv@e8u>_+?5sQZDmtuydDgiA`UO!^ z!)a^SG}{*>>$`hJZp6}IDouAlQ~7DlK0@frE}&OTF0OWt39%a8pUIiUN2cupKGUiW zgqXoZNU7^BjEb?CV4bB$n)f6dA`5fHI<0Rk%A#lDt4Oz?^2@e{@ zPgCxyRNa@2H)@9kvf@y*a9eA!6J}Zp>m~ZifB-1#lO7BmWjY+|#tae8O=Nj-S9B+= zInHXXSHJz=iXW78*t^9)9y)+{SS6K++%DKMkRzl_)-m)1-Q@{h*HGa+u-0MHD9#ak zaa$w(ltzf~r@#=GN7w+8b*#kBz{#F#Sg_y-!b7ps5a*MlM(k;-<*R@~k25CW@VaDe z0&W%4b&>@P%U!K-W@z-6eEayOF_fOE;;wxj-i?!-Rzy0`bzf8M0rS)2c`_*0^ebf) zD%}QXAcXBG@mU&hv|MfKB9s^gUU+TS=};L^ETQn8Eq#23^7R_2pK)9_r6lw_)>9Pr z6x^lnP$|)!lEm_lSoR-`NOP(2eaHdiXa_9x0PR$$)G`@v)*=%k@D+$k9vL>+Yn5K^ z;7{VALh1LJGGk->W3X!j>L=BhZWLD)EN)57T7(yLq9MBI^kRHxuB&8m!WEQLLEuW> zDiX(kXOuMHWt*EOru#VmtC;@?G2;h*OiS(1_<|akeDgs?0?uv!wzp8W(zr21?_46X zDcJhC3zd}`o%oGMAS@-o(0?+p2v#y9rJOg=U3ZSVGxt}g02N0cbNBn-+$%VuA}W;4 z*OT)lI&k>bki|(I)>VT)lj96>8^Ug1K0)he!7ypESJD{kgmkq^>^TrxY{aZeqMQ%V}cPf3`Gs_Fp-G=||i4VevZ zbDrU&`;P)HF0)=+2uTXya^X()-G^?FB3u&TcnWCLBpO;QCzH#FNn~Oyjg7)~L(~4MrVW^S(=XVqrYH#OVg9i@2-2D&DaP1ETHVP^*a#2b905~tG$EE zihKfw<`C6RG_(N-@^=d61e)cmd9=<8@Kp$@y%gke4XAiYQ(=8AxyUcyL+jkt{f5ub zvR_I?Kmcxok$L@^i%;DG8qs}^3zl_$vB%G`gA_fy_+zKLAghLojBaxGZxOEd~0E9KBPxT^Czm706rB_I@hF7B#9D;pIhW!sRJf#DL zK9kwom~?@lA>Z1Q8GQG+D+BbGPtAtP#OUlgm3hOFlA8*!nC^npL%Xx@@S9ujFf6RI z@=9o9yxISdu!4HJ%>`*&WghW@dTiAv8hr%W32HFXlP2kVU!c&Y0&1ZKQryG4cg`aq z>@)WPki-K>?I1Isfh*`7b@JF$41|=}nJFNk=Dg%V5yq_wkXoyps0yrl_qF2)_M7d0 z)8z&8=7o-(k`&&l0q(`1&y#WD)Qz?AmvYSV>-zATe@Az4PjOCGa9#l(A)XN4L82sO zY5J2fNF#_@*h^(0xpLx1BlR$>aBtrdU{nTDsaZD(e9dUjlfMod#!-Z?!coFqs)1$O zY$nKvEeA8p#sy^2q9v@7GoUs#PR`PJVD{5TJIBNa{2Vy$JqX>tEpTGH?TJ~^*&u`7 zSQ?`e+!p+Szp$Htr2j&?2ZL6L=is)iTs1axD}0I-qfG)PsTFA*hMY#xtZAl;Tt9zn zu*hRbCuY$(Lo_F$x49%o*}4-3@r2wQvgDNDe1x+P z(6BuUiB^Bk8;HU=-fjJR4|WTpD%Q0acI6>H^;dQMPas!25C_`Y=@mSc^K+Je8`17s zlvwWaSx+%Jr(9%Be-Xq1;;FJ=OL4rDkD25T1Ybc36|c7AEPT*+M>xcY=`Cu+PfM}o zTv$;@9lABC<-T)%YJSkT+qJ<4S~7x_I>(J59NL^obp?bNLOU+Un*t*a?by?clHbCM zK(H!HCO`Kzu2}9qQg-#%C(_ix>Vdg&?2Mlzk&AQ|JspDo>=lllQBZ9qbF3(bwCk_1gjdq4R z@|1G3_?L$#1G1Wf?nBD41D8}L+57vCh{2nsDu%rvoVDsJGy!^t==6o5iJb2yyfHIaT+8O$jCwOk74 z^O>Z3Cm%R7;!KI_BBKi%oGQ{olom%hyMYV_5|)|a~3=W^TkMfhZHkjq($)cC}&BYw}ZS=@|)u`p2`1e z0y`w8R&DEBnUUd(pDeTg`vfWDyNv3XM<_CvK~0L2_2{mt^NIT8JXl;5xERVKIR(RW zEQ`|zL6CSp8HdaFAstyt*;EJz1fnFGZIWeOrbh@a7U==8gWSJ?2^7r%r3&udQPz8g zB>}g@;6KyBky?Cq8?pet#tE;QQUP^5R7Vypz$O6;tCEvT^H>f>NVoT=8F2D!R4olS zG2*FH_w%%+qDZH?PEcOU1G9f`gcDsyK_o)^D- zOa}$LhSxhy#W0D4m3v%-5Jc0Yr9gk~rf18NzFn>cu&gNpm;sKOn!!mcvW|1Lffj_u z=WtNwE3`diiAv(SvA2*+9PjjBc7yg$rg2u;uoUf<;7%2$Js#%>pnLzQjTx0L<(t3m z`)3(Dk>gdP>Y#5Q3~BIBem}n5qbeE>h{EMR>b;Gn@@3jSem%W>U-aPaYH`q=svqRDx6PyZCSceNLT^=fy%o26i@QmD*REH+ zce2FKF*0(@0cqGu!K(CYDhw+iJ9S1vF0^v)yJRKl#lrY(N*K?Ov^_%GN0xG(@}Ado zy}J&0jy!2y-Yud7(B~XdQbUpSTItjlrtpsSPEI8m7p(X+QJ9BiZEfg+F1*juC5l%79L_V+I7duRozhr`KW$`xL zid*0W0qNU+18uKerlIr1G_g3-dxKx^r6)D=M?6tRb-B&`Nv`dB2_RtB)}<;6SsW_S z>HyVt{-_P_@7S3JQ4|SVZhy*;&wt+_>H`-jlp#?+rRapQ2n!J{>wGJ1aG2kVF>1TE z-<^b@U39vOlge)tEzT(h^bPRsB$>5mXz=VQX~l6ghM+b~JHqg^p<`CZ_PR{T^Th~! z?x+1~qyTilT*1r(3UE}?=10yKF{*JovjPMK*&J6DgS(KAqvGr|-8je@$e2TyaMBHR zdV+@TRjL2&vOK34r`-TMD-bu_XY@w5N^8|>W&xc+OcJ_X{C_>?(M1q@*|o7n4KINVh;e{gtt<$@t}ihPmWr?vVXK#)qMCP5hAyoj8u_nXqg-o=Jnm zF(KlfHI!`vk-E5uk0QlLyWje)N?$VlMa}L39M3E{M7Z#P;-xq@Sj%U5I@8&NHGpdB{1vHC^8y+0y7K2-rL9lFW=j}(nIC2q z*KrR*nVtnyru4p8jiAhufctQWl!atx&c{OWdk4&wm#dGnXjcLOuV&UFFc*N=?RPe> zo?6?TYz4NLAyM#L#$Gu6pnB0224 z^ER;*h=~yT=vA+~ElDLA4xrUmk0+J7pbHO5FU`!HF$KKL|F#I=80D%>rZr(^-NLTX9GDnsR2s0Nkm_G zQ74cVmJ{!_f+)6#{^m0fWc$s+GFCzUq&zrU9CuUP)^qdh(-qyDGJSxLA-K65biH$! zV$^@eE}p(sqS+BA8LO4deRv_|D*r6=p_mtX2EU#1MFxK#^Yjrw>afM}J@PJ4&YZTd z&cQHDMkD8Y(3+|joe^2&S0@=ndOmqb{M>zMM6 zMFp#J7nv_n$Z}yq`HJka=>lv0L<$1xIY*6`&J^WNfvlJ!!{?{;)CNrY zc8byW)t*{S%3|OjN4bqBk!f6~JTAzZS4r79t`dx91AoMy ztOMCB=tQldcUtHrQbhe_!bvV?KjloWirz8_77h@QLOAi z>nfztbu9aP)VVMB9RuEh4e7m [!NOTE] +> RAG is an advanced feature that requires configuration and tuning. The defaults +> work well for getting started, but tailoring the configuration to your specific +> content and use case significantly improves results. + +## The problem: too much context + +Your agent can work with your entire codebase, but it can't fit everything in +its context window. Even with 200K token limits, medium-sized projects are too +large. Finding relevant code buried in hundreds of files wastes context. + +Filesystem tools help agents read files, but the agent has to guess which files +to read. It can't search by meaning, only by filename. Ask "find the retry +logic" and the agent reads files hoping to stumble on the right code. + +Grep finds exact text matches but misses related concepts. Searching +"authentication" won't find code using "auth" or "login." You either get +hundreds of matches or zero, and grep doesn't understand code structure - it +just matches strings anywhere they appear. + +RAG indexes your content ahead of time and enables semantic search. The agent +searches pre-indexed content by meaning, not exact words. It retrieves only +relevant chunks that respect code structure. No wasted context on exploration. + +## How RAG works in cagent + +Configure a RAG source in your cagent config: + +```yaml +rag: + codebase: + docs: [./src, ./pkg] + strategies: + - type: chunked-embeddings + embedding_model: openai/text-embedding-3-small + vector_dimensions: 1536 + database: ./code.db + +agents: + root: + model: openai/gpt-5 + instruction: You are a coding assistant. Search the codebase when needed. + rag: [codebase] +``` + +When you reference `rag: [codebase]`, cagent: + +1. **At startup** - Indexes your documents (first run only, blocks until complete) +2. **During conversation** - Gives the agent a search tool +3. **When the agent searches** - Retrieves relevant chunks and adds them to context +4. **On file changes** - Automatically re-indexes modified files + +The agent decides when to search based on the conversation. You don't manage +what goes in context - the agent does. + +### The indexing process + +On first run, cagent: + +- Reads files from configured paths +- Respects `.gitignore` patterns (can be disabled) +- Splits documents into chunks +- Creates searchable representations using your chosen strategy +- Stores everything in a local database + +Subsequent runs reuse the index. If files change, cagent detects this and +re-indexes only what changed, keeping your knowledge base up to date without +manual intervention. + +## Retrieval strategies + +Different content requires different retrieval approaches. cagent supports +three strategies, each optimized for different use cases. The defaults work +well, but understanding the trade-offs helps you choose the right approach. + +### Semantic search (chunked-embeddings) + +Converts text to vectors that represent meaning, enabling search by concept +rather than exact words: + +```yaml +strategies: + - type: chunked-embeddings + embedding_model: openai/text-embedding-3-small + vector_dimensions: 1536 + database: ./docs.db + chunking: + size: 1000 + overlap: 100 +``` + +During indexing, documents are split into chunks and each chunk is converted +to a 1536-dimensional vector by the embedding model. These vectors are +essentially coordinates in a high-dimensional space where similar concepts are +positioned close together. + +When you search for "how do I authenticate users?", your query becomes a vector +and the database finds chunks with nearby vectors using cosine similarity +(measuring the angle between vectors). The embedding model learned that +"authentication," "auth," and "login" are related concepts, so searching for +one finds the others. + +Example: The query "how do I authenticate users?" finds both "User +authentication requires a valid API token" and "Token-based auth validates +requests" despite different wording. It won't find "The authentication tests +are failing" because that's a different meaning despite containing the word. + +This works well for documentation where users ask questions using different +terminology than your docs. The downside is it may miss exact technical terms +and sometimes you want literal matches, not semantic ones. Requires embedding +API calls during indexing. + +### Keyword search (BM25) + +Statistical algorithm that matches and ranks by term frequency and rarity: + +```yaml +strategies: + - type: bm25 + database: ./bm25.db + k1: 1.5 + b: 0.75 + chunking: + size: 1000 + overlap: 100 +``` + +During indexing, documents are tokenized and the algorithm calculates how often +each term appears (term frequency) and how rare it is across all documents +(inverse document frequency). The scoring index is stored in a local SQLite +database. + +When you search for "HandleRequest function", the algorithm finds chunks +containing these exact terms and scores them based on term frequency, term +rarity, and document length. Finding "HandleRequest" is scored as more +significant than finding common words like "function". Think of it as grep with +statistical ranking. + +Example: Searching "HandleRequest function" finds `func HandleRequest(w +http.ResponseWriter, r *http.Request)` and "The HandleRequest function +processes incoming requests", but not "process HTTP requests" despite that +being semantically similar. + +The `k1` parameter (default 1.5) controls how much repeated terms matter - +higher values emphasize repetition more. The `b` parameter (default 0.75) +controls length normalization - higher values penalize longer documents more. + +This is fast, local (no API costs), and predictable for finding function names, +class names, API endpoints, and any identifier that appears verbatim. The +trade-off is zero understanding of meaning - "RetryHandler" and "retry logic" +won't match despite being related. Essential complement to semantic search. + +### LLM-enhanced semantic search (semantic-embeddings) + +Generates semantic summaries with an LLM before embedding, enabling search by +what code does rather than what it's called: + +```yaml +strategies: + - type: semantic-embeddings + embedding_model: openai/text-embedding-3-small + chat_model: openai/gpt-5-mini + vector_dimensions: 1536 + database: ./code.db + ast_context: true + chunking: + size: 1000 + code_aware: true +``` + +During indexing, code is split using AST structure (functions stay intact), +then the `chat_model` generates a semantic summary of each chunk. The summary +gets embedded, not the raw code. When you search, your query matches against +these summaries, but the original code is returned. + +This solves a problem with regular embeddings: raw code embeddings are +dominated by variable names and implementation details. A function called +`processData` that implements retry logic won't semantically match "retry". But +when the LLM summarizes it first, the summary explicitly mentions "retry +logic," making it findable. + +Example: Consider this code: + +```go +func (c *Client) Do(req *Request) (*Response, error) { + for i := 0; i < 3; i++ { + resp, err := c.attempt(req) + if err == nil { return resp, nil } + time.Sleep(time.Duration(1< [!NOTE] +> Currently only Go is supported; support for additional languages is planned. + +For short, focused content like API references: + +```yaml +chunking: + size: 500 + overlap: 50 +``` + +Brief sections need less overlap since they're naturally self-contained. + +Experiment with these values. If retrieval misses context, increase chunk size +or overlap. If results are too broad, decrease chunk size. + +## Making decisions about RAG + +### When to use RAG + +Use RAG when: + +- Your content is too large for the context window +- You want targeted retrieval, not everything at once +- Content changes and needs to stay current +- Agent needs to search across many files + +Don't use RAG when: + +- Content is small enough to include in agent instructions +- Information rarely changes (consider prompt engineering instead) +- You need real-time data (RAG uses pre-indexed snapshots) +- Content is already in a searchable format the agent can query directly + +### Choosing retrieval strategies + +Use semantic search (chunked-embeddings) for user-facing documentation, content +with varied terminology, and conceptual searches where users phrase questions +differently than your docs. + +Use keyword search (BM25) for code identifiers, function names, API endpoints, +error messages, and any content where exact term matching matters. Essential +for technical jargon and proper nouns. + +Use LLM-enhanced semantic (semantic-embeddings) for code search by +functionality, finding implementations by behavior rather than name, or complex +technical content requiring deep understanding. Choose this when accuracy +matters more than indexing speed. + +Use hybrid (multiple strategies) for general-purpose search across mixed +content, when you're unsure which approach works best, or for production +systems where quality matters most. Maximum coverage at the cost of complexity. + +### Tuning for your project + +Start with defaults, then adjust based on results. + +If retrieval misses relevant content: + +- Increase `limit` in strategies to retrieve more candidates +- Adjust `threshold` to be less strict +- Increase chunk `size` to capture more context +- Add more retrieval strategies + +If retrieval returns irrelevant content: + +- Decrease `limit` to fewer candidates +- Increase `threshold` to be more strict +- Add reranking with specific criteria +- Decrease chunk `size` for more focused results + +If indexing is too slow: + +- Increase `batch_size` for fewer API calls +- Increase `max_embedding_concurrency` for parallelism +- Consider BM25 instead of embeddings (local, no API) +- Use smaller embedding models + +If results lack context: + +- Increase chunk `overlap` +- Increase chunk `size` +- Use `return_full_content: true` to return entire documents +- Add neighboring chunks to results + +## Further reading + +- [Configuration reference](reference/config.md#rag) - Complete RAG options and + parameters +- [RAG examples](https://github.com/docker/cagent/tree/main/examples/rag) - + Working configurations for different scenarios +- [Tools reference](reference/toolsets.md) - How RAG search tools work in agent workflows diff --git a/content/manuals/ai/cagent/reference/_index.md b/content/manuals/ai/cagent/reference/_index.md new file mode 100644 index 000000000000..1e3fdb26253f --- /dev/null +++ b/content/manuals/ai/cagent/reference/_index.md @@ -0,0 +1,6 @@ +--- +build: + render: never +title: Reference +weight: 40 +--- diff --git a/content/manuals/ai/cagent/reference/cli.md b/content/manuals/ai/cagent/reference/cli.md new file mode 100644 index 000000000000..b20e8846fcb3 --- /dev/null +++ b/content/manuals/ai/cagent/reference/cli.md @@ -0,0 +1,482 @@ +--- +title: CLI reference +linkTitle: CLI +description: Complete reference for cagent command-line interface +keywords: [ai, agent, cagent, cli, command line] +weight: 30 +--- + +Command-line interface for running, managing, and deploying AI agents. + +For agent configuration file syntax, see the [Configuration file +reference](./config.md). For toolset capabilities, see the [Toolsets +reference](./toolsets.md). + +## Synopsis + +```console +$ cagent [command] [flags] +``` + +## Global flags + +Work with all commands: + +| Flag | Type | Default | Description | +| --------------- | ------- | ------- | -------------------- | +| `-d`, `--debug` | boolean | false | Enable debug logging | +| `-o`, `--otel` | boolean | false | Enable OpenTelemetry | +| `--log-file` | string | - | Debug log file path | + +Debug logs write to `~/.cagent/cagent.debug.log` by default. Override with +`--log-file`. + +## Runtime flags + +Work with most commands. Supported commands link to this section. + +| Flag | Type | Default | Description | +| ------------------- | ------- | ------- | ------------------------------------ | +| `--models-gateway` | string | - | Models gateway address | +| `--env-from-file` | array | - | Load environment variables from file | +| `--code-mode-tools` | boolean | false | Enable JavaScript tool orchestration | +| `--working-dir` | string | - | Working directory for the session | + +Set `--models-gateway` via `CAGENT_MODELS_GATEWAY` environment variable. + +## Commands + +### a2a + +Expose agent via the Agent2Agent (A2A) protocol. Allows other A2A-compatible +systems to discover and interact with your agent. Auto-selects an available +port if not specified. + +```console +$ cagent a2a agent-file|registry-ref +``` + +> [!NOTE] +> A2A support is currently experimental and needs further work. Tool calls are +> handled internally and not exposed as separate ADK events. Some ADK features +> are not yet integrated. + +Arguments: + +- `agent-file|registry-ref` - Path to YAML or OCI registry reference (required) + +Flags: + +| Flag | Type | Default | Description | +| --------------- | ------- | ------- | ----------------- | +| `-a`, `--agent` | string | root | Agent name | +| `--port` | integer | 0 | Port (0 = random) | + +Supports [runtime flags](#runtime-flags). + +Examples: + +```console +$ cagent a2a ./agent.yaml --port 8080 +$ cagent a2a agentcatalog/pirate --port 9000 +``` + +### acp + +Start agent as ACP (Agent Client Protocol) server on stdio for editor integration. +See [ACP integration](../integrations/acp.md) for setup guides. + +```console +$ cagent acp agent-file|registry-ref +``` + +Arguments: + +- `agent-file|registry-ref` - Path to YAML or OCI registry reference (required) + +Supports [runtime flags](#runtime-flags). + +### alias add + +Create alias for agent. + +```console +$ cagent alias add name target +``` + +Arguments: + +- `name` - Alias name (required) +- `target` - Path to YAML or registry reference (required) + +Examples: + +```console +$ cagent alias add dev ./dev-agent.yaml +$ cagent alias add prod docker.io/user/prod-agent:latest +$ cagent alias add default ./agent.yaml +``` + +Setting alias name to "default" lets you run `cagent run` without arguments. + +### alias list + +List all aliases. + +```console +$ cagent alias list +$ cagent alias ls +``` + +### alias remove + +Remove alias. + +```console +$ cagent alias remove name +$ cagent alias rm name +``` + +Arguments: + +- `name` - Alias name (required) + +### api + +HTTP API server. + +```console +$ cagent api agent-file|agents-dir +``` + +Arguments: + +- `agent-file|agents-dir` - Path to YAML or directory with agents (required) + +Flags: + +| Flag | Type | Default | Description | +| -------------------- | ------- | ---------- | --------------------------------- | +| `-l`, `--listen` | string | :8080 | Listen address | +| `-s`, `--session-db` | string | session.db | Session database path | +| `--pull-interval` | integer | 0 | Auto-pull OCI ref every N minutes | + +Supports [runtime flags](#runtime-flags). + +Examples: + +```console +$ cagent api ./agent.yaml +$ cagent api ./agents/ --listen :9000 +$ cagent api docker.io/user/agent --pull-interval 10 +``` + +The `--pull-interval` flag works only with OCI references. Automatically pulls and reloads at the specified interval. + +### build + +Build Docker image for agent. + +```console +$ cagent build agent-file|registry-ref [image-name] +``` + +Arguments: + +- `agent-file|registry-ref` - Path to YAML or OCI registry reference (required) +- `image-name` - Docker image name (optional) + +Flags: + +| Flag | Type | Default | Description | +| ------------ | ------- | ------- | -------------------------- | +| `--dry-run` | boolean | false | Print Dockerfile only | +| `--push` | boolean | false | Push image after build | +| `--no-cache` | boolean | false | Build without cache | +| `--pull` | boolean | false | Pull all referenced images | + +Example: + +```console +$ cagent build ./agent.yaml myagent:latest +$ cagent build ./agent.yaml --dry-run +``` + +### catalog list + +List catalog agents. + +```console +$ cagent catalog list [org] +``` + +Arguments: + +- `org` - Organization name (optional, default: `agentcatalog`) + +Queries Docker Hub for agent repositories. + +### debug config + +Show resolved agent configuration. + +```console +$ cagent debug config agent-file|registry-ref +``` + +Arguments: + +- `agent-file|registry-ref` - Path to YAML or OCI registry reference (required) + +Supports [runtime flags](#runtime-flags). + +Shows canonical configuration in YAML after all processing and defaults. + +### debug toolsets + +List agent tools. + +```console +$ cagent debug toolsets agent-file|registry-ref +``` + +Arguments: + +- `agent-file|registry-ref` - Path to YAML or OCI registry reference (required) + +Supports [runtime flags](#runtime-flags). + +Lists all tools for each agent in the configuration. + +### eval + +Run evaluation tests. + +```console +$ cagent eval agent-file|registry-ref [eval-dir] +``` + +Arguments: + +- `agent-file|registry-ref` - Path to YAML or OCI registry reference (required) +- `eval-dir` - Evaluation files directory (optional, default: `./evals`) + +Supports [runtime flags](#runtime-flags). + +### exec + +Single message execution without TUI. + +```console +$ cagent exec agent-file|registry-ref [message|-] +``` + +Arguments: + +- `agent-file|registry-ref` - Path to YAML or OCI registry reference (required) +- `message` - Prompt, or `-` for stdin (optional) + +Same flags as [run](#run). + +Supports [runtime flags](#runtime-flags). + +Examples: + +```console +$ cagent exec ./agent.yaml +$ cagent exec ./agent.yaml "Check for security issues" +$ echo "Instructions" | cagent exec ./agent.yaml - +``` + +### feedback + +Submit feedback. + +```console +$ cagent feedback +``` + +Shows link to submit feedback. + +### mcp + +MCP (Model Context Protocol) server on stdio. Exposes agents as tools to MCP +clients. See [MCP integration](../integrations/mcp.md) for setup guides. + +```console +$ cagent mcp agent-file|registry-ref +``` + +Arguments: + +- `agent-file|registry-ref` - Path to YAML or OCI registry reference (required) + +Supports [runtime flags](#runtime-flags). + +Examples: + +```console +$ cagent mcp ./agent.yaml +$ cagent mcp docker.io/user/agent:latest +``` + +### new + +Create agent configuration interactively. + +```console +$ cagent new [message...] +``` + +Flags: + +| Flag | Type | Default | Description | +| ------------------ | ------- | ------- | ------------------------------- | +| `--model` | string | - | Model as `provider/model` | +| `--max-iterations` | integer | 0 | Maximum agentic loop iterations | + +Supports [runtime flags](#runtime-flags). + +Opens interactive TUI to configure and generate agent YAML. + +### pull + +Pull agent from OCI registry. + +```console +$ cagent pull registry-ref +``` + +Arguments: + +- `registry-ref` - OCI registry reference (required) + +Flags: + +| Flag | Type | Default | Description | +| --------- | ------- | ------- | --------------------------- | +| `--force` | boolean | false | Pull even if already exists | + +Example: + +```console +$ cagent pull docker.io/user/agent:latest +``` + +Saves to local YAML file. + +### push + +Push agent to OCI registry. + +```console +$ cagent push agent-file registry-ref +``` + +Arguments: + +- `agent-file` - Path to local YAML (required) +- `registry-ref` - OCI reference like `docker.io/user/agent:latest` (required) + +Example: + +```console +$ cagent push ./agent.yaml docker.io/myuser/myagent:latest +``` + +### run + +Interactive terminal UI for agent sessions. + +```console +$ cagent run [agent-file|registry-ref] [message|-] +``` + +Arguments: + +- `agent-file|registry-ref` - Path to YAML or OCI registry reference (optional) +- `message` - Initial prompt, or `-` for stdin (optional) + +Flags: + +| Flag | Type | Default | Description | +| --------------- | ------- | ------- | ---------------------------- | +| `-a`, `--agent` | string | root | Agent name | +| `--yolo` | boolean | false | Auto-approve all tool calls | +| `--attach` | string | - | Attach image file | +| `--model` | array | - | Override model (repeatable) | +| `--dry-run` | boolean | false | Initialize without executing | +| `--remote` | string | - | Remote runtime address | + +Supports [runtime flags](#runtime-flags). + +Examples: + +```console +$ cagent run ./agent.yaml +$ cagent run ./agent.yaml "Analyze this codebase" +$ cagent run ./agent.yaml --agent researcher +$ echo "Instructions" | cagent run ./agent.yaml - +$ cagent run +``` + +Running without arguments uses the default agent or a "default" alias if configured. + +Shows interactive TUI in a terminal. Falls back to exec mode otherwise. + +#### Interactive commands + +TUI slash commands: + +| Command | Description | +| ---------- | -------------------------------- | +| `/exit` | Exit | +| `/reset` | Clear history | +| `/eval` | Save conversation for evaluation | +| `/compact` | Compact conversation | +| `/yolo` | Toggle auto-approval | + +### version + +Print version information. + +```console +$ cagent version +``` + +Shows cagent version and commit hash. + +## Environment variables + +| Variable | Description | +| ------------------------------ | ------------------------------- | +| `CAGENT_MODELS_GATEWAY` | Models gateway address | +| `TELEMETRY_ENABLED` | Telemetry control (set `false`) | +| `CAGENT_HIDE_TELEMETRY_BANNER` | Hide telemetry banner (set `1`) | +| `OTEL_EXPORTER_OTLP_ENDPOINT` | OpenTelemetry endpoint | + +## Model overrides + +Override models specified in your configuration file using the `--model` flag. + +Format: `[agent=]provider/model` + +Without an agent name, the model applies to all agents. With an agent name, it applies only to that specific agent. + +Apply to all agents: + +```console +$ cagent run ./agent.yaml --model gpt-5 +$ cagent run ./agent.yaml --model anthropic/claude-sonnet-4-5 +``` + +Apply to specific agents only: + +```console +$ cagent run ./agent.yaml --model researcher=gpt-5 +$ cagent run ./agent.yaml --model "agent1=gpt-5,agent2=claude-sonnet-4-5" +``` + +Providers: `openai`, `anthropic`, `google`, `dmr` + +Omit provider for automatic selection based on model name. diff --git a/content/manuals/ai/cagent/reference/config.md b/content/manuals/ai/cagent/reference/config.md new file mode 100644 index 000000000000..b644b536012d --- /dev/null +++ b/content/manuals/ai/cagent/reference/config.md @@ -0,0 +1,557 @@ +--- +title: Configuration file reference +linkTitle: Configuration file +description: Complete reference for the cagent YAML configuration file format +keywords: [ai, agent, cagent, configuration, yaml] +weight: 10 +--- + +This reference documents the YAML configuration file format for cagent agents. +It covers file structure, agent parameters, model configuration, toolset setup, +and RAG sources. + +For detailed documentation of each toolset's capabilities and specific options, +see the [Toolsets reference](./toolsets.md). + +## File structure + +A configuration file has four top-level sections: + +```yaml +agents: # Required - agent definitions + root: + model: anthropic/claude-sonnet-4-5 + description: What this agent does + instruction: How it should behave + +models: # Optional - model configurations + custom_model: + provider: openai + model: gpt-5 + +rag: # Optional - RAG sources + docs: + docs: [./documents] + strategies: [...] + +metadata: # Optional - author, license, readme + author: Your Name +``` + +## Agents + +| Property | Type | Description | Required | +| ---------------------- | ------- | ---------------------------------------------- | -------- | +| `model` | string | Model reference or name | Yes | +| `description` | string | Brief description of agent's purpose | Yes | +| `instruction` | string | Detailed behavior instructions | Yes | +| `sub_agents` | array | Agent names for task delegation | No | +| `handoffs` | array | Agent names for conversation handoff | No | +| `toolsets` | array | Available tools | No | +| `welcome_message` | string | Message displayed on start | No | +| `add_date` | boolean | Include current date in context | No | +| `add_environment_info` | boolean | Include working directory, OS, Git info | No | +| `add_prompt_files` | array | Prompt file paths to include | No | +| `max_iterations` | integer | Maximum tool call loops (unlimited if not set) | No | +| `num_history_items` | integer | Conversation history limit | No | +| `code_mode_tools` | boolean | Enable Code Mode for tools | No | +| `commands` | object | Named prompts accessible via `/command_name` | No | +| `structured_output` | object | JSON schema for structured responses | No | +| `rag` | array | RAG source names | No | + +### Task delegation vs conversation handoff + +Use `sub_agents` to break work into tasks. The root agent assigns work to a +sub-agent and gets results back while staying in control. + +Use `handoffs` to transfer the entire conversation to a different agent. The +new agent takes over completely. + +### Commands + +Named prompts users invoke with `/command_name`. Supports JavaScript template +literals with `${env.VARIABLE}` for environment variables: + +```yaml +commands: + greet: "Say hello to ${env.USER}" + analyze: "Analyze ${env.PROJECT_NAME || 'demo'}" +``` + +Run with: `cagent run config.yaml /greet` + +### Structured output + +Constrain responses to a JSON schema (OpenAI and Gemini only): + +```yaml +structured_output: + name: code_analysis + strict: true + schema: + type: object + properties: + issues: + type: array + items: { ... } + required: [issues] +``` + +## Models + +| Property | Type | Description | Required | +| --------------------- | ------- | ---------------------------------------------- | -------- | +| `provider` | string | `openai`, `anthropic`, `google`, `dmr` | Yes | +| `model` | string | Model name | Yes | +| `temperature` | float | Randomness (0.0-2.0) | No | +| `max_tokens` | integer | Maximum response length | No | +| `top_p` | float | Nucleus sampling (0.0-1.0) | No | +| `frequency_penalty` | float | Repetition penalty (-2.0 to 2.0, OpenAI only) | No | +| `presence_penalty` | float | Topic penalty (-2.0 to 2.0, OpenAI only) | No | +| `base_url` | string | Custom API endpoint | No | +| `parallel_tool_calls` | boolean | Enable parallel tool execution (default: true) | No | +| `token_key` | string | Authentication token key | No | +| `track_usage` | boolean | Track token usage | No | +| `thinking_budget` | mixed | Reasoning effort (provider-specific) | No | +| `provider_opts` | object | Provider-specific options | No | + +### Alloy models + +Use multiple models in rotation by separating names with commas: + +```yaml +model: anthropic/claude-sonnet-4-5,openai/gpt-5 +``` + +### Thinking budget + +Controls reasoning depth. Configuration varies by provider: + +- **OpenAI**: String values - `minimal`, `low`, `medium`, `high` +- **Anthropic**: Integer token budget (1024-32768, must be less than `max_tokens`) + - Set `provider_opts.interleaved_thinking: true` for tool use during reasoning +- **Gemini**: Integer token budget (0 to disable, -1 for dynamic, max 24576) + - Gemini 2.5 Pro: 128-32768, cannot disable (minimum 128) + +```yaml +# OpenAI +thinking_budget: low + +# Anthropic +thinking_budget: 8192 +provider_opts: + interleaved_thinking: true + +# Gemini +thinking_budget: 8192 # Fixed +thinking_budget: -1 # Dynamic +thinking_budget: 0 # Disabled +``` + +### Docker Model Runner (DMR) + +Run local models. If `base_url` is omitted, cagent auto-discovers via Docker +Model plugin. + +```yaml +provider: dmr +model: ai/qwen3 +max_tokens: 8192 +base_url: http://localhost:12434/engines/llama.cpp/v1 # Optional +``` + +Pass llama.cpp options via `provider_opts.runtime_flags` (array, string, or multiline): + +```yaml +provider_opts: + runtime_flags: ["--ngl=33", "--threads=8"] + # or: runtime_flags: "--ngl=33 --threads=8" +``` + +Model config fields auto-map to runtime flags: + +- `temperature` → `--temp` +- `top_p` → `--top-p` +- `max_tokens` → `--context-size` + +Explicit `runtime_flags` override auto-mapped flags. + +Speculative decoding for faster inference: + +```yaml +provider_opts: + speculative_draft_model: ai/qwen3:0.6B-F16 + speculative_num_tokens: 16 + speculative_acceptance_rate: 0.8 +``` + +## Tools + +Configure tools in the `toolsets` array. Three types: built-in, MCP +(local/remote), and Docker Gateway. + +> [!NOTE] +> This section covers toolset configuration syntax. For detailed documentation +> of each toolset's capabilities, available tools, and specific configuration +> options, see the [Toolsets reference](./toolsets.md). + +All toolsets support common properties like `tools` (whitelist), `defer` (deferred loading), `toon` (output compression), `env` (environment variables), and `instruction` (usage guidance). See the [Toolsets reference](./toolsets.md) for details on these properties and what each toolset does. + +### Built-in tools + +```yaml +toolsets: + - type: filesystem + - type: shell + - type: think + - type: todo + shared: true + - type: memory + path: ./memory.db +``` + +All agents automatically have a `transfer_task` tool for delegating to +sub-agents. See the [Toolsets reference](./toolsets.md) for complete documentation +of each toolset. + +### MCP tools + +Local process: + +```yaml +- type: mcp + command: npx + args: + ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/files"] + tools: ["read_file", "write_file"] # Optional: limit to specific tools + env: + NODE_OPTIONS: "--max-old-space-size=8192" +``` + +Remote server: + +```yaml +- type: mcp + remote: + url: https://mcp-server.example.com + transport_type: sse + headers: + Authorization: Bearer token +``` + +### Docker MCP Gateway + +Containerized tools from [Docker MCP Catalog](/manuals/ai/mcp-catalog-and-toolkit/mcp-gateway.md): + +```yaml +- type: mcp + ref: docker:duckduckgo +``` + +## RAG + +Retrieval-augmented generation for document knowledge bases. Define sources at +the top level, reference in agents. + +```yaml +rag: + docs: + docs: [./documents, ./README.md] + strategies: + - type: chunked-embeddings + embedding_model: openai/text-embedding-3-small + vector_dimensions: 1536 + database: ./embeddings.db + +agents: + root: + rag: [docs] +``` + +### Retrieval strategies + +All strategies support chunking configuration. Chunk size and overlap are +measured in characters (Unicode code points), not tokens. + +#### Chunked-embeddings + +Direct semantic search using vector embeddings. Best for understanding intent, +synonyms, and paraphrasing. + +| Field | Type | Default | +| ---------------------------------- | ------- | ------- | +| `embedding_model` | string | - | +| `database` | string | - | +| `vector_dimensions` | integer | - | +| `similarity_metric` | string | cosine | +| `threshold` | float | 0.5 | +| `limit` | integer | 5 | +| `chunking.size` | integer | 1000 | +| `chunking.overlap` | integer | 75 | +| `chunking.respect_word_boundaries` | boolean | true | +| `chunking.code_aware` | boolean | false | + +```yaml +- type: chunked-embeddings + embedding_model: openai/text-embedding-3-small + vector_dimensions: 1536 + database: ./vector.db + similarity_metric: cosine_similarity + threshold: 0.5 + limit: 10 + chunking: + size: 1000 + overlap: 100 +``` + +#### Semantic-embeddings + +LLM-enhanced semantic search. Uses a language model to generate rich semantic +summaries of each chunk before embedding, capturing deeper meaning. + +| Field | Type | Default | +| ---------------------------------- | ------- | ------- | +| `embedding_model` | string | - | +| `chat_model` | string | - | +| `database` | string | - | +| `vector_dimensions` | integer | - | +| `similarity_metric` | string | cosine | +| `threshold` | float | 0.5 | +| `limit` | integer | 5 | +| `ast_context` | boolean | false | +| `semantic_prompt` | string | - | +| `chunking.size` | integer | 1000 | +| `chunking.overlap` | integer | 75 | +| `chunking.respect_word_boundaries` | boolean | true | +| `chunking.code_aware` | boolean | false | + +```yaml +- type: semantic-embeddings + embedding_model: openai/text-embedding-3-small + vector_dimensions: 1536 + chat_model: openai/gpt-5-mini + database: ./semantic.db + threshold: 0.3 + limit: 10 + chunking: + size: 1000 + overlap: 100 +``` + +#### BM25 + +Keyword-based search using BM25 algorithm. Best for exact terms, technical +jargon, and code identifiers. + +| Field | Type | Default | +| ---------------------------------- | ------- | ------- | +| `database` | string | - | +| `k1` | float | 1.5 | +| `b` | float | 0.75 | +| `threshold` | float | 0.0 | +| `limit` | integer | 5 | +| `chunking.size` | integer | 1000 | +| `chunking.overlap` | integer | 75 | +| `chunking.respect_word_boundaries` | boolean | true | +| `chunking.code_aware` | boolean | false | + +```yaml +- type: bm25 + database: ./bm25.db + k1: 1.5 + b: 0.75 + threshold: 0.3 + limit: 10 + chunking: + size: 1000 + overlap: 100 +``` + +### Hybrid retrieval + +Combine multiple strategies with fusion: + +```yaml +strategies: + - type: chunked-embeddings + embedding_model: openai/text-embedding-3-small + vector_dimensions: 1536 + database: ./vector.db + limit: 20 + - type: bm25 + database: ./bm25.db + limit: 15 + +results: + fusion: + strategy: rrf # Options: rrf, weighted, max + k: 60 # RRF smoothing parameter + deduplicate: true + limit: 5 +``` + +Fusion strategies: + +- `rrf`: Reciprocal Rank Fusion (recommended, rank-based, no normalization needed) +- `weighted`: Weighted combination (`fusion.weights: {chunked-embeddings: 0.7, bm25: 0.3}`) +- `max`: Maximum score across strategies + +### Reranking + +Re-score results with a specialized model for improved relevance: + +```yaml +results: + reranking: + model: openai/gpt-5-mini + top_k: 10 # Only rerank top K (0 = all) + threshold: 0.3 # Minimum score after reranking + criteria: | # Optional domain-specific guidance + Prioritize official docs over blog posts + limit: 5 +``` + +DMR native reranking: + +```yaml +models: + reranker: + provider: dmr + model: hf.co/ggml-org/qwen3-reranker-0.6b-q8_0-gguf + +results: + reranking: + model: reranker +``` + +### Code-aware chunking + +For source code, use AST-based chunking. With semantic-embeddings, you can +include AST metadata in the LLM prompts: + +```yaml +- type: semantic-embeddings + embedding_model: openai/text-embedding-3-small + vector_dimensions: 1536 + chat_model: openai/gpt-5-mini + database: ./code.db + ast_context: true # Include AST metadata in semantic prompts + chunking: + size: 2000 + code_aware: true # Enable AST-based chunking +``` + +### RAG properties + +Top-level RAG source: + +| Field | Type | Description | +| ------------ | -------- | --------------------------------------------------------------- | +| `docs` | []string | Document paths (suppports glob patterns, respects `.gitignore`) | +| `tool` | object | Customize RAG tool name/description/instruction | +| `strategies` | []object | Retrieval strategies (see above for strategy-specific fields) | +| `results` | object | Post-processing (fusion, reranking, limits) | + +Results: + +| Field | Type | Default | +| --------------------- | ------- | ------- | +| `limit` | integer | 15 | +| `deduplicate` | boolean | true | +| `include_score` | boolean | false | +| `fusion.strategy` | string | - | +| `fusion.k` | integer | 60 | +| `fusion.weights` | object | - | +| `reranking.model` | string | - | +| `reranking.top_k` | integer | 0 | +| `reranking.threshold` | float | 0.5 | +| `reranking.criteria` | string | "" | +| `return_full_content` | boolean | false | + +## Metadata + +Documentation and sharing information: + +| Property | Type | Description | +| --------- | ------ | ------------------------------- | +| `author` | string | Author name | +| `license` | string | License (e.g., MIT, Apache-2.0) | +| `readme` | string | Usage documentation | + +```yaml +metadata: + author: Your Name + license: MIT + readme: | + Description and usage instructions +``` + +## Example configuration + +Complete configuration demonstrating key features: + +```yaml +agents: + root: + model: claude + description: Technical lead + instruction: Coordinate development tasks and delegate to specialists + sub_agents: [developer, reviewer] + toolsets: + - type: filesystem + - type: mcp + ref: docker:duckduckgo + rag: [readmes] + commands: + status: "Check project status" + + developer: + model: gpt + description: Software developer + instruction: Write clean, maintainable code + toolsets: + - type: filesystem + - type: shell + + reviewer: + model: claude + description: Code reviewer + instruction: Review for quality and security + toolsets: + - type: filesystem + +models: + gpt: + provider: openai + model: gpt-5 + + claude: + provider: anthropic + model: claude-sonnet-4-5 + max_tokens: 64000 + +rag: + readmes: + docs: ["**/README.md"] + strategies: + - type: chunked-embeddings + embedding_model: openai/text-embedding-3-small + vector_dimensions: 1536 + database: ./embeddings.db + limit: 10 + - type: bm25 + database: ./bm25.db + limit: 10 + results: + fusion: + strategy: rrf + k: 60 + limit: 5 +``` + +## What's next + +- Read the [Toolsets reference](./toolsets.md) for detailed toolset documentation +- Review the [CLI reference](./cli.md) for command-line options +- Browse [example configurations](https://github.com/docker/cagent/tree/main/examples) +- Learn about [sharing agents](../sharing-agents.md) diff --git a/content/manuals/ai/cagent/reference/examples.md b/content/manuals/ai/cagent/reference/examples.md new file mode 100644 index 000000000000..82fa44dbe01e --- /dev/null +++ b/content/manuals/ai/cagent/reference/examples.md @@ -0,0 +1,33 @@ +--- +title: Examples +description: Get inspiration from agent examples +keywords: [ai, agent, cagent] +weight: 40 +--- + +Get inspiration from the following agent examples. +See more examples in the [cagent GitHub repository](https://github.com/docker/cagent/tree/main/examples). + +## Development team + +{{% cagent-example.inline "dev-team.yaml" %}} +{{- $example := .Get 0 }} +{{- $baseUrl := "https://raw.githubusercontent.com/docker/cagent/refs/heads/main/examples" }} +{{- $url := fmt.Printf "%s/%s" $baseUrl $example }} +{{- with resources.GetRemote $url }} +{{ $data := .Content | transform.Unmarshal }} + +```yaml {collapse=true} +{{ .Content }} +``` + +{{ end }} +{{% /cagent-example.inline %}} + +## Go developer + +{{% cagent-example.inline "gopher.yaml" /%}} + +## Technical blog writer + +{{% cagent-example.inline "blog.yaml" /%}} diff --git a/content/manuals/ai/cagent/reference/toolsets.md b/content/manuals/ai/cagent/reference/toolsets.md new file mode 100644 index 000000000000..5d66355388fd --- /dev/null +++ b/content/manuals/ai/cagent/reference/toolsets.md @@ -0,0 +1,493 @@ +--- +title: Toolsets reference +linkTitle: Toolsets +description: Complete reference for cagent toolsets and their capabilities +keywords: [ai, agent, cagent, tools, toolsets] +weight: 20 +--- + +This reference documents the toolsets available in cagent and what each one +does. Tools give agents the ability to take action—interacting with files, +executing commands, accessing external resources, and managing state. + +For configuration file syntax and how to set up toolsets in your agent YAML, +see the [Configuration file reference](./config.md). + +## How agents use tools + +When you configure toolsets for an agent, those tools become available in the +agent's context. The agent can invoke tools by name with appropriate parameters +based on the task at hand. + +Tool invocation flow: + +1. Agent analyzes the task and determines which tool to use +2. Agent constructs tool parameters based on requirements +3. cagent executes the tool and returns results +4. Agent processes results and decides next steps + +Agents can call multiple tools in sequence or make decisions based on tool +results. Tool selection is automatic based on the agent's understanding of the +task and available capabilities. + +## Tool types + +cagent supports three types of toolsets: + +Built-in toolsets +: Core functionality built directly into cagent (`filesystem`, `shell`, + `memory`, etc.). These provide essential capabilities for file operations, + command execution, and state management. +MCP toolsets +: Tools provided by Model Context Protocol servers, either local processes + (stdio) or remote servers (HTTP/SSE). MCP enables access to a wide ecosystem + of standardized tools. +Custom toolsets +: Shell scripts wrapped as tools with typed parameters (`script_shell`). This + lets you define domain-specific tools for your use case. + +## Configuration + +Toolsets are configured in your agent's YAML file under the `toolsets` array: + +```yaml +agents: + my_agent: + model: anthropic/claude-sonnet-4-5 + description: A helpful coding assistant + toolsets: + # Built-in toolset + - type: filesystem + + # Built-in toolset with configuration + - type: memory + path: ./memories.db + + # Local MCP server (stdio) + - type: mcp + command: npx + args: ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"] + + # Remote MCP server (SSE) + - type: mcp + remote: + url: https://mcp.example.com/sse + transport_type: sse + headers: + Authorization: Bearer ${API_TOKEN} + + # Custom shell tools + - type: script_shell + tools: + build: + cmd: npm run build + description: Build the project +``` + +### Common configuration options + +All toolset types support these optional properties: + +| Property | Type | Description | +| ------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `instruction` | string | Additional instructions for using the toolset | +| `tools` | array | Specific tool names to enable (defaults to all) | +| `env` | object | Environment variables for the toolset | +| `toon` | string | Comma-delimited regex patterns matching tool names whose JSON outputs should be compressed. Reduces token usage by simplifying/compressing JSON responses from matched tools using automatic encoding. Example: `"search.*,list.*"` | +| `defer` | boolean or array | Control which tools load into initial context. Set to `true` to defer all tools, or array of tool names to defer specific tools. Deferred tools don't consume context until explicitly loaded via `search_tool`/`add_tool`. | + +### Tool selection + +By default, agents have access to all tools from their configured toolsets. You +can restrict this using the `tools` option: + +```yaml +toolsets: + - type: filesystem + tools: [read_file, write_file, list_directory] +``` + +This is useful for: + +- Limiting agent capabilities for security +- Reducing context size for smaller models +- Creating specialized agents with focused tool access + +### Deferred loading + +Deferred loading keeps tools out of the initial context window, loading them +only when explicitly requested. This is useful for large toolsets where most +tools won't be used, significantly reducing context consumption. + +Defer all tools in a toolset: + +```yaml +toolsets: + - type: mcp + command: npx + args: ["-y", "@modelcontextprotocol/server-filesystem", "/path"] + defer: true # All tools load on-demand +``` + +Or defer specific tools while loading others immediately: + +```yaml +toolsets: + - type: mcp + command: npx + args: ["-y", "@modelcontextprotocol/server-filesystem", "/path"] + defer: [search_files, list_directory] # Only these are deferred +``` + +Agents can discover deferred tools via `search_tool` and load them into context +via `add_tool` when needed. Best for toolsets with dozens of tools where only a +few are typically used. + +### Output compression + +The `toon` property compresses JSON outputs from matched tools to reduce token +usage. When a tool's output is JSON, it's automatically compressed using +efficient encoding before being returned to the agent: + +```yaml +toolsets: + - type: mcp + command: npx + args: ["-y", "@modelcontextprotocol/server-github"] + toon: "search.*,list.*" # Compress outputs from search/list tools +``` + +Useful for tools that return large JSON responses (API results, file listings, +search results). The compression is transparent to the agent but can +significantly reduce context consumption for verbose tool outputs. + +### Per-agent tool configuration + +Different agents can have different toolsets: + +```yaml +agents: + coordinator: + model: anthropic/claude-sonnet-4-5 + sub_agents: [code_writer, code_reviewer] + toolsets: + - type: filesystem + tools: [read_file] + + code_writer: + model: openai/gpt-5-mini + toolsets: + - type: filesystem + - type: shell + + code_reviewer: + model: anthropic/claude-sonnet-4-5 + toolsets: + - type: filesystem + tools: [read_file, read_multiple_files] +``` + +This allows specialized agents with focused capabilities, security boundaries, +and optimized performance. + +## Built-in tools reference + +### Filesystem + +The `filesystem` toolset gives your agent the ability to work with +files and directories. Your agent can read files to understand +context, write new files, make targeted edits to existing files, +search for content, and explore directory structures. Essential for +code analysis, documentation updates, configuration management, and +any agent that needs to understand or modify project files. + +Access is restricted to the current working directory by default. Agents can +request access to additional directories at runtime, which requires your +approval. + +#### Configuration + +```yaml +toolsets: + - type: filesystem + + # Optional: restrict to specific tools + - type: filesystem + tools: [read_file, write_file, edit_file] +``` + +#### Available tools + +| Tool | Capability | +| --------------------------- | --------------------------------------------- | +| `read_file` | Read file contents | +| `write_file` | Write new files | +| `edit_file` | Make targeted edits to existing files | +| `read_multiple_files` | Read multiple files at once | +| `search_files` | Find files by name or pattern | +| `search_files_content` | Search file contents (grep-like) | +| `list_directory` | List directory contents | +| `directory_tree` | View recursive directory structure | +| `create_directory` | Create directories | +| `move_file` | Move or rename files | +| `get_file_info` | Get file/directory metadata | +| `add_allowed_directory` | Request access to additional directories | +| `list_allowed_directories` | Check currently allowed directories | +| `list_directory_with_sizes` | List directory contents with size information | + +### Shell + +The `shell` toolset lets your agent execute commands in your system's shell +environment. Use this for agents that need to run builds, execute tests, manage +processes, interact with CLI tools, or perform system operations. The agent can +run commands in the foreground or background. + +Commands execute in the current working directory and inherit environment +variables from the cagent process. This toolset is powerful but should be used +with appropriate security considerations. + +#### Configuration + +```yaml +toolsets: + - type: shell +``` + +#### Available tools + +| Tool | Capability | +| ------- | ----------------------------------------- | +| `shell` | Execute shell commands in the environment | + +### Think + +The `think` toolset provides your agent with a reasoning scratchpad. The agent +can record thoughts and reasoning steps without taking actions or modifying +data. Particularly useful for complex tasks where the agent needs to plan +multiple steps, verify requirements, or maintain context across a long +conversation. + +Agents use this to break down problems, list applicable rules, verify they have +all needed information, and document their reasoning process before acting. + +#### Configuration + +```yaml +toolsets: + - type: think +``` + +#### Available tools + +| Tool | Capability | +| ------- | ----------------------------------------------- | +| `think` | Record reasoning thoughts without taking action | + +### Todo + +The `todo` toolset gives your agent task-tracking capabilities for managing +multi-step operations. Your agent can break down complex work into discrete +tasks, track progress through each step, and ensure nothing is missed before +completing a request. Especially valuable for agents handling complex +workflows with multiple dependencies. + +The `shared` option allows todos to persist across different agents in a +multi-agent system, enabling coordination. + +#### Configuration + +```yaml +toolsets: + - type: todo + + # Optional: share todos across agents + - type: todo + shared: true +``` + +#### Available tools + +| Tool | Capability | +| -------------- | --------------------------------- | +| `create_todo` | Create individual todo items | +| `create_todos` | Create multiple todos at once | +| `update_todo` | Update todo status | +| `list_todos` | List all current todos and status | + +### Memory + +The `memory` toolset allows your agent to store and retrieve information across +conversations and sessions. Your agent can remember user preferences, project +context, previous decisions, and other information that should persist. Useful +for agents that interact with users over time or need to maintain state about +a project or environment. + +Memories are stored in a local database file and persist across cagent +sessions. + +#### Configuration + +```yaml +toolsets: + - type: memory + + # Optional: specify database location + - type: memory + path: ./agent-memories.db +``` + +#### Available tools + +| Tool | Capability | +| ----------------- | ---------------------------------- | +| `store_memory` | Store information persistently | +| `retrieve_memory` | Retrieve stored information by key | +| `search_memory` | Search memories by content or tags | +| `list_memories` | List all stored memories | + +### Fetch + +The `fetch` toolset enables your agent to retrieve content from HTTP/HTTPS URLs. +Your agent can fetch documentation, API responses, web pages, or any content +accessible via HTTP GET requests. Useful for agents that need to access +external resources, check API documentation, or retrieve web content. + +The agent can specify custom HTTP headers when needed for authentication or +other purposes. + +#### Configuration + +```yaml +toolsets: + - type: fetch +``` + +#### Available tools + +| Tool | Capability | +| ------- | --------------------------------------- | +| `fetch` | Retrieve content from URLs via HTTP GET | + +### API + +The `api` toolset lets you define custom tools that call HTTP APIs. Similar to +`script_shell` but for web services, this allows you to expose REST APIs, +webhooks, or any HTTP endpoint as a tool your agent can use. The agent sees +these as typed tools with automatic parameter validation. + +Use this to integrate with external services, call internal APIs, trigger +webhooks, or interact with any HTTP-based system. + +#### Configuration + +Each API tool is defined with an `api_config` containing the endpoint, HTTP method, and optional typed parameters: + +```yaml +toolsets: + - type: api + api_config: + name: search_docs + endpoint: https://api.example.com/search + method: GET + instruction: Search the documentation database + headers: + Authorization: Bearer ${API_TOKEN} + args: + query: + type: string + description: Search query + limit: + type: number + description: Maximum results + required: [query] + + - type: api + api_config: + name: create_ticket + endpoint: https://api.example.com/tickets + method: POST + instruction: Create a support ticket + args: + title: + type: string + description: Ticket title + description: + type: string + description: Ticket description + required: [title, description] +``` + +For GET requests, parameters are interpolated into the endpoint URL. For POST +requests, parameters are sent as JSON in the request body. + +Supported argument types: `string`, `number`, `boolean`, `array`, `object`. + +### Script Shell + +The `script_shell` toolset lets you define custom tools by wrapping shell +commands with typed parameters. This allows you to expose domain-specific +operations to your agent as first-class tools. The agent sees these custom +tools just like built-in tools, with parameter validation and type checking +handled automatically. + +Use this to create tools for deployment scripts, build commands, test runners, +or any operation specific to your project or workflow. + +#### Configuration + +Each custom tool is defined with a command, description, and optional typed +parameters: + +```yaml +toolsets: + - type: script_shell + tools: + deploy: + cmd: ./deploy.sh + description: Deploy the application to an environment + args: + environment: + type: string + description: Target environment (dev, staging, prod) + version: + type: string + description: Version to deploy + required: [environment] + + run_tests: + cmd: npm test + description: Run the test suite + args: + filter: + type: string + description: Test name filter pattern +``` + +Supported argument types: `string`, `number`, `boolean`, `array`, `object`. + +#### Tools + +The tools you define become available to your agent. In the previous example, +the agent would have access to `deploy` and `run_tests` tools. + +## Automatic tools + +Some tools are automatically added to agents based on their configuration. You +don't configure these explicitly—they appear when needed. + +### transfer_task + +Automatically available when your agent has `sub_agents` configured. Allows +the agent to delegate tasks to sub-agents and receive results back. + +### handoff + +Automatically available when your agent has `handoffs` configured. Allows the +agent to transfer the entire conversation to a different agent. + +## What's next + +- Read the [Configuration file reference](./config.md) for YAML file structure +- Review the [CLI reference](./cli.md) for running agents +- Explore [MCP servers](/manuals/ai/mcp-catalog-and-toolkit/mcp-gateway.md) for extended capabilities +- Browse [example configurations](https://github.com/docker/cagent/tree/main/examples) diff --git a/content/manuals/ai/cagent/sharing-agents.md b/content/manuals/ai/cagent/sharing-agents.md new file mode 100644 index 000000000000..0d6a5efa38ce --- /dev/null +++ b/content/manuals/ai/cagent/sharing-agents.md @@ -0,0 +1,96 @@ +--- +title: Sharing agents +description: Distribute agent configurations through OCI registries +keywords: [cagent, oci, registry, docker hub, sharing, distribution] +weight: 30 +--- + +Push your agent to a registry and share it by name. Your teammates +reference `agentcatalog/security-expert` instead of copying YAML files +around or asking you where your agent configuration lives. + +When you update the agent in the registry, everyone gets the new version +the next time they pull or restart their client. + +## Prerequisites + +To push agents to a registry, authenticate first: + +```console +$ docker login +``` + +For other registries, use their authentication method. + +## Publishing agents + +Push your agent configuration to a registry: + +```console +$ cagent push ./agent.yml myusername/agent-name +``` + +Push creates the repository if it doesn't exist yet. Use Docker Hub or +any OCI-compatible registry. + +Tag specific versions: + +```console +$ cagent push ./agent.yml myusername/agent-name:v1.0.0 +$ cagent push ./agent.yml myusername/agent-name:latest +``` + +## Using published agents + +Pull an agent to inspect it locally: + +```console +$ cagent pull agentcatalog/pirate +``` + +This saves the configuration as a local YAML file. + +Run agents directly from the registry: + +```console +$ cagent run agentcatalog/pirate +``` + +Or reference it directly in integrations: + +### Editor integration (ACP) + +Use registry references in ACP configurations so your editor always uses +the latest version: + +```json +{ + "agent_servers": { + "myagent": { + "command": "cagent", + "args": ["acp", "agentcatalog/pirate"] + } + } +} +``` + +### MCP client integration + +Agents can be exposed as tools in MCP clients: + +```json +{ + "mcpServers": { + "myagent": { + "command": "/usr/local/bin/cagent", + "args": ["mcp", "agentcatalog/pirate"] + } + } +} +``` + +## What's next + +- Set up [ACP integration](./integrations/acp.md) with shared agents +- Configure [MCP integration](./integrations/mcp.md) with shared agents +- Browse the [agent catalog](https://hub.docker.com/u/agentcatalog) for examples diff --git a/content/manuals/ai/cagent/tools/_index.md b/content/manuals/ai/cagent/tools/_index.md deleted file mode 100644 index f867d57c1178..000000000000 --- a/content/manuals/ai/cagent/tools/_index.md +++ /dev/null @@ -1,195 +0,0 @@ ---- -title: Builtin tools -description: cagent's builtin tools -keywords: [ai, agent, cagent] -weight: 10 ---- - -Tools are what make agents useful. They give your agent the ability to interact -with external systems, execute commands, access files, fetch web content, and -much more. Without tools, an agent can only generate text-with tools, it can -take action. - -## What are Toolsets? - -`cagent` organizes tools into **toolsets** - logical groups of related tools -that work together to accomplish specific tasks. For example: - -- The **filesystem** toolset provides tools for reading, writing, searching, and - managing files -- The **shell** toolset enables command execution in your terminal -- The **memory** toolset allows agents to remember information across - conversations -- An **MCP server** is a toolset that can provide any number of custom tools - -Toolsets are configured in your agent YAML file and determine what capabilities -your agent has access to. - -## Types of Toolsets - -`cagent` supports several types of toolsets: - -### Builtin Toolsets - -Built directly into cagent, these toolsets provide core functionality: - -| Toolset | Description | -| ---------------- | ------------------------------------------------------------- | -| `filesystem` | Read, write, search, and manage files and directories | -| `shell` | Execute shell commands in your environment | -| `memory` | Store and retrieve persistent information about users | -| `fetch` | Retrieve content from HTTP/HTTPS URLs | -| `think` | Reasoning scratchpad for complex planning | -| `todo` | Task tracking for multi-step operations | -| `script_shell` | Define custom parameterized shell commands as tools | - -[Learn more about builtin toolsets →](/docs/tools/builtin/filesystem) - -### MCP Servers - -The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) is an open -standard for connecting AI assistants to external systems. MCP servers act as -toolsets in cagent, providing standardized access to a wide ecosystem of tools. - -**Local MCP Servers (stdio):** Run as local processes that communicate via -standard input/output. Great for accessing local resources and services. - -**Remote MCP Servers (SSE/HTTP):** Connect to remote servers over HTTP, enabling -access to web APIs, databases, and cloud services. - -[Learn more about MCP servers →](/docs/mcp) - -### Custom Shell Scripts - -Using the `script_shell` toolset, you can define your own custom tools that -execute shell commands with typed parameters: - -```yaml -toolsets: - - type: script_shell - tools: - deploy: - cmd: "./deploy.sh" - description: "Deploy the application" - args: - environment: - type: string - description: "Target environment" - required: - - environment -``` - -[Learn more about custom shell tools →](/docs/tools/builtin/script_shell) - -## Configuring Toolsets - -Toolsets are configured in your agent's YAML file under the `toolsets` array: - -```yaml -agents: - my_agent: - model: gpt-4o - description: "A helpful coding assistant" - toolsets: - # Builtin toolset - simple type reference - - type: filesystem - - # Builtin toolset with configuration - - type: memory - path: "./memories.db" - - # Local MCP server (stdio) - - type: mcp - command: npx - args: - - "-y" - - "@modelcontextprotocol/server-filesystem" - - "/path/to/directory" - - # Remote MCP server (SSE) - - type: mcp - remote: - url: "https://api.example.com/mcp" - transport_type: sse - headers: - Authorization: "Bearer ${API_TOKEN}" - - # Custom shell tools - - type: script_shell - tools: - build: - cmd: "npm run build" - description: "Build the project" -``` - -## Toolset Configuration Options - -Each toolset type may have specific configuration options. Common options are: - -- `instruction`: Additional instructions for using the toolset (optional) -- `tools`: Array of specific tool names to enable (optional, defaults to all) -- `env`: Environment variables for the toolset (optional) - -## Tool Selection - -By default, agents have access to all tools provided by their configured -toolsets. You can restrict this using the `tools` option: - -```yaml -toolsets: - - type: filesystem - tools: - - read_file - - write_file - - list_directory - # Agent only gets these three filesystem tools -``` - -This is useful for: -- Limiting agent capabilities for security -- Reducing context size for smaller models -- Creating specialized agents with focused tool access - -## Best Practices - -### Performance - -- **Choose appropriate toolsets**: Don't load toolsets the agent won't use -- **Limit tool selection**: Use the `tools` array to restrict available tools -- **Consider model capabilities**: Smaller models may struggle with too many - tools - -## Multi-Agent Systems - -Different agents in a multi-agent system can have different toolsets: - -```yaml -agents: - coordinator: - model: gpt-4o - sub_agents: - - code_writer - - code_reviewer - toolsets: - - type: transfer_task - - code_writer: - model: gpt-4o - toolsets: - - type: filesystem - - type: shell - - code_reviewer: - model: gpt-4o - toolsets: - - type: filesystem - tools: - - read_file - - read_multiple_files -``` - -This allows you to: -- Create specialized agents with focused capabilities -- Implement security boundaries between agents -- Optimize performance by limiting each agent's toolset - diff --git a/content/manuals/ai/cagent/tools/builtin/fetch.md b/content/manuals/ai/cagent/tools/builtin/fetch.md deleted file mode 100644 index 193cebebe067..000000000000 --- a/content/manuals/ai/cagent/tools/builtin/fetch.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -title: Fetch -description: Fetch content from remote URLs ---- - -This toolset allows your agent to fetch content from HTTP and HTTPS URLs. It -supports multiple URLs in a single call, respects robots.txt restrictions, and -can return content in different formats (text, markdown, or HTML). - -## Usage - -```yaml -toolsets: - - type: fetch - timeout: 30 # Optionally set a default timeout for HTTP requests -``` - -## Tools - -| Name | Description | -| ------- | ---------------------------------------------------------------- | -| `fetch` | Fetch content from one or more HTTP/HTTPS URLs with metadata | - -### fetch - -Fetches content from one or more HTTP/HTTPS URLs and returns the response body -along with metadata such as status code, content type, and content length. - -Args: - -- `urls`: Array of URLs to fetch (required) -- `format`: The format to return the content in - `text`, `markdown`, or `html` - (required) -- `timeout`: Request timeout in seconds, default is 30, maximum is 300 - (optional) - -**Features:** - -- Support for multiple URLs in a single call -- Returns response body and metadata (status code, content type, length) -- Automatic HTML to markdown or text conversion based on format parameter -- Respects robots.txt restrictions -- Configurable timeout per request - -**Example:** - -Single URL: -```json -{ - "urls": ["https://example.com"], - "format": "markdown", - "timeout": 60 -} -``` - -Multiple URLs: -```json -{ - "urls": ["https://example.com", "https://another.com"], - "format": "text" -} -``` diff --git a/content/manuals/ai/cagent/tools/builtin/filesystem.md b/content/manuals/ai/cagent/tools/builtin/filesystem.md deleted file mode 100644 index c5bba41839c4..000000000000 --- a/content/manuals/ai/cagent/tools/builtin/filesystem.md +++ /dev/null @@ -1,147 +0,0 @@ ---- -title: Filesystem -description: Access and manage the filesystem ---- - -This toolset gives your agent access to your filesystem. By default the -filesystem tool has access to the current working directory, this can be -modified in your agent file, an agent can also decide, thanks to -`add_allowed_directory`, to ask for permission for a directory access. - -## Usage - -```yaml title="agent.yaml" -toolsets: - - type: filesystem -``` - -## Tools - -| Name | Description | -| --------------------------- | --------------------------------------------------- | -| `add_allowed_directory` | Adds a directory to the list of allowed directories | -| `create_directory` | Creates a directory | -| `directory_tree` | Returns a recursive view of the directory tree | -| `edit_file` | Modifies a file | -| `get_file_info` | Get stat info about a file/directory | -| `list_allowed_directories` | Returns the list of currently allowed directories | -| `list_directory` | Lists the contents of a directory | -| `list_directory_with_sizes` | Desc | -| `move_file` | Moves a file | -| `read_file` | Reads a file | -| `read_multiple_files` | Reads multiple files | -| `search_files` | Search for files by filename/pattern | -| `search_files_content` | Grep-like search | -| `write_file` | Writes a file | - -### add_allowed_directory - -By default the filesystem tool only has access to the current working directory. -This tool allows the agent to request access to other directories. - -Args: - -- `path`: The directory path to add to the list of allowed directories. - -### create_directory - -Creates a directory at the specified path. - -Args: - -- `path`: The directory path to create. - -### directory_tree - -Returns a recursive view of the directory tree starting from the specified path. - -Args: - -- `path`: The directory path to view. -- `max_depth`: The maximum depth to recurse into the directory tree. - -### edit_file - -Modifies a file at the specified path using a series of edit operations. - -Args: - -- `path`: The file path to edit. -- `edits`: Array of edit operations. - -### get_file_info - -Gets stat info about a file or directory. - -Args: - -- `path`: The file or directory path to get info about. - -### list_allowed_directories - -Returns the list of currently allowed directories. - -### list_directory - -Lists the contents of a directory. - -Args: -- `path`: The directory path to list. - -### list_directory_with_sizes - -Lists the contents of a directory along with the sizes of each item. - -Args: -- `path`: The directory path to list. - -### move_file - -Moves a file from one location to another. - -Args: -- `source`: The source file path. -- `destination`: The destination file path. - -### read_file - -Reads a file at the specified path. - -Args: -- `path`: The file path to read. - -### read_multiple_files - -Reads multiple files at the specified paths. - -Args: -- `paths`: An array of file paths to read. -- `json` (optional): If true, returns the contents as a JSON object. - -### search_files - -Search for files by filename or pattern. - -Args: -- `pattern`: The filename or pattern to search for. -- `path`: The starting directory path. -- `excludePatterns`: Patterns to exclude from search. - -### search_files_content - -Grep-like search for file contents. - -Args: -- `pattern`: The content pattern to search for. -- `path`: The starting directory path. -- `query`: The text or regex pattern to search for. -- `isRegex`: If true, treat query as regex; otherwise literal text. -- `excludePatterns`: Patterns to exclude from search. - -### write_file - -Writes a file at the specified path. - -Args: -- `path`: The file path to write. -- `content`: The content to write to the file. diff --git a/content/manuals/ai/cagent/tools/builtin/memory.md b/content/manuals/ai/cagent/tools/builtin/memory.md deleted file mode 100644 index fda1ec5468c7..000000000000 --- a/content/manuals/ai/cagent/tools/builtin/memory.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: Memory -description: Agentic memory for your agent ---- - -The memory toolset gives your agent the ability to store, retrieve, and delete -persistent memories about the user. This allows the agent to remember important -information across conversations. - -## Usage - -```yaml -toolsets: - - type: memory - path: "./memories.db" -``` - -## Tools - -| Name | Description | -| ---------------- | -------------------------------------- | -| `add_memory` | Add a new memory to the database | -| `get_memories` | Retrieve all stored memories | -| `delete_memory` | Delete a specific memory by ID | - -### add_memory - -Adds a new memory to the database with an auto-generated ID and timestamp. - -Args: - -- `memory`: The memory content to store (required) - -**Example:** - -```json -{ - "memory": "User prefers dark mode for all interfaces" -} -``` - -### get_memories - -Retrieves all stored memories from the database. Returns an array of memory -objects, each containing an ID, creation timestamp, and the memory content. - -No arguments required. - -**Response format:** - -```json -[ - { - "id": "1234567890", - "createdAt": "2024-01-15T10:30:00Z", - "memory": "User prefers dark mode for all interfaces" - } -] -``` - -### delete_memory - -Deletes a specific memory from the database by its ID. - -Args: - -- `id`: The ID of the memory to delete (required) - -**Example:** - -```json -{ - "id": "1234567890" -} -``` - -## Best Practices - -- Use `get_memories` at the start of conversations to retrieve relevant context -- Store important user preferences, facts, and context -- Delete outdated or incorrect memories when necessary -- Create specific, actionable memories rather than vague observations diff --git a/content/manuals/ai/cagent/tools/builtin/script_shell.md b/content/manuals/ai/cagent/tools/builtin/script_shell.md deleted file mode 100644 index bad18239f4bf..000000000000 --- a/content/manuals/ai/cagent/tools/builtin/script_shell.md +++ /dev/null @@ -1,141 +0,0 @@ ---- -title: Script Shell -description: Define custom shell command as tools ---- - -This toolset allows you to define custom shell command tools with typed -parameters in your agent configuration. This enables you to create reusable, -parameterized shell commands as first-class tools for your agent. - -## Usage - -```yaml -toolsets: - - type: script_shell - tools: - deploy: - cmd: "./deploy.sh" - description: "Deploy the application to production" - workingDir: "./scripts" - args: - environment: - type: string - description: "Target environment (staging/production)" - version: - type: string - description: "Version to deploy" - required: - - environment - - version - - run_tests: - cmd: "go test -v -race ./..." - description: "Run Go tests with race detection" - workingDir: "." - args: - package: - type: string - description: "Specific package to test (optional)" -``` - -## Configuration - -Each custom tool is defined with the following properties: - -- `cmd`: The shell command to execute (required) -- `description`: Human-readable description of what the tool does (optional, - defaults to showing the command) -- `workingDir`: Working directory to execute the command in (optional) -- `args`: Object defining typed parameters that can be passed to the command - (optional) -- `required`: Array of required parameter names (optional, defaults to all args - being required) - -## Parameters - -Parameters defined in `args` are passed to the command as environment variables. -Each parameter can specify: - -- `type`: The parameter type (string, number, boolean, etc.) -- `description`: Description of what the parameter is for - -## Examples - -### Simple command without parameters - -```yaml -toolsets: - - type: script_shell - tools: - build: - cmd: "npm run build" - description: "Build the frontend application" - workingDir: "./frontend" -``` - -### Command with required parameters - -```yaml -toolsets: - - type: script_shell - tools: - create_migration: - cmd: "migrate create -ext sql -dir ./migrations -seq $name" - description: "Create a new database migration" - args: - name: - type: string - description: "Name of the migration" - required: - - name -``` - -### Command with optional parameters - -```yaml -toolsets: - - type: script_shell - tools: - run_benchmark: - cmd: | - if [ -n "$package" ]; then - go test -bench=. -benchmem $package - else - go test -bench=. -benchmem ./... - fi - description: "Run Go benchmarks" - args: - package: - type: string - description: "Specific package to benchmark (optional)" - required: [] -``` - -## How It Works - -1. Parameters are passed as environment variables to the shell command -2. Commands execute in the specified `workingDir` or current directory -3. The command runs in the user's default shell (`$SHELL` on Unix, or `/bin/sh`) -4. stdout and stderr are combined and returned as the tool result - -## Best Practices - -- **Use descriptive names** - Tool names should clearly indicate their purpose -- **Document parameters** - Provide clear descriptions for all parameters -- **Set working directories** - Use `workingDir` to ensure commands run in the - correct context -- **Handle optional parameters** - Use shell conditionals when parameters are - optional -- **Keep commands focused** - Each tool should do one thing well -- **Use shell scripts for complex logic** - For multi-step operations, call a - shell script rather than inlining complex commands - -## Use Cases - -- Deployment automation -- Running tests with specific configurations -- Database migrations -- Code generation -- Build system integration -- CI/CD operations -- Custom project-specific workflows diff --git a/content/manuals/ai/cagent/tools/builtin/shell.md b/content/manuals/ai/cagent/tools/builtin/shell.md deleted file mode 100644 index 2574e2e34c82..000000000000 --- a/content/manuals/ai/cagent/tools/builtin/shell.md +++ /dev/null @@ -1,93 +0,0 @@ ---- -title: Shell -description: Execute shell commands in the user's environment ---- - -This toolset allows your agent to execute shell commands in the user's default -shell environment. Commands run with full access to environment variables and -can be executed in any working directory. - -## Usage - -```yaml -toolsets: - - type: shell -``` - -## Tools - -| Name | Description | -| ------- | ------------------------------------------------ | -| `shell` | Execute shell commands in the user's environment | - -### shell - -Executes shell commands in the user's default shell. On Windows, PowerShell -(pwsh/powershell) is used when available; otherwise, cmd.exe is used. On -Unix-like systems, the `$SHELL` environment variable is used, or `/bin/sh` as a -fallback. - -Args: - -- `cmd`: The shell command to execute (required) -- `cwd`: Working directory to execute the command in (required, use "." for - current directory) -- `timeout`: Command execution timeout in seconds, default is 30 (optional) - -**Features:** - -- Supports pipes, redirections, and complex shell operations -- Each command runs in a fresh shell session (no state persists) -- Automatic timeout protection to prevent hanging commands -- Full access to environment variables -- Support for heredocs and multi-line scripts - -**Examples:** - -Basic command: -```json -{ - "cmd": "ls -la", - "cwd": "." -} -``` - -Long-running command with custom timeout: -```json -{ - "cmd": "npm run build", - "cwd": ".", - "timeout": 120 -} -``` - -Using pipes: -```json -{ - "cmd": "cat package.json | jq '.dependencies'", - "cwd": "frontend" -} -``` - -Complex script with heredoc: -```json -{ - "cmd": "cat << 'EOF' | ${SHELL}\necho 'Hello'\necho 'World'\nEOF", - "cwd": "." -} -``` - -## Best Practices - -- Use the `cwd` parameter for directory-specific commands -- Quote arguments containing spaces or special characters -- Use the `timeout` parameter for long-running operations (builds, tests, etc.) -- Prefer heredocs over writing temporary script files -- Leverage this tool for batch file operations - -## Error Handling - -- Commands with non-zero exit codes return error information along with any - output -- Commands that exceed their timeout are automatically terminated -- Output includes both stdout and stderr combined diff --git a/content/manuals/ai/cagent/tools/builtin/think.md b/content/manuals/ai/cagent/tools/builtin/think.md deleted file mode 100644 index c4a2d6f74512..000000000000 --- a/content/manuals/ai/cagent/tools/builtin/think.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: Think -description: Thought recording and reasoning tool ---- - -This toolset provides your agent with a scratchpad for reasoning and planning. -It allows the agent to record thoughts without performing actions or changing -data, making it useful for complex reasoning tasks. - -## Usage - -```yaml -toolsets: - - type: think -``` - -## Tools - -| Name | Description | -| ------- | ---------------------------------------------- | -| `think` | Record thoughts for reasoning and planning | - -### think - -Use this tool to think about something. It will not obtain new information or -change the database, but will append the thought to a log. This is useful when -complex reasoning or cache memory is needed. - -Args: - -- `thought`: The thought to think about (required) - -**Use Cases:** - -- List specific rules that apply to the current request -- Check if all required information has been collected -- Verify that planned actions comply with policies -- Iterate over tool results for correctness -- Break down complex problems into steps -- Record intermediate reasoning steps - -**Example:** - -```json -{ - "thought": "The user wants to create a new feature. I need to: 1) Check existing code structure, 2) Identify dependencies, 3) Create new files, 4) Update configuration" -} -``` - -## Best Practices - -- Use the think tool generously before taking actions -- Record your reasoning process step-by-step -- Use it to verify tool results and plan next steps -- Helpful for maintaining context during multi-step tasks -- Use it to check compliance with rules and policies before proceeding - -## Output - -The tool returns all accumulated thoughts in the session, allowing you to review -your reasoning process: - -``` -Thoughts: -First thought here -Second thought here -Third thought here -``` diff --git a/content/manuals/ai/cagent/tools/builtin/todo.md b/content/manuals/ai/cagent/tools/builtin/todo.md deleted file mode 100644 index 56f2e5bf0609..000000000000 --- a/content/manuals/ai/cagent/tools/builtin/todo.md +++ /dev/null @@ -1,116 +0,0 @@ ---- -title: Todo -description: Task tracking for your agent ---- - -This toolset provides your agent with task tracking capabilities. It allows the -agent to create, update, and list todo items to maintain progress through -complex multi-step tasks. - -## Usage - -```yaml -toolsets: - - type: todo -``` - -## Tools - -| Name | Description | -| --------------- | ---------------------------------------------- | -| `create_todo` | Create a new todo item with a description | -| `create_todos` | Create multiple todo items at once | -| `update_todo` | Update the status of a todo item | -| `list_todos` | List all current todos with their status | - -### create_todo - -Creates a single new todo item with a unique ID and sets its initial status to -"pending". - -Args: - -- `description`: Description of the todo item (required) - -**Example:** - -```json -{ - "description": "Implement user authentication module" -} -``` - -### create_todos - -Creates multiple todo items at once. Useful for breaking down a complex task -into multiple steps at the beginning. - -Args: - -- `descriptions`: Array of todo item descriptions (required) - -**Example:** - -```json -{ - "descriptions": [ - "Read existing code structure", - "Design new feature architecture", - "Implement core functionality", - "Write tests", - "Update documentation" - ] -} -``` - -### update_todo - -Updates the status of an existing todo item. Valid statuses are: `pending`, -`in-progress`, and `completed`. - -Args: - -- `id`: ID of the todo item (required) -- `status`: New status - `pending`, `in-progress`, or `completed` (required) - -**Example:** - -```json -{ - "id": "todo_1", - "status": "completed" -} -``` - -### list_todos - -Lists all current todos with their ID, description, and status. No arguments -required. - -**Response format:** - -``` -Current todos: -- [todo_1] Implement user authentication module (Status: completed) -- [todo_2] Design new feature architecture (Status: in-progress) -- [todo_3] Write tests (Status: pending) -``` - -## Best Practices - -- **Always create todos before starting complex tasks** - Break down work into - manageable steps -- **Use list_todos frequently** - Check remaining work before responding to - users -- **Update status regularly** - Mark todos as completed to track progress -- **Never skip steps** - Ensure all todos are addressed before considering a - task complete -- **Use create_todos for batch creation** - More efficient than multiple - create_todo calls - -## Workflow - -1. **Before starting:** Use `create_todos` to break down the task into steps -2. **While working:** Use `list_todos` to check what remains -3. **After each step:** Use `update_todo` to mark completed items -4. **Before finishing:** Use `list_todos` to verify all steps are done diff --git a/content/manuals/ai/cagent/tutorial.md b/content/manuals/ai/cagent/tutorial.md new file mode 100644 index 000000000000..d23bf1207b96 --- /dev/null +++ b/content/manuals/ai/cagent/tutorial.md @@ -0,0 +1,288 @@ +--- +title: Building a coding agent +description: Create a coding agent that can read, write, and validate code changes in your projects +keywords: [cagent, tutorial, coding agent, ai assistant] +weight: 10 +--- + +This tutorial teaches you how to build a coding agent that can help with software +development tasks. You'll start with a basic agent and progressively add +capabilities until you have a production-ready assistant that can read code, +make changes, run tests, and even look up documentation. + +By the end, you'll understand how to structure agent instructions, configure +tools, and compose multiple agents for complex workflows. + +## What you'll build + +A coding agent that can: + +- Read and modify files in your project +- Run commands like tests and linters +- Follow a structured development workflow +- Look up documentation when needed +- Track progress through multi-step tasks + +## What you'll learn + +- How to configure cagent agents in YAML +- How to give agents access to tools (filesystem, shell, etc.) +- How to write effective agent instructions +- How to compose multiple agents for specialized tasks +- How to adapt agents for your own projects + +## Prerequisites + +Before starting, you need: + +- **cagent installed** - See the [installation guide](_index.md#installation) +- **API key configured** - Set `ANTHROPIC_API_KEY` or `OPENAI_API_KEY` in your + environment. Get keys from [Anthropic](https://console.anthropic.com/) or + [OpenAI](https://platform.openai.com/api-keys) +- **A project to work with** - Any codebase where you want agent assistance + +## Creating your first agent + +A cagent agent is defined in a YAML configuration file. The minimal agent needs +just a model and instructions that define its purpose. + +Create a file named `agents.yml`: + +```yaml +agents: + root: + model: openai/gpt-5 + description: A basic coding assistant + instruction: | + You are a helpful coding assistant. + Help me write and understand code. +``` + +Run your agent: + +```console +$ cagent run agents.yml +``` + +Try asking it: "How do I read a file in Python?" + +The agent can answer coding questions, but it can't see your files or run +commands yet. To make it useful for real development work, it needs access to +tools. + +## Adding tools + +A coding agent needs to interact with your project files and run commands. You +enable these capabilities by adding toolsets. + +Update `agents.yml` to add filesystem and shell access: + +```yaml +agents: + root: + model: openai/gpt-5 + description: A coding assistant with filesystem access + instruction: | + You are a helpful coding assistant. + You can read and write files to help me develop software. + Always check if code works before finishing a task. + toolsets: + - type: filesystem + - type: shell +``` + +Run the updated agent and try: "Read the README.md file and summarize it." + +Your agent can now: + +- Read and write files in the current directory +- Execute shell commands +- Explore your project structure + +> [!NOTE] +> By default, filesystem access is restricted to the current working directory. +> The agent will request permission if it needs to access other directories. + +The agent can now interact with your code, but its behavior is still generic. +Next, you'll teach it how to work effectively. + +## Structuring agent instructions + +Generic instructions produce generic results. For production use, you want your +agent to follow a specific workflow and understand your project's conventions. + +Update your agent with structured instructions. This example shows a Go +development agent, but you can adapt the pattern for any language: + +```yaml +agents: + root: + model: anthropic/claude-sonnet-4-5 + description: Expert Go developer + instruction: | + Your goal is to help with code-related tasks by examining, modifying, + and validating code changes. + + + # Workflow: + # 1. Analyze: Understand requirements and identify relevant code. + # 2. Examine: Search for files, analyze structure and dependencies. + # 3. Modify: Make changes following best practices. + # 4. Validate: Run linters/tests. If issues found, return to Modify. + + + Constraints: + - Be thorough in examination before making changes + - Always validate changes before considering the task complete + - Write code to files, don't show it in chat + + ## Development Workflow + - `go build ./...` - Build the application + - `go test ./...` - Run tests + - `golangci-lint run` - Check code quality + + add_date: true + add_environment_info: true + toolsets: + - type: filesystem + - type: shell + - type: todo +``` + +Try asking: "Add error handling to the `parseConfig` function in main.go" + +The structured instructions give your agent: + +- A clear workflow to follow (analyze, examine, modify, validate) +- Project-specific commands to run +- Constraints that prevent common mistakes +- Context about the environment (`add_date` and `add_environment_info`) + +The `todo` toolset helps the agent track progress through multi-step tasks. +When you ask for complex changes, the agent will break down the work and update +its progress as it goes. + +## Composing multiple agents + +Complex tasks often benefit from specialized agents. You can add sub-agents that +handle specific responsibilities, like researching documentation while your main +agent stays focused on coding. + +Add a librarian agent that can search for documentation: + +```yaml +agents: + root: + model: anthropic/claude-sonnet-4-5 + description: Expert Go developer + instruction: | + Your goal is to help with code-related tasks by examining, modifying, + and validating code changes. + + When you need to look up documentation or research how something works, + delegate to the librarian agent. + + (rest of instructions from previous section...) + toolsets: + - type: filesystem + - type: shell + - type: todo + sub_agents: + - librarian + + librarian: + model: anthropic/claude-haiku-4-5 + description: Documentation researcher + instruction: | + You are the librarian. Your job is to find relevant documentation, + articles, or resources to help the developer agent. + + Search the internet and fetch web pages as needed. + toolsets: + - type: mcp + ref: docker:duckduckgo + - type: fetch +``` + +Try asking: "How do I use `context.Context` in Go? Then add it to my server code." + +Your main agent will delegate the research to the librarian, then use that +information to modify your code. This keeps the main agent's context focused on +the coding task while still having access to up-to-date documentation. + +Using a smaller, faster model (Haiku) for the librarian saves costs since +documentation lookup doesn't need the same reasoning depth as code changes. + +## Adapting for your project + +Now that you understand the core concepts, adapt the agent for your specific +project: + +### Update the development commands + +Replace the Go commands with your project's workflow: + +```yaml +## Development Workflow +- `npm test` - Run tests +- `npm run lint` - Check code quality +- `npm run build` - Build the application +``` + +### Add project-specific constraints + +If your agent keeps making the same mistakes, add explicit constraints: + +```yaml +Constraints: + - Always run tests before considering a task complete + - Follow the existing code style in src/ directories + - Never modify files in the generated/ directory + - Use TypeScript strict mode for new files +``` + +### Choose the right models + +For coding tasks, use reasoning-focused models: + +- `anthropic/claude-sonnet-4-5` - Strong reasoning, good for complex code +- `openai/gpt-5` - Fast, good general coding ability + +For auxiliary tasks like documentation lookup, smaller models work well: + +- `anthropic/claude-haiku-4-5` - Fast and cost-effective +- `openai/gpt-5-mini` - Good for simple tasks + +### Iterate based on usage + +The best way to improve your agent is to use it. When you notice issues: + +1. Add specific instructions to prevent the problem +2. Update constraints to guide behavior +3. Add relevant commands to the development workflow +4. Consider adding specialized sub-agents for complex areas + +## What you learned + +You now know how to: + +- Create a basic cagent configuration +- Add tools to enable agent capabilities +- Write structured instructions for consistent behavior +- Compose multiple agents for specialized tasks +- Adapt agents for different programming languages and workflows + +## Next steps + +- Learn [best practices](best-practices.md) for handling large outputs, structuring + agent teams, and optimizing performance +- Integrate cagent with your [editor](integrations/acp.md) or use agents as + [tools in MCP clients](integrations/mcp.md) +- Review the [Configuration reference](reference/config.md) for all available + options +- Explore the [Tools reference](reference/toolsets.md) to see what capabilities you can + enable +- Check out [example configurations](https://github.com/docker/cagent/tree/main/examples) + for different use cases +- See the full [golang_developer.yaml](https://github.com/docker/cagent/blob/main/golang_developer.yaml) + that the Docker team uses to develop cagent