# 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 接口验证通过; - **界面**:检索器切换、答案/引用/实体关系/子图展示、缩放平移、示例问题等交互完成。