From 365ce6beb82e1e41655f55ae31ceab95826eb2bc Mon Sep 17 00:00:00 2001 From: Djordje Lukic Date: Fri, 28 Nov 2025 10:21:17 +0100 Subject: [PATCH] cagent: completely document fundamentals Signed-off-by: Djordje Lukic --- _vale/config/vocabularies/Docker/accept.txt | 2 + content/manuals/ai/cagent/_index.md | 302 ++++------ content/manuals/ai/cagent/best-practices.md | 248 ++++++++ content/manuals/ai/cagent/examples.md | 166 ------ .../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 | 183 ++++++ content/manuals/ai/cagent/integrations/mcp.md | 294 +++++++++ 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 | 562 ++++++++++++++++++ .../manuals/ai/cagent/reference/examples.md | 33 + .../manuals/ai/cagent/reference/toolsets.md | 438 ++++++++++++++ content/manuals/ai/cagent/sharing-agents.md | 96 +++ content/manuals/ai/cagent/tutorial.md | 291 +++++++++ 16 files changed, 3189 insertions(+), 356 deletions(-) create mode 100644 content/manuals/ai/cagent/best-practices.md delete mode 100644 content/manuals/ai/cagent/examples.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 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 create mode 100644 content/manuals/ai/cagent/tutorial.md diff --git a/_vale/config/vocabularies/Docker/accept.txt b/_vale/config/vocabularies/Docker/accept.txt index fb8c78ea7667..0b4cbb806fda 100644 --- a/_vale/config/vocabularies/Docker/accept.txt +++ b/_vale/config/vocabularies/Docker/accept.txt @@ -292,4 +292,6 @@ Zsh [Vv]irtiofs [Vv]irtualize [Ww]alkthrough +[Tt]oolsets? +[Rr]erank(ing|ed)? diff --git a/content/manuals/ai/cagent/_index.md b/content/manuals/ai/cagent/_index.md index 77ea20c69b29..17ec7493f12f 100644 --- a/content/manuals/ai/cagent/_index.md +++ b/content/manuals/ai/cagent/_index.md @@ -5,236 +5,158 @@ 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: -- ️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). - -## 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` +- **Winget**: `winget install Docker.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](./reference/config.md) +for complete details. -```bash -cagent pull / -``` +## Share agent teams -The agent's configuration file is named `_.yaml`. Run -it with the `cagent run ` command. +Agent configurations are packaged as OCI artifacts. Push and pull them like +container images: -## Related pages +```console +$ cagent push ./debugger.yaml myusername/debugger +$ cagent pull myusername/debugger +``` -- 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) +Use Docker Hub or any OCI-compatible registry. Pushing creates the repository if +it doesn't exist yet. + +## What's next + +- 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..24651673fb52 --- /dev/null +++ b/content/manuals/ai/cagent/best-practices.md @@ -0,0 +1,248 @@ +--- +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. + +## 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 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 8f0388852a2a..000000000000 --- a/content/manuals/ai/cagent/examples.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -title: cagent examples -description: Get inspiration from agent examples -keywords: [ai, agent, cagent] -weight: 10 ---- - -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/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 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 [!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..7cbf5b45959d --- /dev/null +++ b/content/manuals/ai/cagent/reference/config.md @@ -0,0 +1,562 @@ +--- +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 | No | +| `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 versus 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 +``` + +### 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..f9dfcf99441b --- /dev/null +++ b/content/manuals/ai/cagent/reference/toolsets.md @@ -0,0 +1,438 @@ +--- +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] +``` + +### 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 +``` + +### 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 +``` + +### 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 +``` + +### 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 +``` + +### 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 +``` + +### 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/tutorial.md b/content/manuals/ai/cagent/tutorial.md new file mode 100644 index 000000000000..46befbb6ce8c --- /dev/null +++ b/content/manuals/ai/cagent/tutorial.md @@ -0,0 +1,291 @@ +--- +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, + ask 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