资讯详情

资讯详情

LLM+RAG+知识图谱:打造可追溯多跳推理的AI Agent

简介《Building AI Agents with LLMs, RAG, and Knowledge Graphs》是Packt Publishing推出的英文原版技术手册面向具备机器学习与自然语言处理基础、熟悉Python的AI开发者、研究人员和技术负责人。全书聚焦大语言模型、检索增强生成与知识图谱的协同应用系统讲解从Transformer架构、注意力机制到AI Agent“大脑-感知-行动”范式的完整知识链并深入剖析朴素RAG的局限、高级RAG的优化手段分层索引、查询转换、重排序以及模块化RAG设计同时演示如何利用LLM自动构建知识图谱并与Agent集成覆盖低幻觉AI助手、高精度领域问答、复杂推理等典型落地场景还探讨了强化学习在Agent决策优化中的作用。资源为单个PDF文件大小约46.39MB排版清晰、目录完整便携易读。目前已有163人学习下载。读者可借此掌握RAG各阶段优化策略、知识图谱构建流程、HybridRAG融合思路以及强化学习在Agent行为决策中的应用配合GitHub配套代码动手实践能有效提升AI系统从原型到落地的效率。1. 一句话讲透为什么 LLM RAG 还不够非得上知识图谱做了两年多对话式 AI 落地我见过太多翻车案例把企业文档一股脑切块塞进向量库看起来 RAG 链路跑通了一问「这个客户去年跟咱们签过哪几份合同跟哪几个项目有关联」模型瞬间语塞。不是模型不行是纯向量的 RAG 只能回答「哪段文本长得像」回答不了「实体之间有什么关系」。这就是 LLM RAG 知识图谱组合拳的用武之地用 LLM 做理解和生成用 RAG 召回碎片证据用知识图谱把证据串成一张有结构的网让 AI Agent 真正具备多跳推理和可追溯的决策能力。这篇文章不聊学术定义直接用一套可复现的构建方案带你从零搭出这样的 Agent。读者画像先对齐你至少跑通过一个 LangChain 或 LlamaIndex 的 RAG Demo知道 embedding 和向量检索大概是怎么回事。接下来我讲的路径是我在模拟项目X里踩了两个月坑之后的稳定解图谱建什么、怎么跟 RAG 共存、Agent 推理链路怎么设计、哪些参数是玄学、哪些坑是硬伤。2. 架构选型RAG 和知识图谱不是二选一是两条互补的证据链2.1 为什么纯向量检索会在关系类问题上失效先看一个真实场景。一个知识库里有这么两句「A 项目由 B 团队负责B 团队使用了 C 框架」和「C 框架的许可证是 Apache 2.0」。用户问 AgentA 项目能不能商业化纯 RAG 的做法是把问题转成向量召回最相似的文本块。大概率召回到第一句因为「商业化」这个词跟「许可证」没有直接字面关联模型只能对着第一句瞎猜于是输出幻觉。向量检索的本质是字面相似度它不理解「A 项目」和「Apache 2.0」之间隔着一条「B 团队负责了 A 项目又使用了 C 框架」的路径。这不是 embedding 模型不够好而是信息组织形式的问题。文本块是扁平的地毯实体关系是立体的网。地毯上你只能看到局部花纹网里你才能顺着一条边走到下一跳。把 200 篇文档切块后做相似度召回本质上是把所有信息压在了一个平面上丢失了结构。知识图谱解决的正是这个把文档里的实体项目、团队、框架、许可证和关系负责、使用、属于结构化让 Agent 不仅能「找到相似内容」还能「沿着关系走下一步」。RAG 负责广度召回候选图谱负责深度推理两者各管一段。2.2 图谱选型评估Neo4j、原生向量图谱库还是纯内存方案常见做法是 Neo4j 加向量插件这也是社区最成熟的组合。但如果你是个人开发者或者团队规模不大我强烈建议先想清楚量级再选型。三个维度做判断实体数量、关系密度、查询模式。选型维度Neo4j 向量插件原生向量图谱库如某些开源图数据库纯内存方案NetworkX 向量库实体上万级支持但导入慢支持需测试内存吃紧多跳关系查询Cypher 天然合适图查询语言成熟度不一手写 BFS/DFS代码量大跟向量检索融合中等需要自己拼结果好很多库原生支持自己拼逻辑可控部署运维成本高一个独立服务中低进程内搞定适合阶段生产环境、团队协作生产环境、追求一体化原型验证、学习我一般会这样建议先在纯内存方案里把流程跑通验证「图谱到底对你的场景有没有帮助」再决定要不要上重型图数据库。原因很实际——Neo4j 的导入和 schema 设计是有学习曲线的如果一开始连实体类型都没想清楚就建索引后面重构成本极高。2.3 数据流总览文档进来之后发生了什么把构建流程拆成六步后面章节逐一展开文档清洗与切块去掉页眉页脚、目录、重复段落控制块大小在 500 到 1000 字之间。实体抽取用 LLM 从每个文本块里抽出人物、组织、项目、技术、产品等实体。关系抽取抽实体对和关系类型这一步是质量瓶颈需要精心设计 prompt 和校验逻辑。图谱入库把三元组写入图数据库或内存图结构。向量索引原始文本块照常做 embedding 入库和图谱节点通过 chunk_id 关联。Agent 路由问题进来时先判断走 RAG 还是走图谱查询或者两者并行再融合。这套流程里第 2 和第 3 步是黑匣子也是最容易出问题的地方。LLM 抽取结果不稳定同一个实体可能被抽出「B 团队」「B组」「B-Team」三个写法关系类型也可能驴唇不对马嘴。第 5 步经常被忽略很多人建了图谱就忘了把文本块索引留好结果 Agent 查图谱查出实体却拿不到对应的原文证据可追溯性直接没了。下一步就是从零实现这套流程。3. 从零构建知识图谱层实体抽取、关系抽取与入库3.1 用一个可控的数据集避免一上来就失控别拿全量文档直接冲。我第一次就是把 5000 页文档直接丢给 LLM 抽取结果抽取出来的三元组里一半是错的跑完图谱查询发现「财务部」连到「财务部」自己这种自环。正确的做法是先挑 20 到 50 篇代表性文档做一个带标注的小数据集把抽取模板调稳定了再全量铺开。我用的是某跨平台系统的技术支持文档选了三类产品手册、工单记录、版本发布说明。三类文档的实体习惯完全不同——手册里是「模块」「接口」「参数」工单里是「客户」「问题」「解决人」版本说明里是「版本号」「变更项」。如果混在一起抽LLM 容易抽飞。每个文档块我控制在 800 字左右保证一个块内的实体关系基本完整。3.2 实体与关系抽取一个稳定可用的 Prompt 模板这是整个构建流程里最需要反复调的部分。我最终落地的 prompt 不是一句「请抽取实体和关系」而是拆成两步。先抽实体再基于实体抽关系不要一步到位。一步到位的 prompt 看起来省事但实际输出里关系经常引用没有定义过的实体后面对齐工作量更大。第一步实体抽取 promptentity_prompt 你是一个信息抽取专家。请从 包裹的文本中抽取所有业务实体。 实体类型定义 - Person: 人员姓名或角色 - Org: 部门或团队 - Project: 项目代号或名称 - Tech: 技术栈、框架或工具 - Doc: 文档或合同 抽取要求 1. 只用原文中出现的词不要改写或翻译 2. 每个实体输出 JSON 格式包含 name 和 type 两个字段 3. 同一个实体在不同位置出现只保留一个 4. 如果没有实体输出空列表 [] 文本 {chunk_text} /文本 只输出 JSON 数组不要输出任何解释文字。 这个 prompt 有几个关键设计第一明确实体类型LLM 就按这个分类了第二「只用原文中出现的词」堵住了 LLM 自己发挥的毛病第三「只输出 JSON」减少了解析出错率。实际跑下来这个 prompt 的实体抽取准确率在 85% 到 90% 之间剩下的错误集中在实体类型误判上比如把「财务模块」抽成 Tech 而不是 Doc。第二步关系抽取 promptrelation_prompt 你是一个关系抽取专家。给定实体列表和文本找出实体之间在文本中明确表达的关系。 实体列表 {entities_json} 关系类型定义 - responsible_for: A 负责 B人或团队对项目/模块负责 - uses: A 使用 B项目或团队使用某技术/工具 - related_to: A 与 B 相关模糊关联 - part_of: A 是 B 的一部分模块从属关系 抽取要求 1. 仅基于文本中明确陈述的事实不要推测 2. 每个关系输出 JSON 格式包含 source, target, relation, evidence 字段 3. evidence 必须是原文中的原句用于后续追溯 4. 没有明确关系就输出 [] 文本 {chunk_text} /文本 只输出 JSON 数组。 参数说明这里的{chunk_text}和{entities_json}在代码里用字符串格式化填入。我实践中会把实体抽取结果的 JSON 字符串作为entities_json传进去这样 LLM 不会自己造新实体。一个容易被忽视的细节source和target必须严格使用实体列表里的名字这一步靠 prompt 约束不够还要在代码里做一次字符串匹配校验抽出来的关系如果引用了不存在的实体直接丢弃。3.3 合并对齐把「B 团队」和「B组」合并成同一个节点抽取完最头疼的就是实体对齐。LLM 可能把同一个团队抽出「B 团队」「B组」「B-Team」三种写法。这会直接导致图谱查询失败——用户问「B 团队负责了什么」图谱里有「B组」查不到。我用的对齐策略分三层从便宜到贵# 1. 完全一致的实体名直接合并 def merge_exact(entities): seen {} for e in entities: name e[name].strip().lower() if name in seen: e[canonical] seen[name] else: seen[name] e[name] e[canonical] e[name] return entities # 2. 编辑距离小于阈值的候选合并 def merge_fuzzy(entities, threshold2): import difflib result [] for e in entities: for existing in result: if difflib.SequenceMatcher(None, e[canonical], existing[canonical]).ratio() 0.85: e[canonical] existing[canonical] break result.append(e) return result # 3. 同义词映射人工维护一份小词典 alias_map { b团队: B团队, b组: B团队, b-team: B团队 }这三层不是全自动跑的。第一层零成本直接过第二层要抽样看一眼层级多了会产生连锁错误第三层靠业务人员维护只能做高频实体的映射。经验是第二层的阈值不要设太松0.85 已经够用再松会把「前端团队」和「前端框架」合并成同一个实体这种错误比不合并更致命。3.4 入库与索引关联图谱节点和文本块之间必须有桥图谱建完最后一步是入库同时把图谱节点和向量库里的文本块关联起来。这个关联是做可追溯 Agent 的关键。我的做法是给每个实体节点存一个source_chunk_ids属性存的是这个实体出现在哪些文本块里。// Cypher 示例创建实体节点并关联到源文本块 CREATE (e:Entity { name: $name, type: $type, canonical: $canonical, source_chunk_ids: $chunk_ids })逻辑说明$chunk_ids是一个字符串数组存的是该实体出现过的所有 chunk 的 ID。有了这个桥Agent 在图谱里查到一个实体时可以立刻知道这些信息来自哪些原文然后从向量库取回原文内容。很多团队建了图谱却不管这个关联Agent 回答的实体是对的却给不出原文依据等于没有证据链这在企业场景里几乎不可用。source_chunk_ids这个设计同时解决了两个问题一是追溯二是图谱和 RAG 的结果融合。下一步把它接进 Agent 的运行链路里。4. 把 RAG 和知识图谱接进 Agent一个可运行的推理链路4.1 Agent 路由设计先判断问题类型再决定走哪条路有了图谱和向量库两个知识源Agent 面对用户问题时要回答「去哪查」。我的做法是加一层路由用 LLM 判断问题属于三种类型之一事实检索型、关系推理型、混合型。route_prompt 根据用户问题判断应该使用哪种检索策略。 - fact: 问题只涉及单一实体的事实例如「C 框架的许可证是什么」 - relation: 问题涉及多个实体之间的关系或路径例如「A 项目能不能用 C 框架」 - hybrid: 问题既需要事实也需要关系例如「B 团队负责的项目用了哪些框架这些框架的许可证是什么」 只输出一个词fact / relation / hybrid 用户问题{question} 路由命中率不需要做到 100%做得差不多就行因为后面还有个融合校验。实际跑下来单一问题 80% 到 90% 能正确路由复杂问题容易分错这时候就让 hybrid 兜底两条路都走再融合成本高一些但不会漏信息。4.2 图谱查询构建从自然语言到 Cypher 的两种安全姿势这里有两个方案。方案一是让 LLM 直接生成 Cypher 语句灵活但有风险生成的语句可能语法错误或者查询范围失控。方案二是先把自然语言问题转成一个结构化的「查询意图」再用模板拼 Cypher。我两者都写过最终稳妥的组合是能用模板的用模板模板覆盖不了再走 LLM 生成而且生成结果必须经过语法校验。def query_knowledge_graph(question: str, entities: list[str]) - list[dict]: 基于实体列表和图谱结构走模板化查询。 entities 是从问题里抽取出的实体名列表。 # 场景1: 用户问的是某个实体的属性或直接关系 if len(entities) 1: cypher MATCH (e:Entity {canonical: $name})-[r]-(n) RETURN type(r) AS relation, n.canonical AS target LIMIT 20 params {name: entities[0]} # 场景2: 用户问的是两个实体之间的关系路径 elif len(entities) 2: cypher MATCH p shortestPath( (a:Entity {canonical: $first})-[*..3]-(b:Entity {canonical: $second}) ) RETURN p params {first: entities[0], second: entities[1]} else: return [] # 执行查询结果做格式统一 records graph_db.query(cypher, paramsparams) return [{relation: r[relation], target: r[target]} for r in records]逻辑说明[*..3]表示允许最多 3 跳的关系路径——这是一个关键参数。我一开始设成 5 跳结果查出一大堆间接关系Agent 反而不知道哪些是核心答案改成 3 跳后效果好了很多。工业界的实践里这个数字一般在 2 到 4 之间具体看你图谱的关系密度。还要注意这里假设了实体抽取对齐已经完成代码里对entities[0]直接用了 canonical 名称查询如果这一步没做对齐Cypher 返回空结果很多初学者查不出来是因为这个。对比 LLM 直接生成 Cypher 的做法模板法的缺点是覆盖面窄优点是确定性和安全性。生产环境里我强烈建议模板为主、生成为辅生成出来的 Cypher 先跑一个EXPLAIN确认不会全表扫描再执行。4.3 双路召回融合图谱证据和 RAG 证据按什么规则合并图谱查询结果和 RAG 向量检索结果谁先谁后我的答案是图谱结果作为骨架RAG 结果作为补充证据。def fuse_results(graph_hits: list[dict], rag_hits: list[dict]) - str: graph_hits: [{relation: ..., target: ..., source: ...}] rag_hits: [{text: ..., score: 0.92, chunk_id: ...}] # 1. 图谱结果转成自然语言描述作为主答案骨架 graph_lines [] for hit in graph_hits: graph_lines.append( f根据知识图谱{hit[source]} - [{hit[relation]}] - {hit[target]} ) # 2. RAG 结果去重跟图谱里的证据句重复的跳过 unique_rag [] seen_evidence {h.get(source, ) for h in graph_hits} for hit in rag_hits: if hit[text] not in seen_evidence and len(unique_rag) 3: unique_rag.append(hit[text]) # 3. 拼接成最终的 evidence 上下文 context \n.join(graph_lines unique_rag) return context参数说明rag_hits里的score是向量相似度分数我设了 0.75 的阈值低于这个分数的直接丢弃否则噪声会盖过图谱信息。unique_rag限制 3 条是因为 LLM 的上下文窗口有限给太多碎片反而稀释重点。这个融合策略里图谱证据永远是主答案RAG 只是补细节。有个反直觉的经验RAG 检索出来的文本片段跟图谱路径上的证据句重合度越高答案的可靠性越高完全没重合时要警惕图谱里存的信息已经过时。4.4 回答生成把结构化成自然语言并强制引用证据最后一步是把融合后的 context 交给 LLM 生成回答。这里有一个比 prompt 技巧更重要的点强制 LLM 在答案里标注信息来源编号。answer_prompt 你是一个企业知识库智能助手。请根据提供的证据回答问题。 证据 {evidence_with_ids} 要求 1. 回答必须严格基于证据证据中没有的信息不要编造 2. 在句子末尾标注证据编号格式如 [1][2] 3. 如果证据不足以回答直接说「信息不足」不要猜测 用户问题{question} 逻辑说明{evidence_with_ids}是上一步fuse_results的输出但每条证据需要加一个编号前缀。比如[1] 根据知识图谱A项目 - [使用] - C框架。要求 LLM 标注编号是这个方案里最值回票价的设计——看起来只是格式要求实际把幻觉率压下去一大截因为模型知道自己的回答会被编号追溯到证据编造成本变高了。我在模拟项目X里对比过加与不加编号的效果不加时幻觉率在 15% 左右加了之后降到 5% 以内。5. 避坑与排查图谱构建和 Agent 推理的 5 个高频事故现场5.1 实体抽取结果里「张三」和「张总」永远合并不上现象图谱里同一个人的不同称谓抽成了两个节点关系查询时明明张三就是张总图谱却给不出跨称谓的路径。原因实体抽取时 LLM 严格照抄原文没有做归一化。不同文档对同一实体的称谓习惯不同有的用全名有的用角色有的用英文缩写。解决三层方案里加一层「别名扩展」——在做模糊匹配前先用规则把「姓头衔」和「姓名」的格式差异处理掉。我的做法是所有 Person 类型实体统一取姓氏作为对齐候选再人工确认。这个投入很少但对查询体验的提升立竿见影。另外当实体数量超过 5000 时不要全量做两两相似度比对先按类型分桶每个桶里再比对能把计算量降一个数量级。5.2 图谱查询返回空结果但实体确实存在现象Cypher 查不到东西但打开图数据库界面肉眼能看到这个节点。原因八成是实体名对齐没处理好。用户在问题里说的是「前端部门」图库里存的是「前端组」LLM 路由时把用户说法原样传给了查询接口cannonical name 匹配失败。解决查询前加一层「实体归一化」步骤把用户问题里的实体名先映射到图谱里的 canonical 名。我在代码里维护了一张别名表查询时先做映射映射不到再退回模糊匹配。还有两成原因是实体抽取时生成的 canonical 非法比如带着特殊字符或换行符入库前需要做一次字符串清洗。5.3 LLM 生成的 Cypher 查出了莫名其妙的路径现象Agent 回答正确但图查询中间结果里出现大量不相关节点执行耗时 5 秒以上。原因短路径查询时跳数设置过大我在 4.2 节提到过[*..3]和[*..5]的耗时差距是几何级的。更大的坑是查询条件里没限制实体类型导致同名不同类的节点被卷进来。解决两个措施。第一路径深度从 2 开始逐步加稳定在 3 就不用再加了。第二Cypher 里把类型条件写死比如(a:Entity {canonical: $first, type: Project})不要让图谱在错误的方向上扩展。排查时用EXPLAIN看查询计划直观看到哪些部分缩放严重。5.4 RAG 召回的高分文本和图谱证据打架现象图谱说「A 项目用的是 C 框架」RAG 召回的文档里却写着「A 项目弃用了 C 框架改用 D 框架」。Agent 融合时两个证据都给了回答自相矛盾。原因知识库的时间线问题。旧文档和新文档的结论不一致纯 RAG 按相似度召回容易把两边的文本都捞出来而图谱里存的是旧关系。解决这个坑没有全自动解法只能从机制上降损。我的做法是给每个文本块加一个timestamp或version属性融合时优先采用时间较近的证据并在回答里主动标注「根据某年某月文档」。图谱构建流程里如果有增量更新机制旧关系要标记为deprecated不要直接删除保留变更记录。优先级顺序是时间新的文档 人工维护的图谱关系 旧的 RAG 证据。5.5 图谱构建耗时太长增量更新变成全量重建现象文档库每周新增 200 篇图谱重建一次要跑 4 个小时LLM 调用成本也扛不住。原因全量重建的设计每次把所有文档重新走一遍实体抽取。这一步是 LLM 调用成本和耗时都跟文档总量线性相关。解决改成增量更新——每次只对新文档做实体抽取然后跟已有的图谱实体做「增量对齐」新实体的节点和关系直接追加已有实体只补新的source_chunk_ids。这里有一个关键优化实体对齐时不需要把所有已有实体都列出来只把新抽取实体跟高频实体子集做比对就够了低频实体大概率不会跟新文档产生关联。我把这个子集规模控制在 2000 以内对齐耗时缩短了 80%。还有一个实践细节实体抽取结果要做缓存同一个文本块如果没改过不要重复调用 LLM。6. 用一页报告验证 Agent 质量再决定要不要调 prompt最终章我不写总结写一个验证方法。很多团队的 Agent 上线后处于「凭感觉调 prompt」的状态这不行。我用的验证思路是「证据覆盖率」加「路径合理性」两个指标都不需要人工读太多答案。第一个指标证据覆盖率。拿 100 个真实业务问题跑完整链路统计最终回答里「引用了哪几号证据」。理想情况是图谱证据和 RAG 证据各占一定比例并且图谱证据占主的比例应该超过 60%。如果 RAG 证据占了 80% 以上说明你的图谱根本没起到推理作用问题可能出在实体抽取的召回率上。我用一个脚本自动统计这个分布每周跑一次观察趋势。第二个指标路径合理性。针对典型的「多跳问题」把图谱查询出来的路径打印出来人工看一遍是不是符合直觉。比如问「A 项目能不能商业化」期望路径是A项目 - [使用] - C框架 - [属于] - Apache 2.0如果路径变成A项目 - [负责] - B团队 - [使用] - C框架 - [存在] - Linux说明图谱里混入了错误关系。这个检查每周做 20 条就够了目的是抓抽取质量波动。一个值得投入的进阶技巧做一套「质量回归集」。把过去发现的所有必现错误做成一个自动化测试集合每次修改 prompt、调整抽取模板、换 embedding 模型之前先在回归集上跑一遍指标不降级才允许动线上链路。这个习惯救过我好几次——有一次我把实体抽取 prompt 里的实体类型定义顺序改了一下核心场景没出问题但边缘实体识别率降了 12%要不是回归集先跑出来线上就翻车了。希望这个验证习惯能帮到你也愿你别踩我踩过的那些坑。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →