进阶 B · Graph RAG:知识图谱增强检索
当知识之间存在关联("A 依赖 B"、"X 是 Y 的子模块"),传统 RAG 无法捕捉这些关系。Graph RAG 用知识图谱把知识织成网。
1. 传统 RAG 的盲区
arduino
问题:"项目中哪些模块依赖了 userService?"
传统 RAG:检索到 3 个提到 userService 的文档 Chunk
实际需要:遍历 import 图,找出所有直接/间接引用 userService 的文件 → 可能有 15 个
传统 RAG 只能检索"文本相似"的内容,无法理解"关系"。Graph RAG 弥补了这个缺陷。
2. Graph RAG 架构
markdown
文档摄入
│
├──→ 文本分块 → 向量存储(传统 RAG)
│
└──→ 实体关系抽取 → 知识图谱(Neo4j / NebulaGraph)
│
▼
图谱检索
│
▼
检索结果融合 → LLM 生成
2.1 实体关系抽取(Node.js)
csharp
// graph/extract.ts
import { ChatOpenAI } from "@langchain/openai";
interface EntityRelation {
entities: { name: string; type: string }[];
relations: { from: string; to: string; type: string }[];
}
class GraphExtractor {
private llm = new ChatOpenAI({ model: "gpt-4o", temperature: 0 });
async extractFromDocument(doc: string): Promise<EntityRelation> {
const response = await this.llm.invoke(`
从以下文档中提取实体和关系,以 JSON 返回。
实体类型:模块、组件、函数、API、配置项
关系类型:depends_on, contains, calls, configures
文档:
${doc}
返回格式:
{
"entities": [{"name": "名称", "type": "类型"}],
"relations": [{"from": "实体A", "to": "实体B", "type": "关系类型"}]
}
`);
return JSON.parse(response.content as string);
}
}
2.2 存入 Neo4j
php
// graph/store.ts
import neo4j from "neo4j-driver";
const driver = neo4j.driver(
"bolt://localhost:7687",
neo4j.auth.basic("neo4j", "password")
);
class GraphStore {
async insert(entityRelation: EntityRelation): Promise<void> {
const session = driver.session();
try {
// 创建节点
for (const entity of entityRelation.entities) {
await session.run(
`MERGE (n:Entity {name: $name}) SET n.type = $type`,
entity
);
}
// 创建关系
for (const rel of entityRelation.relations) {
await session.run(
`MATCH (a:Entity {name: $from}), (b:Entity {name: $to})
MERGE (a)-[r:${rel.type}]->(b)`,
rel
);
}
} finally {
await session.close();
}
}
}
2.3 图谱检索
vbnet
// graph/search.ts
class GraphRetriever {
async search(query: string): Promise<GraphResult> {
const session = driver.session();
try {
// 1. 找出与查询相关的实体
const entities = await session.run(`
MATCH (n:Entity)
WHERE n.name CONTAINS $query
RETURN n.name, n.type
LIMIT 10
`, { query });
const entityNames = entities.records.map(r => r.get("n.name"));
// 2. 查找 1 跳邻居关系(直接依赖/引用)
const neighbors = await session.run(`
MATCH (n:Entity)-[r]->(m:Entity)
WHERE n.name IN $names
RETURN n.name AS source, type(r) AS relation, m.name AS target
LIMIT 50
`, { names: entityNames });
return {
entities: entityNames,
relations: neighbors.records.map(r => ({
from: r.get("source"),
relation: r.get("relation"),
to: r.get("target"),
})),
};
} finally {
await session.close();
}
}
}
3. 与文本 RAG 融合
javascript
// graph/fusion.ts
async function graphEnhancedRAG(query: string) {
// 1. 文本 RAG 检索
const textResults = await textVectorStore.similaritySearch(query, 5);
// 2. 图谱检索
const graphResults = await graphRetriever.search(query);
// 3. 融合生成
const prompt = `
## 相关文档
${textResults.map(d => d.pageContent).join("\n\n")}
## 知识关联
${graphResults.relations.map(r => `"${r.from}" --[${r.relation}]--> "${r.to}"`).join("\n")}
## 问题
${query}
请结合文档内容和知识关联来回答。如果知识关联揭示了文档中不明显的依赖/module关系,请特别指出。
`;
return llm.invoke(prompt);
}
4. 适用场景与局限
| 场景 | 传统 RAG | +Graph RAG |
|---|---|---|
| "React.memo 怎么用?" | ✅ | 没帮助 |
| "哪些模块依赖了 AuthService?" | ⚠️ 可能漏 | ✅ 精确遍历 |
| "支付流程涉及哪些服务和中间件?" | 能搜到部分 | ✅ 完整链路 |
| "这个 Bug 可能影响哪些模块?" | 粗糙 | ✅ 追踪调用链 |
建议:
- Graph RAG 是传统 RAG 的增强,不是替代
- 知识关系密集的场景(代码库、微服务文档、API 文档)收益最大
- 需要额外维护 Neo4j 实例和抽取流水线,投入产出需评估