Paperclip 里的 Agent 不是常驻进程:心跳、唤醒与一次运行链路
很多人第一次配 Paperclip 的 agent,会拿常驻服务的思路去理解它:以为建好一个 agent,它就在后台一直转,任务来了自动接。然后就会遇到一堆看起来矛盾的现象——明明 agent 状态是 active,却半天没动静;改了提示词,下一次运行好像还带着上一轮的记忆;把超时时间填成 0,本以为是”不允许运行”,结果它跑得比谁都久。
这些都不是 bug,是因为 Paperclip 的执行模型和常驻进程根本不是一回事。官方文档《How Agents Work》开篇就把话说死了:agent 是”醒来、干活、再睡回去”的 AI 员工,它们不持续运行,而是在被称作 heartbeat(心跳)的短窗口里执行。
搞懂这一句,上面那些现象就全串起来了。这篇按官方的《How Agents Work》和《Agent Runtime Guide》两份文档,把一次心跳从触发到落盘的完整链路、以及每一环你能配什么,逐段捋一遍。
一次 heartbeat 到底发生了什么
《How Agents Work》给的执行模型是六步:
- Trigger——某个东西把 agent 叫醒(调度、任务分派、被提及、手动触发);
- Adapter invocation——Paperclip 调用这个 agent 配置的适配器;
- Agent process——适配器拉起 agent 运行时,文档举的例子是 Claude Code CLI;
- Paperclip API calls——agent 自己去查分派、认领任务、干活、更新状态;
- Result capture——适配器把输出、用量、成本和会话状态捕获回来;
- Run record——Paperclip 把这次运行的结果存下来,供审计和调试。
《Agent Runtime Guide》从运维视角又描述了一遍同一件事,措辞更贴近实际发生的顺序:启动配置好的适配器(比如 Claude CLI 或 Codex CLI)→ 把当前的提示词和上下文给它 → 让它一直干到自己退出、超时或被取消 → 存下结果(状态、token 用量、错误、日志)→ 实时更新界面。
两份文档合起来,有三个点值得单独拎出来:
第一,真正在干活的是适配器拉起来的那个进程,不是 Paperclip 本身。Paperclip 负责叫醒、给上下文、收结果。
第二,agent 是主动调 Paperclip API 的一方。它醒来之后自己去查”有什么分派给我”、自己认领、自己更新状态,而不是被动接收一条指令。
第三,每次心跳都会留下一条 run 记录,带状态、错误文本、stderr/stdout 摘录、token 用量。这条记录是后面所有排查的起点。
四种唤醒来源,以及重复唤醒为什么不会叠加
《Agent Runtime Guide》列了四种唤醒方式:
| 唤醒类型 | 含义 |
|---|---|
timer | 按调度间隔(文档举例:每 5 分钟) |
assignment | 有工作被分派/签出给这个 agent 时 |
on_demand | 手动唤醒(按钮或 API) |
automation | 系统触发,文档写明是留给后续自动化用的 |
关键一句在下面:如果一个 agent 已经在运行中,新的唤醒会被合并(coalesced),而不是再拉起一次重复运行。
这条对排查很有用。如果你连点几次手动唤醒、又赶上定时器到点,看到的仍然只是一次运行,那是设计如此。同样地,“agent 状态 active 却没动静”通常也不神秘:active 在文档里的定义只是”准备好接收心跳”,不等于此刻正在跑;正在跑的状态叫 running。
对应的心跳策略在 agent 运行时设置里配,字段是这几个:
enabled:是否允许调度心跳;intervalSec:定时器间隔,填 0 表示禁用;wakeOnAssignment:分派时唤醒;wakeOnOnDemand:允许 ping 式的按需唤醒;wakeOnAutomation:允许系统自动化唤醒。
运行时注入了哪些环境变量
这部分是写自定义 agent 时最实用的一段。《How Agents Work》说明,每个 agent 在运行时都会被注入一组环境变量:
| 变量 | 说明 |
|---|---|
PAPERCLIP_AGENT_ID | agent 的唯一 ID |
PAPERCLIP_COMPANY_ID | agent 所属的公司 |
PAPERCLIP_API_URL | Paperclip API 的基础 URL |
PAPERCLIP_API_KEY | 用于 API 鉴权的短期 JWT |
PAPERCLIP_RUN_ID | 当前这次心跳的 run ID |
当这次唤醒有具体触发源时,还会额外注入一组上下文变量:
| 变量 | 说明 |
|---|---|
PAPERCLIP_TASK_ID | 触发这次唤醒的 issue |
PAPERCLIP_WAKE_REASON | 唤醒原因,文档给的例子是 issue_assigned、issue_comment_mentioned |
PAPERCLIP_WAKE_COMMENT_ID | 触发这次唤醒的具体评论 |
PAPERCLIP_APPROVAL_ID | 被处理掉的审批 |
PAPERCLIP_APPROVAL_STATUS | 审批结论,取值 approved、rejected |
注意 PAPERCLIP_API_KEY 是短期 JWT,不是长期密钥;换句话说,agent 侧的代码不该把它缓存到下一次心跳去用。第二组变量则决定了 agent 醒来后第一步该干什么——同样是被叫醒,issue_assigned 和 issue_comment_mentioned 显然要走不同分支。
内置适配器:跑的其实是你本机那个 CLI
《Agent Runtime Guide》列出的内置适配器有这些:
claude_local:跑你本地的claudeCLIcodex_local:跑你本地的codexCLIopencode_local:跑你本地的opencodeCLIcursor:以后台模式运行 Cursorpi_local:本地跑一个内嵌的 Pi agenthermes_local:通过@paperclipai/hermes-paperclip-adapter启动本地hermesCLIhermes_gateway:通过@paperclipai/hermes-paperclip-adapter/gateway调用一个已经在跑的 Hermes API 服务openclaw_gateway:连接一个 OpenClaw 网关端点process:通用 shell 命令适配器http:调用外部 HTTP 端点
另外还有外部插件适配器,通过适配器管理器或 API 安装,文档列的是 droid_local(跑本地 Factory Droid CLI,包名 @henkey/droid-paperclip-adapter)。
有一条前置假设必须记住:对本地 CLI 类适配器(claude_local、codex_local、opencode_local、hermes_local、droid_local),Paperclip 假定这些 CLI 已经在宿主机上装好并完成认证。它不负责替你装、替你登录。hermes_gateway 同理,假定 Hermes API 服务已在运行、Paperclip 服务器能访问到、并且配了 API key。文档还专门说明,旧的 @paperclipai/adapter-hermes-gateway npm 包只是一个已废弃的兼容垫片,适配器类型名仍然是 hermes_gateway。
适配器怎么选、各自的接法差异,另有专篇,可参考 五类适配器分别怎么接。
timeoutSec 填 0 和填负数,语义完全不同
本地适配器要配工作目录和执行限制,这几个字段的语义有坑:
cwd:工作目录;timeoutSec:每次心跳的最长运行时间。填0用的是目标(target)的默认值——本地/SSH 目标下没有适配器超时,沙箱目标下有 4 小时兜底;填负值则在所有环境下都关闭适配器超时,包括沙箱;graceSec:超时或取消之后,强杀之前留的缓冲时间;- 可选的环境变量和额外 CLI 参数。
所以开头那个”填 0 反而跑得没完没了”的现象,就是这条规则的直接后果:0 不是”不给时间”,是”用目标默认”。真要限制,就填一个正数。文档同时提到,agent 配置里有 Test environment,可以在保存前跑适配器相关的诊断。
提示词模板这块只有一个字段是推荐的:promptTemplate,每次运行都用(首次运行和续接会话都一样),支持 {{agent.id}}、{{agent.name}} 这类变量以及运行上下文的值。文档明确写了 bootstrapPromptTemplate 已废弃,新 agent 不要用,老配置虽然还能跑但应迁移到托管指令包(managed instructions bundle)体系。
工作目录这一环还牵扯到执行工作区的组织方式,见 执行工作区与 git worktree。
会话为什么会”记得”上一轮,以及什么时候该重置
《How Agents Work》讲的是机制:agent 通过会话持久化在多次心跳之间保持对话上下文,适配器在每次运行后把会话状态序列化下来(例如 Claude Code 的 session ID),下次唤醒时再恢复。文档给的收益是——agent 不用把所有东西重读一遍就记得自己在干什么。
《Agent Runtime Guide》讲的是运维口径:Paperclip 会给可续接的适配器存 session ID,下一次心跳自动复用;你也可以在上下文变陈旧或跑歪时重置会话。文档给的三个重置时机:
- 你大幅改了提示词策略;
- agent 陷在一个坏循环里出不来;
- 你就是想干干净净重来一次。
这也解释了”改完提示词第一次运行没生效”的困惑:会话是续接的,旧上下文还在。改了策略就顺手重置一次。
两张状态表:agent 状态 vs run 状态
这两套状态经常被混着看,实际上是两个维度。
agent 状态(来自《How Agents Work》):
| 状态 | 含义 |
|---|---|
active | 准备好接收心跳 |
idle | 处于活跃状态,但当前没有心跳在跑 |
running | 心跳进行中 |
error | 上一次心跳失败 |
paused | 手动暂停,或超预算被停 |
terminated | 永久停用 |
单次 run 的状态(来自《Agent Runtime Guide》):queued、running、succeeded、failed、timed_out、cancelled。每条 run 还带错误文本与 stderr/stdout 摘录、适配器能提供时的 token 用量与成本,以及完整日志(日志单独存放,针对大输出做过优化,本地/开发环境下落在配置的 run-log 路径里)。
注意 paused 的两种成因里有一条是超预算——所以 agent 突然不干活了,别只盯着适配器,先看预算是不是被打满了。至于”心跳该有却没有”这一类问题,另有 Agent 不干活:心跳、看门狗、卡住怎么查 专门讲排查路径。
运行反复失败时,按这个顺序查
文档给了固定顺序,照着走就行:
- 检查适配器命令是否可用(
claude/codex/opencode/hermes装了没、登录了没); - 确认
cwd存在且可访问; - 先看 run 的错误信息和 stderr 摘录,再看完整日志;
- 确认超时时间不是设得太低;
- 重置会话再试;
- 如果它在反复产生糟糕的更新,先把 agent 暂停掉。
典型失败原因文档也列了:CLI 没装或没认证、工作目录不对、适配器参数或环境变量写错、提示词太宽泛缺约束、进程超时。
还有一条 Claude 专属提醒:如果适配器环境变量或宿主机环境里设了 ANTHROPIC_API_KEY,Claude 会走 API key 认证而不是订阅登录。Paperclip 在环境测试里把这个报成警告,不是硬错误。这条容易背刺——你以为在用订阅额度,实际在按 API 计费。
三种运行节奏,按风险选
文档把常见操作模式归成三类:
| 模式 | 配置要点 |
|---|---|
| 简单自治循环 | 开定时唤醒(举例 300 秒)+ 保持分派唤醒;用聚焦的提示词模板,要求 agent 在同一次心跳内就动手、留下可持久的进展、被卡住时标明负责人和动作;再看日志迭代配置 |
| 事件驱动循环 | 关掉定时器或设很长的间隔,保留分派唤醒;用子 issue、评论、按需唤醒来做交接,而不是靠轮询 agent、会话或进程 |
| 安全优先循环 | 短超时、保守提示词、盯错误并及时取消、发现漂移就重置会话 |
最小可用清单也很短:选适配器 → 本地适配器设好 cwd → 可选加 promptTemplate 或用托管指令包 → 配心跳策略 → 手动触发一次唤醒 → 确认运行成功且会话与 token 用量都记上了 → 看实时更新并迭代。
一次运行内部具体经历了哪些语义环节,超出了这两份文档的范围,见 执行语义:一次运行到底做了什么。
什么时候这套模型不适合你
有几种情况值得先想清楚再上手。
要求毫秒级响应的场景不适合。 心跳是短窗口执行,定时器最快也是按秒配的间隔,加上唤醒合并机制,天然不是给同步请求用的。
不能接受本地无沙箱执行的环境要慎重。 文档在安全与风险一节写得很直白:本地 CLI 适配器在宿主机上不加沙箱运行。由此文档提醒三件事——提示词里写的指令是有实际后果的、配置的凭据和环境变量是敏感的、工作目录权限很重要。官方给的原则是尽量最小权限起步,除非确有必要,别把密钥暴露在通用的可复用提示词里。
如果你的 agent 状态必须完全靠外部系统维护,会话续接反而是负担。 它默认帮你保留上下文,而你可能每次都想要干净起点,那就得主动管理重置节奏。
最后是这两份文档没覆盖的部分。适配器各自的参数细节、process 和 http 两类通用适配器怎么对接自建 agent、审批与预算的具体机制、执行工作区的隔离方式,都在各自的专篇里,本文只到”驱动链路”这一层为止。文档标注 automation 唤醒是留给后续自动化能力的,具体形态官方文档在这两份里没有展开。
延伸阅读
- 从头读起:Paperclip 是什么:一个自己不跑 Agent 的控制平面,怎么管住一整家 AI 公司
- 本专题共 40 篇,完整分组目录见专题页
- Paperclip 执行语义:一次运行到底保证了什么(幂等、锁、终态与超时的定义)
- Paperclip 执行工作区与 git worktree:一个 issue 到底跑在哪份代码上
本文依据 Paperclip 官方仓库(github.com/paperclipai/paperclip,MIT 协议)的 docs/ 用户文档
与 doc/ 下的规范、运维与连接器手册整理,核对日 2026-08-17。
我们没有部署或运行过 Paperclip,因此不涉及界面外观与操作手感;
部分规范文档描述的是目标架构而非当前实现,文中已就地标注,不构成对实际行为的保证。
请以仓库最新内容为准。