# 政务公文 RAG 系统 — 产品需求文档 (PRD)

## 1. 项目概述

### 1.1 项目背景

政务机关在日常运作中产生大量公文（通知、决定、报告、请示、批复、函、纪要等），现有 OA 系统承担文档流转和存储，但缺乏智能检索和深度分析能力。用户在查找历史公文、梳理政策脉络、研究关联决策时效率低下。

本项目构建一套基于 RAG（Retrieval-Augmented Generation）的智能检索与研究分析系统，与现有 OA 系统对接，提供混合检索、知识图谱关联发现和 LLM 辅助分析能力。

### 1.2 项目目标

- 提供高效的公文智能检索能力，支持关键词、语义化描述混合检索
- 构建公文知识图谱，发现文档间的实体关联和引用关系
- 提供研究型分析能力，辅助材料梳理、政策分析和报告生成
- 严格遵循权限模型，确保用户仅能检索有权限的文档

### 1.3 项目范围

**包含**：
- 文档入库管线（OA 推送 → 解析 → 索引 → 图谱构建）
- 混合检索（向量 + BM25 + 权限过滤）
- GraphRAG 知识图谱（Neo4j）
- Research 研究型分析（LLM 辅助）
- Web 前端（搜索、研究、图谱可视化、管理后台）

**首版范围说明**：
- 首版优先交付文档级、主题级、事项级能力
- 条款级定位、条款级证据回链、条款级可解释问答作为后续 Phase 2 能力，不纳入首版交付

**不包含**：
- OA 系统本身的改造
- 用户管理和认证系统（复用 OA 已有体系）
- 公文的创建、审批、流转功能

---

## 2. 用户与使用场景

### 2.1 用户画像

| 指标 | 数值 |
|------|------|
| 日活用户（DAU） | ~30,000 |
| 峰值并发 | ~50 |
| 用户类型 | 政务机关工作人员 |
| 用户所属部门数 | 大多 1 个，最多 10 个 |

### 2.2 使用场景

#### 场景一：普通检索

**用户故事**：作为一名政务工作人员，我需要通过关键词或口语化描述快速找到相关公文，以便参考历史文件处理当前工作。

**流程**：
1. 用户在搜索框输入查询（如"关于机构改革的最新通知"）
2. 系统执行混合检索（向量语义 + BM25 关键词），仅返回用户有权限的文档
3. 结果以文档列表形式展示，包含标题、文号、发文机关、日期、匹配片段高亮
4. 支持高级筛选：按发文机关、文种、日期范围、主题词过滤
5. 点击可查看文档详情和 PDF 原文

**验收标准**：
- 搜索结果 P95 延迟 < 500ms
- 关键词匹配和语义相似结果均能召回
- 无权限文档不出现在结果中
- 支持结果高亮和分页

#### 场景二：Research 研究型分析

**用户故事**：作为一名政策研究人员，我需要围绕某个主题梳理所有相关公文，分析政策演变和关联决策，以便撰写研究报告或辅助决策。

**流程**：
1. 用户在研究页面输入分析问题（如"梳理 2024 年以来所有关于机构改革的公文，分析改革方向"）
2. 系统拆分子查询，执行多路召回（ES 混合检索 + Neo4j 图谱查询）
3. 利用知识图谱发现关联文档、引用链、相关决策
4. LLM 生成结构化研究报告，包含引用标注，流式输出；首版引用粒度到文档级和 Chunk 级，条款级引用后置到 Phase 2
5. 右侧展示引用文档列表和关联知识图谱子图
6. 引用标注可点击跳转到对应文档或对应文本片段；条款级定位后续支持

**验收标准**：
- 能发现不仅靠关键词匹配的关联文档（通过图谱关系）
- 生成的报告包含准确的文档级 / Chunk 级引用标注；条款级引用后置到 Phase 2
- 支持 SSE 流式输出
- 图谱子图可视化展示当前话题涉及的实体关系

---

## 3. 功能需求

### 3.1 文档入库

#### 3.1.1 OA Webhook 接收

- OA 系统在公文发布/传阅时主动推送数据到 RAG 系统
- 推送内容：PDF 文件（multipart）+ JSON 元数据
- 元数据字段：

| 字段 | 类型 | 说明 |
|------|------|------|
| doc_id | string | 文档唯一标识 |
| title | string | 公文标题 |
| doc_number | string | 文号，如"国发〔2024〕15号" |
| issuing_org | string | 发文机关 |
| receiving_orgs | string[] | 收文单位列表 |
| doc_type | string | 文种（通知/决定/报告/请示/批复/函/纪要） |
| subject_words | string[] | 主题词 |
| signer | string | 签发人 |
| publish_date | string | 发文日期 (yyyy-MM-dd) |
| acl_dept_ids | string[] | 有权限的部门 ID 列表 |
| acl_user_ids | string[] | 有权限的用户 ID 列表 |
| knowledge_category | string | 知识分类，上传时由调用方手动指定（非 LLM 自动抽取） |

#### 3.1.2 PDF 解析

- 使用 PyMuPDF 提取 PDF 文本内容
- 文本清洗：去除页眉页脚、水印文字、重复标题
- 表格内容提取（如有）

#### 3.1.3 智能分块

- 按自然段落分块，每块约 512 tokens
- 相邻块重叠约 64 tokens，保证上下文连贯
- 每个 chunk 附带文档级元数据（标题、文号等）
- 保留 chunk 在原文中的位置信息（chunk_index）

#### 3.1.4 Embedding 生成

- 调用 OpenAI 兼容的 Embedding API
- 维度：1024
- 批量处理（batch_size=32），含错误重试

#### 3.1.5 索引写入

- ES Bulk API 批量写入 chunks（含向量、全文、元数据、权限字段）
- 每个 chunk 一条 ES 文档

### 3.2 混合检索

#### 3.2.1 检索策略

- **BM25 全文检索**：基于 IK 分词器 + 政务同义词表，搜索 title (权重 ×3) 和 content 字段
- **向量检索**：查询文本生成 1024 维 Embedding，kNN 近邻搜索，cosine 相似度
- **融合策略**：ES 8.x 原生 RRF (Reciprocal Rank Fusion) Retriever
  - rank_window_size: 100
  - rank_constant: 60
- **结果聚合**：chunk 级结果聚合到文档级，每文档保留 top-3 匹配 chunks

#### 3.2.2 高级筛选

- 发文机关筛选
- 文种筛选
- 日期范围筛选
- 主题词筛选
- 筛选条件与混合检索组合使用

#### 3.2.3 结果展示

- 文档列表：标题、文号、发文机关、日期、匹配度、知识分类、主题词
- 匹配片段高亮（使用 ES highlight）
- 匹配块详情（matched_chunks）：每个命中块包含高亮文本、所在页码、段落层级标题、元素类型、块序号，支持快速预览中展示页码和段落信息
- 聚合统计侧边栏：按机关、文种、年份分布
- 分页（每页 20 条）
- 页面底部展示权限提示说明

### 3.3 权限模型

#### 3.3.1 权限规则

| 规则 | 说明 |
|------|------|
| 权限来源 | 发送给部门（acl_dept_ids）+ 传阅给个人（acl_user_ids） |
| 匹配逻辑 | **并集**：命中部门权限 OR 命中个人权限 → 有权限 |
| 部门匹配 | 用户所属部门（不含下属部门）在文档的 acl_dept_ids 中 |
| 个人匹配 | 用户 userId 在文档的 acl_user_ids 中 |
| 权限变更 | 仅追加，不撤回 |
| 用户部门数 | 大多 1 个，最多 10 个 |

#### 3.3.2 权限过滤实现

- 在 ES 查询中通过 `bool.should` + `terms` 实现权限过滤
- kNN 使用 pre-filter（在向量搜索前缩小候选集）
- BM25 使用 filter clause
- 两路检索使用相同的权限过滤条件

#### 3.3.3 权限追加

- OA 系统通过 Webhook 推送权限变更
- 使用 ES `_update_by_query` 批量更新文档所有 chunks 的权限字段
- 同时更新 Neo4j 中 Document 节点的权限属性（如需）

#### 3.3.4 权限缓存

- Redis 缓存用户的部门列表（TTL 5 分钟）
- 部门/用户关系变更时清除相关缓存

### 3.4 GraphRAG 知识图谱

首版 GraphRAG 优先支持文档间关系、主题关系和事项级结构化能力。
条款级 Article 建模、条款级证据回链和条款级问答作为后续 Phase 2 建设内容，不纳入首版交付范围。

#### 3.4.1 实体类型

| 实体类型 | 标签 | 关键属性 | 说明 |
|----------|------|----------|------|
| 公文 | Document | doc_id, title, doc_number, publish_date, summary | 文档节点，与 ES doc_id 一致 |
| 机关/部门 | Organization | name, aliases, level, parent_org | 发文/收文机关 |
| 人员 | Person | name, org, title | 签发人、经办人、涉及人员 |
| 地名/区域 | Region | name, level, parent | 文中涉及地区 |
| 事项/决策 | Subject | name, description, category | 核心内容概括 |
| 文种 | DocCategory | name | 决定/通知/报告等 |
| 主题词 | Keyword | name | 公文主题词 |

#### 3.4.2 关系类型

| 源节点 | 关系 | 目标节点 | 说明 |
|--------|------|----------|------|
| Organization | ISSUED | Document | 机关发文 |
| Document | RECEIVED_BY | Organization | 收文单位 |
| Person | SIGNED | Document | 签发 |
| Person | HANDLED | Document | 经办 |
| Document | INVOLVES | Person | 涉及人员 |
| Document | COVERS_REGION | Region | 涉及区域 |
| Document | ABOUT | Subject | 关于事项 |
| Document | CATEGORIZED_AS | DocCategory | 属于文种 |
| Document | TAGGED | Keyword | 标记主题词 |
| Document | REFERENCES | Document | 引用其他公文（通过文号识别） |
| Subject | RELATED_TO | Subject | 事项关联 |

#### 3.4.3 实体抽取

- LLM 从公文全文抽取实体和关系
- 实体消歧/归一化：同一实体不同表述合并（如"市政府" = "XX市人民政府"）
- 文号引用识别：正则 + LLM 识别文中引用的其他公文文号，建立 REFERENCES 关系

### 3.5 Research 研究模式

#### 3.5.1 处理流程

1. **意图识别**：LLM 分析用户问题，识别研究意图
2. **查询拆分**：将复杂问题拆分为多个子查询
3. **多路召回**：
   - 每个子查询执行 ES 混合检索（含权限过滤）
   - Neo4j 图谱查询：基于实体关系发现关联文档
4. **图谱增强**：在召回文档基础上，通过图谱扩展发现引用链、同主题文档、相关决策
5. **合并去重 + Rerank**：Cross-Encoder 或 LLM 重排序
6. **报告生成**：LLM 生成结构化研究报告，SSE 流式输出，包含引用标注；首版引用粒度为文档级 / Chunk 级，条款级解释后置

#### 3.5.2 输出内容

- 研究报告（Markdown 格式，流式输出）
- 引用文档列表（标题、文号、匹配度；首版到文档级 / Chunk 级）
- 知识图谱子图（当前话题涉及的实体关系，可视化数据）
- 条款级引用定位与条款级可解释问答作为后续 Phase 2 能力，不纳入首版输出承诺

### 3.6 关联文件

- 文档上传时支持绑定关联文档，标记当前文档为"正文"或"附件"角色
- 关联关系通过统一的 `RelatedDocsService` 服务管理，支持双向自动同步
- `PUT /document/{doc_id}/related-docs` 为声明式全量替换接口：调用方发送最终状态，后端自动计算增删改差异
- **双向同步规则**：
  - 新增关联 → 在目标文档追加反向关联
  - 删除关联 → 同步删除目标文档的反向关联
  - 变更关联 → 同步更新目标文档的反向关系类型
  - 反向关系类型映射：正文 ↔ 附件
- **校验规则**：自引用拒绝、重复条目去重、不存在的目标文档过滤
- 响应包含 `warnings`（结构化警告码：SELF_REFERENCE、DUPLICATE、TARGET_NOT_FOUND、TARGET_UPDATE_FAILED、SYNC_EXCEPTION）和 `affected_doc_ids`（实际成功写入的目标文档 ID 列表）
- 文档上传流程中，入库任务在管线执行前快照旧 related_docs，入库完成后（COMPLETED 或 PARTIAL_FAILED）尽力同步关联关系；同步为尽力后处理，不影响主任务状态
- 文档详情页新增"关联文件"标签页，以表格形式展示关联文档列表

---

## 4. 非功能需求

### 4.1 性能

| 指标 | 目标值 |
|------|--------|
| 搜索响应时间 (P95) | < 500ms |
| 支持并发数 | 50 |
| 支持日活 | 30,000 |
| 文档入库速度 | 单文档 < 30s（不含图谱构建） |
| Research 首字输出 | < 3s |

### 4.2 数据规模

| 指标 | 数值 |
|------|------|
| 文档总量 | ~500,000 |
| 每文档 chunk 数 | ~20 |
| 总 chunk 数 | ~10,000,000 |
| Embedding 维度 | 1024 |
| 向量数据量 | ~40GB（原始） |

### 4.3 可靠性

- 文档入库任务失败自动重试（最多 3 次）
- ES、Neo4j 服务异常不影响基本搜索功能降级
- 入库任务幂等，重复推送不产生重复数据

### 4.4 安全性

- API 认证：JWT Token 校验
- 权限过滤：所有检索 API 强制携带权限过滤
- 输入校验：防 SQL/Cypher 注入
- API 限流：防止恶意高频请求

---

## 5. 数据模型

### 5.1 ES 索引：gov_doc_chunks

```json
{
  "mappings": {
    "properties": {
      "chunk_id":        { "type": "keyword" },
      "doc_id":          { "type": "keyword" },
      "chunk_index":     { "type": "integer" },
      "content":         { "type": "text", "analyzer": "ik_max_word_synonym", "search_analyzer": "ik_smart_synonym" },
      "content_vector":  { "type": "dense_vector", "dims": 1024, "index": true, "similarity": "cosine" },
      "title":           { "type": "text", "analyzer": "ik_max_word_synonym", "fields": { "keyword": { "type": "keyword" } } },
      "doc_number":      { "type": "keyword" },
      "issuing_org":     { "type": "keyword" },
      "doc_type":        { "type": "keyword" },
      "subject_words":   { "type": "keyword" },
      "signer":          { "type": "keyword" },
      "publish_date":    { "type": "date", "format": "yyyy-MM-dd" },
      "acl_dept_ids":    { "type": "keyword" },
      "acl_user_ids":    { "type": "keyword" },
      "created_at":      { "type": "date" }
    }
  }
}
```

- 5 分片，0 副本（单机部署）
- IK 分词器 + 政务同义词表
- HNSW 向量索引：m=16, ef_construction=256

### 5.2 ES 索引：gov_doc_meta（文档元数据）

```json
{
  "mappings": {
    "properties": {
      "doc_id":          { "type": "keyword" },
      "title":           { "type": "text", "analyzer": "ik_max_word_synonym", "fields": { "keyword": { "type": "keyword" } } },
      "doc_number":      { "type": "keyword" },
      "issuing_org":     { "type": "keyword" },
      "receiving_orgs":  { "type": "keyword" },
      "doc_type":        { "type": "keyword" },
      "subject_words":   { "type": "keyword" },
      "signer":          { "type": "keyword" },
      "publish_date":    { "type": "date", "format": "yyyy-MM-dd" },
      "summary":         { "type": "text", "analyzer": "ik_smart_synonym" },
      "chunk_count":     { "type": "integer" },
      "acl_dept_ids":    { "type": "keyword" },
      "acl_user_ids":    { "type": "keyword" },
      "status":          { "type": "keyword" },
      "pdf_path":        { "type": "keyword", "index": false },
      "related_docs":    { "type": "nested", "properties": {
                             "doc_id":        { "type": "keyword" },
                             "title":         { "type": "text" },
                             "relation_type": { "type": "keyword" }
                         }},
      "created_at":      { "type": "date" }
    }
  }
}
```

### 5.3 Neo4j 图模型

节点和关系定义见 3.4.1 和 3.4.2 节。

约束与索引：
- Document.doc_id 唯一约束
- Organization.name 唯一约束
- (Person.name, Person.org) 复合唯一约束
- Region.name 唯一约束
- Subject.name 唯一约束
- Document 全文索引：title, summary, doc_number
- Organization 全文索引：name
- Subject 全文索引：name, description

---

## 6. 接口设计

### 6.1 Python AI 服务 API（前缀：/api/ai/v1）

| 方法 | 路径 | 说明 |
|------|------|------|
| POST | /search | 混合检索，返回文档列表 |
| POST | /search/suggest | 搜索建议/自动补全 |
| POST | /research | Research 研究分析（SSE 流式） |
| GET | /document/{doc_id} | 文档详情 |
| GET | /document/{doc_id}/graph | 文档关联图谱子图 |
| POST | /graph/query | 图谱自由查询 |
| GET | /graph/entity/{id} | 实体详情 + 邻居 |
| POST | /ingest/trigger | 手动触发入库 |
| GET | /ingest/status/{task_id} | 入库任务状态 |
| GET | /admin/stats | 系统统计 |
| GET | /health | 健康检查 |

#### 搜索接口详细设计

**请求** `POST /api/ai/v1/search`
```json
{
  "query": "关于机构改革的最新通知",
  "filters": {
    "issuing_org": ["省政府办公厅"],
    "doc_type": ["通知"],
    "date_from": "2024-01-01",
    "date_to": "2024-12-31",
    "subject_words": ["机构改革"]
  },
  "page": 1,
  "page_size": 20
}
```

**响应**
```json
{
  "total": 128,
  "page": 1,
  "page_size": 20,
  "documents": [
    {
      "doc_id": "xxx",
      "title": "关于深化机构改革的通知",
      "doc_number": "X发〔2024〕15号",
      "issuing_org": "省政府办公厅",
      "doc_type": "通知",
      "publish_date": "2024-03-15",
      "score": 0.85,
      "highlights": [
        "根据<em>机构改革</em>总体部署，现将有关事项<em>通知</em>如下..."
      ],
      "matched_chunks": [
        {
          "text": "根据<em>机构改革</em>总体部署...",
          "page_number": 1,
          "page_numbers": [],
          "heading_hierarchy": ["一、总体要求"],
          "element_type": "text",
          "chunk_index": 3
        }
      ]
    }
  ],
  "aggregations": {
    "by_org": [{"key": "省政府办公厅", "count": 45}],
    "by_type": [{"key": "通知", "count": 32}],
    "by_year": [{"key": "2024", "count": 78}]
  }
}
```

#### Research 接口详细设计

**请求** `POST /api/ai/v1/research`
```json
{
  "question": "梳理2024年以来所有关于机构改革的公文，分析改革方向",
  "session_id": "optional-session-id"
}
```

**响应（SSE 流式）**
```
event: chunk
data: {"type": "text", "content": "## 机构改革公文梳理\n\n"}

event: chunk
data: {"type": "text", "content": "根据检索到的相关公文..."}

event: chunk
data: {"type": "reference", "doc_id": "xxx", "title": "...", "doc_number": "..."}

event: chunk
data: {"type": "graph", "nodes": [...], "edges": [...]}

event: done
data: {"references": [...], "graph_data": {...}}
```

### 6.2 Java 业务服务 API（前缀：/api/biz）

| 方法 | 路径 | 说明 |
|------|------|------|
| POST | /webhook/document | OA 文档推送 Webhook |
| POST | /webhook/permission | OA 权限变更推送 |
| GET | /auth/user-info | 用户信息 + 权限 |
| POST | /auth/token | JWT 签发 |
| GET | /file/{doc_id}/pdf | PDF 文件下载 |
| GET | /dept/user-mapping | 部门-用户映射 |

---

## 7. 技术选型

| 层级 | 选型 | 说明 |
|------|------|------|
| 后端 AI 服务 | Python + FastAPI | 异步框架，适合 AI/RAG 场景 |
| 后端业务服务 | Java Spring Boot（已有） | 对接 OA 系统，认证/权限 |
| 前端 | Vue 3 + Ant Design Vue | 国内企业应用主流，TypeScript |
| 向量 + 全文检索 | Elasticsearch 8.x | 统一引擎，原生支持 RRF 混合检索 |
| 中文分词 | IK Analysis Plugin | ik_smart + ik_max_word 双模式 |
| 图数据库 | Neo4j 5.x + APOC | 知识图谱存储与查询 |
| 缓存/队列 | Redis 7.x | 权限缓存 + Celery Broker |
| 异步任务 | Celery | 文档入库、图谱构建等后台任务 |
| PDF 解析 | PyMuPDF (fitz) | 高性能 PDF 文本提取 |
| LLM / Embedding | OpenAI 兼容 API | 现有接口，格式兼容 |
| 图谱可视化 | @antv/g6 | AntV 图可视化引擎 |
| 反向代理 | Nginx | API 路由、静态资源、限流 |
| 容器化 | Docker | ES、Neo4j、Redis 容器化部署 |

---

## 8. 部署架构

### 8.1 服务器资源

单台服务器：510GB 内存，96 核 CPU，1TB SSD

### 8.2 资源分配

| 组件 | 内存 | CPU | 部署方式 |
|------|------|-----|----------|
| Elasticsearch 8.x | ~120GB | 32 核 | Docker |
| Neo4j 5.x | 32GB | 8 核 | Docker |
| Redis 7.x | 4GB | 2 核 | Docker |
| Python FastAPI + Celery | 32GB | 24 核 | 源码直接部署 |
| Java 服务 | 8GB | 8 核 | 源码直接部署 |
| Nginx | - | - | 源码直接部署 |
| OS + Buffer | ~314GB | 22 核 | - |

### 8.3 部署拓扑

```
服务器
├── Docker 容器
│   ├── elasticsearch:8.x (端口 9200)
│   ├── neo4j:5.x (端口 7474/7687)
│   └── redis:7.x (端口 6379)
├── 原生部署
│   ├── Python FastAPI (端口 8900, uvicorn)
│   ├── Celery Worker (8 进程)
│   ├── Java Spring Boot (端口 8080)
│   └── Nginx (端口 80/443)
└── 数据目录
    ├── /data/elasticsearch/  (ES 数据)
    ├── /data/neo4j/          (Neo4j 数据)
    ├── /data/pdf/            (PDF 文件暂存)
    └── /data/logs/           (日志)
```

---

## 9. 前端页面设计

### 9.1 搜索页 (/search)

- 顶部搜索栏 + 高级筛选折叠面板
- 搜索结果列表（ResultCard：标题高亮、文号、机关、日期、知识分类、主题词、匹配片段）
- 快速预览面板展示匹配块详情（matched_chunks），包含页码、段落层级信息
- 右侧聚合统计（按机关/文种/年份分布）
- 分页导航
- 页面底部展示权限提示说明

### 9.2 研究分析页 (/research)

- 左侧：对话面板（消息列表 + 输入框），支持 Markdown 渲染和流式输出
- 右侧可折叠 Sidebar：
  - Tab 1：引用文档列表
  - Tab 2：关联知识图谱子图（迷你图谱）
- 引用标注 [1] [2] 可点击展开文档片段；首版定位到文档或 Chunk 片段，条款级定位后续支持
- 历史会话抽屉

### 9.3 文档详情页 (/doc/:id)

- 文档头：标题、文号、知识分类（knowledge_category）、元数据卡片
- Tab 切换：
  - 原文：PDF.js 嵌入预览
  - 摘要：LLM 生成的结构化摘要
  - 关联文件：关联文档列表（related_docs），以表格形式展示关联文档标题和关系类型
  - 图谱：该文档的知识图谱子图

### 9.4 事项查询与办事指南查询入口说明

- 事项查询页 (/matters) 面向“事项语义层”，用于先定位用户要办理的事项对象，查看该事项的条件、材料、时限、目标群体、承办机构、依据文档和适用地域等核心知识卡信息。
- 办事指南页 (/guides) 面向“指南明细层”，用于核对某份标准办事指南中的办理细节，例如收费、窗口、预约、快递、咨询方式、流程步骤等结构化信息。
- 两者关系是“事项在前，指南在后”：事项代表抽象的办理对象，办事指南代表该事项在具体指南文档中的结构化落地版本。推荐用户路径为“先查事项，再进入关联办事指南核对细节”；当用户的问题本身就是细节核对型问题时，也允许直接进入办事指南查询。

### 9.5 知识图谱探索页 (/graph)

- 图谱画布：G6 可视化，节点按类型区分颜色/形状
- 图谱内搜索框
- 过滤面板：按实体/关系类型筛选
- 选中节点详情面板
- 交互：点击展开、拖拽、缩放

### 9.6 管理后台 (/admin)

- Dashboard：文档总数、今日入库、图谱节点数、系统状态
- 入库日志：任务列表（状态/错误/重试）
- 系统配置：同义词管理

---

## 10. 约束与假设

### 约束

1. 单台服务器部署，不考虑多机分布式
2. 文档统一 PDF 格式
3. LLM / Embedding 通过 OpenAI 兼容 API 调用（非本地模型）
4. 用户认证复用 OA 已有体系（JWT）
5. 公文权限只追加不撤回

### 假设

1. OA 系统能够实现 Webhook 推送功能
2. 现有 LLM / Embedding 接口性能足够支撑入库和查询负载
3. PDF 文档质量足以支持文本提取（非扫描件或有 OCR 层）
4. 部门-用户映射关系可从 OA 系统获取
