# Copilot 配置中心实施计划 > 日期:2026-07-12 > 状态:已实施并验证(平台 Provider、账户模型/行为、Embedding 重建) > 菜单名称:`Copilot 配置` > 目标:为 GoChat 自托管部署提供可安全管理、可测试、可运行时生效的 Copilot/LLM 配置入口,并让页面选择的模型真正作用于 LLM 请求。 --- ## 1. 已确认的产品决策 1. 设置菜单统一命名为 **Copilot 配置**,不在用户界面继续使用“Captain AI 配置”作为供应商配置名称。 2. `Captain` 仍保留为 AI 助手、知识库、场景、工具等业务模块的内部领域名称,不做全仓重命名。 3. LLM Provider、Base URL、API Key 属于**平台级配置**,对整套 GoChat 安装生效。 4. 功能开关、功能模型、回复风格属于**账户级配置**,只影响当前 Account。 5. Assistant 的提示词、Guardrails、Response Guidelines、知识文档和 Inbox 绑定继续在 Assistant 页面管理,不塞入 Copilot 配置页。 6. API Key 以明文写入 `installation_configs.value`;接口只返回配置状态和掩码,日志禁止记录 Key 或 Authorization Header,页面不提供查看明文能力。 7. Copilot 在系统中全局永久启用,不提供总开关,也不允许通过环境变量、配置文件或页面关闭。 8. Copilot Provider 参数只从数据库读取,不定义任何 Copilot/Captain 环境变量。 9. 除 Embedding 维度变化外,配置保存后应运行时生效,不要求重启服务。 --- ## 2. 实施前缺口 实施前仓库已经有 Captain/Copilot 页面、API 和 LLM Provider,但还不是一个可用的配置闭环: - 设置侧栏中的 Captain AI 入口被注释。 - 旧设置页仅允许 Cloud/Enterprise 安装类型访问,自托管 Community 配置为不可访问。 - 旧设置页只能保存 `captain_models` 和 `captain_features`,没有 Provider、Base URL、API Key。 - Provider 在 `bootstrap.go` 启动时一次性创建,页面配置无法替换运行中的 Provider。 - 旧启动配置工厂接收 `provider` 参数,但实际固定创建 OpenAI-compatible Eino ChatModel;该入口现已移除。 - 多个 Service 仍使用硬编码模型或 Provider 默认模型,账户保存的模型选择没有完整进入请求链路。 - `installation_configs` 已有平台级 CRUD,但目前是无类型字符串存储,不能直接安全暴露 LLM 密钥。 因此本任务不能只解开旧菜单;需要同时补齐配置存储、权限、运行时 Provider 和模型解析。 --- ## 3. 页面入口与权限 ### 3.1 菜单与路由 - 菜单位置:`设置 → Copilot 配置` - 页面路由:`/app/accounts/:accountId/settings/copilot` - 前端路由名:`copilot_settings_index` - 移除旧路由的 Cloud/Enterprise 安装类型限制。 - 不使用 `captain_integration` Feature Flag 或其他开关控制配置页和 Copilot 总体可用性。 - Provider 尚未配置时页面展示“配置不完整”,而不是“未启用”。 ### 3.2 权限分层 | 用户角色 | 平台 Provider 配置 | 账户功能配置 | 连接测试 | API Key 状态 | |---|---:|---:|---:|---:| | SuperAdmin | 可读写 | 可读写 | 可执行 | 仅显示是否已配置和掩码 | | Account Administrator | 只读状态 | 可读写 | 不可执行平台密钥测试 | 仅显示“已配置/未配置” | | Agent | 不可访问 | 不可访问 | 不可访问 | 不可访问 | 平台配置的写接口必须使用 SuperAdmin 中间件保护,不能仅依赖前端隐藏。 --- ## 4. 页面信息架构 页面分为五个区块: 1. **运行状态**:当前 Provider、连接状态和配置完整性。Copilot 始终启用,不提供总开关。 2. **对话模型**:Provider、Base URL、API Key、默认 Chat Model。 3. **Embedding 模型**:Embedding Provider、Key、Endpoint、Model、Dimensions。 4. **生成与可靠性**:Temperature、Max Tokens、Timeout、Retry。 5. **当前账户功能**:Editor、Copilot、Assistant、标签建议、知识库语义搜索的开关和模型。 “保存配置”和“测试连接”必须是两个独立动作:测试候选参数不落库;保存前后端均需再次校验。 --- ## 5. 平台级参数规划 ### 5.1 基础状态 | 字段 | 配置键 | 类型/默认值 | 页面规则 | 生效方式 | |---|---|---|---|---| | 配置状态 | `configured` | derived boolean | 根据页面保存的 Provider、Model、Base URL 与 API Key 判断 | 只读 | 不定义 `enabled` 字段。Copilot 路由、菜单和服务始终注册;账户级功能开关只控制具体能力,不等价于关闭系统 Copilot。 ### 5.2 对话模型 | 字段 | 配置键 | 类型/默认值 | 校验与交互 | |---|---|---|---| | Provider | `chat.provider` | enum:`openai` / `anthropic` / `openai_compatible`;默认 `openai` | 必填;决定请求协议,不再把所有 Provider 当 OpenAI 协议处理 | | Base URL | `chat.base_url` | URL | `openai`/`anthropic` 自动提供官方默认值;`openai_compatible` 必填;禁止非 HTTP(S) URL | | API Key | `chat.api_key` | secret | 写入时允许空值表示“不修改”;清除必须单独确认;GET 永不返回明文 | | 默认模型 | `chat.model` | string,默认 `gpt-4o-mini` | 必填;支持手工输入,不能只依赖前端静态模型列表 | Provider 预设只负责填充默认 Endpoint,不锁死模型: | Provider | 默认 Base URL | 协议实现 | |---|---|---| | OpenAI | `https://api.openai.com/v1` | `llm.OpenAIProvider`,OpenAI Chat Completions API | | Anthropic | `https://api.anthropic.com` | `llm.AnthropicProvider`,Anthropic Messages API | | OpenAI Compatible | 用户填写 | `llm.OpenAIProvider` + 自定义 Base URL;适用于 DeepSeek、通义千问、豆包及自建网关 | `EinoProvider` 适配器仍保留给显式构造的 Eino ChatModel/Embedder 使用;数据库驱动的 `ProviderManager` 运行时配置链路不经过该适配器,而是按协议直接创建上述 Provider。 首期不单独增加 DeepSeek/Qwen/Doubao 枚举,避免把供应商宣传名称与真实协议实现绑死。 ### 5.3 Embedding 模型 | 字段 | 配置键 | 类型/默认值 | 校验与交互 | |---|---|---|---| | Embedding 模式 | `embedding.mode` | enum:`reuse_chat_credentials` / `separate`;默认 `reuse_chat_credentials` | Chat Provider 为 Anthropic 时必须使用 `separate` | | Provider | `embedding.provider` | enum:`openai` / `openai_compatible` | 仅在 separate 模式展示 | | Base URL | `embedding.base_url` | URL | 自定义兼容 Provider 必填 | | API Key | `embedding.api_key` | secret | 与 Chat Key 分开存储;允许复用但不能在响应中回显 | | 模型 | `embedding.model` | string,默认 `text-embedding-3-small` | 必填;连接测试必须实际调用 embeddings API | | 向量维度 | `embedding.dimensions` | integer,默认 `1536` | 范围 1-4096;修改时二次确认,并通过重建接口迁移现有文章向量 | Embedding 配置不能隐式依赖 Anthropic,因为 Anthropic 当前没有 Embeddings API。 ### 5.4 生成与可靠性 | 字段 | 配置键 | 类型/默认值 | 校验 | 生效方式 | |---|---|---|---|---| | Temperature | `generation.temperature` | number,默认 `0.7` | 0-2 | 热生效 | | 最大输出 Tokens | `generation.max_tokens` | integer,默认 `1024` | 64-32768 | 热生效 | | 请求超时 | `request.timeout_seconds` | integer,默认 `60` | 5-300 秒 | 热生效 | | 最大重试次数 | `request.max_retries` | integer,默认 `3` | 0-5;只重试网络错误、429 和 5xx | 热生效 | 首期不开放以下高风险或不稳定参数:关闭 TLS 校验、任意代理地址、任意请求 Header、完整请求/响应日志。 --- ## 6. 账户级参数规划 账户配置继续存入 Account 的 `captain_models`、`captain_features` 和 `captain_preferences`,但 UI 名称统一为 Copilot。 ### 6.1 功能开关和模型 | 功能 | Feature Key | Model Key | 默认状态 | 说明 | |---|---|---|---:|---| | 编辑器改写 | `editor` | `editor` | 开启 | 润色、改写、语气调整 | | Copilot 对话 | `copilot` | `copilot` | 开启 | 客服侧边栏问答与回复建议 | | AI Assistant | `assistant` | `assistant` | 关闭 | Assistant Playground、自动辅助能力 | | 标签建议 | `label_suggestion` | `label_suggestion` | 关闭 | 仅建议;自动应用由账户策略决定 | | 知识库语义搜索 | `help_center_search` | 使用平台 Embedding 模型 | 关闭 | 开启前必须通过 Embedding 测试并完成索引 | 首期暂不在页面开放 `audio_transcription`,直到后端具有独立的语音转写 Provider 和真实调用链。 模型下拉选项应由后端根据当前 Provider 返回,且始终提供“手工填写模型 ID”。不可再返回无法由当前 Provider 调用的静态模型列表。 ### 6.2 回复行为 | 字段 | 现有字段 | 默认值 | 说明 | |---|---|---|---| | 回复语气 | `tone` | `professional` | professional / friendly / casual / formal | | 回复语言 | `language` | `auto` | `auto` 表示跟随客户消息;同时允许明确语言代码 | | 最大回复长度 | `max_response_length` | `500` | 范围 50-5000 字符 | | 附加指令 | `custom_prompt_suffix` | 空 | 最大 1000 字;页面提示其作用范围 | | 自动应用标签 | `auto_label_enabled` | false | 与“标签建议”功能分开控制 | | 自动创建跟进 | `auto_follow_up_enabled` | false | 默认关闭 | | 自动回复客户 | `auto_reply_enabled` | false | 高风险开关,需二次确认并提示绑定 Assistant/Inbox | Assistant 自身的 Temperature、Guardrails、Response Guidelines 优先级高于账户默认值。 --- ## 7. 配置来源 - Copilot 始终启用,不存在部署级启用开关。 - Provider、Base URL、API Key 与默认模型仅允许通过页面和对应 API 配置。 - 不定义或读取任何 Captain/Copilot 环境变量,也不读取 `config*.yaml` 中的 Provider、模型或启用配置。 - 数据库没有完整配置时,业务 API 返回“Provider 未配置”,不会使用空 Key 请求外部服务。 - 页面、菜单、接口响应文案使用“Copilot”;内部 Captain 领域名保持不变。 --- ## 8. 存储与密钥呈现 ### 8.1 存储方案 复用 `installation_configs`,但通过专用 `CopilotConfigService` 管理以下固定键,不允许前端直接操作通用键值 CRUD: | InstallationConfig Name | 内容 | |---|---| | `COPILOT_PROVIDER_CONFIG` | 不含密钥的 JSON 配置 | | `COPILOT_CHAT_API_KEY` | Chat Provider API Key 明文 | | `COPILOT_EMBEDDING_API_KEY` | 独立 Embedding Provider API Key 明文;复用 Chat 凭据时为空 | ### 8.2 API Key 处理要求 - Copilot API Key 直接以明文写入 `installation_configs.value`,不做 AES-256-GCM 或其他应用层加密。 - 数据库、备份和运维访问权限承担密钥保护责任;密钥不得进入普通配置导出、日志或错误响应。 - API 响应只返回:`configured: true/false`、`masked_value: "sk-****abcd"`。 - 更新请求中省略 `api_key` 表示保留原值;`clear_api_key: true` 才允许清除。 - 错误日志、审计日志、连接测试响应均不得包含 Key、Authorization Header 或完整请求体。 --- ## 9. 后端 API 规划 ### 9.1 平台级接口(SuperAdmin) | 方法 | 路径 | 用途 | |---|---|---| | GET | `/platform/api/v1/copilot/config` | 获取平台配置、掩码和状态 | | PUT | `/platform/api/v1/copilot/config` | 校验并保存平台配置 | | POST | `/platform/api/v1/copilot/config/test` | 用候选配置测试 Chat 和 Embedding,不保存 | | GET | `/platform/api/v1/copilot/embeddings/reindex` | 获取文章 Embedding 重建进度 | | POST | `/platform/api/v1/copilot/embeddings/reindex` | 后台启动文章 Embedding 重建 | 连接测试返回: ```json { "chat": { "ok": true, "latency_ms": 320, "model": "gpt-4o-mini" }, "embedding": { "ok": true, "latency_ms": 180, "model": "text-embedding-3-small", "dimensions": 1536 } } ``` 失败响应仅返回归一化错误:认证失败、Endpoint 不可达、模型不存在、限流、超时、响应格式不兼容、Embedding 维度不匹配。 ### 9.2 账户级接口(Administrator/SuperAdmin) | 方法 | 路径 | 用途 | |---|---|---| | GET | `/api/v1/accounts/:account_id/copilot/config` | 返回平台只读状态 + 当前账户配置 | | PUT | `/api/v1/accounts/:account_id/copilot/config` | 更新账户功能、模型和回复行为 | 旧 `/captain/preferences` 接口保留兼容,但新页面只调用 `/copilot/config` 聚合接口。 --- ## 10. 运行时 Provider 设计 使用 `llm.ProviderManager` 替代 bootstrap 中固定注入单个 `llm.Provider`: ```text CopilotConfigService → 解析有效平台配置 → 构造候选 Chat Provider + Embedding Provider → 执行轻量健康校验 → 原子替换 Provider Snapshot → 新请求使用新 Snapshot,进行中的请求继续完成 ``` 核心约束: 1. 保存失败或 Provider 初始化失败时,不替换当前可用配置。 2. Service 依赖 `ProviderResolver`,每次任务开始时获取不可变 Snapshot。 3. Snapshot 包含 Chat Provider、Embedding Provider、默认模型和生成参数。 4. 账户模型解析顺序:`账户 feature model → 平台 chat.model → provider default`。 5. 所有硬编码 `gpt-4`、静态模型和直接读取启动配置的调用都必须迁移到 resolver。 6. Provider 配置不完整时返回明确业务错误,不向外部服务发请求。 Embedding 维度由迁移 `000056_make_article_embedding_dimension_dynamic` 改为动态 pgvector 列;保存维度变更后由 SuperAdmin 通过重建接口重新生成全部文章向量,页面展示进度和失败状态。 --- ## 11. 前端交互要求 - 菜单展示名称固定为“Copilot 配置”。 - 页面顶部显示状态徽标:配置完整、配置不完整、连接正常、连接异常。 - API Key 输入框默认空白并显示“已配置”;不把掩码当作真实值回传。 - Provider 改变时自动填充推荐 Base URL,但不覆盖用户已经编辑的值。 - Anthropic + 复用 Chat Embedding 凭证属于无效组合,前端即时提示,后端再次拒绝。 - “测试连接”分别展示 Chat 与 Embedding 结果。 - 保存成功后展示实际生效的 Provider、模型和时间。 - 修改 Embedding Dimensions 时展示阻断式警告,不允许无确认保存。 - Account Administrator 看到平台配置状态但不能看到 Endpoint 之外的敏感信息,也不能编辑平台配置。 --- ## 12. 实施阶段 ### 阶段 1:配置闭环(P0) 1. 新增 Copilot typed config DTO、Service 和平台/账户 API。 2. 接入 InstallationConfig 明文密钥存储、API 掩码和不回显规则。 3. 实现 Provider 工厂的 OpenAI、Anthropic、OpenAI-compatible 分支。 4. 实现 `CopilotProviderManager` 和热切换。 5. 新建“Copilot 配置”页面和菜单,移除安装类型限制。 6. 接通 Chat/Embedding 连接测试。 ### 阶段 2:运行链路统一(P0) 1. 清理 Service 中硬编码模型。 2. 将账户 feature model 接入所有对应 LLM 请求。 3. 统一 Temperature、Max Tokens、Timeout、Retry 的解析。 4. Provider 未配置时返回统一错误码和前端提示。 ### 阶段 3:账户策略与质量(P1) 1. 聚合现有 CaptainPreference 回复行为配置。 2. 增加配置变更审计事件,但不记录密钥。 3. 增加 Provider 健康状态和最近测试时间。 4. 提供知识库 Embedding 重建入口和进度状态。 ### 阶段 4:后续能力(P2) - Provider 动态模型列表发现与缓存。 - 独立语音转写 Provider。 - Token 用量、成本和账户配额。 - Langfuse/OpenTelemetry AI 调用观测。 - 多 Provider fallback;首期不自动跨供应商降级,避免数据和成本不可控。 --- ## 13. 验证结果 ### 后端自动化 - `GOCHAT_TEST_DB=sqlite go test ./internal/... ./pkg/... ./cmd/...` 通过。 - `go test ./...` 通过。 - 覆盖明文存储、掩码/不回显、保留/替换/清除、候选测试不落库、Provider 热切换、 OpenAI/OpenAI-compatible/Anthropic、超时/重试、统一安全错误、动态 Embedding 维度和重建状态。 - Service → `ProviderManager` → Fake HTTP Provider 集成测试证明 editor、copilot、assistant、 label suggestion 分别使用账户配置的功能模型,未配置时由 Manager 回退平台默认模型。 - Assistant 显式配置的 Temperature(包括 `0`)优先于平台默认值;未显式配置时继续使用平台值。 - OpenAI-compatible 的错误响应与 malformed SSE chunk 不进入日志或健康检查响应;对外只返回稳定错误类别。 - SuperAdmin 平台权限、Administrator 账户权限和 Agent 禁止访问均有 Handler/Middleware 回归测试。 - 配置变更写入 `audits`,只记录 Provider、模型、维度、配置状态和是否变更 Key,不记录 Key 或掩码。 ### 前端自动化 - Vitest 覆盖菜单/路由、Pinia 平台与账户请求、SuperAdmin 识别、Provider 条件字段、 API Key 不回填/清除确认、清除 Key 时允许保存但禁止连接测试、Anthropic 非法组合、 维度确认、回复行为和自动回复确认。 - `pnpm build` 通过。 ### 真实运行验证 - Playwright 在 `http://127.0.0.1:3036/app/accounts/1/settings/copilot` 验证页面名称、路由、 SuperAdmin 可编辑态、无框架错误覆盖,以及 1440×1000 和 390×844 布局。 - 本地 Fake LLM Server 验证候选连接测试同时调用 Chat 与 Embedding;请求使用页面填写的 `gpt-4o-mini`、`text-embedding-3-small`、Bearer Key 和 `dimensions: 1536`。 - 候选测试前数据库无 Copilot Key;保存后 `COPILOT_CHAT_API_KEY` 明文为测试 Key;刷新页面后 输入框为空,仅显示掩码,再次测试使用已保存 Key 成功。 - PostgreSQL 审计记录包含 Account、SuperAdmin、Provider/模型/维度和 Key 变更布尔值, 不包含 API Key 或掩码。 --- ## 14. 验收标准 1. ✅ 自托管 Community 安装可以进入“设置 → Copilot 配置”。 2. ✅ 只有 SuperAdmin 可以修改平台 Provider 和 API Key。 3. ✅ API Key 明文落库,所有读取接口只返回掩码/状态且日志不记录密钥。 4. ✅ 页面可以独立测试 Chat 与 Embedding 连接。 5. ✅ 保存有效配置后不重启即可让新 LLM 请求使用新 Provider。 6. ✅ editor/copilot/assistant/label suggestion 使用账户模型,未指定时回退平台默认模型。 7. ✅ Anthropic Chat 可以搭配独立 OpenAI-compatible Embedding 配置。 8. ✅ Provider 不可用或未配置时返回稳定错误码,不使用空 Key 请求外部服务。 9. ✅ 候选测试和失败配置不会替换当前正在工作的 Provider。 10. ✅ 相关后端测试、前端测试/构建和浏览器端到端验证通过。 --- ## 15. 本次明确不做 - 不将所有 `Captain` 代码、表名和 API 全量改名为 `Copilot`。 - 不允许普通 Agent 或任意 Account Administrator 修改平台 API Key。 - 不提供 Copilot 总开关、环境变量配置或 YAML Provider 配置。 - 不在首期支持任意 Header、关闭 TLS 校验或输出完整 LLM 请求日志。 - 不展示或提供复制已保存 API Key 明文的能力。 - 不在首期实现自动跨 Provider fallback。 - 不把 Assistant 的知识库、工具、Guardrails 和 Inbox 绑定搬入 Copilot 配置页。