GraphRAG工程化实践:基于Dify与Neo4j构建知识图谱RAG系统

发布时间:2026/9/2 23:19:09
GraphRAG工程化实践:基于Dify与Neo4j构建知识图谱RAG系统 简介面向AI应用开发者和知识图谱、RAG技术学习者这份Demo课件材料围绕“用Dify搭建基于知识图谱的RAG系统”展开完整演示了从环境配置到流程编排的关键环节适用于教学演示、项目实战及技术进阶。压缩包共3个文件包含2个JSON数据文件和1个YAML编排配置文件整体约633MB。其中JSON文件提供可直接使用的食谱语料数据可供知识图谱建构与实体关系抽取训练YAML文件对应GraphRAG工作流配置清晰展示了检索增强生成链路便于动手复现与二次开发。目前已有2586人学习在同类Demo材料中拥有较高的关注度与实用参考价值。借助配套的数据与配置读者不仅可以理解知识图谱与RAG协同工作的原理还能完成从图谱构建、索引建立到智能问答的端到端实践是课程教学、毕业设计或个人项目的实用参考。 前不久要准备一份内部培训材料讲RAG怎么从“文档问答”往“知识密集型问答”走顺手在dify社区版上把“基于知识图谱的RAG系统”搭成了一个能现场跑通、能截图演示、能让不懂AI的同事也看明白的Demo。做完之后发现这套Demo不只用于培训自身就是一套很好的GraphRAG入门参考实现于是我把课件、代码、配置一起整理了一下。本篇文章就把这个过程从头拆一遍包括方案选型、图谱建模、dify工作流编排、评估指标设计以及课件材料怎么组织才能拿去直接开讲。适合正在做RAG落地、想了解GraphRAG工程化并且手头准备用dify做原型验证的团队参考。1. 为什么这个Demo要用“图谱向量”两条腿走路1.1 纯向量RAG回答不了哪类问题先回到一个基本问题普通的知识库RAG到底缺什么我在做课件之前先拿dify自带的知识库做了一轮测试建了一个包含20篇技术文档、10段课程讲义的测试集然后问了几类问题。单纯用向量检索的效果大致是这样事实型问题比如“某某公司的成立日期是多少”答案基本准确。归纳型问题比如“这几篇文档里都提到了哪些技术栈”答案开始模糊因为向量检索找的是相似片段不是全局视角。关系链问题比如“某某公司投资了哪些子方向这些方向和哪些论文有关”纯向量RAG直接答不上来甚至会把不相关片段拼在一起。这里暴露了一个本质问题向量检索本质上做的是“语义相似度匹配”它擅长定位一段话但不擅长处理“实体与实体之间的复杂关系”。知识图谱恰好补上这个短板它把实体和关系显式建模让“A投资了BB研发了CC被论文D引用”这类多跳查询变成结构化查询结果确定、可解释。所以这个Demo的定位从一开始就不是“用知识图谱替换向量检索”而是两者互补。这也是现在业内常说的GraphRAG混合范式向量库负责开放问答图谱负责关系推理和结构化证据最后由大模型统一组织答案。1.2 为什么选dify而不是从零搭一套既然要做成课件材料我第一考虑的是“别人能不能复现”。如果从零写RAG流程代码量不小还要处理Embedding管理、文档切分、Prompt模板、UI展示这些对于培训场景来说都是干扰项。dify的价值在于它把大部分工程基建都封装好了知识库管理上传文档、自动分段、选择Embedding模型、配置检索模式和Rerank基本是可视化完成。工作流编排用节点拖拽把检索、条件判断、外部API调用、模型生成串起来学员能直接看到数据流向。演示友好有对话界面的WebApp可以直接输入问题看效果不用额外开发前端。整个Demo的技术栈可以压缩成三块neo4j做图存储与Cypher查询dify做知识库和Agent/工作流编排大模型做最终生成。这个组合足够轻量也足够有代表性学员学会了之后把neo4j换成其他图数据库、把dify换成别的编排框架思路是通的。1.3 Demo的演示场景选什么最合适课件Demo最忌讳的是场景太抽象。我在设计时选了一个“企业投资关系技术方向科研论文”的图谱场景用公开的模拟数据做了一张小图包含公司、人物、论文、技术方向四类实体以及投资、任职、发表、研究四类关系。为什么选这个场景因为它天然适合展示“关系链问答”用户会问“某公司投了哪些涉及大模型的公司”“某位技术负责人的研究方向和哪些论文相关”。这类问题在纯向量知识库中很难答全但图谱一查就出结果现场演示时对比效果非常明显说服力很强。数据量刻意控制在几十个节点、上百条关系左右这样neo4j跑在低配开发机上没有任何压力导出、清洗、重建都能秒级完成学员也可以手动改数据来观察效果。2. 先用neo4j把知识图谱搭起来2.1 节点与关系怎么设计图谱建模是GraphRAG里最体现功力的部分。我的建议是不要一味追求O(1)级复杂本体课件场景下用“四类实体四条关系”就足够了。具体设计如下实体类型Company公司Person人物/技术负责人Paper论文TechDirection技术方向关系类型(Company)-[:INVESTED_IN {amount:xxx, round:A轮}]-(Company)投资关系(Person)-[:WORKS_AT {since:2021}]-(Company)任职关系(Person)-[:AUTHORED]-(Paper)发表论文(Paper)-[:RESEARCHES]-(TechDirection)论文研究的技术方向(Company)-[:FOCUSES_ON]-(TechDirection)公司关注的技术方向这里有一个关键设计把“技术方向”单独建模成实体而不是做成属性。因为在真实的问答里用户会问“哪些公司都在搞大模型应用”“这些方向之间有没有重叠”如果技术方向只是公司节点的字符串属性就没法快速关联查询。单独建模后一条Cypher就可以做多跳关联演示时很出效果。2.2 用Python批量灌入数据neo4j的数据导入方式有很多教程里常见的LOAD CSV可以但我更推荐写一个Python脚本用官方驱动批量写入原因有两个第一脚本可以随时修改并重跑方便演示前调数据第二学员后续对接自己的业务数据时换数据源只改脚本不需要学neo4j的导入语法。下面是我在课件里用的导入脚本核心部分为了方便大家参考我保留了最简版本from neo4j import GraphDatabase driver GraphDatabase.driver(bolt://localhost:7687, auth(neo4j, your_password)) def create_graph(tx): tx.run( MERGE (a:Company {name: $a_name}) MERGE (b:Company {name: $b_name}) MERGE (a)-[:INVESTED_IN {amount: $amount, round: $round}]-(b) , a_name星河科技, b_name图谱智能, amount2000万, roundA轮) tx.run( MERGE (p:Person {name: $p_name}) MERGE (c:Company {name: $c_name}) MERGE (p)-[:WORKS_AT {since: $since}]-(c) , p_name张明远, c_name图谱智能, since2022) tx.run( MERGE (pap:Paper {title: $title, venue: $venue, year: $year}) MERGE (d:TechDirection {name: $direction}) MERGE (pap)-[:RESEARCHES]-(d) , title基于知识图谱的工业知识问答方法, venueAI期刊, year2024, direction知识图谱) with driver.session() as session: for i in range(20): session.execute_write(create_graph) driver.close()这里用MERGE而不是CREATE关键作用就是幂等重复执行脚本不会产生重复节点和关系。做课件时学员可能随手改数据再跑如果表里出现一堆重复公司名演示效果直接失真。这个细节我在讲课时特意强调过。2.3 提前准备几条“镇场”Cypher在dify工作流里调用neo4j时不会让大模型随便生成Cypher那样有语法错误风险。正确做法是提前把演示要用的查询写成固定模板工作流根据问题意图做有限映射。我准备了四条核心查询分别对应四类演示问题查询某公司直接投资的子公司MATCH (c:Company {name: $name})-[:INVESTED_IN]-(sub) RETURN sub.name AS subsidiary, sub.industry AS industry查询两家公司之间的关联路径MATCH path shortestPath((a:Company {name: $a_name})-[:INVESTED_IN|WORKS_AT*..4]-(b:Company {name: $b_name})) RETURN path查询某位技术负责人发表过哪些技术方向的论文MATCH (p:Person {name: $p_name})-[:AUTHORED]-(pap:Paper)-[:RESEARCHES]-(d:TechDirection) RETURN pap.title AS paper, d.name AS direction查询某个技术方向被哪些公司关注、被哪些论文研究MATCH (d:TechDirection {name: $direction})-[:FOCUSES_ON]-(c:Company) MATCH (d)-[:RESEARCHES]-(pap:Paper) RETURN c.name AS company, pap.title AS paper这些查询的共性是可参数化、结果简单、适合LLM二次总结。演示时不用让模型真去写Cypher只需要在外部服务里根据关键词做一次简单映射即可。3. 在Dify里落地知识库与工作流3.1 知识库的配置细节我在dify里建了两个知识库一个用于存放文档原文叫“技术资料库”另一个用于存放图谱实体与关系描述文本叫“图谱说明库”。这里有一个经验很多刚接触的人容易忽略知识库可以是“文档库”也可以是“结构化描述库”你完全可以把图谱里的每个节点和关系转成一段自然语言文本塞进知识库做语义召回。这相当于给图谱加了一个语义索引层。具体做法是对每个实体节点生成一段说明文字比如“图谱智能是一家专注企业知识问答的大模型公司成立于2021年已完成A轮融资”对每条关系也生成一小段文本然后把它们导入知识库。这样纯向量检索也能找到图谱相关内容再结合neo4j的结构化查询结果两路召回一起送到LLM效果比单用一路稳定得多。索引模式我选择“高质量”因为课件演示对回答质量要求高经济模式会丢细节。Embedding模型用的text-embedding-3-small检索模式设置为“混合检索”并配置了一个rerank模型做精排。这一步对演示效果影响很大纯向量检索时用户问“和张明远研究方向最匹配的是哪家公司”这种语义匹配题排序经常不理想加了rerank之后前三个结果基本就是可用答案。3.2 在dify工作流里接neo4jdify本身没有原生neo4j节点我采用的方式是写一个非常小的FastAPI服务封装刚才那四条Cypher查询然后用dify的“HTTP请求”节点来调用它。为什么不在dify的代码节点里直接连neo4j确实是可行的但代码节点的运行环境是隔离沙箱要装neo4j驱动、调试超时都比较麻烦而且把业务逻辑藏在工作流节点里学员看课件时很难追踪。独立小服务的好处是逻辑清晰、可单独调试、发布后还能给多个工作流复用。FastAPI服务的核心结构如下from fastapi import FastAPI from neo4j import GraphDatabase from pydantic import BaseModel app FastAPI() driver GraphDatabase.driver(bolt://localhost:7687, auth(neo4j, your_password)) class QueryRequest(BaseModel): question: str app.post(/graph/query) def graph_query(req: QueryRequest): question req.question if 投资 in question and 公司 in question: # 简单意图识别提取公司名后执行对应Cypher name extract_company(question) records, _, _ driver.execute_query( MATCH (c:Company {name: $name})-[:INVESTED_IN]-(sub) RETURN sub.name AS subsidiary, namename ) return {result: [r[subsidiary] for r in records]} # 其他意图分支省略在大模型应用中意图识别未必都要用LLM完成规则匹配在有限场景下更快、更可控也更容易向学员解释。课件里我明确标注了这类轻量级意图路由使用规则优先当问题类型复杂时再换成LLM分类器。3.3 工作流怎么编排才直观课件里我展示的是一个四段式工作流结构如下开始节点接收用户问题。知识检索节点先去“技术资料库”和“图谱说明库”做混合检索返回召回片段。HTTP请求节点调用上面的FastAPI服务拿到neo4j图谱查询结果作为结构化证据。LLM节点把召回片段图谱证据用户问题一起放进Prompt要求模型优先引用图谱证据回答关系类问题。结束节点输出最终答案并在回答中标注“本回答参考了知识图谱关系数据”或“本回答来源为文档检索”方便演示时说明证据来源。这里有一个细节值得注意知识检索节点和HTTP请求节点是并行执行的不是串行。这样设计有两个好处一是响应速度快不需要等图谱查询完再检索知识库两路同时跑二是演示时可以在dify的追踪面板里清晰看到两路召回结果便于讲解“混合召回”的概念。很多新手会把节点串成一条长链路其实在dify工作流里分支并行是常见优化手段成本几乎为零。LLM节点里我用的Prompt模板也写上给需要的朋友直接抄你是一个企业知识问答助手。请结合“文档检索结果”和“知识图谱查询结果”回答用户问题。 文档检索结果 {{#context#}} 知识图谱查询结果 {{#graph_result#}} 要求 1. 如果知识图谱结果能回答问题优先使用结构化证据并说明实体间的关系。 2. 如果图谱结果不完整再用文档检索结果补充。 3. 不要编造数据若两者都没有覆盖到直接回答“当前知识库中未找到相关信息”。 4. 输出控制在150字以内。这套Prompt的核心是“图谱优先文档补充”它保证在演示关系链问题时答案直接从结构化数据里得出而不是靠模型猜测。4. 演示效果与质量指标怎么设计4.1 现场演示三类问题课件材料里我把演示环节固定为三个问题每个问题对应一个能力层次讲的时候节奏非常清晰第一问文档事实型问题。比如“星河科技成立于哪一年”。这一步展示的是基础RAG能力向量检索直接命中。第二问关系链问题。比如“星河科技投资了哪些与大模型相关的公司这些公司的技术负责人发表过什么方向的论文”这一步让neo4j先查投资路径再关联技术方向最后LLM汇总成一段流利的回答。第三问对比型问题。比如“为什么第二个问题比第一个问题更好回答”在系统返回里展示图谱查询的json结果再展示纯向量检索的片段列表直观说明“图谱给的是证据链条向量给的是相似文本”。这套演示顺序能让听众在三分钟内建立理解先知道RAG是什么再感受图谱带来的增量最后理解两种技术各自的位置。我在实际培训中还特意把第二个问题的完整链路截图放进了课件包括dify工作流每个节点的输入输出学员反馈“看到数据在节点间流动”比看PPT讲概念有效得多。4.2 RAG知识库指标怎么量化课件里不能只讲“效果不错”得有量化数据。我的做法是准备了一个20道题的评测集覆盖三类问题事实型、归纳型、关系链型。然后分别跑“纯向量RAG”和“图谱RAG混合方案”记录四个关键指标命中率Hit Rate检索结果中是否包含正确答案所在文档。忠实度Faithfulness模型回答是否忠于检索到的上下文有没有编造。上下文相关性Context Relevance召回的片段与问题的相关程度。端到端回答正确率Answer Correctness人工判断最终答案质量。实测数据的结论很有意思事实型问题上两种方案差距不大但关系链问题上混合方案的命中率从55%提升到90%忠实度也从70%左右提升到95%左右。这也成了课件里最有说服力的一页。指标这个东西不需要做得太复杂选三五个关键维度做个对比表听众就能理解“图谱为什么有价值”。4.3 用dify的日志和标注辅助评估dify本身自带了一些可观测能力在课件里我也会演示一下每条对话记录都可以打开查看“检索结果来源模型输出内容”这个功能在评估阶段非常实用。我会在准备评测数据时把每道题的“预期答案”和“实际输出”粘贴到一张Excel表里人工打标是否忠实、是否完整然后统计得分。这个方法虽然原始但对于Demo和课件材料来说足够可靠。真正的自动化评测工具比如RAGAS以后可以集成但在目前阶段人工打标反而能加深对指标定义的理解学员跟着做一遍就知道哪些环节弱。课件里我专门留了一页“评估陷阱”比如不能只看答案正确率上下文丢了但模型恰好蒙对了这种情况必须拆开看。只有分开记录召回质量和生成质量定位问题才有的放矢。5. 课件材料的组织与常见坑5.1 课件目录结构参考这份材料最终交付时我按“能直接讲课能直接复现”的标准组织目录结构如下demo-kg-rag/ ├── 00_课件总览.pptx ├── 01_背景与痛点.pptx ├── 02_技术方案讲解.pptx ├── 03_图谱构建实操.pptx ├── 04_Dify工作流实操.pptx ├── 05_评估结果与复盘.pptx ├── demo/ │ ├── neo4j_import.py │ ├── fastapi_graph_api.py │ └── sample_data.csv ├── docs/ │ ├── 环境安装手册.md │ └── 演示脚本.md └── eval/ ├── eval_questions.xlsx └── eval_metrics.py其中“演示脚本.md”是我强烈建议保留的里面写清楚每页PPT对应演示哪个问题、点哪个按钮、预期结果是什么、如果网络波动备用方案是什么。真正站到讲台前就会知道这些细节能救命。5.2 踩过的几个坑我在准备这个Demo时遇到不少问题挑几个有代表性的记录下来供大家避坑第一neo4j的Cypher参数传递容易踩坑。直接在查询字符串里拼接参数字段含中文没问题但含特殊字符容易报错必须在代码里用参数化查询。课件的脚本里所有动态值都通过$name、$amount这样的参数传入禁止拼字符串这点我在代码注释里高亮标出了。第二dify知识库的分段设置直接影响召回质量。默认分段可能把完整句子切断导致语义不完整。我在“图谱说明库”里把自定义分段标识符设为“句号换行”让每条描述尽量保持独立语义。实际操作时可以通过知识库的“召回测试”功能反复调分段大小直到检索结果稳定。第三FastAPI服务被调用时的超时问题。如果neo4j查询的图谱数据较多或本机性能一般HTTP请求可能超过dify默认的超时时间。我的解决方案是为演示场景单独准备一个轻量子图复制到本地库里查询基本在几百毫秒内完成。同时在FastAPI服务里设置了合理的连接池把initial_connection_pool_size设小一些避免本地服务连接数过多。第四模型选择上不要为了“高级”选择超大参数模型。本地部署一个7B左右的模型当生成模型响应速度快也够用在线API虽然效果好但现场演示一旦网络出问题就容易翻车。我最终采用“本地小模型备用在线API”双配置课件里也写明了两种模式下Prompt的变化。5.3 后续扩展的小思路这个Demo做完后后续扩展空间其实很大。比较自然的一个方向是把“固定Cypher模板”升级为“NL2Cypher”即将用户问题经过LLM转成Cypher语句由系统执行后再返回结果这需要提前限定图谱schema并设计few-shot示例。另一个方向是把意图识别从规则换成轻量分类模型提升复杂问题的路由准确率。还有同事建议接入dify的Agent模式让Agent自动决定什么时候需要查图谱、什么时候直接走文档检索这个我也在尝试等跑通了再单独写一篇。如果你准备在团队内做类似的RAG进阶分享我建议先别急着铺大概念把一个能跑的Demo拆成“图谱建模、检索增强、工作流编排、效果评估”四个模块一次讲透一个。我这次课件之所以做得顺利很大程度也是因为前期把图谱和向量各管什么事想清楚了后面所有环节都很顺。本文还有配套的精品资源点击获取