Building a Production AI Copilot¶
A step-by-step guide to building a production-grade AI copilot that combines streaming chat, rich UIs, RAG knowledge, tool use, memory, multi-agent orchestration, and multi-protocol exposure — all in declarative YAML. From zero to a deployed, multi-agent copilot in 12 incremental steps.
Table of Contents¶
- What is a Copilot?
- Architecture Overview
- Step 1 — Core Chat Agent
- Step 2 — Streaming Chat Frontend (AG-UI)
- Step 3 — Rich Declarative UIs (A2UI)
- Step 4 — RAG Knowledge Base
- Step 5 — Memory and Session Persistence
- Step 6 — Tool Integration
- Step 7 — MCP Tool Server Integration
- Step 8 — Multi-Agent Orchestration
- Step 9 — Security and Authentication
- Step 10 — Multi-Protocol Exposure
- Step 11 — Observability and Quality Monitoring
- Step 12 — Production Deployment
- Complete Copilot — Full YAML Reference
- Copilot Patterns
- Frontend Integration Guide
1. What is a Copilot?¶
A copilot is an AI assistant embedded into an application that helps users accomplish tasks through natural language conversation. Unlike a simple chatbot, a production copilot needs:
- Streaming responses — Real-time token-by-token delivery for responsive UX
- Knowledge grounding — RAG over domain-specific documents to prevent hallucination
- Tool use — Ability to search, query databases, call APIs, read/write files
- Memory — Conversation history within sessions and semantic recall across sessions
- Multi-agent reasoning — Specialist agents for different domains, coordinated by a supervisor
- Rich UI rendering — Beyond text: cards, buttons, forms, data tables, charts
- Multi-protocol access — Chat frontend, Claude Desktop (MCP), other agents (A2A)
- Security — Authentication, authorization, rate limiting, tenant isolation
- Observability — Metrics, tracing, quality evaluation, alerting
Helix provides all of these as declarative YAML configuration. No Go code required.
2. Architecture Overview¶
┌─────────────────────────────┐
│ Helix Copilot Engine │
│ │
┌──────────────┐ AG-UI (SSE) │ ┌───────────────────────┐ │
│ Chat Frontend│◄────────────────►│ │ Supervisor Agent │ │
│ (CopilotKit) │ │ │ ┌─────┐ ┌─────────┐ │ │
└──────────────┘ │ │ │ReAct│ │Plan-Exec│ │ │
│ │ │Agent│ │ Agent │ │ │
┌──────────────┐ A2UI (SSE) │ │ └──┬──┘ └────┬────┘ │ │
│ Rich UI App │◄────────────────►│ │ │ │ │ │
│ (Google A2UI)│ │ └─────┼─────────┼───────┘ │
└──────────────┘ │ │ │ │
│ ┌─────▼─────────▼───────┐ │
┌──────────────┐ MCP (HTTP) │ │ Tool Layer │ │
│Claude Desktop│◄────────────────►│ │ ┌────┐┌────┐┌──────┐ │ │
│ / Cursor │ │ │ │RAG ││MCP ││HTTP │ │ │
└──────────────┘ │ │ │Tool││Tool││Tools │ │ │
│ │ └──┬─┘└──┬─┘└──┬───┘ │ │
┌──────────────┐ A2A (JSON-RPC)│ └────┼─────┼─────┼──────┘ │
│ Other Agents │◄────────────────►│ │ │ │ │
│ (A2A clients)│ │ ┌────▼─────▼─────▼──────┐ │
└──────────────┘ │ │ Knowledge Layer │ │
│ │ ┌────────┐ ┌────────┐ │ │
┌──────────────┐ REST (HTTP) │ │ │pgvector│ │External│ │ │
│ REST API │◄────────────────►│ │ │ RAG │ │MCP Svrs│ │ │
│ Clients │ │ │ └────────┘ └────────┘ │ │
└──────────────┘ │ │ ┌────────┐ ┌────────┐ │ │
│ │ │Composite│ │ Graph │ │ │
│ │ │ Memory │ │ KG │ │ │
│ │ └────────┘ └────────┘ │ │
│ └───────────────────────┘ │
└─────────────────────────────┘
Protocol Endpoints:
| Protocol | Port | Purpose | Client |
|---|---|---|---|
| AG-UI | 4100 | Streaming chat (SSE) | CopilotKit, custom frontends |
| A2UI | 4200 | Rich declarative UIs (SSE) | Google A2UI renderers |
| MCP | 3000 | Tool exposure (HTTP/SSE/stdio) | Claude Desktop, Cursor, IDEs |
| A2A | 9000 | Agent-to-agent (JSON-RPC) | Other AI agents |
| HTTP | 8080 | REST API | Mobile apps, scripts, webhooks |
3. Step 1 — Core Chat Agent¶
Start with a single ReAct agent that forms the copilot brain.
agents:
- id: copilot
type: react
trigger: http-server://0.0.0.0:8080/api/chat
model:
provider: anthropic
model: claude-sonnet-4-20250514
api_key: "${env:ANTHROPIC_API_KEY}"
temperature: 0.4
max_tokens: 2048
system_prompt: |
You are a helpful AI copilot for [Your Company].
You assist users with questions about our products,
documentation, and internal processes.
Guidelines:
- Be concise and direct
- Cite sources when referencing documents
- If you don't know something, say so honestly
- Suggest follow-up actions when appropriate
max_iterations: 8
Run it:
Test it:
curl -X POST http://localhost:8080/api/chat \
-H "Content-Type: application/json" \
-d '{"body": "What can you help me with?"}'
4. Step 2 — Streaming Chat Frontend (AG-UI)¶
Replace the HTTP endpoint with AG-UI for real-time streaming to CopilotKit or any SSE-capable frontend.
agents:
- id: copilot
type: react
trigger: agui://0.0.0.0:4100/api/chat?name=copilot&corsOrigin=*
model:
provider: anthropic
model: claude-sonnet-4-20250514
api_key: "${env:ANTHROPIC_API_KEY}"
temperature: 0.4
max_tokens: 2048
system_prompt: |
You are a helpful AI copilot. Provide clear, well-structured
answers. Use markdown formatting for readability.
When you finish answering, suggest 2-3 relevant follow-up
questions the user might want to ask.
max_iterations: 8
How AG-UI Streaming Works¶
- Frontend sends a POST with
RunAgentInput(thread ID, messages, tools, state) - Helix extracts the last user message and runs it through the agent
- The agent's LLM response streams back as SSE events:
event: RUN_STARTED
data: {"type":"RUN_STARTED","threadId":"t-123","runId":"r-456"}
event: TEXT_MESSAGE_START
data: {"type":"TEXT_MESSAGE_START","messageId":"m-789","role":"assistant"}
event: TEXT_MESSAGE_CONTENT
data: {"type":"TEXT_MESSAGE_CONTENT","messageId":"m-789","delta":"Here's "}
event: TEXT_MESSAGE_CONTENT
data: {"type":"TEXT_MESSAGE_CONTENT","messageId":"m-789","delta":"what I found..."}
event: TEXT_MESSAGE_END
data: {"type":"TEXT_MESSAGE_END","messageId":"m-789"}
event: RUN_FINISHED
data: {"type":"RUN_FINISHED","threadId":"t-123","runId":"r-456"}
CopilotKit Integration¶
import { CopilotKit } from "@copilotkit/react-core";
import { CopilotChat } from "@copilotkit/react-ui";
function App() {
return (
<CopilotKit runtimeUrl="http://localhost:4100/api/chat">
<CopilotChat
labels={{ title: "AI Copilot", initial: "How can I help?" }}
/>
</CopilotKit>
);
}
5. Step 3 — Rich Declarative UIs (A2UI)¶
Add A2UI for rendering rich interactive surfaces — cards, buttons, forms, tables — alongside the chat stream.
agents:
# Chat endpoint (AG-UI)
- id: copilot
type: react
trigger: agui://0.0.0.0:4100/api/chat?name=copilot&corsOrigin=*
model:
provider: anthropic
model: claude-sonnet-4-20250514
api_key: "${env:ANTHROPIC_API_KEY}"
temperature: 0.4
system_prompt: "You are a helpful copilot."
max_iterations: 8
# Rich UI surface endpoint (A2UI)
- id: ui-agent
type: react
trigger: a2ui://0.0.0.0:4200/api/surface?name=copilot-ui&corsOrigin=*
model:
provider: openai
model: gpt-4o
api_key: "${env:OPENAI_API_KEY}"
temperature: 0.3
system_prompt: |
You create rich UI responses. When the user asks for data,
format your response as structured JSON that can be rendered
as cards, tables, or interactive elements.
max_iterations: 5
A2UI Protocol¶
GET — Stream UI surface updates:
Response (SSE JSONL):
{"beginRendering": {"surfaceId": "main", "root": "root"}}
{"updateComponents": {"surfaceId": "main", "components": [
{"id": "root", "type": "Column", "children": ["card-1"]},
{"id": "card-1", "type": "Card", "children": ["title", "content"]},
{"id": "title", "type": "TextStatic", "properties": {"text": "Recent Orders"}},
{"id": "content", "type": "TextStatic", "properties": {"text": "Here are your orders..."}}
]}}
POST — Receive user interactions:
{
"event": {
"surfaceId": "main",
"componentId": "approve-btn",
"type": "action",
"data": {"orderId": "ORD-123"}
}
}
6. Step 4 — RAG Knowledge Base¶
Ground the copilot in domain-specific knowledge to prevent hallucination.
6.1 Document Ingestion Pipeline¶
workflows:
# Nightly full reindex
- id: copilot-ingest-full
trigger:
uri: cron://nightly?schedule=0 2 * * *
steps:
- rag-ingest:
reader: directory
path: ./knowledge-base
recursive: true
extensions:
- ".md"
- ".pdf"
- ".html"
- ".txt"
- ".docx"
- rag-chunk:
strategy: semantic
threshold: 0.45
chunk_size: 1000
overlap: 150
- rag-embed:
provider: openai
model: text-embedding-3-small
api_key: "${env:OPENAI_API_KEY}"
- rag-store:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: copilot_knowledge
dimensions: 1536
# Extract metadata for filtering
- rag-extract:
extractors:
- title
- keywords
- summary
model:
provider: openai
model: gpt-4o-mini
api_key: "${env:OPENAI_API_KEY}"
- target: log://ingestion?level=info
# Hourly incremental updates
- id: copilot-ingest-incremental
trigger:
uri: cron://hourly?schedule=0 * * * *
steps:
- rag-ingest:
reader: directory
path: ./knowledge-base
recursive: true
- filter:
starlang: |
import "time"
modified = headers["HelixFileLastModified"]
modified > time.now() - time.hour
- rag-chunk:
strategy: semantic
threshold: 0.45
chunk_size: 1000
overlap: 150
- rag-embed:
provider: openai
model: text-embedding-3-small
api_key: "${env:OPENAI_API_KEY}"
- rag-store:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: copilot_knowledge
6.2 Copilot with RAG Tools¶
Now give the agent search tools to query the knowledge base:
agents:
- id: copilot
type: react
trigger: agui://0.0.0.0:4100/api/chat?name=copilot&corsOrigin=*
model:
provider: anthropic
model: claude-sonnet-4-20250514
api_key: "${env:ANTHROPIC_API_KEY}"
temperature: 0.4
max_tokens: 2048
system_prompt: |
You are a helpful copilot with access to a knowledge base.
WORKFLOW:
1. When the user asks a question, search the knowledge base first
2. Use search_knowledge for standard queries
3. Use search_diverse when you need broader perspectives
4. Synthesize your answer from the retrieved documents
5. Always cite which documents support your claims
6. If the knowledge base doesn't have the answer, say so honestly
Format responses with markdown for readability.
tools:
- name: search_knowledge
type: rag-query
description: "Search the knowledge base for information. Use specific, targeted queries."
parameters:
type: object
properties:
query:
type: string
description: "Specific search query"
top_k:
type: integer
default: 5
description: "Number of results to retrieve"
required:
- query
config:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: copilot_knowledge
retriever: hybrid
alpha: 0.7
- name: search_diverse
type: rag-query
description: "Search with diversity emphasis for broader coverage. Use when standard search returns too-similar results."
parameters:
type: object
properties:
query:
type: string
top_k:
type: integer
default: 8
config:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: copilot_knowledge
retriever: mmr
lambda: 0.3
fetch_k: 25
max_iterations: 8
6.3 Multi-Collection RAG¶
For copilots with multiple knowledge domains:
tools:
- name: search_product_docs
type: rag-query
description: "Search product documentation, features, and guides"
parameters:
type: object
properties:
query:
type: string
top_k:
type: integer
default: 5
config:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: product_docs
retriever: hybrid
alpha: 0.7
- name: search_api_reference
type: rag-query
description: "Search API documentation and code examples"
parameters:
type: object
properties:
query:
type: string
top_k:
type: integer
default: 5
config:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: api_docs
retriever: hybrid
alpha: 0.8
- name: search_support_history
type: rag-query
description: "Search past support tickets for similar issues and resolutions"
parameters:
type: object
properties:
query:
type: string
top_k:
type: integer
default: 5
config:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: support_tickets
retriever: hybrid
alpha: 0.5
- name: search_internal_wiki
type: rag-query
description: "Search internal wiki for processes, policies, and team knowledge"
parameters:
type: object
properties:
query:
type: string
top_k:
type: integer
default: 5
config:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: internal_wiki
retriever: mmr
lambda: 0.5
7. Step 5 — Memory and Session Persistence¶
Add memory so the copilot remembers conversation context and can recall past interactions.
7.1 Basic Session Memory¶
The session_id_header is critical for multi-user copilots — each user gets isolated memory. The frontend must send the session ID as a header with each request.
7.2 Persistent Cross-Session Memory¶
memory:
enabled: true
type: sql-long-term
dsn: "${env:DATABASE_URL}"
session_id_header: "X-Session-ID"
This stores all conversation history in PostgreSQL. Conversations survive server restarts.
7.3 Production Composite Memory (Recommended)¶
Combine multiple memory types for the best of all worlds:
memory:
enabled: true
type: composite
session_id_header: "X-Session-ID"
backends:
# Recent context: last 25 messages for immediate conversation flow
- type: sliding-window
window_size: 25
# Semantic recall: find relevant past conversations by meaning
- type: vector
top_k: 5
embed:
provider: openai
model: text-embedding-3-small
api_key: "${env:OPENAI_API_KEY}"
# Persistent storage: full history survives restarts
- type: sql-long-term
dsn: "${env:DATABASE_URL}"
session_id_header: "X-Session-ID"
How composite memory works: - Add (new messages): Written to ALL backends simultaneously - Get (conversation history): Merges results from all backends, deduplicates - Search (semantic): Each backend searches independently, results merged
This means the copilot: 1. Always has the last 25 messages for immediate context 2. Can semantically recall relevant past conversations ("as we discussed last week...") 3. Never loses history even if the server restarts
7.4 Memory-Aware System Prompt¶
Update the system prompt to leverage memory:
system_prompt: |
You are a helpful copilot with conversation memory.
- You remember past conversations with this user
- Reference relevant past discussions when applicable
- If the user says "as we discussed" or "remember when",
check your memory for context
- Build on previous conversations rather than starting fresh
- Note: Your sliding window shows the last 25 messages,
and semantic search finds relevant older conversations
8. Step 6 — Tool Integration¶
Give the copilot tools to interact with external systems.
8.1 HTTP API Tools¶
tools:
# Existing RAG tools...
- name: create_ticket
type: connector
description: "Create a support ticket in the ticketing system"
parameters:
type: object
properties:
title:
type: string
description: "Ticket title"
description:
type: string
description: "Detailed description of the issue"
priority:
type: string
enum:
- low
- medium
- high
- urgent
assignee:
type: string
description: "Team or person to assign to"
required:
- title
- description
config:
endpoint: "https://api.ticketing.example.com/tickets"
method: POST
headers:
Authorization: "Bearer ${env:TICKET_API_KEY}"
- name: lookup_user
type: connector
description: "Look up a user's account information by email or ID"
parameters:
type: object
properties:
email:
type: string
user_id:
type: string
config:
endpoint: "https://api.internal.example.com/users"
method: GET
headers:
Authorization: "Bearer ${env:INTERNAL_API_KEY}"
- name: check_system_status
type: connector
description: "Check the health status of a system or service"
parameters:
type: object
properties:
service:
type: string
description: "Service name (e.g., api, database, cache)"
config:
endpoint: "https://status.example.com/api/services"
method: GET
8.2 Function Tools (Custom Logic)¶
tools:
- name: calculate
type: function
description: "Perform mathematical calculations"
parameters:
type: object
properties:
expression:
type: string
description: "Math expression to evaluate (e.g., '42 * 1.15')"
required:
- expression
- name: format_date
type: function
description: "Convert dates between formats and timezones"
parameters:
type: object
properties:
date:
type: string
description: "Date string to convert"
from_format:
type: string
default: "RFC3339"
to_format:
type: string
default: "2006-01-02"
timezone:
type: string
default: "UTC"
9. Step 7 — MCP Tool Server Integration¶
Connect the copilot to external MCP tool servers for filesystem, database, and third-party access.
9.1 Consuming MCP Tools¶
tools:
# Import tools from external MCP servers
- name: filesystem_tools
type: mcp
description: "Read and search files on the server"
mcp_server:
url: "http://localhost:3001"
transport: http
headers:
Authorization: "Bearer ${env:MCP_FS_TOKEN}"
tools:
- read_file
- list_directory
- search_files
- name: database_tools
type: mcp
description: "Query the application database"
mcp_server:
url: "http://localhost:3002"
transport: http
headers:
Authorization: "Bearer ${env:MCP_DB_TOKEN}"
tools:
- query
- list_tables
- describe_table
- name: github_tools
type: mcp
description: "Interact with GitHub repos, issues, and PRs"
mcp_server:
command: "npx"
args:
- "@modelcontextprotocol/server-github"
transport: stdio
tools:
- search_code
- get_file_contents
- list_issues
- create_issue
9.2 Exposing the Copilot as an MCP Server¶
Allow Claude Desktop, Cursor, and other MCP clients to use the copilot:
helix:
mcp:
server:
enabled: true
transport: http
addr: "0.0.0.0:3000"
name: "my-copilot"
version: "1.0.0"
workflows:
- copilot-chat
tools:
copilot-chat:
description: "Ask the AI copilot any question about our products and docs"
input_schema: '{"type":"object","properties":{"query":{"type":"string","description":"Your question"}},"required":["query"]}'
Claude Desktop configuration (claude_desktop_config.json):
10. Step 8 — Multi-Agent Orchestration¶
Scale the copilot from a single agent to a coordinated team of specialists.
10.1 Supervisor with Specialist Agents¶
agents:
- id: copilot-chat
type: supervisor
trigger: agui://0.0.0.0:4100/api/chat?name=copilot&corsOrigin=*
model:
provider: anthropic
model: claude-sonnet-4-20250514
api_key: "${env:ANTHROPIC_API_KEY}"
temperature: 0.3
system_prompt: |
You are the copilot supervisor. Route user questions to the
most appropriate specialist agent:
- product_expert: Product features, pricing, capabilities, comparisons
- technical_support: Bugs, errors, configuration, troubleshooting
- api_developer: API usage, code examples, SDK integration, webhooks
- account_manager: Billing, subscriptions, account settings, team management
For questions spanning multiple domains, route to the primary domain
first. If the answer is incomplete, route to additional specialists.
memory:
enabled: true
type: composite
session_id_header: "X-Session-ID"
backends:
- type: sliding-window
window_size: 25
- type: sql-long-term
dsn: "${env:DATABASE_URL}"
session_id_header: "X-Session-ID"
sub_agents:
- id: product_expert
type: react
model:
provider: anthropic
model: claude-sonnet-4-20250514
api_key: "${env:ANTHROPIC_API_KEY}"
temperature: 0.4
system_prompt: |
You are a product expert. Search the product documentation to
answer questions about features, pricing, and capabilities.
Be specific, cite documentation, and suggest relevant features
the user might not know about.
tools:
- name: search_product
type: rag-query
description: "Search product documentation"
parameters:
type: object
properties:
query:
type: string
top_k:
type: integer
default: 5
config:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: product_docs
retriever: hybrid
alpha: 0.7
max_iterations: 5
- id: technical_support
type: react
model:
provider: openai
model: gpt-4o
api_key: "${env:OPENAI_API_KEY}"
temperature: 0.2
system_prompt: |
You are a technical support specialist. Diagnose issues
methodically. Search both documentation and past support
tickets. Provide step-by-step solutions. If the issue
requires escalation, recommend creating a support ticket.
tools:
- name: search_docs
type: rag-query
description: "Search technical documentation and troubleshooting guides"
parameters:
type: object
properties:
query:
type: string
top_k:
type: integer
default: 5
config:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: technical_docs
retriever: hybrid
alpha: 0.7
- name: search_tickets
type: rag-query
description: "Search past support tickets for similar issues"
parameters:
type: object
properties:
query:
type: string
top_k:
type: integer
default: 5
config:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: support_tickets
retriever: hybrid
alpha: 0.5
- name: create_ticket
type: connector
description: "Escalate by creating a support ticket"
parameters:
type: object
properties:
title:
type: string
description:
type: string
priority:
type: string
enum: [low, medium, high, urgent]
required: [title, description]
config:
endpoint: "https://api.ticketing.example.com/tickets"
method: POST
headers:
Authorization: "Bearer ${env:TICKET_API_KEY}"
max_iterations: 8
- id: api_developer
type: react
model:
provider: openai
model: gpt-4o
api_key: "${env:OPENAI_API_KEY}"
temperature: 0.3
system_prompt: |
You are an API developer assistant. Help with API integration,
code examples, SDK usage, and webhook configuration.
Always provide working code snippets in the user's language.
Search the API reference for accurate endpoint details.
tools:
- name: search_api
type: rag-query
description: "Search API documentation and code examples"
parameters:
type: object
properties:
query:
type: string
top_k:
type: integer
default: 8
config:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: api_docs
retriever: hybrid
alpha: 0.8
max_iterations: 6
- id: account_manager
type: react
model:
provider: anthropic
model: claude-sonnet-4-20250514
api_key: "${env:ANTHROPIC_API_KEY}"
temperature: 0.3
system_prompt: |
You are an account manager. Help with billing questions,
subscription changes, team management, and account settings.
Search policies for accurate information. For account changes,
gather the required information and guide the user through
the self-service portal.
tools:
- name: search_policies
type: rag-query
description: "Search billing and account policies"
parameters:
type: object
properties:
query:
type: string
config:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: account_policies
retriever: simple
- name: lookup_account
type: connector
description: "Look up account details"
parameters:
type: object
properties:
account_id:
type: string
config:
endpoint: "https://api.internal.example.com/accounts"
method: GET
headers:
Authorization: "Bearer ${env:INTERNAL_API_KEY}"
max_iterations: 5
10.2 Parallel Analysis Copilot¶
For copilots that need multi-perspective analysis:
- agent:
type: sequential
model:
provider: anthropic
model: claude-sonnet-4-20250514
api_key: "${env:ANTHROPIC_API_KEY}"
system_prompt: "Coordinate analysis and synthesis."
sub_agents:
# Phase 1: Parallel analysis from 3 perspectives
- id: parallel-analysis
type: parallel
model:
provider: anthropic
model: claude-sonnet-4-20250514
api_key: "${env:ANTHROPIC_API_KEY}"
system_prompt: "Analyze from multiple perspectives."
sub_agents:
- id: technical-view
type: react
model:
provider: openai
model: gpt-4o
api_key: "${env:OPENAI_API_KEY}"
temperature: 0.2
system_prompt: "Analyze from a technical feasibility perspective."
tools:
- name: search
type: rag-query
description: "Search technical docs"
parameters:
type: object
properties:
query: { type: string }
config:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: technical_docs
retriever: hybrid
alpha: 0.7
- id: business-view
type: react
model:
provider: anthropic
model: claude-sonnet-4-20250514
api_key: "${env:ANTHROPIC_API_KEY}"
temperature: 0.4
system_prompt: "Analyze from a business impact perspective."
tools:
- name: search
type: rag-query
description: "Search business docs"
parameters:
type: object
properties:
query: { type: string }
config:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: business_docs
retriever: hybrid
alpha: 0.6
- id: risk-view
type: react
model:
provider: anthropic
model: claude-sonnet-4-20250514
api_key: "${env:ANTHROPIC_API_KEY}"
temperature: 0.1
system_prompt: "Analyze risks, security concerns, and compliance issues."
tools:
- name: search
type: rag-query
description: "Search compliance docs"
parameters:
type: object
properties:
query: { type: string }
config:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: compliance_docs
retriever: simple
# Phase 2: Synthesize all perspectives
- id: synthesizer
type: react
model:
provider: anthropic
model: claude-sonnet-4-20250514
api_key: "${env:ANTHROPIC_API_KEY}"
temperature: 0.5
max_tokens: 3000
system_prompt: |
You receive three independent analyses (technical, business, risk).
Synthesize them into a unified recommendation:
1. Executive Summary (3 sentences)
2. Key Findings (from each perspective)
3. Points of Agreement
4. Points of Tension
5. Recommendation with Confidence Level
max_iterations: 2
11. Step 9 — Security and Authentication¶
Protect the copilot with authentication, rate limiting, and tenant isolation.
11.1 JWT Authentication¶
workflows:
- id: copilot-chat
trigger:
uri: agui://0.0.0.0:4100/api/chat?name=copilot&corsOrigin=*
security:
- type: jwt
jwks_url: "${env:JWKS_URL}"
issuer: "https://auth.example.com"
audience: "copilot-api"
steps:
# Rate limit per user
- throttle:
rate: 30
per: minute
key:
starlang: 'headers["auth.subject"]'
- agent:
# ... agent config ...
After JWT validation, all claims become headers:
- auth.subject — User ID
- auth.email — User email (if present)
- auth.role — User role
- auth.<any> — Any custom claim
11.2 API Key Authentication¶
trigger:
uri: http-server://0.0.0.0:8080/api/chat
security:
- type: apikey
header: "X-API-Key"
keys:
- "${env:API_KEY_1}"
- "${env:API_KEY_2}"
- "${env:API_KEY_3}"
11.3 OAuth2 Token Introspection¶
trigger:
uri: http-server://0.0.0.0:8080/api/chat
security:
- type: oauth2
token_url: "https://auth.example.com/token/introspect"
client_id: "copilot-service"
client_secret: "${env:OAUTH2_CLIENT_SECRET}"
scopes:
- "copilot:read"
- "copilot:write"
11.4 Multi-Tenant Copilot¶
Isolate knowledge and memory per tenant:
workflows:
- id: tenant-copilot
trigger:
uri: agui://0.0.0.0:4100/api/chat?name=copilot&corsOrigin=*
security:
- type: jwt
jwks_url: "${env:JWKS_URL}"
steps:
- throttle:
rate: 60
per: minute
key:
starlang: 'headers["auth.tenant_id"]'
- agent:
type: react
model:
provider: anthropic
model: claude-sonnet-4-20250514
api_key: "${env:ANTHROPIC_API_KEY}"
temperature: 0.4
system_prompt: |
You are a copilot. Only use information from this tenant's
knowledge base. Never reference other tenants' data.
memory:
enabled: true
type: sql-long-term
dsn: "${env:DATABASE_URL}"
session_id_header: "X-Session-ID"
tools:
- name: search
type: rag-query
description: "Search this tenant's knowledge base"
parameters:
type: object
properties:
query:
type: string
top_k:
type: integer
default: 5
config:
store: pgvector
connection: "${env:DATABASE_URL}"
# Dynamic collection per tenant
collection:
starlang: '"tenant_" ++ headers["auth.tenant_id"]'
retriever: hybrid
alpha: 0.7
max_iterations: 6
helix:
tenant:
enabled: true
header: "auth.tenant_id"
required: true
11.5 TLS / HTTPS¶
workflows:
- id: copilot-secure
trigger:
uri: http-server://0.0.0.0:8443/api/chat?tls=true&tlsCert=/etc/helix/tls/server.crt&tlsKey=/etc/helix/tls/server.key&tlsMinVersion=1.3
For mutual TLS (client certificate verification):
12. Step 10 — Multi-Protocol Exposure¶
Expose the copilot through all protocols simultaneously — chat frontends, Claude Desktop, other agents, and REST API.
agents:
# ── Core logic (shared by all protocols) ──
- id: copilot-core
type: supervisor
trigger: direct://copilot-core
model:
provider: anthropic
model: claude-sonnet-4-20250514
api_key: "${env:ANTHROPIC_API_KEY}"
temperature: 0.3
system_prompt: |
You are a helpful copilot. Route to the appropriate specialist.
memory:
enabled: true
type: composite
session_id_header: "X-Session-ID"
backends:
- type: sliding-window
window_size: 25
- type: vector
top_k: 5
embed:
provider: openai
model: text-embedding-3-small
api_key: "${env:OPENAI_API_KEY}"
- type: sql-long-term
dsn: "${env:DATABASE_URL}"
session_id_header: "X-Session-ID"
sub_agents:
- id: general
type: react
model:
provider: anthropic
model: claude-sonnet-4-20250514
api_key: "${env:ANTHROPIC_API_KEY}"
system_prompt: "Answer general questions using the knowledge base."
tools:
- name: search
type: rag-query
description: "Search knowledge base"
parameters:
type: object
properties:
query: { type: string }
config:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: copilot_knowledge
retriever: hybrid
alpha: 0.7
max_iterations: 6
- id: technical
type: react
model:
provider: openai
model: gpt-4o
api_key: "${env:OPENAI_API_KEY}"
system_prompt: "Help with technical issues and troubleshooting."
tools:
- name: search_docs
type: rag-query
description: "Search technical docs"
parameters:
type: object
properties:
query: { type: string }
config:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: technical_docs
retriever: hybrid
alpha: 0.7
- name: search_tickets
type: rag-query
description: "Search past support tickets"
parameters:
type: object
properties:
query: { type: string }
config:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: support_tickets
retriever: hybrid
alpha: 0.5
max_iterations: 8
workflows:
# ── AG-UI: Streaming chat for CopilotKit frontends ──
- id: copilot-agui
trigger:
uri: agui://0.0.0.0:4100/api/chat?name=copilot&corsOrigin=*
steps:
- target: direct://copilot-core
# ── A2UI: Rich declarative UI surfaces ──
- id: copilot-a2ui
trigger:
uri: a2ui://0.0.0.0:4200/api/surface?name=copilot-ui&corsOrigin=*
steps:
- target: direct://copilot-core
# ── A2A: Agent-to-agent (other AI agents call this copilot) ──
- id: copilot-a2a
trigger:
uri: a2a-server://0.0.0.0:9000?name=company-copilot&description=AI+copilot+for+company+knowledge&version=1.0.0
steps:
- target: direct://copilot-core
# ── REST: Traditional HTTP API for scripts and mobile apps ──
- id: copilot-rest
trigger:
uri: http-server://0.0.0.0:8080/api/chat
security:
- type: jwt
jwks_url: "${env:JWKS_URL}"
steps:
- target: direct://copilot-core
# ── MCP: Expose copilot as tools for Claude Desktop ──
# (Configured via helix.mcp.server, not as a workflow)
helix:
mcp:
server:
enabled: true
transport: http
addr: "0.0.0.0:3000"
name: "company-copilot"
version: "1.0.0"
workflows:
- copilot-core
tools:
copilot-core:
description: "Ask the company AI copilot any question"
input_schema: '{"type":"object","properties":{"query":{"type":"string"}},"required":["query"]}'
a2a:
servers:
- enabled: true
addr: "0.0.0.0:9000"
name: "company-copilot"
description: "AI copilot with access to company knowledge base, support history, and account management"
version: "1.0.0"
agui:
servers:
- enabled: true
addr: "0.0.0.0:4100"
path: "/api/chat"
name: "copilot"
a2ui:
servers:
- enabled: true
addr: "0.0.0.0:4200"
path: "/api/surface"
name: "copilot-ui"
13. Step 11 — Observability and Quality Monitoring¶
Monitor copilot performance, response quality, and usage patterns.
13.1 RAG Quality Evaluation¶
Evaluate every RAG-augmented response:
# Add after the agent step in copilot-core
- id: copilot-core
trigger:
uri: direct://copilot-core
steps:
- agent:
# ... supervisor config ...
# Evaluate response quality
- rag-eval:
metrics:
- faithfulness
- answer_relevancy
model:
provider: openai
model: gpt-4o-mini
api_key: "${env:OPENAI_API_KEY}"
# Non-blocking: send metrics to monitoring pipeline
- wiretap: seda://copilot-metrics
# Tag response with quality score
- set-header:
name: X-Copilot-Quality
starlang: |
faith = float(properties.get("rag_faithfulness", "1"))
if faith >= 0.7:
"high"
elif faith >= 0.4:
"medium"
else:
"low"
13.2 Metrics Collection and Alerting¶
workflows:
# Metrics processor (non-blocking via SEDA)
- id: copilot-metrics-processor
trigger:
uri: seda://copilot-metrics?concurrency=4
steps:
# Log for Prometheus scraping
- target: log://copilot-quality?level=info
# Alert on low quality
- choice:
when:
- starlang: |
faith = float(properties.get("rag_faithfulness", "1"))
faith < 0.3
steps:
- set-body:
starlang: |
write({
"channel": "copilot-alerts",
"text": "Low quality response detected (faithfulness: " ++
properties.get("rag_faithfulness", "N/A") ++ ")"
}, "application/json")
- target: slack://alerts?webhook=${env:SLACK_WEBHOOK}
# Daily quality digest
- id: copilot-daily-digest
trigger:
uri: cron://daily?schedule=0 9 * * 1-5
steps:
- agent:
type: react
model:
provider: openai
model: gpt-4o-mini
api_key: "${env:OPENAI_API_KEY}"
temperature: 0.1
system_prompt: |
Generate a daily copilot quality report:
1. Total queries (24h)
2. Average quality scores
3. Quality trend
4. Top failed queries
5. Improvement recommendations
tools:
- name: query_metrics
type: connector
description: "Query Prometheus metrics"
parameters:
type: object
properties:
query: { type: string }
config:
endpoint: "http://prometheus:9090/api/v1/query"
method: GET
max_iterations: 3
- target: slack://copilot-ops?channel=daily-report&webhook=${env:SLACK_WEBHOOK}
13.3 Observability Configuration¶
helix:
metrics:
enabled: true
addr: "0.0.0.0:9090"
path: "/metrics"
tracing:
enabled: true
exporter: otlp
endpoint: "http://jaeger:4318"
sample_rate: 0.1
audit:
enabled: true
buffer_size: 10000
logging:
level: info
format: json
14. Step 12 — Production Deployment¶
Final production configuration with graceful shutdown, clustering, and resilience.
helix:
shutdown:
timeout: 30s
drain_timeout: 15s
cluster:
enabled: true
backend: redis
redis_addr: "${env:REDIS_URL}"
node_name: "${env:HOSTNAME}"
tenant:
enabled: true
header: "auth.tenant_id"
required: true
audit:
enabled: true
buffer_size: 10000
logging:
level: info
format: json
components:
agent: debug
rag: info
Dockerfile¶
FROM golang:1.25-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o helix ./cmd/helix
FROM alpine:3.21
RUN apk add --no-cache ca-certificates
COPY --from=builder /app/helix /usr/local/bin/helix
COPY copilot.yaml /etc/helix/copilot.yaml
COPY knowledge-base/ /data/knowledge-base/
EXPOSE 4100 4200 3000 8080 9000 9090
ENTRYPOINT ["helix", "run", "/etc/helix/copilot.yaml"]
Docker Compose¶
version: "3.9"
services:
copilot:
build: .
ports:
- "4100:4100" # AG-UI (chat frontend)
- "4200:4200" # A2UI (rich UI)
- "3000:3000" # MCP (Claude Desktop)
- "8080:8080" # REST API
- "9000:9000" # A2A (agent-to-agent)
- "9090:9090" # Prometheus metrics
environment:
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
- OPENAI_API_KEY=${OPENAI_API_KEY}
- DATABASE_URL=postgres://helix:helix@postgres:5432/copilot
- REDIS_URL=redis:6379
- JWKS_URL=https://auth.example.com/.well-known/jwks.json
depends_on:
- postgres
- redis
postgres:
image: pgvector/pgvector:pg17
environment:
POSTGRES_USER: helix
POSTGRES_PASSWORD: helix
POSTGRES_DB: copilot
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
prometheus:
image: prom/prometheus
volumes:
- ./deploy/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9091:9090"
grafana:
image: grafana/grafana
volumes:
- ./deploy/grafana/dashboards:/var/lib/grafana/dashboards
ports:
- "3001:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
pgdata:
15. Complete Copilot — Full YAML Reference¶
A single-file copilot configuration combining all capabilities:
# ================================================================
# Complete AI Copilot Configuration
# ================================================================
# Features:
# - AG-UI streaming chat (CopilotKit compatible)
# - A2UI rich declarative UIs
# - MCP tool server (Claude Desktop, Cursor)
# - A2A agent-to-agent protocol
# - REST API endpoint
# - RAG knowledge base with nightly + incremental ingestion
# - Composite memory (sliding window + vector + SQL persistent)
# - Multi-agent supervisor with 4 specialists
# - JWT authentication + rate limiting
# - Quality evaluation + Slack alerting
# - Prometheus metrics + audit logging
# ================================================================
workflows:
# ── PROTOCOL ENDPOINTS ──────────────────────────────────────
# AG-UI: Streaming chat for frontends
- id: copilot-agui
trigger:
uri: agui://0.0.0.0:4100/api/chat?name=copilot&corsOrigin=*
steps:
- target: direct://copilot-core
# A2UI: Rich UI surfaces
- id: copilot-a2ui
trigger:
uri: a2ui://0.0.0.0:4200/api/surface?name=copilot-ui&corsOrigin=*
steps:
- target: direct://copilot-core
# A2A: Other agents can call this copilot
- id: copilot-a2a
trigger:
uri: a2a-server://0.0.0.0:9000?name=company-copilot&version=1.0.0
steps:
- target: direct://copilot-core
# REST: HTTP API for scripts, mobile, webhooks
- id: copilot-rest
trigger:
uri: http-server://0.0.0.0:8080/api/chat
security:
- type: jwt
jwks_url: "${env:JWKS_URL}"
issuer: "https://auth.example.com"
audience: "copilot-api"
steps:
- throttle:
rate: 30
per: minute
key:
starlang: 'headers["auth.subject"]'
- target: direct://copilot-core
# ── CORE COPILOT LOGIC ──────────────────────────────────────
- id: copilot-core
trigger:
uri: direct://copilot-core
steps:
- agent:
type: supervisor
model:
provider: anthropic
model: claude-sonnet-4-20250514
api_key: "${env:ANTHROPIC_API_KEY}"
temperature: 0.3
system_prompt: |
You are the copilot supervisor for [Company Name].
Route questions to the appropriate specialist:
- general_assistant: General questions, product info, how-to guides
- technical_support: Bugs, errors, configuration, troubleshooting
- api_developer: API usage, code examples, SDK integration
- account_manager: Billing, subscriptions, account settings
For cross-domain questions, start with the primary domain.
memory:
enabled: true
type: composite
session_id_header: "X-Session-ID"
backends:
- type: sliding-window
window_size: 25
- type: vector
top_k: 5
embed:
provider: openai
model: text-embedding-3-small
api_key: "${env:OPENAI_API_KEY}"
- type: sql-long-term
dsn: "${env:DATABASE_URL}"
session_id_header: "X-Session-ID"
sub_agents:
- id: general_assistant
type: react
model:
provider: anthropic
model: claude-sonnet-4-20250514
api_key: "${env:ANTHROPIC_API_KEY}"
temperature: 0.4
max_tokens: 2048
system_prompt: |
You are a helpful assistant. Search the knowledge base to answer
questions. Cite sources. If unsure, say so honestly.
tools:
- name: search_knowledge
type: rag-query
description: "Search the company knowledge base"
parameters:
type: object
properties:
query:
type: string
top_k:
type: integer
default: 5
required: [query]
config:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: copilot_knowledge
retriever: hybrid
alpha: 0.7
- name: search_diverse
type: rag-query
description: "Search for diverse perspectives"
parameters:
type: object
properties:
query:
type: string
config:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: copilot_knowledge
retriever: mmr
lambda: 0.3
fetch_k: 25
max_iterations: 6
- id: technical_support
type: react
model:
provider: openai
model: gpt-4o
api_key: "${env:OPENAI_API_KEY}"
temperature: 0.2
system_prompt: |
You are technical support. Diagnose issues methodically.
Search docs and past tickets. Provide step-by-step solutions.
tools:
- name: search_docs
type: rag-query
description: "Search technical documentation"
parameters:
type: object
properties:
query: { type: string }
top_k: { type: integer, default: 5 }
config:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: technical_docs
retriever: hybrid
alpha: 0.7
- name: search_tickets
type: rag-query
description: "Search past support tickets"
parameters:
type: object
properties:
query: { type: string }
config:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: support_tickets
retriever: hybrid
alpha: 0.5
- name: create_ticket
type: connector
description: "Escalate by creating a support ticket"
parameters:
type: object
properties:
title: { type: string }
description: { type: string }
priority: { type: string, enum: [low, medium, high, urgent] }
required: [title, description]
config:
endpoint: "https://api.ticketing.example.com/tickets"
method: POST
headers:
Authorization: "Bearer ${env:TICKET_API_KEY}"
- name: github_tools
type: mcp
description: "Search code and manage issues on GitHub"
mcp_server:
command: "npx"
args: ["@modelcontextprotocol/server-github"]
transport: stdio
tools: [search_code, get_file_contents, list_issues, create_issue]
max_iterations: 8
- id: api_developer
type: react
model:
provider: openai
model: gpt-4o
api_key: "${env:OPENAI_API_KEY}"
temperature: 0.3
system_prompt: |
You are an API developer assistant. Provide working code
snippets, accurate endpoint details, and SDK integration guides.
tools:
- name: search_api
type: rag-query
description: "Search API documentation and examples"
parameters:
type: object
properties:
query: { type: string }
top_k: { type: integer, default: 8 }
config:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: api_docs
retriever: hybrid
alpha: 0.8
max_iterations: 6
- id: account_manager
type: react
model:
provider: anthropic
model: claude-sonnet-4-20250514
api_key: "${env:ANTHROPIC_API_KEY}"
temperature: 0.3
system_prompt: |
You are an account manager. Help with billing, subscriptions,
and account settings. Search policies for accurate info.
tools:
- name: search_policies
type: rag-query
description: "Search account and billing policies"
parameters:
type: object
properties:
query: { type: string }
config:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: account_policies
retriever: simple
- name: lookup_account
type: connector
description: "Look up account details"
parameters:
type: object
properties:
account_id: { type: string }
config:
endpoint: "https://api.internal.example.com/accounts"
method: GET
headers:
Authorization: "Bearer ${env:INTERNAL_API_KEY}"
max_iterations: 5
# Quality evaluation
- rag-eval:
metrics:
- faithfulness
- answer_relevancy
model:
provider: openai
model: gpt-4o-mini
api_key: "${env:OPENAI_API_KEY}"
# Non-blocking metrics
- wiretap: seda://copilot-metrics
# ── RAG INGESTION ────────────────────────────────────────────
# Nightly full reindex
- id: copilot-ingest-full
trigger:
uri: cron://nightly?schedule=0 2 * * *
steps:
- rag-ingest:
reader: directory
path: ./knowledge-base
recursive: true
extensions: [".md", ".pdf", ".html", ".txt", ".docx"]
- rag-chunk:
strategy: semantic
threshold: 0.45
chunk_size: 1000
overlap: 150
- rag-embed:
provider: openai
model: text-embedding-3-small
api_key: "${env:OPENAI_API_KEY}"
- rag-store:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: copilot_knowledge
dimensions: 1536
- rag-extract:
extractors: [title, keywords, summary]
model:
provider: openai
model: gpt-4o-mini
api_key: "${env:OPENAI_API_KEY}"
- target: log://ingestion?level=info
# Hourly incremental updates
- id: copilot-ingest-incremental
trigger:
uri: cron://hourly?schedule=0 * * * *
steps:
- rag-ingest:
reader: directory
path: ./knowledge-base
recursive: true
- filter:
starlang: |
import "time"
modified = headers["HelixFileLastModified"]
modified > time.now() - time.hour
- rag-chunk:
strategy: semantic
threshold: 0.45
chunk_size: 1000
overlap: 150
- rag-embed:
provider: openai
model: text-embedding-3-small
api_key: "${env:OPENAI_API_KEY}"
- rag-store:
store: pgvector
connection: "${env:DATABASE_URL}"
collection: copilot_knowledge
# ── MONITORING ───────────────────────────────────────────────
- id: copilot-metrics-processor
trigger:
uri: seda://copilot-metrics?concurrency=4
steps:
- target: log://copilot-quality?level=info
- choice:
when:
- starlang: |
faith = float(properties.get("rag_faithfulness", "1"))
faith < 0.3
steps:
- set-body:
starlang: |
write({
"text": "Low quality copilot response (faithfulness: " ++
properties.get("rag_faithfulness", "N/A") ++ ")"
}, "application/json")
- target: slack://alerts?webhook=${env:SLACK_WEBHOOK}
- id: copilot-daily-digest
trigger:
uri: cron://daily?schedule=0 9 * * 1-5
steps:
- agent:
type: react
model:
provider: openai
model: gpt-4o-mini
api_key: "${env:OPENAI_API_KEY}"
temperature: 0.1
system_prompt: "Generate a daily copilot quality report."
tools:
- name: query_metrics
type: connector
description: "Query Prometheus"
parameters:
type: object
properties:
query: { type: string }
config:
endpoint: "http://prometheus:9090/api/v1/query"
method: GET
max_iterations: 3
- target: slack://copilot-ops?webhook=${env:SLACK_WEBHOOK}
# ── GLOBAL CONFIGURATION ──────────────────────────────────────
helix:
mcp:
server:
enabled: true
transport: http
addr: "0.0.0.0:3000"
name: "company-copilot"
version: "1.0.0"
workflows: [copilot-core]
tools:
copilot-core:
description: "Ask the AI copilot any question"
input_schema: '{"type":"object","properties":{"query":{"type":"string"}},"required":["query"]}'
a2a:
servers:
- enabled: true
addr: "0.0.0.0:9000"
name: "company-copilot"
description: "AI copilot with company knowledge, support, and account management"
version: "1.0.0"
agui:
servers:
- enabled: true
addr: "0.0.0.0:4100"
path: "/api/chat"
name: "copilot"
a2ui:
servers:
- enabled: true
addr: "0.0.0.0:4200"
path: "/api/surface"
name: "copilot-ui"
shutdown:
timeout: 30s
drain_timeout: 15s
audit:
enabled: true
buffer_size: 10000
logging:
level: info
format: json
metrics:
enabled: true
addr: "0.0.0.0:9090"
path: "/metrics"
16. Copilot Patterns¶
16.1 Customer Support Copilot¶
| Feature | Configuration |
|---|---|
| Agent type | Supervisor with 4 specialists (product, technical, billing, escalation) |
| RAG collections | Product docs, technical docs, support tickets, FAQs |
| Memory | Composite (sliding window + SQL persistent) |
| Tools | RAG search, ticket creation, account lookup |
| Protocols | AG-UI (chat widget), REST API (mobile), MCP (internal) |
16.2 Developer Copilot¶
| Feature | Configuration |
|---|---|
| Agent type | ReAct with code-focused tools |
| RAG collections | API docs (code chunking), examples, changelogs |
| Memory | Composite (sliding window + vector semantic) |
| Tools | RAG search, GitHub MCP (code search, issues), code execution |
| Protocols | AG-UI (IDE panel), MCP (Cursor/VS Code) |
16.3 Enterprise Knowledge Copilot¶
| Feature | Configuration |
|---|---|
| Agent type | Supervisor with domain experts |
| RAG collections | Per-department knowledge bases (engineering, legal, HR, finance) |
| Memory | Composite (sliding window + vector + SQL) with per-tenant isolation |
| Tools | RAG search (4 collections), document upload, Slack notifications |
| Security | JWT + multi-tenant + rate limiting |
| Protocols | AG-UI, A2A (other agents query this), MCP, REST |
16.4 Research Copilot¶
| Feature | Configuration |
|---|---|
| Agent type | Plan-Execute or Deep (for complex research) |
| RAG collections | Research corpus with semantic chunking |
| Memory | Composite (sliding window + vector for cross-session recall) |
| Tools | RAG search (hybrid + MMR + HyDE), web search MCP |
| Quality | RAG evaluation on every response, self-reflective loop |
| Protocols | AG-UI (research dashboard), REST API |
16.5 Internal Operations Copilot¶
| Feature | Configuration |
|---|---|
| Agent type | Supervisor with ops specialists |
| RAG collections | Runbooks, incident history, architecture docs |
| Memory | SQL persistent (audit trail requirement) |
| Tools | RAG search, MCP (filesystem, database, Kubernetes), HTTP (PagerDuty, Jira) |
| Security | JWT + mTLS + audit logging |
| Protocols | AG-UI (ops dashboard), A2A (incident bots call this), MCP |
17. Frontend Integration Guide¶
17.1 CopilotKit (React)¶
import { CopilotKit } from "@copilotkit/react-core";
import { CopilotChat } from "@copilotkit/react-ui";
import "@copilotkit/react-ui/styles.css";
function App() {
return (
<CopilotKit
runtimeUrl="http://localhost:4100/api/chat"
headers={{
"X-Session-ID": getSessionId(),
"Authorization": `Bearer ${getToken()}`
}}
>
<YourApp />
<CopilotChat
labels={{
title: "AI Copilot",
initial: "Hi! How can I help you today?"
}}
/>
</CopilotKit>
);
}
17.2 Custom SSE Client (Vanilla JS)¶
async function sendMessage(message, sessionId) {
const response = await fetch("http://localhost:4100/api/chat", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Session-ID": sessionId,
},
body: JSON.stringify({
threadId: sessionId,
runId: crypto.randomUUID(),
messages: [{ role: "user", content: message }],
}),
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n");
buffer = lines.pop();
for (const line of lines) {
if (line.startsWith("data: ")) {
const event = JSON.parse(line.slice(6));
switch (event.type) {
case "TEXT_MESSAGE_CONTENT":
appendToChat(event.delta);
break;
case "RUN_FINISHED":
finishMessage();
break;
case "RUN_ERROR":
showError(event.message);
break;
}
}
}
}
}
17.3 A2UI Surface Client¶
function connectSurface(message) {
const url = `http://localhost:4200/api/surface?message=${encodeURIComponent(message)}&surface=main`;
const eventSource = new EventSource(url);
eventSource.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.beginRendering) {
initSurface(msg.beginRendering.surfaceId);
}
if (msg.updateComponents) {
renderComponents(msg.updateComponents.components);
}
if (msg.dataModelUpdate) {
updateData(msg.dataModelUpdate.updates);
}
};
eventSource.onerror = () => eventSource.close();
}
// Send user action back
async function sendAction(componentId, data) {
await fetch("http://localhost:4200/api/surface", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
event: {
surfaceId: "main",
componentId,
type: "action",
data,
},
}),
});
}
17.4 Claude Desktop (MCP)¶
Add to claude_desktop_config.json:
Claude Desktop will automatically discover and list the copilot as an available tool.
17.5 Python Client (A2A)¶
import requests
def ask_copilot(message: str) -> str:
"""Call the copilot via A2A protocol."""
response = requests.post(
"http://localhost:9000/invoke",
json={
"jsonrpc": "2.0",
"method": "message/send",
"params": {
"message": {
"role": "user",
"parts": [{"text": message}]
}
},
"id": "1"
}
)
result = response.json()
# Extract text from response
return result["result"]["parts"][0]["text"]
This guide covers building a complete AI copilot from scratch to production deployment. For more patterns, see the Use Cases & Examples. For component reference, see Components. For agent internals, see AI Agents Guide. For RAG details, see RAG Guide.