digital-employee-platform/docs/plans/2026-05-05-digital-employee-design.md
root 5c13f08c03 docs: add digital employee platform design document
Multi-tenant platform design with unified LLM provider abstraction,
RAG knowledge base, and tenant-isolated data storage. MVP uses SQLite +
ChromaDB, with planned migration to PostgreSQL + pgvector.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-05 05:54:05 +08:00

598 lines
22 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.

# 数字员工平台设计文档
> 日期2026-05-05
> 状态:已批准
## 1. 背景与目标
### 1.1 背景
企业对 AI 数字员工的需求日益增长,但不同企业的业务场景、知识体系、合规要求各不相同。现有解决方案要么是通用聊天机器人(缺乏企业定制),要么是单点 SaaS 产品(数据隔离弱、模型选择受限)。需要一个多租户平台,让每家企业能配置专属数字员工,接入自选 LLM使用私有知识库。
### 1.2 目标
- 构建多租户数字员工平台,支持企业级数据隔离与独立计费
- 统一 LLM Provider 抽象层企业可自选模型提供商OpenAI / 通义千问 / 可扩展)
- RAG 知识库支持企业私有文档上传与检索增强生成
- 提供管理后台与对话前端,形成完整可演示链路
### 1.3 成功标准
| 指标 | MVP 目标 |
|------|----------|
| 租户隔离 | 企业间数据零泄露API Key 独立计费 |
| 对话延迟 | 首 token 响应 < 3s不含 LLM 调用开销 |
| RAG 准确性 | 知识库内问题 Top-3 召回率 > 80% |
| 可演示性 | 管理后台 + 对话界面端到端可用 |
| Provider 扩展 | 新增 Provider 实现工作量 < 1 |
## 2. 范围
### 2.1 MVP 范围P0
1. 企业租户管理CRUD + API Key 配置
2. 数字员工人设配置角色语气专业知识描述
3. 对话接口实时聊天SSE 流式响应
4. RAG 知识库上传文档 分块嵌入 检索增强生成
5. 对话历史存储与查询
### 2.2 MVP 基础版P1
6. 管理后台 UI企业管理员工配置对话监控
7. 对话前端 UI聊天界面
### 2.3 后续迭代P2
8. 用量统计 / 计费
9. 权限控制企业管理员 vs 普通用户
### 2.4 拓展功能P3按价值排序
1. **对话质量评估**自动评估 RAG 回答的准确性与相关性
2. **数字员工模板市场**预置客服/HR/销售等角色模板一键启用
3. **多渠道接入**微信/钉钉/飞书/Web 统一对话接口
4. **审批工作流**敏感操作前需人类确认
5. **审计日志与合规**完整对话审计链路
### 2.5 非目标
- 自训练/微调模型
- 实时语音对话
- 多模态输入图片/视频
- 移动端原生 App
## 3. 方案对比
### 3.1 方案 A轻量直连架构
```
FastAPI → Provider 抽象层 → OpenAI/通义千问 API
→ SQLite + ChromaDB嵌入式向量库
→ 文件存储(对话历史)
```
- 零外部依赖`pip install` 即跑
- 开发周期 1-2
- 单机部署不适合大规模
### 3.2 方案 BLangChain 编排架构
```
FastAPI → LangChain/LangGraph → 多 LLM Provider
→ PostgreSQL + pgvector
→ Redis缓存/会话)
```
- LangChain 500+ 集成LangGraph 支持多步 Agent
- 重依赖升级频繁有 breaking change
- 生产维护成本高
### 3.3 方案 C自研编排 + PostgreSQL 全栈
```
FastAPI → 自研 Provider 抽象 + RAG Pipeline
→ PostgreSQL + pgvector统一存储
→ Redis会话/缓存)
```
- 完全可控无重框架依赖
- PostgreSQL 统一关系数据 + 向量检索
- RAG pipeline 需自建初始工作量较大
### 3.4 决策:方案 A 先行,演进到方案 C
| 阶段 | 存储 | 向量库 | 缓存 | 理由 |
|------|------|--------|------|------|
| MVP | SQLite | ChromaDB | | 零外部依赖快速验证 |
| V1 | PostgreSQL | pgvector | Redis | 生产级统一存储 |
## 4. 推荐方案详细设计
### 4.1 系统架构总览
```
┌─────────────────────────────────────────────────────────┐
│ 前端层 (TypeScript) │
│ ┌──────────────┐ ┌──────────────┐ ┌───────────────┐ │
│ │ 管理后台 UI │ │ 对话前端 UI │ │ 模板市场 UI │ │
│ └──────┬───────┘ └──────┬───────┘ └───────┬───────┘ │
└─────────┼─────────────────┼──────────────────┼──────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────┐
│ API 网关层 (FastAPI) │
│ ┌──────────┐ ┌───────────┐ ┌──────────┐ ┌───────────┐ │
│ │ 租户管理 │ │ 员工配置 │ │ 对话接口 │ │ 知识库管理 │ │
│ └──────────┘ └───────────┘ └──────────┘ └───────────┘ │
└─────────────────────┬───────────────────────────────────┘
┌───────────┴───────────┐
▼ ▼
┌──────────────────┐ ┌─────────────────────┐
│ 对话编排层 │ │ RAG Pipeline │
│ ┌─────────────┐ │ │ ┌─────────────────┐ │
│ │ 会话管理 │ │ │ │ 文档解析/分块 │ │
│ │ Prompt 构建 │ │ │ │ 嵌入生成 │ │
│ │ 流式响应 │ │ │ │ 向量检索 │ │
│ └─────────────┘ │ │ │ 上下文注入 │ │
│ ┌─────────────┐ │ │ └─────────────────┘ │
│ │ Provider │ │ └─────────────────────┘
│ │ 抽象层 │ │
│ │ ┌────────┐ │ │
│ │ │ OpenAI │ │ │
│ │ │ 通义 │ │ │
│ │ │ ... │ │ │
│ │ └────────┘ │ │
│ └─────────────┘ │
└──────────────────┘
┌─────────────────────────────────────────────────────────┐
│ 存储层 │
│ MVP: SQLite + ChromaDB + 文件存储 │
│ V1: PostgreSQL + pgvector + Redis │
└─────────────────────────────────────────────────────────┘
```
### 4.2 核心模块设计
#### 4.2.1 租户管理模块
**数据模型:**
```python
class Tenant:
id: UUID # 租户 ID
name: str # 企业名称
slug: str # URL 标识(唯一)
status: TenantStatus # active / suspended / deleted
created_at: datetime
updated_at: datetime
class TenantConfig:
id: UUID
tenant_id: UUID # 关联租户
llm_provider: str # openai / qwen / anthropic
llm_api_key: str # 加密存储的 API Key
llm_model: str # gpt-4o / qwen-max / ...
llm_base_url: str | None # 自定义 API 端点(可选)
max_tokens_per_month: int # 用量限制
created_at: datetime
updated_at: datetime
```
**API 端点:**
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/api/v1/tenants` | 创建租户 |
| GET | `/api/v1/tenants/{id}` | 获取租户详情 |
| PUT | `/api/v1/tenants/{id}` | 更新租户 |
| DELETE | `/api/v1/tenants/{id}` | 删除租户软删除 |
| PUT | `/api/v1/tenants/{id}/config` | 更新 LLM 配置 |
**安全约束:**
- API Key 使用 AES-256 加密存储密钥从环境变量读取
- 租户间数据严格隔离所有查询强制带 `tenant_id` 过滤
- 软删除策略删除后 30 天可恢复
#### 4.2.2 数字员工配置模块
**数据模型:**
```python
class DigitalEmployee:
id: UUID
tenant_id: UUID # 所属租户
name: str # 员工名称(如"客服小助手"
role: str # 角色customer_service / hr / sales / ...
avatar_url: str | None # 头像
system_prompt: str # 人设系统提示词
greeting: str | None # 开场白
temperature: float # 生成温度 0.0-1.0
max_context_messages: int # 上下文窗口消息数
knowledge_base_ids: list[UUID] # 关联知识库
status: EmployeeStatus # active / inactive
created_at: datetime
updated_at: datetime
```
**API 端点:**
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/api/v1/tenants/{tid}/employees` | 创建数字员工 |
| GET | `/api/v1/tenants/{tid}/employees` | 列出数字员工 |
| GET | `/api/v1/tenants/{tid}/employees/{eid}` | 获取详情 |
| PUT | `/api/v1/tenants/{tid}/employees/{eid}` | 更新配置 |
| DELETE | `/api/v1/tenants/{tid}/employees/{eid}` | 删除 |
#### 4.2.3 对话接口模块
**对话流程:**
```
用户消息
→ 会话加载(历史上下文)
→ RAG 检索(如果员工关联了知识库)
→ 查询向量化
→ ChromaDB/pgvector 相似度检索 Top-K
→ 上下文注入到 Prompt
→ Prompt 构建system + 知识上下文 + 历史消息 + 用户消息)
→ LLM Provider 调用(流式)
→ SSE 流式响应
→ 持久化对话记录
```
**数据模型:**
```python
class Conversation:
id: UUID
tenant_id: UUID
employee_id: UUID
user_id: str # 外部用户标识
title: str | None
created_at: datetime
updated_at: datetime
class Message:
id: UUID
conversation_id: UUID
role: MessageRole # user / assistant / system
content: str
token_count: int | None
sources: list[dict] | None # RAG 引用来源
created_at: datetime
```
**API 端点:**
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/api/v1/tenants/{tid}/employees/{eid}/conversations` | 创建对话 |
| GET | `/api/v1/tenants/{tid}/conversations` | 列出对话 |
| GET | `/api/v1/tenants/{tid}/conversations/{cid}` | 获取对话详情含消息 |
| POST | `/api/v1/tenants/{tid}/conversations/{cid}/messages` | 发送消息SSE 流式响应 |
| DELETE | `/api/v1/tenants/{tid}/conversations/{cid}` | 删除对话 |
**SSE 响应格式:**
```
event: message_start
data: {"conversation_id": "...", "message_id": "..."}
event: token
data: {"content": "你"}
event: token
data: {"content": "好"}
event: sources
data: {"documents": [{"title": "...", "snippet": "...", "score": 0.92}]}
event: message_end
data: {"token_count": 156}
event: error
data: {"code": "provider_error", "message": "..."}
```
#### 4.2.4 RAG 知识库模块
**处理流程:**
```
文档上传
→ 文件类型检测PDF / DOCX / TXT / MD
→ 文档解析(提取纯文本)
→ 文本分块RecursiveCharacterTextSplitter
→ chunk_size: 500, overlap: 50
→ 嵌入生成(调用 Embedding API
→ 向量存储ChromaDB按 tenant_id 隔离命名空间)
→ 元数据存储(文档标题、来源、分块偏移量)
```
**数据模型:**
```python
class KnowledgeBase:
id: UUID
tenant_id: UUID
name: str
description: str | None
embedding_model: str # text-embedding-3-small / ...
chunk_count: int
created_at: datetime
updated_at: datetime
class Document:
id: UUID
knowledge_base_id: UUID
tenant_id: UUID
filename: str
file_type: str # pdf / docx / txt / md
file_size: int
chunk_count: int
status: DocumentStatus # processing / ready / failed
error_message: str | None
created_at: datetime
```
**API 端点:**
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/api/v1/tenants/{tid}/knowledge-bases` | 创建知识库 |
| POST | `/api/v1/tenants/{tid}/knowledge-bases/{kid}/documents` | 上传文档 |
| GET | `/api/v1/tenants/{tid}/knowledge-bases/{kid}/documents` | 列出文档 |
| DELETE | `/api/v1/tenants/{tid}/knowledge-bases/{kid}/documents/{did}` | 删除文档 |
| GET | `/api/v1/tenants/{tid}/knowledge-bases` | 列出知识库 |
| DELETE | `/api/v1/tenants/{tid}/knowledge-bases/{kid}` | 删除知识库 |
#### 4.2.5 LLM Provider 抽象层
**设计原则:**
- 统一接口Provider 可插拔
- 每个企业独立 API Key按租户路由
- 支持流式和非流式调用
- 统一错误处理与重试
```python
from abc import ABC, abstractmethod
from dataclasses import dataclass
@dataclass
class LLMMessage:
role: str # system / user / assistant
content: str
@dataclass
class LLMResponse:
content: str
token_usage: dict
model: str
finish_reason: str
class BaseLLMProvider(ABC):
"""LLM Provider 抽象基类"""
@abstractmethod
async def chat(
self,
messages: list[LLMMessage],
model: str,
temperature: float = 0.7,
max_tokens: int = 2048,
**kwargs,
) -> LLMResponse:
...
@abstractmethod
async def chat_stream(
self,
messages: list[LLMMessage],
model: str,
temperature: float = 0.7,
max_tokens: int = 2048,
**kwargs,
) -> AsyncIterator[str]:
...
@abstractmethod
async def embed(self, texts: list[str], model: str) -> list[list[float]]:
...
class OpenAIProvider(BaseLLMProvider):
"""OpenAI / 兼容 OpenAI 接口的 Provider"""
class QwenProvider(BaseLLMProvider):
"""通义千问 Provider兼容 OpenAI 接口格式)"""
# Provider 注册表
PROVIDERS: dict[str, type[BaseLLMProvider]] = {
"openai": OpenAIProvider,
"qwen": QwenProvider,
}
def get_provider(provider_name: str, api_key: str, base_url: str | None = None) -> BaseLLMProvider:
provider_cls = PROVIDERS.get(provider_name)
if not provider_cls:
raise ValueError(f"Unknown provider: {provider_name}")
return provider_cls(api_key=api_key, base_url=base_url)
```
**Provider 配置约定:**
| Provider | chat model | embedding model | 特殊说明 |
|----------|-----------|----------------|---------|
| openai | gpt-4o / gpt-4o-mini | text-embedding-3-small | 标准 OpenAI API |
| qwen | qwen-max / qwen-plus | text-embedding-v3 | 兼容 OpenAI SDK 格式 |
### 4.3 安全设计
#### 4.3.1 数据隔离
- 所有数据表包含 `tenant_id` 字段
- 所有查询强制 `WHERE tenant_id = :current_tenant`
- API 中间件自动注入 `tenant_id`业务代码无法绕过
- ChromaDB 使用 tenant_id 作为 collection namespace 隔离向量数据
#### 4.3.2 API Key 安全
- LLM API Key 使用 AES-256-GCM 加密存储
- 加密密钥从环境变量 `ENCRYPTION_KEY` 读取不入库不入日志
- 运行时解密仅用于发起 LLM 调用不返回给前端
#### 4.3.3 输入校验
- 所有 API 入参使用 Pydantic 校验
- 文档上传限制单文件 < 20MB仅允许 pdf/docx/txt/md
- 对话消息长度限制单条 < 10000 字符
- 速率限制每租户每分钟 60 次请求
### 4.4 技术栈
| | MVP 技术选型 | V1 演进 |
|----|------------|---------|
| 后端框架 | FastAPI 0.115+ | FastAPI |
| 数据库 | SQLite (aiosqlite) | PostgreSQL 16 |
| 向量库 | ChromaDB 0.5+ | pgvector |
| 缓存 | | Redis |
| ORM | SQLAlchemy 2.0+ (async) | SQLAlchemy 2.0+ |
| 嵌入/LLM | openai SDK | openai SDK |
| 文档解析 | PyPDF2 + python-docx | 同左 |
| 前端框架 | React + TypeScript + Vite | 同左 |
| 前端 UI | Ant Design | Ant Design |
| 对话前端 | 自研 Chat UI 组件 | 同左 |
### 4.5 项目结构
```
digital-employee-platform/
├── backend/
│ ├── app/
│ │ ├── __init__.py
│ │ ├── main.py # FastAPI 入口
│ │ ├── config.py # 配置管理
│ │ ├── database.py # 数据库连接
│ │ ├── models/ # SQLAlchemy 模型
│ │ │ ├── __init__.py
│ │ │ ├── tenant.py
│ │ │ ├── employee.py
│ │ │ ├── conversation.py
│ │ │ └── knowledge.py
│ │ ├── schemas/ # Pydantic 请求/响应模型
│ │ │ ├── __init__.py
│ │ │ ├── tenant.py
│ │ │ ├── employee.py
│ │ │ ├── conversation.py
│ │ │ └── knowledge.py
│ │ ├── api/ # API 路由
│ │ │ ├── __init__.py
│ │ │ ├── v1/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── tenants.py
│ │ │ │ ├── employees.py
│ │ │ │ ├── conversations.py
│ │ │ │ └── knowledge.py
│ │ │ └── deps.py # 依赖注入
│ │ ├── services/ # 业务逻辑
│ │ │ ├── __init__.py
│ │ │ ├── tenant_service.py
│ │ │ ├── employee_service.py
│ │ │ ├── conversation_service.py
│ │ │ ├── knowledge_service.py
│ │ │ └── rag_service.py
│ │ ├── providers/ # LLM Provider 抽象层
│ │ │ ├── __init__.py
│ │ │ ├── base.py
│ │ │ ├── openai_provider.py
│ │ │ └── qwen_provider.py
│ │ └── middleware/ # 中间件
│ │ ├── __init__.py
│ │ └── tenant.py # 租户隔离中间件
│ ├── tests/ # 测试
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ ├── test_tenants.py
│ │ ├── test_employees.py
│ │ ├── test_conversations.py
│ │ ├── test_knowledge.py
│ │ └── test_providers.py
│ ├── alembic/ # 数据库迁移
│ │ └── ...
│ ├── pyproject.toml
│ └── .env.example
├── frontend/
│ ├── src/
│ │ ├── api/ # API 客户端
│ │ ├── components/ # 通用组件
│ │ ├── pages/
│ │ │ ├── admin/ # 管理后台页面
│ │ │ │ ├── Tenants.tsx
│ │ │ │ ├── Employees.tsx
│ │ │ │ └── Knowledge.tsx
│ │ │ └── chat/ # 对话前端页面
│ │ │ └── Chat.tsx
│ │ ├── App.tsx
│ │ └── main.tsx
│ ├── package.json
│ ├── tsconfig.json
│ └── vite.config.ts
└── docs/
└── plans/
```
## 5. 错误处理
| 错误类型 | 处理策略 | HTTP 状态码 |
|---------|---------|------------|
| LLM Provider 调用失败 | 指数退避重试 2 记录日志返回降级提示 | 502 |
| LLM API Key 无效/过期 | 返回配置错误提示不重试 | 401 |
| 租户 API 限流 | 返回 429附带 Retry-After | 429 |
| 文档解析失败 | 标记文档状态为 failed记录错误信息 | 200异步 |
| RAG 无相关结果 | 正常返回不注入知识上下文LLM 按通用知识回答 | - |
| 向量库写入失败 | 重试 1 失败则文档标记 failed | 200异步 |
| 数据库连接失败 | 返回 503记录告警 | 503 |
## 6. 测试策略
### 6.1 单元测试
- Provider 抽象层mock LLM API 响应验证接口一致性
- 服务层mock 数据库验证业务逻辑
- RAG Pipelinemock embedding验证分块检索逻辑
### 6.2 集成测试
- 端到端对话流使用真实 LLM API测试 Key验证完整链路
- 租户隔离创建两个租户验证数据不互渗
- RAG 问答上传文档后提问验证回答包含文档内容
### 6.3 安全测试
- 越权访问租户 A 尝试访问租户 B 资源必须 403
- API Key 不出现在日志和 API 响应中
- 文档上传类型和大小限制
### 6.4 性能测试
- 对话接口p99 < 3s不含 LLM 调用
- 文档上传10MB PDF 解析 < 30s
- 并发单租户 10 并发对话无异常
## 7. 演进路径
```
MVP (方案 A) V1 (方案 C)
───────────── ──────────────
SQLite + ChromaDB ──迁移──→ PostgreSQL + pgvector
文件存储对话历史 ──迁移──→ PostgreSQL JSONB
无缓存 ──新增──→ Redis 会话缓存
单实例部署 ──演进──→ Docker Compose 多服务
基础对话 ──新增──→ 对话质量评估
无模板 ──新增──→ 数字员工模板市场
Web 对话 ──新增──→ 多渠道接入
```