Skip to content

AI Agents Guide

Build intelligent, tool-using AI agents with Helix. This guide covers all 7 agent types, 17 LLM providers, tool calling, 8 memory backends, multi-agent orchestration, and agent flows — all configurable via declarative YAML or Go code.

Table of Contents

  1. Overview
  2. Agent Types (Deep Dive)
  3. LLM Providers
  4. Memory Configuration
  5. Tools — incl. middleware, custom orchestration, checkpointing, async interrupts
  6. Governance — kill switches, rings, policy engine, audit chain
  7. Agent Flows
  8. Exposing Agents via Protocols
  9. RAG-Augmented Agents
  10. Advanced Patterns
  11. Production Deployment
  12. Complete Examples
  13. Strict YAML Validation
  14. Human-in-the-Loop
  15. Agent Memory with MemX Backend
  16. Multimodal Agents
  17. Built-in Function Registry
  18. SmartMemory -- Token-Managed Memory
  19. Playwright MCP Browser Tools

1. Overview

What Are Helix Agents?

Helix agents are AI-powered processing units that live inside your integration pipelines. Unlike standalone agent frameworks, Helix agents are first-class integration components: they receive messages from Kafka, respond to HTTP requests, process database events, and route results to downstream systems -- all within the same workflow definition.

An agent in Helix combines four capabilities:

  • LLM reasoning via pluggable providers (OpenAI, Anthropic, Ollama)
  • Tool use for interacting with external systems during reasoning
  • Memory for maintaining conversational and long-term context
  • Orchestration patterns for multi-agent coordination

Top-Level agents: Primitive

Agents can be defined as top-level agents: primitives in YAML, not just as inline steps inside workflows:. The top-level syntax is simpler and AI-first:

# Top-level agents: primitive (recommended for standalone agents)
agents:
  - id: assistant
    type: react
    trigger: http-server://0.0.0.0:8080/chat
    model:
      provider: openai
      model: gpt-4o
    system_prompt: "You are a helpful assistant."
    tools:
      - name: search
        type: function
        description: "Search the knowledge base"
    memory:
      enabled: true
      type: sliding-window
      window_size: 20

Compare with the equivalent inline syntax inside a route:

# Inline agent step inside workflows: (use when the agent is part of a larger pipeline)
workflows:
  - id: chat-route
    trigger:
      uri: http-server://0.0.0.0:8080/chat
    steps:
      - agent:
          id: assistant
          type: react
          model:
            provider: openai
            model: gpt-4o
          system_prompt: "You are a helpful assistant."

Use agents: when the agent IS the endpoint. Use inline agent: steps when the agent is one step in a larger integration pipeline (e.g., consuming from Kafka, then processing with an agent, then routing to another system).

Architecture

                    +------------------+
                    |   Exchange       |
                    |  (Message Body)  |
                    +--------+---------+
                             |
                    +--------v---------+
                    |   Agent Step     |
                    |  (agent://id)    |
                    +--------+---------+
                             |
              +--------------+--------------+
              |              |              |
     +--------v---+  +------v-----+  +-----v------+
     | LLM Provider|  |   Tools    |  |   Memory   |
     | (OpenAI,   |  | (FuncTool, |  | (Sliding,  |
     |  Anthropic,|  |  MCP,      |  |  Vector,   |
     |  Ollama)   |  |  SimpleTool)|  |  SQL, ...) |
     +--------+---+  +------+-----+  +-----+------+
              |              |              |
              +--------------+--------------+
                             |
                    +--------v---------+
                    |   Agent Output   |
                    |  (set on Exchange|
                    |   body/headers)  |
                    +------------------+
                             |
              +--------------+--------------+-----------+
              |              |              |           |
     +--------v---+  +------v-----+  +-----v----+ +---v-----+
     |   AG-UI    |  |    A2A     |  |   A2UI   | |   MCP   |
     | (Frontend  |  | (Agent-to- |  | (Rich UI)| | (Tool   |
     |  Streaming)|  |  Agent)    |  |          | |  Export) |
     +------------+  +------------+  +----------+ +---------+

When to Use Which Agent Type

Agent Type Use When
ReAct General-purpose tasks with tool use. Default choice.
Supervisor You have specialist sub-agents and need intelligent routing.
Plan-Execute Complex tasks that benefit from upfront planning before execution.
Deep Hierarchically complex problems that need recursive decomposition.
Sequential Pipeline of agents where each refines the previous output.
Parallel Independent subtasks that can run concurrently (e.g., multi-perspective analysis).
Loop Iterative refinement until a quality threshold is met.

2. Agent Types (Deep Dive)

2.1 ReAct Agent

What it does: Implements the ReAct (Reason + Act) loop -- the most widely used agent pattern. The agent reasons about what to do, optionally calls tools, observes the results, and repeats until it has a final answer.

How it works internally: 1. Loads conversation history from memory 2. Appends the user message 3. Sends messages + tool definitions to the LLM 4. If the LLM returns tool calls, executes them and appends results 5. Loops back to step 3 (up to max_iterations) 6. If no tool calls, the LLM response is the final answer

YAML example:

agents:
  - id: my-assistant
    type: react
    trigger: direct://assistant
    model:
      provider: openai
      model: gpt-4o
      api_key: "${OPENAI_API_KEY}"
    system_prompt: |
      You are a helpful assistant with access to tools.
      Always verify information before responding.
    memory:
      enabled: true
      type: sliding-window
      window_size: 20
    max_iterations: 15
    tools:
      - name: search_web
        type: function
        description: "Search the web for current information"
        parameters:
          type: object
          properties:
            query:
              type: string
              description: "Search query"
          required: ["query"]

Key parameters: | Parameter | Default | Description | |---|---|---| | max_iterations | 10 | Maximum ReAct loop iterations before failing | | system_prompt | (empty) | System message prepended to all conversations | | memory.type | conversation | Memory backend to use |

Use cases: Customer support chatbots, research assistants, code generation agents, data analysis assistants.


2.2 Supervisor Agent

What it does: Routes incoming requests to the most appropriate specialist sub-agent. The supervisor uses the LLM to analyze the request and decide which sub-agent should handle it.

How it works internally: 1. Presents the LLM with available sub-agent IDs and the user request 2. Asks the LLM to respond with JSON: {"agent": "<id>", "message": "<refined message>"} 3. Parses the routing decision and delegates to the chosen sub-agent 4. Falls back to the first sub-agent if JSON parsing fails

YAML example:

agents:
  - id: support-supervisor
    type: supervisor
    trigger: direct://support
    model:
      provider: anthropic
      model: claude-sonnet-4-20250514
      api_key: "${ANTHROPIC_API_KEY}"
    system_prompt: |
      You are a customer support supervisor. Route requests to the
      appropriate specialist agent based on the customer's needs.
      - billing-agent: payment issues, invoices, refunds
      - technical-agent: bugs, errors, configuration problems
      - sales-agent: pricing, plans, upgrades
    sub_agents:
      - id: billing-agent
        type: react
        model:
          provider: anthropic
          model: claude-sonnet-4-20250514
          api_key: "${ANTHROPIC_API_KEY}"
        system_prompt: "You handle billing and payment inquiries."
        max_iterations: 5
      - id: technical-agent
        type: react
        model:
          provider: openai
          model: gpt-4o
          api_key: "${OPENAI_API_KEY}"
        system_prompt: "You are a technical support specialist."
        max_iterations: 10
        tools:
          - name: query_logs
            type: function
            description: "Query application logs"
            parameters:
              type: object
              properties:
                service:
                  type: string
                timerange:
                  type: string
              required: ["service"]
      - id: sales-agent
        type: react
        model:
          provider: openai
          model: gpt-4o
          api_key: "${OPENAI_API_KEY}"
        system_prompt: "You are a friendly sales specialist."
        max_iterations: 5

Key parameters: | Parameter | Default | Description | |---|---|---| | sub_agents | (required) | List of specialist sub-agents | | system_prompt | (empty) | Instructions for the routing decision |

Use cases: Customer support triage, multi-department helpdesk, domain-specific routing.


2.3 Plan-Execute Agent

What it does: Separates planning from execution. First asks the LLM to create a step-by-step plan (JSON array), then executes each step sequentially using the ReAct pattern, passing accumulated context forward.

How it works internally: 1. Asks the LLM to create a plan: [{"step": 1, "action": "description"}, ...] 2. For each step, runs a ReAct loop with the step description + accumulated results 3. Returns the combined results from all steps

YAML example:

agents:
  - id: research-agent
    type: plan-execute
    trigger: direct://research
    model:
      provider: openai
      model: gpt-4o
      api_key: "${OPENAI_API_KEY}"
    system_prompt: |
      You are a research analyst. When given a research topic,
      create a structured plan and execute each step thoroughly.
    max_iterations: 20
    tools:
      - name: search_papers
        type: function
        description: "Search academic papers"
        parameters:
          type: object
          properties:
            query:
              type: string
            limit:
              type: integer
          required: ["query"]
      - name: summarize_paper
        type: function
        description: "Get and summarize a specific paper"
        parameters:
          type: object
          properties:
            paper_id:
              type: string
          required: ["paper_id"]

Key parameters: | Parameter | Default | Description | |---|---|---| | max_iterations | 10 | Max ReAct iterations per step (not total) | | system_prompt | (empty) | Instructions for both planning and execution |

Use cases: Research tasks, report generation, multi-step data analysis, complex problem solving.


2.4 Deep Agent

What it does: Performs hierarchical task decomposition. Recursively breaks complex tasks into subtasks, solves leaf tasks via ReAct, and synthesizes results back up the tree.

How it works internally: 1. Asks the LLM whether the task can be solved directly or needs decomposition 2. If direct: solves with ReAct 3. If decomposition needed: LLM returns {"direct": false, "subtasks": ["task1", "task2", ...]} 4. Recursively decomposes each subtask (up to deep_max_depth) 5. At max depth, solves directly with ReAct 6. After all subtasks complete, synthesizes a final answer from combined results

YAML example:

agents:
  - id: deep-analyst
    type: deep
    trigger: direct://analyze
    model:
      provider: openai
      model: gpt-4o
      api_key: "${OPENAI_API_KEY}"
    system_prompt: |
      You are a deep analysis agent. Break complex problems into
      manageable subtasks and solve them systematically.
    max_iterations: 10
    deep_max_depth: 4
    tools:
      - name: query_database
        type: function
        description: "Run a SQL query against the analytics database"
        parameters:
          type: object
          properties:
            sql:
              type: string
          required: ["sql"]

Key parameters: | Parameter | Default | Description | |---|---|---| | deep_max_depth | 3 | Maximum recursion depth for task decomposition | | max_iterations | 10 | Max ReAct iterations at each leaf node |

Use cases: Complex data analysis, competitive landscape assessment, architectural reviews, multi-faceted research.


2.5 Sequential Agent

What it does: Runs sub-agents in sequence, passing each agent's output as the next agent's input. This creates a processing pipeline where each agent refines or transforms the previous result.

How it works internally: 1. Runs the first sub-agent with the original user message 2. Takes the response content and passes it as the input to the next sub-agent 3. Repeats until all sub-agents have processed 4. Returns the last sub-agent's response

YAML example:

agents:
  - id: content-pipeline
    type: sequential
    trigger: direct://create-content
    model:
      provider: openai
      model: gpt-4o
      api_key: "${OPENAI_API_KEY}"
    sub_agents:
      - id: researcher
        type: react
        model:
          provider: openai
          model: gpt-4o
          api_key: "${OPENAI_API_KEY}"
        system_prompt: "Research the given topic and provide key facts and data points."
        max_iterations: 5
      - id: writer
        type: react
        model:
          provider: anthropic
          model: claude-sonnet-4-20250514
          api_key: "${ANTHROPIC_API_KEY}"
        system_prompt: "Write a well-structured article based on the research provided."
        max_iterations: 3
      - id: editor
        type: react
        model:
          provider: openai
          model: gpt-4o
          api_key: "${OPENAI_API_KEY}"
        system_prompt: "Edit the article for clarity, grammar, and style. Output the final version."
        max_iterations: 3

Use cases: Content creation pipelines, code review chains, translation + localization, data enrichment.


2.6 Parallel Agent

What it does: Runs all sub-agents concurrently with the same input, collects results, and combines them. Each sub-agent processes independently in its own goroutine.

How it works internally: 1. Launches all sub-agents concurrently (one goroutine per sub-agent) 2. Each sub-agent receives the same user message 3. Collects all responses (errors are logged but do not block other agents) 4. Combines results as [agent-id]: response\n\n for each agent 5. Returns the combined response

YAML example:

agents:
  - id: perspectives
    type: parallel
    trigger: direct://analyze
    model:
      provider: openai
      model: gpt-4o
      api_key: "${OPENAI_API_KEY}"
    sub_agents:
      - id: technical-reviewer
        type: react
        model:
          provider: openai
          model: gpt-4o
          api_key: "${OPENAI_API_KEY}"
        system_prompt: "Analyze this from a technical feasibility perspective."
      - id: business-reviewer
        type: react
        model:
          provider: anthropic
          model: claude-sonnet-4-20250514
          api_key: "${ANTHROPIC_API_KEY}"
        system_prompt: "Analyze this from a business impact perspective."
      - id: risk-reviewer
        type: react
        model:
          provider: openai
          model: gpt-4o
          api_key: "${OPENAI_API_KEY}"
        system_prompt: "Analyze this from a risk and compliance perspective."

Use cases: Multi-perspective analysis, ensemble responses, parallel data processing, A/B response testing.

parallel_error_policy controls what happens when one sub-agent fails:

Value Behaviour
"" (default) First error returned; siblings keep running and their results are collected for later inspection.
fail_fast Cancel siblings on the first error and return it. Best for "all or nothing" pipelines.
collect Wait for all sub-agents, then return errors.Join(err1, err2, …) of every failure. Best for "give me everything that worked plus everything that didn't".
agents:
  - id: perspectives
    type: parallel
    parallel_error_policy: fail_fast
    sub_agents: [ ... ]

Failures always surface as delegate_error events on OnEvent regardless of policy, so dashboards see every miss.


2.7 Loop Agent

What it does: Iteratively refines a response until the agent signals completion (by including "DONE", "COMPLETE", or "FINISHED" in its output) or reaches max_iterations.

How it works internally: 1. Runs a ReAct loop with the user message 2. Checks the response for completion keywords (case-insensitive) 3. If not complete, prepends "Previous iteration result:" and asks the agent to continue refining 4. Repeats up to max_iterations

YAML example:

agents:
  - id: refinement-agent
    type: loop
    trigger: direct://refine
    model:
      provider: openai
      model: gpt-4o
      api_key: "${OPENAI_API_KEY}"
    system_prompt: |
      You are a code quality agent. Review and iteratively improve
      the given code. When you are satisfied with the quality,
      include "DONE" in your response along with the final code.
    max_iterations: 5

Completion signals: The agent must include one of these keywords (case-insensitive) in its response to stop iterating: DONE, COMPLETE, FINISHED.

Key parameters: | Parameter | Default | Description | |---|---|---| | max_iterations | 10 | Maximum refinement iterations |

Use cases: Code quality improvement, essay refinement, iterative problem solving, self-correcting agents.


3. LLM Providers

Helix supports 17 LLM providers (plus a gemini alias for Vertex), all implementing the ChatModel interface with both synchronous and streaming modes. The three quick-start examples below cover the most common cases; the full provider list with auth and endpoint details lives in the dedicated LLM & Embedding Providers guide.

Quick reference:

Provider Use for
openai, anthropic, gemini / vertex General-purpose, multimodal
groq, together, fireworks, deepinfra, mistral, xai, cohere, huggingface, replicate Hosted open + frontier models
qwen Qwen3 thinking mode + Qwen-Omni audio I/O (DashScope compatible-mode/v1)
ollama, vllm Self-hosted / air-gapped
azure, bedrock Enterprise compliance

Reasoning, finish-reason, and audio I/O are uniform across the chat-completions providers — see Reasoning, FinishReason & Audio I/O for WithReasoningEffort, WithEnableThinking, WithAudioOutput, and the Message.FinishReason / Message.ReasoningContent surfaces.

3.1 OpenAI

API endpoint: POST {base_url}/chat/completions Authentication: Authorization: Bearer {api_key} Default base URL: https://api.openai.com/v1 HTTP timeout: 120 seconds

model:
  provider: openai
  model: gpt-4o                          # Required: model identifier
  api_key: "${OPENAI_API_KEY}"           # Required: API key (env var recommended)
  base_url: "https://api.openai.com/v1"  # Optional: custom endpoint (Azure, vLLM, etc.)
  temperature: 0.7                        # Optional: 0.0 - 2.0 (default: provider default)
  max_tokens: 4096                        # Optional: max output tokens
  top_p: 0.9                             # Optional: nucleus sampling
  stop: ["END", "STOP"]                  # Optional: stop sequences
  response_format: "json_object"          # Optional: "text" or "json_object"

Recommended models: | Model | Best For | Cost | |---|---|---| | gpt-4o | General purpose, tool use | Medium | | gpt-4o-mini | Fast responses, simple tasks | Low | | o3 | Complex reasoning | High |

Compatible services (via base_url): Azure OpenAI, vLLM, LiteLLM, Groq, Together AI, any OpenAI-compatible API.


3.2 Anthropic

API endpoint: POST {base_url}/v1/messages Authentication: x-api-key: {api_key} + anthropic-version: 2023-06-01 Default base URL: https://api.anthropic.com HTTP timeout: 120 seconds Default max_tokens: 4096

model:
  provider: anthropic
  model: claude-sonnet-4-20250514                      # Required: model identifier
  api_key: "${ANTHROPIC_API_KEY}"         # Required: API key
  base_url: "https://api.anthropic.com"   # Optional: custom endpoint
  max_tokens: 8192                        # Optional: default 4096
  temperature: 0.7                        # Optional
  top_p: 0.9                             # Optional

Recommended models: | Model | Best For | Cost | |---|---|---| | claude-sonnet-4-20250514 | General purpose, tool use | Medium | | claude-opus-4-20250514 | Complex reasoning, analysis | High | | claude-haiku-3-20250414 | Fast responses, simple tasks | Low |

Note: Anthropic uses a different message format internally. The system prompt is sent as a top-level system field (not a message). Tool results are sent as user messages with tool_result content blocks. Helix handles all format translation automatically.


3.3 Ollama (Local)

API endpoint: POST {base_url}/api/chat Authentication: None (local inference) Default base URL: http://localhost:11434 HTTP timeout: 300 seconds (local models can be slow on first load) Streaming format: Newline-delimited JSON (not SSE)

model:
  provider: ollama
  model: llama3.1:8b                       # Required: model name:tag
  base_url: "http://localhost:11434"        # Optional: custom Ollama endpoint
  temperature: 0.7                          # Optional
  max_tokens: 2048                          # Optional: maps to num_predict
  top_p: 0.9                               # Optional
  stop: ["\n\n"]                            # Optional

Recommended models: | Model | Parameters | Best For | |---|---|---| | llama3.1:8b | 8B | General purpose, fast | | llama3.1:70b | 70B | Complex reasoning | | qwen2.5:7b | 7B | Coding, reasoning | | mistral:7b | 7B | Fast, multilingual |

Prerequisites: Install and run Ollama (ollama serve), then pull models (ollama pull llama3.1:8b).


3.4 API Key Configuration

Environment variables (recommended):

model:
  api_key: "${OPENAI_API_KEY}"     # Resolved at load time

Vault integration (for production):

helix:
  config:
    vault:
      addr: "https://vault.example.com"
      token: "${VAULT_TOKEN}"
      path: "secret/data/helix/openai"
      key: "api_key"

Direct (development only -- never commit to source control):

model:
  api_key: "sk-..."

3.5 Streaming vs Non-Streaming

All three providers support both modes:

  • Non-streaming (default): agent.Run(ctx, message) -- waits for the full response. Used when the exchange does not set HelixStream=true.
  • Streaming: runner.QueryStream(ctx, message) -- returns a *llm.StreamReader that yields StreamChunk values. Enabled by setting the exchange header HelixStream to "true". Used by AG-UI, A2A, and A2UI protocol servers.

3.6 Token Usage Tracking

All LLM components set token usage on exchange headers after each call:

Header Description
llm.prompt_tokens Input tokens consumed
llm.completion_tokens Output tokens generated
llm.total_tokens Total tokens (prompt + completion)

Use these headers for cost tracking, rate limiting, and monitoring.


4. Memory Configuration

Memory determines what conversational context the agent sees. All memory types implement the Memory interface:

type Memory interface {
    Add(ctx context.Context, message *llm.Message) error
    Get(ctx context.Context) ([]*llm.Message, error)
    Search(ctx context.Context, query string, limit int) ([]*llm.Message, error)
    Clear(ctx context.Context) error
}

4.1 Conversation Memory

What it does: Stores the full, unabridged conversation history. Every message ever added is returned by Get(). Simple and unbounded. In-memory by default, or durable via a store.

When to use: Short conversations, debugging, when you never want to lose context.

steps:
  - agent:
      id: my-agent
      memory:
        enabled: true
        type: conversation

This is the default if no memory is configured. With no store it lives in process memory (lost on restart). To persist the full history, add a store:

      memory:
        enabled: true
        type: conversation
        store: mongodb                       # redis | file | memx | mongodb
        connection: "mongodb://localhost:27017/helix"   # db in the path (default: helix)
        collection: agent_memory             # optional (default: agent_memory)
        session_id: "session-abc-123"
store Backend Connection field
(unset) in-process (ephemeral) —
redis Redis list redis_url
file local JSON file file_path
memx memx (postgres wire) memx_url
mongodb MongoDB collection connection (mongodb:// URI)

For a SQL database (PostgreSQL/Oracle/SQL Server/MySQL) with the same full-history semantics, use type: sql-long-term.

Caution: Unbounded growth. For long conversations, token limits will eventually be exceeded.


4.2 Sliding Window Memory

What it does: Keeps only the last N messages. The system message (first message with role system) is always preserved and does not count toward the window size.

When to use: Chat applications where recent context matters most.

steps:
  - agent:
      id: chat-agent
      memory:
        enabled: true
        type: sliding-window
        window_size: 20
Parameter Required Default Description
window_size Yes -- Maximum number of messages to retain

Behavior: When the window is exceeded, the oldest non-system messages are dropped. The system message always occupies one slot.


4.3 Summary Memory

What it does: When message count exceeds max_messages, uses the LLM to summarize the oldest half of messages into a running summary. The summary is prepended as a system message on Get(), followed by the most recent messages.

When to use: Long-running conversations where you need both context compression and recent detail.

steps:
  - agent:
      id: long-chat
      memory:
        enabled: true
        type: summary
        max_messages: 30
Parameter Required Default Description
max_messages Yes -- Threshold that triggers summarization

Note: Summary memory requires an LLM call each time summarization triggers. When configured via YAML, the agent's own model is used for summarization. The prompt is: "Summarize the following conversation concisely, preserving key facts and decisions."


4.4 Token Window Memory

What it does: Keeps messages within an estimated token budget. Token count is estimated as len(content) / 4. The system message is always preserved.

When to use: When you want precise control over context window usage to avoid exceeding model limits.

steps:
  - agent:
      id: budget-agent
      memory:
        enabled: true
        type: token-window
        max_tokens: 8000
Parameter Required Default Description
max_tokens Yes -- Maximum estimated token budget

Behavior: Drops the oldest non-system messages until the total estimated token count fits within the budget.


4.5 Long-Term Memory

What it does: Key-value memory with categories, tags, and search. Backed by a Store interface.

long-term is only durable with a store

Despite the name, type: long-term with no store (or store: memory) is in-memory only and is lost on restart — the engine logs a warning at load time. For persistence set store: redis, store: file, or store: memx (or use type: sql-long-term with a dsn).

When to use: Agents that need to remember facts across sessions (with a durable store).

steps:
  - agent:
      id: persistent-agent
      memory:
        enabled: true
        type: long-term
        store: file           # memory (ephemeral) | redis | file | memx
        file_path: .helix/memory/user-123.json
        namespace: "user-123"
Parameter Required Default Description
store No memory Backend: memory (ephemeral), redis, file, memx
redis_url / file_path / memx_url No — Connection for the chosen store
namespace No agent ID Scope for stored memories

Each stored entry has: ID, Namespace, Content, Category (fact/preference/decision/procedure), Role, Tags, CreatedAt.


4.6 SQL Database Long-Term Memory

What it does: Stores all messages persistently in a PostgreSQL (or any database/sql compatible) database. Messages are scoped by session ID. Search uses ILIKE for content matching.

When to use: Production agents that need durable, queryable conversation history.

steps:
  - agent:
      id: production-agent
      memory:
        enabled: true
        type: sql-long-term
        dsn: "${DATABASE_URL}"
        session_id: "session-abc-123"
Parameter Required Default Description
dsn Yes -- Database connection string (e.g., postgres://user:pass@host/db)
session_id Yes -- Scopes messages to a specific session

Table schema (auto-created via EnsureTable):

CREATE TABLE IF NOT EXISTS agent_memory (
    id         SERIAL PRIMARY KEY,
    session_id TEXT NOT NULL,
    role       TEXT NOT NULL,
    content    TEXT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);


4.7 Vector Memory

What it does: Provides semantic search over past messages using vector embeddings. Messages are embedded and stored in a vector store. Search() finds semantically similar messages. Get() returns an empty slice (vector memory is search-only, not sequential).

When to use: When the agent needs to recall relevant past interactions by meaning rather than recency.

steps:
  - agent:
      id: semantic-agent
      memory:
        enabled: true
        type: vector
        top_k: 5
        store: qdrant                       # durable — persists across restarts
        connection: "http://localhost:6333"
        collection: agent-semantic
        embed:
          provider: openai
          model: text-embedding-3-small
          api_key: "${OPENAI_API_KEY}"
Parameter Required Default Description
top_k No 5 Number of results to return from similarity search
store No memory Vector backend (see below)
connection Depends -- Backend URL/addr (qdrant/chromadb/weaviate/opensearch/elasticsearch URL, milvus/redis addr, pgvector/postgis connstring; for memx use memx_url)
collection No namespace Collection/table/index name
api_key / username / password Depends -- Backend auth (qdrant/pinecone/milvus/weaviate/elasticsearch key; opensearch/elasticsearch user+pass)
embed.provider Yes -- Embedding provider: openai, ollama
embed.model Yes -- Embedding model name
embed.api_key Depends -- API key for embedding provider

Supported vector stores (dimension is derived from the embedding model, and the collection/table is created automatically):

  • memory — in-process, brute-force cosine (default, ephemeral)
  • memx — durable, via memx_url (see §14)
  • External vector databases (durable): qdrant, pinecone, milvus, pgvector, postgis, chromadb, weaviate, opensearch, elasticsearch, redis — the same backends the RAG pipeline uses, bridged into agent memory. pinecone takes its index host in connection plus api_key (and reuses namespace).

4.8 Composite Memory

What it does: Combines multiple memory backends. Add() writes to all backends. Get() and Search() merge results from all backends, deduplicating by content.

When to use: When you need both sequential history (sliding window) and semantic search (vector) simultaneously.

steps:
  - agent:
      id: hybrid-agent
      memory:
        enabled: true
        type: composite
        backends:
          - type: sliding-window
            window_size: 20
          - type: vector
            top_k: 5
            embed:
              provider: openai
              model: text-embedding-3-small
              api_key: "${OPENAI_API_KEY}"
Parameter Required Default Description
backends Yes -- List of memory configurations to combine

Deduplication: Messages are deduplicated by role:content key across all backends.


4.9 Per-Session Memory Isolation

By default, all requests to an agent share a single memory instance. This means User A's conversation leaks into User B's context — a critical problem for any multi-user deployment.

The session_id_header field solves this. When set, Helix extracts the session ID from the specified request header and creates an isolated memory instance per session. Each unique session ID gets its own memory, created on first access and cached for subsequent requests.

Basic Usage

agents:
  - id: support-bot
    type: react
    trigger: http-server://0.0.0.0:8080/api/chat
    model:
      provider: openai
      model: gpt-4o
    system_prompt: "You are a helpful support agent."
    memory:
      enabled: true
      type: sliding-window
      window_size: 30
      session_id_header: X-Session-ID

Clients include the session header with each request:

# User Alice
curl -X POST http://localhost:8080/api/chat \
  -H "X-Session-ID: alice-session-001" \
  -d "What's your return policy?"

# User Bob (completely isolated memory)
curl -X POST http://localhost:8080/api/chat \
  -H "X-Session-ID: bob-session-042" \
  -d "How do I reset my password?"

Alice and Bob each have their own conversation history. Neither can see the other's messages.

How It Works

  1. Request arrives — the agentProcessor reads the header specified by session_id_header
  2. Session lookup — the SessionMemoryManager checks its cache (sync.Map) for that session ID
  3. First access — if not found, the MemoryFactory creates a new memory instance using the same config (type, window size, DSN, etc.) but scoped to that session
  4. Subsequent requests — the cached memory is returned instantly
  5. Agent runs — the agent's memory is swapped to the session-scoped instance before Run() executes

If the header is missing from a request, the exchange ID is used as a fallback — effectively giving that request a one-shot memory with no persistence.

The session ID is also set as an exchange header agent.session.id for downstream processors to reference.

With SQL Database Persistence

For production deployments where memory must survive restarts, combine session_id_header with sql-long-term:

steps:
  - agent:
      id: production-agent
      type: react
      model:
        provider: anthropic
        model: claude-sonnet-4-20250514
      memory:
        enabled: true
        type: sql-long-term
        dsn: "${DATABASE_URL}"
        session_id_header: X-Session-ID

Each session ID becomes the session_id column value in the agent_memory table. This means:

  • Memory persists across server restarts
  • Sessions are queryable: SELECT * FROM agent_memory WHERE session_id = 'alice-session-001'
  • Cleanup is straightforward: DELETE FROM agent_memory WHERE session_id = '...'

With Vector Memory

Semantic search scoped per session:

steps:
  - agent:
      id: research-agent
      memory:
        enabled: true
        type: vector
        top_k: 10
        session_id_header: X-Session-ID
        embed:
          provider: openai
          model: text-embedding-3-small
          api_key: "${OPENAI_API_KEY}"

Each session gets its own vector namespace, so similarity search only returns results from that user's conversation history.

With Composite Memory

Combine per-session sliding window with per-session vector search:

steps:
  - agent:
      id: advanced-agent
      memory:
        enabled: true
        type: composite
        session_id_header: X-Session-ID
        backends:
          - type: sliding-window
            window_size: 30
          - type: vector
            top_k: 5
            embed:
              provider: openai
              model: text-embedding-3-small
              api_key: "${OPENAI_API_KEY}"

Both sub-backends are isolated per session.

Session ID Sources

The session_id_header can reference any header set by upstream processors or the transport layer:

Source Header Name How to Set
Custom header X-Session-ID Client sends directly
JWT claim jwt.sub or jwt.session_id Set by JWT security interceptor
Cookie Cookie-Session Set by HTTP server component
Tenant ID helix.tenant Set by TenantInterceptor
Auto-generated X-Request-ID Set by HTTP server (per-request, no persistence)

Configuration Reference

Parameter Required Default Description
session_id_header No -- Header name to extract session ID from. When set, enables per-session memory isolation

When session_id_header is not set, the agent uses a single shared memory instance (legacy behavior). This is fine for single-user CLIs, batch processing, or agents that intentionally share context across all requests.


5. Tools

Tools give agents the ability to interact with external systems during their reasoning loop.

5.1 Tool Interface

All tools implement:

type Tool interface {
    Info() *ToolInfo      // Name, description, JSON Schema parameters
    Invoke(ctx context.Context, arguments string) (string, error)
}

The arguments string is raw JSON matching the tool's parameter schema. The return value is a string that the LLM sees as the tool result.

5.2 Defining Tools in YAML

steps:
  - agent:
      id: my-agent
      tools:
        - name: get_weather
          type: function
          description: "Get current weather for a city"
          parameters:
            type: object
            properties:
              city:
                type: string
                description: "City name"
              units:
                type: string
                enum: ["celsius", "fahrenheit"]
                description: "Temperature units"
            required: ["city"]

5.3 Tool Types Reference

Helix agents support 8 tool types. Each type lets an agent interact with a different kind of resource during its reasoning loop.

Type Description Use Case
function Generic function tool -- implemented in Go via FuncTool or SimpleTool Custom logic in Go code
tool Reference a top-level tools: or pipelines: definition by ID Reuse schema-validated tools across agents; RAG retrieval; data processing
a2a Call an external A2A agent over the A2A protocol Remote agent delegation
mcp Connect to an external MCP server and import its tools Remote tool discovery and invocation
connector Invoke any Helix connector by URI (439 integrations) HTTP REST APIs, SaaS, databases, messaging

type: tool -- Reference Top-Level Tools

Reference a tool defined in the top-level tools: block. The agent inherits the tool's description, input schema, and output schema.

tools:
  - id: search
    description: "Search knowledge base"
    input:
      query: { type: string, required: true }
    output:
      results: { type: array }
    steps:
      - transform:
          starlang: '"[]"'

agents:
  - id: assistant
    type: react
    trigger: http-server://0.0.0.0:8080/chat
    model:
      provider: openai
      model: gpt-4o
    tools:
      - name: search
        type: tool
        tool_id: search            # references tools.[].id above

RAG Pipelines as Tools (via type: tool)

Expose a RAG pipeline to an agent by giving it an id you can reference. A pipeline defaults to direct://<id> as its trigger, so a type: tool reference (which resolves tool:// then direct://) can address it directly.

pipelines:
  - id: knowledge-base
    source: direct://knowledge-base
    steps:
      - rag-chunk: { strategy: semantic }
      - rag-embed: { provider: openai, model: text-embedding-3-small }
      - rag-store: { store: pgvector, connection: "${env:PG_URL}" }
      - rag-retrieve: { strategy: hybrid, top_k: 10 }

agents:
  - id: support-bot
    type: react
    trigger: http-server://0.0.0.0:8080/support
    model:
      provider: anthropic
      model: claude-sonnet-4-20250514
    tools:
      - name: query_docs
        type: tool
        tool_id: knowledge-base       # references the pipeline above
        description: "Search the knowledge base for relevant documents"

type: a2a -- Call External A2A Agents

Delegate work to a remote agent that speaks the A2A protocol. This is the correct choice for calling remote agents. For local agents within the same flow, use sub_agents instead.

agents:
  - id: coordinator
    type: supervisor
    trigger: http-server://0.0.0.0:8080/coordinate
    model:
      provider: openai
      model: gpt-4o
    tools:
      - name: research_agent
        type: a2a
        description: "Delegate research tasks to a remote research agent"
        a2a_server:
          url: "http://research-agent.internal:9000"
        auth:
          type: bearer
          token: "${env:A2A_TOKEN}"
      - name: finance_agent
        type: a2a
        description: "Get financial analysis from the finance team agent"
        a2a_server:
          url: "http://finance-agent.internal:9000"
        auth:
          type: oauth2
          token_url: "https://auth.example.com/token"
          client_id: "${env:OAUTH_CLIENT_ID}"
          client_secret: "${env:OAUTH_CLIENT_SECRET}"
          scopes: ["agent:invoke"]

type: mcp -- Call External MCP Tools (with Auth)

agents:
  - id: coder
    type: react
    trigger: http-server://0.0.0.0:8080/code
    model:
      provider: anthropic
      model: claude-sonnet-4-20250514
    tools:
      - name: code-tools
        type: mcp
        description: "Code analysis and formatting tools"
        mcp_server:
          url: "http://mcp-tools.internal:3100"
          transport: http
          tools: ["format-code", "lint"]
        auth:
          type: api-key
          header: "X-API-Key"
          key: "${env:MCP_API_KEY}"

type: connector -- Use Any Registered Connector

Bind any of the 439 built-in connectors (HTTP REST APIs, SaaS, databases, queues, etc.) as a tool by URI.

agents:
  - id: lookup-bot
    type: react
    trigger: http-server://0.0.0.0:8080/lookup
    model:
      provider: openai
      model: gpt-4o
    tools:
      - name: get_customer
        type: connector
        connector: "http://customer-api.internal:8080/v1/customers"
        description: "Look up a customer by ID"
        parameters:
          type: object
          properties:
            id: { type: string, description: "Customer ID" }
          required: ["id"]

For inline expression logic, define a type: tool referencing a top-level tools: entry whose steps: use a transform: { starlang: ... } step — the canonical [[feedback_tools_route_split]] pattern.

auth: on Tools

The auth: block can be added to type: a2a, type: mcp, and type: connector tools to secure outbound calls. Four auth types are supported:

Auth Type Fields
bearer token
api-key header, key
oauth2 token_url, client_id, client_secret, scopes
mtls cert_file, key_file, ca_file
# Bearer token
auth:
  type: bearer
  token: "${env:API_TOKEN}"

# API key in custom header
auth:
  type: api-key
  header: "X-API-Key"
  key: "${env:SERVICE_KEY}"

# OAuth2 client credentials
auth:
  type: oauth2
  token_url: "https://auth.example.com/token"
  client_id: "${env:CLIENT_ID}"
  client_secret: "${env:CLIENT_SECRET}"
  scopes: ["read", "write"]

# Mutual TLS
auth:
  type: mtls
  cert_file: "/certs/client.crt"
  key_file: "/certs/client.key"
  ca_file: "/certs/ca.crt"

All ${env:...} placeholders are resolved at load time via Helix's secret resolution system (environment variables, Vault, AWS Secrets Manager).

The Clear Rule: Local vs Remote

Scenario Mechanism Example
Local agents (same flow) sub_agents: sub_agents: [researcher, writer]
Remote agents (A2A) type: a2a tool a2a_server: { url: "http://..." }
Remote tools (MCP) type: mcp tool mcp_server: { url: "http://..." }

5.4 MCP Server Tools

Connect an external MCP server as a tool source. All tools from the MCP server are automatically registered with the agent.

steps:
  - agent:
      id: my-agent
      tools:
        - name: external-tools
          type: mcp
          description: "Tools from external MCP server"
          mcp_server:
            url: "http://localhost:3000"
            transport: http            # http, sse, or stdio
            tools: ["search", "fetch"] # Optional: filter specific tools (empty = all)
        - name: local-tools
          type: mcp
          mcp_server:
            transport: stdio
            command: "npx"
            args: ["-y", "@modelcontextprotocol/server-filesystem", "/data"]

MCP server tool parameters:

Parameter Required Description
url For http/sse Server URL
transport No http (default), sse, or stdio
command For stdio Command to run
args For stdio Command arguments
headers No Extra HTTP headers (e.g., auth)
tools No Filter: only import these tool names

5.5 FuncTool (Go API)

Type-safe tool with automatic JSON deserialization:

type SearchArgs struct {
    Query string `json:"query"`
    Limit int    `json:"limit"`
}

searchTool := tool.NewFuncTool[SearchArgs](
    "search_docs",
    "Search the documentation",
    map[string]any{
        "type": "object",
        "properties": map[string]any{
            "query": map[string]any{"type": "string"},
            "limit": map[string]any{"type": "integer"},
        },
        "required": []string{"query"},
    },
    func(ctx context.Context, args SearchArgs) (string, error) {
        results := doSearch(args.Query, args.Limit)
        return fmt.Sprintf("Found %d results: %v", len(results), results), nil
    },
)

5.6 SimpleTool (Go API)

For tools that process raw JSON arguments:

echoTool := tool.NewSimpleTool(
    "echo",
    "Echo the input back",
    map[string]any{"type": "object"},
    func(ctx context.Context, args string) (string, error) {
        return "Echo: " + args, nil
    },
)

5.7 Tool Registry and Middleware

registry := tool.NewRegistry()
registry.Register(searchTool)
registry.Register(echoTool)

// Apply middleware to a tool
logged := tool.WithLogging()(searchTool)
registry.Register(logged)

// Use in agent config
agent := adk.New(adk.AgentConfig{
    Tools: registry,
})

5.8 Model & Memory Middleware

Both the LLM model and the memory backend can be wrapped with named middleware that the DSL resolves at load time. Use this to add structured logging, redaction, caching, rate-limiting, or tracing without forking the agent core.

agents:
  - id: traced-agent
    type: react
    model: { provider: openai, name: gpt-4o-mini }
    model_middleware: [logging]      # wraps the ChatModel
    memory: { enabled: true, type: sliding-window, max_messages: 20 }
    memory_middleware: [logging]     # wraps the Memory backend

Built-in logging middleware emits structured slog records for every model call (request + response, tokens, finish reason) and every memory op (add / get / search / clear). Register custom middleware from Go via llm.RegisterModelMiddleware(name, factory) and memory.RegisterMiddleware(name, factory); an unknown name in YAML fails the load with the registered set in the error message — no silent fallback.

Middleware list order is outermost-first (the first entry sees raw input, the last entry is closest to the underlying model/memory).

5.9 Custom Agent Orchestration

The seven built-in types (react, supervisor, plan-execute, deep, sequential, parallel, loop) cover most patterns. To add your own:

adk.RegisterAgentType("my-orchestrator",
    func(ctx context.Context, a *adk.Agent, input string) (string, error) {
        // … your orchestration logic …
    })

Reference it from YAML as type: my-orchestrator. The agent dispatcher rejects unknown types with the full list of built-ins plus the registered set, so configuration drift surfaces at load time, not at first invocation.

5.10 Checkpointing & Resumable Runs

adk.Runner persists interrupt/resume checkpoints. The default in-process MemoryCheckpointStore is fast but loses state on restart; supply a durable backend for production:

import (
    "github.com/arvenza/helix/pkg/agent/adk"
    "github.com/arvenza/helix/pkg/agent/kvstore"
)

kv := kvstore.NewRedis(redisClient)            // or memx, SQL, etc.
runner := adk.NewRunner(adk.RunnerConfig{
    Agent:           agent,
    CheckpointStore: adk.NewKVCheckpointStore(kv, "helix:agent:checkpoint:"),
})

// Run; if a tool calls tool.Interrupt, a checkpoint is saved
result, err := runner.Run(ctx, runID, input)
if ie, ok := adk.AsInterrupt(err); ok {
    // ie.Tool / ie.Info — surface to human; persist the runID
}

// Later, after the human responds:
result, err = runner.Resume(ctx, runID, humanResponse)

The checkpoint shape is JSON-tagged so any KV backend that can hold opaque bytes works.

5.11 Human Input & Async Interrupts

Two ways for a tool to ask a human:

Synchronous (RequestHumanInput) — the tool blocks in place until a human responds via the approval queue. Use when the conversation is interactive.

Asynchronous (tool.Interrupt) — the tool returns a typed *tool.InterruptError, the agent aborts cleanly and surfaces a *adk.InterruptError from Run, plus an interrupt event on the OnEvent stream. The caller persists a checkpoint (via CheckpointStore) and resumes later with the human's reply. Use for long-running workflows, ticket-based approvals, or queue-driven flows.

import "github.com/arvenza/helix/pkg/agent/tool"

func (t *escalateTool) Invoke(ctx context.Context, args string) (string, error) {
    if needsHuman(args) {
        return "", tool.Interrupt("escalate to compliance — needs human reviewer")
    }
    return process(args)
}

// Caller side
result, err := agent.Run(ctx, input)
if ie, ok := adk.AsInterrupt(err); ok {
    persistTicket(ie.Tool, ie.Info)   // surface to human queue
    return // resume later via Runner with a CheckpointStore
}

Governance (cross-cuts every tool call)

The runtime-governance layer wires deterministic, app-layer guardrails around every tool call: scoped kill switches, 4-tier privilege rings, a rule-based policy engine with Starlang predicates, and a hash-chained tamper-evident audit log. Configure under agent.governance:.

agents:
  - id: finance-agent
    type: react
    model: { provider: openai, name: gpt-4o-mini }
    tools: [transfer, get_balance]

    governance:
      audit_chain: true
      rings:
        assignment: standard
        permissions:
          standard: [get_balance, transfer]
          admin: ["*"]
      policy:
        - action: transfer
          effect: review
          starlang_when: |
            ctx["args"]["amount"] > 10000
        - action: get_balance
          effect: allow
          max_calls: 100
          window: 1m
        - action: "*"
          effect: allow

Evaluation order per tool call: kill switches → privilege rings → policy engine → audit. First non-allow decision wins; the dispatcher feeds a synthetic Tool call denied by governance: <reason> tool_result back to the LLM so it can adjust its plan.

Blocks emit a governance_block AgentEvent:

agentCfg.OnEvent = func(e adk.AgentEvent) {
    if e.Type == adk.EventGovernanceBlock {
        slog.Warn("governance.block",
            "agent", e.AgentID, "tool", e.Tool, "reason", e.Err)
    }
}

Runtime control via the management API: POST /api/kill-switch (operator), GET /api/audit (viewer), POST /api/approvals/{id} (operator). See Governance Guide for the full schema, Go API, conflict resolver, MCP scanner, and prompt-defense classifier.


6. Agent Flows

Agent flows are composable processor patterns that implement engine.Processor. They can be nested, combined, and used anywhere a processor is expected -- including inside workflow steps.

6.1 SequentialFlow

Runs processors in order. Each processor receives the same exchange (mutations accumulate). Stops at the first error.

flow := flow.NewSequentialFlow(
    agentProcessor,
    validationProcessor,
    formattingProcessor,
)

Exchange behavior: All steps share the same exchange. Step 2 sees the body and headers set by step 1.

6.2 ParallelFlow

Runs processors concurrently. Each processor receives a copy of the exchange. Results are aggregated back into the original exchange.

pf := flow.NewParallelFlow(
    technicalReview,
    businessReview,
    riskReview,
)
// Optional: custom aggregation
pf.Aggregator = func(results []*exchange.Exchange, target *exchange.Exchange) {
    var combined map[string]any
    for _, r := range results {
        // merge results...
    }
    target.In().SetBody(combined)
}

Default aggregation: Concatenates all branch bodies with newlines.

6.3 ConditionalFlow

Routes to different processors based on a predicate.

cf := flow.NewConditionalFlow(
    func(ctx context.Context, ex *exchange.Exchange) (bool, error) {
        return ex.In().HeaderString("priority") == "high", nil
    },
    urgentProcessor,
).WithElse(normalProcessor)

6.4 LoopFlow

Repeats a processor until a condition returns false or max iterations is reached. The condition is evaluated after each iteration.

lf := flow.NewLoopFlow(
    refinementProcessor,
    func(ctx context.Context, ex *exchange.Exchange, iteration int) (bool, error) {
        score := ex.In().HeaderString("quality_score")
        return score != "excellent", nil  // continue if not excellent
    },
    10, // max iterations
)

6.5 PipelineFlow

Chains processors where each step gets a fresh exchange seeded with the previous step's output body and headers. Unlike SequentialFlow, steps are isolated.

pf := flow.NewPipelineFlow(
    extractProcessor,   // extracts data -> sets body
    transformProcessor, // transforms body -> sets new body
    loadProcessor,      // loads body into destination
)

Key difference from SequentialFlow: PipelineFlow creates new exchanges between steps (only body and headers carry forward). SequentialFlow shares the same exchange throughout.

6.6 Combining Flows

Flows implement engine.Processor, so they compose naturally:

pipeline := flow.NewSequentialFlow(
    flow.NewParallelFlow(researchAgent, dataAgent),
    flow.NewConditionalFlow(isComplex, deepAnalysis).WithElse(quickSummary),
    flow.NewLoopFlow(refinement, qualityCheck, 5),
)

7. Exposing Agents via Protocols

Helix supports four standard protocols for exposing agents to external systems. Each is implemented as a native component with its own URI scheme.

7.1 AG-UI (Frontend Streaming)

When to use: You want to expose agents to web or mobile frontends with real-time streaming.

Protocol: AG-UI (Agent-User Interaction). Used by CopilotKit and other frontend frameworks. Streams Server-Sent Events (SSE) back to the client.

URI scheme: agui://host:port/path?name=agent-name Default port: 4100

SSE event flow:

RUN_STARTED
TEXT_MESSAGE_START (messageID, role=assistant)
TEXT_MESSAGE_CONTENT (messageID, "Hello")
TEXT_MESSAGE_CONTENT (messageID, " world")
...
TEXT_MESSAGE_END (messageID)
RUN_FINISHED

Complete YAML example:

helix:
  agui:
    servers:
      - enabled: true
        addr: "0.0.0.0:4100"
        path: "/agent"
        name: "my-assistant"

agents:
  - id: chat-agent
    type: react
    trigger: direct://chat
    model:
      provider: openai
      model: gpt-4o
      api_key: "${OPENAI_API_KEY}"
    system_prompt: "You are a helpful assistant."
    memory:
      enabled: true
      type: sliding-window
      window_size: 20

workflows:
  - id: agui-server-chat
    trigger: "agui://0.0.0.0:4100/agent?name=my-assistant"
    steps:
      - target: "direct://chat"

Frontend integration (JavaScript):

const response = await fetch('http://localhost:4100/agent', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    thread_id: 'thread-1',
    run_id: 'run-1',
    messages: [
      { role: 'user', content: 'Explain quantum computing' }
    ]
  })
});

const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  const text = decoder.decode(value);
  const lines = text.split('\n');
  for (const line of lines) {
    if (line.startsWith('data: ')) {
      const event = JSON.parse(line.slice(6));
      if (event.type === 'TEXT_MESSAGE_CONTENT') {
        process.stdout.write(event.content);
      }
    }
  }
}

CORS: The AG-UI server sets Access-Control-Allow-Origin: * by default.


7.2 A2A (Agent-to-Agent)

When to use: You want agents to communicate with other AI agents using the Google A2A protocol. Enables multi-system agent orchestration.

Protocol: A2A (Agent-to-Agent). Uses JSON-RPC over HTTP. Includes agent discovery via Agent Card.

URI schemes: - Server: a2a-server://host:port?name=...&description=...&version=... - Client: a2a://host:port (for calling external A2A agents)

Default port: 9000

Agent Card: Automatically published at /.well-known/agent-card.json. Contains agent name, description, skills, capabilities, and preferred transport.

Complete YAML example (Server):

helix:
  a2a:
    servers:
      - enabled: true
        addr: "0.0.0.0:9000"
        name: "helix-support-agent"
        description: "Customer support agent with billing and technical expertise"
        version: "1.0.0"

agents:
  - id: support-agent
    type: supervisor
    trigger: direct://support
    description: "Handle customer support requests"
    model:
      provider: anthropic
      model: claude-sonnet-4-20250514
      api_key: "${ANTHROPIC_API_KEY}"
    system_prompt: "Route support requests to the appropriate specialist."
    sub_agents:
      - id: billing
        type: react
        model:
          provider: anthropic
          model: claude-sonnet-4-20250514
          api_key: "${ANTHROPIC_API_KEY}"
        system_prompt: "Handle billing inquiries."
      - id: technical
        type: react
        model:
          provider: openai
          model: gpt-4o
          api_key: "${OPENAI_API_KEY}"
        system_prompt: "Handle technical support requests."

workflows:
  - id: a2a-server
    trigger: "a2a-server://0.0.0.0:9000?name=helix-support&description=Support+Agent"
    steps:
      - target: "direct://support"

Calling an external A2A agent from a route:

workflows:
  - id: delegate-to-external
    trigger: "direct://delegate"
    steps:
      - target: "a2a://external-agent.example.com:9000"

Streaming: The A2A server sends streaming artifacts when the agent produces a *llm.StreamReader or chan string body. Chunks are sent as TaskArtifactUpdateEvent messages.

Skill discovery: The server auto-discovers agents registered via the agent component and invocable workflows (direct:// and seda://) to populate the Agent Card's skills list.


7.3 A2UI (Rich Declarative UI)

When to use: You want agents to render rich UIs -- cards, forms, tables, icons -- not just text. Used for dashboard agents, interactive assistants, and form-driven workflows.

Protocol: A2UI (Agent to UI). Google protocol. Streams JSONL messages over SSE with declarative UI components.

URI scheme: a2ui://host:port/path?name=agent-name Default port: 4200

Request methods: - GET /path?message=<text>&surface=<id> -- Initiates SSE stream with A2UI messages - POST /path -- Sends client-to-server events (button clicks, form submissions)

A2UI message types: - beginRendering -- Start rendering a surface - updateComponents -- Update UI components (Column, Card, Row, Text, Icon, Form, Table, etc.) - dataModelUpdate -- Update data model

Complete YAML example:

helix:
  a2ui:
    servers:
      - enabled: true
        addr: "0.0.0.0:4200"
        path: "/surface"
        name: "dashboard-agent"

agents:
  - id: dashboard-agent
    type: react
    trigger: direct://dashboard
    model:
      provider: openai
      model: gpt-4o
      api_key: "${OPENAI_API_KEY}"
    system_prompt: |
      You are a dashboard assistant. Analyze data and
      provide insights in a structured format.

workflows:
  - id: a2ui-server
    trigger: "a2ui://0.0.0.0:4200/surface?name=dashboard-agent"
    steps:
      - target: "direct://dashboard"

Streaming behavior: When the agent produces a streaming body, the A2UI server progressively updates a Card component with accumulated text. Each chunk sends an updateComponents message with the full text so far.

Client events: The POST endpoint accepts ClientMessage JSON with a userAction event containing type, surfaceID, and componentID.


7.4 MCP (Model Context Protocol)

When to use: You want to expose Helix workflows as tools that other LLMs (Claude, ChatGPT, Cursor, etc.) can call. Or you want to consume tools from external MCP servers.

Protocol: MCP (Model Context Protocol). Standard for LLM tool use.

URI schemes: - Server: mcp-server://host:port?name=...&transport=http&workflows=workflow1,workflow2 - Client: mcp://host:port/tool-name?transport=http

Transports: http (Streamable HTTP, default), sse (Server-Sent Events), stdio (standard I/O)

Complete YAML example (Server):

helix:
  mcp:
    servers:
      - enabled: true
        transport: http
        addr: "0.0.0.0:8080"
        name: "helix-tools"
        version: "1.0.0"
        workflows: ["search-docs", "query-db"]  # Optional: filter routes (empty = all)

agents:
  - id: search-agent
    type: react
    trigger: direct://search-docs
    description: "Search the documentation knowledge base"
    model:
      provider: openai
      model: gpt-4o-mini
      api_key: "${OPENAI_API_KEY}"
    system_prompt: "Search and summarize documentation."

workflows:
  - id: query-db
    trigger: "direct://query-db"
    description: "Query the analytics database"
    steps:
      - target: "postgres://analytics?query=SELECT * FROM metrics WHERE name = :input"

  - id: mcp-server
    trigger: "mcp-server://0.0.0.0:8080?name=helix-tools&transport=http"
    steps:
      - log: "MCP server running"

What gets exposed: Only workflows with direct:// or seda:// trigger-URIs are exposed as MCP tools. Workflows with other triggers (timer, cron, kafka) are consumer-driven and cannot be called on demand.

Resources: The MCP server also exposes helix://workflows as a resource that lists all registered workflows with their status.

Consuming external MCP tools in a route:

workflows:
  - id: use-external-tool
    trigger: "direct://process"
    steps:
      - target: "mcp://localhost:3000/search?transport=http"


7.5 Multi-Protocol Gateway

Expose the same agent logic via all four protocols simultaneously:

helix:
  management:
    port: 8081
    enabled: true
  health:
    enabled: true
    port: 8082
  agui:
    servers:
      - enabled: true
        addr: "0.0.0.0:4100"
        path: "/agent"
        name: "gateway-agent"
  a2a:
    servers:
      - enabled: true
        addr: "0.0.0.0:9000"
        name: "gateway-agent"
        description: "Multi-protocol AI agent gateway"
  a2ui:
    servers:
      - enabled: true
        addr: "0.0.0.0:4200"
        path: "/surface"
        name: "gateway-agent"
  mcp:
    servers:
      - enabled: true
        addr: "0.0.0.0:8080"
        name: "gateway-tools"

workflows:
  # Core agent logic -- single route, multiple protocol frontends
  - id: core-agent
    trigger: "direct://agent"
    description: "Core AI agent with tools and memory"
    steps:
      - agent:
          id: gateway-agent
          type: react
          model:
            provider: openai
            model: gpt-4o
            api_key: "${OPENAI_API_KEY}"
          system_prompt: "You are a versatile AI assistant."
          memory:
            enabled: true
            type: composite
            backends:
              - type: sliding-window
                window_size: 20
              - type: vector
                top_k: 5
                embed:
                  provider: openai
                  model: text-embedding-3-small
                  api_key: "${OPENAI_API_KEY}"

  # Protocol servers -- all point to the same core-agent route
  - id: agui-server
    trigger: "agui://0.0.0.0:4100/agent?name=gateway-agent"
    steps:
      - target: "direct://agent"

  - id: a2a-server
    trigger: "a2a-server://0.0.0.0:9000?name=gateway-agent"
    steps:
      - target: "direct://agent"

  - id: a2ui-server
    trigger: "a2ui://0.0.0.0:4200/surface?name=gateway-agent"
    steps:
      - target: "direct://agent"

  - id: mcp-server
    trigger: "mcp-server://0.0.0.0:8080?name=gateway-tools"
    steps:
      - log: "MCP server active"

When to use: You have a single agent that serves multiple client types -- web frontends (AG-UI), other agents (A2A), rich UIs (A2UI), and LLM tools (MCP).


8. RAG-Augmented Agents

RAG Pipeline Overview

Document --> Ingest --> Chunk --> Embed --> Store
                                              |
Query ------> Embed ------> Retrieve -------> +----> Augment --> Generate

Pipeline Types

Stage Type Options
Ingest rag-ingest Parsers: pdf, html, markdown, text, docx, csv, json, xml, starlang (10 total)
Chunk rag-chunk Strategies: fixed, sentence, semantic, recursive, parent-child, token (6 total)
Embed rag-embed Providers: openai, ollama, voyage, cohere (4 total)
Store Vector store Milvus, Qdrant, Pinecone, in-memory
Rerank rag-rerank Strategies: mmr (Maximal Marginal Relevance), cross-encoder
Augment rag-augment Template-based context injection

Data Types

type Document struct {
    ID       string
    Content  string
    Metadata map[string]string
    Source   string
}

type Chunk struct {
    ID         string
    DocumentID string
    Content    string
    Index      int
    Metadata   map[string]string
}

type SearchResult struct {
    Chunk     *Chunk
    Score     float64
    Embedding *Embedding
}

YAML Example: Agent with RAG Tool

workflows:
  # Ingestion pipeline (run on startup or via API)
  - id: ingest-docs
    trigger: "direct://ingest"
    steps:
      - rag-ingest:
          parser: markdown
      - rag-chunk:
          strategy: recursive
          chunk_size: 512
          overlap: 64
      - rag-embed:
          provider: openai
          model: text-embedding-3-small
      - target: "qdrant://knowledge-base?collection=docs"

# Query agent with RAG tool
agents:
  - id: knowledge-bot
    type: react
    trigger: direct://ask
    model:
      provider: openai
      model: gpt-4o
      api_key: "${OPENAI_API_KEY}"
    system_prompt: |
      You are a knowledge base assistant. Use the search_docs tool
      to find relevant information before answering questions.
      Always cite the source of your information.
    memory:
      enabled: true
      type: sliding-window
      window_size: 10
    tools:
      - name: search_docs
        type: rag-query
        description: "Search the documentation knowledge base"
        config:
          collection: "docs"
          store: "qdrant://knowledge-base"
          embed_provider: "openai"
          embed_model: "text-embedding-3-small"
          top_k: 5
          rerank: "mmr"
          rerank_lambda: 0.7

Supported Vector Stores

Store URI Scheme Notes
Milvus milvus://host:port Production-grade, distributed
Qdrant qdrant://host:port High performance, filtering
Pinecone pinecone://index-name Fully managed, serverless
In-memory (default) For development and testing only

9. Advanced Patterns

9.1 Agent Processing Messages from Kafka

workflows:
  - id: kafka-agent
    trigger: "kafka://support-tickets?brokers=kafka:9092&group=agent-group"
    steps:
      - agent:
          id: ticket-classifier
          type: react
          model:
            provider: openai
            model: gpt-4o-mini
            api_key: "${OPENAI_API_KEY}"
          system_prompt: |
            Classify the support ticket into one of: billing, technical,
            feature-request, complaint. Respond with JSON:
            {"category": "...", "priority": "high|medium|low", "summary": "..."}
          max_iterations: 3
      - target: "kafka://classified-tickets?brokers=kafka:9092"

9.2 Agent with Database Context

workflows:
  - id: contextual-agent
    trigger: "direct://customer-query"
    steps:
      # Enrich with customer data before agent processing
      - poll-enrich:
          uri: "postgres://crm?query=SELECT * FROM customers WHERE id = ${header.customer_id}"
      - set-header:
          name: customer_context
          expression: "${body}"
      - agent:
          id: customer-agent
          type: react
          model:
            provider: anthropic
            model: claude-sonnet-4-20250514
            api_key: "${ANTHROPIC_API_KEY}"
          system_prompt: |
            You are a customer service agent. The customer's account
            information is provided in the system context. Use it to
            provide personalized assistance.

9.3 Multi-Agent Pipeline with Supervisor

agents:
  - id: analysis-supervisor
    type: supervisor
    trigger: direct://analyze
    model:
      provider: openai
      model: gpt-4o
      api_key: "${OPENAI_API_KEY}"
    system_prompt: |
      You supervise three analysis teams. Route each request to
      the most appropriate specialist:
      - data-analyst: for quantitative analysis and SQL queries
      - research-agent: for qualitative research and web search
      - report-writer: for generating final reports
    sub_agents:
      - id: data-analyst
        type: plan-execute
        model:
          provider: openai
          model: gpt-4o
          api_key: "${OPENAI_API_KEY}"
        system_prompt: "You are a data analyst. Create plans for data analysis."
        max_iterations: 15
        tools:
          - name: run_sql
            type: function
            description: "Execute SQL query"
            parameters:
              type: object
              properties:
                query: { type: string }
              required: ["query"]
      - id: research-agent
        type: deep
        model:
          provider: anthropic
          model: claude-sonnet-4-20250514
          api_key: "${ANTHROPIC_API_KEY}"
        system_prompt: "You are a research specialist."
        deep_max_depth: 3
      - id: report-writer
        type: loop
        model:
          provider: openai
          model: gpt-4o
          api_key: "${OPENAI_API_KEY}"
        system_prompt: |
          Write a professional report based on the analysis.
          Refine until the quality is excellent, then say DONE.
        max_iterations: 3

9.4 Agent with Circuit Breaker

workflows:
  - id: resilient-agent
    trigger: "direct://resilient"
    on-exception:
      - exceptions: ["*"]
        handled: true
        steps:
          - set-body:
              expression: "I'm temporarily unable to process your request. Please try again later."
    steps:
      - circuit-breaker:
          threshold: 5
          half-open-after: "30s"
          steps:
            - agent:
                id: main-agent
                type: react
                model:
                  provider: openai
                  model: gpt-4o
                  api_key: "${OPENAI_API_KEY}"
                system_prompt: "You are a helpful assistant."

9.5 Agent with Audit Trail

helix:
  audit:
    enabled: true
    store: "memory"
    capacity: 10000

workflows:
  - id: audited-agent
    trigger: "direct://audited"
    steps:
      - agent:
          id: audited-bot
          type: react
          model:
            provider: anthropic
            model: claude-sonnet-4-20250514
            api_key: "${ANTHROPIC_API_KEY}"
          system_prompt: "You are a compliance-aware assistant."
          memory:
            enabled: true
            type: sql-long-term
            dsn: "${DATABASE_URL}"
            session_id: "${header.session_id}"
      - target: "log://audit?level=INFO&showBody=true"

Every exchange processed through the workflow is recorded in the audit store with: exchange ID, workflow ID, timestamps, body snapshots (before/after), headers, and any errors.


10. Production Deployment

10.1 Monitoring Agents

Metrics (Prometheus-compatible):

helix:
  management:
    port: 8081
    enabled: true

Key metrics to monitor: - helix_route_exchange_total{route="agent-route"} -- Total exchanges processed - helix_route_exchange_duration_seconds -- Processing latency (with p50/p95/p99) - helix_route_exchange_errors_total -- Error count - helix_inflight_count -- Currently in-flight exchanges

Tracing (OpenTelemetry-compatible): - Each agent invocation generates a span with agent ID, type, and model info - Tool calls are traced as child spans - LLM calls include token usage attributes

Unified event stream (OnEvent). The recommended observability surface — a stable, mapped projection of every step the agent takes. Distinct from OnDebugEvent (raw, IDE-grade, gated by Debug):

agentCfg := adk.AgentConfig{
    OnEvent: func(e adk.AgentEvent) {
        slog.Info("agent.event",
            "type",   e.Type,        // model_request / model_response / tool_call /
                                     // tool_result / tool_error / approval_request /
                                     // approval_decision / budget_exceeded / delegate /
                                     // delegate_error / guardrail_blocked /
                                     // governance_block / interrupt / loop_iteration /
                                     // decompose / context_overflow_retry / ...
            "iter",   e.Iteration,
            "tool",   e.Tool,
            "args",   e.Args,
            "result", e.Result,
            "err",    e.Err,
            "ms",     e.DurationMS,  // milliseconds (int64)
        )
    },
}

AgentEvent is the stable wire form. RawType preserves the underlying internal type string for new event classes that fall through to EventOther. DurationMS is milliseconds end-to-end — the JSON name is duration_ms and the value is consistent across DebugEvent, AgentEvent, and the workflow trace surface.

10.2 Rate Limiting LLM Calls

Use the throttle processor before agent steps:

workflows:
  - id: rate-limited-agent
    trigger: "http://0.0.0.0:8080/chat"
    steps:
      - throttle:
          rate: 10           # max 10 requests
          period: "1m"       # per minute
      - agent:
          id: chat-agent
          type: react
          model:
            provider: openai
            model: gpt-4o
            api_key: "${OPENAI_API_KEY}"
          system_prompt: "You are a helpful assistant."

10.3 Cost Tracking via Token Usage Headers

After each agent invocation, the exchange carries token usage headers. Route these to your monitoring system:

workflows:
  - id: tracked-agent
    trigger: "direct://tracked"
    steps:
      - agent:
          id: tracked-bot
          type: react
          model:
            provider: openai
            model: gpt-4o
            api_key: "${OPENAI_API_KEY}"
          system_prompt: "Respond concisely."
      - target: "kafka://token-usage?brokers=kafka:9092"

Headers available after agent step: llm.prompt_tokens, llm.completion_tokens, llm.total_tokens, agent.id, agent.type, agent.model.

10.4 Graceful Shutdown

helix:
  shutdown:
    timeout: "30s"    # Wait for in-flight exchanges to complete
    drain: true       # Stop accepting new exchanges before shutting down

The engine tracks all in-flight exchanges via InflightRepository. On shutdown: 1. Stops accepting new exchanges 2. Waits for in-flight exchanges to complete (up to timeout) 3. Stops all protocol servers (AG-UI, A2A, A2UI, MCP) 4. Stops stoppable processors (Throttle, Aggregate, Resequencer)

10.5 Scaling with Clustering

helix:
  cluster:
    enabled: true
    bind_addr: "0.0.0.0"
    bind_port: 7946
    seeds: ["node2:7946", "node3:7946"]

Clustering provides: - Leader election for singleton agents - Distributed locks for exclusive processing - Redis-backed state sharing


11. Complete Examples

Example 1: Simple Chat Agent (Ollama, Conversation Memory, AG-UI)

A local chat agent using Ollama for inference, exposed via AG-UI for frontend streaming.

helix:
  management:
    port: 8081
    enabled: true
  health:
    enabled: true
    port: 8082
  agui:
    servers:
      - enabled: true
        addr: "0.0.0.0:4100"
        path: "/chat"
        name: "local-chat"

agents:
  - id: local-assistant
    type: react
    trigger: direct://chat
    description: "Local chat agent powered by Ollama"
    model:
      provider: ollama
      model: llama3.1:8b
      base_url: "http://localhost:11434"
    system_prompt: |
      You are a friendly, helpful local AI assistant. You run entirely
      on the user's machine with no cloud dependencies. Be concise
      but thorough in your responses.
    memory:
      enabled: true
      type: conversation
    max_iterations: 5

workflows:
  - id: agui-server
    trigger: "agui://0.0.0.0:4100/chat?name=local-chat"
    steps:
      - target: "direct://chat"

Run: helix run chat-agent.yaml Access: POST to http://localhost:4100/chat with AG-UI RunAgentInput JSON.


Example 2: RAG Knowledge Agent (OpenAI, Vector Memory, Qdrant, A2A)

A knowledge base agent that ingests documents, stores embeddings in Qdrant, and answers questions using RAG. Exposed via A2A for inter-agent communication.

helix:
  management:
    port: 8081
    enabled: true
  health:
    enabled: true
    port: 8082
  a2a:
    servers:
      - enabled: true
        addr: "0.0.0.0:9000"
        name: "knowledge-agent"
        description: "Knowledge base agent with RAG-powered search"
        version: "1.0.0"

agents:
  # Knowledge query agent
  - id: knowledge-bot
    type: react
    trigger: direct://ask
    description: "Ask questions about the knowledge base"
    model:
      provider: openai
      model: gpt-4o
      api_key: "${OPENAI_API_KEY}"
    system_prompt: |
      You are a knowledge base assistant. Always use the search_docs
      tool to find relevant information before answering. Cite your
      sources by mentioning the document name and section.
      If you cannot find relevant information, say so clearly.
    memory:
      enabled: true
      type: composite
      backends:
        - type: sliding-window
          window_size: 10
        - type: vector
          top_k: 3
          embed:
            provider: openai
            model: text-embedding-3-small
            api_key: "${OPENAI_API_KEY}"
    max_iterations: 8
    tools:
      - name: search_docs
        type: rag-query
        description: "Search the knowledge base for relevant documents"
        config:
          collection: "knowledge"
          store: "qdrant://localhost:6333"
          embed_provider: "openai"
          embed_model: "text-embedding-3-small"
          top_k: 5
          rerank: "mmr"
          rerank_lambda: 0.7

workflows:
  # Document ingestion pipeline
  - id: ingest-pipeline
    trigger: "direct://ingest"
    description: "Ingest documents into the knowledge base"
    steps:
      - rag-ingest:
          parser: markdown
      - rag-chunk:
          strategy: recursive
          chunk_size: 512
          overlap: 64
      - rag-embed:
          provider: openai
          model: text-embedding-3-small
      - target: "qdrant://localhost:6333?collection=knowledge"
      - target: "log://ingest?level=INFO&showBody=false"

  # A2A server
  - id: a2a-server
    trigger: "a2a-server://0.0.0.0:9000?name=knowledge-agent&description=Knowledge+Agent"
    steps:
      - target: "direct://ask"

Agent Card: Automatically available at http://localhost:9000/.well-known/agent-card.json.


Example 3: Multi-Agent Supervisor (3 Sub-Agents, Plan-Execute, Tools)

A supervisor agent that delegates to three specialist sub-agents, each with different capabilities and LLM providers.

helix:
  management:
    port: 8081
    enabled: true
  health:
    enabled: true
    port: 8082
  audit:
    enabled: true
    store: memory
    capacity: 5000

agents:
  - id: enterprise-supervisor
    type: supervisor
    trigger: direct://assist
    description: "Enterprise AI assistant with specialized sub-agents"
    model:
      provider: openai
      model: gpt-4o
      api_key: "${OPENAI_API_KEY}"
    system_prompt: |
      You are an enterprise assistant supervisor. Analyze each request
      and route to the best specialist:
      - data-analyst: SQL queries, data analysis, reporting, metrics
      - code-assistant: code generation, debugging, code review
      - research-agent: market research, competitive analysis, trend reports
    sub_agents:
      - id: data-analyst
        type: plan-execute
        model:
          provider: openai
          model: gpt-4o
          api_key: "${OPENAI_API_KEY}"
        system_prompt: |
          You are a data analyst. Plan your analysis steps, then
          execute them using available tools. Always validate your
          SQL before running it.
        max_iterations: 15
        memory:
          enabled: true
          type: sliding-window
          window_size: 15
        tools:
          - name: run_sql
            type: function
            description: "Execute a SQL query against the analytics database"
            parameters:
              type: object
              properties:
                query:
                  type: string
                  description: "SQL query to execute"
                database:
                  type: string
                  enum: ["analytics", "warehouse"]
              required: ["query"]
          - name: create_chart
            type: function
            description: "Create a chart from query results"
            parameters:
              type: object
              properties:
                data:
                  type: string
                  description: "JSON data for the chart"
                chart_type:
                  type: string
                  enum: ["bar", "line", "pie", "scatter"]
              required: ["data", "chart_type"]

      - id: code-assistant
        type: react
        model:
          provider: anthropic
          model: claude-sonnet-4-20250514
          api_key: "${ANTHROPIC_API_KEY}"
        system_prompt: |
          You are an expert software engineer. Help with code
          generation, debugging, and code review. Always explain
          your reasoning and suggest best practices.
        max_iterations: 10
        memory:
          enabled: true
          type: sliding-window
          window_size: 20
        tools:
          - name: run_tests
            type: function
            description: "Run unit tests for a code snippet"
            parameters:
              type: object
              properties:
                language:
                  type: string
                  enum: ["go", "python", "javascript"]
                code:
                  type: string
              required: ["language", "code"]
          - name: external-code-tools
            type: mcp
            mcp_server:
              url: "http://localhost:3001"
              transport: http
              tools: ["lint", "format", "analyze"]

      - id: research-agent
        type: deep
        model:
          provider: openai
          model: gpt-4o
          api_key: "${OPENAI_API_KEY}"
        system_prompt: |
          You are a research specialist. Break complex research
          questions into sub-questions, research each thoroughly,
          and synthesize comprehensive answers.
        deep_max_depth: 3
        max_iterations: 10
        tools:
          - name: web_search
            type: function
            description: "Search the web for information"
            parameters:
              type: object
              properties:
                query:
                  type: string
              required: ["query"]

workflows:
  - id: http-interface
    trigger: "http://0.0.0.0:8080/assist"
    steps:
      - target: "direct://assist"

Example 4: Customer Support Agent (Anthropic, SQL Memory, Kafka Integration)

A production customer support agent that reads tickets from Kafka, persists conversation history in PostgreSQL, and writes classified results back to Kafka.

helix:
  management:
    port: 8081
    enabled: true
  health:
    enabled: true
    port: 8082
  shutdown:
    timeout: "30s"
    drain: true
  audit:
    enabled: true
    store: memory
    capacity: 10000
  logging:
    level: INFO
    format: json

workflows:
  # Process incoming support tickets from Kafka
  - id: ticket-processor
    trigger: "kafka://support-tickets?brokers=${KAFKA_BROKERS}&group=support-agents&autoOffsetReset=earliest"
    steps:
      # Classify and respond to the ticket
      - agent:
          id: support-agent
          type: react
          model:
            provider: anthropic
            model: claude-sonnet-4-20250514
            api_key: "${ANTHROPIC_API_KEY}"
            max_tokens: 2048
          system_prompt: |
            You are a customer support agent for Acme Corp. Process each
            support ticket and provide:
            1. A classification (billing, technical, feature-request, complaint)
            2. A priority level (critical, high, medium, low)
            3. A draft response to the customer
            4. Internal notes for the support team

            Respond with JSON:
            {
              "classification": "...",
              "priority": "...",
              "customer_response": "...",
              "internal_notes": "...",
              "escalate": false
            }

            Use tools to look up customer information and check the
            knowledge base before responding.
          memory:
            enabled: true
            type: sql-long-term
            dsn: "${DATABASE_URL}"
            session_id: "${header.kafka.key}"
          max_iterations: 8
          tools:
            - name: lookup_customer
              type: function
              description: "Look up customer information by email or ID"
              parameters:
                type: object
                properties:
                  customer_id:
                    type: string
                  email:
                    type: string
                required: []
            - name: search_kb
              type: rag-query
              description: "Search the support knowledge base"
              config:
                collection: "support-kb"
                store: "qdrant://qdrant:6333"
                embed_provider: "openai"
                embed_model: "text-embedding-3-small"
                top_k: 3
            - name: create_ticket
              type: function
              description: "Create or update a support ticket in the ticketing system"
              parameters:
                type: object
                properties:
                  action:
                    type: string
                    enum: ["create", "update", "escalate"]
                  ticket_id:
                    type: string
                  data:
                    type: object
                required: ["action"]

      # Route based on classification
      - choice:
          when:
            - expression: "${bodyAs(map).escalate} == true"
              steps:
                - target: "kafka://escalated-tickets?brokers=${KAFKA_BROKERS}"
            - expression: "${bodyAs(map).priority} == 'critical'"
              steps:
                - target: "kafka://critical-tickets?brokers=${KAFKA_BROKERS}"
          otherwise:
            - target: "kafka://processed-tickets?brokers=${KAFKA_BROKERS}"

      # Always log the result
      - target: "log://support?level=INFO"

  # Expose via AG-UI for internal support dashboard

agents:
  - id: interactive-support
    type: react
    trigger: direct://support-chat
    description: "Interactive support agent chat"
    model:
      provider: anthropic
      model: claude-sonnet-4-20250514
      api_key: "${ANTHROPIC_API_KEY}"
    system_prompt: |
      You are an interactive support agent for the internal team.
      Help agents resolve customer issues by searching the knowledge
      base and customer records.
    memory:
      enabled: true
      type: sliding-window
      window_size: 30
    max_iterations: 10
    tools:
      - name: lookup_customer
        type: function
        description: "Look up customer by ID"
        parameters:
          type: object
          properties:
            customer_id:
              type: string
          required: ["customer_id"]
      - name: search_kb
        type: rag-query
        description: "Search support knowledge base"
        config:
          collection: "support-kb"
          store: "qdrant://qdrant:6333"
          embed_provider: "openai"
          embed_model: "text-embedding-3-small"
          top_k: 5

workflows:
  - id: agui-support
    trigger: "agui://0.0.0.0:4100/support?name=support-dashboard"
    steps:
      - target: "direct://support-chat"

Example 5: Multi-Protocol Agent Gateway (All 4 Protocols, Audit, Metrics)

A full-featured agent gateway exposing the same intelligent agent via all four protocols with enterprise features enabled.

helix:
  management:
    port: 8081
    enabled: true
  health:
    enabled: true
    port: 8082
  shutdown:
    timeout: "45s"
    drain: true
  logging:
    level: INFO
    format: json
    components:
      agent: DEBUG
      mcp: DEBUG
  audit:
    enabled: true
    store: memory
    capacity: 50000
  tenant:
    enabled: true
    header: "X-Tenant-ID"
    required: false
  agui:
    servers:
      - enabled: true
        addr: "0.0.0.0:4100"
        path: "/agent"
        name: "helix-gateway"
  a2a:
    servers:
      - enabled: true
        addr: "0.0.0.0:9000"
        name: "helix-gateway"
        description: "Helix AI Gateway - multi-protocol agent with tools, memory, and RAG"
        version: "2.0.0"
  a2ui:
    servers:
      - enabled: true
        addr: "0.0.0.0:4200"
        path: "/surface"
        name: "helix-gateway"
  mcp:
    servers:
      - enabled: true
        transport: http
        addr: "0.0.0.0:8080"
        name: "helix-gateway-tools"
        version: "2.0.0"
        workflows: ["search-docs", "query-analytics", "manage-tasks"]

workflows:
  # ---- Core Agent ----
  - id: gateway-agent
    trigger: "direct://gateway"
    description: "Primary AI gateway agent"
    steps:
      - throttle:
          rate: 100
          period: "1m"
      - agent:
          id: gateway-bot
          type: react
          model:
            provider: openai
            model: gpt-4o
            api_key: "${OPENAI_API_KEY}"
            temperature: 0.7
            max_tokens: 4096
          system_prompt: |
            You are the Helix AI Gateway agent. You have access to:
            - Document search (search_docs): Search the knowledge base
            - Analytics queries (query_analytics): Query business data
            - Task management (manage_tasks): Create and update tasks
            - External MCP tools: Code analysis and formatting

            Always search for relevant information before answering.
            Be precise, cite sources, and offer to help further.
          memory:
            enabled: true
            type: composite
            backends:
              - type: sliding-window
                window_size: 25
              - type: vector
                top_k: 5
                embed:
                  provider: openai
                  model: text-embedding-3-small
                  api_key: "${OPENAI_API_KEY}"
          max_iterations: 12
          tools:
            - name: search_docs
              type: rag-query
              description: "Search the documentation knowledge base"
              config:
                collection: "docs"
                store: "qdrant://qdrant:6333"
                embed_provider: "openai"
                embed_model: "text-embedding-3-small"
                top_k: 5
                rerank: "mmr"
            - name: query_analytics
              type: function
              description: "Query the analytics database"
              parameters:
                type: object
                properties:
                  query:
                    type: string
                    description: "SQL query"
                  database:
                    type: string
                    enum: ["analytics", "warehouse"]
                required: ["query"]
            - name: manage_tasks
              type: function
              description: "Create, update, or list tasks"
              parameters:
                type: object
                properties:
                  action:
                    type: string
                    enum: ["create", "update", "list", "close"]
                  task_id:
                    type: string
                  title:
                    type: string
                  assignee:
                    type: string
                required: ["action"]
            - name: code-tools
              type: mcp
              description: "Code analysis and formatting tools"
              mcp_server:
                url: "http://code-tools:3000"
                transport: http

  # ---- Invocable utility workflows (exposed via MCP) ----
  - id: search-docs
    trigger: "direct://search-docs"
    description: "Search the documentation knowledge base for relevant articles"
    steps:
      - target: "agent://gateway-bot"

  - id: query-analytics
    trigger: "direct://query-analytics"
    description: "Query the analytics database with SQL"
    steps:
      - target: "postgres://analytics-db?query=${body}"

  - id: manage-tasks
    trigger: "direct://manage-tasks"
    description: "Create, update, or list project tasks"
    steps:
      - target: "http://task-api:8080/tasks"

  # ---- Protocol servers ----
  - id: agui-server
    trigger: "agui://0.0.0.0:4100/agent?name=helix-gateway"
    steps:
      - target: "direct://gateway"

  - id: a2a-server
    trigger: "a2a-server://0.0.0.0:9000?name=helix-gateway&description=Helix+AI+Gateway"
    steps:
      - target: "direct://gateway"

  - id: a2ui-server
    trigger: "a2ui://0.0.0.0:4200/surface?name=helix-gateway"
    steps:
      - target: "direct://gateway"

  - id: mcp-server
    trigger: "mcp-server://0.0.0.0:8080?name=helix-gateway-tools&transport=http"
    steps:
      - log: "MCP server active"

  # ---- Ingestion pipeline ----
  - id: doc-ingestion
    trigger: "direct://ingest-docs"
    description: "Ingest documents into the knowledge base"
    steps:
      - rag-ingest:
          parser: markdown
      - rag-chunk:
          strategy: recursive
          chunk_size: 512
          overlap: 64
      - rag-embed:
          provider: openai
          model: text-embedding-3-small
      - target: "qdrant://qdrant:6333?collection=docs"
      - target: "log://ingestion?level=INFO"

Ports summary: | Port | Protocol | Purpose | |---|---|---| | 4100 | AG-UI | Frontend streaming (CopilotKit) | | 4200 | A2UI | Rich declarative UI | | 8080 | MCP | Tool exposure for LLMs | | 8081 | HTTP | Management API + metrics | | 8082 | HTTP | Health checks | | 9000 | A2A | Agent-to-agent communication |

Run: helix run gateway.yaml


Appendix: Quick Reference

Agent YAML Schema

agent:
  id: string                    # Required: unique agent identifier
  type: string                  # react|supervisor|plan-execute|deep|sequential|parallel|loop
  model:                        # Required: LLM configuration
    provider: string            # openai|anthropic|ollama
    model: string               # Model identifier
    api_key: string             # API key (supports ${ENV_VAR})
    base_url: string            # Custom API endpoint
    temperature: float          # 0.0-2.0
    max_tokens: int             # Maximum output tokens
    top_p: float                # Nucleus sampling
    stop: [string]              # Stop sequences
    response_format: string     # text|json_object
  system_prompt: string         # System message for the agent
  max_iterations: int           # Max reasoning iterations (default: 10)
  deep_max_depth: int           # Max recursion for deep agent (default: 3)
  memory:                       # Memory configuration
    type: string                # conversation|sliding-window|summary|token-window|
                                # long-term|sql-long-term|vector|composite
    store: string               # memory(default)|redis|file|memx|postgres
    window_size: int            # For sliding-window
    max_tokens: int             # For token-window
    max_messages: int           # For summary
    dsn: string                 # For sql-long-term
    redis_url: string           # For store: redis
    file_path: string           # For store: file
    memx_url: string            # For store: memx (postgres:// wire)
    session_id: string          # For sql-long-term / memx
    top_k: int                  # For vector
    connection: string          # For external vector stores (qdrant/pinecone/milvus/pgvector/…)
    collection: string          # For external vector stores: collection/table/index name
    api_key: string             # For external vector stores: key/token
    username: string            # For external vector stores: opensearch/elasticsearch
    password: string            # For external vector stores: redis/opensearch/elasticsearch
    embed:                      # For vector
      provider: string
      model: string
      api_key: string
    backends: [memory]          # For composite
  tools:                        # Tool definitions
    - name: string
      type: string              # function|tool|a2a|mcp|connector
      description: string
      parameters: object        # JSON Schema
      config: object            # Type-specific configuration
      mcp_server:               # For type=mcp
        url: string
        transport: string       # http|sse|stdio
        command: string         # For stdio
        args: [string]          # For stdio
        headers: {string: string}
        tools: [string]         # Filter specific tools
  sub_agents: [agent]           # For supervisor|sequential|parallel

URI Schemes

Scheme Direction Purpose
agent://id Producer (To) Send messages to an agent
agui://host:port/path Consumer (From) AG-UI server
a2a-server://host:port Consumer (From) A2A server
a2a://host:port Producer (To) A2A client
a2ui://host:port/path Consumer (From) A2UI server
mcp-server://host:port Consumer (From) MCP server
mcp://host:port/tool Producer (To) MCP client

Exchange Headers Set by Agents

Header Description
agent.id Agent identifier
agent.type Agent type (react, supervisor, etc.)
agent.model Provider/model (e.g., openai/gpt-4o)
llm.prompt_tokens Input tokens used
llm.completion_tokens Output tokens generated
llm.total_tokens Total tokens consumed
HelixStream Set to "true" to enable streaming mode
HelixAGUIThreadID AG-UI thread ID
HelixAGUIRunID AG-UI run ID
HelixA2ATaskID A2A task ID
HelixA2UISurfaceID A2UI surface ID
HelixMcpToolName MCP tool name
Content-Type Set to "text/event-stream" for streaming responses

12. Strict YAML Validation

Helix enforces strict validation on all agent YAML definitions at load time. Invalid configurations are rejected with clear error messages before any route starts.

Required Fields

Field Required For Description
id All agents Unique agent identifier
type All agents Agent type: react, supervisor, plan-execute, deep, sequential, parallel, loop
model.provider All agents LLM provider: openai, anthropic, ollama
model.model All agents Model name (e.g., gpt-4o, claude-sonnet-4-20250514)
sub_agents supervisor, sequential, parallel List of sub-agent definitions
trigger Top-level agents: URI that triggers the agent (e.g., http-server://..., direct://...)

Enum Validation

Field Valid Values
type react, supervisor, plan-execute, deep, sequential, parallel, loop
model.provider openai, anthropic, ollama
memory.type conversation, sliding-window, summary, token-window, long-term, sql-long-term, vector, composite
tools[].type function, tool, a2a, mcp, connector
approval_policy never, always, tagged

Tool Required Fields

Each tool type has specific required fields:

Tool Type Required Fields
function name, description, parameters
tool name, tool_id
a2a name, description, url
mcp name, mcp_server.url or mcp_server.command
connector name, description, connector

13. Human-in-the-Loop

Control when agents can act autonomously and when they must pause for human approval. The approval_policy field determines tool execution behavior.

Approval Policies

Policy Behavior
never All tool calls execute automatically (default)
always Every tool call pauses and waits for human approval
tagged Only tools marked with requires_approval: true pause

Configuration

agents:
  - id: autonomous-agent
    type: react
    trigger: http-server://0.0.0.0:8080/agent
    model:
      provider: openai
      model: gpt-4o
    approval_policy: tagged
    tools:
      - name: search
        type: function
        description: "Search the knowledge base"
        parameters:
          type: object
          properties:
            query: { type: string }
          required: ["query"]
        # No requires_approval -- executes automatically

      - name: delete_record
        type: function
        description: "Delete a database record"
        requires_approval: true    # Pauses for human approval
        parameters:
          type: object
          properties:
            record_id: { type: string }
          required: ["record_id"]

      - name: send_email
        type: function
        description: "Send an email to a customer"
        requires_approval: true    # Pauses for human approval
        parameters:
          type: object
          properties:
            target: { type: string }
            subject: { type: string }
            body: { type: string }
          required: ["to", "subject", "body"]

When a tool with requires_approval: true is called and approval_policy: tagged is set, the agent sets the exchange header agent.approval.pending to true and returns the pending tool call details as a JSON body. The frontend or orchestrator can then approve or reject the call.


14. Agent Memory with MemX Backend

For production agents that need durable memory that survives restarts, set store: memx. memx is reached over the PostgreSQL wire, so the connection is a postgres:// URL supplied in memx_url. It works with every non-sql-long-term memory type:

  • conversation / long-term → a native durable, append-only message store (optionally semantic when embed is set).
  • sliding-window / token-window / summary / smart → the in-memory type keeps its retrieval semantics on read while every message is persisted to memx, so a restart replays the history and re-derives the window.
  • vector → a durable semantic store (type: vector); embeddings survive a restart. The table is sized to the embedding model automatically.

The backing table(s) are created automatically on startup.

Configuration

agents:
  - id: production-agent
    type: react
    trigger: http-server://0.0.0.0:8080/chat
    model:
      provider: openai
      model: gpt-4o
    memory:
      enabled: true
      type: vector
      top_k: 10
      session_id_header: X-Session-ID
      store: memx
      memx_url: "postgres://user:pass@localhost:5432/memx"   # postgres:// wire
      embed:
        provider: openai
        model: text-embedding-3-small
        api_key: "${OPENAI_API_KEY}"
Parameter Required Description
store Yes memx
memx_url Yes memx connection (postgres://…); dsn is accepted as a fallback
session_id / session_id_header No scope key (static, or per-request from a header)
embed Vector only embedding provider/model (also enables semantic search on conversation/long-term)

Composite Memory with MemX

Combine a sliding window (recent context) with memx vector search (durable long-term recall):

agents:
  - id: advanced-agent
    type: react
    trigger: http-server://0.0.0.0:8080/chat
    model:
      provider: anthropic
      model: claude-sonnet-4-20250514
    memory:
      enabled: true
      type: composite
      session_id_header: X-Session-ID
      backends:
        - type: sliding-window
          window_size: 20
        - type: vector
          top_k: 10
          store: memx
          memx_url: "postgres://user:pass@localhost:5432/memx"
          embed:
            provider: openai
            model: text-embedding-3-small
            api_key: "${OPENAI_API_KEY}"

15. Multimodal Agents

Agents can process images, voice, and file uploads natively via the AG-UI protocol. This enables rich chat interfaces where users can send any combination of text, images, documents, and audio.

Image Analysis Agent

agents:
  - id: vision-agent
    type: react
    trigger: agui://0.0.0.0:4100/agent
    model:
      provider: openai
      model: gpt-4o
    system_prompt: |
      You are a multimodal assistant. You can see images, read documents,
      and listen to audio. Respond based on whatever the user provides.

Voice-Enabled Agent

agents:
  - id: voice-agent
    type: react
    trigger: agui://0.0.0.0:4100/voice
    model:
      provider: openai
      model: gpt-4o
    system_prompt: "You are a voice-enabled assistant. Keep responses concise and conversational."

When audio is received without text, the agent automatically transcribes the audio before processing. Supported formats: WAV, WebM, MP3, OGG, M4A.

File Processing Agent

agents:
  - id: file-agent
    type: react
    trigger: agui://0.0.0.0:4100/files
    model:
      provider: openai
      model: gpt-4o
    system_prompt: |
      You process uploaded files:
      - Images: describe and analyze
      - PDFs: extract text, summarize, answer questions
      - CSV: analyze data, compute statistics
      - Text: summarize and explain

Multimodal Content Types

Input Type How It Arrives Agent Behavior
Image (JPEG, PNG, etc.) ContentPart with type: image Sent to vision-capable LLM
Voice recording ContentPart with type: audio Auto-transcribed, then processed as text
PDF document ContentPart with type: file Extracted via reader or OCR, then processed
CSV / data file ContentPart with type: file Parsed into structured text
Plain text ContentPart with type: text Standard text processing

For PDF OCR, configure the helix.ocr section in helix.yaml. See the Configuration Guide for backend options.


16. Built-in Function Registry

Helix ships with 14 ready-to-use built-in functions that agents can call without writing any Go code. Declare them in YAML with type: function and set the function: field to a registry key. No implementation needed -- Helix provides the logic.

16.1 Available Functions

Category Function Description
File System fs.read Read a file and return its contents
File System fs.write Write content to a file
File System fs.edit Apply a targeted string replacement to a file
File System fs.list List files and directories at a path
File System fs.glob Find files matching a glob pattern
File System fs.grep Search file contents with a regex pattern
File System fs.mkdir Create a directory (with parents)
File System fs.delete Delete a file or empty directory
Shell shell.exec Execute a shell command (sandboxed)
Web web.fetch Fetch a URL, strip HTML to text, truncate to 30KB
Data data.json_parse Parse JSON string with optional path extraction
Data data.json_format Pretty-print a JSON value
Data data.csv_parse Parse CSV text into an array of objects
Math math.eval Evaluate a math expression

16.2 YAML Syntax

Use type: function with a function: field referencing the registry key:

agents:
  - id: code-agent
    type: react
    trigger: http-server://0.0.0.0:8080/code
    model:
      provider: anthropic
      model: claude-sonnet-4-20250514
      api_key: "${env:ANTHROPIC_API_KEY}"
    system_prompt: |
      You are a coding assistant. Use the tools to read, edit,
      and search files in the project directory.
    tools:
      - name: read-file
        type: function
        function: fs.read
        description: Read a file from disk
      - name: write-file
        type: function
        function: fs.write
        description: Write content to a file
      - name: edit-file
        type: function
        function: fs.edit
        description: Apply a targeted edit to a file
      - name: list-files
        type: function
        function: fs.list
        description: List directory contents
      - name: find-files
        type: function
        function: fs.glob
        description: Find files matching a pattern
      - name: search-code
        type: function
        function: fs.grep
        description: Search file contents with regex
      - name: run-command
        type: function
        function: shell.exec
        description: Execute a shell command
      - name: fetch-url
        type: function
        function: web.fetch
        description: Fetch a web page

16.3 Security -- shell.exec

The shell.exec function is sandboxed with the following protections:

  • Timeout: Commands are killed after a configurable timeout (default 30 seconds)
  • Blocked commands: Dangerous commands are rejected (e.g., rm -rf /, shutdown, reboot, mkfs, dd)
  • Working directory: Constrained to the agent's configured --dir path
  • No shell expansion: Commands are executed directly, not through a shell interpreter

16.4 Complete Code Agent Example

agents:
  - id: code-assistant
    type: react
    trigger: http-server://0.0.0.0:8080/chat
    model:
      provider: anthropic
      model: claude-sonnet-4-20250514
      api_key: "${env:ANTHROPIC_API_KEY}"
    system_prompt: |
      You are an expert coding assistant. You can read, write, and search
      files in the current project. Always read a file before editing it.
      Use grep to find relevant code. Use shell.exec to run tests.
    memory:
      enabled: true
      type: smart
      max_tokens: 100000
    tools:
      - name: read-file
        type: function
        function: fs.read
        description: Read a file
      - name: write-file
        type: function
        function: fs.write
        description: Write content to a file
      - name: edit-file
        type: function
        function: fs.edit
        description: Apply a string replacement to a file
      - name: list-dir
        type: function
        function: fs.list
        description: List directory contents
      - name: find-files
        type: function
        function: fs.glob
        description: Find files matching a glob pattern
      - name: search
        type: function
        function: fs.grep
        description: Search file contents with regex
      - name: run
        type: function
        function: shell.exec
        description: Run a shell command
      - name: fetch
        type: function
        function: web.fetch
        description: Fetch a URL
      - name: parse-json
        type: function
        function: data.json_parse
        description: Parse a JSON string
      - name: calc
        type: function
        function: math.eval
        description: Evaluate a math expression

17. SmartMemory -- Token-Managed Memory

SmartMemory is a token-aware memory backend designed for tool-using agents (especially code agents). Unlike sliding-window memory which drops messages by count, SmartMemory tracks token usage and intelligently compresses the conversation to stay within budget.

17.1 Why SmartMemory?

Tool-using agents produce large messages -- a single fs.read result can be thousands of tokens. Traditional turn-based memory (sliding window of N messages) doesn't account for this. SmartMemory solves this by:

  • Tracking tokens, not turns -- eviction is based on actual token count, not message count
  • Compressing large tool results -- tool outputs over a threshold are compressed to first 10 + last 10 lines with a summary
  • Maintaining a context map -- evicted messages are summarized so the agent retains awareness of what it previously saw
  • Preserving pairs -- tool call and tool response messages are always dropped together (never orphaned)
  • Protecting system messages -- the system prompt is never evicted

17.2 Configuration

agents:
  - id: code-agent
    type: react
    trigger: http-server://0.0.0.0:8080/chat
    model:
      provider: anthropic
      model: claude-sonnet-4-20250514
    memory:
      enabled: true
      type: smart          # or "code" (alias for smart)
      max_tokens: 100000   # token budget for conversation history
Field Default Description
type -- smart or code (alias)
max_tokens 100000 Maximum tokens allowed in conversation history before compression kicks in

17.3 How It Works

  1. After each agent turn, SmartMemory counts the total tokens in the conversation
  2. If the total exceeds max_tokens, the oldest non-system messages are evicted
  3. Tool call + tool result pairs are evicted together
  4. Large tool results (e.g., file contents) are compressed: first 10 lines + "..." + last 10 lines
  5. A context map entry is created for each evicted block summarizing what was discussed
  6. The context map is prepended to the system message so the agent knows what it previously explored

18. Playwright MCP Browser Tools

Agents can browse the web interactively using Playwright MCP. This gives agents 22 browser automation tools including navigation, clicking, typing, screenshots, and DOM inspection.

18.1 Configuration

agents:
  - id: browser-agent
    type: react
    trigger: http-server://0.0.0.0:8080/browse
    model:
      provider: anthropic
      model: claude-sonnet-4-20250514
      api_key: "${env:ANTHROPIC_API_KEY}"
    system_prompt: |
      You are a web research agent. Use the browser tools to navigate
      websites, extract information, and take screenshots.
    tools:
      - name: browser
        type: mcp
        description: "Browser automation via Playwright"
        mcp_server:
          command: npx
          args: ["@playwright/mcp"]
          transport: stdio

18.2 How It Works

The type: mcp tool with transport: stdio spawns the Playwright MCP server as a subprocess. Helix discovers all 22 tools via the MCP protocol and makes them available to the agent. The agent can then call individual tools like browser_navigate, browser_click, browser_type, etc.

18.3 Available Browser Tools

Tool Description
browser_navigate Navigate to a URL
browser_click Click an element
browser_type Type text into an input
browser_snapshot Get an accessibility snapshot of the page
browser_take_screenshot Capture a screenshot
browser_go_back Go back in browser history
browser_go_forward Go forward in browser history
browser_wait Wait for a condition
browser_select_option Select a dropdown option
browser_hover Hover over an element
browser_drag Drag an element
browser_press_key Press a keyboard key
browser_scroll Scroll the page
browser_fill Fill a form field
browser_get_text Get text content of an element
browser_get_attribute Get an attribute value
browser_evaluate Evaluate JavaScript in the page
browser_new_tab Open a new tab
browser_close_tab Close a tab
browser_switch_tab Switch to a tab
browser_file_upload Upload a file
browser_pdf Save page as PDF

18.4 Prerequisites

Install Playwright MCP:

npm install -g @playwright/mcp
# or use npx (no install needed):
npx @playwright/mcp

18.5 Browser Agent Example

agents:
  - id: web-researcher
    type: react
    trigger: http-server://0.0.0.0:8080/research
    model:
      provider: anthropic
      model: claude-sonnet-4-20250514
      api_key: "${env:ANTHROPIC_API_KEY}"
    system_prompt: |
      You are a web research agent. Navigate to websites, read their content,
      and summarize findings. Use browser_snapshot to read page content
      (it's faster than screenshots). Take screenshots only when visual
      context matters.
    memory:
      enabled: true
      type: smart
      max_tokens: 100000
    tools:
      - name: browser
        type: mcp
        description: "Browse the web with Playwright"
        mcp_server:
          command: npx
          args: ["@playwright/mcp"]
          transport: stdio
      - name: fetch
        type: function
        function: web.fetch
        description: "Quick-fetch a URL as plain text (no JS rendering)"