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

1290 lines
44 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 |