WeKnora 知识图谱(GraphRAG)功能深度解析:从 Neo4j 配置到实体关系抽取与图谱增强检索
【免费下载链接】WeKnoraOpen-source LLM knowledge platform: turn raw documents into a queryable RAG, an autonomous reasoning agent, and a self-maintaining Wiki.项目地址: https://gitcode.com/GitHub_Trending/we/WeKnora
导读
本文基于 WeKnora 开源仓库的官方特性文档,系统讲解知识图谱(Knowledge Graph / GraphRAG)功能的完整技术脉络:如何通过两级开关启用该能力、文档入库时如何借助 LLM 抽取实体与关系并写入 Neo4j、问答时又如何沿"实体 → 关联片段"补充关系上下文,与向量检索、关键词检索协同工作。读完本文,你将掌握 WeKnora 知识图谱的配置方法、构建与检索两条核心链路、Neo4j 存储层的底层实现,以及 Agent 模式下query_knowledge_graph工具的使用方式。
知识图谱在文档入库时提取实体与关系,并在问答时沿关联关系检索更多相关片段,可与向量和关键词检索共同使用,为回答补充关系上下文。该功能适用于人物、组织、产品或条款之间关系较多的资料;启用后会增加入库阶段的模型调用,并需要部署 Neo4j。
一、功能概述与适用场景
WeKnora 的知识图谱功能解决的是传统 RAG 的"关系盲区"问题:向量检索擅长语义相似匹配,关键词检索擅长字面命中,但两者都无法直接回答"A 与 B 是什么关系""谁影响了谁"这类需要跨片段串联的问题。图谱功能在文档入库时将散落在各个 chunk 中的实体与关系结构化抽取出来存入图数据库,查询阶段再从用户问题中抽取实体、沿图的一跳邻居关系召回关联片段,从而为生成答案补充关系上下文。
适用场景包括:
- 人物关系密集的资料:如传记、组织架构、合作网络;
- 产品与条款关联:如技术文档中多个产品版本、API 模块之间的依赖与从属关系;
- 跨文档聚合:同一实体出现在多个知识库/多个文件中时,通过
chunks属性实现"一实体多出处"的聚合召回。
需要明确的是,启用图谱是有成本的:入库阶段每个文本 chunk 都会触发一次额外的 LLM 抽取调用(源码注释称之为"管线中最昂贵的增强扇出"),同时必须部署 Neo4j 数据库(依赖 APOC 插件)。
二、开启配置:两级开关
图谱功能需要两级开关同时满足才会真正生效——全局的 Neo4j 环境变量开关 + 知识库级别的索引策略开关。
2.1 全局开关:Neo4j 环境变量
NEO4J_ENABLE是知识图谱的唯一全局开关。需要注意版本演进:docker-compose.yml注释明确说明ENABLE_GRAPH_RAG自 v0.1.6 起已被NEO4J_ENABLE取代,Go 主应用不再读取旧变量。
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
NEO4J_ENABLE | string | 空(关闭) | 置为true启用图谱;container.go 的initNeo4jClient与任务入队、检索管线都会检查它 |
NEO4J_URI | string | bolt://neo4j:7687 | Neo4j 连接地址(Bolt 协议) |
NEO4J_USERNAME | string | neo4j | 用户名 |
NEO4J_PASSWORD | string | password | 密码 |
源码层面的两个关键实现细节:
- 启动重试与降级:
initNeo4jClient在NEO4J_ENABLE != "true"时直接返回nildriver(不报错),此时Neo4jRepository的所有方法降级为 no-op,仅打印NOT SUPPORT RETRIEVE GRAPH日志;启用时最多重试 30 次(每次间隔 2s)创建 driver 并调用VerifyAuthentication验证连接。 - 状态上报:
GET /system信息接口通过 system.go 的getGraphDatabaseEngine()报告当前图数据库引擎为"Neo4j"或"Not Enabled",便于运维确认开关是否真正生效。
docker-compose 的neo4j服务预装 APOC 插件(NEO4JLABS_PLUGINS=["apoc"]):图谱写入依赖apoc.merge.node/apoc.merge.relationship,删除依赖apoc.periodic.iterate。
2.2 知识库级开关:IndexingStrategy + ExtractConfig
全局开关之外,每个知识库还需在索引策略中打开图谱抽取。核心判断逻辑位于 internal/types/knowledgebase.go:
// IsGraphEnabled checks if knowledge graph extraction is enabled. // Requires both the IndexingStrategy flag and a valid ExtractConfig. func (kb *KnowledgeBase) IsGraphEnabled() bool { return kb != nil && kb.IndexingStrategy.GraphEnabled && kb.ExtractConfig != nil && kb.ExtractConfig.Enabled }两个组成部分:
IndexingStrategy.GraphEnabled(internal/types/indexing_strategy.go):知识库索引策略中的图谱开关,字段名为graph_enabled,默认false;旧字段ExtractConfig.Enabled会在读取时向IndexingStrategy.GraphEnabled单向同步(legacy sync),保证存量配置平滑迁移。ExtractConfig(internal/types/knowledgebase.go):承载抽取的 few-shot 配置,各字段含义如下:
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
enabled | bool | false | 是否启用抽取 |
text | string | 空 | few-shot 示例原文 |
tags | []string | nil | 关系类型标签集合 |
nodes | []*GraphNode | nil | 示例实体节点(name / attributes) |
relations | []*GraphRelation | nil | 示例关系(node1 / node2 / type) |
custom_instructions | string | 空 | 领域自定义抽取指令(追加进系统提示,结构化输出协议仍由系统控制) |
2.3 配置向导辅助 API
为帮助用户快速搭建ExtractConfig,initialization.go(路由注册见 router.go)提供了三个辅助接口:
POST /initialization/extract/text-relation(ExtractTextRelations):对一段文本(≤5000 字符)按选定标签试跑关系抽取,用于预览效果;POST /initialization/extract/fabri-text(FabriText):让 LLM 生成示例文本,辅助填充text字段;POST /initialization/extract/fabri-tag(FabriTag):让 LLM 推荐标签,辅助填充tags字段。
三、实体关系抽取流程(图谱构建)
3.1 触发与任务编排
文档解析完成后,knowledge_post_process.go 在增强扇出阶段对每个文本 chunk 计数(启用图谱时graphChunkCount = len(textChunks)),并调用 extract.go 的NewChunkExtractTask逐 chunk 入队:
func NewChunkExtractTask(...) (bool, error) { if strings.ToLower(os.Getenv("NEO4J_ENABLE")) != "true" { logger.Warn(ctx, "NEO4J is not enabled, skip chunk extract task") return false, nil } ... task := asynq.NewTask(types.TypeChunkExtract, payload, asynq.Queue(types.QueueGraph), asynq.MaxRetry(3), asynq.Timeout(30*time.Minute)) ... }关键工程细节:
- 任务走独立的 asynq
QueueGraph队列,与向量化、摘要等队列隔离; - 每个任务
MaxRetry=3、Timeout=30min,即单 chunk 的抽取有三次重试机会,超时上限半小时; - 当
NEO4J_ENABLE未开启时入队函数返回(false, nil),调用方必须释放已占用的pending_subtasks_count计数,否则父知识会永远停留在 "finalizing" 状态(源码注释明确警告了这一死锁风险); - 被取消、被删除或被新解析尝试取代(
attemptSuperseded)的任务会跳过执行并释放父任务的子任务计数; - 每个 chunk 一次 LLM 调用,受模型级后台并发限流(limiter)约束,避免打爆模型服务。
3.2 抽取执行(ChunkExtractService.Handle)
internal/application/service/extract.go中ChunkExtractService.Handle的执行流程分为五步:
- 加载上下文:加载 chunk、知识库与文件级
ProcessOverrides,用ResolveProcessConfig求出生效的ExtractConfig(未启用则跳过该 chunk)。 - 组装结构化提示:系统协议部分来自
config.ExtractManager.ExtractGraph(即 config.yaml 的extract.extract_graph),它是一个分步指令——先"实体抽取 + 属性丰富",再"关系抽取与验证"(关系类型只能从指定列表%s中选择),随后叠加知识库的custom_instructions、tags与ExtractConfig的 few-shot 示例(Text/Nodes/Relations)。 - 调用模型并解析:
chatpipeline.NewExtractor(chatModel, template).Extract(ctx, chunk.Content)调用 Chat 模型,参数为temperature 0.3、max_tokens 4096、关闭 thinking,随后由Formater.ParseGraph解析为types.GraphData(internal/types/extract_graph.go):
type GraphNode struct { Name string `json:"name,omitempty"` Chunks []string `json:"chunks,omitempty"` Attributes []string `json:"attributes,omitempty"` } type GraphRelation struct { Node1 string `json:"node1,omitempty"` Node2 string `json:"node2,omitempty"` Type string `json:"type,omitempty"` }- 回填与写入:为每个节点回填
node.Chunks = []string{chunk.ID},随后调用graphEngine.AddGraph(ctx, NameSpace{KnowledgeBase, Knowledge}, ...)写入 Neo4j——这样每个实体都记录了它"出自哪个 chunk",为查询阶段的关联召回奠定基础。 - 可观测性:全程由 SpanTracker 追踪,产生
postprocess.graph.chunk[i]子 span,记录 nodes/relations 数量与样例,便于在 Langfuse 等追踪平台排障。
3.3 存储后端:Neo4j 仓库实现
internal/application/repository/retriever/neo4j/repository.go 实现interfaces.RetrieveGraphRepository(AddGraph/DelGraph/SearchNode三个方法),几个关键设计:
- 命名空间即标签:
NameSpace{KnowledgeBase, Knowledge}映射为节点标签ENTITY<kb_id>、ENTITY<knowledge_id>(连字符替换为下划线),即实体在库中被双层标签命名空间隔离,不同知识库/不同文件的同名实体可共存; - 节点属性:含
name(实体名)、kg(knowledge_id)、attributes(属性列表)、chunks(来源 chunk ID 列表); - 幂等写入:用 APOC 做合并写入,同名实体的
chunks取并集,重复解析同一文档不会产生重复节点:
UNWIND $data AS row CALL apoc.merge.node(row.labels, {name: row.name, kg: row.knowledge_id}, row.props, {}) YIELD node SET node.chunks = apoc.coll.union(node.chunks, row.chunks)- 级联删除:删除知识/知识库时(
knowledge_delete.go、knowledgebase.go)调用DelGraph,用apoc.periodic.iterate按 1000 批并行删除边与点,保证大图删除时不会拖垮事务。
四、检索时的图谱增强(GraphRAG)
图谱的价值体现在查询阶段。传统聊天管线(internal/application/service/chat_pipeline)中挂载了两个插件完成"查询实体抽取 + 图谱关联召回"。
4.1 PluginExtractEntity:从用户查询中抽取实体
extract_entity.go挂在QUERY_UNDERSTAND事件上。NEO4J_ENABLE=true时,它先筛选出ExtractConfig.Enabled的知识库(存入chatManage.EntityKBIDs/EntityKnowledge),再使用ExtractManager.ExtractEntity模板(config.yaml 的extract.extract_entity,包含"分析逻辑连接 → 提取关键实体 → 按关联紧密程度排序"三步指令)+ Chat 模型,从用户查询中抽取实体名列表,存入chatManage.Entity。
4.2 PluginSearchEntity:图谱关联召回
search_entity.go挂在ENTITY_SEARCH事件上,执行"实体 → 关联 chunk"的补充召回:
- 对每个启用图谱的知识库/文件并行调用
graphRepo.SearchNode; - 底层 Cypher 用
n.name CONTAINS nodeText做实体名的模糊匹配,并返回实体的一跳邻居与关系(MATCH (n)-[r]-(m) WHERE ANY(...) RETURN n, r, m),合并为chatManage.GraphResult; filterSeenChunk取出图谱节点携带的chunks,去掉向量检索已命中的部分,避免重复召回;- 从
chunkRepo拉取对应原文并转换为SearchResult并入候选集。
最终图谱召回的片段与向量、关键词结果一起进入重排与生成阶段,为回答提供关系上下文。
4.3 Agent 模式的 query_knowledge_graph 工具
Agent 模式下,WeKnora 提供query_knowledge_graph工具(internal/agent/tools/query_knowledge_graph.go):
- 校验各知识库是否配置了图谱(
ExtractConfig.Nodes/Relations非空,见Execute中 L177 附近的判定); - 并发对多个知识库执行检索,按 chunk 去重排序;
- 输出中附带各库的图谱配置状态(
graph_configs字段,包含实体类型 / 关系类型清单,由summarizeGraphConfig/uniqueSortedNodeNames等辅助函数生成); - 未配置图谱的知识库回落为普通混合检索结果,保证 Agent 链路不中断。
五、构建与查询流程图
5.1 构建流程
5.2 查询流程
六、可视化与相关实现边界
关于图谱可视化,需要厘清几个容易混淆的实现:
- Mermaid 图生成:internal/application/service/graph.go 的
graphBuilder是types.GraphBuilder接口的内存版实现(LLM 抽实体 → 抽关系 → 按 PMI×0.6 + Strength×0.4 计算关系权重并归一到 1-10 → 计算实体度数 → 构建 chunk 关联图),其generateKnowledgeGraphDiagram用 DFS 找连通分量并输出 Mermaidgraph TD子图(高频实体高亮、强度 >7 的关系用粗箭头)。注意:NewGraphBuilder目前没有被容器装配调用(仓库内无其他引用),属于独立/遗留的图构建与可视化实现,生成的 Mermaid 图输出到日志。 - prompt 模板:config/prompt_templates/graph_extraction.yaml 提供
default_extract_entities等模板(含实体类型枚举 Person/Organization/Location/... 与 JSON 输出协议),经 internal/config/config.go 的extract_entities_prompt_id/extract_relationships_prompt_id解析进Conversation.ExtractEntitiesPrompt/ExtractRelationshipsPrompt,供上述内存版graphBuilder使用;生产异步抽取路径使用的是 config.yaml 中extract.extract_graph/extract.extract_entity模板(ExtractManagerConfig),两者不要混淆。 - 对外 API:知识图谱本身没有专门的可视化 REST 端点;
query_knowledge_graph工具的结构化输出(graph_configs、结果列表)供 Agent 前端渲染。GET /wiki/graph(wikiHandler.GetGraph)是 Wiki 功能自己的图接口,与本文的实体关系图谱无关。
七、排查要点速查
| 现象 | 排查方向 |
|---|---|
| 入库后图谱无数据 | 确认两级开关:NEO4J_ENABLE=true且知识库IndexingStrategy.GraphEnabled+ExtractConfig.Enabled同时为真;检查GET /system返回的图引擎是否"Neo4j" |
| 文档卡在 finalizing | 检查任务是否因NEO4J_ENABLE未开启而未被入队,父任务的pending_subtasks_count计数是否被正确释放 |
| 查询不返回图谱结果 | 确认查询实体是否能被name CONTAINS模糊命中;确认该知识库ExtractConfig.Enabled为真 |
| Neo4j 连接失败 | 启动日志查看initNeo4jClient的重试输出,确认NEO4J_URI/账号密码正确、APOC 插件已启用 |
结语
WeKnora 的知识图谱功能是一套完整的"构建-存储-检索"闭环:以两级开关精确控制启用范围,以独立 asynq 队列承载高成本的 LLM 抽取,以 Neo4j + APOC 实现幂等的图写入与批量删除,最终在查询阶段通过实体抽取与一跳邻居召回为 RAG 答案补充关系上下文。对于人物、组织、产品、条款间关系密集的知识库,这套能力与向量、关键词检索形成了互补的三角检索格局。相关源码可继续深入:存储层 neo4j/repository.go、任务编排 extract.go、容器装配 container.go、Agent 工具 query_knowledge_graph.go。
【免费下载链接】WeKnoraOpen-source LLM knowledge platform: turn raw documents into a queryable RAG, an autonomous reasoning agent, and a self-maintaining Wiki.项目地址: https://gitcode.com/GitHub_Trending/we/WeKnora
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考