news 2026/6/8 14:52:32

GraphRAG 入门:知识图谱增强检索,比向量搜索多想一层

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
GraphRAG 入门:知识图谱增强检索,比向量搜索多想一层

🦞 一只用 AI Agent 搭副业产线的程序员


先给你一个场景。

技术文档库里有三篇文档:

  • 文档 A:「支付服务依赖库存服务的 /api/stock/check 接口」
  • 文档 B:「库存服务在 v2.3 版本中将 /api/stock/check 迁移为 /api/inventory/verify」
  • 文档 C:「支付服务已更新,调用路径改为 /api/inventory/verify」

你用向量搜索问:「支付服务依赖库存服务的哪个接口?」

向量搜索会返回文档 A(关键词匹配最多)。但正确回答应该是「历史依赖 /api/stock/check,当前依赖 /api/inventory/verify」——需要综合文档 A、B、C 的关系才能推出来。

向量搜索看的是语义相似度。GraphRAG 看的是实体关系。当知识之间有明显的关联(依赖、调用、版本变更、父子关系),GraphRAG 能帮 LLM 多想一层。


GraphRAG 是什么

一句话:先用 LLM 从文档里提取实体和关系,建一个迷你知识图谱。查询时同时从向量库和图谱检索,让 LLM 看到更多上下文。

流程拆开:

文档 → LLM 提取实体(服务名、接口、版本号...) → LLM 提取关系(依赖、迁移、实现...) → 写入 Neo4j 图数据库 查询时: → 向量搜索找相关文档 → Cypher 查询找图谱中的关联实体 → 两者合并 → LLM 回答

第一步:从文档中提取实体和关系

// internal/graph/extractor.gopackagegraphimport("encoding/json""fmt")typeEntitystruct{Namestring`json:"name"`Typestring`json:"type"`// Service, API, Database, Config...Descstring`json:"desc"`}typeRelationstruct{Fromstring`json:"from"`// 源实体名称Tostring`json:"to"`// 目标实体名称Typestring`json:"type"`// depends_on, calls, implements...Descstring`json:"desc"`}typeExtractionResultstruct{Entities[]Entity`json:"entities"`Relations[]Relation`json:"relations"`}funcExtractEntitiesAndRelations(llm*llm.Client,documentstring,)(*ExtractionResult,error){prompt:=fmt.Sprintf(`从以下技术文档中提取所有实体和关系。 实体类型:Service, API, Database, Config, ErrorCode 关系类型:depends_on(依赖), calls(调用), implements(实现), migrates_to(迁移) 返回 JSON 格式: { "entities": [ {"name": "实体名", "type": "类型", "desc": "一句话描述"} ], "relations": [ {"from": "源实体", "to": "目标实体", "type": "关系类型", "desc": "描述"} ] } 只返回 JSON,不要其他内容。 文档: %s`,document,)response,_:=llm.Chat([]llm.Message{{Role:"user",Content:prompt},},0.0,500)// 清理 markdown 包裹response=strings.TrimSpace(response)response=strings.TrimPrefix(response,"```json")response=strings.TrimPrefix(response,"```")response=strings.TrimSuffix(response,"```")varresult ExtractionResultiferr:=json.Unmarshal([]byte(response),&result);err!=nil{returnnil,fmt.Errorf("解析实体失败: %w",err)}return&result,nil}

用 LLM 从文档中提取结构化信息。对技术文档来说,准确率很高——技术文档的实体和关系通常很明确。


第二步:写入 Neo4j

// internal/graph/neo4j.gopackagegraphimport("fmt""github.com/neo4j/neo4j-go-driver/v5/neo4j")typeGraphStorestruct{driver neo4j.DriverWithContext}funcNewGraphStore(uri,user,passwordstring)(*GraphStore,error){driver,err:=neo4j.NewDriverWithContext(uri,neo4j.BasicAuth(user,password,""))iferr!=nil{returnnil,err}return&GraphStore{driver:driver},nil}func(g*GraphStore)UpsertEntitiesAndRelations(result*ExtractionResult,)error{ctx:=context.Background()session:=g.driver.NewSession(ctx,neo4j.SessionConfig{})defersession.Close(ctx)_,err:=session.ExecuteWrite(ctx,func(tx neo4j.ManagedTransaction)(any,error){// 创建实体节点for_,entity:=rangeresult.Entities{query:=fmt.Sprintf(`MERGE (e:%s {name: $name}) SET e.desc = $desc`,entity.Type,)_,err:=tx.Run(ctx,query,map[string]any{"name":entity.Name,"desc":entity.Desc,})iferr!=nil{returnnil,err}}// 创建关系for_,rel:=rangeresult.Relations{query:=fmt.Sprintf(`MATCH (a {name: $from}) MATCH (b {name: $to}) MERGE (a)-[r:%s]->(b) SET r.desc = $desc`,rel.Type,)_,err:=tx.Run(ctx,query,map[string]any{"from":rel.From,"to":rel.To,"desc":rel.Desc,})iferr!=nil{returnnil,err}}returnnil,nil})returnerr}

核心 Cypher 操作:

  • MERGE创建节点,%s动态设置 label(Service、API 等类型)
  • CREATE创建关系,关系类型也是动态的(depends_on、calls 等)

第三步:查询时结合图谱

// internal/graph/querier.gopackagegraphfunc(g*GraphStore)QueryRelatedEntities(entityNamestring,depthint,)([]GraphContext,error){ctx:=context.Background()session:=g.driver.NewSession(ctx,neo4j.SessionConfig{})defersession.Close(ctx)query:=fmt.Sprintf(` MATCH (e {name: $name})-[r*1..%d]-(related) RETURN e.name AS source, type(r[0]) AS relation, related.name AS target, related.desc AS desc LIMIT 10 `,depth)result,err:=session.Run(ctx,query,map[string]any{"name":entityName,})iferr!=nil{returnnil,err}varcontexts[]GraphContextforresult.Next(ctx){record:=result.Record()source,_:=record.Get("source")rel,_:=record.Get("relation")target,_:=record.Get("target")desc,_:=record.Get("desc")contexts=append(contexts,GraphContext{SourceEntity:source.(string),Relation:rel.(string),TargetEntity:target.(string),Description:fmt.Sprintf("%v",desc),})}returncontexts,nil}typeGraphContextstruct{SourceEntitystringRelationstringTargetEntitystringDescriptionstring}

这个查询找出与指定实体有关系的所有实体(深度可配,一般 depth=2 就够)。


第四步:合并图谱结果到 RAG Prompt

funcBuildGraphRAGPrompt(querystring,vectorDocs[]SearchResult,graphContexts[]GraphContext,)[]Message{// 常规 RAG 的文档上下文vardocBlocks[]stringfori,doc:=rangevectorDocs{docBlocks=append(docBlocks,fmt.Sprintf("[文档%d] %s",i+1,doc.Text))}// 图谱上下文——告诉 LLM 实体之间的关系vargraphBlocks[]stringfor_,gc:=rangegraphContexts{graphBlocks=append(graphBlocks,fmt.Sprintf("[关系] %s --[%s]--> %s (%s)",gc.SourceEntity,gc.Relation,gc.TargetEntity,gc.Description))}systemPrompt:=`你是技术文档助手。你会同时收到两部分信息: 1. 文档片段(语义检索结果) 2. 实体关系图(知识图谱) 请综合两部分信息回答。当文档之间有矛盾时, 基于关系图判断最新状态。`userPrompt:=fmt.Sprintf("文档上下文:\n%s\n\n知识关系图:\n%s\n\n问题:%s\n\n"+"请综合以上信息回答。如果关系图揭示了文档中未显式提及的联系,"+"请明确指出。",strings.Join(docBlocks,"\n\n"),strings.Join(graphBlocks,"\n"),query,)return[]Message{{Role:"system",Content:systemPrompt},{Role:"user",Content:userPrompt},}}

GraphRAG vs 普通 RAG:什么场景选哪个

场景普通RAGGraphRAG原因
FAQ / 技术文档问答问题-答案是一对一映射
服务依赖分析需要追踪多个实体的关系链
故障根因分析需要沿调用链追溯
API 版本变更影响分析依赖关系 + 迁移路径
代码库架构理解类/模块关系天然是图

经验法则:如果你搜索时经常要问「这个东西跟什么有关联」「改了它会影响什么」——那就是 GraphRAG 的场景。

GraphRAG 的成本更高(建图要调 LLM + 维护 Neo4j),不适合用 GraphRAG 解决所有问题。把它当 RAG 的增强插件——只在需要关系推理时启用。


最小部署:Neo4j Docker

# docker-compose.ymlservices:neo4j:image:neo4j:5ports:-"7474:7474"# HTTP-"7687:7687"# Boltenvironment:-NEO4J_AUTH=neo4j/password123volumes:-neo4j_data:/data
dockercompose up-d# 浏览器打开 http://localhost:7474 就能看到图谱可视化

启动后,你可以在 Neo4j Browser 里跑测试 Cypher 语句,亲眼看到构建的实体关系图——这对理解 GraphRAG 非常有帮助。


本篇核心收获

GraphRAG 不是替代 RAG,是补充 RAG。当文档之间存在显式的依赖、调用、版本关系时,知识图谱能提供向量搜索看不到的结构化上下文。但如果不是这类场景,就别折腾了——维护图谱的成本不低。

下一篇我们回到 RAG 本身——怎么评估你的 RAG 好不好用?三个指标 + 自动化测试脚本,用数据说话。

关注我,别错过。


🦞 一只用 AI Agent 搭副业产线的程序员

全平台同名:虾哥不加班
需要定制 AI 工具?来聊聊 → lob_ai

源码:GitHub - lobster-bujiaban/rag-from-scratch

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/8 14:51:23

软件模拟I2C:无硬件模块时驱动I2C外设的完整实现方案

1. 项目概述与核心价值在嵌入式开发中,I2C总线因其简洁的两线制(SCL时钟线和SDA数据线)和强大的多主多从支持能力,成为了连接传感器、存储器、转换器等外设的首选协议之一。然而,并非所有的微控制器(MCU&am…

作者头像 李华
网站建设 2026/6/8 14:50:13

最佳 7 家公司数据提供商:全面指南

最佳 7 家公司数据提供商:全面指南 在本文中,我将讨论当今一些最好的公司数据提供商。我会拆解每家供应商的功能、定价,以及它们最适合哪些类型的企业。我们的机构使用过所有这些提供商,因此我们可以概述每家的优缺点。 2025 年…

作者头像 李华
网站建设 2026/6/8 14:46:59

TPU硬件解码单相霍尔信号:原理、配置与电机控制实践

1. 项目概述:单相霍尔解码与TPU的硬核协同在电机控制、转速测量或者任何需要精确感知旋转机械位置的嵌入式系统里,霍尔传感器是个绕不开的元件。它像个沉默的哨兵,通过磁场变化输出一个个方波脉冲,告诉我们转子此刻经过了哪个位置…

作者头像 李华
网站建设 2026/6/8 14:46:54

Rust模块系统与crate发布实践:从私有项目到开源分享

Rust模块系统与crate发布实践:从私有项目到开源分享一、模块系统的困惑:mod、use、pub到底怎么组织 Rust的模块系统是我学Rust时最困惑的部分之一——不是概念难,而是"怎么做"不清晰。mod.rs和文件名的关系、use的路径规则、pub的可…

作者头像 李华