# graph-RAG
**Repository Path**: BUOOtchis/graph-rag
## Basic Information
- **Project Name**: graph-RAG
- **Description**: 2026 graph-RAG
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-09-03
- **Last Updated**: 2026-09-17
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 《山海经》全十八卷 GraphRAG 知识问答系统
> 课程:知识工程综合实践 | 主题:基于知识图谱的 RAG 系统构建
> 语料:《山海经》**全十八卷**(山经五卷 + 海经十三卷),原文 38169 字 / 843 个经文切片
> 图谱:**1634 节点 / 1602 边**(7 类实体、10 类关系,山经规则抽取 + 海经 LLM 语义抽取 + 人工校订补录三通道)
> 界面:可切换**四种检索器**的 Web 问答页(答案 + 引用原文 + 相关实体关系 + 证据子图可视化)
> 环境:Python 3.8+(本项目在 3.13 下验证),**核心零第三方依赖**;LLM 可选(不配置则离线模板模式)
> 最近更新:2026-09-14
---
## 一、项目立意与设计
### 1.1 要解决的问题
通用大模型并不"认识"《山海经》:它不知道"祝余"食之不饥、不知道"青丘之山"生有"灌灌"(且常与"青丘之国·九尾狐"混淆)、也无法把"鹊山系"的山按方位与里程串成一条空间链、更理不清"帝俊—后稷—台蠒—叔均"的跨卷世系。直接问会一本正经地编造(幻觉);只做文本切片检索的普通 RAG,又答不好需要**多跳串联**的问题(谱系、地望、事件、方位链)。
本项目要解决三件事:
1. **答案有出处、可复核**:答案不是模型记忆,而是检索自经文原文与受控图谱,每条证据都带出处(`山海经·卷名·节ID`);
2. **多跳推理成为可能**:生育/世系、化为、巡至方位链、事件参与需要跨段落串联——这是纯向量检索做不到、图谱天然擅长的;
3. **一条可复现的"文本 → 图谱 → 检索 → 生成"数据管理方法**:每一步(清洗、Schema、抽取、审计、建图、检索、生成)都留产物与报告,可解释、可复现。
### 1.2 目标用户与预期结果
| 目标用户 | 使用场景 | 预期结果 |
|---|---|---|
| 古籍/民俗爱好者 | 问"某山有什么灵物""某神什么来历" | 得到**带出处**的准确答案,而非幻觉 |
| 知识工程学习者 | 查看零散经文如何被组织为可推理结构 | 可切换四种检索器对比效果、可查看子图与证据 |
| 课程教师 | 验收全流程成果 | 数据/图谱/检索/生成四层产物与审计报告齐备 |
### 1.3 总体架构:数据 / 知识图谱 / 检索 / 生成的关系
```mermaid
flowchart LR
subgraph L1["① 数据准备"]
RAW["data/raw/shanhaijing_full.txt
十八卷原文 878 行 / 38169 字"]
PREP["data_prep.py 清洗 + 按卷切分
→ sections_full.jsonl(843 节)"]
end
subgraph L2["② 知识图谱构建(三通道抽取 + 审计)"]
SCHEMA["schema.py 本体 Schema
7 类实体 / 10 类关系"]
R1["auto_extract.py 规则抽取
山经五卷(句式模式化)
835 节点 / 1073 边"]
R2["llm_extract.py LLM 语义抽取
海经叙述体十三卷(362/362 节)
840 节点 / 522 边"]
R3["curated_myth/ 人工校订补录
长尾神话事件与漏抽谱系
5 节点 / 10 边(逐条附经文证据)"]
AUD["抽取审计:原文片段校验
extract_report*.txt"]
KG["graph_store.py 图谱存储
shanhaijing_graph_all.json
1634 节点 / 1602 边"]
end
subgraph L3["③ 检索层(四种可切换,retrievers.py)"]
V["VectorRetriever
TF-IDF 向量"]
VC["VectorCypherRetriever
向量 + 实体链接 + 一跳图查询"]
H["HybridRetriever
向量 + BM25 → RRF 融合"]
HG["HybridGraphRetriever
混合 + 2 跳图遍历"]
end
subgraph L4["④ 生成层(知识注入 + 防幻觉)"]
ANS["answer.py:仅依据检索证据生成
证据不足 → 如实说明无法得出"]
end
UI["web/ 前端界面:答案 + 引用 + 实体关系 + 子图"]
RAW --> PREP --> L3
SCHEMA --> R1 & R2 --> AUD --> KG --> L3
L3 --> ANS --> UI
```
数据流一句话:**同一批经文,一路切成"语料单元"(843 节,供文本检索),一路按本体 Schema 抽成"图三元组"(1629 节点/1590 边,供图检索);问答时用户选择检索器,检索器产出"原文切片 + 子图证据",生成层把证据注入 LLM 上下文作答,前端同时展示答案与全部证据。**
### 1.4 技术选型与合理性
| 环节 | 选择 | 为什么合理 / 局限与升级 |
|---|---|---|
| 语言/环境 | Python 3.8+,**核心纯标准库** | 课堂环境不可控,零依赖保证"任何机器能跑";`server.py` 用 `http.server`、LLM 用 `urllib` |
| 图存储 | 自研 `Graph`(邻接表 + JSON 持久化) | 规模中等(1629 节点)时全透明、便于讲解;生产级可平滑换 Neo4j(Schema/查询接口已隔离) |
| 向量检索 | 字符 n-gram TF-IDF + 余弦 | 中文零分词依赖、离线可跑、可解释(对比实验基线);升级路径为 bge-m3 等语义向量 |
| 词面检索 | BM25 | 与向量互补:人名/山名等精确命中有优势 |
| 抽取 | 山经用**规则**,海经用**LLM + Schema 约束** | 山经句式高度模式化,规则可靠可复现;海经是叙述体(讲国、讲神、讲世系),无句式可写,改用 LLM 并按"实体名必须是原文连续片段"约束 + 抽查审计 |
| 生成 | OpenAI 兼容 LLM(可选)+ 离线模板降级 | 检索与生成解耦:无 Key 也能完整演示;提示词强制"只用证据、不足则如实说明" |
| 前端 | 原生 HTML/CSS/JS(无框架) | 零构建、零依赖,`python server.py` 即可;子图为自研力导向布局 + SVG |
---
## 二、数据准备
### 2.1 数据来源
- **语料**:《山海经》公版通行文本(郭璞注本一系)全十八卷,内置 `data/raw/shanhaijing_full.txt`(878 行 / 38169 字);
- 结构上分两类文本,这也是图谱采用**双通道抽取**的原因:
| 文本类型 | 卷目 | 句法特征 | 抽取方式 |
|---|---|---|---|
| 山经体 | 南/西/北/东/中山经(五卷) | 高度模式化:`又东三百里,曰X之山`、`有兽焉,其名曰Y`、`Z水出焉,注于W` | `auto_extract.py` 正则规则 |
| 叙述体 | 海外/海内/大荒/海内经(十三卷) | 叙述体:讲异国、神人、世系、事迹,无固定句式 | `llm_extract.py` LLM 语义抽取(Schema 约束) |
### 2.2 代表性原始样例
```
南山经之首曰䧿山。其首曰招摇之山,临于西海之上,多桂,多金玉。有草焉,其状如韭而青华,其名曰祝余,食之不饥……
(山经体:方位 + 里程 + 山名 + 物产,句式规整)
有青丘之国,有狐,九尾。
帝俊生晏龙,晏龙是为琴瑟。
羿与凿齿战于寿华之野,羿射杀之。
(叙述体:异国 + 谱系 + 事件,无固定句式)
```
### 2.3 数据规模(实际统计,见 `results/data_report_full.txt`)
| 指标 | 数值 |
|---|---|
| 原始/清洗后文本 | 878 行 / **38169 字**(全文底本无噪声行;节选版另演示 969→845 字清洗过程) |
| 切分节数 | **843 节**(山经 481 节:南 43/西 82/北 92/东 51/中 213;海经 362 节:海外 86/海内 90/大荒 147/海内经 39) |
| 山经五卷抽取 | **835 节点 / 1073 边**(山 420、水 190、灵物 189、山系 26、神 10;`results/extract_report.txt`) |
| 海经十三卷抽取 | **840 节点 / 522 边**(362/362 节成功;异国 193、山 202、神人 179、灵物 143、水 91、事件 32;`results/extract_report_llm.txt`) |
| 人工校订补录 | **5 节点 / 10 边**(长尾神话事件 3 + 漏抽谱系与立国 2;`data/curated_myth/`,逐条附经文证据) |
| 合并后全图 | **1634 节点 / 1602 边**(同名同类型自动合并;`data/graph/build_report_all.txt`) |
| 校验丢弃 | LLM 抽取 133 条非原文片段/非法类型的三元组被审计剔除,全文留痕于缓存 |
### 2.4 处理前后对比(可演示的证据链)
| 步骤 | 输入 → 输出 | 产物 |
|---|---|---|
| 清洗 | raw 全文 → 去批注行/全角空格/折叠空行 | `processed/cleaned_all.txt` |
| 切分 | 整篇 → 按卷/节结构化 | `processed/sections_full.jsonl`(843 条,含 id/卷/节题/正文) |
| 实体关系抽取 | 经文 → 受控表(山经规则 / 海经 LLM) | `curated_full/*.csv`、`curated_llm/*.csv` |
| 人工校订 | 长尾神话事件与漏抽谱系 → 补录表(逐条附经文证据) | `curated_myth/*.csv` |
| 抽取审计 | 逐卷对账 + 原文片段校验 + 抽检 | `results/extract_report.txt`、`extract_report_llm.txt` |
| 建图 | 三张受控表 → 图存储 + 校验报告 | `data/graph/shanhaijing_graph_all.json`、`build_report_all.txt` |
---
## 三、知识图谱构建
### 3.1 本体 Schema(`shanhaijing/schema.py`,单一事实来源)
- **7 类实体**:山系、山、水、灵物、神人、异国、事件
- **10 类关系**:
| 关系 | 语义 | 示例(均可在图谱中复核) |
|---|---|---|
| `part_of` 隶属于 | 山 → 山系 | 招摇之山 → 南山经·鹊山系 |
| `route_to` 巡至 | 按方位/里程相连的相邻山(带 direction/distance) | 招摇之山 —东 三百里→ 堂庭之山 |
| `hosts` 山上生有 | 山 → 灵物 | 青丘之山 → 灌灌 |
| `dwells` 居于 | 神人/灵物 居于 某地 | 叔均 → 苍梧之野 |
| `procreates` 生育 | 世系(跨卷谱系链) | 帝俊 → 后稷 → 台蠒 → 叔均 |
| `governs` 君临/立国 | 先祖 → 其后裔之国 | 互人 → 互人之国 |
| `involved_in` 参与 | 神人/灵物 → 事件 | 叔均 → 始作耕 |
| `transforms_to` 化为 | 化身/物化互见 | 夸父 → 邓林(弃其杖,化为邓林) |
| `vanquishes` 战胜/击杀 | 神话战争 | 应龙 → 蚩尤;成汤 → 夏桀 |
| `located` 位于 | 地望(带 direction) | 羽民国 → 比翼鸟(direction=东南) |
### 3.2 双通道抽取与可审计性
- **规则通道**(`auto_extract.py`,山经五卷):正则命中"又X百里·曰Y之山""有兽焉·其名曰Z""W水出焉·注于V",逐卷对账(经文自带的"凡 N 山"总数 vs 实抽山站):南 39/40、西 77/77、北 88/87、东 46/46、中 197/197,差异经确认为底本传抄异文。
- **LLM 通道**(`llm_extract.py`,海经十三卷):把 Schema 写进提示词,要求输出 JSON 三元组,并施加**三条硬约束**:
1. 实体名必须是该节经文**原文的连续片段**(2~10 字,程序用 `in text` 复核,不通过即丢弃);
2. 每条边必须附 `evidence`(支持该三元组的原文短语);
3. 类型必须在 Schema 白名单内;端点在 nodes 中自动补建。
逐节结果缓存至 `results/llm_extract_cache.jsonl`(断点续跑、可复查输入输出),审计报告汇总逐卷统计与抽检样本。
> 一句话答辩口径:**Schema 定模板 → 山经用规则、海经用 LLM + 原文校验 → 再用经文自带总数与抽检做审计对账**,把 1629 节点/1590 边的可信度量化到逐卷差数与丢弃条目上。
---
## 四、四种检索器与知识注入生成
### 4.1 四种检索器(`shanhaijing/retrievers.py`,界面可一键切换)
| 检索器 | 机制 | 适用 |
|---|---|---|
| **向量检索** `vector` | TF-IDF 字符 n-gram 向量 + 余弦相似度,在 843 个切片上召回 Top-K | 词面相近的原文查找(纯文本 RAG 基线) |
| **VectorCypher 检索** `vector_cypher` | 向量召回切片 → 切片与问题中做**实体链接** → 对命中实体执行一跳图查询(`MATCH (e)-[r]-(m)`) | 需要"实体周边关系"补充上下文 |
| **混合检索** `hybrid` | 向量 + BM25 双路召回,**RRF 倒数排名融合** | 兼顾语义相关与关键词精确命中 |
| **混合 + 图遍历** `hybrid_graph`(默认) | 混合召回切片并链接实体后,从命中实体做 **2 跳 BFS 图遍历**扩展子图 | 谱系/地望/事件等多跳问题 |
四种检索器统一返回:`citations(引用切片 + 得分)`、`nodes/edges(子图证据)`、`entities(链接命中的图内实体)`、`strategy(策略说明,可解释)`。
### 4.2 知识注入与防幻觉(`answer.py`)
- 把检索证据渲染为结构化上下文:引文带编号 `[1] 《山海经·大荒西经》大荒西经-08:…`,图关系带标签与出处;
- 系统提示词强制三条:**只用证据作答**、**引用标注编号**、**证据不足时必须如实说明"根据现有经文证据,无法得出……"**;
- 离线/异常降级:未配置 LLM 或调用失败时,用模板直接摘录证据作答;**证据为空时任何模式都拒答**,不编造;
- 实测效果(`帝俊的后代有谁?`):系统能从 `帝俊生晏龙/后稷/黑齿`、`帝俊有子八人` 归纳子女,并指出"叔均属后稷之弟台蠒所生,非帝俊直接所生";问 `青丘之山` 时明确区分"青丘之山(灌灌、赤鱬)"与"青丘之国(九尾狐)",不混淆。
---
## 五、快速开始
### 5.1 三步跑起来
```bash
# 1) 数据准备 + 建图(若仓库内已含 processed/ 与 graph/ 产物可跳过)
python -m shanhaijing.data_prep full # 清洗 + 切分全十八卷 → 843 节
python build_graph.py --all # 合并三通道受控表 → 全十八卷图谱
# (如需从零重建海经十三卷抽取,需先配置 LLM:见 5.3,运行 python llm_extract.py)
# 2) 启动 Web 界面(推荐,含四种检索器切换与子图可视化)
python server.py # 打开 http://127.0.0.1:8000
# 3) 或命令行问答
python main.py # 默认hybrid_graph,--retriever 可切换
```
> **零第三方依赖**:无需 `pip install` 任何包即可完成上述全部操作。
> 配置 LLM 后,答案由大模型基于检索证据生成;不配置则自动进入"离线模板模式",功能完整。
### 5.2 Web 界面功能
- **检索器切换**:左侧四张卡片一键切换,可对比同一问题的四种检索路径;
- **答案区**:展示答案、生成方式(LLM 生成/离线模板)、检索与生成耗时、检索策略;
- **证据区**:引用原文(编号 + 卷名 + 节 ID + 相关度得分,答案中的 `[n]` 角标可点击跳转高亮)、相关实体(按类型着色)、图谱关系列表(含关系标签、方位属性与出处);
- **证据子图**:力导向布局 SVG,节点按实体类型着色、悬停查看属性、滚轮缩放、拖拽平移;
- **交互**:`Enter` 提问(`Shift+Enter` 换行)、示例问题一键填入、召回数量 2~8 可调。
### 5.3 LLM 配置(可选)
```bash
# 复制样例并填写(.env 已在 .gitignore 中,不会提交密钥)
cp .env.example .env # Windows: copy .env.example .env
```
```ini
LLM_API_KEY=sk-你的密钥
LLM_BASE_URL=https://api.deepseek.com/v1 # 任何 OpenAI 兼容端点
LLM_MODEL=deepseek-chat
```
| 平台 | `LLM_BASE_URL` | `LLM_MODEL` 示例 |
|---|---|---|
| DeepSeek | `https://api.deepseek.com/v1` | `deepseek-chat` / `deepseek-v4-flash` |
| 通义百炼 | `https://dashscope.aliyuncs.com/compatible-mode/v1` | `qwen-plus` |
| 智谱 GLM | `https://open.bigmodel.cn/api/paas/v4` | `glm-4-flash` |
| Kimi | `https://api.moonshot.cn/v1` | `moonshot-v1-8k` |
| 自建 OneAPI/中转 | `https://你的域名/v1` | 网关中配置的模型名 |
说明:`.env` 由 `shanhaijing/llm_client.py` **自动加载**(零依赖逐行解析,不覆盖已有环境变量),无需手动 `export`。字段自检:
```bash
python -c "import os, shanhaijing.llm_client as l; print(l.llm_enabled(), l._cfg())"
# 密钥生效输出 True {'key': 'sk-…', 'base': '…', 'model': '…'};False 说明 .env 未生效
```
兼容性说明:要求接口为 **OpenAI 兼容的 `/chat/completions`**(Bearer 鉴权)。推理型模型(思维链占用 token)导致的空回复已做自动扩容重试与 `reasoning_content` 兜底。
### 5.4 常见问题
| 现象 | 原因与处理 |
|---|---|
| 答案显示"离线模板" | 未配置/未生效 `LLM_API_KEY`;按 5.3 检查 `.env` 后重启服务 |
| Web 打开白屏 | 请通过 `python server.py` 访问,不要直接双击 `web/index.html`(前端依赖 `/api` 接口) |
| 问题无任何证据 | 语料中确无该内容(如"什么是量子力学"),系统会如实说明"无法得出",这是设计行为而非故障 |
| 想重建全量图谱 | 依次执行 `python -m shanhaijing.data_prep full` → `python build_graph.py --all`(复用已有抽取表);若受控表也要重建:`python auto_extract.py`、`python llm_extract.py` |
---
## 六、目录结构
```
.
├─ README.md # 本文件
├─ MANUAL.md # 面向组员的理解手册(逐步讲解)
├─ requirements.txt # 无必装依赖(仅列可选增强)
├─ .env.example # LLM 配置样例(复制为 .env)
├─ server.py # Web 服务(静态页 + /api/status + /api/ask)
├─ main.py # 命令行交互问答(--retriever 切换检索器)
├─ auto_extract.py # 山经五卷:规则抽取 → curated_full/
├─ llm_extract.py # 海经十三卷:LLM 语义抽取(Schema 约束 + 审计)→ curated_llm/
├─ build_graph.py # 建图入口:--full(山经)/ --all(全十八卷合并)
├─ shanhaijing/ # 工程包
│ ├─ config.py # 路径与卷目配置
│ ├─ schema.py # 本体 Schema(7 类实体 / 10 类关系)
│ ├─ data_prep.py # 清洗 / 切分 / 统计
│ ├─ graph_store.py # 图存储(邻接表 + JSON)
│ ├─ graph_build.py # 受控表 → 图 + 校验报告
│ ├─ graph_query.py # 谱系 / 空间链 / 事件邻域查询
│ ├─ text_search.py # 字符 TF-IDF 索引(兼容旧流程)
│ ├─ retrievers.py # 四种检索器 + 向量/BM25 索引 + 实体链接
│ ├─ pipeline.py # 统一装载与 ask() 编排
│ ├─ answer.py # 知识注入生成 + 拒答 + 离线模板
│ └─ llm_client.py # LLM 客户端(.env 自动加载、空回复重试)
├─ web/ # 前端(原生 HTML/CSS/JS)
│ ├─ index.html / style.css / app.js
├─ data/
│ ├─ raw/ # 十八卷原文
│ ├─ processed/ # sections_full.jsonl / cleaned_all.txt
│ ├─ curated_full/ # 山经五卷规则抽取表
│ ├─ curated_llm/ # 海经十三卷 LLM 抽取表
│ ├─ curated/ # 别名/异体字表(aliases.csv,供实体归并)
│ ├─ curated_myth/ # 人工校订补录:长尾神话事件与漏抽谱系
│ └─ graph/ # shanhaijing_graph_all.json + build_report_all.txt
└─ results/ # 各报告:data_report_full / extract_report(_llm) / demo_report
```
---
## 七、进度与分工
### 7.1 成员分工
| 成员 | 职责 | 交付物 |
|---|---|---|
| 郭开,翟越 | 数据准备、Schema 与山经规则抽取、审计 | `data_prep.py`、`schema.py`、`auto_extract.py`、抽取报告 |
| 韦程伟,巨奕飞 | 海经 LLM 抽取、检索器与生成层 | `llm_extract.py`、`retrievers.py`、`answer.py` |
| 薛晗宇 | Web 界面与服务、文档 | `server.py`、`web/`、`README.md` |
> 占位名单,答辩前请替换为真实成员姓名与学号。
### 7.2 当前进度(截至 2026-09-14)
- **数据**:全十八卷清洗切分完成(878 行 / 38169 字 → 843 节);
- **图谱**:双通道抽取完成并合并(1629 节点 / 1590 边),逐卷审计报告齐备;
- **检索**:四种检索器全部实现并实测(含向量、VectorCypher、混合 RRF、混合+图遍历);
- **生成**:知识注入 + 证据不足拒答 + 离线降级,均通过实测;端到端 HTTP 接口验证通过;
- **界面**:检索器切换、答案/引用/实体关系/子图展示、缩放平移、示例问题等交互完成。