s10_团队协议设计:为什么多智能体协作不能只靠发消息
团队协议设计为什么多智能体协作不能只靠发消息很多人第一次做多智能体协作时直觉都是只要能让队友之间互相发消息团队就算搭起来了。这个想法不算错但只对了一半。s09确实已经把“常驻队友 文件邮箱 线程执行”这套基础设施搭出来了。队友有名字、有状态、能收信、能回信也能继续干活。可一旦任务开始变得更严肃一点光有消息通道其实还不够。比如lead 想让某个队友优雅下线不能只靠一句“你先停一下”队友准备做大改动不能上来就开干最好先让 lead 过一遍计划一次协商发出去之后外部最好能看见它到底是在等待中、已经批准还是已经被拒绝agents/s10_team_protocols.py补上的正是这层“协作规矩”。它让队友之间的沟通从“能发消息”升级成了“带编号、带状态、带回执的结构化握手”。链接 s10_team_protocols.py先说结论如果让我用一句话概括s10我会这么说团队协作真正难的不是把消息发出去而是把约定变成可以追踪、可以确认、可以复用的协议。这节做得很克制没有引入复杂调度中心没有上数据库没有做一套很重的工作流引擎它只是站在s09的基础上再往前补了一层用request_id给每次协商编号用pending / approved / rejected给协商建状态用固定 handler 把协议流程收口到统一入口就这一步团队协作的味道一下子就出来了。为什么有了邮箱还不够s09解决的是“谁给谁发消息”。s10解决的是“这条消息属于哪次协商以及这次协商现在走到哪一步”。这两个问题看起来接近实际上完全不是一回事。举个最直观的例子。如果没有协议层lead 想让 Alice 停下来最自然的做法可能是给 Alice 发一句你先停一下收个尾。这句话人当然能看懂但系统很难围绕它稳定地工作。因为它缺 3 个关键属性没有唯一编号同一时间如果发了两次类似请求系统很难判断 Alice 回的是哪一次。没有明确状态外部不知道这件事现在是还没看见、已经看到但没决定、已经同意还是明确拒绝。没有统一回执格式队友回一句“好”或者“稍等”人能懂程序却很难稳定处理。所以我会把s10理解成这样一句话s09给团队补了通信层s10给团队补了协议层。这节真正新增的不是 3 个工具而是一套公共骨架从代码表面看s10比s09多出来的主要是这几类能力shutdown_requestshutdown_responseplan_approval但如果只把它看成“又多了几个工具”很容易错过重点。这一节最值钱的其实是下面这套统一骨架shutdown_requests{}plan_requests{}_tracker_lockthreading.Lock()再加上每次发起时都会生成的request_idreq_idstr(uuid.uuid4())[:8]这两样东西一拼起来意思就完全不一样了。现在系统不再只是“发了一条消息”而是“发起了一次有编号的协商”。之后所有回应、审批、状态查询都会围着这个编号转。我觉得这就是协议层和普通消息层真正的分水岭。整体结构图消息层没变协议层加在它上面提交计划回应关机发起关机审批计划用户Lead 主循环TOOL_HANDLERSTeammateManagerMessageBusshutdown_requestsplan_requestsAlice 线程Bob 线程alice.jsonlbob.jsonllead.jsonl这张图最关键的一点是MessageBus仍然是底层通信通道但真正让系统具备“协商能力”的是上面的两个 tracker。也就是说消息负责送达协议负责认账。这两个角色分开以后代码就变得很清楚谁收消息看 inbox谁管流程看 tracker谁执行决策看各自 handler两套协议虽然业务不同但骨架完全一样s10里有两套协议关机协议lead 发起teammate 回应计划审批teammate 发起lead 回应方向不同但骨架完全相同。approve trueapprove falsePendingApprovedRejected这张状态图看着很简单但我觉得它正是这一节最有工程味道的地方。因为它说明了一件事协议不一定要先按业务拆很多时候可以先按状态机拆。只要某类协商都满足下面这个模式一方发起请求另一方给出批准或拒绝整个过程需要可追踪那它就可以先套进这套pending - approved / rejected的骨架里。这也是为什么shutdown和plan approval明明是两种不同业务却可以共用一套思路。关机协议重点不是“停线程”而是“优雅退出”很多人第一次看到shutdown直觉会把重点放在“把队友关掉”。但这段代码真正想解决的不是“怎么停”而是“怎么别停得太粗暴”。因为如果一个队友正在改文件写配置跑工具调用维护自己的上下文状态这时直接粗暴结束线程很容易留下半拉子现场。所以handle_shutdown_request()的做法是lead 先生成request_id把这次请求登记到shutdown_requests通过收件箱给队友发一条shutdown_request等队友自己调用shutdown_response如果批准则在本轮工具调用收尾后退出线程并把状态改成shutdown关键代码片段可以看这里defhandle_shutdown_request(teammate:str)-str:req_idstr(uuid.uuid4())[:8]with_tracker_lock:shutdown_requests[req_id]{target:teammate,status:pending}BUS.send(lead,teammate,Please shut down gracefully.,shutdown_request,{request_id:req_id},)returnfShutdown request{req_id}sent to {teammate} (status: pending)而队友侧真正批准关机时也不是立刻强退而是先把结果记录下来ifblock.nameshutdown_responseandblock.input.get(approve):should_exitTrue然后等这一轮工具结果都回写完再在循环尾部把成员状态设成shutdown。这一点我觉得很值得记住好的协议不只是给出结论还要给执行过程留出收尾空间。计划审批协议重点不是“领导拍板”而是“高风险动作先过闸”plan approval看起来像是给系统加了一层审批但我更愿意把它理解成“重大动作前的闸门”。队友不是不能干活而是在做大动作前先把计划正文交出来。这段逻辑放在 teammate 侧的_exec()里iftool_nameplan_approval:plan_textargs.get(plan,)req_idstr(uuid.uuid4())[:8]with_tracker_lock:plan_requests[req_id]{from:sender,plan:plan_text,status:pending}BUS.send(sender,lead,plan_text,plan_approval_response,{request_id:req_id,plan:plan_text},)returnfPlan submitted (request_id{req_id}). Waiting for lead approval.这里有个我觉得很有意思的点。从命名上看plan_approval_response稍微有点拧巴因为它既承载了“提交计划”也承载了“审批结果”。但从教学角度看这个命名反而提醒我们一件事协议消息最重要的不是字面名字漂不漂亮而是它有没有稳定的结构和固定的处理入口。真正关键的是这些协议字段一直都在request_idplanapprovefeedback只要这些结构是稳定的系统就能围绕它稳稳地跑起来。把两次握手放进时序图里会更容易看懂1. 关机协议时序图TeammateMessageBusshutdown_requestsLeadTeammateMessageBusshutdown_requestsLead写入 request_id, statuspending发送 shutdown_request队友读取 inbox判断 approve / reject更新状态 approved / rejected发送 shutdown_responselead 下轮读取 inbox若 approvetrue本轮收尾后退出线程2. 计划审批时序图LeadMessageBusplan_requestsTeammateLeadMessageBusplan_requestsTeammate写入 request_id, statuspending提交计划正文lead 读取 inbox审阅计划更新状态 approved / rejected发送审批结果与反馈队友下轮读取 inbox这两张图放一起看会更容易体会到s10的设计取向它没有去追求“协议自动执行到头”而是把“请求、追踪、回应”这 3 件关键事情先固定下来。这很克制但也很实用。协议层和消息层之间其实是“分工”关系我觉得很多人第一次读这节时最容易混淆的一点就是协议消息和普通消息看起来都只是 JSONL 里的一行记录那它们到底差在哪我的理解是这样的。普通消息解决的是表达问题比如我做完了什么我还缺什么信息你帮我看一下这个结果协议消息解决的是流程问题比如这是一条正式请求这条请求的编号是什么它现在处于什么状态谁有权给出最终回应所以协议层不是替代消息层而是压在消息层之上。这也是一个很实用的工程思路先把基础通道搭稳再在通道上叠加规则而不是每来一种流程就重做一套通信机制。我觉得这一节最值得带走的 5 个判断1. 团队能发消息不等于团队有协议消息解决的是送达协议解决的是确认、回执和追踪。没有协议协作更多靠默契有了协议协作才开始变成系统能力。2.request_id比很多人想象中更重要它不只是一个编号而是一次协商的身份证。没有它系统很难在多轮往返里稳定对齐“谁回应了谁”。3. 好的协议通常先长得像状态机而不是先长得像业务表单s10最妙的地方就是先抽出了pending - approved / rejected这个公共骨架再把shutdown和plan approval两种业务挂上去。4. 协议层最有价值的地方是把“口头约定”变成“外部可见状态”一旦shutdown_requests和plan_requests存在lead 就不只是“记得自己问过什么”而是系统里真的有地方能查。5. 这份实现仍然把最终决策留给模型而不是把流程全写死比如“重大工作前先提计划”这件事当前主要还是靠 system prompt 和工具设计引导。这说明它不是重工作流引擎而是“轻协议 模型决策”的路线。这份实现还有几个边界反而很值得注意1. tracker 还是内存态不是持久化状态config.json和inbox/*.jsonl会落盘但shutdown_requests/plan_requests还只是进程内字典。这意味着它已经有了协议雏形但还没变成完整的跨重启协议系统。2. 没有 timeout、retry、仲裁这类高级机制如果某个请求发出后一直没人回应这里不会自动超时也不会自动重试。这让它保持了教学上的清晰也说明协议层还有继续长大的空间。3. 工具名是按角色定义的不是全局语义完全对称比如 lead 侧的shutdown_response工具其实是“查询状态”teammate 侧的shutdown_response才是真的“发送回应”。这看起来有一点绕但它也提醒我们工具名本质上是“某个角色看到的接口”不一定是系统全局统一语义。4. 协议层没有替代消息层而是压在消息层之上s10并没有废掉MessageBus而是继续复用它承载协议消息。这说明一个很朴素但很实用的工程判断基础通道一旦成立后续很多复杂能力都应该优先考虑“叠加规则”而不是“重做底座”。如果把s09和s10连起来看会更容易看清这条演进线我自己会这样理解这两节s09回答的是多智能体怎样长期存在、彼此找到、互相发消息s10回答的是多智能体怎样围绕一件事达成有状态的协商也可以把它理解成s09让团队“能对话”s10让团队“有规矩”这一步其实非常关键因为很多系统做到能分工、能通信就停住了。但真正稍微复杂一点的协作最后都会走到协议层谁能发起什么请求对方怎么回应外部怎么追踪状态怎么流转被拒绝之后怎么处理s10虽然只做了两个很小的协议但已经把这条路打开了。最后总结agents/s10_team_protocols.py最值得学的不是它又加了 3 个工具也不是它画出两个流程就算完。我觉得它真正讲明白的是多智能体系统一旦开始走向协作迟早要从“自由聊天”走到“结构化握手”。而这一步最小可以怎么做s10给了一个很漂亮的答案底层继续用现成的消息通道每次协商分配一个request_id用 tracker 记录状态用固定 handler 收口流程用简单状态机统一不同业务这套实现很轻但已经足够让团队协作从“能聊”升级到“能协商”。致谢学习主线受益于shareAI-lab/learn-claude-code
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2489898.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!