Paperclip 里 Agent 建好了却不干活?从心跳、看门狗到卡住任务的排查顺序
Agent 建好了,adapter 选了,任务也建了,然后就没有然后了:看板上那条 issue 安静地待在 todo,Agent 状态是 idle,活动日志干干净净。也可能是另一种——Agent 确实跑了一轮,留下一句”我先梳理一下方案”,任务状态没动,之后再没人来接。还有更隐蔽的第三种:一整棵任务树的叶子全停了,有的 done、有的 blocked、有的挂在 in_review,看着挺完整,其实没有任何一条路径会再推动它。
这三种症状都叫”Agent 不干活”,成因却完全不同。最容易走弯路的做法是一上来就翻 run 日志——很多时候根本没有 run 可翻,因为 Agent 压根没被唤醒。
下面这套顺序按官方文档写明的机制整理,从”能不能被叫醒”开始一层层往下走。我们没有安装运行过 Paperclip,只讲文档写明的机制与配置。
先明确:Agent 不是常驻进程
《Agent Runtime Guide》第一句就把这事说死了:Paperclip 里的 Agent 不持续运行,而是以**心跳(heartbeat)**方式运行——由一次唤醒触发的短执行窗口。一次心跳做五件事:启动配置好的 adapter(比如本地 claude 或 codex CLI)、交付当前提示词与上下文、让它工作到退出/超时/被取消、存下结果(状态、token 用量、错误、日志),并实时更新 UI。
所以”不干活”必须先拆成两个问题:是没有心跳,还是心跳跑了没产出。两条排查路径几乎不重叠。
唤醒来源文档只写了四种:
| 唤醒方式 | 触发条件 |
|---|---|
timer | 按配置间隔调度(例如每 5 分钟) |
assignment | 有工作被指派/checkout 给该 Agent |
on_demand | 手动唤醒(按钮或 API) |
automation | 系统触发,面向后续自动化 |
还有一条容易被误判成”丢唤醒”的规则:Agent 已在运行时,新唤醒会被合并(coalesce)而不是另起一次重复运行——连点几次手动唤醒只看到一次 run,是设计如此。
第一层:这个 Agent 还能被唤醒吗
先看 Agent 自身状态,《Managing Agents》给的表是:
| 状态 | 含义 |
|---|---|
active | 可以接收工作 |
idle | 活动中,但当前没有心跳在跑 |
running | 正在执行心跳 |
error | 上一次心跳失败 |
paused | 被手动暂停,或被预算暂停 |
terminated | 永久停用,不可逆 |
两个坑值得单拎。一是 paused 有两种来路:除手动 POST /api/agents/{agentId}/pause 外,文档写明Agent 用满月度预算的 100% 时会被自动暂停。“上周还好好的 Agent 这周全线安静”,第一个该看的不是日志而是预算。二是 POST /api/agents/{agentId}/terminate 永久且不可逆,没有撤销路径。
Agent 状态没问题,再看心跳策略这几项:enabled(是否允许调度心跳)、intervalSec(定时间隔,0 等于关闭定时唤醒)、wakeOnAssignment、wakeOnOnDemand、wakeOnAutomation。常见的自伤组合是为省 token 把 intervalSec 设成 0,同时 wakeOnAssignment 也关了——这时除手动 ping 外没有任何入口。文档给的事件驱动配法是:定时关掉或设长间隔,但保留 wake-on-assignment,靠子任务与评论交接而不是轮询。
第二层:唤醒了但 run 起不来
如果有 run 记录但状态是 failed 或 timed_out,问题在执行层。文档的排查清单是有顺序的:确认 adapter 命令可用(claude / codex / opencode / hermes 已安装并登录)→ 确认 cwd 存在且可访问 → 看 run 的 error 与 stderr 摘要、再看完整日志 → 确认 timeoutSec 不是太低 → 重置会话重试;如果它在反复产生糟糕的更新,先暂停这个 Agent。对本地 CLI 类 adapter,Paperclip 的假设是这些 CLI 已在宿主机装好并完成认证,它不负责替你登录。
超时语义要看清:timeoutSec 为 0 是用目标环境默认值(本地/SSH 无 adapter 超时,沙箱有 4 小时兜底),负值会在所有环境(含沙箱)禁用 adapter 超时;graceSec 是强杀前的宽限时间。
另有一类失败根本不该被派发出去。「预派发配置校验」这道闸门在所有权与 checkout 解决之后、真正派发 run 之前校验必需的 secret/env 绑定,缺绑定时产出的是**「配置不完整」的显式阻塞**,而不是一次注定失败的 run。所以看到的若是一条点名了缺失绑定的等待状态而非失败日志,那是机制在按设计工作,补绑定即可。
第三层:心跳跑了,但它没接到活
run 成功、任务却纹丝不动,这一层对着心跳协议逐步核。《Heartbeat Protocol》定义的是每次唤醒都要走的固定流程:
Step 1 GET /api/agents/me
Step 3 GET /api/companies/{companyId}/issues?assigneeAgentId={yourId}&status=todo,in_progress,in_review,blocked
Step 5 POST /api/issues/{issueId}/checkout
Step 8 PATCH /api/issues/{issueId}
第 3 步是收件箱。 查询条件写死了 assigneeAgentId={yourId} 与那四个状态,结果按优先级排序。任务若没指派给它,或状态落在四个之外(比如停在 backlog),根本不会出现在收件箱里——这是”任务建了没人接”最常见的一种。执行语义文档把 backlog 明确定义为停放态而非派发态:带 assignee 的 backlog 不会因为有指派人就去叫醒对方(反过来,创建接口收到 assignee 却没给显式 status 时会默认置为 todo)。
第 4 步是取活顺序。 先做 in_progress,其次是因评论被唤醒时的 in_review,最后才是 todo;blocked 除非能解开否则跳过。“我建的新任务它不做,反而在啃老任务”往往不是 bug。
第 5 步 checkout 最容易卡。 动手前必须先 checkout:
POST /api/issues/{issueId}/checkout
Headers: X-Paperclip-Run-Id: {runId}
{ "agentId": "{yourId}", "expectedStatuses": ["todo", "backlog", "blocked", "in_review"] }
已被自己 checkout 过则成功;属于另一个 Agent 则返回 409 Conflict——文档加粗写了两遍:停下来换一个任务,绝不重试 409。
409 有时来自陈旧的锁。执行语义文档区分两个字段:checkoutRunId 是谁当前拥有该 issue 的执行权,executionRunId 是当下真正活着的哪次运行。一次 run 只在自身非终态期间持有 checkoutRunId,走到终态时收尾流程必须清掉仍指向它的锁列;但Paperclip 不会清理或接管由非终态 run 持有的锁。所以陈旧清理跑过之后的 409 就该当成真冲突:有活属主、状态/指派人不匹配、有未解决 blocker,或还有生效中的闸门。
第 8 步的状态变更同样必须带 X-Paperclip-Run-Id 头,且永远不要手动 PATCH 成 in_progress,那是 checkout 的职责。
第四层:接了活但停在半路
这一层看运行活性(run liveness):它是记在心跳 run 上的元数据,不是 issue 状态也不替代状态机,只描述最近一次运行的结果,取值有 completed、advanced、plan_only、empty_response、blocked、failed、needs_followup。
关键规则只有一条:只有 plan_only 和 empty_response 会触发有界的活性续跑唤醒。续跑会在 issue 仍活跃、预算与执行策略允许时重新唤醒同一个 Agent 处理同一个 issue,次数记在 continuationAttempt 上。
那句”我先梳理一下方案”对应的多半就是 plan_only。心跳协议对此的要求是:issue 可执行就在同一次心跳里采取具体动作,不要停在计划上,除非它要的就是做计划。续跑也不是无限的,它既不会把 issue 标成 blocked 也不会标成 done;自动续跑用尽后 Paperclip 会留一条审计评论交给人或经理处理。看到”一条系统评论然后没下文”,就是额度用完在等你。
还有两条判据很实用。一是只把工作区准备好不算真实进展——持久进展必须体现为工具/动作事件、issue 评论、文档或工作产物修订、活动日志、提交或测试。二是专治”我在后台跑着呢”:未被管理的本地进程不是持久动作路径,用 &、nohup 起的 shell 任务、本地轮询循环、adapter 子进程都不能让 issue 保持存活,除非它被持久化成一次 run,或配上监视器、计划唤醒、blocker、委派 issue 之一。
第五层:整棵树停住了,没人叫醒
前几层都是单个 Agent 或单个任务的问题,这一层最难发现:每个叶子看着都”有交代”,整体却不再前进。执行语义文档为此定义了非终态 issue 的活性契约:健康的标准是能回答”接下来什么推动它”,而不需要人读完整个线程重建意图。合法的「动作路径原语」只有八条——活跃 run;能投递给责任 Agent 的排队唤醒或续跑;有类型的执行策略参与者(executionState.currentParticipant);等待特定应答者的 issue 线程交互或关联审批;一次性 issue 监视器(executionPolicy.monitor.nextCheckAt);assigneeUserId 指定的人类属主;一级 blocker 链且其未解决叶子自身健康;写明属主与动作的显式恢复动作。
一个非终态 issue 若既无活跃执行路径、又无显式等待路径、也无恢复路径,它就是 stalled。 把这八条当 checklist 逐条比对,比盯着状态标签有用。
崩溃重启后的恢复规则分两种、形状一样:滞留的已指派 todo(run 死了、重启后无排队唤醒)排一次自动的指派恢复唤醒,滞留的已指派 in_progress(活的 run 消失、也无排队续跑)排一次自动续跑唤醒。两者共用同一条兜底——若这次恢复唤醒也结束而 issue 依然滞留,就把它移到 blocked 并打开或更新一条显式恢复动作。自动恢复只有一次,别指望它反复救场。
但有个反直觉的例外:有意的等待不是丢失的 run。被陈旧度闸门以 issue_continuation_waiting_on_review 取消的续跑是刻意停放,若有真实等待目标会被转成一级依赖等待后自行恢复,不该按滞留处理。
三个都叫「看门狗」,别搞混
《Task Watchdog》开头就提醒:内部有三个概念共用这个词。
| 概念 | 看什么 | 什么时候触发 |
|---|---|---|
| 任务看门狗 | 一个被配置的 issue 及其非看门狗后代 | 整棵被观察子树都停了,且这次停止是新的 |
| 静默活跃运行看门狗 | 单个仍在运行的进程 | 进程在阈值窗口内没有任何输出 |
| 活性恢复 | 任何无活路径、Agent 拥有的 in_progress issue | 周期性恢复扫描中检出停滞工作 |
后两个每个项目自动运行,不用你配;任务看门狗是按 issue opt-in,文档明确写了没有全局的”监控一切”模式。所以只有第三种症状——整棵树”看起来都结束了”而你不信——才轮到你自己动手配。
任务看门狗怎么配、管得到哪
它只有三个字段:被观察 issue(由你编辑的那条隐式决定)、看门狗 Agent(同公司、可调用,不能是已暂停、已终止或被预算阻塞的)、可选的自定义指令(能收窄关注点,不能扩大权限)。一条被观察 issue 最多一个活跃看门狗;换 Agent 或改指令会作废此前已审阅的状态。
GET /api/issues/:issueId/watchdog
PUT /api/issues/:issueId/watchdog { "agentId": "...", "instructions": "..." | null }
DELETE /api/issues/:issueId/watchdog
PUT 是 upsert;DELETE 是禁用而非硬删除,表里保留历史供审计;三个路由都要求写权限。
扫描发生在服务启动时、每轮心跳结束时和任何改变子树的变更之后:遍历时排除所有 originKind = 'task_watchdog' 的 issue 及其下方(所以它不会触发自己);只要还有 queued/running/scheduled_retry 的 run、排队唤醒或计划重试就算存活;否则算一个 SHA-256 停止指纹,与 lastReviewedFingerprint 相同则抑制,新指纹才建评审子 issue 并唤醒看门狗。
被唤醒后它读默认授权书加你的指令,核心要求是把每个停止的叶子当作一项待核验的主张去对照证据验,不能把”我做不到”或”在等审批”当成自动成立。它的权限边界由服务端在路由层强制:只能在子树内活动,不得冒充正式审批,不得再建看门狗或唤醒自己。
一张按顺序走的定位表
| 顺序 | 现象 | 先看什么 | 判据 |
|---|---|---|---|
| 1 | 完全没有 run 记录 | Agent 状态、intervalSec / wakeOnAssignment | 预算打满会自动暂停;intervalSec=0 等于关闭定时 |
| 2 | 有 run 但 failed / timed_out | CLI 是否装好登录、cwd、stderr、timeoutSec | 本地 CLI 需宿主机已认证 |
| 3 | 有等待状态而非失败 run | 缺失的 secret/env 绑定 | 预派发校验产出「配置不完整」阻塞 |
| 4 | run 成功但任务没动 | 是否停在 backlog、是否指派给它 | 收件箱只查那四个状态 |
| 5 | 反复 409 | 有无活属主、状态/指派人是否匹配、有无未解 blocker | 陈旧锁清理后即真实冲突,绝不重试 |
| 6 | 只留计划或空响应 | run liveness 值与 continuationAttempt | 仅 plan_only/empty_response 触发续跑 |
| 7 | 声称”后台跑着” | 有无 run/监视器/唤醒/blocker/委派 issue | 未管理的本地进程不是动作路径 |
| 8 | 单 issue 停滞 | 对照八条动作路径原语逐条排除 | 三类路径皆无即 stalled |
| 9 | 整棵树静止 | 是否该 opt-in 配任务看门狗 | 自动机制只覆盖静默运行与活性恢复 |
什么时候这套不适用
文档自己列了任务看门狗不该用的场景:监控单个运行进程是否静默、对停滞 issue 做活性恢复(这两件都是自动的);正式审批或安全敏感的下游动作;替代执行策略里有类型评审阶段的人类评审人。如果你要的其实是”这件事做完了叫我一声”,文档给的答案是用 routine,或用 continuationPolicy: wake_assignee 的 issue 线程交互。
边界也要认清。运行活性不改变 issue 状态机,别指望调它能修好一条状态填错的 issue。看门狗触发的前提是”停止是新的”,指纹没变就会被抑制。静默活跃运行看门狗那边,continue 只是对当前证据的短确认,默认重新武装窗口 30 分钟;已知的、有时限的安静期建议用 snooze 而非 continue。
审批卡住、预算打满导致的连锁停摆,各有自己的排查路径。可以接着看 Agent 增删改与配置 了解生命周期与预算设置的落点,看 执行语义:一次运行到底做了什么 补齐状态机与恢复模型的完整定义,看 五类适配器分别怎么接 排执行层的接入问题,看 预算与 token 工资超支怎么控 处理预算自动暂停这条线。
这套顺序从”能不能被叫醒”开始,是因为越靠前的层越便宜:看一眼 Agent 状态和心跳配置是秒级的事,翻完整日志、比对指纹、读子树证据要高一个量级。
延伸阅读
- 从头读起:Paperclip 是什么:一个自己不跑 Agent 的控制平面,怎么管住一整家 AI 公司
- 本专题共 40 篇,完整分组目录见专题页
- Paperclip 审批卡住怎么办:从审批类型、生命周期到用 API 把队列查清楚
- Paperclip 的预算怎么设:给 AI 员工发「token 工资」,80% 报警、100% 自动停
本文依据 Paperclip 官方仓库(github.com/paperclipai/paperclip,MIT 协议)的 docs/ 用户文档
与 doc/ 下的规范、运维与连接器手册整理,核对日 2026-08-17。
我们没有部署或运行过 Paperclip,因此不涉及界面外观与操作手感;
部分规范文档描述的是目标架构而非当前实现,文中已就地标注,不构成对实际行为的保证。
请以仓库最新内容为准。