1290 lines
44 KiB
Markdown
1290 lines
44 KiB
Markdown
# IM 工作台 AI 后端概要设计文档
|
||
|
||
> 版本:V1.0
|
||
> 日期:2026-09-25
|
||
> 关联文档:[IM 工作台 AI 接入需求文档](./IM工作台AI接入需求文档.md) · [IM 工作台 AI 前后端对接文档](./IM工作台AI前后端对接文档.md) · [IM 前后端对接文档](./IM-前后端对接文档.md)
|
||
> DeepSeek 依据:[DeepSeek 首次调用 API](https://api-docs.deepseek.com/zh-cn/) · [DeepSeek Chat Completions API](https://api-docs.deepseek.com/zh-cn/api/create-chat-completion/)
|
||
> 说明:本文描述 IM 工作台 AI 能力的后端概要设计,覆盖智能文档、智能表格、AI 美文、日报/月报 AI 接入、会议笔记 AI 总结、AI 任务、AI 记录、DeepSeek 接入、配置、异步处理、数据表、安全和发布回滚。
|
||
|
||
---
|
||
|
||
## 一、设计目标
|
||
|
||
IM 工作台在现有日报、月报、会议、文件和组织能力基础上新增 AI 能力。后端需要在不破坏原 workbench 业务接口的前提下,提供统一的 AI 能力接入层、任务调度层、记录存储层和 DeepSeek Provider 适配层。
|
||
|
||
后端需要完成以下核心能力:
|
||
|
||
1. **工作台 AI 入口**:按租户、用户、功能开关返回可用 AI 能力、用量限制和入口配置。
|
||
2. **智能文档**:根据用户输入生成结构化文档,支持重新生成、编辑保存、记录查询。
|
||
3. **智能表格**:接收已上传表格文件,解析数据、识别核心指标、生成报告解读,支持记录查询和导出。
|
||
4. **AI 美文**:为普通文案、日报、月报提供同步润色能力,只回填业务表单,不替代原业务提交。
|
||
5. **会议 AI 总结**:基于会议笔记附件和会议上下文生成结论、决策、后续安排,支持编辑保存。
|
||
6. **DeepSeek 接入**:通过 DeepSeek OpenAI 兼容 Chat Completions API 接入模型,统一处理 prompt、超时、重试、错误映射、审计、脱敏和用量统计。
|
||
7. **可靠性**:支持幂等、异步任务、失败重试、并发限制、租户隔离、内容安全和灰度开关。
|
||
|
||
### 设计原则
|
||
|
||
- AI 能力放在 `im_workbench` 服务内建设,登录、租户、用户、组织、文件、会议仍复用 IM 与 workbench 既有能力。
|
||
- 所有 AI 业务表统一使用 `wb_ai_` 前缀。
|
||
- 所有业务数据必须带 `tenant_id`、`tenant_code`、`user_id`;用户身份由 IM Token 解析,客户端不得传入并覆盖身份。
|
||
- 所有对外 ID 统一按字符串返回,包括 `task_id`、`record_id`、`meeting_id`、`file_id`、`user_id`。
|
||
- AI Provider 必须抽象为接口,首版实现 DeepSeek,后续可切换到其他模型供应商。
|
||
- DeepSeek API Key、base_url、model、超时、并发、用量限制全部配置化,不写死在代码里。
|
||
- AI 输入、输出、文件解析内容、prompt 和模型响应都可能包含敏感信息,日志只记录摘要、hash、长度、token 用量和错误码。
|
||
- 同步接口只用于短文本 AI 美文;文档、表格、会议总结统一按异步任务处理。
|
||
- AI 输出默认不作为最终事实,前端必须展示“由 AI 生成,请核对后使用”。
|
||
|
||
---
|
||
|
||
## 二、系统架构
|
||
|
||
### 2.1 服务边界
|
||
|
||
首版建议直接在 `im_workbench` 中新增 AI 模块,避免多服务联调成本。模块内部按 Provider、任务、记录、业务适配做清晰边界,后续可平滑拆成独立 AI Worker 服务。
|
||
|
||
```text
|
||
移动端 / Web
|
||
|
|
||
v
|
||
IM Gateway / Workbench API
|
||
|
|
||
+--------------------> IM Auth / Tenant
|
||
| Token、租户、用户上下文
|
||
|
|
||
v
|
||
im_workbench
|
||
├─ Workbench API AI HTTP 路由、参数校验、响应转换
|
||
├─ AI Application 智能文档、智能表格、AI 美文、会议总结用例
|
||
├─ AI Task Domain 任务状态机、幂等、进度、取消、重试
|
||
├─ AI Record Domain 文档记录、表格记录、会议总结记录
|
||
├─ AI Provider Adapter DeepSeek 适配、Prompt 编排、错误映射
|
||
├─ File Adapter IM 文件下载、表格解析、导出文件上传
|
||
├─ Meeting Adapter 会议、会议笔记、权限和附件
|
||
├─ Worker Kafka/Redis/MySQL 任务消费
|
||
└─ MySQL / Redis / Kafka
|
||
|
|
||
+---------------> DeepSeek API
|
||
| Chat Completions
|
||
+---------------> IM File Service
|
||
| 文件上传、下载、导出
|
||
+---------------> IM User/Org
|
||
用户、租户、权限、组织信息
|
||
```
|
||
|
||
### 2.2 推荐代码分层
|
||
|
||
```text
|
||
workbench/ai/
|
||
├── api/ HTTP Handler、DTO、参数校验
|
||
├── application/
|
||
│ ├── capability/ AI 能力开关和配额
|
||
│ ├── document/ 智能文档用例
|
||
│ ├── table/ 智能表格用例
|
||
│ ├── polish/ AI 美文用例
|
||
│ └── meeting/ 会议 AI 总结用例
|
||
├── domain/
|
||
│ ├── task/ AI 任务状态机、幂等、取消、重试
|
||
│ ├── record/ AI 记录模型
|
||
│ ├── quota/ 用量限制、并发限制
|
||
│ └── prompt/ Prompt 模板、变量、输出 Schema
|
||
├── repository/ MySQL/Redis 数据访问
|
||
├── integration/
|
||
│ ├── deepseek/ DeepSeek Provider 实现
|
||
│ ├── file/ IM 文件服务与导出文件
|
||
│ ├── meeting/ 会议和会议笔记权限
|
||
│ ├── user/ 用户显示名、组织信息
|
||
│ └── safety/ 内容安全检查
|
||
├── worker/ AI 异步任务消费
|
||
├── parser/ Excel/CSV 解析和采样
|
||
├── exporter/ DOCX/PDF 导出
|
||
└── migrations/ wb_ai_ 表结构和增量脚本
|
||
```
|
||
|
||
目录名称可按实际项目框架调整,但 AI Provider、任务状态机、业务用例和基础设施适配层必须解耦,不能在 Handler 中直接拼 prompt 调 DeepSeek。
|
||
|
||
### 2.3 请求上下文
|
||
|
||
Gateway 或中间件鉴权成功后向 AI 模块传递可信上下文:
|
||
|
||
| 字段 | 来源 | 用途 |
|
||
| --- | --- | --- |
|
||
| `request_id` | `X-Request-ID` 或服务端生成 | 全链路追踪 |
|
||
| `tenant_id` | Token/租户解析结果 | 数据隔离和联合索引 |
|
||
| `tenant_code` | `X-Tenant-Code`/Token | 日志、配置、文件服务路由 |
|
||
| `user_id` | Token | 当前操作用户 |
|
||
| `device_id` | Token/请求头 | 多端诊断和风控 |
|
||
| `api_key` | `X-MeetingIM-APIKey` | 复用 IM 客户端鉴权 |
|
||
| `locale` | 请求头/用户配置 | Prompt 语言和返回文案 |
|
||
|
||
### 2.4 外部依赖
|
||
|
||
| 依赖 | 用途 | 失败策略 |
|
||
| --- | --- | --- |
|
||
| IM Auth/Tenant | Token、租户和用户上下文 | 鉴权失败直接拒绝 |
|
||
| IM User/Org | 用户名、组织、参与人展示 | 展示可降级为用户 ID,写权限不可绕过 |
|
||
| Workbench Meeting | 会议、会议笔记、附件权限 | 无权限直接拒绝 |
|
||
| IM File | 表格源文件下载、会议附件读取、导出文件上传 | 文件不可用则任务失败 |
|
||
| DeepSeek API | 文案生成、结构化总结和报告解读 | 超时/失败按策略重试,最终任务失败 |
|
||
| MySQL | 任务、记录、用量、幂等、审计 | 核心状态最终一致来源 |
|
||
| Redis | 短期配额、并发锁、任务进度缓存 | MySQL 兜底,Redis 失败不放宽限制 |
|
||
| Kafka | 异步任务分发 | 不可用时写入任务但保持 `queued`,恢复后补偿扫描 |
|
||
|
||
---
|
||
|
||
## 三、DeepSeek 接入方案
|
||
|
||
### 3.1 接入方式
|
||
|
||
DeepSeek 支持 OpenAI 兼容的 Chat Completions API。服务端封装 `AIProvider` 接口,首版实现 `DeepSeekProvider`。
|
||
|
||
推荐配置:
|
||
|
||
| 配置项 | 示例 | 说明 |
|
||
| --- | --- | --- |
|
||
| `AI_PROVIDER` | `deepseek` | 当前 AI Provider |
|
||
| `DEEPSEEK_BASE_URL` | `https://api.deepseek.com` | OpenAI 兼容接口 base URL |
|
||
| `DEEPSEEK_API_KEY` | `******` | DeepSeek API Key,只放密钥系统 |
|
||
| `DEEPSEEK_DEFAULT_MODEL` | `deepseek-flash` | 默认模型,适合 AI 美文、智能文档 |
|
||
| `DEEPSEEK_COMPLEX_MODEL` | `deepseek-v4-pro` | 复杂表格分析、会议总结可选 |
|
||
| `DEEPSEEK_TIMEOUT_MS` | `60000` | 单次请求超时 |
|
||
| `DEEPSEEK_MAX_RETRIES` | `2` | 供应商 429/5xx/网络错误重试次数 |
|
||
| `DEEPSEEK_TEMPERATURE` | `0.2` | 结构化输出建议较低温度 |
|
||
| `DEEPSEEK_MAX_OUTPUT_TOKENS` | `4096` | 输出上限 |
|
||
|
||
不建议使用旧模型名 `deepseek-chat`、`deepseek-reasoner` 作为新接入默认值。模型名必须配置化,方便 DeepSeek 后续升级时只改配置。
|
||
|
||
### 3.2 Provider 接口
|
||
|
||
```go
|
||
type AIProvider interface {
|
||
Generate(ctx context.Context, req GenerateRequest) (GenerateResponse, error)
|
||
}
|
||
|
||
type GenerateRequest struct {
|
||
Feature string
|
||
TaskID string
|
||
TenantID string
|
||
UserID string
|
||
Model string
|
||
SystemPrompt string
|
||
UserPrompt string
|
||
ResponseSchema string
|
||
Temperature float64
|
||
MaxTokens int
|
||
Timeout time.Duration
|
||
}
|
||
|
||
type GenerateResponse struct {
|
||
ProviderRequestID string
|
||
Model string
|
||
Content string
|
||
PromptTokens int
|
||
CompletionTokens int
|
||
TotalTokens int
|
||
FinishReason string
|
||
LatencyMs int64
|
||
}
|
||
```
|
||
|
||
业务层只依赖 `AIProvider`,不得直接依赖 DeepSeek HTTP Client。这样后续切换 Provider 或增加备用模型时不影响业务用例。
|
||
|
||
### 3.3 DeepSeek 请求格式
|
||
|
||
请求 DeepSeek 时统一调用:
|
||
|
||
```http
|
||
POST /chat/completions HTTP/1.1
|
||
Host: api.deepseek.com
|
||
Authorization: Bearer ${DEEPSEEK_API_KEY}
|
||
Content-Type: application/json
|
||
```
|
||
|
||
示例请求:
|
||
|
||
```json
|
||
{
|
||
"model": "deepseek-flash",
|
||
"messages": [
|
||
{
|
||
"role": "system",
|
||
"content": "你是 IM 工作台的企业办公助手。请严格按 JSON 输出,不要输出额外解释。"
|
||
},
|
||
{
|
||
"role": "user",
|
||
"content": "请将以下日报原文润色为专业、简洁的工作总结..."
|
||
}
|
||
],
|
||
"temperature": 0.2,
|
||
"max_tokens": 800,
|
||
"stream": false
|
||
}
|
||
```
|
||
|
||
首版后端不使用流式输出,统一 `stream=false`。原因是前端当前交互以按钮生成、任务轮询、结果页展示为主;非流式更容易做幂等、审计、失败重试和结构化解析。后续如要做逐字生成,可在 Provider 层扩展流式接口。
|
||
|
||
### 3.4 输出结构化处理
|
||
|
||
DeepSeek 输出必须经过服务端解析和校验,不能直接原样入库给前端。
|
||
|
||
处理流程:
|
||
|
||
1. Prompt 明确要求模型只输出 JSON。
|
||
2. Provider 返回文本后,先截取 JSON 主体,去除 Markdown code fence。
|
||
3. 按当前 feature 的 schema 反序列化。
|
||
4. 校验必填字段、数组长度、字符串长度和枚举值。
|
||
5. 校验失败时最多执行一次“修复 JSON”请求,仍失败则任务失败,reason=`AI_TASK_FAILED`。
|
||
6. 校验通过后,生成标准化业务结果并入库。
|
||
|
||
示例:智能文档输出 Schema。
|
||
|
||
```json
|
||
{
|
||
"title": "string",
|
||
"sections": [
|
||
{"heading": "string", "content": "string"}
|
||
],
|
||
"summary": "string"
|
||
}
|
||
```
|
||
|
||
示例:会议总结输出 Schema。
|
||
|
||
```json
|
||
{
|
||
"conclusions": ["string"],
|
||
"decisions": ["string"],
|
||
"action_items": [
|
||
{
|
||
"owner_name": "string",
|
||
"content": "string",
|
||
"due_text": "string"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 3.5 Prompt 模板管理
|
||
|
||
Prompt 不应散落在业务代码中,建议以数据库配置或内置模板文件管理。首版可内置在代码,并预留版本字段。
|
||
|
||
| 模板 | 用途 | 推荐模型 | 输出 |
|
||
| --- | --- | --- | --- |
|
||
| `smart_document_v1` | 智能文档生成 | `deepseek-flash` | JSON + markdown |
|
||
| `smart_table_v1` | 表格报告解读 | `deepseek-v4-pro` 或 `deepseek-flash` | JSON |
|
||
| `text_polish_v1` | AI 美文 | `deepseek-flash` | JSON |
|
||
| `meeting_summary_v1` | 会议 AI 总结 | `deepseek-v4-pro` 或 `deepseek-flash` | JSON |
|
||
|
||
模板变量示例:
|
||
|
||
| 变量 | 说明 |
|
||
| --- | --- |
|
||
| `{{language}}` | 输出语言 |
|
||
| `{{source_text}}` | 用户输入原文 |
|
||
| `{{document_type}}` | 文档类型 |
|
||
| `{{table_schema}}` | 表头、字段类型、样例数据 |
|
||
| `{{table_stats}}` | 服务端预计算统计 |
|
||
| `{{meeting_title}}` | 会议标题 |
|
||
| `{{meeting_files_text}}` | 会议附件抽取文本 |
|
||
| `{{participants}}` | 参会人展示名 |
|
||
|
||
Prompt 版本必须写入 `wb_ai_task.prompt_version` 和 `wb_ai_record.prompt_version`,方便回溯同一条记录由哪个模板生成。
|
||
|
||
### 3.6 错误映射
|
||
|
||
| DeepSeek/网络错误 | 服务端 reason | 处理 |
|
||
| --- | --- | --- |
|
||
| 401/403 | `AI_PROVIDER_AUTH_FAILED` | 配置错误,告警,不重试 |
|
||
| 400 | `AI_PROVIDER_BAD_REQUEST` | Prompt 或参数错误,任务失败 |
|
||
| 429 | `AI_PROVIDER_RATE_LIMITED` | 按退避重试,仍失败返回 `RATE_LIMITED` |
|
||
| 5xx | `AI_PROVIDER_FAILED` | 重试,仍失败任务失败 |
|
||
| 网络超时 | `AI_TIMEOUT` | 重试,仍失败任务失败 |
|
||
| 输出为空 | `AI_TASK_FAILED` | 可重试一次 |
|
||
| JSON 解析失败 | `AI_TASK_FAILED` | 修复 JSON 一次,仍失败则失败 |
|
||
|
||
Provider 原始错误只允许写入内部审计表的脱敏字段,不直接返回给前端。
|
||
|
||
### 3.7 超时、重试和熔断
|
||
|
||
- AI 美文同步接口总耗时目标小于 8 秒,超过直接返回 `AI_TIMEOUT`。
|
||
- 智能文档单任务超时 60 秒。
|
||
- 智能表格和会议总结单任务超时 120 秒。
|
||
- 重试仅针对网络错误、429、5xx、输出为空;内容安全、参数错误、权限错误不重试。
|
||
- 重试采用指数退避,例如 1 秒、3 秒。
|
||
- 连续失败率超过阈值时开启 Provider 熔断,新的异步任务保持 `queued` 或快速失败,AI 美文返回“服务繁忙”。
|
||
|
||
### 3.8 安全与脱敏
|
||
|
||
发送 DeepSeek 前必须做最小必要原则:
|
||
|
||
1. 智能文档只发送用户输入,不附带用户 Token、手机号、设备信息。
|
||
2. 智能表格只发送表头、数据类型、聚合统计、异常点和必要样例,不发送完整大表。
|
||
3. 会议总结只发送会议标题、附件抽取文本、参会人展示名;不发送内部权限字段。
|
||
4. 日志只记录输入长度、输出长度、hash、模型、耗时、token 用量、错误 reason。
|
||
5. API Key 只从密钥系统或环境变量读取,不入库、不打印、不下发前端。
|
||
|
||
---
|
||
|
||
## 四、数据库设计
|
||
|
||
### 4.1 数据库公共约定
|
||
|
||
- 数据库建议使用 MySQL 8.0,字符集 `utf8mb4`。
|
||
- 所有 AI 表名使用 `wb_ai_` 前缀,主键可使用 `BIGINT UNSIGNED` 或字符串 ID,接口统一返回字符串。
|
||
- 所有租户业务表必须包含 `tenant_id`、`tenant_code`、`user_id`。
|
||
- `created_at`、`updated_at` 使用 `DATETIME(3)`;服务端写 UTC。
|
||
- JSON 字段保存低频扩展内容;状态、类型、时间、用户、任务 ID 必须拆列建索引。
|
||
- AI 原始输入和模型原始输出可按合规要求加密存储或只保存摘要,默认不保存完整供应商响应。
|
||
|
||
### 4.2 表清单
|
||
|
||
| 表名 | 领域 | 用途 |
|
||
| --- | --- | --- |
|
||
| `wb_ai_capability_config` | 配置 | 租户级 AI 能力开关、模型、限额 |
|
||
| `wb_ai_task` | 任务 | AI 异步任务主表 |
|
||
| `wb_ai_task_step` | 任务 | 表格等多步骤任务进度 |
|
||
| `wb_ai_record` | 记录 | 通用 AI 记录索引 |
|
||
| `wb_ai_document` | 智能文档 | 文档保存内容 |
|
||
| `wb_ai_table_analysis` | 智能表格 | 表格解读报告 |
|
||
| `wb_ai_meeting_summary` | 会议总结 | 会议 AI 总结内容 |
|
||
| `wb_ai_usage_log` | 用量 | AI 调用、token、耗时、结果 |
|
||
| `wb_ai_idempotency_record` | 通用 | 写接口幂等请求和结果摘要 |
|
||
| `wb_ai_prompt_template` | Prompt | Prompt 模板和版本,可后续管理后台维护 |
|
||
|
||
### 4.3 `wb_ai_capability_config` — AI 能力配置
|
||
|
||
保存租户维度或默认维度的 AI 能力开关、模型、配额和限制。
|
||
|
||
关键字段:
|
||
|
||
| 字段 | 说明 |
|
||
| --- | --- |
|
||
| `tenant_id` | 租户 ID,默认配置可使用 `0` |
|
||
| `feature` | `smart_document`、`smart_table`、`text_polish`、`meeting_summary` |
|
||
| `enabled` | 是否开启 |
|
||
| `model` | 默认模型,例如 `deepseek-flash` |
|
||
| `daily_limit` | 单用户每日次数 |
|
||
| `concurrency_limit` | 单用户并发任务数 |
|
||
| `max_input_chars` | 输入字符上限 |
|
||
| `max_file_size_mb` | 文件大小上限 |
|
||
| `config_json` | 扩展配置 |
|
||
|
||
关键索引:
|
||
|
||
- `uk_tenant_feature(tenant_id, feature)`。
|
||
- 查询时先查租户配置,查不到再用默认配置。
|
||
|
||
### 4.4 `wb_ai_task` — AI 任务主表
|
||
|
||
智能文档、智能表格、会议总结都创建任务。AI 美文首版可同步处理,不强制落任务,但仍写 `wb_ai_usage_log`。
|
||
|
||
关键字段:
|
||
|
||
| 字段 | 说明 |
|
||
| --- | --- |
|
||
| `task_id` | 对外任务 ID,例如 `aitask_xxx` |
|
||
| `tenant_id`、`tenant_code`、`user_id` | 数据隔离 |
|
||
| `feature` | AI 能力类型 |
|
||
| `status` | `queued`、`processing`、`succeeded`、`failed`、`canceled`、`expired` |
|
||
| `progress` | 0~100 |
|
||
| `source_type` | `text`、`file`、`meeting` |
|
||
| `source_ref_id` | 关联文件、会议或业务对象 ID |
|
||
| `input_hash` | 输入内容 hash,用于审计和幂等诊断 |
|
||
| `input_snapshot` | 脱敏后的输入快照,可按合规决定是否为空 |
|
||
| `prompt_version` | Prompt 模板版本 |
|
||
| `model` | 实际调用模型 |
|
||
| `result_ref_id` | 成功后的记录 ID |
|
||
| `error_reason`、`error_message` | 失败原因 |
|
||
| `retry_count` | 已重试次数 |
|
||
| `started_at`、`finished_at` | 执行时间 |
|
||
|
||
关键索引:
|
||
|
||
- `uk_task_id(task_id)`。
|
||
- `idx_user_feature_time(tenant_id, user_id, feature, created_at)`。
|
||
- `idx_status_time(status, created_at)`,用于 Worker 拉取和补偿。
|
||
|
||
### 4.5 `wb_ai_task_step` — AI 任务步骤
|
||
|
||
用于智能表格进度展示,也可复用到会议总结。
|
||
|
||
字段包括 `task_id`、`step`、`title`、`status`、`progress`、`started_at`、`finished_at`、`error_reason`。
|
||
|
||
智能表格固定步骤:
|
||
|
||
1. `read_file`:读取表格数据。
|
||
2. `detect_metrics`:识别核心指标。
|
||
3. `analyze_trends`:分析趋势变化。
|
||
4. `generate_report`:生成解读报告。
|
||
|
||
### 4.6 `wb_ai_record` — AI 记录索引
|
||
|
||
保存智能文档、智能表格等记录列表所需公共字段。
|
||
|
||
关键字段:
|
||
|
||
| 字段 | 说明 |
|
||
| --- | --- |
|
||
| `record_id` | 对外记录 ID,例如 `airec_doc_xxx` |
|
||
| `record_type` | `smart_document`、`smart_table`、`meeting_summary` |
|
||
| `title` | 标题 |
|
||
| `summary` | 摘要 |
|
||
| `status` | `succeeded`、`deleted` 等 |
|
||
| `detail_table` | 详情表名或类型 |
|
||
| `detail_id` | 详情表 ID |
|
||
| `task_id` | 来源任务 |
|
||
| `created_at`、`updated_at`、`deleted_at` | 时间 |
|
||
|
||
关键索引:
|
||
|
||
- `uk_record_id(record_id)`。
|
||
- `idx_user_type_time(tenant_id, user_id, record_type, created_at)`。
|
||
- `idx_title_search(tenant_id, user_id, title)`,实际搜索可接全文索引或外部搜索。
|
||
|
||
### 4.7 `wb_ai_document` — 智能文档详情
|
||
|
||
保存用户确认保存后的智能文档。
|
||
|
||
关键字段:
|
||
|
||
- `record_id`
|
||
- `document_type`
|
||
- `title`
|
||
- `content_markdown`
|
||
- `content_plain`
|
||
- `sections_json`
|
||
- `source_excerpt`
|
||
- `prompt_version`
|
||
- `model`
|
||
|
||
智能文档生成成功但未保存时,只保留在 `wb_ai_task.result_json` 或短期缓存中;过期后不可保存。
|
||
|
||
### 4.8 `wb_ai_table_analysis` — 表格解读详情
|
||
|
||
保存表格报告解读结果。
|
||
|
||
关键字段:
|
||
|
||
- `record_id`
|
||
- `file_id`、`file_name`、`file_type`、`file_size`
|
||
- `topic`
|
||
- `metrics_json`
|
||
- `preview_json`
|
||
- `alerts_json`
|
||
- `interpretation`
|
||
- `export_file_id`
|
||
- `prompt_version`
|
||
- `model`
|
||
|
||
文件解析结果不建议完整入库。大表只保存字段统计、样例行和 AI 输出,完整源文件仍在 IM 文件服务中。
|
||
|
||
### 4.9 `wb_ai_meeting_summary` — 会议 AI 总结
|
||
|
||
保存会议维度的 AI 总结,一个会议默认只有一份当前有效总结。
|
||
|
||
关键字段:
|
||
|
||
- `summary_id`
|
||
- `meeting_id`
|
||
- `task_id`
|
||
- `conclusions_json`
|
||
- `decisions_json`
|
||
- `action_items_json`
|
||
- `source_file_ids_json`
|
||
- `edited_by`
|
||
- `edited_at`
|
||
- `version`
|
||
|
||
关键索引:
|
||
|
||
- `uk_tenant_meeting(tenant_id, meeting_id)`。
|
||
- `idx_user_time(tenant_id, user_id, updated_at)`。
|
||
|
||
编辑保存时使用 `version` 做乐观锁,避免多端同时编辑覆盖。
|
||
|
||
### 4.10 `wb_ai_usage_log` — AI 用量日志
|
||
|
||
每次调用 DeepSeek,无论成功失败都写用量日志。
|
||
|
||
字段包括:
|
||
|
||
- `tenant_id`、`tenant_code`、`user_id`
|
||
- `feature`
|
||
- `task_id`
|
||
- `provider=deepseek`
|
||
- `model`
|
||
- `provider_request_id`
|
||
- `prompt_tokens`、`completion_tokens`、`total_tokens`
|
||
- `latency_ms`
|
||
- `status`
|
||
- `error_reason`
|
||
- `input_hash`
|
||
- `output_hash`
|
||
- `created_at`
|
||
|
||
用量统计优先从该表聚合,日配额可用 Redis 加速、MySQL 兜底。
|
||
|
||
### 4.11 `wb_ai_idempotency_record` — 幂等记录
|
||
|
||
写接口统一记录幂等键、请求 hash 和响应摘要。
|
||
|
||
关键约束:
|
||
|
||
- `uk_idempotency(tenant_id, user_id, idempotency_key)`。
|
||
- 同一 key 请求 hash 相同,直接返回第一次响应。
|
||
- 同一 key 请求 hash 不同,返回 `IDEMPOTENCY_CONFLICT`。
|
||
|
||
---
|
||
|
||
## 五、核心业务设计
|
||
|
||
### 5.1 AI 能力配置读取
|
||
|
||
`GET /v1/workbench/ai/capabilities` 流程:
|
||
|
||
1. 解析 IM Token,得到租户和用户。
|
||
2. 读取租户 AI 配置;租户没有配置时读取默认配置。
|
||
3. 查询用户当天各 feature 已使用次数。
|
||
4. 组装 `features[]`,返回开关、入口名、每日限制、剩余次数、输入限制、文件限制。
|
||
|
||
配置建议缓存 1 分钟。配置变化后可通过后台主动清缓存,首版可接受短延迟。
|
||
|
||
### 5.2 AI 美文同步流程
|
||
|
||
AI 美文用于短文本润色,流程:
|
||
|
||
```text
|
||
请求进入
|
||
-> 鉴权和租户解析
|
||
-> 参数校验 source_text <= 300
|
||
-> 能力开关、日配额、频控校验
|
||
-> 内容安全预检查
|
||
-> 构造 text_polish_v1 prompt
|
||
-> 调用 DeepSeek
|
||
-> 解析 JSON 输出 polished_text
|
||
-> 内容安全后检查
|
||
-> 写 usage_log
|
||
-> 返回 polished_text
|
||
```
|
||
|
||
失败处理:
|
||
|
||
- DeepSeek 超时:返回 `AI_TIMEOUT`。
|
||
- 输出解析失败:重试一次;仍失败返回 `AI_TASK_FAILED`。
|
||
- 内容安全失败:返回 `AI_CONTENT_REJECTED`。
|
||
- 用量超限:返回 `AI_QUOTA_EXCEEDED`。
|
||
|
||
AI 美文不自动提交日报/月报,原业务提交接口保持不变。
|
||
|
||
### 5.3 智能文档流程
|
||
|
||
创建任务:
|
||
|
||
1. 校验输入长度、能力开关、配额和幂等。
|
||
2. 写 `wb_ai_task(status=queued, feature=smart_document)`。
|
||
3. 投递 Kafka 消息或写入本地任务队列。
|
||
4. 返回 `task_id` 给前端轮询。
|
||
|
||
Worker 执行:
|
||
|
||
1. 将任务置为 `processing`。
|
||
2. 构造 `smart_document_v1` prompt。
|
||
3. 调用 DeepSeek。
|
||
4. 解析 `title`、`sections`、`content_markdown`、`summary`。
|
||
5. 结果写入 `wb_ai_task.result_json`,任务置为 `succeeded`。
|
||
6. 用户点击保存后,写 `wb_ai_record` 和 `wb_ai_document`。
|
||
|
||
注意:
|
||
|
||
- 生成成功不等于保存成功。
|
||
- 未保存结果可设置 2 小时过期。
|
||
- 重新生成创建新任务,不覆盖旧任务。
|
||
|
||
### 5.4 智能表格流程
|
||
|
||
智能表格必须先上传文件,再创建任务。
|
||
|
||
创建任务:
|
||
|
||
1. 校验 `file_id` 属于当前租户和用户。
|
||
2. 校验文件类型为 `xlsx/xls/csv`,大小不超过配置。
|
||
3. 写 `wb_ai_task`、`wb_ai_task_step`,预生成 `record_id`。
|
||
4. 投递任务,返回 `task_id`、`record_id`。
|
||
|
||
Worker 执行:
|
||
|
||
```text
|
||
read_file
|
||
-> 从 IM 文件服务下载文件
|
||
-> 解析 xlsx/xls/csv
|
||
-> 抽取表头、字段类型、样例行、总行数
|
||
detect_metrics
|
||
-> 服务端先做基础统计:合计、均值、最大最小、环比、空值、异常点
|
||
analyze_trends
|
||
-> 将表结构、统计摘要、异常点输入 DeepSeek
|
||
generate_report
|
||
-> 解析模型 JSON,生成指标、预览、异常提醒、核心解读
|
||
-> 写 wb_ai_record、wb_ai_table_analysis
|
||
```
|
||
|
||
表格数据处理原则:
|
||
|
||
- 不把完整大表直接发给 DeepSeek。
|
||
- 优先由服务端计算确定性统计,再让 AI 解释。
|
||
- 样例行数量限制,例如最多 20 行。
|
||
- 敏感列可按列名规则脱敏,例如手机号、身份证、邮箱。
|
||
|
||
### 5.5 会议 AI 总结流程
|
||
|
||
会议 AI 总结必须复用会议笔记权限。
|
||
|
||
流程:
|
||
|
||
1. 校验当前用户可查看该会议笔记。
|
||
2. 获取会议标题、时间、组织者、参会人、附件列表。
|
||
3. 校验 `file_ids` 均属于该会议且当前用户可读。
|
||
4. 抽取附件文本;若附件为空,可基于会议描述和已有笔记文本生成,但必须有有效输入。
|
||
5. 创建 `meeting_summary` AI 任务。
|
||
6. Worker 调用 DeepSeek 生成结论、关键决策、后续安排。
|
||
7. 写入或更新 `wb_ai_meeting_summary`。
|
||
|
||
编辑保存:
|
||
|
||
- 校验会议笔记编辑权限。
|
||
- 使用 `version` 乐观锁。
|
||
- 保存用户编辑内容,记录 `edited_by`、`edited_at`。
|
||
|
||
### 5.6 导出报告流程
|
||
|
||
表格报告导出流程:
|
||
|
||
1. 校验记录属于当前用户。
|
||
2. 读取 `wb_ai_table_analysis`。
|
||
3. 使用服务端模板生成 DOCX 或 PDF。
|
||
4. 上传到 IM 文件服务,得到 `file_id`。
|
||
5. 返回下载 URL 和过期时间。
|
||
|
||
导出不再调用 DeepSeek,避免同一报告重复生成内容不一致。
|
||
|
||
### 5.7 应用搜索接入
|
||
|
||
工作台应用列表和搜索本质不是 AI Provider 调用,但需要接入 AI 能力配置。
|
||
|
||
处理方式:
|
||
|
||
- 应用注册表增加 AI 应用:`smart_document`、`smart_table`。
|
||
- 返回应用列表前读取 capabilities。
|
||
- 未开启的 AI 应用不返回,或返回 `enabled=false`,由产品决定。
|
||
- 搜索只搜索当前用户可见应用。
|
||
|
||
---
|
||
|
||
## 六、关键技术方案
|
||
|
||
### 6.1 事务边界
|
||
|
||
需要事务的场景:
|
||
|
||
- 创建 AI 任务和幂等记录。
|
||
- 表格任务成功后写记录索引和详情。
|
||
- 智能文档保存时写记录索引和详情。
|
||
- 会议总结更新和版本递增。
|
||
- 用量日志和日配额计数。
|
||
|
||
不应放入同一事务的场景:
|
||
|
||
- 调用 DeepSeek。
|
||
- 下载文件和解析大文件。
|
||
- 导出文件上传。
|
||
- 发送 WebSocket 或系统通知。
|
||
|
||
外部调用必须在事务外执行,事务只保护本地状态变更。
|
||
|
||
### 6.2 幂等与弱网
|
||
|
||
所有写接口都要求 `Idempotency-Key`:
|
||
|
||
1. 请求进入后计算规范化请求体 hash。
|
||
2. 查询 `wb_ai_idempotency_record`。
|
||
3. 不存在则插入 `processing` 记录并继续。
|
||
4. 已存在且 hash 相同,返回首次响应。
|
||
5. 已存在且 hash 不同,返回 `IDEMPOTENCY_CONFLICT`。
|
||
6. 业务成功后更新响应摘要。
|
||
|
||
异步任务的幂等响应应返回同一个 `task_id`,不能重复创建任务。
|
||
|
||
### 6.3 任务调度
|
||
|
||
首选 Kafka:
|
||
|
||
| Topic | 用途 |
|
||
| --- | --- |
|
||
| `workbench.ai.task.created` | AI 任务创建 |
|
||
| `workbench.ai.task.retry` | AI 任务重试 |
|
||
| `workbench.ai.task.completed` | AI 任务完成事件,可选 |
|
||
|
||
消息体:
|
||
|
||
```json
|
||
{
|
||
"task_id": "aitask_tbl_2Fh9aQx73K",
|
||
"tenant_id": "1",
|
||
"tenant_code": "system",
|
||
"user_id": "usrABAAnqj42BU",
|
||
"feature": "smart_table",
|
||
"created_at": "2026-09-25T08:30:15.123Z"
|
||
}
|
||
```
|
||
|
||
消费要求:
|
||
|
||
- Consumer 收到消息后先按 `task_id` 查询 DB,只有 `queued` 可执行。
|
||
- 任务置为 `processing` 使用条件更新,避免多 Consumer 重复执行。
|
||
- 执行失败按错误类型决定是否重试。
|
||
- 服务重启后通过补偿任务扫描 `queued` 和超时 `processing`。
|
||
|
||
如果首版 Kafka 未就绪,可用 MySQL 轮询表兜底,但接口设计保持不变。
|
||
|
||
### 6.4 并发与配额
|
||
|
||
配额维度:
|
||
|
||
- 租户总开关。
|
||
- 单用户每日次数。
|
||
- 单用户并发任务数。
|
||
- 单租户并发任务数。
|
||
- Provider 全局并发数。
|
||
|
||
实现建议:
|
||
|
||
- Redis 维护短期计数和并发锁。
|
||
- MySQL `wb_ai_usage_log` 做最终用量统计。
|
||
- Redis 不可用时,使用 MySQL 当天聚合兜底,宁可保守拒绝,不放宽限制。
|
||
|
||
### 6.5 表格解析
|
||
|
||
解析规则:
|
||
|
||
- `xlsx/xls`:读取第一个非空 sheet;如果前端指定 `sheet_name`,必须校验存在。
|
||
- `csv`:自动识别 UTF-8/GBK,分隔符支持逗号,必要时兼容制表符。
|
||
- 空行、合并单元格、公式结果需要规范化。
|
||
- 超过最大行数时只采样前 N 行,并计算全量统计时注意内存限制。
|
||
|
||
输出给 DeepSeek 的内容建议:
|
||
|
||
```json
|
||
{
|
||
"topic": "8月销售数据",
|
||
"columns": [
|
||
{"name": "日期", "type": "date"},
|
||
{"name": "销售额", "type": "number"},
|
||
{"name": "订单量", "type": "number"}
|
||
],
|
||
"row_count": 31,
|
||
"sample_rows": [
|
||
["8/1", "42300", "126"]
|
||
],
|
||
"stats": {
|
||
"销售额": {"sum": 1286000, "max": 62000, "min": 32100},
|
||
"订单量": {"sum": 3842}
|
||
},
|
||
"outliers": [
|
||
{"row": 15, "column": "订单量", "description": "较前一日下降 18%"}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 6.6 内容安全
|
||
|
||
内容安全分为前置检查和后置检查:
|
||
|
||
- 前置检查:用户输入、表格标题、会议附件抽取文本。
|
||
- 后置检查:DeepSeek 输出结果。
|
||
|
||
首版如没有独立内容安全服务,可先做基础敏感词、长度、HTML/script 清理和文件类型校验,同时预留 `safety` adapter。命中安全规则返回 `AI_CONTENT_REJECTED`。
|
||
|
||
### 6.7 缓存策略
|
||
|
||
| 缓存 | Key | TTL | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| AI 能力配置 | `wb:ai:cap:{tenant_id}` | 60s | 能力开关和限制 |
|
||
| 用户日用量 | `wb:ai:quota:{tenant_id}:{user_id}:{date}` | 当天 | 快速判断剩余次数 |
|
||
| 任务进度 | `wb:ai:task:{task_id}` | 2h | 轮询加速 |
|
||
| Provider 熔断 | `wb:ai:provider:deepseek:circuit` | 30s | 熔断标记 |
|
||
|
||
缓存不可作为唯一事实来源。任务最终状态以 MySQL 为准。
|
||
|
||
### 6.8 错误处理
|
||
|
||
对前端统一返回 workbench 错误包:
|
||
|
||
```json
|
||
{
|
||
"code": 30050,
|
||
"reason": "AI_TIMEOUT",
|
||
"message": "ai timeout",
|
||
"request_id": "01K8ZC3FZ6BTX9Z1SK8HV7QYWB",
|
||
"server_time": "2026-09-25T08:30:15.123Z"
|
||
}
|
||
```
|
||
|
||
错误分层:
|
||
|
||
- 参数和权限错误:接口同步返回。
|
||
- 异步任务执行错误:任务置为 `failed`,轮询接口返回 `error`。
|
||
- Provider 配置错误:接口返回失败并触发告警。
|
||
- Kafka 不可用:任务保持 `queued`,补偿扫描重投。
|
||
|
||
### 6.9 可观测性
|
||
|
||
日志字段:
|
||
|
||
- `request_id`
|
||
- `tenant_code`
|
||
- `user_id_hash`
|
||
- `feature`
|
||
- `task_id`
|
||
- `provider`
|
||
- `model`
|
||
- `status`
|
||
- `latency_ms`
|
||
- `prompt_tokens`
|
||
- `completion_tokens`
|
||
- `error_reason`
|
||
|
||
指标:
|
||
|
||
- AI 请求数、成功数、失败数。
|
||
- DeepSeek 调用耗时 P50/P90/P99。
|
||
- 任务排队时长、执行时长。
|
||
- 任务失败率、重试次数。
|
||
- 每租户 token 用量。
|
||
- Provider 429/5xx/超时次数。
|
||
|
||
告警:
|
||
|
||
- DeepSeek 连续 5 分钟失败率超过阈值。
|
||
- 任务积压超过阈值。
|
||
- Kafka 消费延迟超过阈值。
|
||
- API Key 鉴权失败。
|
||
- 单租户用量异常增长。
|
||
|
||
---
|
||
|
||
## 七、C 端与管理后台需求
|
||
|
||
### 7.1 C 端职责边界
|
||
|
||
C 端负责:
|
||
|
||
- 输入长度和文件类型的第一层校验。
|
||
- 调用任务创建接口并轮询。
|
||
- 展示 AI 结果、失败、重试、取消和空态。
|
||
- 在日报/月报中使用 AI 美文结果回填字段。
|
||
- 展示“由 AI 生成,请核对后使用”提示。
|
||
|
||
C 端不负责:
|
||
|
||
- 判断最终权限。
|
||
- 控制真实用量。
|
||
- 保存 DeepSeek Key。
|
||
- 拼接最终 prompt。
|
||
- 解析供应商原始响应。
|
||
|
||
### 7.2 管理后台能力
|
||
|
||
建议后续在 manager 或 workbench admin 增加 AI 配置:
|
||
|
||
| 能力 | 说明 |
|
||
| --- | --- |
|
||
| 租户 AI 总开关 | 控制某租户是否开放 AI |
|
||
| 功能开关 | 分别控制智能文档、智能表格、AI 美文、会议总结 |
|
||
| 模型配置 | 每个功能选择默认模型 |
|
||
| 每日限额 | 单用户每日次数、租户每日总次数 |
|
||
| 文件限制 | 表格文件大小、行数、类型 |
|
||
| Prompt 版本 | 查看当前模板版本,灰度新模板 |
|
||
| 用量统计 | 按租户、用户、功能统计 token 和次数 |
|
||
| 失败日志 | 查看脱敏后的失败原因和任务 ID |
|
||
|
||
### 7.3 运维配置
|
||
|
||
Kubernetes/Gitea Secrets 中需要配置:
|
||
|
||
```text
|
||
AI_PROVIDER=deepseek
|
||
DEEPSEEK_BASE_URL=https://api.deepseek.com
|
||
DEEPSEEK_API_KEY=<deepseek api key>
|
||
DEEPSEEK_DEFAULT_MODEL=deepseek-flash
|
||
DEEPSEEK_COMPLEX_MODEL=deepseek-v4-pro
|
||
DEEPSEEK_TIMEOUT_MS=60000
|
||
DEEPSEEK_MAX_RETRIES=2
|
||
DEEPSEEK_TEMPERATURE=0.2
|
||
```
|
||
|
||
注意:
|
||
|
||
- `DEEPSEEK_API_KEY` 只能放 Secret,不放 ConfigMap,不提交 Git。
|
||
- 发版前确认 Pod 环境变量或配置文件最终生效。
|
||
- 模型名必须和 DeepSeek 当前可用模型一致。
|
||
- 不同环境可使用不同 Key 和限额。
|
||
|
||
---
|
||
|
||
## 八、开发阶段与依赖
|
||
|
||
### 8.1 开发阶段
|
||
|
||
#### 阶段一:基础框架和 AI 美文
|
||
|
||
目标:
|
||
|
||
- 建表:能力配置、用量日志、幂等。
|
||
- 实现 DeepSeek Provider。
|
||
- 实现 AI 美文接口。
|
||
- 接入日报/月报回填流程。
|
||
|
||
验收:
|
||
|
||
- 能通过 DeepSeek 返回润色文案。
|
||
- 超时、Key 错误、用量超限有明确错误。
|
||
- 日报/月报原业务提交不受影响。
|
||
|
||
#### 阶段二:智能文档
|
||
|
||
目标:
|
||
|
||
- 实现 AI 任务表、任务轮询、智能文档生成、保存、记录列表。
|
||
- 接入 Kafka 或 MySQL 任务队列。
|
||
|
||
验收:
|
||
|
||
- 能生成文档、重新生成、保存、查询记录。
|
||
- 弱网重复提交不创建重复任务。
|
||
|
||
#### 阶段三:智能表格
|
||
|
||
目标:
|
||
|
||
- 文件归属校验、Excel/CSV 解析、统计摘要、DeepSeek 报告生成、记录详情。
|
||
- 实现导出 DOCX/PDF。
|
||
|
||
验收:
|
||
|
||
- xlsx/xls/csv 都能分析。
|
||
- 大文件不会把完整内容发给 DeepSeek。
|
||
- 任务进度和失败状态可见。
|
||
|
||
#### 阶段四:会议 AI 总结
|
||
|
||
目标:
|
||
|
||
- 接入会议笔记权限和附件。
|
||
- 生成会议结论、关键决策、后续安排。
|
||
- 支持编辑保存。
|
||
|
||
验收:
|
||
|
||
- 无权限不能生成或查看。
|
||
- 会议附件不可用时有明确失败原因。
|
||
- 编辑保存不会被任务结果覆盖。
|
||
|
||
### 8.2 开发依赖
|
||
|
||
| 依赖 | 负责人 | 说明 |
|
||
| --- | --- | --- |
|
||
| DeepSeek API Key | 运维/后端 | 测试、生产分别配置 |
|
||
| Kafka | 后端/运维 | AI 异步任务队列 |
|
||
| Redis | 后端/运维 | 配额、进度缓存、并发锁 |
|
||
| 文件服务 | IM 后端 | 表格源文件下载、导出文件上传 |
|
||
| 会议笔记接口 | workbench 后端 | 会议权限、附件列表 |
|
||
| 前端页面 | 前端 | AI 入口、轮询、结果、记录 |
|
||
|
||
---
|
||
|
||
## 九、发布与回滚
|
||
|
||
### 9.1 发布顺序
|
||
|
||
1. 发布数据库 migration。
|
||
2. 配置 DeepSeek Secret、AI Provider、模型和限额。
|
||
3. 发布后端代码,默认租户 AI 开关关闭。
|
||
4. 验证 Provider 健康检查和 AI 美文测试接口。
|
||
5. 灰度开启测试租户 AI 美文。
|
||
6. 依次开启智能文档、智能表格、会议总结。
|
||
7. 前端打开入口。
|
||
|
||
### 9.2 发布检查
|
||
|
||
- Pod 中 `DEEPSEEK_API_KEY` 已注入,但日志不打印明文。
|
||
- `DEEPSEEK_BASE_URL` 为 `https://api.deepseek.com`。
|
||
- 默认模型可调用。
|
||
- Kafka consumer 正常消费。
|
||
- Redis 可写入配额 key。
|
||
- MySQL 表和索引已创建。
|
||
- 文件服务可下载测试表格。
|
||
- 任务失败告警已配置。
|
||
|
||
### 9.3 回滚策略
|
||
|
||
| 问题 | 回滚方式 |
|
||
| --- | --- |
|
||
| DeepSeek 大面积失败 | 关闭租户 AI 开关或 Provider 熔断 |
|
||
| AI 美文影响日报/月报 | 隐藏 AI 美文入口,原提交接口不回滚 |
|
||
| 智能文档失败率高 | 关闭 `smart_document` 功能 |
|
||
| 智能表格解析异常 | 关闭 `smart_table` 功能,保留历史记录只读 |
|
||
| 会议总结权限问题 | 关闭 `meeting_summary` 功能,保留会议笔记原能力 |
|
||
| 新表结构问题 | 代码回滚到不访问新表版本;migration 回滚需单独评估 |
|
||
|
||
回滚时不删除已生成记录。前端入口关闭后,历史记录可按产品要求保留只读或隐藏。
|
||
|
||
---
|
||
|
||
## 附录 A:MySQL 8.0 建表草案
|
||
|
||
```sql
|
||
CREATE TABLE wb_ai_capability_config (
|
||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||
tenant_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
|
||
tenant_code VARCHAR(64) NOT NULL DEFAULT '',
|
||
feature VARCHAR(64) NOT NULL,
|
||
enabled TINYINT(1) NOT NULL DEFAULT 0,
|
||
model VARCHAR(64) NOT NULL DEFAULT 'deepseek-flash',
|
||
daily_limit INT NOT NULL DEFAULT 0,
|
||
concurrency_limit INT NOT NULL DEFAULT 1,
|
||
max_input_chars INT NOT NULL DEFAULT 0,
|
||
max_file_size_mb INT NOT NULL DEFAULT 0,
|
||
config_json JSON NULL,
|
||
created_at DATETIME(3) NOT NULL,
|
||
updated_at DATETIME(3) NOT NULL,
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_tenant_feature (tenant_id, feature)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||
|
||
CREATE TABLE wb_ai_task (
|
||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||
task_id VARCHAR(64) NOT NULL,
|
||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||
tenant_code VARCHAR(64) NOT NULL,
|
||
user_id VARCHAR(64) NOT NULL,
|
||
feature VARCHAR(64) NOT NULL,
|
||
status VARCHAR(32) NOT NULL,
|
||
progress INT NOT NULL DEFAULT 0,
|
||
source_type VARCHAR(32) NOT NULL DEFAULT '',
|
||
source_ref_id VARCHAR(128) NOT NULL DEFAULT '',
|
||
input_hash VARCHAR(128) NOT NULL DEFAULT '',
|
||
input_snapshot JSON NULL,
|
||
prompt_version VARCHAR(64) NOT NULL DEFAULT '',
|
||
model VARCHAR(64) NOT NULL DEFAULT '',
|
||
result_ref_id VARCHAR(64) NOT NULL DEFAULT '',
|
||
result_json JSON NULL,
|
||
error_reason VARCHAR(64) NOT NULL DEFAULT '',
|
||
error_message VARCHAR(255) NOT NULL DEFAULT '',
|
||
retry_count INT NOT NULL DEFAULT 0,
|
||
started_at DATETIME(3) NULL,
|
||
finished_at DATETIME(3) NULL,
|
||
created_at DATETIME(3) NOT NULL,
|
||
updated_at DATETIME(3) NOT NULL,
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_task_id (task_id),
|
||
KEY idx_user_feature_time (tenant_id, user_id, feature, created_at),
|
||
KEY idx_status_time (status, created_at)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||
|
||
CREATE TABLE wb_ai_task_step (
|
||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||
task_id VARCHAR(64) NOT NULL,
|
||
step VARCHAR(64) NOT NULL,
|
||
title VARCHAR(128) NOT NULL,
|
||
status VARCHAR(32) NOT NULL,
|
||
progress INT NOT NULL DEFAULT 0,
|
||
error_reason VARCHAR(64) NOT NULL DEFAULT '',
|
||
started_at DATETIME(3) NULL,
|
||
finished_at DATETIME(3) NULL,
|
||
created_at DATETIME(3) NOT NULL,
|
||
updated_at DATETIME(3) NOT NULL,
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_task_step (task_id, step)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||
|
||
CREATE TABLE wb_ai_record (
|
||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||
record_id VARCHAR(64) NOT NULL,
|
||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||
tenant_code VARCHAR(64) NOT NULL,
|
||
user_id VARCHAR(64) NOT NULL,
|
||
record_type VARCHAR(64) NOT NULL,
|
||
title VARCHAR(128) NOT NULL,
|
||
summary VARCHAR(512) NOT NULL DEFAULT '',
|
||
status VARCHAR(32) NOT NULL,
|
||
detail_id VARCHAR(64) NOT NULL DEFAULT '',
|
||
task_id VARCHAR(64) NOT NULL DEFAULT '',
|
||
created_at DATETIME(3) NOT NULL,
|
||
updated_at DATETIME(3) NOT NULL,
|
||
deleted_at DATETIME(3) NULL,
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_record_id (record_id),
|
||
KEY idx_user_type_time (tenant_id, user_id, record_type, created_at)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||
|
||
CREATE TABLE wb_ai_document (
|
||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||
record_id VARCHAR(64) NOT NULL,
|
||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||
user_id VARCHAR(64) NOT NULL,
|
||
document_type VARCHAR(64) NOT NULL,
|
||
title VARCHAR(128) NOT NULL,
|
||
content_markdown MEDIUMTEXT NOT NULL,
|
||
content_plain MEDIUMTEXT NULL,
|
||
sections_json JSON NULL,
|
||
source_excerpt VARCHAR(1024) NOT NULL DEFAULT '',
|
||
prompt_version VARCHAR(64) NOT NULL DEFAULT '',
|
||
model VARCHAR(64) NOT NULL DEFAULT '',
|
||
created_at DATETIME(3) NOT NULL,
|
||
updated_at DATETIME(3) NOT NULL,
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_record_id (record_id)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||
|
||
CREATE TABLE wb_ai_table_analysis (
|
||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||
record_id VARCHAR(64) NOT NULL,
|
||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||
user_id VARCHAR(64) NOT NULL,
|
||
file_id VARCHAR(128) NOT NULL,
|
||
file_name VARCHAR(255) NOT NULL,
|
||
file_type VARCHAR(16) NOT NULL,
|
||
file_size BIGINT UNSIGNED NOT NULL DEFAULT 0,
|
||
topic VARCHAR(128) NOT NULL,
|
||
metrics_json JSON NULL,
|
||
preview_json JSON NULL,
|
||
alerts_json JSON NULL,
|
||
interpretation MEDIUMTEXT NULL,
|
||
export_file_id VARCHAR(128) NOT NULL DEFAULT '',
|
||
prompt_version VARCHAR(64) NOT NULL DEFAULT '',
|
||
model VARCHAR(64) NOT NULL DEFAULT '',
|
||
created_at DATETIME(3) NOT NULL,
|
||
updated_at DATETIME(3) NOT NULL,
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_record_id (record_id),
|
||
KEY idx_file_id (tenant_id, file_id)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||
|
||
CREATE TABLE wb_ai_meeting_summary (
|
||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||
summary_id VARCHAR(64) NOT NULL,
|
||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||
tenant_code VARCHAR(64) NOT NULL,
|
||
meeting_id VARCHAR(128) NOT NULL,
|
||
user_id VARCHAR(64) NOT NULL,
|
||
task_id VARCHAR(64) NOT NULL DEFAULT '',
|
||
conclusions_json JSON NULL,
|
||
decisions_json JSON NULL,
|
||
action_items_json JSON NULL,
|
||
source_file_ids_json JSON NULL,
|
||
prompt_version VARCHAR(64) NOT NULL DEFAULT '',
|
||
model VARCHAR(64) NOT NULL DEFAULT '',
|
||
edited_by VARCHAR(64) NOT NULL DEFAULT '',
|
||
edited_at DATETIME(3) NULL,
|
||
version INT NOT NULL DEFAULT 1,
|
||
created_at DATETIME(3) NOT NULL,
|
||
updated_at DATETIME(3) NOT NULL,
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_summary_id (summary_id),
|
||
UNIQUE KEY uk_tenant_meeting (tenant_id, meeting_id)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||
|
||
CREATE TABLE wb_ai_usage_log (
|
||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||
tenant_code VARCHAR(64) NOT NULL,
|
||
user_id VARCHAR(64) NOT NULL,
|
||
feature VARCHAR(64) NOT NULL,
|
||
task_id VARCHAR(64) NOT NULL DEFAULT '',
|
||
provider VARCHAR(32) NOT NULL,
|
||
model VARCHAR(64) NOT NULL,
|
||
provider_request_id VARCHAR(128) NOT NULL DEFAULT '',
|
||
prompt_tokens INT NOT NULL DEFAULT 0,
|
||
completion_tokens INT NOT NULL DEFAULT 0,
|
||
total_tokens INT NOT NULL DEFAULT 0,
|
||
latency_ms INT NOT NULL DEFAULT 0,
|
||
status VARCHAR(32) NOT NULL,
|
||
error_reason VARCHAR(64) NOT NULL DEFAULT '',
|
||
input_hash VARCHAR(128) NOT NULL DEFAULT '',
|
||
output_hash VARCHAR(128) NOT NULL DEFAULT '',
|
||
created_at DATETIME(3) NOT NULL,
|
||
PRIMARY KEY (id),
|
||
KEY idx_user_feature_day (tenant_id, user_id, feature, created_at),
|
||
KEY idx_provider_time (provider, model, created_at)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||
|
||
CREATE TABLE wb_ai_idempotency_record (
|
||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||
user_id VARCHAR(64) NOT NULL,
|
||
idempotency_key VARCHAR(128) NOT NULL,
|
||
request_hash VARCHAR(128) NOT NULL,
|
||
response_code INT NOT NULL DEFAULT 0,
|
||
response_body JSON NULL,
|
||
status VARCHAR(32) NOT NULL,
|
||
created_at DATETIME(3) NOT NULL,
|
||
updated_at DATETIME(3) NOT NULL,
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uk_idempotency (tenant_id, user_id, idempotency_key)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||
```
|
||
|
||
## 附录 B:DeepSeek 调用伪代码
|
||
|
||
```go
|
||
func (p *DeepSeekProvider) Generate(ctx context.Context, req GenerateRequest) (GenerateResponse, error) {
|
||
body := map[string]any{
|
||
"model": req.Model,
|
||
"messages": []map[string]string{
|
||
{"role": "system", "content": req.SystemPrompt},
|
||
{"role": "user", "content": req.UserPrompt},
|
||
},
|
||
"temperature": req.Temperature,
|
||
"max_tokens": req.MaxTokens,
|
||
"stream": false,
|
||
}
|
||
|
||
httpReq := NewJSONRequest(ctx, "POST", p.baseURL+"/chat/completions", body)
|
||
httpReq.Header.Set("Authorization", "Bearer "+p.apiKey)
|
||
|
||
resp, err := p.httpClient.Do(httpReq)
|
||
if err != nil {
|
||
return GenerateResponse{}, mapNetworkError(err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode >= 400 {
|
||
return GenerateResponse{}, mapDeepSeekStatus(resp.StatusCode, resp.Body)
|
||
}
|
||
|
||
parsed := parseChatCompletion(resp.Body)
|
||
return GenerateResponse{
|
||
ProviderRequestID: parsed.ID,
|
||
Model: parsed.Model,
|
||
Content: parsed.Choices[0].Message.Content,
|
||
PromptTokens: parsed.Usage.PromptTokens,
|
||
CompletionTokens: parsed.Usage.CompletionTokens,
|
||
TotalTokens: parsed.Usage.TotalTokens,
|
||
FinishReason: parsed.Choices[0].FinishReason,
|
||
LatencyMs: elapsed.Milliseconds(),
|
||
}, nil
|
||
}
|
||
```
|
||
|
||
## 附录 C:已确认产品与技术决策
|
||
|
||
| 决策 | 说明 |
|
||
| --- | --- |
|
||
| AI Provider | 首版接入 DeepSeek |
|
||
| 接口形态 | DeepSeek 走 OpenAI 兼容 Chat Completions |
|
||
| 默认模型 | `deepseek-flash` |
|
||
| 复杂任务模型 | 可配置为 `deepseek-v4-pro` |
|
||
| 流式输出 | 首版不做流式,统一非流式和任务轮询 |
|
||
| 表格处理 | 服务端先解析和统计,不把完整大表直接发给模型 |
|
||
| 日报/月报 | AI 只润色文案,不改变原提交接口 |
|
||
| 会议总结 | 必须复用会议笔记权限 |
|
||
| 日志 | 不打印完整输入、输出、Token、API Key |
|