reading-notes — essential-graphrag
边读边记:哪章讲了什么机制、关键数字、值得引的段落行号。行号 = text/xx.txt:N。
前置材料
- foreword (
04-fm-foreword.txt):Paco Nathan(Senzing)作序。卖点:「从零实现 GraphRAG,不依赖现有框架」(L2-3);练习带 arXiv 一手来源(L5);从简单 RAG → GraphRAG → agentic(L5-6)。金句:「AI 应用的力量不来自不可言说的魔法,而来自自信、有经验的建设者」(L18-19)。 - preface (
05-fm-preface.txt):两人都在 Neo4j 共事多年;Oskar 20+ 年工程师、Neo4j 10 年、带 Neo4j GenAI 工程团队(L9-10);Tomaž 深耕图算法/ML/LLM,给 LangChain 和 LlamaIndex 贡献代码(L10-12)。动机:「有人该写一本把知识图谱和 RAG 结合的书……那就我们来写」(L2-4)。LLM 局限:过时信息、缺领域细节(L6-7)。 - about this book (
07-fm-about-this-book.txt):目标=不依赖现有框架从零建 GraphRAG(L10);8 章路线图(L28-48):ch1 LLM 局限→ch2 向量检索+混合检索→ch3 进阶检索→ch4 自然语言转 Cypher→ch5 agentic RAG→ch6 从文本构建知识图谱→ch7 微软 GraphRAG(用《奥德赛》)→ch8 评测。代码仓库 github.com/tomasonjo/kg-rag(L65-66)。 - about the authors (
08-fm-about-the-authors.txt):Tomaž Bratanic——写过图算法书、LangChain/LlamaIndex 贡献者;Oskar Hane——Neo4j 资深 staff 工程师。利益相关:两人都是 Neo4j 生态的人,本书通篇用 Neo4j——写进总纲第 2 节。
ch01 Improving LLM accuracy (10-ch01-…txt,36874 字符)
主线:LLM 是什么 → 四大局限 → 微调不行 → RAG → 为什么知识图谱是合适的存储。
- 1.1(L46-115):LLM=下一词预测器,transformer(Vaswani 2017)。「Never gonna → give you up」例(L57-64)。关键点:模型不存具体事实,存的是学到的权重(L92-95);Karpathy 引语:「它们构建某种知识库,但这个知识库非常奇怪、不完美」(L112-115,YouTube zjkBMFhNj_g 12:40)。权重 0.04 例子(L132-136)。
- 1.2 局限(L140-253):
- 知识截止(L146-157):ChatGPT 知道到 2023-10;
- 过时信息(L161-178):Mark Cuban 卖 Dallas Mavericks 多数股权给 Adelson 家族,模型还答他是唯一老板(2023 底的事,Rader 2023);
- 纯幻觉(L181-214):美国律师提交 ChatGPT 编造的判例(Neumeister 2023,L188-193);「LLM 不是推理引擎,是概率语言模型」(L195-200);WikiData ID 例:问 Mavericks 的 ID,模型自信答 Q152232,实际那是电影 Womanlight 的 ID(L201-210);
- 缺私有信息(L216-231);
- 其他局限(框注,L233-253):偏见、缺真理解、prompt injection 脆弱、同一问题答案不一致——本书只管「事实正确+时效」这一类,其他不覆盖。
- 1.3 克服(L256-435):
- LLM 训练四阶段(Karpathy 引,L270-290):pretraining(万亿 token、几千 GPU、数月)→ SFT → reward modeling → RL;
- SFT 灌事实:证据矛盾——Tian et al. 2023 说能提升事实性,Ovadia et al. 2023 说 LLM 难靠微调学新事实(L310-313);结论:微调不实用(L314-318);
- RAG(L320-435):两阶段 retrieval + augmented generation(L329-331);Lewis et al. 2020(L325);prompt 模板={context}+{question}(L396-401);检索在幕后自动发生(L407-413);ChatGPT 联网搜索就是 RAG(L414-435)。
- 1.4 知识图谱作为 RAG 存储(L436-499):
- 图=节点(概念/实体)+关系;一个库里同时放结构化(员工/任务/层级)+非结构化(文章正文+embedding)(图 1.14,L445-468);
- 结构化数据支持精确操作:过滤/计数/聚合(「多少任务完成了」「谁报告给谁」)(L488-494);
- 非结构化单独答不了这类问题,要「穷举文本解析」,贵且不精确(L492-494);
- 结构化↔非结构化的显式连接解锁进阶检索(文本实体链到图节点、结构化结果带源文)(L496-499)。
- Summary(L501-523)。
ch02 Vector similarity search and hybrid search (11-ch02-…txt,31546)
主线:RAG 两组件(retriever/generator)→ 向量检索的全部零件 → 用「爱因斯坦专利」论文走通一个最小 RAG → 加全文检索变混合检索。
- 2.1(L34-119):
- retriever(L40-106):向量索引=「近似最近邻」,拿速度换准确(L46-54);相似度函数:cosine vs 欧氏,cosine 0–1、文本聊天机器人首选(L60-69);embedding 模型全程必须同一个,换了要重灌索引(L70-75);embedding 维度决定能装多少信息、维度越高越贵(L76-79);chunking 没有标准答案,按句/段/语义/滑窗都行,要实验(L87-98);
- generator(L107-119):RAG 的隐性好处——模型不用大;用 LLM 的语言能力而非知识,更小、更快、更少幻觉。
- 2.2 实战(L121-435):语料=Einstein's Patents and Inventions 论文(L172-176);滑窗 500 字符、重叠 40 → 89 个 chunk(L184-187/L226-229);只在空格处切,避免断词(L188-192);embedding:OpenAI text-embedding-3-small,1536 维(L253/L260);替代品 all-MiniLM-L12-v2 可跑本地 CPU(L235-238);Neo4j 建 Chunk 节点(index/text/embedding)+向量索引(L268-314);
- 主走查素材:问题「At what time was Einstein really interested in experimental works?」→ 向量检索 top2:chunk#42 得分 0.8185、chunk#44 得分 0.7906(L333-384);塞进 prompt → 答「During his ETH days…」(L434-435)。
- 2.3 混合检索(L441-543):全文检索=关键词精确匹配(L451-454);混合=两路各查 k 条、各自除以本路最高分归一化、去重、取 top k(L467-499);结果:top1 归一化后 1.0(chunk#42),第二名变了——全文检索找到比向量更好的 chunk#31(0.9836)(L539-542)。
- 2.4(L544-554):混合检索更好,但纯非结构化数据仍有硬顶:「文中的引用关系抓不到、周边上下文不够 LLM 理解」——逼出后面章节。
ch03 Advanced vector retrieval strategies (12-ch03-…txt,38559)
主线:查询侧(改写问题)与文档侧(换 embedding 对象)两个杠杆 → 实现 step-back + parent-document retriever。
- 开场:query embedding 与文档 embedding 错位(术语/上下文不同)→ 高相关文档被漏掉(L20-29)。
- 改写策略:HyDE(Gao et al. 2022)、step-back prompting(Zheng et al. 2023)(L36-38);图 3.1 例:Estella Leopold 1954 年 8-11 月在哪所学校 → 改写成「她的教育经历」撒大网(L40-69)。
- 文档侧策略(图 3.2,L80-142):假设问题策略(把「文档能回答的问题」当 embedding 对象,HAS_QUESTION 边);parent-document(子块算 embedding 精确匹配,命中后返回整个父文档保上下文);动机:长文档整个 embed 会把不同想法平均糊掉(L138-142)。
- 其他策略只点名不展开(框注 L144-175):微调 embedding 模型、reranking、元数据过滤、混合检索。
- 3.1 step-back(L195-287):Thierry Audel 例:「2007-2008 效力于哪队」→「职 业生涯效力过哪些队」(L198-201);系统 prompt=指令+两个 few-shot 示例(L231-241);zero-shot vs few-shot 的解释(L243-252);实测改写成「What is the career history of Thierry Audel?」(L276)。
- 3.2 parent document(L289-540):图结构 PDF→HAS_PARENT→Parent→HAS_CHILD→Child(图 3.4);先按章节结构切(正则找标题行)→ 9 节(L333-372);各节 token 数:154/254/4186/570/2703/1441/194/600——第三节超 4000 token(用 tiktoken 数,L379-391);父块 2000 字符、子块 500 字符重叠 20(L396-399/L442);检索时取 k×4 个子块再沿 HAS_CHILD 上卷去重——因为多个子块可能同属一个父文档,k×4 是安全缓冲(L510-520)。
- 3.3 全流程(L543-596):改写后的问题拿去检索、原始问题拿去生成(L575-580);测试:「Einstein 何时获得衬衫设计专利?」→ step-back「What are some notable achievements in Einstein's life?」→ 答「October 27, 1936」(L589-592)。
ch04 Generating Cypher queries from natural language (13-ch04-…txt,23412)
主线:向量检索答不了聚合/过滤类问题 → 让 LLM 生成 Cypher;生成质量的四个抓手。
- 动机例题:「List the top three highest-rated movies directed by Steven Spielberg and their average score」——永远不可能靠向量相似度回答,必须聚合(L68-71);Cypher 示例(L74-80);text2cypher 可当「catchall」检索器(L89-90)。
- schema 是关键:不给 schema,LLM 只能猜节点/关系/属性名; 给了 schema,它就是「问题语义 ↔ 图模型」的映射(L33-37)。
- 四个抓手(L92-309):
- few-shot(L99-136):每个图手工定制;例:LLM 想读 m.country 属性,但国家是节点,加一个 PRODUCED_IN→Country 的示例纠正(L110-136);
- schema(L138-175):Neo4j 内部研究「格式影响不大」(L140-142);用 APOC 的 apoc.meta.data() 推断(L170-201);全库推断贵,常采样(L166-169);
- 术语映射(L277-295):「用户说 person → Person 标签」;图特定、随时间演化(L287-289);
- 格式指令(L297-309):只输出 Cypher、不带代码块。
- 实战(L311-440):Movies 数据集;Movie{tagline,title,released}、Person{born,name};关系 ACTED_IN/DIRECTED/PRODUCED/WROTE/FOLLOWS/REVIEWED(L392-408);「Who directed the most movies?」→ 生成聚合查询(L437-440)。
- 4.5 微调模型(L442-454):Neo4j 开源训练数据 huggingface.co/datasets/neo4j/text2cypher;微调的 Gemma2/Llama 3.1 仍明显落后 GPT/Gemini,但更高效(L448-450)。
ch05 Agentic RAG (14-ch05-…txt,31269)
主线:多种检索器并存 → 路由器选人 → 答案批评家验收;工具调用机制。
- 三件套(L52-58):retriever router(问题→选检索器)、retriever agents(实际干活)、answer critic(验收,blocking)。
- 工具调用:LDM 原生 function calling(GPT-3.5/4);无原生支持的可用 ReAct(arXiv 2210.03629)(L20-27)。关键机制:LLM 只决定「用哪个工具+传什么参数」,真正调用是外部系统做的(L245-248)。
- 泛用 vs 专用检索器(L60-71/L106-125): 泛用的(vector、text2cypher)达不到生产水准 → 造窄而准的专用检索器,随时间把 text2cypher 答不好的问题沉淀成专用检索器,text2cypher 退居 catchall。
- 路由例(L78-90):「What is the capital of France?」→ capital_by_country("France")。
- 答案批评家(L95-105):不够格就生成新问题再走一轮;必须有退出条件,否则死循环。
- 实现(L127-596):4 个工具:text2cypher / movie_info_by_title / movies_info_by_actor / answer_given(L147-280);answer_given 处理「答案就在问题里」:例「What's Dave Smith's last name?」(L249-280);连续问题更新:「Who has won the most Oscars, and is that person alive?」拆成两问,第二问依赖第一问答案,query updater 用已得答案把问题改写得更完整(L329-389);router 的 prompt 很短就够,因为 function calling 是内置能力(L425-433);critique 只做一轮,不完整就直接返回并靠 LLM 说明缺什么(L594-596)。
ch06 Constructing knowledge graphs with LLMs (15-ch06-…txt,47197)
主线:法律合同场景暴露向量 RAG 的两个死穴(跨文档混块、聚合统计)→ LLM 结构化抽取 → 建图 → 实体消解 → 结构化+非结构化共存。
- 死穴一(图 6.1):问「ACME 那份合同的付款条款」,top-k chunk 可能来自不同合同——相似度不管文档边界(L34-70);死穴二:「现在和 ACME 有几份有效合同?」要过滤+计数(L71-79)。
- 历史背景:结 构化抽取=老的 information extraction,过去要多个 ML 模型+工程团队,只有大机构玩得起;LLM 把门槛打下来(L144-154)。
- OpenAI Structured Outputs + Pydantic(L155-206):字段名+类型+description 三件套引导抽取;date 用 str+「yyyy-MM-dd」说明,因为无原生 datetime 类型(L189-201);Optional 很重要——不标 optional,LLM 会编值填空(L265-269);enum 锁定取值(contract_type 5 种,L283-289);role 不用 enum 只给示例,因为取值不可穷尽(L321-323);嵌套对象别太深,影响性能(L320);country 用两位 ISO 标准化(L341-347)。
- extract 函数:temperature=0、gpt-4o-2024-08-06、response_format=Contract(L394-405)。
- CUAD 数据集(Hendrycks et al., 2021)(L420-428);抽取结果实例(L445-474):Mortgage Logic.com Inc(Client,Irvine CA)/ TrueLink Inc(Provider,San Luis Obispo CA);生效 1999-02-26;期限 1 年自动续;end_date、total_amount=None。
- 建图(6.2):图模型 Contract–HAS_PARTY(role)–Organization–LOCATED_AT–Location(图 6.3 用词 HAS_LOCATION,代码用 LOCATED_AT——小出入,我们写 LOCATED_AT);唯一约束(L553-565);import 用 randomUUID() 当合同 ID,不幂等,重跑会重复导入(L611-612)。
- 实体消解(6.2.2):UTI Asset Management Company / …Limited / …Ltd 三种写法一个实体(L638-641);手段:字符串匹配、聚类、ML;高度领域特定,没有通用解(L654-656);领域本体/规则+领域专家+迭代反馈(L659-666)。
- 非结构化并入图(6.2.3):Contract–HAS_CHUNK–Chunk;法律域按条款切块优于按字数(L704-709)。
- 注:本文件 L735-756 混入了 ch07 开头(Microsoft's GraphRAG implementation 的章首+图 7.1 起步)。
ch07 Microsoft's GraphRAG(16/17/18/19 四个文件)
- 7.1 数据集选择(
16-…txt,2068):图 7.1 是 MS GraphRAG 全管道(Edge et al. 2024,CC BY 4.0);两阶段:抽取+摘要 → 社区+摘要;实体类型要预先定义并影响下游(L20-26);选《奥德赛》:叙事丰富(人/神/神器),Ulysses 跨多个 chunk 出现,适合测跨块摘要(L4-7,Gutenberg #1727)。 - 7.2 图索引(
17-…txt,33190):- chunking(7.2.1):24 卷,平均 6515 token、最小 4459、最大 10760(L61-63);chunk size 越小抽出的实体引用越多(600>1200>2400,图 7.2,L82-88);self-reflection 迭代(对同一文本多抽几遍)也加检出量(L84-88)——即微软论文的 gleaning;本书用 1000 词、重叠 40(L89-94);
- 抽取(7.2.2):借论文附录的 prompt:实体(名字/类型/描述)+关系(源/目标/描述/strength 分数),用 tuple/record/completion 三个分隔符包成可解析文本(L104-133);实体类型 PERSON/ORGANIZATION/LOCATION/GOD/EVENT/CREATURE/WEAPON_OR_TOOL(L146-154);EVENT、LOCATION 界限模糊,分类更难但也给 LLM 留弹性(L156-160);第一卷抽出 66 实体、182 关系(每次运行会变)(L230-231);ORESTES 有 6 条描述,有重复但 collectively 保全关键细节(L244-255);关系最多的一对:Telemachus×Minerva 14 条(L271-273),5 条示例(L274-280);
- 摘要(7.2.3):合并同一实体/关系对的多条描述成一条,「矛盾也要消解成连贯一段、第三人称」(L296-316);ORESTES 摘要成品(L363-368);Telemachus–Minerva 关系摘要成品(L430-440);
- super node 框注:大图里某些实体(如雅典)关系爆多,不排序会把 prompt 撑爆——要过滤/排序策略(L446-455);
- 社区(7.2.4):社区=内部连得比外部密的实体群(L459-461);本书用 Louvain(GDS),原论文用 Leiden(也在 GDS)(L481-484);9 个社区,大小 2–13 节点;Louvain 不确定性:同输入两次跑结果可能略不同(L491-494);论文用层级 Louvain,小图只做单层(L496-501);社区摘要 prompt:TITLE/SUMMARY/IMPACT SEVERITY RATING(0-10)/RATING EXPLANATION/DETAILED FINDINGS(5-10 条)(L507-536);最大社区摘要成品:「Minerva, Telemachus, and the Ithacan Household」(L15-22 of 18-…txt);
- 大社区框注:超 token 限要按相关性选实体和关系(L24-30 of 18)。
- 7.3 图检索器(
18-…txt,29715):- global search(7.3.1):map-reduce(L60-73);map:每份社区报告产中间响应=关键点列表,每点带 0-100 importance score,「不知道就直说、引用格式 [Data: Reports (ids)]、最多列 5 个 id + more」(L84-125);reduce:按重要性排好序的多份「分析师报告」合成一个 markdown 答案(L139-194);实现:先按 rating≥阈值过滤社区(阈值默认 5)(L199-209);成品回答「What is this story about?」(L263-287);
- 层级选择框注(L76-82):低层社区详细但 LLM 调用多、慢;高层抽象但丢粒度;
- local search(7.3.2):实体级问题(例:「chamomile 的疗效」);先用向量检索找相关实体当入口(L307-309),再扩四类上下文:相关 chunk(按命中实体数排频)、社区报告(按 rank/weight)、SUMMARIZED_RELATIONSHIP、实体摘要——都排序+限量(L379-424);实体 summary 的 embedding 建向量索引(L343-369);成品回答「Who is Ulysses?」(L519-544)——与 global search 的回答几乎相同(本书只建了 1 卷的图,9 个社区),这点值得在拆解里点破:示例的可分辨度受限于小图。
- summary(
19-…txt,2020):两阶段、类型预定义、跨块摘要 合并、社区+摘要、global=map-reduce、local=向量+图遍历、chunk 越小抽得越全、排序机制管规模。
ch08 RAG application evaluation (20-…txt,30471)
- 四个评测位(图 8.1,L26-44):工具选得对不对 → 检回的上下文切不切题 → 给对了上下文答得对不对 → 端到端。
- 三种检索器分工回顾(L46-54):vector=语义、Cypher 模板=精确、text2cypher=动态灵活。
- benchmark 数据集设计(8.1,L83-109):工具选择、实体/值映射、多步检索、边界情况、对话可用性(问候/超范围问题);
- ground truth 用 Cypher 而不是静态答案:数据变了考卷仍有效(L127-147):例「Tom Hanks 演了几部电影」→ count 查询;问候/超范围用假 RETURN(L157-171);共 17 个例子(L238-239);
- RAGAS 三指标(8.2):
- context recall:答案逐句判断「能不能归因给上下文」1/0(L246-258);
- faithfulness:两步——先把答案拆成原子陈述(不许带代词),再逐句判 1/0(L262-288);
- answer correctness:与标准答案对齐分 TP/FP/FN(L292-316);
- 跑法:缺失答案填「I don't know」再喂 RAGAS(L369-385);
- 结果(表 8.5):answer_correctness 0.7774 / context_recall 0.7941 / faithfulness 0.9657(L393-395);解读:模型几乎不编造(忠实度高),但检索漏信息拖低正确率——该修的是检索,不是生成(L398-407);
- 观察(L417-423):不走 text2cypher 的延迟明显低(省一次 LLM 调用);LLM 当裁判有不一致(Hello 例);失败例「Who has the longest name among all actors?」——模型写不出对应 Cypher,解法=加 few-shot 或专用工具。
- 8.3 next steps(L437-456)。
appendix The Neo4j environment (21-…txt,54446;后半全是 Movies 数据集 Cypher 清单)
- Neo4j=原生图数据库(Java),Cypher 查询,Bolt 协议;节点/关系是一等公民,属性=键值对(L3-13);
- 两个插件:APOC(数据导入导出/转换/日期/地理/文本等过程库)+ GDS(图算法:最短路、PageRank、社区检测、节点嵌入、链接预测)(L14-24);
- Cypher:声明式,ASCII 画图式语法(L26-35);openCypher 开放化,被 Amazon/AgensGraph/Katana Graph/Memgraph/RedisGraph/SAP HANA 采用(L44-45);ISO 的 GQL 项目以 SQL 为底统一图查询语言,学 Cypher 是好起点(L46-52);
- 安装三条路:Desktop/Docker/Aura;ch7 要 GDS,Aura 免费版没有,得用 AuraDS(L168-177);版本要求 5.9.0+(L100-101);Docker 镜像 neo4j:5.26.0(L152-158);
- Browser 配置:关掉 Connect Result Nodes 免得看花眼(L179-188);
- Movies 数据集:三种载入法(:play movies / demo.neo4jlabs.com / Cypher 清单)(L189-1064)。