多 Agent 协作:什么时候该拆成多个 Agent 一只用 AI Agent 搭副业产线的程序员我写过的最容易出 Bug 的 Agent是一个「全能型」Agent——它能写代码、能审查、能测试、能部署。System Prompt 有 1500 字注册了 20 个工具。结果是什么呢它在写代码的时候偶尔会调用测试工具。在审查的时候偶尔会调用部署工具。像个实习生喝多了——什么都会但什么都乱来。后来我把这个 Agent 拆成了两个一个负责生成代码一个负责审查代码。Bug 率直接降了一半。这篇讲的就是这个决策——什么时候拆怎么拆拆完怎么协作。先看反面一个 Agent 做所有事用户实现一个用户积分管理系统 Agent Step 1: 生成代码 ← OK Step 2: 写单元测试 ← OK Step 3: 自我审查代码 ← 问题来了能客观吗 Step 4: 改代码 ← 它改了但改对了吗 Step 5: 觉得没问题了直接输出同一个 Agent 生成代码又审查代码就像同一个人出题又批卷——它会对自己写的东西产生「确认偏差」。它倾向于觉得自己的代码没问题。拆成两个 Agent一个生成一个审查┌─────────────────┐ │ Orchestrator │ 编排者 │ (协调 Agent) │ └───────┬─────────┘ │ ┌─────────────┼─────────────┐ │ │ │ ▼ ▼ ▼ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ Coder │ │ Reviewer │ │ Tester │ │ 代码生成 │ │ 代码审查 │ │ 测试验证 │ └────────────┘ └────────────┘ └────────────┘每个 Agent 只做一件事有自己独立的 System Prompt 和工具集。完整实现packagemainimport(bytesencoding/jsonfmtionet/httpos)typeMessagestruct{Rolestringjson:roleContentstringjson:content}// Agent 定义每个子 Agent 的配置typeAgentConfigstruct{Namestringjson:nameSystemPromptstringjson:system_promptTools[]stringjson:tools// 工具名列表Temperaturefloat64json:temperature}// MultiAgentSystem 多 Agent 协作系统typeMultiAgentSystemstruct{apiKeystringbaseURLstringagentsmap[string]AgentConfig codebasestring// 共享的代码上下文}funcNewMultiAgentSystem(apiKeystring)*MultiAgentSystem{returnMultiAgentSystem{apiKey:apiKey,baseURL:https://api.deepseek.com/anthropic/v1/chat/completions,agents:make(map[string]AgentConfig),}}func(m*MultiAgentSystem)RegisterAgent(config AgentConfig){m.agents[config.Name]config}// callAgent 调用单个 Agentfunc(m*MultiAgentSystem)callAgent(agentNamestring,taskstring,contextstring)(string,error){config,ok:m.agents[agentName]if!ok{return,fmt.Errorf(未知 Agent: %s,agentName)}messages:[]Message{{Role:system,Content:config.SystemPrompt},{Role:user,Content:fmt.Sprintf(上下文\n%s\n\n任务\n%s,context,task)},}body,_:json.Marshal(map[string]interface{}{model:deepseek-v4-pro,messages:messages,temperature:config.Temperature,})req,_:http.NewRequest(POST,m.baseURL,bytes.NewReader(body))req.Header.Set(Authorization,Bearer m.apiKey)req.Header.Set(Content-Type,application/json)resp,err:http.DefaultClient.Do(req)iferr!nil{return,err}deferresp.Body.Close()data,_:io.ReadAll(resp.Body)varresultstruct{Choices[]struct{Messagestruct{Contentstringjson:content}json:message}json:choices}json.Unmarshal(data,result)iflen(result.Choices)0{return,fmt.Errorf(Agent %s 返回空,agentName)}returnresult.Choices[0].Message.Content,nil}// ──────────── 协作流程代码审查 ────────────// CodeReviewOrchestrator 编排者func(m*MultiAgentSystem)CodeReview(requirementstring)(string,[]string,error){// 步骤 1Coder 生成代码fmt.Println( Coder Agent: 生成代码...)code,err:m.callAgent(coder,requirement,)iferr!nil{return,nil,fmt.Errorf(代码生成失败: %w,err)}fmt.Printf(✅ 生成代码 (%d 字符)\n,len(code))// 步骤 2Reviewer 审查代码fmt.Println( Reviewer Agent: 审查代码...)review,err:m.callAgent(reviewer,请审查以下代码列出所有问题,code)iferr!nil{return,nil,fmt.Errorf(代码审查失败: %w,err)}fmt.Printf(✅ 审查完成 (%d 字符)\n,len(review))// 步骤 3如果有问题让 Coder 修改issues:parseIssues(review)iflen(issues)0{fmt.Printf(⚠️ 发现 %d 个问题交给 Coder 修复...\n,len(issues))fixTask:fmt.Sprintf(基于以下审查意见修复代码\n%s\n\n原始需求%s,review,requirement,)code,errm.callAgent(coder,fixTask,code)iferr!nil{return,nil,fmt.Errorf(修复失败: %w,err)}fmt.Println(✅ 代码已修复)}returncode,issues,nil}// parseIssues 从审查结果中提取问题列表funcparseIssues(reviewstring)[]string{varissues[]string// 简化找包含问题的行for_,line:rangebytes.Split([]byte(review),[]byte(\n)){lineStr:string(line)iflen(lineStr)0{issuesappend(issues,lineStr)}}returnissues}// ──────────── 两个 Agent 的 System Prompt ────────────varcoderPrompt你是一个 Go 后端代码生成专家。 行为规则 1. 生成完整可编译的 Go 代码包含 package 声明和必要的 import 2. 所有公开函数包含 godoc 注释 3. 处理所有可能的错误不使用 panic 4. 使用标准库优先第三方依赖仅在必要时引入 5. 输出格式只输出代码不要解释如何运行 输出格式 go // 代码 varreviewerPrompt你是一个严格的 Go 代码审查员。 审查规则按优先级排序 1. 严重问题线程安全问题、资源泄漏、SQL 注入、空指针 2. 中等问题错误处理缺失、异常未处理、输入未校验 3. 建议命名不规范、性能可优化、代码可简化 输出格式 # 代码审查报告 ## 严重问题必须修复 - [行号] 问题描述 修复建议 ## 中等问题建议修复 - [行号] 问题描述 修复建议 ## 改进建议可选 - [行号] 建议描述 不要表扬代码优点。只说问题。// ──────────── 主函数 ────────────funcmain(){m:NewMultiAgentSystem(os.Getenv(DEEPSEEK_API_KEY))m.RegisterAgent(AgentConfig{Name:coder,SystemPrompt:coderPrompt,Temperature:0.1,// 低温度代码要稳定})m.RegisterAgent(AgentConfig{Name:reviewer,SystemPrompt:reviewerPrompt,Temperature:0.0,// 极低温度审查要一致})code,issues,err:m.CodeReview(实现一个线程安全的 LRU 缓存支持 Get/Put/Len 方法使用 Go 泛型,)iferr!nil{fmt.Printf(❌ 失败: %v\n,err)return}fmt.Printf(\n 最终代码 \n%s\n,code)fmt.Printf(\n 审查发现 %d 个问题 \n,len(issues))fori,issue:rangeissues{fmt.Printf(%d. %s\n,i1,issue)}}什么时候该拆什么时候不该拆场景拆不拆理由生成代码 → 审查代码拆同一个人不能批自己卷子读取文件 → 分析文件不拆同一个连续流程写 API → 写前端可选共享同一份需求理解有助于一致性数据分析 → 生成图表 → 写报告拆三个独立技能分开更专注搜索 → 总结不拆搜索结果是总结的直接输入判断标准很简单如果两个任务需要「互相挑刺」就必须拆。如果只是线性的「A 的输出是 B 的输入」可以不拆。Agent 之间的通信协议多 Agent 系统最容易被忽略的问题是——Agent 之间怎么传递信息我用过三种方式1. 纯文本传递最简单Agent A 输出 → 直接当 Agent B 的 prompt 输入适合简单流转但结构化信息会丢失。2. JSON 协议最常用typeAgentMessagestruct{Fromstringjson:from// 发送方Tostringjson:to// 接收方Typestringjson:type// request/response/errorPayloadstringjson:payload// 内容Metadatamap[string]stringjson:metadata// 额外信息}3. 共享内存黑盒级别创建一个结构体所有 Agent 都能读写由 Orchestrator 管理并发。一个真实的坑Agent 之间互相甩锅有一次Coder Agent 写了个有 bug 的代码Reviewer 指出了问题。Coder 修改后Reviewer 又指出了一个新问题。然后 Coder 又改……循环了 7 轮。最后我发现两个 Agent 在一个设计决策上分歧了——Coder 想用 interfaceReviewer 想用 concrete type。两个都没错但互相不妥协。教训给 Reviewer 和 Coder 共享一个「设计原则」上下文或者在循环超过 3 轮后由 Orchestrator 强制裁决。总结多 Agent 协作的核心不是「把 Agent 拆开」而是「让每个 Agent 只承担一个角色」。单 Agent 像全科医生——什么都能看但开颅手术不太行。多 Agent 像会诊——每个科室一个专家但需要一个主治医生协调。下一篇我们解决一个更根本的问题Agent 怎么记住东西没有记忆的 Agent每次对话都是从零开始的新人。关注我别错过。 一只用 AI Agent 搭副业产线的程序员全平台同名虾哥不加班需要定制 AI 工具来聊聊 → lob_ai源码GitHub - lobster-bujiaban