370 lines
19 KiB
Markdown
370 lines
19 KiB
Markdown
# 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 配置页。
|