# 数字员工平台设计文档 > 日期: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 方案 B:LangChain 编排架构 ``` 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 Pipeline:mock 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 对话 ──新增──→ 多渠道接入 ```