跳转至

08 · Adapter、会话与上下文组装

Adapter SPIRegistrySession ResumeSkills InjectionStreaming Events

一句话结论: Adapter 是 Paperclip 与具体 Agent runtime 之间的完整协议层:不仅启动模型,还声明配置、诊断、会话、技能、事件和 UI/CLI 展示能力;Heartbeat 负责治理与上下文,Adapter 负责把它翻译成目标运行时能执行的形式。

1. 为什么不能只抽象一个 execute(prompt)

Claude Code、Codex、Gemini CLI、HTTP bot 的差异远超过请求格式:

  • 有的支持结构化 streaming event,有的只有 stdout;
  • 有的用 session ID resume,有的需要完整 transcript;
  • 有的从固定全局目录读 skills,有的支持额外目录参数;
  • 有的能报告 token/模型/工具,有的只能估算;
  • 有的在本机运行,有的是云端 job 或 gateway;
  • stop 可能是进程组信号、HTTP cancel 或 provider API;
  • config form 和输出展示也不同。

所以 Adapter contract 必须覆盖“执行生命周期”,而不仅是模型调用。

2. 三面一体的 adapter package

一个完整 adapter 通常有三类出口:

adapter package
├── server/
│   ├─ execute
│   ├─ environment test
│   ├─ session codec
│   └─ skills / model / instructions capabilities
├── ui/
│   ├─ metadata
│   ├─ config fields
│   └─ stdout / transcript parser
└── cli/
    └─ terminal event formatter

这让同一 adapter 在:

  • Server 中可执行;
  • Board UI 中可配置并正确展示运行;
  • CLI watch 中可读地输出事件。

某些通用 adapter 的 UI/CLI 实现在主 ui/cli registry 内,外部 adapter 则可以通过插件路径贡献。

3. ServerAdapterModule 能力面

从 Server 看,一个 adapter module 大致声明:

能力 作用
metadata/config schema 类型、显示信息、必填配置、secret ref
execute(context) 启动或调用 Agent runtime
testEnvironment() 在正式运行前诊断二进制/凭据/网络
session codec 解码、编码、清理 provider-specific session state
skills sync/materialization 把选定技能放到运行时可发现的位置
model metadata/profile 模型列表、能力、成本或路由提示
local JWT support 是否接受 Paperclip 注入的短期身份
instructions bundle support 怎样交付 AGENTS/系统说明等文件
runtime feature flags 结构化事件、工具、workspace 等能力

调用方应按 capability 分支,而不是通过 adapter type 字符串猜行为。

4. Registry 把内建与外部实现统一

当前 registry 能装载:

  • 通用 process、http;
  • claude_local、codex_local;
  • cursor_cloud、cursor_local;
  • gemini_local、grok_local;
  • Hermes local/gateway;
  • OpenClaw gateway;
  • opencode_local、pi_local;
  • 插件贡献的 external adapter。
flowchart LR
  CFG["agent.adapterType"] --> REG["Server Adapter Registry"]
  BUILTIN["built-in modules"] --> REG
  PLUGIN["plugin adapter store"] --> REG
  REG --> MOD["resolved ServerAdapterModule"]
  MOD --> HB["Heartbeat executeRun"]

Registry bootstrap 还会 reconcile metadata,使旧 Agent 配置、disabled adapter 和插件状态在启动时保持一致。

5. Context 的内容来自控制平面

Adapter 不应该自己重新查询整套业务。Heartbeat 组装 execution context,典型内容包括:

身份

  • company/agent/run/issue/project IDs;
  • responsible user;
  • 短期 Paperclip API token;
  • invocation source/reason。

工作

  • Issue 标题、描述、状态、优先级;
  • 父任务、目标祖先和项目背景;
  • wake comment/interaction;
  • 当前 checkout 和 execution policy。

运行环境

  • cwd/workspace identity;
  • environment/provider reference;
  • runtime services;
  • allowed file roots;
  • stop/timeout callbacks。

能力与约束

  • materialized skills;
  • MCP/tool gateway config;
  • secret/env projections;
  • trust preset;
  • instructions bundle;
  • log/progress/event/session callbacks。

Adapter 的职责是把这些字段翻译成 CLI args、env、stdin、HTTP body 或 provider job。

6. Prompt 不是一段字符串,而是分层上下文

可把组装顺序理解为:

flowchart TB
  SYS["Paperclip runtime protocol"]
  ID["Agent identity + org instructions"]
  GOAL["Company / goal / project context"]
  TASK["Issue + thread + wake reason"]
  ENV["Workspace / tools / skills / secrets contract"]
  TRUST["Trust boundary + governance constraints"]
  SESSION["Adapter session continuation"]

  SYS --> OUT["Adapter-ready input"]
  ID --> OUT
  GOAL --> OUT
  TASK --> OUT
  ENV --> OUT
  TRUST --> OUT
  SESSION --> OUT

这比每次把完整数据库 dump 进 Prompt 更可控。部分内容通过文件、env 或 MCP 暴露,不一定全部变成 token。

7. Instructions bundle

Agent 可以拥有 instructions path/bundle,包含 AGENTS.md 等运行说明。Server 会:

  1. 验证路径或数据库保存的 bundle;
  2. 选择适配目标 runtime 的文件布局;
  3. 在 workspace/scratch 中安全 materialize;
  4. 避免路径逃逸或覆盖不允许的文件;
  5. 把可见位置传给 adapter。

这样组织角色和操作协议不必硬编码在 adapter 中。

8. Skills 注入因运行时而异

共同逻辑是:运行前解析 selected company skills,创建受控快照;具体注入由 adapter 负责。

Adapter 类型 可能的注入方式
Claude local 临时目录/符号链接并通过额外目录参数暴露
Codex local 对接其 skills 目录与选择规则
其他 CLI materialized runtime skills + runtime-specific config
不支持 skills capability 明确为不可用,不能静默假装成功

Skill 的 frontmatter name + description 先作为路由索引,正文按需加载,降低初始上下文。

9. Session:跨 Run 的 Provider 连续性

sequenceDiagram
  participant H as Heartbeat
  participant S as agent_task_sessions
  participant C as Session Codec
  participant A as Adapter

  H->>S: load(company, agent, adapter, taskKey)
  S-->>H: encoded provider state
  H->>C: decode + validate workspace compatibility
  C-->>H: resume input
  H->>A: execute(resume input)
  A-->>H: result + new session state
  H->>C: encode / sanitize
  H->>S: upsert latest state

Session codec 的存在避免 Heartbeat 知道每家 provider 的内部字段。

9.1 Session 与 cwd 的关系

本地 coding agent 的会话可能隐含 cwd/repository。如果新的 execution workspace 不兼容,盲目 resume 可能让 Agent 引用旧文件或错误分支。因此 Server/adapter 会记录 session workspace context,并在复用前校验。

9.2 Reset session

UI/CLI/API 可以显式 reset Agent session。Reset 不删除历史 run,只清理下一轮的 continuation state,这是排查“上下文污染”的重要运维动作。

10. 结构化事件质量有层级

不同执行引擎提供的观测质量不同:

ACP / 原生结构化协议
  > CLI JSON event stream
  > stdout/stderr parser
  > generic process/http logs

结构化程度越高,UI 越能可靠显示:当前工具、assistant snippet、token usage、tool arguments/result、stop reason。低层 adapter 只能提供 best-effort parser,不应把解析失败等同运行失败。

Adapter event 最终被规范化后进入:

  • heartbeat run events;
  • run log store;
  • live WebSocket;
  • UI transcript parser;
  • CLI formatter。

11. Process metadata 为什么要尽早上报

本地 adapter 一启动子进程,就通过 callback 保存:

  • PID;
  • process group ID;
  • command/runtime identity;
  • provider job/session reference;
  • start time。

这使 stop、timeout、hot restart adoption 和 orphan reaping 有证据。等进程结束才记录 PID 没有恢复价值。

12. Stop 与取消

取消路径依据 adapter/runtime:

  • local process:优先对进程组发送温和信号,再有界升级;
  • provider job:调用取消 API;
  • HTTP/gateway:发送协议级 interrupt;
  • 无原生取消:标记 control-plane cancellation,并防晚到结果覆盖终态。

无论底层能否立即停,Run 的状态写入仍用 CAS,晚到 callback 不能复活 terminal run。

13. 用量与成本归一化

Adapter result 可以报告:

  • input/output/cache tokens;
  • model/provider/biller;
  • provider cost;
  • subscription/local execution 等非 token 账务信息。

Server 把它们归一成 run usage 和 cost events,并连接 company/agent/issue/project/goal。Adapter 不直接修改预算;预算服务消费规范化成本事实。

14. 本地与远程 Git 的边界

Paperclip 明确避免让 adapter 假设远程环境能直接操作本地 Git remote:

  • local execution workspace 是持久化边界;
  • SSH 可用 Git bundle/import/export;
  • sandbox 可用 archive upload/download;
  • provider 插件可以定义自己的同步协议;
  • 核心执行路径不应擅自 git push。

这是安全与可移植性共同驱动的决定:远程环境可能没有用户凭据,也不应默认拥有发布权限。

15. 新增 Adapter 的真实工作量

不仅是实现 execute:

  • shared adapter type/metadata;
  • Server module 和 config validation;
  • environment test;
  • session codec;
  • event/usage/process callbacks;
  • stop/cancel;
  • skills/instructions/workspace capabilities;
  • UI config fields 与 transcript parser;
  • CLI formatter;
  • registry bootstrap;
  • secret refs 和 redaction;
  • integration tests、packaging、docs。

外部 adapter package 可以沿插件路径接入,但仍要满足相同的 Server contract。

16. 设计边界与风险

运行时能力不齐

统一 contract 无法让所有 provider 瞬间同质化。调用方必须按 capability 降级,UI 也要诚实展示缺失事件/usage/session。

Parser 是信任边界

UI parser 处理 adapter 输出。仓库使用 sandboxed parser worker 等机制降低 parser 卡死/污染主 UI 的风险,但 external parser/同源插件仍需信任评估。

Session 可能隐藏状态

Provider session 是便捷连续性,也是难以完全审计的外部状态。Paperclip 通过 task key、workspace compatibility、reset 和 context snapshot 尽量约束它。

17. 可复用经验

  1. Agent runtime SPI 应覆盖执行、诊断、会话、技能、事件和取消。
  2. 用 capability 描述差异,不要让调用方通过 type 名猜特性。
  3. 控制面组装受治理上下文,Adapter 只做 runtime translation。
  4. Provider session 要有 codec、task key、workspace 兼容检查和 reset。
  5. Process identity 越早持久化,恢复越可靠。
  6. 成本由 Adapter 报告、Server 归一、预算服务执行,职责要分开。

源码入口

  • packages/adapter-utils/src/types.ts 及 package exports
  • server/src/adapters/registry.ts、services/adapter-registry-bootstrap.ts
  • packages/adapters/*/src/server/
  • packages/adapters/*/src/ui/
  • ui/src/adapters/registry.ts
  • cli/src/adapters/registry.ts
  • packages/db/src/schema/agent_task_sessions.ts
  • docs/adapters/creating-an-adapter.md

Adapter 要在一个真实执行位置工作。下一章:09 · Workspace、Environment 与隔离。