
这篇不先堆名词。我们把《我把GraphRAG接进项目后先推翻了几个想当然》拆成几级台阶看完至少知道下一步该学什么、该练什么。摘要GraphRAG把知识图谱和RAG结合听起来是个完美的组合。但当我们把这个方案从个人Demo推进到团队协作场景时才发现很多理所当然的判断都需要重新审视。本文记录我们在企业知识库项目中从需求提出到方案落地的真实过程以及几个关键的取舍决策。---目录1. 传统RAG的瓶颈2. 知识图谱建模3. 实体关系抽取4. 图检索增强5. 评估与优化6. 总结---传统RAG的瓶颈我们团队接到这个需求的时候业务方原话是客户问的问题越来越复杂光靠向量检索答不上来。我第一反应是加更强的embedding模型、调更好的chunk策略。试了一圈效果提升有限。问题出在哪里传统RAG的核心逻辑是把文档切片、向量化、检索、召回、回答。这个流程在单文档、单主题场景下表现不错但一旦涉及多实体关联、跨文档推理就露怯了。举个例子。我们有份技术文档讲了A模块和B模块的集成关系另一份文档讲了B模块和C模块的依赖。当用户问A如何与C交互时基于向量检索的RAG很难把这两份文档关联起来因为A与C交互这个查询在原始文档中根本不存在。我们做过一个对比实验。用纯向量检索回答一组涉及实体关系的问题准确率大概60%左右而这些问题如果用知识图谱来检索准确率能到85%以上。差距不在于模型能力而在于检索方式——向量检索找的是语义相似知识图谱找的是结构关联。这里有一个判断标准如果你的知识库中问题往往涉及多个实体之间的关系或者需要跨文档综合推理那就不是简单调RAG参数能解决的需要引入图谱结构。---知识图谱建模建模是GraphRAG最容易踩坑的环节。我们一开始犯的错误是把文档里的所有内容都抽成实体和关系。结果图谱变得又臭又长查询慢噪声大。后来我们重新思考这个图谱到底要解决什么问题答案是支撑复杂问答。所以我们把建模目标收窄到问答所需的最小图谱。具体做法是1. 先梳理业务场景列出典型问题类型2. 根据问题类型确定需要哪些实体和关系3. 只抽取对回答问题有贡献的三元组我们最终建出的图谱包含三类实体产品、模块、问题三类关系依赖、包含、解决。看起来简单但覆盖了80%以上的问答场景。这里有一个取舍我们主动放弃了概念这种抽象实体。因为概念之间的关系很难定义而且对问答帮助不大。图谱不是越大越好而是要精准。建模完成之后我们用了Neo4j来存储。导入的时候注意两点一是给节点和关系加上合理的标签方便查询二是建索引否则查询性能会很差。from neo4j import GraphDatabase class GraphBuilder: def __init__(self, uri, user, password): self.driver GraphDatabase.driver(uri, auth(user, password)) def create_schema(self): with self.driver.session() as session: # 创建约束保证实体唯一性 session.run(CREATE CONSTRAINT FOR (p:Product) REQUIRE p.id IS UNIQUE) session.run(CREATE CONSTRAINT FOR (m:Module) REQUIRE m.id IS UNIQUE) session.run(CREATE CONSTRAINT FOR (q:Question) REQUIRE q.id IS UNIQUE) # 创建索引加速查询 session.run(CREATE INDEX FOR (m:Module) ON (m.name)) session.run(CREATE INDEX FOR (p:Product) ON (p.name)) def create_relationship(self, source_type, source_id, target_type, target_id, rel_type): with self.driver.session() as session: session.run( fMATCH (a:{source_type} {{id: $source_id}}), (b:{target_type} {{id: $target_id}}) fCREATE (a)-[r:{rel_type}]-(b) RETURN r, source_idsource_id, target_idtarget_id )---实体关系抽取抽取环节我们试过几个方案最后选了LLM规则的组合。纯用LLM抽取的问题是不稳定同一份文档抽两遍结果可能不一样。纯用规则又覆盖不了复杂场景。所以我们的策略是用LLM做粗抽用规则做精修。具体流程1. 把文档分段每段发给LLM让它提取实体和关系2. 用正则和词典做后处理过滤明显错误的结果3. 对实体做归一化避免同义词导致的实体分裂这里有个实际案例。文档里出现API网关和网关LLM会把它当成两个实体。我们用同义词词典做归一化把它们合并成一个实体。抽取prompt的设计也很关键。我们用的是这种格式请从以下文本中提取实体和关系以JSON格式输出 文本{text} 要求 1. 实体类型Product, Module, Question 2. 关系类型depends_on, contains, solves 3. 只提取与问答相关的内容 4. 输出格式{entities: [...], relations: [...]}注意最后一条要求这是控制图谱规模的关键。我们一开始没加这条LLM会把文档里的所有名词都当成实体图谱瞬间膨胀。---图检索增强这是GraphRAG的核心也是和传统RAG最大的区别。我们实现的图检索流程是这样的1. 用户提问后先用LLM做意图理解提取问题中的关键实体2. 在图谱中定位这些实体3. 沿关系遍历找到相关子图4. 把子图结构转化为文本作为上下文喂给LLM这里的关键是第3步遍历多深我们试过遍历两层和三层发现两层效果更好。因为遍历太深会引入噪声而且查询延迟也会增加。def graph_query(graph_db, question_entities, hop2): 在图谱中查询相关子图 results [] for entity in question_entities: # 查询实体的直接关系 query f MATCH (e {{name: $entity}}) CALL {{ WITH e MATCH path (e)-[*1..{hop}]-() RETURN path }} RETURN elements(path) as nodes result graph_db.execute_query(query, entityentity) results.extend(result) return results检索到的子图需要转化为LLM能理解的文本。我们用的是邻接表格式比如Product: API平台 - contains - Module: 网关服务 - depends_on - Module: 认证服务 - depends_on - Module: 限流服务 - contains - Module: 服务注册 - depends_on - Module: 发现服务这种格式保留了结构信息LLM读起来比纯文本更清晰。---评估与优化GraphRAG上线后我们建立了一套评估体系。指标主要有三个1. 检索准确率图谱检索召回的相关子图是否覆盖问题所需信息2. 回答准确率LLM基于检索结果生成的答案是否正确3. 端到端延迟从用户提问到得到答案的总耗时我们收集了200个真实问题作为测试集分别用纯RAG和GraphRAG跑了一遍。结果检索准确率纯RAG 65%GraphRAG 88%回答准确率纯RAG 60%GraphRAG 82%端到端延迟纯RAG 1.2秒GraphRAG 2.8秒延迟增加是GraphRAG的代价但换来的是准确率的显著提升。对于企业知识库这种场景准确性比速度更重要。优化方面我们做了两件事一是缓存热点查询。同一个问题被多次提问时直接返回缓存结果。二是定期更新图谱。文档变更后重新抽取实体关系增量更新图谱。---总结GraphRAG不是银弹但它在处理复杂问答场景时确实有优势。我们的实践经历了几轮迭代最终确定的方案是建模要精准不要大而全抽取要稳定LLM加规则组合检索要可控遍历深度有上限评估要持续用真实问题验证效果从Demo到团队协作最大的挑战不是技术本身而是如何在一个真实的业务场景中权衡各种因素。GraphRAG给了我们一个更好的答案质量但也带来了更高的工程复杂度。值不值取决于你的场景。如果你的知识库问答涉及大量实体关系推理GraphRAG值得尝试。如果只是简单的文档检索传统RAG可能就够了。资料展示下面是我整理的AI大模型学习资料和工具包预览适合收藏后按主题逐步学习。如果你想看完整资料目录可以在评论区留言「资料」也欢迎告诉我你更关注AI大模型里的哪类内容。