Paperclip 源码拆解¶
从一个任务如何被创建、认领、唤醒、执行、治理、恢复并最终交付出发,读懂一个多 Agent 控制平面的完整工程实现。
22 个主题章节 · 111 个数据库 schema 文件 · 1,229 个测试文件 · 全链路 Mermaid 图解
研究对象
本站基于本地 paperclip-master 快照进行静态源码分析。快照没有 .git 元数据;核心包清单版本为 0.3.1。本文优先相信实际代码,其次才是仓库内文档,因此会主动标出两者不一致之处。
为什么值得拆 Paperclip¶
很多 Agent 项目停留在“调用模型 + 工具循环”。Paperclip 处理的是更难的后一半:当几十个不同运行时的 Agent 共同工作时,怎样让任务不被重复领取、上下文可以续接、预算能真正止损、权限不会越界、失败后仍有恢复路径,而且人随时能介入。
它的价值不只在某个框架或 API,而在一组能迁移到其他 Agent 系统的工程命题:
- 任务所有权如何成为数据库事实,而不是 Prompt 中的一句约定;
- 一次唤醒如何成为可持久化的运行记录,并具备排队、合并、重试与取消语义;
- 执行环境如何和规划对象解耦,既支持本地目录,也支持 worktree、SSH 与远程 sandbox;
- 权限、审批、预算和工具风险如何进入执行热路径,而不是事后审计;
- 进程退出、服务器重启、静默运行与悬空任务如何被系统性收敛;
- 前端如何消费高频运行事件,又不把服务端和浏览器拖进刷新风暴。
全站路线图¶
flowchart LR
A["产品与领域模型"] --> B["API 与数据契约"]
B --> C["Issue 原子认领"]
C --> D["Heartbeat 执行闭环"]
D --> E["Adapter 与 Workspace"]
E --> F["权限与治理"]
F --> G["实时 UI 与扩展系统"]
G --> H["恢复、测试与发布"]
H --> I["端到端源码追踪"]
| 模块 | 你会得到什么 | 代表章节 |
|---|---|---|
| 产品与建模 | 理清“控制平面”边界、公司/目标/项目/任务/Agent 的关系 | 01–03 |
| 接口与状态 | 看懂服务启动、共享契约、Issue 状态机和原子 checkout | 04–06 |
| 执行内核 | 逐步追踪 wakeup、run、session、adapter、workspace | 07–09 |
| 安全治理 | 理解 actor、RBAC/ABAC、审批、预算、成本、审计 | 10–11 |
| 数据与体验 | 看懂 Drizzle/PostgreSQL 与 WebSocket/React Query 的协作 | 12–13 |
| 可扩展能力 | 分析插件、MCP、密钥、Skills、Teams 与自动化 | 14–17 |
| 交付与运维 | 研究文档/产物、CLI、部署、恢复、CI 与发布 | 18–21 |
| 综合串联 | 用一条真实任务时序把所有部件重新拼起来 | 22 |
规模画像¶
这不是一个“几个文件的 Demo”。对快照进行静态计数后,可以看到它已经具备大型 TypeScript 产品的形态:
| 指标 | 数量 | 说明 |
|---|---|---|
| TypeScript / TSX 文件 | 3,202 | UI、Server、CLI、Packages 合计 |
| 测试文件 | 1,229 | 单测、集成、E2E、脚本契约测试 |
| Markdown 文件 | 408 | 用户文档、设计稿、计划、技能与团队模板 |
| SQL 文件 | 214 | 其中 210 个位于 Drizzle migrations 目录 |
| 数据库 schema 文件 | 111 | 每个文件通常对应一组强相关表 |
| Server 路由文件 | 58 | REST、认证、OpenAPI、工具网关等 |
| Server 业务服务文件 | 216 | 不含测试文件 |
| UI 源文件 | 1,278 | 页面、组件、API client、context 与工具函数 |
最能说明复杂度的不是文件数量,而是核心文件的体积:heartbeat.ts 约 1.9 万行,routes/issues.ts 约 1.2 万行,issues.ts 约 9 千行,tool-access.ts 约 7.8 千行。本文不会按文件从头复述,而会把这些巨型服务拆成可理解的状态机和时序。
本站的拆解方式¶
每章尽量回答同一组问题:
- 它解决什么问题? 先给业务约束,不从类名和函数名开始。
- 数据事实放在哪里? 指出表、字段、唯一索引或状态常量。
- 请求如何走? 从 route 追到 service、transaction、event 和 UI cache。
- 为什么这样设计? 解释并发、幂等、故障恢复和安全边界。
- 代价是什么? 标出巨型服务、单机事件总线、规格与实现漂移等现实限制。
- 能复用什么? 把 Paperclip 的实现提炼成其他 Agent 系统可采用的模式。
文中所有“源码入口”均使用仓库相对路径和符号名。在线上游分支可能继续变化,因此比固定行号更耐用。
先记住四条主线¶
Company → Goal → Project → Issue → Run → Work Product
规划对象与执行对象分开:Issue 表达意图,Run 表达一次尝试,Work Product 表达可验证输出。
assigneeAgentId + checkoutRunId + executionRunId
谁负责、哪次运行取得 checkout、哪次运行持有执行锁是三个相关但不同的问题。
Wakeup Request → Heartbeat Run → Adapter → Workspace → Liveness → Recovery
唤醒不是直接 spawn();它先被持久化、去重、排队,再进入一套带清理和恢复的生命周期。
Actor → Authorization → Budget/Approval → Mutation → Activity Log
每次关键动作都要回答:谁做、能不能做、是否要人批、是否超预算、怎样留下证据。
适合谁读¶
- 在做 Agent orchestration、coding agent 平台或 AI 内部工具的工程师;
- 想把“Prompt 约定”升级成“可验证系统不变量”的架构师;
- 需要评估 Paperclip 二次开发、插件开发或部署风险的团队;
- 想系统学习 TypeScript Monorepo、Express、React Query、Drizzle 与 PostgreSQL 的读者。
如果只想快速了解,读第 01、02、06、07、10、22 章即可;如果准备改代码,建议按顺序读完并配合源码导航图。
下一步:阅读指南会说明如何选择路线、怎样核对源码,以及本站对“事实、推断、规格”的标注约定。