01 · 产品定位与拆解方法¶
一句话结论: Paperclip 不是“再包一层模型调用”,而是把 Agent 当成员、把任务当可竞争资源、把每次执行当可审计事务的组织控制平面。
1. 从产品口号还原工程问题¶
README 用一句很准确的话定位它:如果某个 Agent 运行时像“员工”,Paperclip 就像“公司”。这句话背后至少包含七个必须由软件解决的问题:
- 身份:谁是人,谁是 Agent,当前请求代表谁?
- 组织:Agent 向谁汇报,谁能委派、暂停或终止谁?
- 工作:任务处于什么状态,谁拥有它,依赖何时满足?
- 执行:什么时候唤醒哪个 Agent,在什么目录、环境和会话中运行?
- 治理:哪些动作必须审批,哪些工具可见,预算耗尽时能否硬停?
- 恢复:进程、服务器或上游失败后,怎样避免永久悬空或重复执行?
- 证据:人如何知道任务真的完成,而不是 Agent 只说“完成了”?
普通任务管理器主要处理第 3 项,普通 Agent SDK 主要处理第 4 项的一小部分。Paperclip 的主体恰恰在它们之间:把组织意图转成受约束、可恢复的执行。
2. “控制平面”到底控制什么¶
Paperclip 把系统划成两层:
flowchart TB
subgraph CP["Paperclip 控制平面"]
ID["身份与权限"]
WORK["目标 / 项目 / Issue"]
SCHED["Wakeup / Heartbeat / Recovery"]
GOV["审批 / 预算 / 审计"]
RUNTIME["Workspace / Environment / Tool Gateway"]
end
subgraph EP["外部执行面"]
CLAUDE["Claude Code"]
CODEX["Codex"]
GEMINI["Gemini / Grok / Pi"]
HTTP["HTTP / Process / Gateway Agent"]
end
CP -->|"任务、短期凭据、上下文、约束"| EP
EP -->|"事件、用量、产物、状态、会话"| CP
控制平面保存“应然”和“事实”:应该做什么、谁能做、一次执行是否仍然有效、消耗多少、是否需要批准。执行面负责真正调用模型和工具。
控制平面不等于完全不启动进程
本地 adapter 会由 Server 启动 CLI 子进程,runtime service 也可能由 Paperclip 管理。所谓“控制面而非执行面”是架构职责:Paperclip 不把某个模型 loop 写死为核心,它通过 adapter contract 管理多种运行时。
这解释了为什么 adapter-utils、adapter registry、session codec、workspace realization 都是一等模块,而不是散落在某个 runAgent() 函数里的 if/else。
3. 四大支柱怎样落到源码¶
README 的“四大支柱”不是纯营销分类,它们可以映射到明确的代码群。
| 支柱 | 产品含义 | 主要源码落点 |
|---|---|---|
| Agentic Task Manager | 意图、任务、评审与输出证据 | issues、comments、documents、work products、execution policy |
| Org Chart for Agents | 人与 Agent 的角色、汇报、权限和边界 | agents、memberships、authorization、approvals |
| Agent Employee Training | Skills、测试运行、团队模板与学习 | company skills、skills catalog、teams catalog、evals |
| Agentic OS | 多运行时、sandbox、MCP、密钥、成本和恢复 | heartbeat、adapters、workspace、tool gateway、secrets、recovery |
这四组能力共享同一个 Company 边界和活动日志,所以它们不是四个互不相干的产品。
4. 最重要的对象不是 Agent,而是 Company¶
Paperclip 支持一个实例运行多家公司。绝大多数业务表都有 companyId,绝大多数服务方法要求先给出 company scope,实时事件也按 company channel 发布。
这带来一个很重要的建模顺序:
Instance
└─ Company
├─ people / agents / memberships / permissions
├─ goals / projects / issues / documents
├─ budgets / approvals / secrets / tool policies
└─ runs / workspaces / events / audit
Agent 不是跨公司漂移的全局进程;它首先是某家公司的 principal 和 employee。跨公司读取失败时,很多路由倾向返回“not found”而不是暴露目标存在性。
5. 规划对象、执行对象和证据对象¶
Paperclip 没把所有东西塞进 Issue:
| 类别 | 代表对象 | 回答的问题 |
|---|---|---|
| 规划 | Goal、Project、Issue、Routine、Pipeline Case | 为什么做、做什么、怎样分解 |
| 执行 | Wakeup Request、Heartbeat Run、Session、Execution Workspace、Lease | 这一次由谁、在哪里、以什么上下文做 |
| 证据 | Activity、Run Event、Document Revision、Attachment、Work Product、Cost Event | 做过什么、花了多少、输出在哪里 |
这种三分法是读懂源码的关键。比如:
- Issue 是
done,不代表任何历史 run 必须保持running; - Run
succeeded,也不必然代表 Issue 已done,它可能只推进了一步; - 文件出现在 workspace 中,不等于它已经成为可远程审查的 artifact;
- 一次 wakeup 被合并,不等于相关业务事件消失,它仍可有持久化请求记录。
6. 产品承诺被写成哪些硬不变量¶
仓库的 AGENTS.md 和实现共同强调一组“不能只靠 Prompt”的规则:
6.1 公司隔离¶
创建、读取、更新都必须携带或推导 company scope;关联对象要再次验证同公司。
6.2 单一受让人¶
Issue 同时只有一个 assignee。责任所有权和当前执行锁分字段保存,checkout 用条件更新竞争。
6.3 原子认领¶
两个 Agent 同时认领,数据库只允许一个满足旧状态前置条件;另一个得到 409 或进入明确的 stale-lock adoption 分支。
6.4 审批门¶
聘用、策略、工具写操作等高影响动作不会因为 Agent 声称“已经获批”就放行,服务端要核对持久化审批状态、参数哈希和权限上下文。
6.5 预算硬停¶
预算不是仪表盘数字。达到硬阈值后,invokability 检查会阻断新运行,并触发暂停或收敛动作。
6.6 活动留痕¶
关键 mutation 同时产生 activity 或更细粒度的 audit/cost/run event,使“当前状态”与“为何到达当前状态”可以分别查询。
7. 为什么 Heartbeat 是核心抽象¶
Agent 不作为永远在线的线程存在,而在短暂执行窗口中醒来:
stateDiagram-v2
[*] --> Idle
Idle --> Queued: timer / assignment / mention / manual
Queued --> Running: claim queue slot
Running --> Running: tool / model / API work
Running --> Idle: succeeded + no continuation
Running --> Queued: retry / follow-up / completion wake
Running --> Paused: budget / governance / operator
Running --> Error: terminal failure
Heartbeat 让系统可以:
- 给每次执行独立的身份、成本和日志;
- 在会话层恢复上下文,而不是让进程永驻;
- 对同一 Agent 做排队、合并和并发控制;
- 服务器重启后依据数据库重建真实状态;
- 把“还有没有下一步”单独建模为 liveness。
8. 本站怎样拆解它¶
参考 dg-ai-notes 的写法,本文不以文件列表为纲,而以系统问题为纲。每个问题同时给出:
同时做三种横切:
- 垂直追踪:从 HTTP/UI 入口一路追到 service、DB、event 和 adapter;
- 状态还原:把分散的常量、字段和条件更新还原成状态机;
- 故障追踪:不只讲成功路径,还追 timeout、stale lock、restart、approval、budget 和 cleanup。
9. 当前实现的三个现实边界¶
9.1 巨型服务集中复杂度¶
heartbeat.ts、issues.ts、tool-access.ts 等文件非常大。优点是跨步骤不变量集中,代价是认知负担和修改风险很高。本文会按阶段拆分它们,而不是假装每个文件对应一个小职责。
9.2 事件总线仍是进程内实现¶
实时事件由 Node EventEmitter 发布,再通过 WebSocket 转发。它简单、低延迟,但没有持久化回放,也天然假定单服务进程;浏览器断线后通过 refetch 对账。
9.3 规格和代码会漂移¶
例如较旧架构文档提到 PGlite,但当前代码用 embedded PostgreSQL;插件规格也清楚标注当前 UI 扩展仍是 same-origin trusted JavaScript。评估能力时必须把“目标设计”和“已落地防线”分开。
10. 可复用经验¶
- 先把组织与资源边界写进 schema,再写 Agent Prompt。
- 把“任务”“执行尝试”“交付证据”建成不同对象。
- 为 every wake 建立持久化请求,为 every try 建立独立 run。
- 让预算、审批和授权出现在动作之前,而不是事后报警。
- 把恢复当成正常状态机,而不是 catch 中的一次补救。
源码入口¶
README.md:产品定位、四大支柱与核心承诺doc/GOAL.md、doc/PRODUCT.md:目标与产品边界doc/SPEC-implementation.md:实现级不变量docs/start/what-is-paperclip.md:控制面/执行面模型packages/shared/src/constants.ts:全局状态词汇
下一章把产品模型映射到代码结构:02 · Monorepo 分层与依赖方向。