# zm-rag — 政务公文 GraphRAG 系统

面向政务公文的 RAG 检索与研究分析系统，支持混合检索（向量 + BM25）、权限过滤、GraphRAG 知识图谱。

---

## 目录

- [系统架构](#系统架构)
- [技术栈](#技术栈)
- [快速启动](#快速启动)
- [配置说明](#配置说明)
- [功能列表](#功能列表)
- [API 文档](#api-文档)
- [项目结构](#项目结构)
- [开发进度](#开发进度)
- [已知问题 & 注意事项](#已知问题--注意事项)

---

## 系统架构

```
┌──────────────────────────────────────────────────────────────┐
│           前端 (Vue 3 + Ant Design Vue)  :3000               │
│  搜索 | 研究 | 图谱 | 管理后台 | 转换记录 | Mock 测试台       │
└────────────────────────┬─────────────────────────────────────┘
                         │  HTTP / SSE
┌────────────────────────┴─────────────────────────────────────┐
│              Python FastAPI (AI 服务)  :8900                  │
│  多格式入库 | 混合检索 | 权限过滤 | 图谱构建 | 研究报告生成    │
└────────┬────────────────┬────────────────────┬───────────────┘
         │                │ HTTP               │ Celery Worker
┌────────┴──────────┐ ┌───┴────────────┐ ┌────┴──────────────┐
│ OpenSearch 2.19.4  │ │ Java 转换服务   │ │   Redis 7.x       │
│ (kNN+BM25+RRF)    │ │ (Spire.Office) │ │ (缓存/任务队列)   │
└───────────────────┘ │ :8901          │ └───────────────────┘
                      └───┬────────────┘
┌───────────────┐        │ JPA
│  Neo4j 5.x    │  ┌─────┴───────────┐
│ (知识图谱)    │  │ MySQL zm_tag    │
└───────────────┘  │ (转换日志)      │
                   └─────────────────┘
```

**数据流**：
- **写入**：文件上传(多格式) → 类型检测 → MD5 去重 → [转换] → Docling 解析 → 结构化分块 → Embedding → OpenSearch + Neo4j
- **查询**：前端 → FastAPI（权限解析 → OpenSearch RRF 混合检索 → Rerank → LLM 生成）

---

## 技术栈

| 层次 | 技术 | 版本 |
|------|------|------|
| 前端 | Vue 3 + Vite + Ant Design Vue | Vue ^3.5 |
| 后端 | Python FastAPI + Uvicorn | Python 3.11+ |
| 文档转换服务 | Spring Boot + Spire.Office (Java 8) | 2.7.18 |
| 异步任务 | Celery (solo 模式 for Windows) | ^5.6 |
| 搜索引擎 | OpenSearch (kNN + BM25 + RRF) | 2.19.4 |
| 监控面板 | OpenSearch Dashboards | 2.19.4 |
| 图数据库 | Neo4j 5 Community + APOC | 5.x |
| 缓存/队列 | Redis 7 | ^7 |
| 文档解析 | Docling (IBM) + RapidOCR | ^2.78 |
| 文档转换 | Spire.Office (e-iceblue, 本地 jar) | 10.6 |
| Embedding | DashScope text-embedding-v4 (1024维) | OpenAI 兼容 API |
| LLM | DashScope Qwen3.5-flash | OpenAI 兼容 API |
| 图谱可视化 | @antv/g6 | ^5.0 |
| 转换日志 | MySQL (zm_tag 库) | 8.x |

---

## 快速启动

### 前置要求

- Python 3.11
- Node.js 18+
- Docker & Docker Compose
- DashScope API Key（用于 Embedding 和 LLM）

### 1. 启动 Docker 基础服务

```bash
cd docker
docker compose up -d
```

启动后：
- OpenSearch: `http://localhost:9200`
- OpenSearch Dashboards: `http://localhost:5601`
- Neo4j Browser: `http://localhost:8474`（账号 neo4j / zm_rag_2024）
- Redis: `localhost:16379`
- Java 转换服务: `http://localhost:8901`

### 2. 初始化索引 & Neo4j Schema

```bash
cd backend

# 初始化 OpenSearch 索引（创建 gov_doc_chunks 和 gov_doc_meta）
py -3.11 scripts/init_es_index.py

# 初始化 Neo4j 约束和索引
py -3.11 scripts/init_neo4j_schema.py

# (可选) 清空旧数据并重建索引（升级到多格式支持时需要）
py -3.11 scripts/reset_and_migrate.py
```

### 3. 配置环境变量

```bash
cp backend/.env.example backend/.env
# 编辑 backend/.env，填写 DashScope API Key 等
```

关键配置项（见 [配置说明](#配置说明)）。

### 4. 安装 Python 依赖

```bash
cd backend
pip install -r requirements.txt
```

> ⚠️ **注意**：Docling 需要 Python ≥ 3.10，推荐 3.11。安装 Docling 会自动安装 PyTorch、RapidOCR 等依赖，首次安装时间较长。

### 5. 启动后端服务

**终端 1 — FastAPI**：
```bash
cd backend
py -3.11 -m uvicorn app.main:app --host 0.0.0.0 --port 8900 --reload
```

**终端 2 — Celery Worker（Windows）**：
```bash
cd backend
py -3.11 -m celery -A app.tasks.celery_app:celery_app worker --loglevel=info -P solo -c 1 -n worker1@%h
```

> ⚠️ Windows 必须使用 `-P solo`，不支持 prefork/gevent。

### 6. 启动前端

```bash
cd frontend
npm install
npm run dev
# 前端地址：http://localhost:5173（或 3001）
```

### 7. 访问 Mock OA 测试台

打开 `http://localhost:5173/mock`：
1. 使用快速登录（admin/admin123）
2. 前往「② 上传文档」标签上传 PDF
3. 前往「③ 任务监控」查看入库进度

---

## 配置说明

`backend/.env` 关键配置项：

```env
# DashScope LLM（Qwen）
LLM_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
LLM_API_KEY=sk-xxxxxxxx
LLM_MODEL=qwen3.5-flash
LLM_ENABLE_THINKING=false        # 关闭思考链可加速响应

# DashScope Embedding
EMBEDDING_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
EMBEDDING_API_KEY=sk-xxxxxxxx
EMBEDDING_MODEL=text-embedding-v4
EMBEDDING_DIMENSIONS=1024
EMBEDDING_BATCH_SIZE=6           # DashScope 单批最多10条，保守设6

# 文件存储路径
FILE_STORAGE_PATH=D:/data/files    # Windows 路径，用正斜杠

# Neo4j
NEO4J_URI=bolt://localhost:8687
NEO4J_PASSWORD=zm_rag_2024

# Docling 文档解析
DOCLING_OCR_ENABLED=true           # 启用 RapidOCR (扫描件 PDF)
DOCLING_MAX_TOKENS=512             # HybridChunker token 预算

# Java 文档转换服务
CONVERTER_BASE_URL=http://localhost:8901

# 知识图谱构建（可设 false 跳过 LLM 实体抽取）
GRAPH_BUILD_ENABLED=true
```

> ⚠️ 不要在生产环境中使用默认 `JWT_SECRET`，需要修改为强随机字符串。

---

## 功能列表

### ✅ 已实现

#### 多格式文档入库管线
- [x] **18 种文档格式**：PDF、DOC/DOCX、XLS/XLSX、PPT/PPTX、WPS、ET、OFD、PNG/JPG/JPEG/TIFF/BMP、TXT、MD
- [x] **Docling 统一解析**：IBM Docling 结构化解析，保留页码、标题层级、元素类型
- [x] **RapidOCR 扫描件支持**：自动 OCR 扫描版 PDF
- [x] **Java 转换服务**：Spire.Office 格式转换 (DOC→DOCX、XLS→XLSX、PPT→PPTX、WPS→DOCX、ET→XLSX、OFD→PDF)
- [x] **转换日志 MySQL 持久化**：记录转换耗时、文件大小、状态，提供查询接口
- [x] **MD5 内容去重**：重复文件跳过处理，共享分块数据
- [x] **Magic bytes 格式检测**：不依赖文件扩展名，精确识别真实格式
- [x] **HybridChunker 结构化分块**：保留 page_number、heading_hierarchy、element_type
- [x] **Embedding 生成**：DashScope text-embedding-v4，批量并发，自动重试
- [x] **⚡ AI 自动元数据提取**：LLM 从文档内容自动识别标题、文号、发文机关等
- [x] **Celery 异步任务**：非阻塞入库，支持进度轮询，自动重试

#### 知识图谱（Phase 4 部分）
- [x] **实体抽取**：LLM 从公文全文抽取 Document/Organization/Person/Region/Subject 等实体
- [x] **关系构建**：ISSUED/INVOLVES/COVERS_REGION/SIGNED 等关系写入 Neo4j
- [x] **幂等 MERGE**：同名实体自动合并去重

#### 权限模型（Phase 3 部分）
- [x] **ACL 字段**：每个 chunk 携带 `acl_dept_ids` + `acl_user_ids`
- [x] **权限过滤**：ES 查询时注入 `bool.should` 过滤（并集语义）
- [x] **JWT 认证**：基于 HS256 JWT，包含 `sub`（user_id）和 `dept_ids`

#### Research 深度研究工作台
- [x] **任务定义 + 计划确认**：Research 从单轮长回答升级为任务驱动工作台，先生成研究计划，再执行深度研究
- [x] **结构化 SSE 协议**：支持 `plan`、`progress`、`summary`、`finding`、`conflict`、`section`、`open_question`、`follow_up` 等事件
- [x] **章节化报告输出**：执行摘要、关键发现、章节报告、一页式摘要、建议后续方向可独立沉淀
- [x] **章节局部重跑**：支持仅针对某一章节重新取证并重写，同时保留“转成新任务”的兜底路径
- [x] **证据工作区**：Research 引用按显式导入资料 / 混合检索命中 / 图谱补充材料分组展示
- [x] **跨页面导入链路**：Search、QA、文档详情、事项详情统一通过共享导入对话框进入 Research 会话
- [x] **导出能力**：支持 Markdown、完整报告 Word 兼容 `.doc`、正式汇报版 `.doc` 导出

#### Mock OA 测试台（前端）
- [x] **账号登录**：预置测试账号（admin/zhang_san/li_si/wang_wu）
- [x] **快速登录**：一键切换测试用户
- [x] **令牌生成器**：自定义 user_id、dept_ids 生成 JWT
- [x] **文档上传**：拖拽上传 PDF + 手动填写元数据
- [x] **⚡ AI 自动元数据**：一键开启，上传后自动识别文档元数据
- [x] **任务监控**：实时轮询 Celery 任务进度，展示 AI 提取结果
- [x] **已入库文档列表**：ES 文档浏览 + 删除
- [x] **搜索验证**：权限感知搜索测试

### 🚧 近期待补

- [ ] **Research 持续跟踪能力**：研究订阅、定期刷新、横向对比、协作批注仍未进入主线实现
- [ ] **条款级证据回链**：条款级别的证据定位、合规审查与更细粒度图谱分析仍在后续规划中
- [ ] **自动化前端回归**：当前前端可靠验证仍以 `npx vite build` 和人工烟测为主，缺少浏览器级 E2E
- [ ] **压力测试脚本**：缓存、限流已落地，但系统化压测脚本和性能基线文档仍待补齐

---

## API 文档

FastAPI 自动生成的交互文档：
- Swagger UI：`http://localhost:8900/docs`
- ReDoc：`http://localhost:8900/redoc`

### 核心端点

| 方法 | 路径 | 说明 |
|------|------|------|
| `POST` | `/api/v1/ingest/webhook/document` | 接收 PDF 文档，触发入库任务 |
| `POST` | `/api/v1/ingest/webhook/permission` | 更新文档权限（只追加） |
| `GET` | `/api/v1/ingest/status/{task_id}` | 查询入库任务状态（需 JWT） |
| `POST` | `/api/v1/search` | 混合检索（需 JWT，含权限过滤） |
| `GET` | `/api/v1/document/{doc_id}` | 文档详情 |
| `POST` | `/api/v1/research` | 兼容模式研究分析（SSE 流式，需 JWT） |
| `POST` | `/api/v1/research/plan` | 生成研究计划 |
| `POST` | `/api/v1/research/run` | 执行深度研究（SSE 流式） |
| `POST` | `/api/v1/research/sections/rerun` | 章节局部重跑（SSE 流式） |
| `GET` | `/api/v1/graph/...` | 图谱查询接口 |

### Mock 端点（仅开发用）

| 方法 | 路径 | 说明 |
|------|------|------|
| `POST` | `/api/v1/mock/login` | 使用预置账号登录 |
| `GET` | `/api/v1/mock/users` | 获取测试账号列表 |
| `POST` | `/api/v1/mock/token` | 生成自定义 JWT |
| `GET` | `/api/v1/mock/docs` | 列出 ES 中所有文档 |
| `DELETE` | `/api/v1/mock/doc/{doc_id}` | 删除测试文档 |
| `GET` | `/api/v1/mock/task/{task_id}` | 查询任务状态（免认证） |
| `GET` | `/api/v1/mock/stats` | 系统状态（ES/Redis/Neo4j） |

---

## 项目结构

```
zm-rag/
├── docker/                          # Docker 基础服务
│   ├── docker-compose.yml           # OpenSearch + Neo4j + Redis + Celery + 转换服务
│   ├── opensearch/                  # OpenSearch Dockerfile + IK 分词
│   └── backend/                     # Celery worker Docker 配置
│
├── converter/                       # Java 文档转换服务 (Spring Boot 2.7 + Spire.Office)
│   ├── pom.xml                      # Maven 配置
│   ├── lib/                         # Spire.Office 本地 jar 文件
│   ├── Dockerfile                   # 多阶段构建
│   └── src/main/java/com/zmrag/converter/
│       ├── controller/              # REST API (/api/convert)
│       ├── service/                 # 格式转换逻辑 + DB 日志
│       ├── entity/                  # JPA 实体 (ConvertLog)
│       ├── repository/              # Spring Data JPA
│       ├── model/                   # DTO (ConvertResult, PageResult)
│       └── util/                    # Apache Tika 格式检测
│
├── backend/                         # Python AI 服务 (FastAPI + Celery)
│   ├── requirements.txt             # Python 依赖
│   ├── app/
│   │   ├── main.py                  # FastAPI 应用入口
│   │   ├── config.py                # 配置（含多格式 + Docling + 转换服务设置）
│   │   ├── api/v1/                  # API 路由
│   │   │   ├── ingest.py            # 多格式文档入库（Webhook）
│   │   │   ├── document.py          # 文档详情 + 多格式下载
│   │   │   ├── search.py            # 混合检索 API
│   │   │   ├── research.py          # 研究计划 / 深度研究执行 / 章节局部重跑
│   │   │   ├── graph.py             # 图谱查询
│   │   │   ├── admin.py             # 管理接口
│   │   │   └── mock.py              # Mock OA 测试接口
│   │   ├── core/                    # 核心业务逻辑
│   │   │   ├── docling_processor.py     # Docling 多格式解析 + RapidOCR
│   │   │   ├── chunker.py               # 结构化分块 (page_number, heading_hierarchy)
│   │   │   ├── ingest_pipeline.py       # 入库管线 (多格式 + 转换 + Docling)
│   │   │   ├── embedding.py             # Embedding 批量生成
│   │   │   ├── metadata_extractor.py    # LLM 自动元数据提取
│   │   │   ├── summary_generator.py     # AI 摘要生成
│   │   │   ├── search_engine.py         # OpenSearch RRF 混合检索
│   │   │   ├── graph_builder.py         # Neo4j 图谱构建
│   │   │   └── permission.py            # 权限解析
│   │   ├── infrastructure/          # 外部服务客户端
│   │   │   ├── es_client.py         # OpenSearch 客户端
│   │   │   ├── neo4j_client.py      # Neo4j 客户端
│   │   │   ├── redis_client.py      # Redis 客户端
│   │   │   ├── llm_client.py        # LLM 客户端
│   │   │   └── embedding_client.py  # Embedding 客户端
│   │   ├── utils/
│   │   │   ├── file_type.py         # Magic bytes 文件类型检测
│   │   │   └── logger.py            # 结构化日志
│   │   └── tasks/                   # Celery 任务
│   │       ├── celery_app.py
│   │       └── ingest_task.py       # 文档入库任务
│   ├── scripts/
│   │   ├── init_es_index.py         # 初始化 OpenSearch 索引
│   │   ├── init_neo4j_schema.py     # 初始化 Neo4j 约束
│   │   ├── reset_and_migrate.py     # 清空旧数据 + 重建索引
│   │   └── bulk_ingest.py           # 批量入库
│   └── tests/                       # 自动化测试 (303 collected)
│
├── frontend/                        # Vue 3 前端
│   └── src/
│       ├── views/
│       │   ├── admin/
│       │   │   ├── Dashboard.vue        # 管理后台
│       │   │   ├── GraphManager.vue     # 图谱管理
│       │   │   └── ConvertLogs.vue      # 转换记录查看
│       │   ├── SearchView.vue       # 搜索页
│       │   ├── ResearchView.vue     # 研究分析页
│       │   ├── DocDetailView.vue    # 文档详情页
│       │   ├── GraphExplorer.vue    # 知识图谱可视化
│       │   └── MockOA.vue           # Mock OA 测试台
│       ├── api/                     # Axios 封装
│       ├── stores/                  # Pinia 状态管理
│       └── composables/             # 组合式函数
│
└── docs/
    ├── CHANGES.md                   # 变更记录（当前文件）
    ├── PRD.md                       # 产品需求文档
    ├── testing.md                   # 测试文档
    └── *.md                         # 其他设计文档
```

---

## 开发进度

详见 `docs/PROGRESS.md`

| Phase | 状态 | 说明 |
|-------|------|------|
| Phase 1: 基础设施 | ✅ 完成 | Docker / ES 8.19.12 Platinum + Kibana 8.19.12 / Neo4j Schema / FastAPI骨架 / Vue骨架 |
| Phase 2: 文档入库 | ✅ 完成 | PDF解析 / 分块 / Embedding / ES写入 / Celery任务 / AI元数据提取 |
| Phase 3: 混合检索 | ✅ 完成 | OpenSearch RRF 混合检索、权限过滤、suggest 与详情链路已落地 |
| Phase 4: 知识图谱 | ✅ 完成 | 实体抽取、图谱构建、批量构建脚本与查询接口已落地 |
| Phase 5: Research | ✅ 完成 | 计划驱动深度研究、结构化 SSE、章节重跑、导出工作台已落地 |
| Phase 6: 图谱可视化 | ✅ 完成 | GraphExplorer、文档详情图谱和管理侧图谱能力已接入真实接口 |
| Phase 7: 性能优化 | ✅ 核心完成 | 查询缓存、Embedding 缓存、限流与生产配置已落地，压测脚本待补 |

---

## 已知问题 & 注意事项

### ⚠️ 依赖版本陷阱

1. **Docling 需要 Python ≥ 3.10**，推荐 3.11。首次加载会下载模型文件。

2. **DashScope text-embedding-v4 每批最多 10 条**，`EMBEDDING_BATCH_SIZE` 默认设 6（保守值）。

3. **Neo4j Community Edition 不支持 `NODE KEY` 约束**，代码中已使用单字段 `UNIQUENESS` 约束。

4. **Spire.Office 本地 jar**：不从在线 Maven 仓库下载，使用 `converter/lib/` 中的本地 jar 文件。

### ⚠️ Windows 开发注意

1. **Celery 必须使用 `-P solo`**，Windows 不支持 fork：
   ```bash
   py -3.11 -m celery -A app.tasks.celery_app:celery_app worker -P solo -c 1
   ```

2. **FILE_STORAGE_PATH** 使用正斜杠：`D:/data/files`

3. **服务重启**：每次开发重启时需手动重启 FastAPI 和 Celery Worker（没有自动守护进程配置）。

4. **端口冲突**：3000 被 Windows 保留，前端用 3001 或 5173。

### ⚠️ publish_date 空值

ES 的 `date` 类型字段不接受空字符串 `""`，代码已统一将空 `publish_date` 转为 `null`（ES 接受）。如果直接调用 ES API 写入时，注意此问题。

### ⚠️ 测试账号

仅用于开发测试，勿在生产使用：

| 用户名 | 密码 | 角色 | 可访问部门 |
|--------|------|------|------------|
| admin | admin123 | 管理员 | dept_001 ~ dept_005 |
| zhang_san | user123 | 员工 | dept_002 |
| li_si | user123 | 员工 | dept_003 |
| wang_wu | manager123 | 经理 | dept_001, dept_003 |

---

## License

内部项目，暂不开源。
