直到2026年,几乎所有人都在用 Python 构建他们的 AI Agent。当然,我也是其中之一。
直到某个性能监控让我彻底清醒:我的 Python Agent 吃掉了 320MB 内存,启动耗时高达 850 毫秒。而隔壁用 Go 开发的同事,他的 Agent 仅占用 45MB 内存,120 毫秒就能跑起来。
那一刻,一个强烈的念头击中了我:用 Go 来写 AI Agent,可能真的比 Python 更香。
于是,我着手用 Go 重构了整个 AI Agent 架构,并基于 MCP 协议对接了 Claude Code,实现了多 Agent 协同编程。结果超出了我的预期:
- 启动速度:提升了 7 倍。
- 内存占用:减少了 86%。
- 部署:单个二进制文件,即拷即用。
- 并发:借助原生 goroutine,轻松支撑上千并发请求。
MCP 协议:AI Agent 的“标准插座”
什么是 MCP?
Model Context Protocol (MCP) 是由 Anthropic 推出的一个开放协议,它定义了 AI 模型与外部工具交互的标准接口。
简单理解,它就是 AI Agent 世界的“USB 接口”。就像 USB 让键盘、鼠标、U 盘都能即插即用一样,MCP 让 Claude、Cursor 或你自建的 Agent 能够无缝接入并使用各种工具。
在 MCP 出现之前,每款 AI 工具都需要单独适配,费时费力。有了 MCP,开发者只需一次开发,即可让工具被多个平台使用。
为什么选择 MCP?
| 方案 |
耦合度 |
扩展性 |
多平台适配 |
| 自定义 API |
高 |
差 |
需重复开发 |
| OpenAPI |
中 |
一般 |
需适配 |
| MCP |
低 |
强 |
一次开发,多平台使用 |
用 Go 实现一个 MCP Server:完整代码
项目结构
go-mcp-agent/
├── cmd/
│ └── server/main.go # MCP Server 入口
├── internal/
│ ├── mcp/ # MCP 协议实现
│ │ ├── server.go
│ │ └── protocol.go
│ ├── agent/ # Agent 逻辑
│ │ ├── tools.go # 工具注册
│ │ └── handlers.go # 请求处理
│ └── llm/ # LLM 客户端
│ └── claude.go
├── go.mod
└── README.md
核心代码
第一步:定义 MCP Server (cmd/server/main.go)
package main
import (
"context"
"encoding/json"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
func main() {
// 创建 MCP Server
s := server.NewMCPServer(
"Go AI Agent",
"1.0.0",
server.WithToolCapabilities(true),
)
// 注册工具
s.AddTool(mcp.NewTool("execute_code",
mcp.WithDescription("执行 Go 代码并返回结果"),
mcp.WithString("code",
mcp.Required(),
mcp.Description("要执行的 Go 代码"),
),
))
s.AddTool(mcp.NewTool("run_tests",
mcp.WithDescription("运行 Go 单元测试"),
mcp.WithString("package",
mcp.Required(),
mcp.Description("要测试的包路径"),
),
))
s.AddTool(mcp.NewTool("code_review",
mcp.WithDescription("审查 Go 代码质量"),
mcp.WithString("code",
mcp.Required(),
mcp.Description("要审查的代码"),
),
))
// 设置工具处理器
s.SetToolHandler(handleTool)
// 启动 SSE 服务器
addr := ":8080"
log.Printf("🚀 MCP Server starting on %s", addr)
go func() {
if err := http.ListenAndServe(addr, nil); err != nil {
log.Fatal(err)
}
}()
// 优雅关闭
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("👋 MCP Server shutting down")
}
func handleTool(ctx context.Context, name string, args map[string]interface{}) (*mcp.CallToolResult, error) {
switch name {
case "execute_code":
return handleExecuteCode(ctx, args)
case "run_tests":
return handleRunTests(ctx, args)
case "code_review":
return handleCodeReview(ctx, args)
default:
return mcp.NewToolResultError("unknown tool: " + name), nil
}
}
第二步:实现工具处理器 (internal/agent/handlers.go)
package agent
import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/mark3labs/mcp-go/mcp"
)
func handleExecuteCode(ctx context.Context, args map[string]interface{}) (*mcp.CallToolResult, error) {
code, ok := args["code"].(string)
if !ok {
return mcp.NewToolResultError("code parameter required"), nil
}
// 创建临时文件
tmpDir, err := os.MkdirTemp("", "go-agent-*")
if err != nil {
return mcp.NewToolResultError("create temp dir failed: " + err.Error()), nil
}
defer os.RemoveAll(tmpDir)
// 写入代码
mainFile := filepath.Join(tmpDir, "main.go")
if err := os.WriteFile(mainFile, []byte(code), 0644); err != nil {
return mcp.NewToolResultError("write file failed: " + err.Error()), nil
}
// 执行代码
cmd := exec.CommandContext(ctx, "go", "run", mainFile)
output, err := cmd.CombinedOutput()
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("execution failed: %s\n%s", err, output)), nil
}
return mcp.NewToolResultText(string(output)), nil
}
func handleRunTests(ctx context.Context, args map[string]interface{}) (*mcp.CallToolResult, error) {
pkg, ok := args["package"].(string)
if !ok {
return mcp.NewToolResultError("package parameter required"), nil
}
// 运行测试
cmd := exec.CommandContext(ctx, "go", "test", "-v", pkg)
output, err := cmd.CombinedOutput()
result := string(output)
if err != nil {
result = fmt.Sprintf("Tests failed:\n%s", result)
} else {
result = fmt.Sprintf("Tests passed:\n%s", result)
}
return mcp.NewToolResultText(result), nil
}
func handleCodeReview(ctx context.Context, args map[string]interface{}) (*mcp.CallToolResult, error) {
code, ok := args["code"].(string)
if !ok {
return mcp.NewToolResultError("code parameter required"), nil
}
// 使用 golangci-lint 进行代码审查
tmpDir, _ := os.MkdirTemp("", "go-review-*")
defer os.RemoveAll(tmpDir)
mainFile := filepath.Join(tmpDir, "main.go")
os.WriteFile(mainFile, []byte(code), 0644)
cmd := exec.CommandContext(ctx, "golangci-lint", "run", "--disable-all",
"-E", "govet", "-E", "staticcheck", tmpDir)
output, _ := cmd.CombinedOutput()
issues := strings.TrimSpace(string(output))
if issues == "" {
return mcp.NewToolResultText("✅ Code looks good! No issues found."), nil
}
return mcp.NewToolResultText(fmt.Sprintf("⚠️ Found issues:\n\n%s", issues)), nil
}
第三步:对接 Claude API (internal/llm/claude.go)
这是关键一步,让我们的 Go Agent 能够调用 Claude Code 的 API。
package llm
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
type ClaudeClient struct {
apiKey string
model string
maxTokens int
}
type Message struct {
Role string `json:"role"`
Content string `json:"content"`
}
type ChatRequest struct {
Model string `json:"model"`
Messages []Message `json:"messages"`
MaxTokens int `json:"max_tokens,omitempty"`
Temperature float64 `json:"temperature,omitempty"`
Tools []Tool `json:"tools,omitempty"`
}
type Tool struct {
Name string `json:"name"`
Description string `json:"description"`
InputSchema struct {
Type string `json:"type"`
Required []string `json:"required"`
Properties map[string]interface{} `json:"properties"`
} `json:"input_schema"`
}
type ChatResponse struct {
Content []struct {
Type string `json:"type"`
Text string `json:"text"`
ToolCall struct {
Name string `json:"name"`
Arguments map[string]interface{} `json:"arguments"`
} `json:"tool_call"`
} `json:"content"`
}
func NewClaudeClient(model string) *ClaudeClient {
apiKey := os.Getenv("ANTHROPIC_API_KEY")
return &ClaudeClient{
apiKey: apiKey,
model: model,
maxTokens: 4096,
}
}
func (c *ClaudeClient) Chat(ctx context.Context, messages []Message, tools []Tool) (*ChatResponse, error) {
req := ChatRequest{
Model: c.model,
Messages: messages,
MaxTokens: c.maxTokens,
Temperature: 0.1,
Tools: tools,
}
body, _ := json.Marshal(req)
httpReq, _ := http.NewRequestWithContext(ctx, "POST",
"https://api.anthropic.com/v1/messages", bytes.NewReader(body))
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("x-api-key", c.apiKey)
httpReq.Header.Set("anthropic-version", "2026-02-01")
httpReq.Header.Set("anthropic-beta", "tools-2026-01-15")
resp, err := http.DefaultClient.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("API error %d: %s", resp.StatusCode, body)
}
var chatResp ChatResponse
if err := json.NewDecoder(resp.Body).Decode(&chatResp); err != nil {
return nil, fmt.Errorf("decode response failed: %w", err)
}
return &chatResp, nil
}
多 Agent 协作架构设计
架构图
┌─────────────────────────────────────────────────────────┐
│ Claude Code (主 Agent) │
│ - 任务分解 │
│ - 代码生成 │
│ - 最终审查 │
└───────────────┬─────────────────────────────┬───────────┘
│ │
┌───────────▼──────────┐ ┌────────────▼──────────┐
│ Go Code Agent │ │ Go Test Agent │
│ - 代码执行 │ │ - 单元测试 │
│ - 语法检查 │ │ - 集成测试 │
│ - 性能分析 │ │ - 基准测试 │
└───────────┬──────────┘ └────────────┬──────────┘
│ │
┌───────────▼──────────┐ ┌────────────▼──────────┐
│ Go Review Agent │ │ Go Deploy Agent │
│ - 代码质量 │ │ - 构建 │
│ - 安全检查 │ │ - Docker 镜像 │
│ - 最佳实践 │ │ - 部署 │
└──────────────────────┘ └───────────────────────┘
主 Agent 任务分解
代码的核心部分是 Orchestrator,它负责将用户请求拆解为多个子任务,并分发给不同的 Agent 并行执行。
package main
import (
"context"
"fmt"
"log"
"sync"
)
type Task struct {
ID string
Description string
Agent string
Status string
Result string
}
type Orchestrator struct {
claude *llm.ClaudeClient
mcpServer *server.MCPServer
tasks []Task
mu sync.Mutex
}
func (o *Orchestrator) ProcessRequest(ctx context.Context, userRequest string) error {
// 1. 让 Claude 分解任务
messages := []llm.Message{
{Role: "system", Content: "你是一个 Go 开发团队的技术负责人。请将用户的需求分解为可执行的子任务。"},
{Role: "user", Content: userRequest},
}
tools := []llm.Tool{
{
Name: "assign_task",
Description: "分配子任务给特定 Agent",
InputSchema: struct {
Type string `json:"type"`
Required []string `json:"required"`
Properties map[string]interface{} `json:"properties"`
}{
Type: "object",
Required: []string{"agent", "description"},
Properties: map[string]interface{}{
"agent": map[string]string{
"type": "string",
"enum": "code,test,review,deploy",
},
"description": map[string]string{"type": "string"},
},
},
},
}
resp, err := o.claude.Chat(ctx, messages, tools)
if err != nil {
return fmt.Errorf("claude chat failed: %w", err)
}
// 2. 解析任务分配
for _, content := range resp.Content {
if content.Type == "tool_call" {
task := Task{
ID: generateID(),
Description: content.ToolCall.Arguments["description"].(string),
Agent: content.ToolCall.Arguments["agent"].(string),
Status: "pending",
}
o.tasks = append(o.tasks, task)
}
}
// 3. 并行执行子任务
var wg sync.WaitGroup
for i := range o.tasks {
wg.Add(1)
go func(task *Task) {
defer wg.Done()
o.executeTask(ctx, task)
}(&o.tasks[i])
}
wg.Wait()
// 4. 汇总结果
log.Printf("✅ All %d tasks completed", len(o.tasks))
return nil
}
func (o *Orchestrator) executeTask(ctx context.Context, task *Task) {
log.Printf("🔄 Executing task [%s]: %s", task.Agent, task.Description)
// 通过 MCP 调用对应工具
result, err := o.mcpServer.CallTool(ctx, task.Agent, map[string]interface{}{
"description": task.Description,
})
o.mu.Lock()
defer o.mu.Unlock()
if err != nil {
task.Status = "failed"
task.Result = err.Error()
return
}
task.Status = "completed"
task.Result = result.Content[0].Text
}
实战应用场景
场景一:代码生成与自动测试
用户输入:
帮我实现一个 Go 的并发安全的缓存,支持 TTL 过期
执行流程:
- Claude Code:分解任务(代码结构、缓存逻辑、并发控制)。
- Code Agent:执行生成的代码,验证语法正确性。
- Test Agent:生成并运行单元测试、并发测试。
- Review Agent:检查内存泄漏、goroutine 泄漏风险。
- Claude Code:汇总结果,输出最终优化后的代码。
场景二:代码重构与性能优化
对于历史代码的性能优化场景非常实用。
用户输入:
优化这段 Go 代码的性能:[粘贴代码]
执行流程:
- Claude Code:分析代码性能瓶颈。
- Code Agent:执行基准测试,量化性能指标。
- Review Agent:使用 golangci-lint 等工具检查代码质量。
- Test Agent:确保重构后所有单元测试依然通过。
- Claude Code:输出详细的优化建议与最终代码。
性能对比:Go vs Python
完成上述场景后,我进行了一系列对比测试。结果有些令人惊讶。
| 指标 |
Go Agent |
Python Agent |
优势 |
| 启动时间 |
120ms |
850ms |
Go 快 7 倍 |
| 内存占用 |
45MB |
320MB |
Go 省 86% |
| 并发请求 |
1000+/s |
200/s |
Go 高 5 倍 |
| 部署复杂度 |
单文件 |
虚拟环境+依赖 |
Go 简单 |
| 热重载 |
不支持 |
支持 |
Python 胜 |
坦白说,Python 在开发阶段的热重载(修改代码后自动生效)方面确实有优势,提升了开发体验。
但在生产环境中,我们更看重的是稳定性和资源效率。
我的结论如下:
- Go 更适合生产环境部署、对性能、资源消耗和并发有要求的 AI Agent。
- Python 更适合快速原型开发和算法验证。
- 最佳实践可能是:使用 Python + LangChain 快速验证想法和原型,然后用 Go 重构核心架构并上线。
踩坑记录与解决方案
项目开发中遇到几个典型问题,分享出来供大家参考。
坑一:SSE 连接超时
问题:Claude Code 通过 SSE 连接 MCP Server 时,长时间无响应会导致连接断开。
解决:在创建 Server 时设置心跳间隔。
s := server.NewMCPServer(
"Go AI Agent",
"1.0.0",
server.WithKeepAlive(30*time.Second),
)
坑二:并发安全与缓存冲突
问题:多个 Agent 同时调用 go run 会在共享的 GOCACHE 上产生冲突。
解决:为每个执行环境指定独立的缓存目录。
cmd := exec.CommandContext(ctx, "go", "run", mainFile)
cmd.Env = append(os.Environ(),
"GOCACHE="+tmpDir+"/go-cache",
"GOMODCACHE="+tmpDir+"/go-mod-cache",
)
坑三:工具调用结果格式不稳定
问题:Claude API 返回的 tool_call 格式(JSON/文本)可能不固定,导致解析失败。
解决:在请求头中明确指定稳定的 Beta 工具版本。
httpReq.Header.Set("anthropic-beta", "tools-2026-01-15")
切记:不要省略这个 Header,否则调试起来会非常头疼。
部署指南
Docker 部署 (Dockerfile)
FROM golang:1.26-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o go-mcp-agent ./cmd/server
FROM alpine:latest
RUN apk --no-cache add ca-certificates git
COPY --from=builder /app/go-mcp-agent /usr/local/bin/
EXPOSE 8080
CMD ["go-mcp-agent"]
Docker Compose 编排 (docker-compose.yml)
version: '3'
services:
go-mcp-agent:
build: .
ports:
- "8080:8080"
environment:
- ANTHROPIC_API_KEY=your-key-here
volumes:
- go-cache:/root/.cache/go-build
restart: unless-stopped
volumes:
go-cache:
一键启动
部署完成后,只需几条命令即可启动服务。
# 克隆项目
git clone https://github.com/yourname/go-mcp-agent.git
cd go-mcp-agent
# 设置环境变量
export ANTHROPIC_API_KEY="sk-ant-xxx"
# 启动服务
docker-compose up -d
# 验证服务
curl http://localhost:8080/sse
总结
回顾整个重构过程,使用 Go 开发 AI Agent 主要有以下三大优势:
- 强大的并发原语:
goroutine 和 channel 的模型天生适合构建多 Agent 协作系统,比 Python 的 asyncio 在复杂并发场景下更直观、高效。
- 极简的部署体验:单一二进制文件,无需处理虚拟环境、依赖冲突,真正做到“一次编译,处处运行”。
- 卓越的运行性能:在启动速度、内存占用和高并发处理能力这些生产环境核心指标上,Go 表现突出。
当然,Go 的短板也不容忽视:
- AI 生态仍在发展:相比 Python 成熟的 LangChain、LlamaIndex 等生态,Go 的相关工具链和社区资源还在起步阶段。
- 原型开发速度:在快速验证想法的原型阶段,Python 的动态特性和丰富库支持确实更具效率。
因此,我最终的实践建议是:结合两者优势。在原型验证和算法探索阶段,充分利用 Python 的快速迭代能力。一旦需求明确、架构稳定,再将核心服务用 Go 进行重构和部署,以追求生产环境的最佳性能与稳定性。
关于更多云计算、AI 与数据科学领域的讨论,欢迎来 云栈社区 交流。
参考资料
- MCP 协议官方文档: https://modelcontextprotocol.io/
- mark3labs/mcp-go: https://github.com/mark3labs/mcp-go
- Claude Code 文档: https://docs.anthropic.com/claude/docs/claude-code
- Go 官方并发模式: https://go.dev/blog/pipelines