Files
pikasTech-agentrun/docs/reference/spec-v01-provider-profile-management.md
2026-06-08 23:31:33 +08:00

236 lines
17 KiB
Markdown
Raw Permalink 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 Provider Profile 管理规格
本文是 AgentRun `v0.1` provider profile 管理能力的长期规格。实施跟踪见 [pikasTech/agentrun#28](https://github.com/pikasTech/agentrun/issues/28)HWLAB v0.2 WebUI 配置入口见 [pikasTech/HWLAB#917](https://github.com/pikasTech/HWLAB/issues/917)。
## 设计目标
-`agentrun-mgr` 提供 provider profile 状态查询、API Key 写入、Secret/config 更新和 canary 验证 REST API。
- AgentRun 后端直接信任来自 HWLAB `hwlab-cloud-api` 的服务端委托调用,不实现浏览器用户登录、Web session、HWLAB 用户角色或 OpenFGA 判断。
- 浏览器和 HWLAB Cloud Web 不直接访问 AgentRun,也不把原始 API Key 传入 run/session/command payload。
- AgentRun 只返回脱敏状态:profile、SecretRef、resourceVersion、hash 后缀、validation result、run/command/job identity 和 failureKind。
- provider profile 仍通过现有 `backendProfile``executionPolicy.secretScope.providerCredentials[]`、profile-scoped SecretRef 和 writable `CODEX_HOME` 装配到 runner。
## 职责边界
| 组件 | 职责 |
| --- | --- |
| `agentrun-mgr` | provider profile 管理 API、Secret/config 写入、状态查询、canary 创建、validation 状态聚合和脱敏审计。 |
| `agentrun-runner` | 按既有 `backendProfile`/SecretRef 装配 runtime,执行 canary run,不读取管理 API 的用户上下文。 |
| HWLAB `hwlab-cloud-api` | 用户鉴权、业务授权、Web/CLI 同源 API、审计 actor、调用 AgentRun 管理 API。 |
| HWLAB `hwlab-cloud-web` | “管理”页面和 API Key 表单;不直接调用 AgentRun。 |
AgentRun 不承担 HWLAB 用户鉴权。它只做机器层 contract 校验:调用来源、tenant/project/profile allowlist、SecretRef scope、payload schema、redaction 和幂等性。AgentRun 不判断某个 HWLAB 用户是否能配置 API Key,也不保存 HWLAB Web session、API key 或 OpenFGA decision。
## Profile 列表
`v0.1` 可管理 profile 分为内建 profile 与动态 profile slug。内建 profile 是无需 Secret 也必须出现在列表中的稳定能力;动态 profile 是管理员通过 HWLAB Cloud API / HWLAB CLI 或 AgentRun CLI 写入受控 Secret 后立即可见、可配置、可验证的 profile,不需要为每个新 slug 修改 AgentRun 或 HWLAB 服务代码。
| profile | backendKind | 默认 SecretRef | 说明 |
| --- | --- | --- | --- |
| `codex` | `codex-app-server-stdio` | `agentrun-v01/agentrun-v01-provider-codex` | Codex API profile。 |
| `deepseek` | `codex-app-server-stdio` | `agentrun-v01/agentrun-v01-provider-deepseek` | DeepSeek profile,经 HWLAB Moon Bridge 到 DeepSeek 官方 upstream。 |
| `minimax-m3` | `codex-app-server-stdio` | `agentrun-v01/agentrun-v01-provider-minimax-m3` | MiniMax-M3 profile。 |
| `dsflash-go` | `codex-app-server-stdio` | `agentrun-v01/agentrun-v01-provider-dsflash-go` | DeepSeek V4 Flash profile,经 HWLAB Moon Bridge 到 OpenCode Zen Go upstreamSecret 额外要求 `model-catalog.json`,模型目录与 `config.toml` 均声明 1M/900k context。 |
动态 profile slug 必须匹配小写 slug 规则 `^[a-z0-9][a-z0-9-]{0,63}$`,并固定映射到同 namespace 内 `agentrun-v01-provider-<profile>` SecretRefrequired keys 仍为 `auth.json``config.toml`。profile 管理 API 不得允许任意 namespace、任意 Secret name、`runtime-default` 或不符合 slug 规则的 profile。新增普通 OpenAI/Codex-compatible provider profile 时,管理员应优先通过 `provider-profiles set-config` / `set-key` / `validate` 创建动态 slug;只有需要新的 backend kind、特殊装配规则、额外 Secret key、租户策略或产品级固定友好名时,才更新本规格、内建 capability、CLI 示例和验证规格。
## REST API
Provider profile 管理 API 属于 `agentrun-mgr` 公共 REST API 的服务端委托面:
```http
GET /api/v1/provider-profiles
GET /api/v1/provider-profiles/:profile
DELETE /api/v1/provider-profiles/:profile
GET /api/v1/provider-profiles/:profile/config
PUT /api/v1/provider-profiles/:profile/config
PUT /api/v1/provider-profiles/:profile/credential
POST /api/v1/provider-profiles/:profile/validate
GET /api/v1/provider-profiles/:profile/validations/:validationId
```
所有成功和失败响应都必须是 JSON。失败响应至少包含 `failureKind``message``requestId`。除显式 `GET /api/v1/provider-profiles/:profile/config` 返回 `config.toml` 明文供 HWLAB admin 管理页查看外,其他响应不得包含 API Key 原文、Codex `auth.json` 明文、Codex `config.toml` 明文、base64 Secret data、Authorization header、Kubernetes token 或 provider request header。
### `GET /api/v1/provider-profiles`
返回所有内建 profile 与已存在动态 profile 的脱敏状态。动态 profile 通过受控 Secret 的 name、label 或 annotation 发现;删除动态 profile 的最后一个 Secret 后,它不再出现在 collection list 中。字段至少包含:
- `profile`
- `backendKind`
- `configured`
- `secretRef.namespace/name/keys`
- `resourceVersion`
- `keyHashSuffix``credentialHashSuffix`
- `configHashSuffix`
- `updatedAt`
- `lastValidation.status/failureKind/message/runId/commandId/jobName`
Secret 缺失时仍要返回 profile capability,并把状态标为 `configured=false``failureKind=secret-unavailable`;不得因为 Secret 未配置而隐藏 profile。
### `DELETE /api/v1/provider-profiles/:profile`
删除 profile 对应 Kubernetes Secret。
- 内建 profile`codex``deepseek``minimax-m3``dsflash-go`)删除后,capability 仍必须保留在 `GET /api/v1/provider-profiles` 列表中,但状态回到 `configured=false` / `failureKind=secret-unavailable`
- 动态 slug 删除后,若没有剩余 Secret,对应 slug 不再出现在 collection list 中;显式 `GET /api/v1/provider-profiles/:profile` 仍可返回该 slug 的未配置状态。
- 响应必须返回 `removed``alreadyAbsent`,并保持 Secret/API Key 脱敏。
### `GET/PUT /api/v1/provider-profiles/:profile/config`
`GET` 返回当前 profile 的 `configToml`、SecretRef、resourceVersion 和 hash 后缀,供 HWLAB admin 管理页查看。`PUT` 接收 `configToml`,保存时只替换同一 Secret 的 `config.toml`,保留现有 `auth.json`,并返回 resourceVersion 和 `configHashSuffix`
### `PUT /api/v1/provider-profiles/:profile/credential`
请求体由 HWLAB 后端或受控 CLI 发送,最小形态:
```json
{
"apiKey": "<write-only>",
"config": {
"model": "<optional>",
"baseUrl": "<optional>"
},
"delegatedBy": {
"system": "hwlab-v02",
"userId": "<hwlab-user-id>",
"username": "<hwlab-username>",
"requestId": "<hwlab-request-id>"
},
"reason": "hwlab-provider-management"
}
```
规则:
- `apiKey` 只在本次 request 内用于生成 Secret data,不能落入 Postgres、event、trace、日志或响应。
- Manager 写入 profile 对应 Kubernetes Secret 的 `auth.json``config.toml`,并返回新的 `resourceVersion` 与不可逆 hash 后缀;`dsflash-go` 同时生成或保留 `model-catalog.json`
- Manager 可记录 `delegatedBy` 的脱敏审计信息,但不把它作为用户鉴权依据。
- 非 HWLAB 委托调用可以用于 operator CLI,但也必须走同一 schema 和 redaction。
- 非法 profile、非法 baseUrl、SecretRef scope 越界、Kubernetes 写入失败和 config render 失败必须结构化失败。
### `POST /api/v1/provider-profiles/:profile/validate`
触发一个真实 canary。它必须通过 AgentRun 自身 run/command/runner-job 路径执行,不得只做静态 Secret 读取、mock provider 或直接 curl provider 作为通过证据。响应短返回:
```json
{
"validationId": "val_...",
"profile": "deepseek",
"runId": "run_...",
"commandId": "cmd_...",
"jobName": "agentrun-v01-runner-...",
"status": "running",
"pollUrl": "/api/v1/provider-profiles/deepseek/validations/val_..."
}
```
`GET /validations/:validationId` 聚合 command result、runner job status、events 和 provider failureKind,返回 `running|completed|failed|cancelled`。成功时必须能证明当前 `backendProfile`、SecretRef、CODEX_HOME、provider status 和 assistant reply;失败时必须保留 provider/secret/runner failureKind。
## DeepSeek 配置规则
`deepseek` profile 的 Codex config 必须指向 HWLAB v0.2 Moon Bridge,而不是 hyue
```text
baseUrl: http://hwlab-deepseek-proxy.hwlab-v02.svc.cluster.local:4000/v1
expected request path: /v1/responses
upstream: DeepSeek 官方 API
```
AgentRun 不直接拥有 DeepSeek 官方 upstream URL 的业务路由;它只把 Codex app-server 请求送到 HWLAB Moon Bridge。若 HWLAB bridge 需要独立 upstream Secret 或 rolloutAgentRun 管理 API 必须在响应中返回 `requiresExternalBridgeUpdate=true` 或由 HWLAB 委托请求显式声明 bridge 同步已完成。AgentRun 不得把 `deepseek` 配置改到 `hyueapi.com`,也不得因 DeepSeek 失败 fallback 到 `codex`
## dsflash-go 配置规则
`dsflash-go` 是内建 DeepSeek V4 Flash / OpenCode Zen Go profile,不是普通动态 slug。它必须通过 Codex stdio profile 形态运行,并满足以下固定规则:
- `model` 必须是 `deepseek-v4-flash`,不得被 `deepseek-chat` 或其他模型覆盖。
- `config.toml` 必须声明 `model_context_window = 1000000``model_auto_compact_token_limit = 900000`
- `config.toml` 必须声明 `model_catalog_json = "/home/agentrun/.codex-dsflash-go/model-catalog.json"`Secret 中必须存在同名 `model-catalog.json`,其中 `deepseek-v4-flash` 的 context window 与 `config.toml` 一致。
- base URL 必须指向 HWLAB Moon Bridge service 或 wrapper-local bridge,禁止指向 `hyueapi.com`;当前 G14 v0.2 默认服务入口是 `http://hwlab-deepseek-proxy.hwlab-v02.svc.cluster.local:4000/v1`
- `PUT /credential``PUT /config` 均必须在不打印 Secret value 的前提下生成或保留 `model-catalog.json`;状态查询只显示 key presence/hash 摘要。
- 若上游 compact 路径返回 404、not found、unsupported、no route 或 not implementedadapter 必须归类为 `provider-compact-unsupported`,避免被泛化成 `backend-failed`
## Secret 与 RBAC
Manager ServiceAccount 需要最小 Secret 管理权限,只允许 `get``list``create``replace``delete``patch` 受控 provider profile Secret
- `agentrun-v01-provider-codex`
- `agentrun-v01-provider-deepseek`
- `agentrun-v01-provider-minimax-m3`
- `agentrun-v01-provider-dsflash-go`
- `agentrun-v01-provider-<dynamic-lowercase-slug>`
不得授予 Manager 更新 namespace 内任意 Secret 的宽权限;动态 Secret 必须受 `agentrun-v01-provider-` 前缀、profile label/annotation 和 slug 校验约束。状态查询只读取受控 Secret metadata 和 key presence/hash,不返回 Secret value。
Secret 写入后,runner Job 仍按 [spec-v01-secret-distribution.md](spec-v01-secret-distribution.md) 通过 SecretRef projection 装配;run/command payload 中不出现 API Key。
Secret 写入不得留下包含 Secret data 的 Kubernetes last-applied 注解。Manager 更新 credential 时应使用不会生成 `kubectl.kubernetes.io/last-applied-configuration` 的 patch/update 路径,或在写入完成后显式删除该注解;状态响应、event、trace 和 CLI 输出仍只能返回 SecretRef、resourceVersion、hash 后缀和 redacted validation identity。
## CLI
AgentRun CLI 提供 operator 和综合联调入口:
```bash
./scripts/agentrun provider-profiles list
./scripts/agentrun provider-profiles show deepseek
./scripts/agentrun provider-profiles config deepseek
./scripts/agentrun provider-profiles remove deepseek
./scripts/agentrun provider-profiles set-key deepseek --key-stdin
./scripts/agentrun provider-profiles set-config deepseek --config-stdin
./scripts/agentrun provider-profiles validate deepseek --wait --timeout-ms 120000
./scripts/agentrun provider-profiles set-config my-provider --config-stdin
./scripts/agentrun provider-profiles set-key my-provider --key-stdin
```
CLI 必须调用 manager REST API,不直连 Postgres,不读取 Kubernetes Secret value。`set-key --key-stdin` 从 stdin 读入 API Key,默认输出只包含 SecretRef、resourceVersion、hash 后缀、failureKind 和下一步验证命令。
## 审计与脱敏
Manager 审计事件允许记录:profile、action、delegatedBy.system、delegatedBy.userId、requestId、SecretRef、old/new hash 后缀、resourceVersion、validationId、runId、commandId、jobName、failureKind。禁止记录:API Key 原文、Secret data、Codex auth/config 明文、Authorization header、provider request header/body、Kubernetes token。
## 验收规格
### T1 profile 状态
阅读本文和 [spec-v01-secret-distribution.md](spec-v01-secret-distribution.md),调用 `GET /api/v1/provider-profiles``./scripts/agentrun provider-profiles list`。确认 `codex``deepseek``minimax-m3``dsflash-go` 等内建 profile 全部可见,缺 Secret 时显示 `configured=false``secret-unavailable`,不隐藏 capability;已创建的动态 slug 也会列出;所有输出都不得包含 Secret value。
### T2 DeepSeek 写入
`./scripts/agentrun provider-profiles set-key deepseek --key-stdin` 写入测试 key。确认输出只有 resourceVersion/hash 后缀和 redacted SecretRefPostgres、event、trace、日志和 CLI 输出不含完整 key。
### T3 DeepSeek canary
`./scripts/agentrun provider-profiles validate deepseek --wait` 触发真实 runner canary。通过证据必须包含 validationId、runId、commandId、jobName、backendProfile=deepseek、SecretRef=`agentrun-v01-provider-deepseek`、terminal completed 和非空 assistant reply。
### T4 HWLAB 委托
通过 HWLAB v0.2 Cloud API `/v1/admin/provider-profiles/deepseek/credential``/validate` 调用本 API,确认 AgentRun 不要求 HWLAB 用户凭据,不读取 Web session,不返回用户权限判断;但会记录 `delegatedBy.system=hwlab-v02` 和 requestId 的脱敏审计。
### T5 redaction
检查 manager 日志、AgentRun events、CLI 输出和 validation result,确认不包含 API Key 原文、Codex `auth.json``config.toml`、Secret data 或 Authorization header。
### T6 profile 删除
`./scripts/agentrun provider-profiles remove <profile>` 删除一个动态 slug,再删除一个内建 profile。确认动态 slug 从 collection list 消失;内建 profile 仍留在 list 中但 `configured=false`CLI/日志/响应不输出 Secret value。
### T7 动态 profile 无代码变更
用 HWLAB CLI 或 AgentRun CLI 对一个临时动态 slug 执行 `set-config``set-key``list``remove`。通过证据必须显示 profile SecretRef 为 `agentrun-v01-provider-<slug>``configured=true` 只在 `auth.json``config.toml` 同时存在时成立、删除后 collection list 不再包含该 slug,并且整个过程没有 AgentRun/HWLAB service code change、PR、PipelineRun 或 rollout 作为前置条件。
### T8 dsflash-go model catalog
`./scripts/agentrun provider-profiles set-key dsflash-go --key-stdin` 或同源 HWLAB 委托 API 写入测试 key。确认输出只包含脱敏 SecretRef、resourceVersion/hash 后缀和 validation identityKubernetes Secret 中存在 `auth.json``config.toml``model-catalog.json` 三个 key`config.toml` 使用 `deepseek-v4-flash`、1M/900k context 和固定 `model_catalog_json` 路径;`provider-profiles show dsflash-go` 必须显示三项 key presence,且不输出任何 Secret value。
## 实现状态
| 能力 | 状态 | 说明 |
| --- | --- | --- |
| Provider profile 管理规格 | 已定义/已落地 | 本文为 AgentRun `v0.1` profile 管理权威规格。 |
| REST 管理 API | 已实现 | `agentrun-mgr` 提供 `/api/v1/provider-profiles*`,覆盖 list/show/remove/set-key/validate/validation。 |
| 动态 profile slug | 已实现 | 小写 slug 通过 `agentrun-v01-provider-<slug>` SecretRef 动态生效;普通 provider API Key/config 轮换不需要为每个新 slug 修改服务代码或触发专门 CI/CD。 |
| CLI 管理入口 | 已实现 | `./scripts/agentrun provider-profiles list/show/remove/set-key/set-config/validate` 调用 manager REST API,不直连 Secret value。 |
| DeepSeek Secret 写入 | 已实现/需硬化 | 已按受控 SecretRef 更新 `auth.json`/`config.toml` 并保持 HWLAB Moon Bridge 官方链路;后续必须去除 credential update 产生 `last-applied-configuration` 注解的副作用。 |
| `dsflash-go` model catalog | 已实现 | `dsflash-go` 使用 `deepseek-v4-flash`、1M/900k context、固定 `model_catalog_json` 路径和 Secret 内 `model-catalog.json`compact unsupported 明确归类为 `provider-compact-unsupported`。 |
| Provider canary | 已实现 | canary 通过真实 run/command/runner-job 路径执行,并返回 validationId、runId、commandId、jobName 和 terminal status。 |
| HWLAB 委托信任边界 | 已验证 | HWLAB v0.2 通过 Cloud API 委托调用本 APIAgentRun 不读取 HWLAB Web session,也不做用户级鉴权。 |