Paperclip 内置 Agent 有哪些角色:briefs 与 learning 的注册表机制与自建方法

2026-08-17

搜”Paperclip 内置 Agent 都有哪些”的人,大多想要一张角色清单:像某些平台那样列十几个预设岗位,挑一个就能用。Paperclip 的答案比预期短得多——官方文档写明”最初的内置 Agent 是 briefslearning”,就这两个。

但清单短不代表这块没东西可讲。真正需要搞清楚的是它背后那套机制:为什么要专门造一个”内置 Agent”的概念,而不是让运维自己建一个普通 Agent 顶上去?答案藏在一句话里——内置 Agent 是能被后端代码用一个稳定的注册表键(registry key)查到的 Agent。普通 Agent 只有数据库 id,后端功能没法在不硬编码 id 的情况下找到”负责整理简报的那个 Agent”。内置 Agent 解决的就是这个引用问题。

这篇按官方文档把这套子系统拆开:它是什么、四层结构、四种状态、三个接口、怎么自己加一个,以及文档特意点名的几个运维坑。

内置 Agent 不是特殊的表,是带标记的普通行

文档的表述很直白:内置 Agent 是第一方的、公司维度(company-scoped)的 Agent,Paperclip 可以通过一个稳定的注册表键把它解析出来。它们在数据库里就是 agents 表里的普通行,和你自己建的 Agent 同一张表。

区别只在一处:它们的 metadata.paperclipBuiltInAgent 下带着一份不可变的元数据标记。服务层靠这个标记找 Agent,而不是靠数据库 id。

这个设计的直接后果是——标记不能被随便写。文档专门警告:不要通过通用的 Agent 创建/更新路由去写内置标记,Agent 服务会拒绝标记的新增、移除和篡改,除非内置服务显式开了口子。也就是说你没法把一个自己建的普通 Agent”提拔”成内置 Agent,反过来也不能把内置标记摘掉。想理解普通 Agent 的完整生命周期怎么走,可以对照看 Agent 增删改与配置

还有一处治理上的取舍值得注意:供给(provision)内置 Agent 需要和普通建 Agent 一样的 agents:create 权限,但它故意跳过了 requireBoardApprovalForNewAgents。文档给的理由是,内置 Agent 属于注册表拥有的系统性产能(registry-owned system capacity),不是临时起意的招聘。这一条对开了董事会审批开关的团队很关键:不要以为打开审批就能拦住所有新 Agent。

四层结构:注册表、标记、供给服务、路由

文档把这个子系统明确划成四层,各自的源文件位置也写了:

文件职责
注册表 Registryserver/src/services/built-in-agents.ts定义静态的 BuiltInAgentDefinition 列表
标记 Markerserver/src/services/built-in-agent-metadata.ts读写 metadata.paperclipBuiltInAgent
供给服务builtInAgentService(db)按公司查找、创建、更新、重置、强制要求内置 Agent
路由 Routesserver/src/routes/built-in-agents.ts暴露 list / provision / reset 接口

看这个分层能明白一件事:定义是静态写死在代码里的,不是运行时可配的数据。新增一个内置 Agent 必须改代码、发版本,不是在后台点几下就能加。这跟适配器(adapter)那套可以走插件从 npm 装的路子完全不同,两者的扩展模型差别可以对照 五类适配器分别怎么接

四种状态:not_provisioned / needs_setup / ready / paused

内置 Agent 的状态不是单独存的字段,而是从那条被标记的 Agent 行推导出来的。文档给了四种:

状态判定条件对调用方意味着什么
not_provisioned该公司/该 key 下不存在活跃的标记行还没供给,需要先调 provision
needs_setup行存在,但对应适配器类型的 adapter 配置不完整运维层面的配置问题
readyadapter 配置完整且 Agent 未暂停可以正常排任务
paused被标记的那行处于暂停状态计划任务/后台任务应记一条暂停告警并跳过排队

paused 这一档的处理方式是文档特意强调的:暂停的内置 Agent 不应该被当成”不存在”报错,而是把 Agent 连同一条 built_in_agent_paused 告警一起返回,让调用方把告警透传到日志或 API 响应里,同时不把它当成 ready 去排队。这个区分在排查”任务为什么没跑”的时候很有用——是没配置,还是配置好了但被人按了暂停,两条路径的处理完全不同。相关的排查思路可以接着看 Agent 不干活:心跳、看门狗、卡住怎么查

三个接口都是公司维度的

文档列了三条路由,全部挂在公司下:

GET  /api/companies/:companyId/built-in-agents
POST /api/companies/:companyId/built-in-agents/:key/provision
POST /api/companies/:companyId/built-in-agents/:key/reset

第一条列出注册表里的定义加上当前公司的状态;第二条为这家公司创建或配置内置 Agent,请求体接受可选的 adapterTypeadapterConfig;第三条是 reset。

reset 的语义值得单说:它恢复注册表拥有的展示字段与默认字段,但保留运维自己配的 adapter 设置。文档给的理由很实在——不希望运维在重置时丢掉本地模型或命令的配置。所以 reset 不是”恢复出厂”,是”把注册表那部分拉回来,你自己那部分不动”。

后端功能怎么正确地拿到内置 Agent

需要用内置 Agent 排任务的后端功能,文档要求统一走 builtInAgentService(db).requireBuiltInAgent(companyId, key),而不是自己去翻标记。缺失或配置不完整时它抛 HTTP 412,错误码是 built_in_agent_not_configured

{
  "error": "Built-in agent is not configured: digest",
  "code": "built_in_agent_not_configured",
  "details": {
    "code": "built_in_agent_not_configured",
    "key": "digest",
    "status": "needs_setup",
    "agentId": "..."
  }
}

后台任务的标准写法是先取、判告警、再唤醒:

const { agent, warning } = await builtInAgentService(db).requireBuiltInAgent(companyId, "digest");
if (warning) {
  logger.info({ warning }, "Skipping digest work because built-in agent is paused");
  return;
}

await heartbeatService(db).wakeup(agent.id, {
  source: "automation",
  triggerDetail: "system",
  reason: "Generate company digest",
});

这段里 digest 是文档举的假想例子,不是实际存在的第三个内置 Agent,别照抄当成现成能力用。

自己加一个内置 Agent:文档给的八步

文档把新增流程写成了一份可执行清单:

  1. server/src/services/built-in-agents.tsDEFINITIONS 里加定义;
  2. 挑一个稳定的小写 key,只用字母、数字、_-发布之后不要改名
  3. 设好 displayNameshortPurposedefaultInstructionsdefaultRole,以及至少一个 featureKeys
  4. allowedAdapterTypes 收到实际能跑的最小集合;
  5. 判断这个内置 Agent 需不需要非零的 defaultBudgetMonthlyCents
  6. server/src/__tests__/built-in-agents.test.ts 里加或改测试;
  7. 如果这个内置 Agent 会出现在 UI 或文档里,同一个 PR 里一起改;
  8. 从仓库根目录跑定向测试。
pnpm --filter @paperclipai/server exec vitest run src/__tests__/built-in-agents.test.ts src/__tests__/built-in-agent-routes.test.ts

定义长什么样,可以看文档给的 learning 这条真实定义:

{
  key: "learning",
  displayName: "Learning Agent",
  featureKeys: ["learning"],
  shortPurpose: "Maintains reusable company learning from completed work and recurring patterns.",
  defaultInstructions:
    "You are Paperclip's built-in Learning agent. Extract durable lessons from completed work, preserve useful patterns, and keep learning artifacts grounded in source context.",
  defaultRole: "general",
  allowedAdapterTypes: ["codex_local", "claude_local", "gemini_local", "opencode_local", "process"],
  defaultBudgetMonthlyCents: 0,
}

几个细节能从这条定义里读出来:defaultRole 用的是 generaldefaultBudgetMonthlyCents 是 0;allowedAdapterTypes 只列了五个本地类型,http 之类的不在其中。第 2 步”发布后不要改名”和第 4 步”收到最小集合”是两条硬约束——key 是外部功能引用它的唯一稳定句柄,改名等于砸掉引用;adapter 白名单开太宽,等于允许把这个内置 Agent 挂到根本跑不通的运行时上。

官方 PR 检查清单里藏着的判断标准

文档结尾给了一份 PR checklist,逐条读比读正文更能看出这套机制在防什么:

  • 注册表定义有稳定 key,且至少一个 feature key;
  • allowedAdapterTypes刻意收窄的;
  • 供给流程不要求董事会招聘审批;
  • 通用的 Agent 创建/更新路径伪造不了也删不掉标记;
  • 路由保持公司维度,且变更操作要写活动记录(activity);
  • 后台消费方用 requireBuiltInAgent(companyId, key),而不是自己开洞去查标记;
  • 暂停的内置 Agent 跳过计划任务/后台任务,并留下可查的日志或告警;
  • 定向测试通过。

运维时最容易踩的四条

文档单列了一节 Operational Notes,都是实打实的边界情况:

  • 每个公司每个 key 只允许一条活跃的内置行。 出现重复的活跃标记会被当成冲突,必须人工修复——系统不会自动挑一条用。
  • 已终止(terminated)的内置行在查找时被忽略。 所以误终止了不是死路,供给流程可以再建一条替代行。
  • reset 保留 adapter 设置,前面说过,别把它当恢复出厂。
  • 未知的标记 key 在启动时的对账(reconciliation)阶段会被忽略。 这条是为了防止被删掉的实验性内置 Agent 把服务启动搞崩——数据库里留着一条指向已不存在 key 的标记,服务照样起得来。

还有一条给功能开发者的:412 built_in_agent_not_configured 应当被当成运维配置问题,不是 500。 意思是别在功能代码里把它捕获成内部错误往上抛,该让运维看到”你还没配这个内置 Agent”。这类接口语义在 Paperclip 的其他 API 上也是一致的思路,可以顺带看 issues 与 agents 两组核心接口

什么时候这套机制帮不上忙

先把最现实的一条说清楚:如果你是奔着”开箱有一堆现成角色”来的,这里满足不了。 官方文档口径下的内置 Agent 只有 briefslearning 两个,而且文档只给出了 learning 的完整定义内容,briefs 只出现了键名。想要产品经理、测试、运维之类的角色,还是得自己建普通 Agent、自己写 instructions、自己接适配器。

第二条,内置 Agent 是给后端功能用的引用机制,不是给人用的模板机制。它的价值在于”某个后台任务需要一个固定角色的 Agent 时,能稳定地找到它”。如果你的场景里没有这种”代码要主动找某个 Agent”的需求,那这套东西对你没什么用。

第三条,扩展成本不低。加一个内置 Agent 要改注册表源码、加测试、同步 UI 和文档、走 PR,跟装个插件不是一个量级的事。文档也没有提供任何运行时注册的口子。

最后是文档没有覆盖的部分:briefs 这个内置 Agent 具体做什么、默认 instructions 是什么、允许哪些适配器,源文档都没写;featureKeys 除了作为定义字段被要求”至少填一个”之外,它在运行时到底被哪些功能消费、消费逻辑是什么,这份文档也没有展开。这些要么去读 built-in-agents.ts 的实际代码,要么等官方补文档,靠推断只会得到错的答案。

延伸阅读


本文依据 Paperclip 官方仓库(github.com/paperclipai/paperclip,MIT 协议)的 docs/ 用户文档 与 doc/ 下的规范、运维与连接器手册整理,核对日 2026-08-17。 我们没有部署或运行过 Paperclip,因此不涉及界面外观与操作手感; 部分规范文档描述的是目标架构而非当前实现,文中已就地标注,不构成对实际行为的保证。 请以仓库最新内容为准。

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