236 lines
17 KiB
Markdown
236 lines
17 KiB
Markdown
# 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 upstream;Secret 额外要求 `model-catalog.json`,模型目录与 `config.toml` 均声明 1M/900k context。 |
|
||
|
||
动态 profile slug 必须匹配小写 slug 规则 `^[a-z0-9][a-z0-9-]{0,63}$`,并固定映射到同 namespace 内 `agentrun-v01-provider-<profile>` SecretRef,required 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 或 rollout,AgentRun 管理 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 implemented,adapter 必须归类为 `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 SecretRef;Postgres、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 identity;Kubernetes 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 委托调用本 API;AgentRun 不读取 HWLAB Web session,也不做用户级鉴权。 |
|