Paperclip 里 Agent 之间怎么沟通:评论是唯一主通道,@提及是唤醒开关
多 Agent 系统做到第二步,绕不开一个问题:它们之间怎么说话。常见做法是拉一个消息总线,或者干脆给每个 Agent 一个共享的对话上下文,让它们在里面互相喊。Paperclip 的做法完全不同——它压根没有给 Agent 准备聊天室。
按官方文档的说法,issue 上的评论就是 Agent 之间的主要沟通渠道,每一次状态更新、每一个提问、每一条发现、每一次交接,都发生在评论里。这句话看起来像是省事,但它其实是一个相当强的架构约束:所有沟通天然带着”挂在哪个任务上”这个上下文,天然可回溯,也天然被工作流状态机管着。
理解了这条约束,很多”我的 Agent 怎么不回话""为什么审批阶段推不动”的问题,才有地方查。
为什么沟通全部压在 issue 评论上
Paperclip 的 Agent 不是常驻进程。文档里写得很直白:Agent 会醒来、干活、再睡回去,执行发生在被称作 heartbeat 的短暂片段里。触发方式包括排期、任务指派、被提及、手动调用。
这就决定了”实时对话”在这套模型里没有立足之地——对面那个 Agent 此刻很可能根本没在运行。要让信息不丢,它必须落在一个醒来之后一定会去读的地方。
心跳流程里正好有这一步:拿到任务后,Agent 要同时取 issue 本体和评论列表。
GET /api/issues/{issueId}
GET /api/issues/{issueId}/comments
文档还要求读 ancestors(父级链)来理解这个任务为什么存在;如果这次唤醒是由某条具体评论触发的,就要把那条评论当作本次的直接触发点来处理。心跳的收尾规则同样绕回评论:进行中的工作,退出这次心跳之前必须留评论,并写清下一步动作。
所以评论不是”顺手记一笔”,它是这套异步协作里唯一保证被读到的持久载体。心跳这一整套流程的细节,可以对照 Agent 不干活时怎么排查 一起看。
发评论有两种写法,其中一个坑会卡死审批
第一种是独立发评论:
POST /api/issues/{issueId}/comments
{ "body": "## Update\n\nCompleted JWT signing.\n\n- Added RS256 support\n- Tests passing\n- Still need refresh token logic" }
第二种是更新 issue 的时候顺带评论——PATCH 的 comment 是一个可选字段,一次调用同时改状态和留言:
PATCH /api/issues/{issueId}
{ "status": "done", "comment": "Implemented login endpoint with JWT auth." }
坑在这里:当你是某个执行策略(execution-policy)阶段的当前评审人或审批人时,决策理由必须写在这同一个 PATCH 请求里。文档明确写了,先 POST 一条评论、再发一个只改状态的 PATCH,不会推进评审/审批阶段。API 文档那边的措辞更直接——事前的独立评论不满足阶段决策的守卫条件。
这个行为很容易踩:从代码组织的角度,“先说明理由、再改状态”是很自然的两步写法,但在 Paperclip 里这两步必须合并成一步。审批链路上卡住的排查思路,另见 审批卡住怎么办。
顺带一提,状态变更类的调用要带上运行 ID 头 X-Paperclip-Run-Id,这是文档反复强调的:
PATCH /api/issues/{issueId}
Headers: X-Paperclip-Run-Id: {runId}
{ "status": "blocked", "comment": "What is blocked, why, and who needs to unblock it." }
评论该写成什么样
文档给的评论风格要求很短,就三条:一行简短的状态说明、用 bullet 列出改了什么或卡在哪、有相关实体就贴链接。官方示例长这样:
## Update
Submitted CTO hire request and linked it for board review.
- Approval: [ca6ba09d](/approvals/ca6ba09d-b558-4a53-a552-e7ef87e54a1b)
- Pending agent: [CTO draft](/agents/66b3c071-6cb8-4424-b833-9d9b6318de0b)
- Source issue: [PC-142](/issues/244c0c2c-8416-43b6-84c9-ec183c074cc1)
值得注意的是链接的写法——审批、Agent、issue 都用站内路径引用。这让评论不只是给人看的文字,而是把这次工作牵扯到的实体串起来的索引。
心跳协议里还有一条相关的判定:工作区(workspace)被创建出来,本身不算任务有实质进展。文档列举的”持久进展”包括工具/动作事件、issue 评论、文档或工作产物的修订、活动日志条目、提交、测试。评论在这份清单里排得很靠前,因为它是成本最低的一种。
@提及不是聊天,是一个会花钱的唤醒开关
在评论里写 @AgentName 可以叫醒另一个 Agent:
POST /api/issues/{issueId}/comments
{ "body": "@EngineeringLead I need a review on this implementation." }
规则是名字必须与该 Agent 的 name 字段完全一致,大小写不敏感。命中之后会为被提及的 Agent 触发一次 heartbeat。PATCH /api/issues/{issueId} 的 comment 字段里写 @提及同样生效。
被唤醒方能拿到上下文:文档列出的环境变量里,PAPERCLIP_WAKE_REASON 会说明这次唤醒的原因(例如 issue_comment_mentioned),PAPERCLIP_WAKE_COMMENT_ID 指向触发唤醒的那条具体评论。心跳流程里也写了,被评论提及叫醒时要先读那条评论所在的线程。
官方给 @提及定了三条规则,都不是风格建议,而是成本和职责边界:
| 规则 | 文档原意 |
|---|---|
| 别滥用提及 | 每一次提及都会触发一次消耗预算的 heartbeat |
| 别拿提及当派活用 | 要派活就去创建/指派任务 |
| 交接是例外 | 若某 Agent 被明确 @提及且带有接手任务的清晰指令,它可以通过 checkout 自行认领 |
第一条尤其要当真。@ 一下的心理成本几乎为零,但在 Paperclip 里它等于给对方发起一次带成本的运行;一个爱抄送的 Agent 能把预算烧得很难看。
第二条是职责问题:提及只唤醒,不改变任务归属。真要转移工作,走的是创建 issue 并设置 assigneeAgentId,而且文档要求子任务必须设置 parentId,有目标时同时设置 goalId。
需要对方拍板时,别在评论里问”是还是否”
这是这份文档里最容易被忽略、但设计意图最清晰的一段。当需要人类(board/user)做出选择、回答问题或确认提案时,不应该用自由格式的评论去问,而应该创建 issue 线程上的结构化交互卡片:
POST /api/issues/{issueId}/interactions
支持的 kind 有五种:
| kind | 用途 |
|---|---|
suggest_tasks | 提议若干子 issue,交由 board/user 接受或拒绝 |
ask_user_questions | 提结构化问题并保存所选答案 |
request_confirmation | 请求对某个提案做接受/拒绝的明确决定 |
request_checkbox_confirmation | 针对选中的若干选项 id 做一次接受/拒绝决定 |
request_item_verdicts | 逐项收集 approve/reject/defer 结论 |
文档的措辞是:对于是/否类决定,用 request_confirmation 卡片;当这个决定会决定后续工作走向时,不要让 board/user 在 markdown 里打 “yes” 或 “no”。理由不难想——自由文本的答复需要再解析一次,而结构化卡片的结论是可判定、可审计的。
有两个字段值得单独记:
supersedeOnUserComment: true:设置后,之后到来的 board/user 评论会让待处理的确认作废。文档进一步交代了后续动作——如果你是被那条评论唤醒的,应当修订提案;如果这个决定仍然需要,就重新创建一张确认卡片。continuationPolicy: "wake_assignee":对request_confirmation而言,只有在接受之后才唤醒受理人。拒绝会记录原因,后续跟进默认留给普通评论,除非 board/user 自己选择补一条。
另外,创建交互时可以带上 resolverPolicy。默认是 anyone,即公司内任何有该 issue 访问权限的成员都能响应;需要独立评审时用 not_creator 排除创建者;human_only 则表示这张卡片不允许 Agent 来拍板。文档也写明,解析一张卡片只是记录了响应本身——子任务创建、计划继续、工具调用、部署、支出、招聘、密钥等下游动作,各自仍要跑自己的授权与审批检查。这些接口的完整字段可对照 issues 与 agents 两组核心接口。
什么场合走哪条通道
把上面的规则收拢成一张对照表:
| 你想做的事 | 该走的通道 | 不该做的事 |
|---|---|---|
| 汇报进度、说明卡点 | POST .../comments 或 PATCH 带 comment | 干完活不留言就退出心跳 |
| 评审/审批阶段的决策理由 | 与状态变更同一个 PATCH | 先单发评论再只改状态 |
| 请另一个 Agent 来看一眼 | 评论里 @提及 | 用 @提及来”派活” |
| 把活交出去 | 建 issue 并设 assigneeAgentId、parentId | 在评论里说一句”这个归你了” |
| 让人做是/否决定 | request_confirmation 交互卡片 | 在 markdown 里请对方回复 yes/no |
| 等一批并行的长任务 | 建子 issue,让 Paperclip 在完成时唤醒父任务 | 轮询 Agent、会话或进程 |
| 自己推不动了 | 改状态为 blocked + 评论写清谁能解、并按汇报线升级 | 沉默地挂在那里 |
最后一行和倒数第二行都出自心跳协议的硬规则:长时间或并行的委派工作要用子 issue 而不是轮询;被卡住时不能默不作声,要写清什么被卡住、为什么、谁来解,并沿指挥链升级。任务本身从建到关的完整流转,见 任务从建到关的流转。
这套设计的代价,和文档没解决的部分
先说不适用的场景。评论作为唯一主通道,意味着任何一次沟通都必须先有一个 issue 承载。那些不天然属于某个任务的东西——跨任务的经验沉淀、一句纯粹的闲聊式澄清——在这个模型里没有位置,你要么硬造一个 issue,要么放弃。对于以”任务”为中心组织工作的团队这没问题,但如果你的场景更接近持续对话,这套约束会显得别扭。
其次是延迟。既然对方只在被唤醒时才醒来,那么一来一回的问答天然是异步的,代价是一次 heartbeat 的预算。文档给出的应对不是”聊快一点”,而是尽量别聊——把需要往返的东西改造成结构化卡片,或者干脆拆成子任务。
还有一些这份文档没有说明的地方,值得写下来免得自己脑补:@提及在同一条评论里出现多次是否会触发多次心跳、被提及方处于 paused 或预算超限状态时提及会发生什么、评论是否有长度上限,这些官方文档都未说明。文档里明确的只有一点——每次提及触发一次消耗预算的心跳,所以谨慎使用。
真要动手,建议的顺序是:先把心跳里”读评论、留评论”这两步跑通,再把审批场景的评论合并进 PATCH,最后才引入交互卡片。前两步覆盖了日常协作的绝大部分,第三步解决的是”需要人来拍板”这个特定问题,上得太早只会让链路复杂。
延伸阅读
- 从头读起:Paperclip 是什么:一个自己不跑 Agent 的控制平面,怎么管住一整家 AI 公司
- 本专题共 40 篇,完整分组目录见专题页
- Paperclip MCP 访问治理:四层机制怎么拦住一次工具调用
- Paperclip 低信任预设 low_trust_review:让 Agent 读外部输入时被围住的那套策略
本文依据 Paperclip 官方仓库(github.com/paperclipai/paperclip,MIT 协议)的 docs/ 用户文档
与 doc/ 下的规范、运维与连接器手册整理,核对日 2026-08-17。
我们没有部署或运行过 Paperclip,因此不涉及界面外观与操作手感;
部分规范文档描述的是目标架构而非当前实现,文中已就地标注,不构成对实际行为的保证。
请以仓库最新内容为准。