# 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_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 |