ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

从零搭建多智能体协作平台:ACP协议实践与踩坑指南

2026/10/8 4:27:38 拓冰建站 浏览量
从零搭建多智能体协作平台:ACP协议实践与踩坑指南 从去年开始我们团队做了一件事把散落在各个IDE里的AI编程助手变成一个能互相协作的智能体流水线。先说结论单纯把多个AI工具装到IDE里效率并不会叠加反而会带来上下文混乱和重复劳动。真正让它跑起来的是一套叫ACP的智能体通信协议Agent Communication Protocol以及基于这套协议构建的多AI编程智能体平台。这篇文章就是复盘我们从立项、设计到落地的全过程包括踩过的坑和可以直接抄走的配置。如果你正在纠结“要不要上多智能体”“ACP协议到底是什么”“多个AI Agent在企业研发里怎么落地”这篇内容应该能帮你少走不少弯路。它适合研发负责人、AI基础设施工程师也适合那些想在公司里推AI研发效能但又怕搞成玩具项目的技术人。1. 为什么企业研发需要“多智能体”而不是“更聪明的大模型”1.1 单模型助手做不到的事我们在评估AI编程工具时做过一个很折腾的实验给同一个需求让几个主流模型分别写代码结果每个模型都给出了能跑的版本但风格完全不同互相看不懂。再尝试让同一个模型先写需求分析再基于需求继续写代码做到第三步它就把第一步的约束忘得差不多了。后来我意识到不是模型不够聪明而是组织方式有问题。一个模型要同时承担需求分析、架构设计、代码生成、测试编写、代码审查这些职责它就必须要在一个上下文中记住所有信息。工程上有一个很朴素的规律上下文的长度提升了可维护性是下降的。你给大模型塞进去两万字的需求和技术栈说明它生成代码时反而容易抓不住重点。指望“一个超级Prompt走天下”在真实业务场景里基本走不通。1.2 从“一个助手”到“一个研发小组”既然一个人干不了所有事那就按研发团队的分工来拆。需求智能体负责把用户诉求拆成结构化任务书开发智能体负责产出代码测试智能体负责生成并执行测试用例审查智能体负责发现代码缺陷文档智能体负责同步更新设计文档。这就是多AI协作的基本形态。每一个智能体都有自己的模型、Prompt、工具和知识库。它们不再是“一个对话框里的聊天记录”而是像真实团队里的成员那样各自干活、互相交付产物。这里要强调一点多智能体不是把任务随机丢给几个模型。它是把研发流程抽象成一条条可路由、可追踪、可回滚的任务链。我们要的是稳定、可控、可观测而不是单纯地“并行跑十个Agent看哪个答案好”。1.3 ACP就是智能体之间的“普通话”多智能体一多第一个问题就冒出来了A智能体怎么把任务交给B智能体是用HTTP回调还是把结果写进数据库还是直接在Prompt里喊一嗓子如果没有统一约定系统很快就变成一团乱麻。ACP协议解决的就是这件事。我把它理解为“智能体之间的HTTP协议”。HTTP让任意客户端和服务器可以对话ACP则让任意研发智能体可以互相发送任务、回传结果、同步状态、协商冲突。有了这层标准化协议我们才能把不同厂商、不同模型、不同语言的智能体像乐高积木一样拼起来。之前GitHub、GitLab上的代码协作靠的是git协议人和人沟通靠的是即时消息协议。现在AI Agent要成为研发环节的“一等公民”就必须有它们自己的协作协议。ACP不是某个大厂的私有接口它本质上是一套消息格式加交互规则的约定谁都可以实现。1.4 我们为什么一定要自己定义ACP刚开始我们想过直接调用各个AI工具的API让它们各自完成独立任务。后来发现行不通需求智能体说要加一个字段开发智能体可能完全不知道测试智能体跑出来失败也没人告诉开发智能体改哪里。工具们之间没有任何依赖关系更谈不上协作。所以我们在内部约定了一套最小可用的ACP规范统一了“智能体能力描述”和“消息信封”。有了这层东西后续新增一个智能体只要它符合协议就能直接接入流水线不用再改其他智能体的代码。这是它成为“平台”而非“工具集合”的关键。2. ACP协议到底定义了哪些东西2.1 六件必须要定义清楚的事一个ACP协议真正落到代码里其实不需要多么复杂的框架但有几件事必须定义清楚。按我们踩坑的顺序我把它们排成六条要素作用容易踩的坑能力注册每个智能体声明自己能处理什么任务没有注册消息乱发消息信封规定消息的公共字段和格式各家定义不统一解析成本高任务路由根据任务类型找到合适的处理者路由规则写死无法扩展上下文传递在任务间传递需求、约束、产物摘要传递全文Token爆炸状态同步让上下游知道当前任务进展没有超时任务永远挂起安全策略控制权限、脱敏、审计代码数据被发到外部API这里我不打算全部展开重点说两个最容易被忽视的部分。能力注册决定了平台的扩展性。我们让每个智能体启动时上报一个agent.json里面写清楚“我能做代码生成”“我能做测试执行”“我接受哪些参数”。这样ACP的消息总线就能自动完成路由而不是在代码里写一长串if-else。后来加入新智能体我们只需要注册一次不改任何现有代码。消息信封则决定了协作的规范性。我们的消息统一分成task、result、query、error四类。task表示分配工作result表示交付成果query表示咨询信息error表示任务失败。所有消息都带message_id、parent_id和timeout字段。message_id用于追踪链路parent_id用于串联整个任务树。没有这两个字段出了问题根本无从查起。2.2 一次标准协作的完整流程拿“给登录模块加一个验证码”这个小需求举例。需求智能体先拿到用户的一句话需求解析出目标、验收标准、涉及模块然后生成一张结构化的任务书。任务书不是自然语言而是一个包含字段的JSON。然后ACP总线的路由模块看到任务类型是“代码开发”把任务书投递给开发智能体。开发智能体生成代码后把改动文件列表、关键代码片段、自测结果打包成result消息回传给总线。总线再根据任务书里的依赖关系把代码转给测试智能体。测试智能体编写用例、执行用例如果有失败就生成一条error消息附上失败日志和可能的怀疑点返回给开发智能体。这整个过程里没有任何两个智能体直接点对点通信。所有消息都经过总线中转每条消息都有唯一的message_id。我们随时可以回答“这段代码是谁写的、基于哪份需求、测试为什么挂了”这类问题。2.3 可直接落地的ACP消息格式示例下面是一个我们当前在用的最小消息信封格式上参考了JSON-RPC但做了一些扩展。{ protocol: acp/1.0, message_id: m-2025-06-01-001, parent_id: m-2025-06-01-000, msg_type: task, from_agent: requirement-agent, to_agent: developer-agent, task: { name: implement_feature, input: { feature: login_captcha, requirement: 用户登录时增加滑动验证码验证通过后才能登录, tech_stack: Python FastAPI PostgreSQL Redis, acceptance_criteria: [ 验证码失败超过3次则锁定5分钟, 接口返回格式保持现有规范 ] } }, context: { ref: task-m-2025-06-01-000, summary: 需求已确认数据库表结构见doc#1123, token_limit: 4000 }, timeout: 120, created_at: 2025-06-01T10:00:00Z }关键点在于context字段。我特别加了summary和token_limit让接收方智能体不会傻乎乎地把整份历史聊天记录都塞进Prompt。它读到的是一段经过压缩的摘要以及本次处理可以消耗的Token上限。这是控制成本和上下文漂移的重要手段。3. 从零搭建ACP平台我踩过的坑和留下的步骤3.1 第一步按真实研发角色设计智能体矩阵不要一上来就写代码。先画一张表把你想让哪些智能体参与研发写清楚。不要为了“多”而“多”初期只需要四个就够了需求分析、代码开发、测试生成、代码审查。后期再逐步补充部署巡检和文档智能体。智能体核心职责需要的输入交付产物需求智能体把需求拆成明确任务书用户原始描述、业务约束结构化需求JSON开发智能体按任务书实现代码任务书、技术栈约束代码diff、自测说明测试智能体生成用例并执行测试代码diff、测试环境地址测试报告、失败用例审查智能体代码规范与潜在缺陷检查代码diff、历史问题库审查意见、风险等级这里有一个很反直觉的经验每个智能体的Prompt不要写太长。你塞给它一堆角色设定、行业知识、行为规范它反而容易丢掉最重要的任务字段。我们后来把所有公共约束放到系统侧也就是总线在派发任务时统一加进上下文而不是让每个智能体自己记着。3.2 第二步消息总线怎么选多智能体的通信层不需要追求绝对的高性能但一定要可观测、可重试、能持久化。我们对比过几个方案方案优势劣势适用场景Redis Streams轻量、运维简单、自带ACK机制大流量下需要额外管理大部分企业研发场景够用MQTT协议天然支持发布订阅、设备生态成熟队列模型偏弱回溯消息麻烦IoT类、边缘节点多的团队WebSocket 自研队列延迟低、完全可控自己造轮子坑很多实验性项目Kafka高吞吐、持久化强运维重、初期没必要日均任务量极大的平台我们最终选了Redis Streams配合FastAPI做一层薄薄的API网关。原因很简单公司内已经有很多业务在用Redis运维同学熟学习成本低。而且Redis Streams自带的消费者组、Pending Entries列表、ACK机制刚好能满足我们对任务可追溯的要求。提示别为了“技术先进”一上来就上Kafka。多智能体协作的瓶颈几乎永远不在消息吞吐而在大模型调用延迟和上下文管理。优先级是先跑通闭环再解决规模问题。3.3 第三步把大模型包成标准Agent不同的智能体可能使用不同的模型。比如需求智能体用擅长中文理解的模型开发智能体用编程能力强的模型测试智能体用执行工具调用能力强的模型。但它们在ACP协议面前都必须暴露同样的接口。我们给每个智能体封装了一层统一的Agent SDK。SDK内部负责三件事从ACP总线拉取属于自己的消息把消息里的task.input转成对应模型的Prompt模板调用模型获得结果后把结果转成标准result消息发回总线这里最需要用心的是提示词模板。同一个开发智能体接收不同类型的代码生成任务Prompt不能是固定的。我们把Prompt拆成基础人格、任务描述、约束条件、输出格式四段。任务描述字段完全来自ACP消息里的input约束条件来自agent.json里声明的checklist。这样模板复用性很高。3.4 第四步平台参数怎么定多Agent平台有三个关键参数必须提前确定任务超时时间、消息重试次数、最大并发数。我们吃过的亏是参数拍脑袋结果线上不断出问题。超时时间建议用这个公式粗算timeout 业务期望的端到端响应时间 / 串行智能体数量 * 0.8比如一个需求希望5分钟内给出初步代码需要经过需求、开发、测试三个智能体串行。那每个智能体的任务超时就应该控制在80秒左右。实际计算是560/30.8 80。预留20%的缓冲给总线传输和排队。如果一个智能体超过80秒还没返回就直接标记失败进入重试或人工接管。最大并发数不建议靠猜。可以这样算max_concurrent 模型每分钟Token配额/ 单次任务平均消耗Token假设你用的是每分钟10万Token配额的模型每个开发任务平均消耗1万Token那么并发上限就是10。这10个并发还要匀给所有智能体所以要按比例分配。我们初期给开发智能体4个并发需求智能体2个测试智能体3个审查智能体1个。实测下来排队基本可控。4. 实操让两个AI智能体跑通第一个闭环4.1 场景定义与目标接下来直接用代码演示一个最简闭环需求智能体发任务开发智能体消费任务并生成代码代码再交回总线。为了演示方便我用FastAPI写一个内存版ACP总线用Python脚本模拟两个Agent。先说目标你部署这套东西后跑一个需求描述进去能拿到一个可追踪的代码生成结果。这中间你要能看到消息的流转过程。4.2 最小版ACP总线实现我用FastAPI提供三个接口注册智能体、发送消息、消费消息。为了让代码可运行消息队列直接用asyncio.Queuefrom fastapi import FastAPI, HTTPException from pydantic import BaseModel from typing import Optional import asyncio app FastAPI() class ACPMessage(BaseModel): message_id: str msg_type: str # task / result / query / error from_agent: str to_agent: str task_name: str payload: dict parent_id: Optional[str] None queues {} app.post(/agent/register) async def register_agent(agent: dict): name agent.get(name) if not name: raise HTTPException(status_code400, detailmissing agent name) queues[name] asyncio.Queue() return {status: registered, agent: name} app.post(/messages) async def post_message(msg: ACPMessage): if msg.to_agent not in queues: raise HTTPException(status_code404, detailfagent {msg.to_agent} not found) await queues[msg.to_agent].put(msg.model_dump()) return {status: queued, message_id: msg.message_id} app.get(/messages/{agent_name}) async def get_message(agent_name: str): if agent_name not in queues: raise HTTPException(status_code404, detailagent not found) try: msg await asyncio.wait_for(queues[agent_name].get(), timeout5) return {status: ok, message: msg} except asyncio.TimeoutError: return {status: empty}这里我没有做持久化和ACK但核心交互已经清楚了。生产环境要把asyncio.Queue换成Redis Streams并且增加任务超时重发机制。逻辑大同小异。4.3 开发智能体工作进程开发智能体本质上是一个不断拉取消息的循环。它从总线拿到task消息调用大模型然后把结果作为result消息发回总线。为了简化示例我用一个本地函数模拟模型调用。import httpx import time BASE_URL http://localhost:8000 def call_llm(requirement: str) - str: # 真实环境这里是模型API调用 return f# 根据需求生成的代码\n# 需求: {requirement}\ndef placeholder():\n pass\n def agent_loop(agent_name: str developer): while True: resp httpx.get(f{BASE_URL}/messages/{agent_name}, timeout6).json() if resp.get(status) empty: time.sleep(1) continue msg resp[message] print(f[{agent_name}] 收到任务 {msg[message_id]}: {msg[task_name]}) if msg[msg_type] task: requirement msg[payload].get(requirement, ) code call_llm(requirement) result_msg { message_id: f{agent_name}-resp-{msg[message_id]}, msg_type: result, from_agent: agent_name, to_agent: requirement-agent, task_name: code_result, payload: { code: code, requirement: requirement }, parent_id: msg[message_id] } httpx.post(f{BASE_URL}/messages, jsonresult_msg).raise_for_status() if __name__ __main__: agent_loop(developer)和真实项目的差距主要在两点第一大模型调用不是同步for循环就能扛住的应该做成异步并发第二result消息不能只发给requirement-agent而是应该由ACP总线根据流程定义路由给下一个环节。我这里为了演示最简闭环固定写死了回发地址。4.4 需求智能体侧的工作方式需求智能体可以做成一次性的HTTP请求入口。用户把需求描述POST到同一个总线总线生成task消息发给开发智能体。这个过程模拟了“需求智能体把任务书推给开发智能体”的效果。httpx.post(http://localhost:8000/messages, json{ message_id: req-001, msg_type: task, from_agent: requirement-agent, to_agent: developer, task_name: implement_feature, payload: { requirement: 实现一个验证码校验接口输入验证码ID和用户答案返回校验结果 } })运行整个流程时你会在开发Agent日志里看到一条消息进入等待几秒后开发Agent把代码生成结果回传。虽然结构很简陋但“消息从A到B再到C”的全链路已经通了。后面要做的所有复杂功能都是在这个链路上不断叠加。5. 上线后最常见的五个故障与排查技巧5.1 智能体互相等待导致“任务假死”现象是任务卡在“处理中”状态每个智能体都显示自己在等待下游结果但下游根本没收到消息。常见原因有三个没有超时机制、消息投递失败后没重试、Agent消费消息后崩溃但已经ACK了。我们的排查技巧是给每个ACP消息加上“停留时间”监控。在Redis里记录消息进入队列和出队列的时间戳超过10秒就报警。有一次我们查到一个开发Agent把消息取走后Prompt模板里拼错了变量名代码异常退出但消息已经被视为消费完成。后来我们改成“先处理完再ACK处理失败则重投队列”问题就不再发生。5.2 上下文漂移上游给的信息下游理解错了这是多智能体场景最隐蔽的坑。需求智能体交付了一份任务书开发智能体只看了任务书里的验收标准没有读完整摘要结果实现出来的逻辑和需求完全不是一回事。原因是我们没有在ACP消息里把“必须遵守的硬约束”和“参考信息”区分开。后来我们在消息格式里加了enforced_rules字段专门放不能违背的约束比如“数据库表名必须保持小写”“接口必须返回统一包装结构”。开发Agent拿到消息后先把enforced_rules原封不动放进Prompt开头再放summary。这样上下文漂移的概率小了很多。5.3 反馈循环两个智能体互改代码停不下来代码审查Agent发现“这里变量名不统一”返回给开发Agent修改。开发Agent改了之后审查Agent又发现“代码风格和团队规范不一致”再返回修改。如此循环十几次Token成本哗哗涨。解法不复杂给每条任务链设置最大迭代次数比如3次。超过次数后自动把消息转给人工评审队列。另外审查Agent不能只提“问题”必须给出具体的、机器可执行修改建议。我们发现当审查意见从“变量名不好”变成“将login_controller.py第37行的userName改为user_name”之后一次修改通过率明显提高。5.4 Token成本失控一次全流程烧掉几十美元多智能体协作最容易忽视成本。每个Agent都觉得自己要用最强模型结果一个简单需求经过五个Agent每个都调用一遍几十万的模型成本直接爆炸。我们的成本控制策略是分级用模型任务类型推荐模型等级原因需求拆分、标题生成小模型任务简单大模型浪费代码生成、复杂重构强逻辑模型需要推理能力代码格式检查、文档更新小模型规则明确不需要深度推理疑难Bug排查大模型 工具调用需要多步试错同时在ACP消息里带token_limit字段每个Agent设置预算。超了就停止生成并返回error而不是无限堆字。实施后我们的单任务平均成本下降了一半以上。5.5 数据安全边界代码仓库内容不能出内网这是企业落地绕不开的红线。我们把整个ACP平台放在内网环境所有请求经统一网关转发到私有化部署的大模型服务。在ACP总线上挂了一层过滤器扫描所有出网消息凡是匹配到密钥、内网IP、敏感业务字段的内容一律拦截并告警。审计日志是我们最后补上的。每条消息都会记录哪个Agent、在什么时间、调用了哪个模型、输入输出摘要。后来安全同事来查我们直接导出按message_id组织的全链路日志三十秒就对完了。如果没有这个设计多智能体系统就是黑盒出事只能干瞪眼。6. 这件事对企业研发的真实影响6.1 研发流程从人工串行到智能体并行以前一个需求从提出到提测要走产品、开发、测试三个角色每一步都是人肉交接。现在ACP平台把交接变成了消息流转。产品经理写完需求描述需求智能体自动拆解开发智能体在几分钟内给出第一版实现测试智能体同时开始生成用例。这并不意味着程序员会被替代而是把程序员从“打字员搜索引擎”变成“架构决策者和结果审计者”。我们的开发时间没有骤降但重复性、机械性的工作明显减少团队可以把精力放到代码审查和系统设计上。6.2 组织能力从“会用AI”到“治理AI”多智能体平台上线后团队里最抢眼的角色不再是谁能写一手复杂Prompt而是谁能让智能体协作不出错。我们在实践中新增了三个工作方向智能体运维、Prompt版本管理、结果质量验收。Prompt模板要进Git仓库每次修改要有code review。智能体的能力配置不能随便改改完要跑回归。ACP平台也要有独立的健康看板显示每个Agent队列长度、平均处理时长、失败率。这些听起来像基础设施工作但恰恰决定了一套AI系统能不能长期稳定跑。6.3 下一步从代码生成扩展到研发全链路我们目前已经把代码审查、单元测试生成、依赖升级检查包装成了标准Agent。下一步计划把发布巡检、线上日志初步分析、故障响应也接入ACP平台。一旦这些场景都能通过统一协议协作它就不再是一个“AI编程工具”而是一个企业研发生产力平台。只有协议足够稳定Agent本身可以随时被更好的模型替换。我们的体会是不要对整个平台做太多定制化修改尽量让每个Agent保持“只懂自己负责那点事”的状态。简单、隔离、可替换才是多智能体平台能长期演进的关键。我个人在实际操作中的体会是ACP协议不是魔法它只是把“人怎么协作”的常识翻译成了机器能读的规则。真正让平台跑出效果的不是Agent数量而是你对研发流程的抽象能力和治理耐心。如果你也要做类似的事别急着上K8s和微服务先用一个消息队列和三个Agent把闭环跑通再去谈规模化。最后再分享一个小技巧消息里永远带上结构化字段少用自然语言描述任务这能帮你避开一大半的上下文故障。