Files
Your Name cf46b27610 fix: P0-1 RateLimiter并发写安全 + P0-2工单操作错误码区分 + P1 rows.Close修复
P0-1 (limits.go): Allow()方法改为全程使用写锁保护counters map读写,避免RLock写入时的data race
P0-2 (ticket_workflow.go+ticket_handler.go): Assign/Resolve/Close操作先查询ticket存在性和状态,返回明确的CS_TICKET_4001/CS_TKT_4002/CS_TICKET_4092/CS_TICKET_4093错误码,handler根据错误前缀路由HTTP状态码
P1-1 (ticket_store.go): 移除GetStats中3处手动rows.Close(),只保留defer Close()
2026-05-01 20:56:25 +08:00

778 lines
37 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.
# AI-Customer-Service 智能客服系统 — 高层设计文档 (HLD)
> 版本v1.0
> 负责人TechLead
> 目标读者后端开发、QA、SRE
> 状态:初稿
---
## 1. 设计目标与约束
### 1.1 核心目标
| 指标 | 基准值 | 目标值 | 验证方式 |
|------|--------|--------|---------|
| 人工客服介入率 | 100% | ≤ 40% | 转人工工单数 / 总会话数 |
| 首次响应时间 | 人工排班时段 | ≤ 10 秒 | 用户消息到达至首次回复的 P99 |
| 常见问题一次解决率 | 0 | ≥ 75% | 用户标记已解决 / (总会话 - 明确转人工) |
| 用户满意度 CSAT | 无 | ≥ 4.0 / 5.0 | 每周抽样调查 |
| 系统可用性 | 无 | ≥ 99.5% | 健康检查通过率 7 天滑动窗口 |
### 1.2 技术约束(强制性)
- **语言**: Go 1.22+
- **HTTP 框架**: 标准库 `net/http` + 自定义中间件(禁止引入 Gin/Echo
- **数据库**: PostgreSQL 15+ ,驱动 `jackc/pgx/v5`
- **缓存**: Redis客户端 `redis/go-redis/v9`
- **配置**: YAML + Viper环境变量覆盖敏感字段
- **日志/审计**: 结构化日志,审计事件模型与 supply-api/ 一致
- **错误码**: `{SOURCE}_{CATEGORY}_{CODE}` 格式,例如 `CS_SES_4001`
- **健康检查**: `/actuator/health``/actuator/health/live``/actuator/health/ready`
- **测试**: Go testing + testify覆盖率门槛 domain ≥ 70%、service/handler ≥ 80%
### 1.3 运行模式
本系统必须同时支持两种运行模式:
| 模式 | 特征 | 部署方式 | 适用场景 |
|------|------|---------|---------|
| **独立运行** | 自有 `cmd/ai-customer-service/main.go`,独立数据库 schema独立 docker-compose | `docker-compose up` 或单独容器 | 外部用户只需要客服能力 |
| **集成运行** | 作为 Go module 被 `gateway/` 引入,共享数据库连接池和配置 | 编译时作为子模块编译,运行时挂载到 gateway 主进程 | 立交桥用户希望获得一体化客服能力 |
**集成约束**:
- 独立运行时,系统必须提供完整的 HTTP API 、Webhook 接入和运营后台。
- 集成运行时,系统必须提供 `IntegrationPlugin` 接口,允许主程序通过配置开关启用/禁用各模块。
- 数据库 schema 必须使用独立的 `cs_` 前缀,避免与主项目表名冲突。
- 配置文件必须支持分离加载:独立运行时读取自己的 `config.yaml`,集成运行时合并到主项目配置。
---
## 2. 系统架构总览
### 2.1 逻辑架构图
```
+---------------------+ +---------------------+ +---------------------+
| 渠道层 (Gateway) | | 运营后台 (Web) | | 外部系统 |
| - Telegram Bot | | - 工单看板 | | - LLM 供应商 A |
| - Discord Bot | | - 会话历史 | | - LLM 供应商 B |
| - 微信公众号 | | - 知识库管理 | | - 向量数据库 |
| - 网页 Widget | | - 转人工统计 | | - 新闻云/火山引擎 |
+----------+----------+ +----------+----------+ +----------+----------+
| | |
v v v
+-----------------------------------------------------------------------------+
| AI-Customer-Service Core Layer |
| +----------------+ +----------------+ +----------------+ +-----------+ |
| | Channel Adapter| | Intent Engine | | RAG Engine | | Dialog | |
| | (渠道适配器) | | (意图识别) | | (知识库检索) | | Manager | |
| +----------------+ +----------------+ +----------------+ +-----------+ |
| +----------------+ +----------------+ +----------------+ +-----------+ |
| | Diagnosis Svc | | Handoff Svc | | Ticket Svc | | Knowledge | |
| | (诊断查询) | | (转人工) | | (工单管理) | | Base Svc | |
| +----------------+ +----------------+ +----------------+ +-----------+ |
| +----------------+ +----------------+ +----------------+ +-----------+ |
| | LLM Client | | Auth/Identity | | Audit Svc | | Monitor | |
| | (模型调用) | | (身份校验) | | (审计日志) | | Svc | |
| +----------------+ +----------------+ +----------------+ +-----------+ |
+-----------------------------------------------------------------------------+
| | |
v v v
+---------------------+ +---------------------+ +---------------------+
| PostgreSQL (cs_*) | | Redis | | 外部只读 API |
| - cs_sessions | | - 会话上下文 | | - supply-api/ |
| - cs_tickets | | - 知识库缓存 | | - token-runtime/ |
| - cs_kb_entries | | - 频率限制 | | - NewAPI/Sub2API |
| - cs_audit_logs | | - 工单锁 | | |
+---------------------+ +---------------------+ +---------------------+
```
### 2.2 组件划分与职责
| 组件 | 职责 | 独立/集成兼容 |
|------|------|-------------|
| **Channel Adapter** | 封装各渠道的 Webhook 接口差异,将外部消息转换为内部统一消息格式 | 两种模式均支持,集成时通过 gateway/ 路由接入 |
| **Intent Engine** | 基于 LLM 的意图识别,输出意图类别、置信度、实体提取 | 两种模式均支持 |
| **RAG Engine** | 知识库向量检索 + 重排序,输出相关文档片段 | 两种模式均支持 |
| **Dialog Manager** | 会话状态管理、上下文维护(最近 5 轮)、转人工判断 | 两种模式均支持 |
| **Diagnosis Service** | 调用 supply-api / token-runtime 只读接口查询用户配额、Token 消耗、错误日志 | 两种模式均支持,集成时通过内部接口调用 |
| **Handoff Service** | 转人工判断逻辑:置信度低、用户要求、敏感意图、身份失败 | 两种模式均支持 |
| **Ticket Service** | 工单创建、分配、状态迁移、关闭、会话上下文附加 | 两种模式均支持 |
| **Knowledge Base Service** | 知识库条目增删改查、索引管理、引用统计 | 两种模式均支持 |
| **LLM Client** | 多供应商 LLM 调用、failover、超时处理、流量控制 | 两种模式均支持 |
| **Auth/Identity Service** | 渠道用户身份校验、立交桥账户关联、API Key 前缀匹配 | 两种模式均支持 |
| **Audit Service** | 审计事件捕获、存储、查询 | 两种模式均支持 |
| **Monitor Service** | 埋点事件收集、指标汇总、暴露 Prometheus /metrics | 两种模式均支持 |
---
## 3. 核心模块设计
### 3.1 渠道适配器 (Channel Adapter)
#### 3.1.1 设计目标
封装 Telegram、Discord、微信、网页 Widget 的消息格式差异,对内部提供统一的 `UnifiedMessage` 结构。
#### 3.1.2 核心结构
```go
type UnifiedMessage struct {
MessageID string // 渠道原生消息 ID
Channel string // telegram | discord | wechat | widget
OpenID string // 渠道用户唯一标识
UserID string // 立交桥账户 ID已绑定时
Content string // 消息内容(已过滤)
ContentType string // text | image | file | voice
Timestamp time.Time
ReplyTo string // 回复的消息 ID
}
type ChannelAdapter interface {
ParseWebhook(r *http.Request) (*UnifiedMessage, error)
SendReply(ctx context.Context, msg *UnifiedMessage, reply string) error
ValidateWebhook(r *http.Request) error // 验证 Webhook 签名
ChannelType() string
}
```
#### 3.1.3 渠道特定处理
| 渠道 | 接入方式 | 特殊处理 |
|------|---------|---------|
| Telegram | Webhook / 长连接 | 支持 Markdown 格式,消息长度限制 4096 字符 |
| Discord | Webhook / Bot API | 支持 Embed 格式,速率限制 5 次/秒 |
| 微信 | 客服消息 Webhook | 需要签名验证,回复时间窗口 48 小时 |
| Widget | WebSocket / SSE | 支持实时打字效果,跨域配置 CORS |
#### 3.1.4 消息过滤与安全
- 图片、文件、语音类消息直接返回 "暂不支持该类型消息",不解析、不存储。
- 内容长度 > 2000 字符时,截断至 2000 字符并提示。
### 3.2 对话引擎 (Dialog Engine)
#### 3.2.1 会话状态机
```
├── idle (空闲)─────────────────────────┐
│ │ │
│ 新消息 │ 超时30分钟
│ ↓ ↓
├── processing (处理中)──────────────────┘
│ │
│ 处理完成 │
│ ↓
├── waiting_feedback (等待用户反馈)───────────┐
│ │ │
│ 解决/未解决 │ 超时30分钟
│ │ ↓
│ ↓ closed (关闭)
├── handoff (已转人工)────────────────────────┘
│ │
│ 工单关闭 → closed
```
#### 3.2.2 上下文管理
- 每个会话保留最近 5 轮对话(用户 5 条 + 机器人 5 条 = 10 条)。
- 超出部分从 Redis List 中自动清理,不再参与 LLM 上下文。
- 会话超时 30 分钟无消息则自动关闭。
#### 3.2.3 处理流程
```
1. 接收 UnifiedMessage
2. 身份校验:已绑定→提取 UserID未绑定→请求邮箱/前缀校验
3. 意图识别LLM 输出 [意图, 置信度, 实体]
4. 判断:
a. 敏感意图(退款/封禁/安全)→ 直接转人工P1 工单)
b. 用户明确要求人工 → 转人工
c. 置信度 < 0.60 → 转人工
d. 其他 → 知识库检索 + LLM 生成回复
5. 回复用户,等待反馈
6. 用户反馈 "已解决" → 会话关闭
7. 用户反馈 "未解决" → 计算轮次,超过 3 轮 → 转人工
```
### 3.3 意图识别 (Intent Engine)
#### 3.3.1 意图分类
| 意图类别 | 示例 | 置信度阈值 | 处理方式 |
|---------|------|-----------|---------|
| api_key_management | "怎么生成 API Key" | ≥ 0.85 | 知识库 + 操作指引 |
| quota_query | "我的配额还剩多少" | ≥ 0.85 | 知识库 + 诊断查询 |
| model_routing | "怎么配置模型路由" | ≥ 0.85 | 知识库 + 代码示例 |
| error_debug | "返回 429 是什么意思" | ≥ 0.85 | 知识库 + 错误码释义 |
| billing | "怎么开发票" | ≥ 0.85 | 知识库 + 流程链接 |
| sensitive_refund | "我要申请退款" | ≥ 0.70 | **强制转人工** |
| sensitive_ban | "我的账户被封了" | ≥ 0.70 | **强制转人工** |
| sensitive_security | "我的数据泄露了" | ≥ 0.70 | **强制转人工** |
| handoff_request | "找人工、投诉" | ≥ 0.90 | **强制转人工** |
| unknown | 无法分类 | < 0.60 | 转人工 |
#### 3.3.2 LLM 调用提示词策略
```
系统 Prompt 结构:
1. 角色:"你是立交桥平台的智能客服助手,仅回答与立交桥相关的问题。"
2. 范围限制:"不要回答与立交桥无关的问题。不要提供内部系统架构、密钥、服务器地址等敏感信息。"
3. 数据隔离:"仅使用当前用户的数据进行查询。如果用户未提供身份信息,不能查询任何个人数据。"
4. 输出格式JSON含 intent、confidence、entities、needs_human、sensitive 字段
```
#### 3.3.3 Failover 策略
- 主模型超时 5 秒 → 切换备用模型供应商。
- 备用模型也超时 5 秒 → 返回兑底回复 + 自动生成工单。
- 兑底回复不依赖大模型,为静态模板:"当前咨询量较大,请稍后或提交工单由人工处理。"
### 3.4 RAG 知识库引擎
#### 3.4.1 索引管理
- 知识库条目使用 Markdown 格式,分块后通过嵌入模型生成向量。
- 向量存储于向量数据库Milvus / Qdrant / PGVector检索延迟 P99 < 200ms。
- 新条目发布后 30 秒内生效(异步重新索引)。
#### 3.4.2 检索流程
```
1. 用户问题 → 嵌入模型生成查询向量
2. 向量数据库 Top-K 检索K=5
3. 重排序:基于相关性 + 条目引用次数 + 最近更新时间
4. 取 Top-3 作为上下文片段
5. 拼接到 LLM Prompt 中生成回复
```
#### 3.4.3 知识库缺失处理
- 检索无结果且意图置信度 < 0.60 → 直接转人工。
- 记录 "知识库未命中" 事件,每日汇总给运营团队。
### 3.5 诊断服务 (Diagnosis Service)
#### 3.5.1 只读查询范围
| 查询类型 | 调用方 | 超时 | 失败处理 |
|---------|--------|------|---------|
| 用户身份校验 | supply-api/ 内部接口 | 2s | 请求邮箱二次校验 |
| 配额查询 | token-runtime/ 内部接口 | 2s | 回复通用说明,提示稍后重试 |
| Token 消耗 | token-runtime/ 内部接口 | 2s | 同上 |
| 最近错误日志 | supply-api/ 内部接口 | 3s | 回复通用排查步骤 |
#### 3.5.2 安全限制
- 所有查询必须携带当前会话的 user_id系统不允许跨用户查询。
- API Key 前缀匹配时,若匹配到多个账户,请求邮箱二次校验;仍无法确定则转人工。
- 错误的 API Key 或密码不记录,仅记录失败次数与事件类型。
### 3.6 转人工机制 (Handoff Service)
#### 3.6.1 转人工触发条件(任意满足即触发)
| 条件 | 工单优先级 | 备注 |
|------|-----------|------|
| 意图置信度 < 0.60 | P2 | 标记原因:意图不明 |
| 用户发送“人工客服”等关键词 | P2 | 标记原因:用户要求 |
| 敏感意图(退款/封禁/安全) | P1 | 标记原因:敏感问题 |
| 身份校验失败累计 3 次 | P2 | 标记原因:身份失败 |
| 多轮对话未解决(> 3 轮) | P2 | 标记原因:未解决 |
| 主备模型均故障 | P1 | 标记原因:模型故障 |
#### 3.6.2 工单分配逻辑
- 未处理工单按优先级P1 > P2 > P3与时间升序排列。
- 客服点击“接收”后,工单状态在 1 秒内变更为 “处理中”并锁定为该客服。
- 排队超过 15 分钟向用户发送排队进度通知。
### 3.7 知识库管理 (Knowledge Base Service)
#### 3.7.1 条目结构
```go
type KBEntry struct {
ID string // UUID
Title string // 标题
Content string // Markdown 内容
Category string // api_key | quota | billing | routing | error_code | onboarding | other
Tags []string // 标签
ReferenceCount int // 被引用次数
LastQueriedAt time.Time // 最近被查询时间
Status string // draft | published | deprecated
CreatedBy string
CreatedAt time.Time
UpdatedAt time.Time
Version int // 乐观锁
}
```
#### 3.7.2 更新机制
- 运营后台增删改查条目,点击“发布”后 30 秒内生效。
- 产品文档变更时,知识库更新为发布 checklist 项。
- 每周生成知识库未命中报告,驱动文档补充。
### 3.8 运营后台
#### 3.8.1 核心视图
| 视图 | 内容 | 权限 |
|------|------|------|
| 工单看板 | 未处理工单按优先级与时间排列,支持分配、关闭、标记 | cs:agent |
| 会话历史 | 用户与机器人的完整对话,支持搜索与筛选 | cs:agent, cs:admin |
| 知识库管理 | 条目增删改查、发布、引用统计 | cs:admin |
| 转人工统计 | 每日 Top 10 转人工原因饼图 | cs:admin |
| 模型回复质检 | 每日抽样 5% 对话,运营人员可标记错误答案 | cs:admin |
### 3.8.X 运营后台数据模型扩展
#### cs_agent_sessions — 客服人员会话绑定
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `id` | UUID | PK | |
| `agent_id` | VARCHAR(64) | NOT NULL | 客服人员ID |
| `ticket_id` | UUID | NOT NULL, FK | 关联工单 |
| `joined_at` | TIMESTAMPTZ | NOT NULL | 加入时间 |
| `left_at` | TIMESTAMPTZ | NULL | 离开时间 |
#### cs_agent_stats — 客服统计(每日聚合)
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `id` | BIGSERIAL | PK | |
| `agent_id` | VARCHAR(64) | NOT NULL | |
| `date` | DATE | NOT NULL | |
| `tickets_handled` | INT | DEFAULT 0 | 处理工单数 |
| `avg_handle_time_sec` | INT | DEFAULT 0 | 平均处理时长 |
| `handoff_count` | INT | DEFAULT 0 | 被转接次数 |
| `csat_score` | DECIMAL(3,2) | NULL | 用户满意度 |
### 3.8.Y 运营后台核心API
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/v1/ai-customer-service/dashboard/stats` | 获取今日统计(会话量/转人工率/解决率/CSAT |
| GET | `/api/v1/ai-customer-service/dashboard/handoff-reasons` | 获取转人工原因分布 Top10 |
| GET | `/api/v1/ai-customer-service/dashboard/kb-miss-rate` | 获取知识库未命中率趋势 |
---
## 4. 数据模型设计
### 4.1 核心实体关系图 (ER)
```
+----------------+ +----------------+ +----------------+
| cs_sessions |<----->| cs_messages |<----->| cs_tickets |
+----------------+ +----------------+ +----------------+
| |
| |
v v
+----------------+ +----------------+ +----------------+
| cs_kb_entries | | cs_audit_logs | | cs_channel_bindings |
+----------------+ +----------------+ +----------------+
```
### 4.2 数据表结构
#### 4.2.1 `cs_sessions` — 会话
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `id` | UUID | PK, 默认 gen_random_uuid() | 会话唯一标识 |
| `channel` | VARCHAR(16) | NOT NULL, CHECK IN ('telegram','discord','wechat','widget') | 渠道 |
| `open_id` | VARCHAR(128) | NOT NULL | 渠道用户标识 |
| `user_id` | VARCHAR(64) | NULL | 立交桥账户 ID已绑定时 |
| `status` | VARCHAR(16) | NOT NULL, DEFAULT 'idle', CHECK IN ('idle','processing','waiting_feedback','handoff','closed') | 会话状态 |
| `turn_count` | INT | NOT NULL, DEFAULT 0 | 已进行轮次 |
| `last_message_at` | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 最后消息时间 |
| `created_at` | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| `updated_at` | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |
**索引**: `CREATE INDEX idx_sessions_channel_openid ON cs_sessions(channel, open_id) WHERE status != 'closed';`
#### 4.2.2 `cs_messages` — 消息
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `id` | UUID | PK | 消息 ID |
| `session_id` | UUID | NOT NULL, FK -> cs_sessions | 所属会话 |
| `direction` | VARCHAR(8) | NOT NULL, CHECK IN ('in','out') | in=用户发送, out=机器人回复 |
| `content` | TEXT | NOT NULL | 消息内容 |
| `content_type` | VARCHAR(16) | NOT NULL, DEFAULT 'text' | text | image | file | voice |
| `intent` | VARCHAR(32) | NULL | 意图类别(仅 in 方向) |
| `confidence` | DECIMAL(3,2) | NULL | 置信度0.00-1.00 |
| `model_provider` | VARCHAR(32) | NULL | 使用的 LLM 供应商 |
| `latency_ms` | INT | NULL | 生成回复耗时(仅 out 方向) |
| `created_at` | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
**索引**: `CREATE INDEX idx_messages_session_id ON cs_messages(session_id, created_at DESC);`
#### 4.2.3 `cs_tickets` — 工单
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `id` | UUID | PK | 工单 ID |
| `session_id` | UUID | NOT NULL, FK -> cs_sessions | 来源会话 |
| `user_id` | VARCHAR(64) | NULL | 用户 ID |
| `priority` | VARCHAR(4) | NOT NULL, CHECK IN ('P0','P1','P2','P3') | 优先级 |
| `status` | VARCHAR(16) | NOT NULL, DEFAULT 'open', CHECK IN ('open','assigned','processing','resolved','closed') | 状态 |
| `handoff_reason` | VARCHAR(32) | NOT NULL | 转人工原因 |
| `assigned_to` | VARCHAR(64) | NULL | 分配给的客服人员 ID |
| `context_snapshot` | JSONB | NOT NULL | 会话上下文快照 |
| `resolution` | TEXT | NULL | 处理结果 |
| `created_at` | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| `resolved_at` | TIMESTAMPTZ | NULL | 解决时间 |
| `updated_at` | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |
**索引**: `CREATE INDEX idx_tickets_status_priority ON cs_tickets(status, priority, created_at);`
#### 4.2.4 `cs_kb_entries` — 知识库条目
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `id` | UUID | PK | 条目 ID |
| `title` | VARCHAR(256) | NOT NULL | 标题 |
| `content` | TEXT | NOT NULL | Markdown 内容 |
| `category` | VARCHAR(32) | NOT NULL | 分类 |
| `tags` | VARCHAR(32)[] | DEFAULT '{}' | 标签数组 |
| `reference_count` | INT | NOT NULL, DEFAULT 0 | 被引用次数 |
| `last_queried_at` | TIMESTAMPTZ | NULL | 最近被查询时间 |
| `status` | VARCHAR(16) | NOT NULL, DEFAULT 'draft', CHECK IN ('draft','published','deprecated') | 状态 |
| `created_by` | VARCHAR(64) | NOT NULL | 创建人 |
| `created_at` | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| `updated_at` | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |
| `version` | INT | NOT NULL, DEFAULT 1 | 乐观锁 |
**索引**: `CREATE INDEX idx_kb_status ON cs_kb_entries(status);`
#### 4.2.5 `cs_channel_bindings` — 渠道绑定
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `id` | UUID | PK | 绑定 ID |
| `channel` | VARCHAR(16) | NOT NULL | 渠道 |
| `open_id` | VARCHAR(128) | NOT NULL | 渠道用户标识 |
| `user_id` | VARCHAR(64) | NOT NULL | 立交桥账户 ID |
| `bound_at` | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 绑定时间 |
| `bound_method` | VARCHAR(16) | NOT NULL | oauth | api_key_prefix | email_verify |
**约束**: `UNIQUE(channel, open_id)`
#### 4.2.6 `cs_audit_logs` — 审计日志
与 supply-api/ 审计规范一致,对象类型包括 `cs_session``cs_ticket``cs_kb_entry`
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `id` | UUID | PK | 事件 ID |
| `tenant_id` | VARCHAR(64) | NOT NULL | 工作区 ID |
| `object_type` | VARCHAR(32) | NOT NULL | 对象类型 |
| `object_id` | VARCHAR(64) | NOT NULL | 对象 ID |
| `action` | VARCHAR(16) | NOT NULL | create | update | delete | handoff | resolve |
| `before_state` | JSONB | NULL | 变更前 |
| `after_state` | JSONB | NULL | 变更后 |
| `actor_id` | VARCHAR(64) | NOT NULL | 操作人 ID |
| `source_ip` | VARCHAR(45) | NULL | 来源 IP |
| `created_at` | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
### 4.3 Redis 缓存设计
| Key 模式 | 用途 | TTL |
|----------|------|-----|
| `cs:session:{session_id}` | 会话状态与上下文 | 30 min |
| `cs:rate_limit:{channel}:{open_id}` | 消息频率限制计数 | 1 min |
| `cs:identity_fail:{session_id}` | 身份校验失败次数 | 10 min |
| `cs:kb:vector:{entry_id}` | 知识库条目向量(若使用 Redis 作为向量存储) | 无 |
| `cs:ticket_lock:{ticket_id}` | 工单分配锁 | 5 min |
---
## 5. 关键流程设计
### 5.1 用户问题自助解决流程
```
用户发送消息
Channel Adapter 解析为 UnifiedMessage
Auth/Identity Service 身份校验
Dialog Manager 检查会话状态,更新上下文
Intent Engine 识别意图 + 置信度
是否敏感/人工/低置信度?
│──是 → Handoff Service 生成工单 → 通知用户排队/等待
↓否
RAG Engine 检索知识库
需要用户数据?
│──是 → Diagnosis Service 查询只读 API
↓否
LLM Client 生成回复
Channel Adapter 发送回复
等待用户反馈30 min 超时关闭)
```
### 5.2 转人工流程
```
触发条件满足
Dialog Manager 更新会话状态 → handoff
Ticket Service 创建工单(含会话上下文快照)
Audit Service 记录 handoff 事件
通知渠道:用户收到排队/等待提示
客服后台:工单入队列
客服接收 → 状态变更为 processing
客服解决 → 状态变更为 resolved → 关闭会话
```
### 5.3 大模型故障 Failover 流程
```
LLM Client 调用主模型
超时 5 秒
切换至备用模型
超时 5 秒
返回兑底回复 + 自动生成工单
Monitor Service 记录 failover 事件并触发告警
```
---
## 6. 技术选型理由及备选方案
| 技术点 | 选型 | 理由 | 备选方案 |
|--------|------|------|---------|
| HTTP 框架 | 标准库 net/http | 与 gateway/ 、supply-api/ 一致,避免框架依赖 | 无 |
| 数据库 | PostgreSQL 15+ | 与主项目一致,支持 JSONB 和向量扩展 | 无 |
| 向量数据库 | PGVector | 无需额外部署,与 PostgreSQL 共存,支持中文语义检索 | Milvus (高性能、分布式) / Qdrant (轻量、Cloud-native) |
| LLM 供应商 | 主OpenAI GPT-4o阿里云通义千问 | 中英文理解能力强API 稳定,备用保障国内访问 | Claude / 火山引擎 |
| 嵌入模型 | OpenAI text-embedding-3-small | 成本低、效果好,与 LLM 供应商一致 | 中文嵌入模型(如 BGE |
| 缓存 | Redis | 与主项目一致,支持会话、频率限制 | 无 |
| 消息队列 | 内部 Go channel + worker pool | 足够支撑当前并发,避免额外依赖 | Kafka (未来高并发) |
| 向量索引更新 | 异步 worker | 知识库变更不频繁,异步更新足够 | 无 |
---
## 7. 与立交桥主系统的集成点
### 7.1 Gateway 集成
| 集成点 | 接口形式 | 说明 |
|--------|---------|------|
| 消息接入 | Webhook POST /api/v1/customer-service/webhook/{channel} | Gateway 将渠道消息转发至客服系统 |
| 消息回复 | HTTP POST 回调 | 客服系统调用 Gateway 消息发送接口 |
| 状态查询 | GET /actuator/health | Gateway 健康检查,不健康时跳过客服路由 |
### 7.2 platform-token-runtime 集成
| 集成点 | 接口形式 | 说明 |
|--------|---------|------|
| 配额查询 | 内部 gRPC / HTTP 只读接口 | 延迟 < 500ms带 user_id 校验 |
| Token 消耗查询 | 内部 gRPC / HTTP 只读接口 | 延迟 < 500ms |
| 错误日志查询 | 内部 gRPC / HTTP 只读接口 | 返回最近 5 条 |
### 7.3 supply-api 集成
| 集成点 | 接口形式 | 说明 |
|--------|---------|------|
| 用户身份校验 | 内部 gRPC / HTTP 只读接口 | API Key 前缀匹配、邮箱验证 |
| 审计日志格式 | 约定 | 与 supply-api/ 审计规范一致 |
### 7.4 NewAPI / Sub2API 集成
| 集成点 | 接口形式 | 说明 |
|--------|---------|------|
| Webhook 接入 | 标准化 POST 接口 | NewAPI/Sub2API 可配置将用户消息转发至本系统 |
| 工单推送 | REST API 或 Webhook 回调 | NewAPI/Sub2API 可定期获取待处理工单状态 |
| 知识库共享 | REST API 查询 | NewAPI/Sub2API 可消费知识库数据 |
| 适配层 | Adapter 接口 | 独立部署时通过配置指定对方 Webhook 地址和鉴权信息 |
---
## 8. 安全设计
### 8.1 数据保护
- 客服系统 **仅拥有只读查询权限**。任何写操作(修改配额、重置密码、删除用户)必须通过工单由人工授权后执行。
- 用户数据查询必须携带当前会话的 user_id系统不允许跨用户查询。
- API Key 前缀匹配时不存储完整 API Key。
- 错误的身份信息不记录,仅记录失败次数。
### 8.2 审计日志
- 所有会话创建、转人工、工单状态变更、知识库变更均需记录审计事件。
- 审计事件与 supply-api/ 保持一致的结构和存储方式。
- 保留期 ≥ 90 天。
### 8.3 越权防护
- 运营后台基于 RBAC角色`cs:agent`(客服)、`cs:admin`(运营管理)。
- 客服系统接口调用 supply-api / token-runtime 时使用内部服务账户,不使用用户凭证。
- 内部服务账户仅拥有只读权限。
### 8.4 Prompt Injection 防护
- 系统 Prompt 中明确禁止回复非当前用户数据、禁止提供内部系统架构或密钥。
- 定期红队测试(每月一次),检验 Prompt Injection 防护效果。
- 敏感操作意图(退款/封禁/安全)强制转人工,不走 LLM 生成回复流程。
---
## 9. 性能考量
### 9.1 并发估算
| 场景 | 峰值 QPS | 平均 QPS | 说明 |
|------|-----------|-----------|------|
| 消息接入 | 100 | 20 | 各渠道汇总,含小流量高峰 |
| 知识库检索 | 100 | 20 | 每次用户消息触发 1 次 |
| LLM 调用 | 100 | 20 | 主模型 + 备用模型合并 |
| 只读 API 查询 | 100 | 20 | 并行于 LLM 调用 |
| 运营后台 | 10 | 2 | 内部使用,低并发 |
### 9.2 延迟目标
| 链路 | 目标延迟 |
|------|---------|
| 消息接收到首次回复 | P99 ≤ 10 秒 |
| 意图识别 | P99 ≤ 2 秒 |
| 知识库检索 | P99 ≤ 200 ms |
| 只读 API 查询 | P99 ≤ 3 秒 |
| 工单创建 | P99 ≤ 1 秒 |
| 运营后台页面加载 | P99 ≤ 2 秒 |
### 9.3 存储估算
| 数据 | 每日增量 | 90 天总量 | 说明 |
|------|---------|------------|------|
| 消息 | 50 万条 | 4500 万条 | 平均每条 200 字符 |
| 会话 | 5 万个 | 450 万个 | 含已关闭会话 |
| 工单 | 5000 个 | 45 万个 | 转人工率 10% |
| 审计日志 | 10 万条 | 900 万条 | 含所有事件 |
| 知识库条目 | 稳定 500 条 | 500 条 | 增长缓慢 |
| 向量数据 | ~200 MB | 200 MB | 500 条 × 1536 维 × 4 字节 |
---
## 10. 风险评估与缓解策略
| 风险编号 | 风险描述 | 概率 | 影响 | 缓解策略 |
|---------|---------|------|------|---------|
| R-1 | LLM 幻觉导致错误指导用户配置 | 中 | 高 | 1. 回答范围限制在知识库内容2. 涉及操作必须附带官方文档链接3. 每日抽样 5% 对话质检4. 高风险意图强制转人工 |
| R-2 | 用户通过 Prompt Injection 泄露敏感数据 | 中 | 高 | 1. 系统 Prompt 明确禁止2. user_id 强制校验3. 全量安全审计日志4. 定期红队测试 |
| R-3 | 模型供应商涨价或停服 | 低 | 中 | 1. 至少 2 家供应商2. 30 秒内切换能力3. 兑底回复不依赖大模型 |
| R-4 | 知识库维护跟不上产品迭代 | 高 | 中 | 1. 发布 checklist 强制同步2. 每周未命中报告3. 预留半日/周运营人力 |
| R-5 | Gateway Webhook 接入改造超出预期 | 中 | 中 | 1. Phase 1 先验证网页 Widget 独立接入2. 明确不改造 Gateway 核心路由 |
| R-6 | 数据库连接池耗尽 | 低 | 高 | 1. 连接池监控与预警2. 降级模式:仅返回静态 FAQ 链接3. 容器自动重启 |
### 10.1 威胁建模
| 威胁场景 | 攻击路径 | 影响 | 控制措施 | 验证要求 |
|---------|---------|------|---------|---------|
| Prompt Injection 绕过安全边界 | 用户输入恶意提示词诱导模型泄露内部信息或跨会话数据 | 敏感信息泄露、错误操作建议 | System Prompt 禁止输出内部信息;敏感意图强制转人工;会话级 user_id 强绑定;响应输出增加敏感词审计 | 红队注入样例每月回归;高风险样例必须稳定拒绝 |
| 渠道伪造 Webhook | 外部伪造渠道回调向系统注入假消息/假工单 | 工单污染、审计失真 | 渠道签名校验、时间戳窗口校验、幂等键、防重放 nonce | 每个渠道提供签名失败/重放攻击测试用例 |
| 运营后台越权查询 | 客服/运营绕过 RBAC 查看非授权会话和工单 | 用户隐私泄露 | RBAC + 资源级过滤;后端强制按 user_id / workspace 过滤;审计查询行为 | QA 必测跨用户/跨角色访问 403 |
| Adapter 调用外部只读 API 失控 | 诊断查询未限流导致压垮 supply-api / token-runtime | 上游链路抖动、级联故障 | 限流、超时、熔断、降级静态 FAQ/排障链接 | 压测和故障注入时验证 fail-open/fail-closed 策略 |
| 审计日志篡改或缺失 | 工单/转人工/知识库变更未留痕或被覆盖 | 无法追责、无法回放 | 审计事件单独写入不可变追加失败重试队列90 天保留 | 审计写入失败必须告警且阻断高风险操作 |
### 10.2 设计阶段门控结论
**结论REQUEST_CHANGES补齐实现与验证门禁后方可进入开发**
**放行前必须满足:**
- HLD 中所有关键能力都能映射到真实实现落点渠道接入、意图识别、RAG、转人工、工单、审计、监控。
- TechLead 任务拆解必须继续细化到文件/函数级,确保 Engineer 不会在实现阶段自行改架构。
- QA 必须基于本 HLD 补充调用链检查点:定义 → 装配 → 调用 → 入口。
- 运行模式、OpenAPI、IntegrationPlugin、NewAPI/Sub2API 适配要求均需在后续实现验证中列为阻断项。
**阻断条件:**
- 任一高风险链路Webhook 鉴权、越权访问、审计留痕、降级策略)未提供可执行验证方案。
- 任一关键能力只有接口声明没有真实挂载入口。
- 无法证明独立运行与集成运行两种模式都可交付。
---
## 11. 技术栈与集成约束
### 11.1 统一技术栈
本项目必须与立交桥主项目保持一致:
- **语言**: Go 1.22+
- **HTTP框架**: 标准库 `net/http` + 自定义中间件(禁止引入 Gin/Echo 等第三方框架,保持与 gateway/ 和 supply-api/ 的一致性)
- **数据库**: PostgreSQL 15+ ,驱动 `jackc/pgx/v5`
- **缓存**: Redis客户端 `redis/go-redis/v9`
- **配置**: YAML + Viper环境变量覆盖敏感字段
- **日志/审计**: 结构化日志,审计事件模型与 supply-api/ 一致
- **错误码**: `{SOURCE}_{CATEGORY}_{CODE}` 格式,例如 `CS_SES_4001`
- **健康检查**: `/actuator/health``/actuator/health/live``/actuator/health/ready`
- **测试**: Go testing + testify覆盖率门槛 domain ≥ 70%、service/handler ≥ 80%
### 11.2 独立运行与集成运行
本系统必须同时支持两种运行模式:
| 模式 | 特征 | 部署方式 | 适用场景 |
|------|------|---------|---------|
| **独立运行** | 自有 `cmd/ai-customer-service/main.go`,独立数据库 schema独立 docker-compose | `docker-compose up` 或单独容器 | 外部用户只需要客服能力,不想接入立交桥全套 |
| **集成运行** | 作为 Go module 被 `gateway/` 引入,共享数据库连接池和配置,通过内部接口注册 | 编译时作为子模块编译,运行时挂载到 gateway 主进程 | 立交桥用户希望获得一体化客服能力 |
**集成约束**:
- 独立运行时,系统必须提供完整的 HTTP API、Webhook 接入和运营后台。
- 集成运行时,系统必须提供 `IntegrationPlugin` 接口,允许主程序通过配置开关启用/禁用各模块。
- 数据库 schema 必须使用独立的 `cs_` 前缀,避免与主项目表名冲突。
- 配置文件必须支持分离加载:独立运行时读取自己的 `config.yaml`,集成运行时合并到主项目配置。
### 11.3 NewAPI / Sub2API 适配支持
本系统的核心能力必须能够对接 NewAPI 和 Sub2API 系统:
- **Webhook 接入**: 提供标准化的 Webhook 接口NewAPI/Sub2API 可配置将用户消息转发至本系统。
- **工单推送**: 提供标准化工单接口NewAPI/Sub2API 可定期获取待处理工单状态。
- **知识库共享**: 提供知识库查询接口NewAPI/Sub2API 可消费此数据补充自己的帮助文档。
- **独立部署时**: 通过配置文件指定 NewAPI/Sub2API 的 Webhook 地址和鉴权信息本系统通过适配层Adapter与之交互。
- **集成部署时**: 若立交桥 gateway/ 已接入 NewAPI/Sub2API本系统通过 gateway/ 的内部路由接口接入客服能力。
### 11.4 对外接口契约
- 必须提供 OpenAPI 3.0 接口文档,确保 NewAPI/Sub2API 开发者可以独立接入。
- 接口路径前缀默认为 `/api/v1/customer-service/`,集成运行时可通过配置改为 `/internal/customer-service/`
---
## 12. 可重用的设计模式
| 设计模式 | 来源 | 应用场景 |
|---------|------|---------|
| **Channel Adapter** | 竞品Intercom | 封装渠道差异,支持新渠道插件化扩展 |
| **RAG Pipeline** | 行业实践 | 知识库检索增强生成,与具体业务解耦 |
| **Failover Chain** | LiteLLM | 多 LLM 供应商自动切换 |
| **Dialog State Machine** | 行业实践 | 会话状态管理,支持异步事件驱动 |
| **Integration Plugin** | 本项目设计 | 独立/集成双模式支持,通过接口隔离主项目 |
---
## 13. 变更日志
| 版本 | 日期 | 修改人 | 内容 |
|------|------|--------|------|
| v1.0 | 2026-04-27 | TechLead | 初稿:系统架构、核心模块、数据模型、流程设计、技术选型、集成点、安全、性能、风险 |
---
## 附录 Y参考文档与外部依赖
| 参考项目 | 版本/日期 | URL | 用途 |
|---------|---------|-----|------|
| LiteLLM | v1.40.0 (2026-03) | https://docs.litellm.ai/ | 模型接口标准化、健康检查设计 |
| Sub2API | main分支 (2026-04) | https://github.com/WeI-Shaw/sub2api | 公告系统、用户体系参考 |
| Intercom | - | https://www.intercom.com/ | 客服体验对标 |
| Prometheus | 3.x (2026-Q1) | https://prometheus.io/ | 时序数据存储 |
| VictoriaMetrics | 1.100.x (2026-Q1) | https://victoriametrics.com/ | 时序数据备选存储 |
| Playwright | 1.50.x (2026-Q1) | https://playwright.dev/ | 浏览器自动化 |
| Qdrant | 1.12.x (2026-Q1) | https://qdrant.tech/ | 向量数据库备选 |
| PGVector | 0.8.x (2026-Q1) | https://github.com/pgvector/pgvector | PostgreSQL向量扩展 |
以上版本号为评审时2026-04-28的最新稳定版随着项目开发应定期更新。