44 KiB
IM 工作台 AI 后端概要设计文档
版本:V1.0
日期:2026-09-25
关联文档:IM 工作台 AI 接入需求文档 · IM 工作台 AI 前后端对接文档 · IM 前后端对接文档
DeepSeek 依据:DeepSeek 首次调用 API · DeepSeek Chat Completions API
说明:本文描述 IM 工作台 AI 能力的后端概要设计,覆盖智能文档、智能表格、AI 美文、日报/月报 AI 接入、会议笔记 AI 总结、AI 任务、AI 记录、DeepSeek 接入、配置、异步处理、数据表、安全和发布回滚。
一、设计目标
IM 工作台在现有日报、月报、会议、文件和组织能力基础上新增 AI 能力。后端需要在不破坏原 workbench 业务接口的前提下,提供统一的 AI 能力接入层、任务调度层、记录存储层和 DeepSeek Provider 适配层。
后端需要完成以下核心能力:
- 工作台 AI 入口:按租户、用户、功能开关返回可用 AI 能力、用量限制和入口配置。
- 智能文档:根据用户输入生成结构化文档,支持重新生成、编辑保存、记录查询。
- 智能表格:接收已上传表格文件,解析数据、识别核心指标、生成报告解读,支持记录查询和导出。
- AI 美文:为普通文案、日报、月报提供同步润色能力,只回填业务表单,不替代原业务提交。
- 会议 AI 总结:基于会议笔记附件和会议上下文生成结论、决策、后续安排,支持编辑保存。
- DeepSeek 接入:通过 DeepSeek OpenAI 兼容 Chat Completions API 接入模型,统一处理 prompt、超时、重试、错误映射、审计、脱敏和用量统计。
- 可靠性:支持幂等、异步任务、失败重试、并发限制、租户隔离、内容安全和灰度开关。
设计原则
- 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 服务。
移动端 / 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 推荐代码分层
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 接口
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 时统一调用:
POST /chat/completions HTTP/1.1
Host: api.deepseek.com
Authorization: Bearer ${DEEPSEEK_API_KEY}
Content-Type: application/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 输出必须经过服务端解析和校验,不能直接原样入库给前端。
处理流程:
- Prompt 明确要求模型只输出 JSON。
- Provider 返回文本后,先截取 JSON 主体,去除 Markdown code fence。
- 按当前 feature 的 schema 反序列化。
- 校验必填字段、数组长度、字符串长度和枚举值。
- 校验失败时最多执行一次“修复 JSON”请求,仍失败则任务失败,reason=
AI_TASK_FAILED。 - 校验通过后,生成标准化业务结果并入库。
示例:智能文档输出 Schema。
{
"title": "string",
"sections": [
{"heading": "string", "content": "string"}
],
"summary": "string"
}
示例:会议总结输出 Schema。
{
"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 前必须做最小必要原则:
- 智能文档只发送用户输入,不附带用户 Token、手机号、设备信息。
- 智能表格只发送表头、数据类型、聚合统计、异常点和必要样例,不发送完整大表。
- 会议总结只发送会议标题、附件抽取文本、参会人展示名;不发送内部权限字段。
- 日志只记录输入长度、输出长度、hash、模型、耗时、token 用量、错误 reason。
- 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。
智能表格固定步骤:
read_file:读取表格数据。detect_metrics:识别核心指标。analyze_trends:分析趋势变化。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_iddocument_typetitlecontent_markdowncontent_plainsections_jsonsource_excerptprompt_versionmodel
智能文档生成成功但未保存时,只保留在 wb_ai_task.result_json 或短期缓存中;过期后不可保存。
4.8 wb_ai_table_analysis — 表格解读详情
保存表格报告解读结果。
关键字段:
record_idfile_id、file_name、file_type、file_sizetopicmetrics_jsonpreview_jsonalerts_jsoninterpretationexport_file_idprompt_versionmodel
文件解析结果不建议完整入库。大表只保存字段统计、样例行和 AI 输出,完整源文件仍在 IM 文件服务中。
4.9 wb_ai_meeting_summary — 会议 AI 总结
保存会议维度的 AI 总结,一个会议默认只有一份当前有效总结。
关键字段:
summary_idmeeting_idtask_idconclusions_jsondecisions_jsonaction_items_jsonsource_file_ids_jsonedited_byedited_atversion
关键索引:
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_idfeaturetask_idprovider=deepseekmodelprovider_request_idprompt_tokens、completion_tokens、total_tokenslatency_msstatuserror_reasoninput_hashoutput_hashcreated_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 流程:
- 解析 IM Token,得到租户和用户。
- 读取租户 AI 配置;租户没有配置时读取默认配置。
- 查询用户当天各 feature 已使用次数。
- 组装
features[],返回开关、入口名、每日限制、剩余次数、输入限制、文件限制。
配置建议缓存 1 分钟。配置变化后可通过后台主动清缓存,首版可接受短延迟。
5.2 AI 美文同步流程
AI 美文用于短文本润色,流程:
请求进入
-> 鉴权和租户解析
-> 参数校验 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 智能文档流程
创建任务:
- 校验输入长度、能力开关、配额和幂等。
- 写
wb_ai_task(status=queued, feature=smart_document)。 - 投递 Kafka 消息或写入本地任务队列。
- 返回
task_id给前端轮询。
Worker 执行:
- 将任务置为
processing。 - 构造
smart_document_v1prompt。 - 调用 DeepSeek。
- 解析
title、sections、content_markdown、summary。 - 结果写入
wb_ai_task.result_json,任务置为succeeded。 - 用户点击保存后,写
wb_ai_record和wb_ai_document。
注意:
- 生成成功不等于保存成功。
- 未保存结果可设置 2 小时过期。
- 重新生成创建新任务,不覆盖旧任务。
5.4 智能表格流程
智能表格必须先上传文件,再创建任务。
创建任务:
- 校验
file_id属于当前租户和用户。 - 校验文件类型为
xlsx/xls/csv,大小不超过配置。 - 写
wb_ai_task、wb_ai_task_step,预生成record_id。 - 投递任务,返回
task_id、record_id。
Worker 执行:
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 总结必须复用会议笔记权限。
流程:
- 校验当前用户可查看该会议笔记。
- 获取会议标题、时间、组织者、参会人、附件列表。
- 校验
file_ids均属于该会议且当前用户可读。 - 抽取附件文本;若附件为空,可基于会议描述和已有笔记文本生成,但必须有有效输入。
- 创建
meeting_summaryAI 任务。 - Worker 调用 DeepSeek 生成结论、关键决策、后续安排。
- 写入或更新
wb_ai_meeting_summary。
编辑保存:
- 校验会议笔记编辑权限。
- 使用
version乐观锁。 - 保存用户编辑内容,记录
edited_by、edited_at。
5.6 导出报告流程
表格报告导出流程:
- 校验记录属于当前用户。
- 读取
wb_ai_table_analysis。 - 使用服务端模板生成 DOCX 或 PDF。
- 上传到 IM 文件服务,得到
file_id。 - 返回下载 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:
- 请求进入后计算规范化请求体 hash。
- 查询
wb_ai_idempotency_record。 - 不存在则插入
processing记录并继续。 - 已存在且 hash 相同,返回首次响应。
- 已存在且 hash 不同,返回
IDEMPOTENCY_CONFLICT。 - 业务成功后更新响应摘要。
异步任务的幂等响应应返回同一个 task_id,不能重复创建任务。
6.3 任务调度
首选 Kafka:
| Topic | 用途 |
|---|---|
workbench.ai.task.created |
AI 任务创建 |
workbench.ai.task.retry |
AI 任务重试 |
workbench.ai.task.completed |
AI 任务完成事件,可选 |
消息体:
{
"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 的内容建议:
{
"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 错误包:
{
"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_idtenant_codeuser_id_hashfeaturetask_idprovidermodelstatuslatency_msprompt_tokenscompletion_tokenserror_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 中需要配置:
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 发布顺序
- 发布数据库 migration。
- 配置 DeepSeek Secret、AI Provider、模型和限额。
- 发布后端代码,默认租户 AI 开关关闭。
- 验证 Provider 健康检查和 AI 美文测试接口。
- 灰度开启测试租户 AI 美文。
- 依次开启智能文档、智能表格、会议总结。
- 前端打开入口。
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 建表草案
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 调用伪代码
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 |