客户端LLM去配置化 #214

Closed
opened 2026-08-31 10:22:22 +08:00 by kdletters · 2 comments
Member

客户端llm配置锁死,不让配,api-server转发LLM请求

客户端llm配置锁死,不让配,api-server转发LLM请求
suzmii self-assigned this 2026-08-31 10:23:15 +08:00
suzmii added the due date 2026-09-01 2026-08-31 14:14:11 +08:00
suzmii modified the due date from 2026-09-01 to 2026-09-03 2026-09-01 19:10:43 +08:00
Member

客户端 LLM 去配置化技术方案补记

背景

AGC 旧模型把 LLM Provider 当作用户可配置项:用户需要理解 API Key、Base URL、模型、API 协议,并配置全局 / Agent 级路由。这带来三个问题:

  1. 普通用户不应该保管或理解 LLM 凭据;
  2. 客户端本地配置容易残留第三方 Key,带来泄漏和排障风险;
  3. LLM 用量、账号、扣费和平台登录态割裂,无法统一治理。

本需求将 AGC 正式发行版收敛为“平台登录态 + api-server 官方代理 + 每用户 Router 账号”的链路。

目标

  • 普通用户不再配置 Provider、API Key、Base URL、模型、API 协议或 Agent 级 LLM 路由。
  • 客户端只使用平台登录 access token,由 api-server 服务端解析账号级 Router Key 并转发请求。
  • Router Key 只存在服务端加密存储和 api-server 进程内,不下发客户端。
  • 复用 external_api_key 正式 Key 体系,并为 Router 账号 Saga 增加独立状态表。
  • 认证、Provisioning、调用、扣费、失效和撤销都有明确状态路径。
  • 后台可安全查询 Key 元数据,但不能看到明文 Key 或 hash。

非目标

  • 不提供普通用户自定义 Provider 入口。
  • 不做进程级或全局 fallback Key。
  • 不把 Router Key 写入客户端配置、项目文件、日志、trace、manifest 或 IPC。
  • 不在本需求内完成正式商业化定价,仅保留当前过渡扣费规则。

客户端方案

正式构建固定使用 codex_app_server 模式:

  • 读取配置时锁定官方运行方式,清除旧配置中的 llm.apiKeyllm.baseUrlllm.modelllm.apiKindagentLlm 和编辑器 API Key。
  • 旧配置首次读取时会被安全迁移 / 清理,第三方 LLM 凭据不再落盘。
  • Runtime Config UI 不再展示可编辑的 LLM Provider 配置,只显示登录、账号凭据、官方路由锁定和运行参数状态。
  • /llm-status/llm-routes 只返回安全状态,不返回 Router 地址、模型、协议或凭据字段。
  • 测试构建允许显式 loopback fixture;正式二进制没有运行时自定义 Provider 分支。

DirectProject 调用链:

AGC 登录态
  → Tauri Rust 进程保存 access token
  → Codex app-server 只连接本机 Provider Proxy
  → Provider Proxy 转发 api-server /api/llm/responses
  → api-server 查询服务端 Router 凭据
  → Router 官方 Responses 入口

本机 Provider Proxy 的安全边界:

  • Codex app-server 只拿到随机 loopback Bearer。
  • 平台 access token 只存在 Rust 进程和 Provider Proxy 内。
  • 不进入 Codex argv、环境变量、项目文件、manifest、trace 或聊天正文。
  • Proxy 只放行 POST /responses
  • 转发时剥离 X-Codex-* 账户额度类响应头,避免 Codex 误判平台代理为 ChatGPT 账号额度。

api-server 转发方案

新增 /api/llm/responses

  1. 使用平台 access token 鉴权。
  2. 请求体最大 32 MiB。
  3. 删除旧客户端可能携带的 Provider 控制字段:apiKeybaseUrlproviderapiKindagentLlmagentMode 及各 snake_case 变体。
  4. 强制覆盖官方模型。
  5. 按登录 owner 读取服务端 Router 凭据。
  6. 调用官方 Router /responses
  7. 原样转发 JSON 或 SSE 响应。
  8. Router 明确返回 401/403 时,将本地 Router Key 标记 revoked,不自动重试。
  9. 网络失败、结果不确定或本地凭据缺失时 fail-closed,不回退旧 LLM Key。

Router 账号与数据模型

新增 llm_router_account 表,保存每用户 Router provisioning Saga 状态:

  • account_key
  • owner_user_id
  • route_origin
  • idempotency_key
  • router_account_id
  • external_api_key_id
  • credential_ciphertext
  • account_json
  • status
  • attempt_count
  • lease_until_micros
  • next_retry_at_micros
  • last_error
  • credential_version
  • created_at
  • updated_at

external_api_key 继续作为正式 Key 记录,末尾追加兼容字段:

  • purpose,默认 external-editor,Router 固定为 llm-router
  • secret_ciphertext
  • provider_account_id
  • provider_base_url
  • provider_model
  • provider_account_json
  • credential_version

普通 External Editor Key 仍保持“明文只显示一次、后端只存 hash/prefix”;LLM Router Key 不走一次性明文返回,客户端永远不可见。

Provisioning 流程

每个平台用户对应一个稳定 Router 账号:

  1. 由完整平台 owner_user_id 与固定 provisioning secret 派生稳定凭据。
  2. Router 用户名使用 agc_user_ + 11 位短哈希,满足 New API 20 字符限制。
  3. Router 用户 remark 保存完整平台 owner_user_id,便于 Router 侧统计回溯。
  4. 先按稳定用户名查询 / 登录远端用户;确认不存在才注册,避免独立开发数据库重复建号。
  5. 创建或恢复用户后:用户分组设置为 taonier;登录 Router;查询 / 创建固定 Token agc_auto_generate;Token / API Key 分组固定为 default;签发 API Key。
  6. 检查固定 plan_id=1 订阅:无 active 订阅时创建;已过期或剩余时间不足 24 小时时续期;剩余超过 24 小时时复用。
  7. 将账号状态与加密 Key 写入 llm_router_account / external_api_key

认证成功后会触发幂等账号准备;如果首次 LLM 请求时仍缺有效本地行,请求路径会同步执行同一 provisioning 流程。任一步外部结果不确定时进入 reconciliation 状态,禁止盲目重复注册。

环境与安全边界

  • Router 控制面固定使用官方公网地址。
  • loopback Router 仅允许本地 fixture。
  • 非官方公网 Router 地址拒绝。
  • 使用共享官方 Router 的开发 / 测试环境启动时告警,因为会触达线上账号和额度。
  • Router Key 加密优先使用 GENARRATIVE_LLM_ROUTER_API_KEY_ENCRYPTION_SECRET;缺省使用带域分离的 JWT secret 派生密钥;生产应配置专用 secret。
  • Router 管理员 Token 仅存在 api-server 环境 / secret file,不进入数据库、客户端或日志。
  • 不存在 fallback Key。

扣费方案

请求前门禁:

  • 读取用户钱包余额。
  • 余额为 0 时直接返回 MUD_POINTS_INSUFFICIENT,不发起 Router 请求。

成功后扣费:

  • 从成功 JSON 或 terminal SSE event 中提取 usage。
  • 临时规则:每开始 10,000 total tokens 扣 1 泥点,成功响应至少 1 点。
  • 幂等键来自 Idempotency-Key / X-Idempotency-Key,缺省 request id。
  • ledger id 由 owner_user_id + idempotency_key 派生。
  • 余额足够时全额扣除。
  • 余额不足但大于 0 时,扣除当前可消费余额,差额记录 waivedPoints,LLM 响应仍返回给用户。
  • Router 失败不扣费。
  • 流式响应等待 response.completed / response.incomplete 后结算。

后台查询

新增 admin-only 专用接口 GET /admin/api/external-api-keys,支持 owner、keyId、name、keyPrefix、createdAfter / createdBefore、status、purpose。

约束:

  • 必须提供有界查询条件,拒绝无界扫描。
  • 只返回安全元数据。
  • 永不返回 key_hash、明文 Key、密文或原始行 JSON。
  • 通用数据库表查询对 external_api_key 做安全脱敏 / 拒绝。

验证

已覆盖的定向验证包括:

  • AGC 配置锁定与旧凭据清理测试
  • Runtime Config UI 无 Provider / API Key 输入测试
  • Codex app-server 只使用平台会话代理测试
  • Provider Proxy 鉴权、路径收窄、SSE 透传和 X-Codex-* 剥离测试
  • api-server 强制官方模型、忽略旧 Provider 字段、无 fallback Key 测试
  • Router provisioning、账号恢复、Token 分组、订阅续期、Key 加密和撤销测试
  • SpacetimeDB schema / migration / bindings 一致性检查
  • 后台 external API Key 安全查询测试
  • 零泥点禁止发起请求测试
  • 后置扣费、幂等、余额不足 waive 测试

待验收 / 风险

  • 生产 Router 管理接口、真实账号注册、订阅和 Key 权限需要用受控账号做最终 smoke。
  • Router 侧统计依赖 remark 中的完整平台 user id,后续如 Router 协议变化需同步映射方案。
  • 当前 10,000 token / 1 泥点是过渡定价,接入正式定价后需替换常量并保留 ledger 兼容。
  • PR #242 的远程 CI 仍需以最新分支重新跑绿后再合并。
## 客户端 LLM 去配置化技术方案补记 ### 背景 AGC 旧模型把 LLM Provider 当作用户可配置项:用户需要理解 API Key、Base URL、模型、API 协议,并配置全局 / Agent 级路由。这带来三个问题: 1. 普通用户不应该保管或理解 LLM 凭据; 2. 客户端本地配置容易残留第三方 Key,带来泄漏和排障风险; 3. LLM 用量、账号、扣费和平台登录态割裂,无法统一治理。 本需求将 AGC 正式发行版收敛为“平台登录态 + api-server 官方代理 + 每用户 Router 账号”的链路。 ### 目标 - 普通用户不再配置 Provider、API Key、Base URL、模型、API 协议或 Agent 级 LLM 路由。 - 客户端只使用平台登录 access token,由 api-server 服务端解析账号级 Router Key 并转发请求。 - Router Key 只存在服务端加密存储和 api-server 进程内,不下发客户端。 - 复用 `external_api_key` 正式 Key 体系,并为 Router 账号 Saga 增加独立状态表。 - 认证、Provisioning、调用、扣费、失效和撤销都有明确状态路径。 - 后台可安全查询 Key 元数据,但不能看到明文 Key 或 hash。 ### 非目标 - 不提供普通用户自定义 Provider 入口。 - 不做进程级或全局 fallback Key。 - 不把 Router Key 写入客户端配置、项目文件、日志、trace、manifest 或 IPC。 - 不在本需求内完成正式商业化定价,仅保留当前过渡扣费规则。 ### 客户端方案 正式构建固定使用 `codex_app_server` 模式: - 读取配置时锁定官方运行方式,清除旧配置中的 `llm.apiKey`、`llm.baseUrl`、`llm.model`、`llm.apiKind`、`agentLlm` 和编辑器 API Key。 - 旧配置首次读取时会被安全迁移 / 清理,第三方 LLM 凭据不再落盘。 - Runtime Config UI 不再展示可编辑的 LLM Provider 配置,只显示登录、账号凭据、官方路由锁定和运行参数状态。 - `/llm-status`、`/llm-routes` 只返回安全状态,不返回 Router 地址、模型、协议或凭据字段。 - 测试构建允许显式 loopback fixture;正式二进制没有运行时自定义 Provider 分支。 DirectProject 调用链: ```text AGC 登录态 → Tauri Rust 进程保存 access token → Codex app-server 只连接本机 Provider Proxy → Provider Proxy 转发 api-server /api/llm/responses → api-server 查询服务端 Router 凭据 → Router 官方 Responses 入口 ``` 本机 Provider Proxy 的安全边界: - Codex app-server 只拿到随机 loopback Bearer。 - 平台 access token 只存在 Rust 进程和 Provider Proxy 内。 - 不进入 Codex argv、环境变量、项目文件、manifest、trace 或聊天正文。 - Proxy 只放行 `POST /responses`。 - 转发时剥离 `X-Codex-*` 账户额度类响应头,避免 Codex 误判平台代理为 ChatGPT 账号额度。 ### api-server 转发方案 新增 `/api/llm/responses`: 1. 使用平台 access token 鉴权。 2. 请求体最大 32 MiB。 3. 删除旧客户端可能携带的 Provider 控制字段:`apiKey`、`baseUrl`、`provider`、`apiKind`、`agentLlm`、`agentMode` 及各 snake_case 变体。 4. 强制覆盖官方模型。 5. 按登录 owner 读取服务端 Router 凭据。 6. 调用官方 Router `/responses`。 7. 原样转发 JSON 或 SSE 响应。 8. Router 明确返回 401/403 时,将本地 Router Key 标记 revoked,不自动重试。 9. 网络失败、结果不确定或本地凭据缺失时 fail-closed,不回退旧 LLM Key。 ### Router 账号与数据模型 新增 `llm_router_account` 表,保存每用户 Router provisioning Saga 状态: - `account_key` - `owner_user_id` - `route_origin` - `idempotency_key` - `router_account_id` - `external_api_key_id` - `credential_ciphertext` - `account_json` - `status` - `attempt_count` - `lease_until_micros` - `next_retry_at_micros` - `last_error` - `credential_version` - `created_at` - `updated_at` `external_api_key` 继续作为正式 Key 记录,末尾追加兼容字段: - `purpose`,默认 `external-editor`,Router 固定为 `llm-router` - `secret_ciphertext` - `provider_account_id` - `provider_base_url` - `provider_model` - `provider_account_json` - `credential_version` 普通 External Editor Key 仍保持“明文只显示一次、后端只存 hash/prefix”;LLM Router Key 不走一次性明文返回,客户端永远不可见。 ### Provisioning 流程 每个平台用户对应一个稳定 Router 账号: 1. 由完整平台 `owner_user_id` 与固定 provisioning secret 派生稳定凭据。 2. Router 用户名使用 `agc_user_` + 11 位短哈希,满足 New API 20 字符限制。 3. Router 用户 `remark` 保存完整平台 `owner_user_id`,便于 Router 侧统计回溯。 4. 先按稳定用户名查询 / 登录远端用户;确认不存在才注册,避免独立开发数据库重复建号。 5. 创建或恢复用户后:用户分组设置为 `taonier`;登录 Router;查询 / 创建固定 Token `agc_auto_generate`;Token / API Key 分组固定为 `default`;签发 API Key。 6. 检查固定 `plan_id=1` 订阅:无 active 订阅时创建;已过期或剩余时间不足 24 小时时续期;剩余超过 24 小时时复用。 7. 将账号状态与加密 Key 写入 `llm_router_account` / `external_api_key`。 认证成功后会触发幂等账号准备;如果首次 LLM 请求时仍缺有效本地行,请求路径会同步执行同一 provisioning 流程。任一步外部结果不确定时进入 reconciliation 状态,禁止盲目重复注册。 ### 环境与安全边界 - Router 控制面固定使用官方公网地址。 - loopback Router 仅允许本地 fixture。 - 非官方公网 Router 地址拒绝。 - 使用共享官方 Router 的开发 / 测试环境启动时告警,因为会触达线上账号和额度。 - Router Key 加密优先使用 `GENARRATIVE_LLM_ROUTER_API_KEY_ENCRYPTION_SECRET`;缺省使用带域分离的 JWT secret 派生密钥;生产应配置专用 secret。 - Router 管理员 Token 仅存在 api-server 环境 / secret file,不进入数据库、客户端或日志。 - 不存在 fallback Key。 ### 扣费方案 请求前门禁: - 读取用户钱包余额。 - 余额为 0 时直接返回 `MUD_POINTS_INSUFFICIENT`,不发起 Router 请求。 成功后扣费: - 从成功 JSON 或 terminal SSE event 中提取 usage。 - 临时规则:每开始 10,000 total tokens 扣 1 泥点,成功响应至少 1 点。 - 幂等键来自 `Idempotency-Key` / `X-Idempotency-Key`,缺省 request id。 - ledger id 由 `owner_user_id + idempotency_key` 派生。 - 余额足够时全额扣除。 - 余额不足但大于 0 时,扣除当前可消费余额,差额记录 `waivedPoints`,LLM 响应仍返回给用户。 - Router 失败不扣费。 - 流式响应等待 `response.completed` / `response.incomplete` 后结算。 ### 后台查询 新增 admin-only 专用接口 `GET /admin/api/external-api-keys`,支持 owner、keyId、name、keyPrefix、createdAfter / createdBefore、status、purpose。 约束: - 必须提供有界查询条件,拒绝无界扫描。 - 只返回安全元数据。 - 永不返回 `key_hash`、明文 Key、密文或原始行 JSON。 - 通用数据库表查询对 `external_api_key` 做安全脱敏 / 拒绝。 ### 验证 已覆盖的定向验证包括: - AGC 配置锁定与旧凭据清理测试 - Runtime Config UI 无 Provider / API Key 输入测试 - Codex app-server 只使用平台会话代理测试 - Provider Proxy 鉴权、路径收窄、SSE 透传和 `X-Codex-*` 剥离测试 - api-server 强制官方模型、忽略旧 Provider 字段、无 fallback Key 测试 - Router provisioning、账号恢复、Token 分组、订阅续期、Key 加密和撤销测试 - SpacetimeDB schema / migration / bindings 一致性检查 - 后台 external API Key 安全查询测试 - 零泥点禁止发起请求测试 - 后置扣费、幂等、余额不足 waive 测试 ### 待验收 / 风险 - 生产 Router 管理接口、真实账号注册、订阅和 Key 权限需要用受控账号做最终 smoke。 - Router 侧统计依赖 `remark` 中的完整平台 user id,后续如 Router 协议变化需同步映射方案。 - 当前 10,000 token / 1 泥点是过渡定价,接入正式定价后需替换常量并保留 ledger 兼容。 - PR #242 的远程 CI 仍需以最新分支重新跑绿后再合并。
Author
Member

再加一个模型选择的菜单,需要支持显示名称映射到实际调用名称,白名单形式

再加一个模型选择的菜单,需要支持显示名称映射到实际调用名称,白名单形式
Sign in to join this conversation.
2 Participants
Notifications
Due Date
2026-09-03
Dependencies

No dependencies set.

Reference: GenarrativeAI/Genarrative#214