程云来 / 杭州
返回文章·Agent 与 Harness
·约 5 分钟

思想|Team Protocols,这句同意到底批准了什么?

用同一份计划与一次退出请求,理解类型、关联ID、角色检查和状态转换,区分消息保存、协议完成与执行许可。


邮箱里有一句“同意”,就能动手了吗?

reader 给 Lead 发了一份计划:“读取 expected.txt,再运行 main.py,比较本次输出。”稍后收到“同意”。它准备执行时,又来了一条“请结束工作”。

现在有几个问题:这句同意对应哪个计划?如果它其实是在同意退出,能否拿来运行程序?同一封回复读了两次,要不要执行两遍?

上一课解决了消息怎样保存、何时进入独立上下文。消息可以到达以后,接下来要让宿主识别它的用途:谁发起哪一份请求,谁可以决定,哪一份回复能够改变它的状态。 这就是本课的团队协议(Team Protocols)。

READERBOUND IDENTITYREQUESTreq-1PLANLEADBOUND IDENTITYRESPONSEreq-1PLANreq-1PENDING发起方请求卡片接收方回复卡片协议记录
队友提出这一次计划

reader准备先读expected.txt,再运行main.py比较结果。宿主分配req-1,明确发起人和接收者,计划正文随后不能改写。

计划审批
  1. 队友提出这一次计划:reader准备先读expected.txt,再运行main.py比较结果。宿主分配req-1,明确发起人和接收者,计划正文随后不能改写。
  2. 请求保存后,Lead才能处理:请求进入Lead邮箱,同一ID登记为pending。收据表示成功保存,尚未有批准决定。
  3. Lead保存批准或拒绝的回复:回复携带plan_response、req-1、布尔决定和反馈。回复已入箱,请求方还没有匹配,所以请求仍pending。
  4. reader核对关联,才推进状态:宿主检查ID、类型、信封角色和已保存决定。全部一致时,这份请求才变为approved;拒绝则为rejected。
  5. 计划批准,运行仍要问宿主:协作门控允许继续这份计划;run_main还会进入原有执行权限检查。宿主拒绝时,程序仍不会运行。
  6. 匹配不会替你执行计划:模型后续提出工具调用,宿主才处理。重复消费同一回复只得到duplicate,不会因为再读一遍信就重跑程序。
协商关闭
  1. Lead请求结束reader的工作:宿主创建shutdown_request,关联新的请求ID。请求退出与成员已经退出,是两个不同事实。
  2. 忙碌时先留着请求:reader仍有活跃轮次时,确认返回deferred。没有关闭确认,也不强制终止当前线程。
  3. 空闲边界才给出确认:本轮工作结束后再处理。reader也可以拒绝并说明理由;同意关闭时先把回复保存到Lead邮箱。
  4. 确认保存后,本地成员标为closed:关闭发生在同一把状态锁内,之后新交付会被拒绝。Lead此刻尚未匹配回复,请求记录仍pending。
  5. Lead匹配同一个关闭请求:收到shutdown_response后核对ID、类型与角色,原请求才更新为approved。计划回复不能关闭另一份请求。
  6. 两边都有各自能证明的结果:成员closed证明本地不再接轮次;请求approved证明Lead已匹配确认。进程崩溃后的恢复需要另外设计。

先把“同意”放回它所属的请求

宿主为这份计划分配 req-1,保存类型 plan、发起人 reader、目标 lead、计划正文和 pending 状态。发出去的请求卡片带 plan_request、req-1 与正文;回复卡片带 plan_response、req-1、布尔决定与反馈。

request_id 回答“哪一次”,type 回答“哪一种”,外层信封的 sender 与 recipient 回答“谁给谁”。三个条件都要对上。模型在正文里声称自己是 Lead,并不会改变宿主绑定的发信角色。

本课的宿主还记住已经成功保存的回复内容。匹配时检查这份决定是否真的经过回复入口保存,避免另一段普通文本恰好拼出协议字段就推进状态。这是同进程中的一致性约束;成员身份仍由本地宿主管理,没有变成网络身份认证。

计划正文登记后不能改写。若把 req-1 的正文换成新方案,却保留旧批准,ID 看似匹配,批准的对象已经变了。需要修改计划时,旧决定保留,另开一份请求。

保存回复和完成协议之间,还有一步

Lead 保存了批准回复,此时 reader 的收件箱里多了一条记录,而原请求仍是 pending。等 reader 的宿主在下一次边界读取并核对它,才更新为 approved;拒绝则更新为 rejected。

观察位置此刻能证明什么
请求收据请求已成功保存并登记,正在等待决定
回复收据决定已经保存到请求方邮箱
请求终态请求方已经匹配同一份回复

这几项没有合并成一个“成功”标志。邮箱写入失败,不登记一个永远等不到对方的请求;回复保存失败,不提前写出批准。请求方重复消费相同记录时,只返回 duplicate,不再次推进状态。

对同一请求重复调用回复入口,也不该不断写信。本课相同决定返回 already_sent,试图把原来的同意改成拒绝则报错。重复投递和改变决定,是两件不同的事。

批准计划,为什么程序还是没运行?

计划审批解决的是“团队是否按这个方案继续”。程序执行解决的是“宿主是否允许这次调用”。

本课在 run_main 前增加一个协作检查:本次请求 ID 必须属于当前队友,类型是 plan,状态是 approved。通过后,调用还会进入之前的宿主权限检查。于是可以实际跑出三种结果:

  1. 计划批准,宿主也允许:真实运行 main.py,得到输出,再和读取的预期比较。
  2. 计划拒绝,宿主允许:在计划门控处停止,没有启动程序。
  3. 计划批准,宿主拒绝:通过计划检查,仍在执行许可处停止。

匹配函数本身不执行任何程序。即使它返回 approved,也只是让后续入口拥有一份可以检查的协作状态。

“请退出”也不能直接写成“已经退出”

关闭协议把方向反过来:Lead 发 shutdown_request,reader 回复 shutdown_response,携带另一份请求 ID。计划回复不能用来匹配关闭请求。

reader 正在工作时,本例返回 deferred,先不发送关闭确认。等活跃轮次结束,在空闲边界再处理。它可以拒绝,也可以同意;同意时先保存确认,再把本地成员标成 closed。旧的交付入口看到 closed,会拒绝新轮次。

成员本地 closed 与 Lead 的请求 approved 仍然是两个时刻。 Lead 只有读到并匹配确认后,才知道这次关闭协商已完成。我们没有强制终止线程,也没有因为发出请求就宣称已经完成收尾。

没有回复时,系统会一直等吗?

当前实现没有自动超时,未收到有效回复就保留 pending。同方向同类只能有一份未决请求,会话最多创建四份;它限制积压,不用不断请求模型来猜对方是否同意。

如果业务要求超时、取消或重新提案,应给它们单独的状态和关联规则。比如超时以后才到的批准,不能悄悄把新计划放行。本课先把一份请求从登记到终态的路径做清楚,再扩展生命周期。

实践篇将 open_request、reply_request、match_response 三个函数和各自的内部图放在一起,断点可以停在“已保存但未匹配”这个位置。

社区课程 Team Protocols以计划和关闭为例解释请求与响应的关联;Claude Code 的官方团队文档也区分计划批准与执行权限,并说明队友可以接受或拒绝关闭。本文的角色检查、有限请求表和协作门控是独立 Python 教学实现,不据此推断产品内部源码。