Files
olons_server/docs/IM工作台AI后端概要设计文档.md
T

44 KiB
Raw Blame History

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 适配层。

后端需要完成以下核心能力:

  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 服务。

移动端 / 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 输出必须经过服务端解析和校验,不能直接原样入库给前端。

处理流程:

  1. Prompt 明确要求模型只输出 JSON。
  2. Provider 返回文本后,先截取 JSON 主体,去除 Markdown code fence。
  3. 按当前 feature 的 schema 反序列化。
  4. 校验必填字段、数组长度、字符串长度和枚举值。
  5. 校验失败时最多执行一次“修复 JSON”请求,仍失败则任务失败,reason=AI_TASK_FAILED。
  6. 校验通过后,生成标准化业务结果并入库。

示例:智能文档输出 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 前必须做最小必要原则:

  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 美文用于短文本润色,流程:

请求进入
  -> 鉴权和租户解析
  -> 参数校验 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 执行:

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 任务完成事件,可选

消息体:

{
  "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_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 中需要配置:

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 建表草案

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