e071084ebe
govulncheck / govulncheck (push) Has been cancelled
Lint / golangci-lint (push) Has been cancelled
Run Tests / Unit Tests (push) Has been cancelled
Run Tests / Etcd Integration Tests (push) Has been cancelled
Harness (E2E) / Harnesses (mock LLM) (push) Has been cancelled
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Has been cancelled
478 lines
14 KiB
Markdown
478 lines
14 KiB
Markdown
---
|
|
layout: default
|
|
---
|
|
|
|
# Agent Integration Patterns
|
|
|
|
This guide covers common patterns for integrating AI agents with Go Micro services, from single-agent workflows to multi-agent architectures.
|
|
|
|
## Pattern 1: Single Agent with Multiple Services
|
|
|
|
The simplest and most common pattern. One AI agent has access to multiple microservices as MCP tools.
|
|
|
|
```
|
|
User → AI Agent → MCP Gateway → [Service A, Service B, Service C]
|
|
```
|
|
|
|
### Setup
|
|
|
|
Run multiple services and expose them all through one MCP gateway:
|
|
|
|
```go
|
|
users := micro.NewService("users", micro.Address(":8081"))
|
|
tasks := micro.NewService("tasks", micro.Address(":8082"))
|
|
notifications := micro.NewService("notifications", micro.Address(":8083"))
|
|
|
|
// Run all together as a modular monolith
|
|
g := micro.NewGroup(users, tasks, notifications)
|
|
g.Run()
|
|
```
|
|
|
|
With `micro run`, all services are discovered automatically via the registry, and the MCP tools endpoint at `/mcp/tools` exposes every endpoint from every service.
|
|
|
|
### When to Use
|
|
|
|
- Most applications start here
|
|
- Agent needs to orchestrate across services (e.g., "create a task and notify the assignee")
|
|
- You want the agent to choose which service to call based on the user's request
|
|
|
|
## Pattern 2: Scoped Agents
|
|
|
|
Different agents have access to different subsets of tools via scopes.
|
|
|
|
```
|
|
Customer Agent → MCP Gateway → [orders:read, support:write]
|
|
Internal Agent → MCP Gateway → [orders:*, users:*, billing:*]
|
|
Admin Agent → MCP Gateway → [*]
|
|
```
|
|
|
|
### Setup
|
|
|
|
Create tokens with different scopes for each agent:
|
|
|
|
```go
|
|
// Gateway with scope enforcement
|
|
mcp.ListenAndServe(":3000", mcp.Options{
|
|
Registry: reg,
|
|
Auth: authProvider,
|
|
Scopes: map[string][]string{
|
|
"billing.Billing.Charge": {"billing:admin"},
|
|
"users.Users.Delete": {"users:admin"},
|
|
"orders.Orders.List": {"orders:read"},
|
|
"orders.Orders.Create": {"orders:write"},
|
|
"support.Support.CreateTicket": {"support:write"},
|
|
},
|
|
})
|
|
```
|
|
|
|
Then issue different tokens:
|
|
- Customer-facing agent token: `scopes=["orders:read", "support:write"]`
|
|
- Internal agent token: `scopes=["orders:read", "orders:write", "users:read"]`
|
|
- Admin agent token: `scopes=["*"]`
|
|
|
|
### When to Use
|
|
|
|
- Different trust levels for different agents
|
|
- Customer-facing vs internal agents
|
|
- Compliance requirements (e.g., PCI, HIPAA)
|
|
|
|
## Pattern 3: Agent as Service Consumer
|
|
|
|
Your Go Micro service itself calls an AI model to process data, using the `ai` package.
|
|
|
|
```
|
|
User → API → Your Service → AI Model (Claude/GPT)
|
|
→ Other Services
|
|
```
|
|
|
|
### Setup
|
|
|
|
```go
|
|
import (
|
|
"go-micro.dev/v6/ai"
|
|
_ "go-micro.dev/v6/ai/anthropic"
|
|
)
|
|
|
|
type SummaryService struct {
|
|
ai ai.Model
|
|
tasks *TaskClient
|
|
}
|
|
|
|
func NewSummaryService() *SummaryService {
|
|
return &SummaryService{
|
|
ai: ai.New("anthropic",
|
|
ai.WithAPIKey(os.Getenv("ANTHROPIC_API_KEY")),
|
|
ai.WithModel("claude-sonnet-4-20250514"),
|
|
),
|
|
}
|
|
}
|
|
|
|
// Summarize generates an AI summary of a project's tasks.
|
|
// Returns a natural language summary of task status, blockers, and progress.
|
|
//
|
|
// @example {"project_id": "proj-1"}
|
|
func (s *SummaryService) Summarize(ctx context.Context, req *SummarizeRequest, rsp *SummarizeResponse) error {
|
|
// Fetch tasks from another service
|
|
tasks, err := s.tasks.List(ctx, req.ProjectID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Use AI to summarize
|
|
resp, err := s.ai.Generate(ctx, &ai.Request{
|
|
Prompt: fmt.Sprintf("Summarize these tasks:\n%s", formatTasks(tasks)),
|
|
SystemPrompt: "You are a concise project manager. Summarize task status in 2-3 sentences.",
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
rsp.Summary = resp.Reply
|
|
return nil
|
|
}
|
|
```
|
|
|
|
### When to Use
|
|
|
|
- Your service needs to process natural language
|
|
- Generating summaries, classifications, or extractions
|
|
- Enriching data with AI before returning to the caller
|
|
|
|
## Pattern 4: Agent with Tool Calling
|
|
|
|
An AI model calls your services as tools, with automatic tool execution via the ai package.
|
|
|
|
```
|
|
User → Your App → AI Model ←→ MCP Tools (your services)
|
|
```
|
|
|
|
### Setup
|
|
|
|
```go
|
|
import (
|
|
"go-micro.dev/v6/ai"
|
|
_ "go-micro.dev/v6/ai/anthropic"
|
|
)
|
|
|
|
// Define tools from your service endpoints
|
|
tools := []ai.Tool{
|
|
{
|
|
Name: "create_task",
|
|
Description: "Create a new task with title and assignee",
|
|
Properties: map[string]any{
|
|
"title": map[string]any{"type": "string", "description": "Task title"},
|
|
"assignee": map[string]any{"type": "string", "description": "Username"},
|
|
},
|
|
},
|
|
{
|
|
Name: "list_tasks",
|
|
Description: "List tasks filtered by status",
|
|
Properties: map[string]any{
|
|
"status": map[string]any{"type": "string", "description": "Filter: todo, in_progress, done"},
|
|
},
|
|
},
|
|
}
|
|
|
|
// Handle tool calls by routing to your services. The handler mirrors a
|
|
// go-micro RPC handler: context first, the call in, a result out.
|
|
toolHandler := func(ctx context.Context, call ai.ToolCall) ai.ToolResult {
|
|
switch call.Name {
|
|
case "create_task":
|
|
var rsp CreateResponse
|
|
err := client.Call(ctx, "tasks", "TaskService.Create", call.Input, &rsp)
|
|
if err != nil {
|
|
return ai.ToolResult{ID: call.ID, Content: fmt.Sprintf(`{"error": "%s"}`, err)}
|
|
}
|
|
b, _ := json.Marshal(rsp)
|
|
return ai.ToolResult{ID: call.ID, Value: rsp, Content: string(b)}
|
|
case "list_tasks":
|
|
var rsp ListResponse
|
|
err := client.Call(ctx, "tasks", "TaskService.List", call.Input, &rsp)
|
|
if err != nil {
|
|
return ai.ToolResult{ID: call.ID, Content: fmt.Sprintf(`{"error": "%s"}`, err)}
|
|
}
|
|
b, _ := json.Marshal(rsp)
|
|
return ai.ToolResult{ID: call.ID, Value: rsp, Content: string(b)}
|
|
}
|
|
return ai.ToolResult{ID: call.ID, Content: `{"error": "unknown tool"}`}
|
|
}
|
|
|
|
m := ai.New("anthropic",
|
|
ai.WithAPIKey(os.Getenv("ANTHROPIC_API_KEY")),
|
|
ai.WithToolHandler(toolHandler),
|
|
)
|
|
|
|
// The model will automatically call tools and return the final answer
|
|
resp, err := m.Generate(ctx, &ai.Request{
|
|
Prompt: "Create a task for Alice to review the PR and tell me what tasks she has",
|
|
SystemPrompt: "You are a helpful project management assistant",
|
|
Tools: tools,
|
|
})
|
|
|
|
fmt.Println(resp.Answer)
|
|
// "I've created a task for Alice to review the PR. She now has 3 tasks: ..."
|
|
```
|
|
|
|
### When to Use
|
|
|
|
- Building a chatbot or assistant that manages your services
|
|
- The agent playground in `micro run` uses this pattern
|
|
- You want the AI to decide which tools to call and in what order
|
|
|
|
## Pattern 5: Event-Driven Agent Triggers
|
|
|
|
Services emit events that trigger agent actions via the broker.
|
|
|
|
```
|
|
Service → Broker Event → Agent Handler → AI Model → Action
|
|
```
|
|
|
|
### Setup
|
|
|
|
```go
|
|
// Publisher: emit events from your service
|
|
broker.Publish("tasks.created", &broker.Message{
|
|
Body: taskJSON,
|
|
})
|
|
|
|
// Subscriber: agent handler reacts to events
|
|
broker.Subscribe("tasks.created", func(p broker.Event) error {
|
|
var task Task
|
|
json.Unmarshal(p.Message().Body, &task)
|
|
|
|
// Use AI to auto-assign based on task content
|
|
resp, err := aiModel.Generate(ctx, &ai.Request{
|
|
Prompt: fmt.Sprintf("Who should handle this task? Title: %s, Description: %s. Team: alice (frontend), bob (backend), charlie (devops)", task.Title, task.Description),
|
|
SystemPrompt: "Reply with just the username of the best person to handle this task.",
|
|
})
|
|
|
|
// Auto-assign
|
|
client.Call(ctx, "tasks", "TaskService.Update", map[string]any{
|
|
"id": task.ID,
|
|
"assignee": strings.TrimSpace(resp.Reply),
|
|
}, nil)
|
|
|
|
return nil
|
|
})
|
|
```
|
|
|
|
### When to Use
|
|
|
|
- Automated workflows triggered by service events
|
|
- AI-powered routing, classification, or triage
|
|
- Background processing without user interaction
|
|
|
|
## Pattern 6: Claude Code Integration
|
|
|
|
Developers use Claude Code with your services as MCP tools for local development workflows.
|
|
|
|
```
|
|
Developer → Claude Code → stdio MCP → [local services]
|
|
```
|
|
|
|
### Setup
|
|
|
|
```bash
|
|
# Start services locally
|
|
micro run
|
|
|
|
# In another terminal, use Claude Code with your services
|
|
# Claude Code config (~/.claude/claude_desktop_config.json):
|
|
```
|
|
|
|
```json
|
|
{
|
|
"mcpServers": {
|
|
"my-project": {
|
|
"command": "micro",
|
|
"args": ["mcp", "serve"]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Now in Claude Code:
|
|
|
|
```
|
|
"List all tasks that are blocked"
|
|
"Create a user account for the new hire"
|
|
"Check the health of all services"
|
|
```
|
|
|
|
### When to Use
|
|
|
|
- Developer productivity workflows
|
|
- Managing services during development
|
|
- Testing and debugging with natural language
|
|
|
|
## Pattern 7: LangChain / LlamaIndex Integration
|
|
|
|
Use the official Python SDKs to connect agent frameworks directly to your services.
|
|
|
|
### LangChain
|
|
|
|
```python
|
|
from langchain_go_micro import GoMicroToolkit
|
|
|
|
# Connect to MCP gateway
|
|
toolkit = GoMicroToolkit(
|
|
base_url="http://localhost:3000",
|
|
token="Bearer <token>",
|
|
)
|
|
|
|
# Get LangChain tools automatically
|
|
tools = toolkit.get_tools()
|
|
|
|
# Use with any LangChain agent
|
|
from langchain.agents import AgentExecutor, create_tool_calling_agent
|
|
agent = create_tool_calling_agent(llm, tools, prompt)
|
|
executor = AgentExecutor(agent=agent, tools=tools)
|
|
executor.invoke({"input": "Create a task for Alice"})
|
|
```
|
|
|
|
### LlamaIndex
|
|
|
|
```python
|
|
from go_micro_llamaindex import GoMicroToolkit
|
|
|
|
toolkit = GoMicroToolkit(
|
|
base_url="http://localhost:3000",
|
|
token="Bearer <token>",
|
|
)
|
|
|
|
# Use as LlamaIndex tools
|
|
tools = toolkit.to_tool_list()
|
|
|
|
# Use with a LlamaIndex agent
|
|
from llama_index.core.agent import ReActAgent
|
|
agent = ReActAgent.from_tools(tools, llm=llm)
|
|
agent.chat("What tasks are assigned to Bob?")
|
|
```
|
|
|
|
### When to Use
|
|
|
|
- Python-based agent pipelines
|
|
- RAG (Retrieval-Augmented Generation) workflows with LlamaIndex
|
|
- Multi-step LangChain chains that orchestrate your services
|
|
- Teams that prefer Python for AI/ML work
|
|
|
|
## Pattern 8: Standalone Gateway for Production
|
|
|
|
Run the MCP gateway as a separate, horizontally scalable process.
|
|
|
|
```
|
|
┌──────────────────┐
|
|
Claude/GPT/Agent ──→│ micro-mcp-gateway │──→ Service A (consul)
|
|
│ (standalone) │──→ Service B (consul)
|
|
└──────────────────┘──→ Service C (consul)
|
|
```
|
|
|
|
### Setup
|
|
|
|
```bash
|
|
micro-mcp-gateway \
|
|
--registry consul \
|
|
--registry-address consul:8500 \
|
|
--address :3000 \
|
|
--auth jwt \
|
|
--rate-limit 10 \
|
|
--rate-burst 20 \
|
|
--audit
|
|
```
|
|
|
|
Or via Docker:
|
|
|
|
```bash
|
|
docker run -p 3000:3000 ghcr.io/micro/micro-mcp-gateway \
|
|
--registry consul \
|
|
--registry-address consul:8500
|
|
```
|
|
|
|
### When to Use
|
|
|
|
- Production deployments where you want the gateway to scale independently
|
|
- Multiple teams deploying services but sharing one MCP endpoint
|
|
- Enterprise environments needing centralized auth and audit
|
|
|
|
## Pattern 9: Planning and Delegation
|
|
|
|
Built into the `Agent` abstraction. Every agent gets two harness tools — `plan` and `delegate` — with no extra setup. They are plain tools, not a separate graph runtime.
|
|
|
|
```
|
|
Conductor ──plan──→ (records ordered steps in memory)
|
|
──delegate──→ registered agent (RPC) or ephemeral sub-agent
|
|
```
|
|
|
|
### Setup
|
|
|
|
Nothing to wire — the tools are added to every agent automatically. Guide their use with the prompt:
|
|
|
|
```go
|
|
conductor := micro.NewAgent("conductor",
|
|
micro.AgentServices("task"),
|
|
micro.AgentPrompt(
|
|
"For multi-step requests, call the plan tool first to record your steps. "+
|
|
"For notifications, delegate to the \"comms\" agent (to: \"comms\")."),
|
|
micro.AgentProvider("anthropic"),
|
|
)
|
|
```
|
|
|
|
- **`plan`** records an ordered list of steps (`task` + `status`) in the agent's store-backed memory, surfaced back on later turns so it stays oriented.
|
|
- **`delegate`** hands a self-contained subtask to another agent. **Delegate-first**: if the target is a registered agent it's reached over RPC; otherwise a focused, short-lived sub-agent is created with a fresh, isolated context. A sub-agent is just an agent — created with `New`, talked to with `Ask`; there's no separate "spawn"/"fork" concept.
|
|
|
|
Full example: [examples/agent-plan-delegate](https://github.com/micro/go-micro/tree/master/examples/agent-plan-delegate).
|
|
|
|
### When to Use
|
|
|
|
- Multi-step tasks where an explicit plan keeps the agent on track
|
|
- Multi-agent systems where domain experts own their own services and you want hand-offs to stay distributed (not one agent doing everything)
|
|
|
|
## Choosing a Pattern
|
|
|
|
| Pattern | Complexity | Best For |
|
|
|---------|-----------|----------|
|
|
| Single Agent | Low | Most applications, getting started |
|
|
| Scoped Agents | Medium | Multi-tenant, compliance |
|
|
| Agent as Consumer | Medium | AI-enhanced services |
|
|
| Tool Calling | Medium | Chatbots, assistants |
|
|
| Event-Driven | High | Automation, background processing |
|
|
| Claude Code | Low | Developer workflows |
|
|
| LangChain/LlamaIndex | Medium | Python agent pipelines, RAG |
|
|
| Standalone Gateway | Medium | Production, enterprise |
|
|
| Planning & Delegation | Medium | Multi-step tasks, distributed multi-agent systems |
|
|
|
|
Start with **Pattern 1** (single agent) and add complexity as needed. Most applications don't need multi-agent architectures.
|
|
|
|
## Anti-Patterns
|
|
|
|
### Don't: Chain Agents Without Coordination
|
|
|
|
```
|
|
Agent A → Agent B → Agent C (no shared state, no trace IDs)
|
|
```
|
|
|
|
Instead, use a single agent with multiple tools, or share trace IDs via metadata.
|
|
|
|
### Don't: Give Agents Unrestricted Access
|
|
|
|
```
|
|
Customer Agent → scopes=["*"] (dangerous!)
|
|
```
|
|
|
|
Always use the minimum required scopes. See the [MCP Security Guide](mcp-security.md).
|
|
|
|
### Don't: Skip Error Documentation
|
|
|
|
If agents don't know what errors are possible, they can't handle them gracefully. Always document error cases in your handler comments.
|
|
|
|
### Don't: Build Agent Logic into Services
|
|
|
|
Keep services as pure business logic. Let the agent harness handle orchestration, retries, and decision-making. Your service should just do one thing well.
|
|
|
|
## Next Steps
|
|
|
|
- [Building AI-Native Services](ai-native-services.md) - End-to-end tutorial
|
|
- [MCP Security Guide](mcp-security.md) - Auth and scopes
|
|
- [Tool Description Best Practices](tool-descriptions.md) - Better docs for agents
|
|
- [AI Package](../../ai/README.md) - AI provider interface
|