
可以，现在应该把它整理成一个**分板块执行的落地方案**。你后面就不要再一直在脑子里绕了，按这个顺序推进。

# 知习知识库落地执行方案（完整版）

---

## 一、服务器部署板块

### 1. 当前服务器分工

```text
4核4G 轻量云：
主业务服务器

8核32G CVM：
知识库 / RAG / 文档处理 / AI 调度服务器

腾讯云 COS：
原始文件存储
```

### 2. 8核32G 服务器用途

这台服务器负责：

```text
Qdrant 向量库
LlamaIndex / RAG Worker
文档解析
PDF / DOCX / TXT / Markdown 处理
OCR / 多模态任务调度
chunking 切片
embedding 调用
知识库检索
AI Gateway 部分调度
DocumentImport 异步任务
KnowledgeChunk 写入
ImportCandidate 生成
```

它不负责：

```text
本地大模型推理
本地多模态大模型
GPU 任务
原始文件长期存储
```

---

## 二、服务器基础环境

### 1. 系统配置

```text
CPU：8核
内存：32G
系统：Ubuntu Server 22.04 LTS
系统盘：30G
数据盘：70G
公网：1Mbps
地域：北京
```

### 2. 数据盘挂载

挂载到：

```text
/data
```

目录规划：

```text
/data/docker          ← Docker data-root
/data/qdrant          ← Qdrant storage（向量数据）
/data/tmp/imports     ← 临时下载、解析工作区
/data/logs            ← Worker 日志
/data/worker-cache    ← llama-index 缓存
/data/backups         ← Qdrant 快照 + MySQL dump
```

系统盘只放：Ubuntu 系统 + 基础配置 + 少量代码。不要让 Docker、Qdrant、临时解析文件堆到 30G 系统盘里。

---

## 三、服务器需要安装的东西

### 1. 基础环境

```text
Docker
Docker Compose
Git
Node.js
pnpm
Python 3.11+
pip / poetry
nginx（可选，后续对外暴露 API 时配）
supervisor / systemd
logrotate
```

### 2. 知识库服务组件

```text
Qdrant（Docker）
RAG Worker（Python）
NestJS API（Node，部署在 4核4G 主服务器）
COS SDK
AI Gateway（Node，主服务器）
OCR Provider SDK
Embedding Provider SDK
```

### 3. Python Worker 主要依赖

```text
llama-index
qdrant-client
pymupdf / pdfplumber
python-docx
markdown
pandas / openpyxl
pydantic
requests / httpx
tencentcloud-sdk-python
Pillow（HEIC → JPG 转换）
```

### 4. Node 后端主要依赖

```text
NestJS
Prisma
MySQL client
Redis / BullMQ（任务队列）
COS SDK
AI Provider SDK
JWT / Auth
class-validator
zod / JSON Schema 校验
```

> **语言分工：** NestJS（Node）负责 API 层 + AI Gateway + 任务入队。Python 负责 RAG Worker（文档解析、chunking、embedding、Qdrant 写入），因为 llama-index 生态在 Python 侧最成熟。两者通过 Redis/BullMQ（入队） + 内部 HTTP（heartbeat/result）通信。

---

## 四、安全组和网络

### 1. 安全组建议

```text
22：只允许你的 IP
80 / 443：如果知识库服务需要对外访问再开
6333 / 6334：Qdrant 不开放公网
3306：MySQL 不开放公网
6379：Redis 不开放公网
```

### 2. 服务器之间通信

```text
4核4G 主服务器
→ 内网调用（HTTP）
8核32G 知识库服务器
```

不要让知识库内部接口直接暴露公网。

### 3. COS 访问方式

COS 不作为本地硬盘长期挂载。正确方式：

```text
Worker 通过 COS SDK 拉取文件
→ 临时下载到 /data/tmp/imports/{jobId}
→ 解析处理
→ 写入 Qdrant / MySQL
→ 删除临时文件
```

COS 负责原始文件存储，服务器负责临时处理和索引。

---

## 五、COS 存储结构

核心原则：

```text
数据库是主关系，COS 是文件仓库
COS 路径按 userId / knowledgeBaseId / sourceId 分类
权限、归属、状态永远以数据库为准
不要直接用用户原文件名当 objectKey（用 fileId/sourceId 避免重名和特殊字符）
```

### 1. Bucket

第一阶段一个 bucket 就够：

```text
zhixi-prod
```

### 2. 用户知识库原始文件

```text
users/{userId}/knowledge-bases/{knowledgeBaseId}/sources/{sourceId}/original/{fileId}.{ext}
```

示例：

```text
users/user_001/knowledge-bases/kb_001/sources/src_001/original/file_001.pdf
users/user_001/knowledge-bases/kb_001/sources/src_002/original/file_002.docx
```

### 3. 解析结果

```text
users/{userId}/knowledge-bases/{knowledgeBaseId}/sources/{sourceId}/parsed/parsed.md
users/{userId}/knowledge-bases/{knowledgeBaseId}/sources/{sourceId}/parsed/metadata.json
```

`parsed.md` 保留完整解析文本，MySQL 不存全文，通过 `parsedObjectKey` 引用。

### 4. OCR / 多模态结果

```text
users/{userId}/knowledge-bases/{knowledgeBaseId}/sources/{sourceId}/processed/ocr/page_001.json
users/{userId}/knowledge-bases/{knowledgeBaseId}/sources/{sourceId}/processed/vision/page_001.json
```

早期可只保存最终 `parsed.md`，调试 OCR/多模态质量时再保存中间结果。

### 5. 用户头像与反馈

```text
users/{userId}/profile/avatar/{fileId}.{ext}
users/{userId}/feedback/{feedbackId}/screenshots/{fileId}.{ext}
```

### 6. 系统内置知识库

```text
system/knowledge-bases/{systemKnowledgeBaseId}/sources/{sourceId}/original/{fileId}.{ext}
system/knowledge-bases/{systemKnowledgeBaseId}/sources/{sourceId}/parsed/parsed.md
system/knowledge-bases/{systemKnowledgeBaseId}/sources/{sourceId}/parsed/metadata.json
```

系统知识库只存一份，用户使用系统知识库时是"引用"，不要给每个用户复制一份。

### 7. 备份（同步到 COS）

```text
system/backups/qdrant/{yyyy-mm-dd}/zhixi_chunks.snapshot
system/backups/mysql/{yyyy-mm-dd}/zhixi.sql.gz
```

### 8. 用户导出

```text
exports/users/{userId}/{exportId}/learning_report.pdf
exports/users/{userId}/{exportId}/knowledge_base_export.zip
```

### 9. 临时文件（服务器本地为主）

默认放服务器 `/data/tmp/imports`，处理完删除。如果必须放 COS：

```text
temp/imports/{importId}/original.{ext}
temp/imports/{importId}/pages/page_001.png
temp/imports/{importId}/ocr/page_001.json
```

### 10. 完整 COS 目录树

```text
zhixi-prod/
  users/
    {userId}/
      knowledge-bases/
        {knowledgeBaseId}/
          sources/
            {sourceId}/
              original/
                {fileId}.{ext}
              parsed/
                parsed.md
                metadata.json
              processed/
                ocr/
                  page_001.json
                vision/
                  page_001.json
      profile/
        avatar/
          {fileId}.{ext}
      feedback/
        {feedbackId}/
          screenshots/
            {fileId}.{ext}

  system/
    knowledge-bases/
      {systemKnowledgeBaseId}/
        sources/
          {sourceId}/
            original/
              {fileId}.{ext}
            parsed/
              parsed.md
              metadata.json
    backups/
      qdrant/
        {yyyy-mm-dd}/
          zhixi_chunks.snapshot
      mysql/
        {yyyy-mm-dd}/
          zhixi.sql.gz

  exports/
    users/
      {userId}/
        {exportId}/
          learning_report.pdf
          knowledge_base_export.zip

  temp/
    imports/
      {importId}/
        original.{ext}

  public/
    app-assets/
      icons/
      illustrations/
```

### 11. 数据库里需保存的 COS 关联字段

`files` 表：`bucket / objectKey / originalFilename / mimeType / sizeBytes / sha256 / purpose / status`

`knowledge_sources` 表：`originalObjectKey / parsedObjectKey / metadataObjectKey`

### 12. COS 生命周期

```text
软删除后保留 7 天
每天凌晨定时任务清理超期文件
```

---

## 六、知识库支持的上传格式及处理策略

### 1. 第一阶段必须支持

```text
PDF   DOCX   TXT   Markdown / MD
PNG   JPG / JPEG   WEBP   HEIC
CSV   XLSX
```

### 2. 详细处理策略

| 类型 | 处理方式 | 工具 |
|------|---------|------|
| TXT / Markdown | 本地解析 | Python 原生 |
| DOCX | 本地解析 | python-docx |
| 文本型 PDF | PyMuPDF 提取文本层 | pymupdf |
| 扫描 PDF（文本层为空） | 百度 OCR / Qwen3-VL 兜底 | 百度 OCR → 硅基流动 |
| 图片文字 | 百度 OCR | 百度 OCR |
| 表格截图 | Qwen3-VL 多模态 | 硅基流动 |
| 图文混排 | Qwen3-VL 多模态 | 硅基流动 |
| CSV / XLSX | 本地解析为 Markdown table | pandas / openpyxl |
| PPTX | 预留，仅提取文本 | python-pptx（后续） |
| HEIC | 先转 JPG 再处理 | Pillow |

### 3. 暂时预留

```text
音频   视频   网页抓取   压缩包批量导入
```

### 4. PDF 解析细化

```text
先尝试 PyMuPDF 提取文本层
如果文本层为空或每页文本 < 50 字符
→ 判断为扫描件
→ 走百度 OCR（普通扫描文字）
→ 复杂排版 / 教材类走 Qwen3-VL
```

---

## 七、核心数据模型

### 1. File

```text
files
- id
- userId
- bucket
- objectKey
- originalFilename
- mimeType
- sizeBytes
- sha256              ← 用于重复文件检测
- purpose
- status
- createdAt
- updatedAt
- deletedAt
```

重复文件检测逻辑：同一用户、同一知识库内 sha256 重复 → 提示用户"该文件已存在"，允许取消 / 引用已有文件 / 仍然新增。

### 2. KnowledgeBase

```text
knowledge_bases
- id
- userId
- title
- description
- icon
- coverColor
- visibility           ← 先预留，第一阶段默认 private
- status
- itemCount
- sourceCount
- storageUsedBytes
- lastImportedAt
- lastStudiedAt
- createdAt
- updatedAt
- deletedAt
```

### 3. KnowledgeSource

```text
knowledge_sources
- id
- userId
- knowledgeBaseId
- fileId
- type
- title
- originalFilename
- mimeType
- sizeBytes
- textLength
- parseStatus
- indexStatus
- learningStatus
- parsedObjectKey
- version              ← 预留，默认 1
- parentSourceId       ← 预留（版本链）
- replacedBySourceId   ← 预留（被哪个新版本替代）
- errorCode
- errorMessage
- createdAt
- updatedAt
- deletedAt
```

### 4. DocumentImport

```text
document_imports
- id
- userId
- knowledgeBaseId
- sourceId
- status
- step
- progress              ← 0~100
- workerId
- retryCount            ← 新增
- maxRetries            ← 新增，默认 3
- heartbeatAt
- errorCode
- errorMessage
- startedAt
- completedAt
- createdAt
- updatedAt
```

状态机：

```text
QUEUED
CLAIMED
DOWNLOADING
PARSING
OCR_PROCESSING
VISION_PROCESSING
CLEANING
CHUNKING
EMBEDDING
INDEXING
GENERATING_CANDIDATES
WAITING_CONFIRM
COMPLETED
FAILED_RETRYABLE
FAILED_FINAL
CANCELED
```

**Heartbeat 机制：** Worker 每 30 秒上报一次。超过 5 分钟无 heartbeat → 状态回退 QUEUED + workerId 清空 + retryCount +1。

**Stale Job Recovery（定时任务，每分钟）：**

```sql
UPDATE document_imports
SET status = 'QUEUED', workerId = NULL
WHERE status IN ('CLAIMED', 'DOWNLOADING', 'PARSING', 'OCR_PROCESSING',
                 'VISION_PROCESSING', 'CLEANING', 'CHUNKING', 'EMBEDDING',
                 'INDEXING', 'GENERATING_CANDIDATES')
  AND heartbeatAt < NOW() - INTERVAL 5 MINUTE;
```

### 5. KnowledgeChunk

```text
knowledge_chunks
- id
- userId
- knowledgeBaseId
- sourceId
- content
- chunkIndex
- pageNumber
- sectionTitle
- tokenCount
- externalVectorId      ← Qdrant point ID
- embeddingModel        ← 'bge-m3'
- embeddingStatus       ← PENDING / COMPLETED / FAILED
- metadataJson          ← { overlapWith, chunkType, ... }
- createdAt
- updatedAt
- deletedAt
```

### 6. ImportCandidate

```text
import_candidates
- id
- userId
- knowledgeBaseId
- sourceId
- importId
- title
- summary
- content
- tagsJson
- recallQuestionsJson
- sourceTextSnippet
- sourceChunkIds        ← 新增：关联哪些 chunk
- confidence            ← 0.0 ~ 1.0
- difficulty            ← 新增：easy / medium / hard
- orderIndex
- status                ← PENDING / ACCEPTED / REJECTED / EDITED / IMPORTED
- createdAt
- updatedAt
```

生成规则（已拍板）：

```text
每 2000 中文字生成 1~2 个候选
单个 source 上限 30 个
最少生成 3 个（即使文档很短）
第一阶段不自动接受，全部 PENDING 等用户确认
```

### 7. KnowledgeItem

```text
knowledge_items
- id
- userId
- knowledgeBaseId
- sourceId
- importId
- title
- summary
- content
- tagsJson
- sourceType
- masteryLevel
- sourceDeleted                    ← 新增：原资料是否已删除
- sourceTitleSnapshot              ← 新增：原资料标题快照
- sourceSnippetSnapshot            ← 新增：原引用片段快照
- createdAt
- updatedAt
- deletedAt
```

### 8. MembershipPlan

```text
membership_plans
- id
- code             ← FREE / PRO_TEST / PRO
- name
- priceMonthly     ← 单位：分（避免浮点），配置化可随时调
- priceYearly
- maxKnowledgeBases
- maxStorageBytes
- maxFileSizeBytes
- monthlyOcrPages
- monthlyVisionPages
- monthlyChatCount
- monthlyAiAnalysisCount
- monthlyRecallCount
- monthlyCardGenCount
- isActive
- createdAt
- updatedAt
```

价格不写死在代码里，后端只认 planId + quotaConfig。第一阶段先用 28 元/月作为 Pro 预设，跑 1 个月真实成本后再正式确定价格和额度。

### 9. BackupJob

```text
backup_jobs
- id
- type            ← QDRANT / MYSQL
- status          ← RUNNING / COMPLETED / FAILED
- localPath
- cosObjectKey
- fileSizeBytes
- startedAt
- completedAt
- errorMessage
- createdAt
```

---

## 八、Chunking 切片策略（已拍板）

### 1. 默认参数

```text
chunk_size = 512 tokens
overlap = 64 tokens（~12%）
策略 = 递归字符分割 + 中文分句保护
```

### 2. 分文档类型规则

| 文档类型 | 切片方式 |
|---------|---------|
| Markdown | 优先按 `#` / `##` / `###` 标题分层切片 |
| PDF | 保留 pageNumber，在段落边界切 |
| DOCX | 按标题 / 段落层级切 |
| 表格 | **整表保留**，不强行切碎 |
| 代码块 | **整块保留** |
| 公式附近 | 公式 + 上下文保留在同一 chunk |
| 普通文本 | 512 tokens |
| 复杂解释型段落 | 允许扩展到 768 tokens |

### 3. 注入每个 chunk 的元数据

```text
sourceId
pageNumber
sectionTitle
chunkIndex
chunkType（text / table / code / formula）
```

---

## 九、Qdrant 设计（已拍板）

### 1. 部署参数

```text
部署模式：单节点 Docker
Collection：zhixi_chunks
vector_size：1024
distance：Cosine
```

### 2. Collection 创建参数

```json
{
  "vectors": {
    "size": 1024,
    "distance": "Cosine"
  },
  "shard_number": 1,
  "replication_factor": 1,
  "hnsw_config": {
    "m": 16,
    "ef_construct": 100
  },
  "optimizers_config": {
    "default_segment_number": 2
  },
  "on_disk_payload": true
}
```

> **注意：** 单节点不要设 `replication_factor = 2`，没有意义且浪费资源。

### 3. Payload

```json
{
  "userId": "user_xxx",
  "knowledgeBaseId": "kb_xxx",
  "sourceId": "src_xxx",
  "chunkId": "chunk_xxx",
  "pageNumber": 3,
  "sectionTitle": "章节标题",
  "deleted": false
}
```

### 4. Payload 索引（必须建）

```text
userId             → keyword index
knowledgeBaseId    → keyword index
sourceId           → keyword index
chunkId            → keyword index
deleted            → bool index
```

### 5. 检索过滤

每次检索必须带：

```text
userId = 当前用户
knowledgeBaseId = 当前知识库
deleted = false
```

### 6. 备份策略（已拍板：本地 + 同步 COS）

```text
每日凌晨 3 点生成 Qdrant snapshot → /data/backups/qdrant/
生成后上传到 COS → system/backups/qdrant/{yyyy-mm-dd}/zhixi_chunks.snapshot
本地保留最近 7 天
COS 保留最近 30 天
```

恢复依赖链：COS 原始文件 + MySQL 元数据 + Qdrant 快照 → 三者共同保证可恢复。

### 7. Qdrant 集群迁移时机

```text
第一阶段：单节点 Qdrant，1 shard，replication_factor = 1
```

迁移触发条件（任一满足即评估）：

```text
collection 超过 100 万 points
Qdrant 内存长期超过 70%
检索 p95 延迟超过 1.5 秒
snapshot / restore 时间过长
```

迁移路径：

```text
阶段 1：单节点 Qdrant（现在）
阶段 2：当前服务器加数据盘
阶段 3：Qdrant 独立迁移到新服务器
阶段 4：Qdrant 集群
```

---

## 十、Embedding & Rerank（已拍板）

### 1. Embedding

```text
模型：BAAI/bge-m3
Provider：硅基流动
维度：1024
Batch size：50~100 chunks / 批次
```

注意：不要一个大文档全塞进一次 embedding 调用，分批处理。单批失败只重试该批，不重跑全部。

备选降级（如果 bge-m3 成本或速度不理想）：

```text
→ 切到 bge-large-zh-v1.5（同样 1024d）
```

但第一版先统一用 bge-m3，不要同时支持多个 embedding 模型。

### 2. Rerank

```text
模型：BAAI/bge-reranker-v2-m3
Provider：硅基流动
输入：query + Top-50 候选 chunks
输出：精排 Top-5~8
```

---

## 十一、RAG 检索流程（已拍板）

```text
用户提问
→ 生成 query embedding（bge-m3）
→ Qdrant ANN 召回 Top-50（带 userId/kbId/deleted 过滤）
→ bge-reranker-v2-m3 精排
→ 取 Top-5（普通问题）~ Top-8（复杂问题）
→ 拼接 context
→ DeepSeek 生成回答 + 引用溯源
```

### Context 拼接格式

```text
[来源：{sourceTitle}，章节：{sectionTitle}，第 {pageNumber} 页]
{chunkContent}
```

### 知识库对话多轮策略

第一阶段：

```text
保存 chat session + messages：✅
取最近 3 轮对话拼入 prompt：✅ 少量做
根据历史重写检索 query：❌ 后面再做
通用 AI 问答（非知识库内容）：❌ 第一阶段不支持
```

检索 query 仍使用用户当前原问题，但 prompt 上下文附带最近 3 轮历史。

当检索不到相关内容时返回：

```text
当前知识库中没有找到足够相关的资料。
你可以上传更多资料，或换一种问法。
```

知习第一阶段只做**基于当前知识库内容的 RAG 问答**，不做纯通用 AI 对话。避免产品定位偏离、增加无意义 token 成本和用户把它当 ChatGPT 用。

---

## 十二、AI Provider 策略

### 1. DeepSeek 官方 — 核心文本智能

```text
知识点提取         默认 V4 Flash（非思考）
摘要 / 标签         默认 V4 Flash
主动回忆题          默认 V4 Flash
复习卡生成          默认 V4 Flash
知识库问答          默认 V4 Flash
主动回忆诊断         V4 Flash thinking
待巩固项分析         V4 Flash thinking
学习报告             V4 Flash thinking
高价值深度分析       V4 Pro
```

### 2. 硅基流动 — 工具模型

```text
Qwen3-VL 多模态     Qwen/Qwen3-VL-32B-Instruct
复杂视觉兜底         Qwen/Qwen3-VL-32B-Thinking
embedding           BAAI/bge-m3
rerank              BAAI/bge-reranker-v2-m3
备用模型池           营销 / 客服 / 润色测试
```

### 3. 百度 OCR

```text
普通扫描文字
图片文字
截图文字
→ 复杂页面不要用 OCR 硬识别，交给 Qwen3-VL
```

---

## 十三、AI Gateway

所有 AI 调用必须走 AI Gateway。负责：

```text
模型路由
Prompt 版本管理
JSON Schema 校验
失败重试 + 超时控制
token 统计 + 成本估算
AIUsageLog
会员额度扣减
模型降级
```

业务模块只调用：

```text
AIGateway.run("extract_knowledge_candidates")
AIGateway.run("analyze_active_recall")
AIGateway.run("knowledge_chat")
AIGateway.run("parse_complex_page")
AIGateway.run("embed_chunks")
AIGateway.run("rerank_chunks")
```

---

## 十四、Worker 部署与并发控制

### 1. 进程数与并发（已拍板：单 Worker 起步）

```text
RAG Worker 进程数：1
```

8 核 32G 服务器起步并发：

```text
文档导入并发        1
embedding batch     50~100 chunks
OCR 并发            1~2
多模态并发          1
候选知识点生成并发   1~2
```

原因：文档解析、embedding、OCR、多模态、Qdrant upsert 都可能吃 CPU/内存/网络。先稳，再提并发。

### 2. 扩展条件

```text
CPU 长期低于 50%
内存长期低于 60%
任务队列积压明显
```

满足以上 → 扩展到 2~3 Worker 进程。多 Worker 时靠数据库原子更新防止重复处理：

```sql
UPDATE document_imports
SET status = 'CLAIMED', workerId = ?, heartbeatAt = NOW()
WHERE id = ?
  AND status = 'QUEUED';
```

### 3. 各环节重试策略

| 环节 | 重试次数 | 退避策略 | 失败后行为 |
|------|---------|---------|-----------|
| COS 下载 | 3 次 | 指数退避 1s/4s/16s | FAILED_RETRYABLE |
| OCR API | 2 次 | 固定 2s | 降级到多模态 |
| 多模态 API | 2 次 | 固定 2s | FAILED_RETRYABLE |
| Embedding batch | 2 次 | 固定 2s | 只重试该 batch |
| Qdrant upsert | 2 次 | 固定 2s | 回滚该 batch |
| Worker 崩溃 | — | heartbeat 超时 5min | 自动回队列 |
| DeepSeek 调用 | 2 次 | 固定 1s | FAILED_RETRYABLE |

### 2. 最大重试

```text
maxRetries = 3
超过 → FAILED_FINAL
→ 通知用户（iOS 解析失败页）
→ 后台告警
```

---

## 十五、删除与清理策略（已拍板）

### 1. 删除单个 KnowledgeSource

```text
source.deletedAt = now()
chunks.deletedAt = now()
Qdrant points → deleted = true（不物理删除）
COS 原文件 → 进入待清理队列（7 天后清除）
KnowledgeItem → 默认保留
```

用户已确认、编辑、学习过的知识点，不应因删除原文件而丢失。KnowledgeItem 记录来源状态：

```text
knowledge_items.sourceDeleted = true
knowledge_items.sourceTitleSnapshot = 原资料标题
knowledge_items.sourceSnippetSnapshot = 原始引用片段
```

这样即使原资料被删，知识点页仍能展示"该知识点来自已删除资料"。

### 2. 用户删除 source 时给两个选项

```text
默认：仅删除原资料，保留已确认知识点
高级：同时删除该资料生成的知识点
```

### 3. 删除整个 KnowledgeBase（与人不同）

```text
knowledgeBase.deletedAt = now()
→ 级联软删该 KB 下所有 sources / chunks / candidates / items / review cards / learning records
→ Qdrant 标记 deleted = true
→ COS 异步清理
```

用户删除的是整个学习空间，所以级联删除。

### 4. 后台物理清理（每日凌晨）

数据保留天数全部配置化（环境变量）：

```text
DATA_RETENTION_DAYS=30          ← 普通业务数据物理删除
SOURCE_PURGE_DAYS=7             ← COS 原文件物理删除
QDRANT_PURGE_DAYS=7             ← Qdrant deleted=true 点物理删除
COS_PURGE_DAYS=7                ← COS 标记删除文件物理删除
AI_USAGE_LOG_RETENTION_DAYS=180  ← AI 成本日志保留更久
ADMIN_AUDIT_RETENTION_DAYS=365   ← 审计日志保留更久
```

规则：

```text
普通业务数据 soft delete 后 30 天物理删除
COS 原文件 soft delete 后 7 天物理删除
Qdrant deleted=true 后 7 天物理删除
AI 成本日志保留 180 天
后台操作审计日志保留 365 天
用户学习记录 / 支付记录不要跟普通 source 一起快速物理删除
```

---

## 十六、备份与灾备策略（已拍板）

### 1. MySQL 备份

```text
优先级最高：MySQL > Qdrant > COS
```

理由：Qdrant 可重建，COS 有原文件，但 MySQL 丢了业务关系、用户数据、学习记录会非常麻烦。

```text
每日凌晨备份 → /data/backups/mysql/zhixi_{yyyy-mm-dd}.sql.gz
备份后上传 COS → system/backups/mysql/{yyyy-mm-dd}/zhixi.sql.gz
本地保留 7 天
COS 保留 30 天
```

### 2. Qdrant 备份

```text
每日凌晨 3 点生成 snapshot → /data/backups/qdrant/
生成后上传 COS → system/backups/qdrant/{yyyy-mm-dd}/zhixi_chunks.snapshot
本地保留 7 天
COS 保留 30 天
```

### 3. 备份任务记录表

```text
backup_jobs
- id
- type            ← QDRANT / MYSQL
- status          ← RUNNING / COMPLETED / FAILED
- localPath
- cosObjectKey
- fileSizeBytes
- startedAt
- completedAt
- errorMessage
- createdAt
```

### 4. 恢复依赖链

```text
COS 原始文件 + MySQL 元数据 + Qdrant 快照 → 三者共同保证可恢复
```

---

## 十七、文档版本管理

第一阶段不做完整版本管理，但预留字段：

```text
KnowledgeSource.version = 1
KnowledgeSource.parentSourceId = nullable
KnowledgeSource.replacedBySourceId = nullable
```

当前行为：用户重新上传同名文件 → 按新 source 独立处理。

后续升级：新版本 ready → 旧版本 Qdrant 标记 deleted=true → 旧版本 COS 保留 7 天。

---

## 十八、后台管理

后台要能看：

```text
用户列表
知识库列表
文件列表
DocumentImport 任务（按状态筛选）
失败任务（含错误原因）
OCR 调用记录（按用户、按时间）
多模态调用记录
DeepSeek 调用记录
AI 成本（按 Provider 汇总）
Qdrant 索引状态（collection 大小、points 数量）
高成本用户 Top N
会员额度使用情况
反馈记录
后台操作审计
```

核心目标是**成本可视化和异常发现**。

---

## 十九、额度系统

### 1. 必须控制的维度

```text
知识库数量
总存储空间
单文件大小
每月上传文件数
每月普通解析页数
每月 OCR 页数
每月多模态页数
每月知识库对话次数
每月 AI 分析次数
每月主动回忆次数
每月复习卡生成次数
```

### 2. 检查点（每次调用前）

```text
上传前 → 存储 + 数量
解析前 → 页数
OCR 前 → OCR 页数
多模态前 → 多模态页数
知识点生成前 → AI 分析次数
知识库对话前 → 对话次数
AI 诊断前 → AI 分析次数
复习卡生成前 → 复习卡次数
```

### 3. 推荐初始额度（已拍板：配置化，随时可调）

| 维度 | 免费用户 | Pro 用户 |
|------|---------|---------|
| 知识库数量 | 3 个 | 30 个 |
| 总存储 | 100 MB | 5 GB |
| 单文件大小 | 20 MB | 100 MB |
| 每月 OCR 页数 | 20 页 | 500 页 |
| 每月多模态页数 | 5 页 | 100 页 |
| 每月知识库对话 | 20 次 | 1000 次 |
| 每月 AI 诊断 | 20 次 | 500 次 |

### 4. 定价策略

```text
价格不写死在代码里，后端只认 planId + quotaConfig
第一阶段按 28 元/月作为 Pro 预设
跑 1 个月真实成本后正式确定价格和额度
重点记录：DeepSeek token 成本 / 硅基流动视觉成本 / 百度 OCR 页数 / COS 存储 / Qdrant 增长 / 高成本用户行为
```

---

## 二十、核心接口设计

### 1. 文件上传

```http
POST /api/files/upload-url
```

返回：

```json
{
  "fileId": "file_xxx",
  "uploadUrl": "...",
  "objectKey": "...",
  "headers": {},
  "duplicateOf": null
}
```

如果 sha256 匹配到已有文件，返回 `duplicateOf` 指向已有 fileId，iOS 提示用户。

### 2. 知识库 CRUD

```http
POST   /api/knowledge-bases
GET    /api/knowledge-bases
GET    /api/knowledge-bases/:id
PATCH  /api/knowledge-bases/:id
DELETE /api/knowledge-bases/:id
```

### 3. 资料来源

```http
POST   /api/knowledge-bases/:id/sources
GET    /api/knowledge-bases/:id/sources
GET    /api/knowledge-sources/:sourceId
DELETE /api/knowledge-sources/:sourceId
```

创建 source 后自动生成 KnowledgeSource + DocumentImport。

### 4. 导入任务

```http
GET  /api/document-imports/:id
GET  /api/knowledge-sources/:sourceId/imports/latest
POST /api/document-imports/:id/retry
POST /api/document-imports/:id/cancel
```

iOS 用这些接口展示导入进度（排队中 → 解析中 → 索引中 → 生成知识点中 → 等待确认 → 完成 / 失败可重试）。

### 5. Worker 内部接口

```http
GET  /internal/rag/jobs/next
POST /internal/rag/jobs/:id/heartbeat
POST /internal/rag/jobs/:id/result
POST /internal/rag/jobs/:id/fail
```

### 6. 候选知识点

```http
GET  /api/knowledge-sources/:sourceId/import-candidates
PATCH /api/import-candidates/:id
POST /api/import-candidates/:id/accept
POST /api/import-candidates/:id/reject
POST /api/import-candidates/batch-accept
```

用户确认后生成 KnowledgeItem。

### 7. 正式知识点

```http
GET    /api/knowledge-bases/:id/items
GET    /api/knowledge-items/:id
POST   /api/knowledge-items           ← 手动创建
PATCH  /api/knowledge-items/:id
DELETE /api/knowledge-items/:id
```

### 8. 知识库对话

```http
POST /api/knowledge-bases/:id/chat
GET  /api/knowledge-bases/:id/chat-sessions
GET  /api/knowledge-chat-sessions/:id/messages
```

返回：

```json
{
  "answer": "...",
  "citations": [
    {
      "sourceId": "src_xxx",
      "chunkId": "chunk_xxx",
      "title": "资料标题",
      "snippet": "引用片段",
      "pageNumber": 3
    }
  ],
  "suggestedActions": [
    "CREATE_KNOWLEDGE_ITEM",
    "GENERATE_ACTIVE_RECALL",
    "ADD_TO_FOCUS_ITEM"
  ]
}
```

### 9. 单文件学习

```http
POST /api/knowledge-sources/:sourceId/prepare-learning
GET  /api/knowledge-sources/:sourceId/learning-view
POST /api/knowledge-items/:id/active-recall
POST /api/active-recall-answers/:id/analyze
GET  /api/knowledge-items/:id/review-cards
```

流程：

```text
source → ImportCandidate → KnowledgeItem
→ ActiveRecall → AIAnalysis → FocusItem → ReviewCard
```

---

## 二十一、知识库主流程

### 1. 索引流程

```text
iOS 上传文件到 COS
→ 后端创建 File（含 sha256 重复检测）
→ 后端创建 KnowledgeSource
→ 创建 DocumentImport（status = QUEUED）
→ Worker claim 任务（QUEUED → CLAIMED）
→ Worker 从 COS 拉文件到 /data/tmp/imports/{jobId}
→ 本地解析 / OCR / 多模态
→ 写入 parsed.md 到 COS
→ 清洗文本
→ chunking（512 tokens + 64 overlap）
→ embedding（bge-m3，batch 50~100）
→ Qdrant upsert
→ 保存 KnowledgeChunk 到 MySQL
→ source.indexStatus = INDEXED
→ import.status = COMPLETED
```

### 2. 学习流程

```text
用户打开文件
→ DeepSeek 生成 ImportCandidate（上限 30 条）
→ 用户确认 / 编辑 / 拒绝
→ 生成 KnowledgeItem
→ 用户主动回忆
→ DeepSeek 诊断（thinking 模式）
→ 生成 FocusItem（待巩固项）
→ 生成 ReviewCard（复习卡）
→ 记录 LearningActivity
```

---

## 二十二、iOS 需要的页面

### 知识库相关

```text
知识库列表页
知识库详情页
创建知识库页
上传资料页
资料列表页
资料详情页
导入进度页（含步骤+进度条）
解析失败页（含错误原因+重试按钮）
候选知识点确认页（批量接受/拒绝/编辑）
知识点详情页
知识库对话页
```

### 学习相关

```text
单文件学习首页
主动回忆输入页
AI 分析结果页
待巩固项页
复习卡页
学习记录页
```

### 额度相关

```text
额度展示页
会员升级页
OCR / 多模态额度提示
文件过大提示
解析额度不足提示
重复文件提示
```

---

## 二十三、执行顺序

### 第一阶段：服务器基础

```text
1. 购买服务器
2. 挂载 /data 数据盘
3. 配置安全组
4. 安装 Docker / Docker Compose
5. 配置 Docker data-root → /data/docker
6. 部署 Qdrant（含 payload 索引创建）
7. 配置 COS SDK
8. 配置 MySQL 每日备份脚本 + COS 同步
9. 配置 Qdrant 每日快照脚本 + COS 同步
```

### 第二阶段：基础数据模型

```text
1. File（含 sha256）
2. KnowledgeBase
3. KnowledgeSource（含 version/reserved 字段）
4. DocumentImport（含 retryCount/heartbeat 机制）
5. KnowledgeChunk
6. ImportCandidate（含 sourceChunkIds/difficulty）
7. KnowledgeItem
```

### 第三阶段：文件导入闭环

```text
1. iOS 申请上传 URL
2. iOS 直传 COS + sha256 计算
3. 后端重复文件检测
4. 创建 source + import 任务
5. Worker claim + heartbeat
6. 本地解析（PDF/DOCX/TXT/MD）
7. OCR / 多模态（按需）
8. 写入 parsed.md 到 COS
9. 更新任务状态
```

### 第四阶段：RAG 索引

```text
1. chunking（512 tokens + 64 overlap）
2. embedding（bge-m3, batch 50~100）
3. Qdrant upsert
4. 保存 KnowledgeChunk 到 MySQL
5. source.indexStatus = INDEXED
```

### 第五阶段：AI 学习化

```text
1. DeepSeek 生成 ImportCandidate
2. iOS 展示候选知识点（含置信度、标签、回忆题）
3. 用户确认 / 编辑
4. 生成 KnowledgeItem
```

### 第六阶段：单文件学习

```text
1. 主动回忆
2. AI 诊断（thinking 模式）
3. 待巩固项 → FocusItem
4. 复习卡 → ReviewCard
5. 学习记录
```

### 第七阶段：知识库增强

```text
1. 知识库对话（单轮 RAG + 最近 3 轮上下文）
2. 引用来源（citations）
3. rerank 精排
4. 推荐学习内容
5. 后台成本监控
6. 额度检查全链路接入
```

---

## 二十四、最终落地原则

```text
COS 存原始文件           → 检索前临时拉取，不留本地
Qdrant 存向量索引         → 单节点，1024d Cosine，deleted 标记而非物理删除
MySQL 存业务状态          → Prisma ORM，软删除 + 后台清理
DeepSeek 负责核心文本智能  → Flash 日常 + thinking 诊断 + Pro 高价值
硅基流动负责工具模型       → embedding / rerank / 多模态
百度 OCR 负责普通扫描文字   → 复杂页面交给 Qwen3-VL
切片 512 token + 64 overlap → 递归分割 + 中文分句保护
候选知识点上限 30 条/source → 不自动接受，用户确认
删除策略软删除 + 7 天冷却   → 后台定时物理清理
所有成本进入额度系统        → 每次调用前查额度
```

---

## 二十五、全部决策汇总

| # | 决策项 | 最终决策 |
|---|--------|---------|
| 1 | Chunk size | 512 tokens |
| 2 | Overlap | 64 tokens（~12%） |
| 3 | 切片策略 | 递归字符分割 + 中文分句保护 |
| 4 | Embedding 模型 | BAAI/bge-m3，硅基流动 |
| 5 | Vector 维度 | 1024 |
| 6 | Qdrant distance | Cosine |
| 7 | Qdrant 部署 | 单节点 Docker，1 shard，replication_factor=1 |
| 8 | Qdrant 集群时机 | 100 万 points 或资源瓶颈后评估 |
| 9 | Rerank 模型 | BAAI/bge-reranker-v2-m3 |
| 10 | RAG 召回 | Top-50 ANN → rerank → Top-5~8 |
| 11 | 知识库对话 | 仅限 KB 内检索，不支持通用 AI 问答 |
| 12 | 多轮对话 | 保存 session + 最近 3 轮上下文拼入 prompt，不做 query rewrite |
| 13 | 候选知识点数量 | 每 2000 字 1~2 条，上限 30，最少 3 |
| 14 | 自动接受 | 第一阶段不做，全部 PENDING 等确认 |
| 15 | OCR | 百度 OCR（简单文字），Qwen3-VL（复杂排版） |
| 16 | 多模态兜底 | 硅基流动 Qwen3-VL-32B-Thinking |
| 17 | 删除 source → KnowledgeItem | 默认保留，记录 sourceDeleted 快照 |
| 18 | 删除 KB → 全对象 | 级联删除 |
| 19 | Qdrant 快照 | 本地 + 同步 COS |
| 20 | Qdrant 本地快照保留 | 7 天 |
| 21 | Qdrant COS 快照保留 | 30 天 |
| 22 | MySQL 备份 | 每日凌晨 + 同步 COS |
| 23 | COS 文件清理 | soft delete 后 7 天 |
| 24 | MySQL 物理删除 | 默认 30 天，环境变量可配 |
| 25 | AI 成本日志保留 | 180 天 |
| 26 | 审计日志保留 | 365 天 |
| 27 | Pro 定价 | 28 元/月预设，配置化，跑 1 个月成本后调整 |
| 28 | Worker 进程数 | 单 Worker 起步 |
| 29 | Worker 扩展 | CPU/内存/队列压力上来后 2~3 个 |
| 30 | 文档版本管理 | 第一阶段不做，预留 version 字段 |
| 31 | 重复文件 | sha256 检测 + 提示用户，不做自动去重 |
| 32 | 语言分工 | Node/NestJS=API+AI Gateway；Python=RAG Worker |

---

你接下来就按这个顺序推进：**先部署服务器环境，再建数据模型，再做上传和导入任务，再接 Qdrant，最后接学习闭环和知识库对话。**
