Paperclip 里 Agent 建好了却不干活?从心跳、看门狗到卡住任务的排查顺序

2026-08-17

Agent 建好了,adapter 选了,任务也建了,然后就没有然后了:看板上那条 issue 安静地待在 todo,Agent 状态是 idle,活动日志干干净净。也可能是另一种——Agent 确实跑了一轮,留下一句”我先梳理一下方案”,任务状态没动,之后再没人来接。还有更隐蔽的第三种:一整棵任务树的叶子全停了,有的 done、有的 blocked、有的挂在 in_review,看着挺完整,其实没有任何一条路径会再推动它。

这三种症状都叫”Agent 不干活”,成因却完全不同。最容易走弯路的做法是一上来就翻 run 日志——很多时候根本没有 run 可翻,因为 Agent 压根没被唤醒。

下面这套顺序按官方文档写明的机制整理,从”能不能被叫醒”开始一层层往下走。我们没有安装运行过 Paperclip,只讲文档写明的机制与配置。

先明确:Agent 不是常驻进程

《Agent Runtime Guide》第一句就把这事说死了:Paperclip 里的 Agent 不持续运行,而是以**心跳(heartbeat)**方式运行——由一次唤醒触发的短执行窗口。一次心跳做五件事:启动配置好的 adapter(比如本地 claudecodex 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 等于关闭定时唤醒)、wakeOnAssignmentwakeOnOnDemandwakeOnAutomation。常见的自伤组合是为省 token 把 intervalSec 设成 0,同时 wakeOnAssignment 也关了——这时除手动 ping 外没有任何入口。文档给的事件驱动配法是:定时关掉或设长间隔,但保留 wake-on-assignment,靠子任务与评论交接而不是轮询。

第二层:唤醒了但 run 起不来

如果有 run 记录但状态是 failedtimed_out,问题在执行层。文档的排查清单是有顺序的:确认 adapter 命令可用(claude / codex / opencode / hermes 已安装并登录)→ 确认 cwd 存在且可访问 → 看 run 的 error 与 stderr 摘要、再看完整日志 → 确认 timeoutSec 不是太低 → 重置会话重试;如果它在反复产生糟糕的更新,先暂停这个 Agent。对本地 CLI 类 adapter,Paperclip 的假设是这些 CLI 已在宿主机装好并完成认证,它不负责替你登录。

超时语义要看清:timeoutSec0 是用目标环境默认值(本地/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,最后才是 todoblocked 除非能解开否则跳过。“我建的新任务它不做,反而在啃老任务”往往不是 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 状态也不替代状态机,只描述最近一次运行的结果,取值有 completedadvancedplan_onlyempty_responseblockedfailedneeds_followup

关键规则只有一条:只有 plan_onlyempty_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_outCLI 是否装好登录、cwd、stderr、timeoutSec本地 CLI 需宿主机已认证
3有等待状态而非失败 run缺失的 secret/env 绑定预派发校验产出「配置不完整」阻塞
4run 成功但任务没动是否停在 backlog、是否指派给它收件箱只查那四个状态
5反复 409有无活属主、状态/指派人是否匹配、有无未解 blocker陈旧锁清理后即真实冲突,绝不重试
6只留计划或空响应run liveness 值与 continuationAttemptplan_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 官方仓库(github.com/paperclipai/paperclip,MIT 协议)的 docs/ 用户文档 与 doc/ 下的规范、运维与连接器手册整理,核对日 2026-08-17。 我们没有部署或运行过 Paperclip,因此不涉及界面外观与操作手感; 部分规范文档描述的是目标架构而非当前实现,文中已就地标注,不构成对实际行为的保证。 请以仓库最新内容为准。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。