知识点思维导图
36 个知识节点
项目实战(12) - 心跳机制:AI 员工如何「上班打卡」
读完后,你应能完成以下任务:
- 绘制“项目实战(12) - 心跳机制:AI 员工如何「上班打卡」 / 为什么 Agent 不是「一直在跑」?”的关键对象与数据流,解释“Agent 平时是「睡着」的。”,并用源码位置、日志或 Trace 标注证据。
- 为“项目实战(12) - 心跳机制:AI 员工如何「上班打卡」 / 种触发方式”设计正常与异常输入,验证“💡 这 5 种触发,决定了 Agent 醒来时拿到的 PAPERCLIP_WAKE_REASON 是什么(见第 06 章环境变量)。”,输出首个偏差位置与回归测试结果。
- 实现“项目实战(12) - 心跳机制:AI 员工如何「上班打卡」 / 心跳协议:醒来后的 9 个步骤”的最小代码或配置,检验“这是 Agent 和 Paperclip 之间的核心契约。”,输出命令、结果与 Diff,并说明不适用边界。
本章目标:彻底搞懂心跳(Heartbeat)。说清它的 5 种触发方式、心跳协议的 9 个步骤、Agent 如何跨心跳「记住」上下文。这是理解整个系统行为的钥匙。
如果说前面都是铺垫,这一章就是 Paperclip 的心脏。 **理解了心跳,你就理解了 AI 员工的一切行为。 **
一、为什么 Agent 不是「一直在跑」?
先纠正一个直觉。 你可能以为 AI 员工像个常驻进程,24 小时待命。 **不是。 **
Agent 平时是「睡着」的。 它们在心跳里醒来——一个个短暂的执行窗口,由 Paperclip 触发。 干完这一轮活,就回去睡觉。
回顾第 01 章的「记忆碎片」模型:Agent 像电影里那个没有持久记忆的男主, 单次很强但不记事。 心跳就是反复把它叫醒、塞给它「这是你、这是你在干的事、接下来做这个」的便利贴。
为什么这么设计?
- 省钱:不干活时不烧 token。
- 可控:每次唤醒都是一个有边界的执行单元,便于记录花费、审计行为。
- 解耦:Paperclip 不需要维持长连接,Agent 跑在哪都行。
二、种触发方式
什么情况下 Agent 会被叫醒?
| 触发 | 说明 | 典型场景 |
|---|---|---|
| Schedule(定时) | 周期性定时器 | 每小时巡检一次、每天总结日报 |
| Assignment(分配) | 有新任务分配给它 | CEO 派了个活给工程师 |
| Comment(评论) | 有人在评论里 @ 它 | 队友 @工程师 说「这个 PR 帮看下」 |
| Manual(手动) | 人在 UI 点「Invoke」 | 你想立刻让某 Agent 动一下 |
| Approval(审批结果) | 待审批被批准/拒绝 | 你批了 CEO 的雇人申请,CEO 被叫醒去执行 |
💡 这 5 种触发,决定了 Agent 醒来时拿到的
PAPERCLIP_WAKE_REASON是什么(见第 06 章环境变量)。Agent 会根据「为什么被叫醒」决定先干哪件事。
三、心跳协议:醒来后的 9 个步骤
每个 Agent 每次醒来, 都走同一套固定流程, 叫心跳协议(Heartbeat Protocol)。 这是 Agent 和 Paperclip 之间的核心契约。 逐步看:
3.1 Step 1:确认身份
GET /api/agents/me
拿到自己的 ID、公司、角色、命令链、预算。先搞清「我是谁」。
3.2 Step 2:处理审批跟进
如果 PAPERCLIP_APPROVAL_ID 有值(说明是被审批结果叫醒的),
先处理它:
GET /api/approvals/{approvalId}
GET /api/approvals/{approvalId}/issues
审批解决了相关任务就关掉它们,否则评论说明为什么还开着。
3.3 Step 3:拉取分配给我的任务
GET /api/companies/{companyId}/issues?assigneeAgentId={yourId}&status=todo,in_progress,in_review,blocked
结果按优先级排序。这就是我的「收件箱」。
3.4 Step 4:挑活
挑选规则有明确优先级:
- 先干
in_progress(进行中)的 - 然后是
in_review(被评论叫醒时) - 再是
todo - 跳过
blocked,除非你能解除阻塞 - 如果
PAPERCLIP_TASK_ID有值且分给了你,优先做它 - 如果是被 @提及叫醒,先读那条评论线程
3.5 Step 5:签出(Checkout)⭐
干任何活之前,必须先签出任务:
POST /api/issues/{issueId}/checkout
Headers: X-Paperclip-Run-Id: {runId}
{ "agentId": "{yourId}", "expectedStatuses": ["todo", "backlog", "blocked", "in_review"] }
- 如果已经是你签出的,幂等成功。
- 如果别的 Agent 持有它:返回
409 Conflict——停下,换个任务。绝不重试 409。
🔒 这一步是多 Agent 不打架的核心。它是原子操作:两个 Agent 抢同一个任务,只有一个成功。这就是第 03 章说的「单负责人」机制的执行点。
3.6 Step 6:理解上下文
GET /api/issues/{issueId}
GET /api/issues/{issueId}/comments
读任务详情和评论,往上读祖先任务,搞清「这活为什么存在」。 被某条评论叫醒就先找到那条评论,当成直接触发点。
3.7 Step 7:干活 ⭐
用自己的工具和能力完成任务。关键纪律:
- 任务可执行就在同一次心跳里采取具体行动,别只停在「计划」(除非任务本身就是要你做计划)。
- 把进展留在评论、文档、产物里(durable progress),并写明下一步动作再退出。
- 长任务/并行任务:创建子任务让 Paperclip 在它们完成时唤醒父任务,而不是傻等轮询。
- 需要董事会做选择/确认时:用
POST /api/issues/{issueId}/interactions创建交互卡片(如request_confirmation),别在 markdown 里让人打「yes/no」。
3.8 Step 8:更新状态
状态变更必须带 run ID 头:
PATCH /api/issues/{issueId}
Headers: X-Paperclip-Run-Id: {runId}
{ "status": "done", "comment": "做了什么、为什么。" }
如果被卡住:
PATCH /api/issues/{issueId}
Headers: X-Paperclip-Run-Id: {runId}
{ "status": "blocked", "comment": "什么被卡住、为什么、需要谁来解除。" }
3.9 Step 9:按需委派
给下属创建子任务:
POST /api/companies/{companyId}/issues
{ "title": "...", "assigneeAgentId": "...", "parentId": "...", "goalId": "..." }
子任务一定要设 parentId 和 goalId,
否则任务树断裂、追溯不回目标。
四、跨心跳记忆:Session 持久化
Agent 怎么「记得」上次干到哪? 靠 session 持久化。
适配器在每次心跳后把 session 状态(如 Claude Code 的 session ID)序列化存下来,
下次唤醒时恢复。
于是 Agent 保留完整上下文,不用重读一切。
(第 06 章讲过 claude_local 的实现细节:cwd 感知、失败自愈。
)
这就是「记忆碎片便利贴」的技术本体:心跳给即时上下文,session 给连续记忆,两者合起来让无记忆的 Agent 表现得像有记忆。
五、几条「铁律」(Critical Rules)
官方明确列出的硬规则,违反就会出乱子:
- 干活前永远先 checkout——绝不手动 PATCH 成
in_progress。 - 绝不重试 409——任务是别人的。
- 退出心跳前永远留评论——说明进行中的工作进展。
- 可执行的活当场就开始做——只有「计划类任务」才允许只产出计划。
- 留下清晰的下一步动作在任务上下文里。
- 长/并行任务用子任务,不要轮询。
- yes/no 决策用
request_confirmation卡片。 - 子任务永远设
parentId。 - 绝不取消跨团队任务——交回给你的经理。
- 卡住就上报(escalate)——用你的命令链。
六、常见错误
-
❌ Agent 重试 409 → 任务已被别人持有。重试是浪费心跳和预算,必须换任务。
-
❌ 只产出计划就退出 → 除非任务要的就是计划,否则要在同一心跳里采取具体行动。
-
❌ 干完不留评论/不更新状态 → 下次心跳(或队友)就不知道进展到哪,上下文断了。
-
❌ 用轮询等子任务完成 → 浪费心跳和预算。正确做法:建子任务,让完成事件去唤醒父任务。
-
❌ 委派时忘了
parentId→ 任务树断裂,追溯不回目标,协作流转也会断(第 08 章详述这个坑)。 -
❌ 被卡住默不作声 → 必须把 blocker 写进评论、置 blocked、上报经理。 「沉默地卡着」是最糟的状态。
七、最佳实践
- ✅ 把心跳协议当成给 Agent 的「岗位 SOP」:写 prompt/技能时就照这 9 步组织,Agent 行为会很稳。
- ✅ 每个心跳产出「可交付的durable进展」:评论、文档、代码,而不是脑子里的想法。
- ✅ 善用唤醒原因:根据
WAKE_REASON让 Agent 先处理最相关的事(被 @ 就先读那条线程)。 - ✅ 用子任务做并行:把大活拆成子任务分发,靠完成事件驱动,比轮询高效得多。
- ✅ 控制 @提及频率:每次 @ 都触发一次消耗预算的心跳,别滥用(第 08 章细讲)。
八、总结
- 为什么 Agent 不是「一直在跑」?:Agent 平时是「睡着」的。
- 种触发方式:💡 这 5 种触发,决定了 Agent 醒来时拿到的 PAPERCLIP_WAKE_REASON 是什么(见第 06 章环境变量)。
- 心跳协议:醒来后的 9 个步骤:这是 Agent 和 Paperclip 之间的核心契约。
- 跨心跳记忆:Session 持久化:于是 Agent 保留完整上下文,不用重读一切。
- 几条「铁律」(Critical Rules):绝不重试 409——任务是别人的。
- 常见错误:重试是浪费心跳和预算,必须换任务。
学完自测
选择所有正确答案;提交后逐项核对判断依据。