Files
pikasTech-agentrun/docs/reference/spec-v01-backend-adapter.md
T
2026-06-02 16:22:27 +08:00

132 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# v0.1 Backend Adapter 规格
Backend adapter 是 runner 与具体 Code Agent 工具之间的适配层。`v0.1` 的正式执行路径只保留 Codex/Codex-compatible backend profile;旧 MiniMax/OpenCode 直连路线废弃,不作为 fallback、judge backend 或 Queue 首版能力。Adapter 把输入、事件、错误和 terminal status 归一化为 AgentRun 公共模型。
## 在系统中的职责划分
- 根据 `backendProfile` 选择具体 backend profile`v0.1` 必须支持 `codex``deepseek``minimax-m3`,三者共享同一个 `codex-app-server-stdio` backend kind。
- 接收 manager 持久化后的 run、command 和 executionPolicy;不得自行扩大 workspace、network、approval 或 secret scope。
- 调用具体 backend,并把 backend 输出归一化为 AgentRun events。
- 负责 provider/auth/backend/protocol 错误到 failureKind 的映射。
- 负责 backend 输出和错误中的 credential redaction。
- 暴露 backend capability 给 manager,用于 `GET /api/v1/backends`
## Adapter 合同
第一版可以把 adapter 实现为 runner 进程内 Bun/TypeScript 模块,不要求独立 Deployment。无论实现形态如何,对 runner 的逻辑合同必须稳定:
```text
resolveProfile(profile) -> capability
prepare(run, command, secretProjection) -> executionContext
startTurn(executionContext) -> normalized event stream
interrupt(executionContext, command) -> backend interrupt attempt
finalize(executionContext) -> terminal status
redact(value) -> redacted value
```
Adapter 输入必须来自 manager 保存的 run/command 和 Kubernetes Secret projection;不得从 CLI 参数、临时环境变量或 Git 文件读取 provider credential 明文。`resolveProfile(profile)` 必须返回 profile、backendKind、protocol、transport、command、requiredSecretKeys 和 isolation policy;不得把 unknown profile 静默映射到默认 `codex`
Backend adapter 消费 RuntimeAssembly 中的 `BackendImageRef``ProfileRef` 结果,但不定义四要素字段;四要素权威见 [spec-v01-runtime-assembly.md](spec-v01-runtime-assembly.md)。
`v0.1` 的第一真实 adapter 是 Codex stdio adapter。它必须走 Codex CLI app-server JSON-RPC over stdioadapter 合同把 Codex 的 thread、turn、notification、tool lifecycle 和 stderr/exit 信息归一化为 AgentRun 标准 events。`codex``deepseek``minimax-m3` 只是该 adapter 的 profile/config/SecretRef 选择,不允许复制多套协议实现。
## HWLAB v0.2 Code Agent 能力吸收
Backend adapter 的第一阶段实现应吸收 HWLAB v0.2 已验证的 Codex stdio 行为,而不是另写一套不兼容协议。吸收只限通用 backend 执行能力,HWLAB 的自然语言业务路由、device-pod 授权和 Workbench 展示不进入 adapter。
| HWLAB v0.2 基线 | 参考入口 | Adapter 固化规则 |
| --- | --- | --- |
| Codex app-server JSON-RPC stdio | `internal/cloud/codex-stdio-session.ts``internal/cloud/codex-stdio-session-turn-state.ts` | 支持 `initialize``thread/start``thread/resume``turn/start`,并处理 app-server client request;未知请求要记录 unsupported error,不能静默等待。 |
| completed 判定 | `docs/reference/code-agent-chat-readiness.md` | 只有 Codex turn terminal completed 且 assistant reply 可聚合时才输出 completedassistant delta、item completed、stdout 或 transport close 不能单独完成。 |
| assistant stream 和 trace | `internal/cloud/code-agent-trace-store.ts``internal/cloud/codex-stdio-session-turn-state.ts` | assistant delta 只能作为 stream/progress 证据;每个非空 completed `agentMessage` item 必须输出一个 `assistant_message` event,保留 `itemId` 和顺序;`item/agentMessage:started``item/agentMessage:completed` 这类 lifecycle 不得额外持久化为 `backend_status`,避免同一消息在 Web/CLI trace 中重复渲染;最终 result reply 必须优先来自最后一个 completed `agentMessage` item,不能把 commentary/progress delta 与 final response 直接串接。event 必须保留 `threadId``turnId`、session 摘要和 redacted backend metadata。 |
| command/tool output bounded | `docs/reference/code-agent-chat-readiness.md``web/hwlab-cloud-web/app-trace.ts` | `tool_call``command_output` 必须记录状态、摘要、字节数、截断标记;完整大输出只能通过后续 log/artifact 引用。 |
| provider/profile 隔离 | `internal/cloud/code-agent-contract.ts` | `codex``deepseek``minimax-m3` 共享同一 backend kind,但必须使用 profile-scoped SecretRef、model/base-url/config 和 writable runtime home。 |
| Secret redaction | `internal/cloud/code-agent-trace-store.ts` | `OPENAI_API_KEY`、auth/config、token、password、kubeconfig、URL credential 不得进入 event、result、log 或 health。 |
## Backend Profile Registry
`GET /api/v1/backends` 和 adapter 内部 registry 至少报告以下 profile
| profile | backendKind | protocol | transport | command | v0.1 状态 |
| --- | --- | --- | --- | --- | --- |
| `codex` | `codex-app-server-stdio` | `codex-app-server-jsonrpc-stdio` | `stdio` | `codex app-server --listen stdio://` | 已有主闭环,必须保持默认兼容。 |
| `deepseek` | `codex-app-server-stdio` | `codex-app-server-jsonrpc-stdio` | `stdio` | `codex app-server --listen stdio://` | 已实现 profile;必须用独立 SecretRef 和 profile-scoped `CODEX_HOME` 完成真实联调。 |
| `minimax-m3` | `codex-app-server-stdio` | `codex-app-server-jsonrpc-stdio` | `stdio` | `codex app-server --listen stdio://` | 已实现 profile;必须用独立 SecretRef 和 profile-scoped `CODEX_HOME` 完成真实联调。 |
Registry 只表达能力和选择边界,不读取 Secret 值。Manager 负责校验 `backendProfile` 是否在 allowlist 内,并校验 `executionPolicy.secretScope.providerCredentials` 是否存在匹配 profile 的 SecretRefrunner 只为当前 run 选择的 profile 准备 Secret projection 和 runtime home。
## 标准事件
Adapter 输出给 runner 的 event 类型至少包括:
- `backend_status`backend 启动、模型/profile、能力和阶段状态,不包含 Secret 值。
- `assistant_message`:模型输出的用户可见 assistant 文本。Codex app-server 的 `item/agentMessage/delta` 只能作为流式过程证据或缺少 completed item 时的兜底;一旦收到 completed `agentMessage` itemadapter 必须为每个非空 completed item 输出一条 `assistant_message`,并用 `itemId``messageIndex``messageCount``replyAuthority``final` 标明顺序与最终 reply authority。最终 result reply 必须以最后一个 `replyAuthority=true` / `final=true``assistant_message` 为准,避免把 commentary/status/progress 堆入 final response。
- `tool_call`:工具调用摘要和 redacted 参数。
- `command_output`stdout/stderr 或命令输出摘要。
- `diff`:代码变更摘要或 patch 片段;必须受长度限制。
- `error`:结构化错误和 failureKind。
- `terminal_status`completed、failed、cancelled、blocked 等终态。
事件必须有上限和分页友好形态。大型日志、完整 stdout 或完整 trace 应进入 logPath 或后续 artifact,不得一次性塞入单个 event 造成输出爆炸。
Codex app-server 的低价值内部 notification 必须在 AgentRun adapter 层收敛,不得要求 HWLAB Web/CLI 或其他消费侧自行过滤。以下事件默认不作为 durable trace event 持久化:`item/reasoning/textDelta`、纯 `reasoning` item 的 `item/started|item/completed`、非 `commandExecution` item 的通用 `item/started|item/completed``thread/tokenUsage/updated``account/rateLimits/updated`、普通 `warning``configWarning`。adapter 可以输出一条有界 `backend_status.phase=codex-app-server-notifications-suppressed` 摘要,只包含总数、`methods: [{ method, count }]``itemTypes: [{ itemType, count }]`,不包含 reasoning 文本、Secret、token 或 env value。method 和 item type 不得作为 JSON object key 输出,避免 `thread/tokenUsage/updated` 这类协议名被 redaction 误判为敏感 key。真实 `agentMessage``commandExecution``command_output`、error、terminal 和关键生命周期事件必须继续保留。
`commandExecution``tool_call` event 只能输出面向人和消费侧的扁平字段,例如 `method``itemId``toolName``type``command``cwd``status``processId``valuesPrinted=false`。不得把 Codex app-server 的原始 `item` JSON、`itemPreview` 或嵌套协议摘要写入 `message``outputSummary``stdoutSummary` 或 payload;命令实际 stdout/stderr 只通过 `command_output` 或 completed `commandExecution` 摘要输出。
## Failure Mapping
Adapter 必须把 backend 错误映射为稳定 failureKind
| failureKind | 典型来源 |
| --- | --- |
| `secret-unavailable` | Secret projection 缺失、文件不存在、权限不可读。 |
| `provider-auth-failed` | provider credential 或 auth file 无效、上游返回 401/403。 |
| `provider-rate-limited` | 上游限流或 quota 错误。 |
| `provider-unavailable` | 上游 provider availability/transient 失败,包括 HTTP 5xx/503、`Service Unavailable``responseStreamDisconnected` 携带 5xx 状态码、明确 `provider unavailable``temporary unavailable` 文案。 |
| `backend-protocol-error` | backend 输出无法解析、协议字段缺失。 |
| `backend-json-parse-error` | backend stdout 不是合法 JSON-RPC 行。 |
| `backend-response-invalid` | backend JSON-RPC response/terminal notification 缺少必需字段。 |
| `thread-resume-failed` | `thread/resume` 遇到任何协议错误、旧 rollout 缺失或 `no rollout found for thread id`。已有 `SessionRef.threadId` 时只能按 Codex stdio 原生 session 执行 `thread/resume` 后接 `turn/start`;resume 失败必须终止当前 turn,不得再启动替代 `thread/start`、拼接历史 prompt 或用其他上下文冒充继续会话。 |
| `backend-spawn-failed` | backend app-server 进程无法启动。 |
| `backend-failed` | backend 进程非零退出或 terminal error。 |
| `backend-timeout` | executionPolicy timeout 触发。 |
| `provider-invalid-tool-call` | provider / Codex app-server 返回无效 tool-call arguments JSON,例如 `invalid_prompt``invalid function arguments json string`。 |
| `cancelled` | interrupt/cancel 生效。 |
## Credential Boundary
- Adapter 只能看到运行时投影出来的最小 Secret 文件或 env;不得枚举整个 namespace Secret。
- Adapter 不得把 Secret 值写入 event、trace、日志、CLI 输出、health 或 Postgres。
- Codex backend 的 `auth.json``config.toml` 整体按敏感文件处理,即使其中包含非敏感配置,也不得输出原文。
- Provider base URL、model 名称和 profile 名称可以输出,但 URL credential、Authorization header、token、api_key、password 必须 redacted。
## 测试规格
### T1 Adapter 自测试
阅读本文,然后用 mock backend 做组件自测试,确认 adapter 能把 assistant/output/error/terminal 输出归一化为标准 events,并能对 token、Authorization header、URL credential 和 Secret 文件内容做 redaction。该测试属于自测试,不作为综合联调通过证据。
### T2 Failure mapping 自测试
阅读本文,然后用 mock 错误覆盖 missing secret、provider auth failure、rate limit、provider availability/transient failure、protocol error、timeout 和 cancel。provider availability/transient 样例必须至少包含 HTTP 503 `Service Unavailable` 或携带 5xx 的 `responseStreamDisconnected`,并确认不会被归类为 `backend-failed`。确认每类错误都映射到稳定 failureKind,且输出为 JSON 或结构化 event。
### T3 真实 backend 联调
阅读本文、[spec-v01-backend-codex.md](spec-v01-backend-codex.md) 和 [spec-v01-validation.md](spec-v01-validation.md),然后分别用 `backendProfile=codex``backendProfile=deepseek``backendProfile=minimax-m3` 完成真实最短 turn。确认 adapter 输出真实 assistant/backend_status/terminal_status events,事件中包含 profile/backendKind/protocol 摘要,且没有 Secret 泄露。
### T4 Profile isolation 自测试
阅读本文,然后用 fake Codex app-server 和多个不同的 profile Secret fixture 做自测试。确认 adapter 只选择 run 指定 profile 的 SecretRef 和 `CODEX_HOME``deepseek``minimax-m3` 缺失时失败为 `secret-unavailable`,不会 fallback 到 `codex`
## 规格的实现情况
| 规格项 | 状态 | 说明 |
| --- | --- | --- |
| Backend adapter 合同 | 已定义 | 本文为 v0.1 adapter 权威。 |
| 通用 adapter 模块 | 已实现 profile 形态 | `src/backend/adapter.ts` 作为 runner 进程内 adapter 入口;`codex``deepseek``minimax-m3` 均路由到同一 Codex stdio backend kind,不复制第二套协议实现。 |
| event normalization | 已实现主路径 | Codex backend 已把 backend_status、assistant_message、tool_call、command_output、error 和 terminal_status 归一化为 manager events;复杂事件审计按人工验收抽查。 |
| failure mapping | 已实现主路径 | Codex backend 已覆盖 missing secret、auth/rate/availability、protocol、JSON parse、invalid response、spawn、timeout 和 cancel 分类;真实负向场景按 [spec-v01-validation.md](spec-v01-validation.md) T7 手动验收。 |
| `deepseek` profile | 已实现/已通过主闭环 | 已进入 registry、validation、runner Secret selection、backend_status metadata、CLI secret render 和 fake stdio 自测试;真实综合联调已按 [spec-v01-validation.md](spec-v01-validation.md) T8 覆盖 `codex -> deepseek -> codex` 切换。 |
| `minimax-m3` profile | 已实现/待真实主闭环 | 已进入 registry、validation、runner Secret selection、backend_status metadata、CLI secret render 和 fake stdio 自测试;真实综合联调需要按 [spec-v01-validation.md](spec-v01-validation.md) T8 覆盖 `codex -> deepseek -> minimax-m3 -> codex` 切换。 |
| 多 backend 路由 | Deferred | 跨 backend kind 的自动路由和 scheduler capacity selection 不进入 v0.1。 |