端到端的 Text2Cypher:使用 llamaIndex 和 Neo4j 为知识图谱构建自然语言查询引擎
本教程介绍如何搭建一个基于LlamaIndex和Neo4j的端到端知识图谱系统,支持文档上传、三元组抽取、图数据库存储及自然语言问答功能。系统采用FastAPI后端,结合LlamaIndex的SchemaLLMPathExtractor进行实体关系抽取,并通过PropertyGraphIndex将数据持久化到Neo4j。前端提供简单的上传和问答界面,支持多格式文档解析。教程详细说明了架构设计、核心
这篇教程带你从零跑通一个“上传文档→抽取三元组→入库 Neo4j→自然语言问答+生成 Cypher”的端到端系统。文末附上常见问题与性能优化建议。
示例工程参考本仓库结构 github链接:
- 后端:
app.py
(FastAPI + LlamaIndex + Neo4j) - 前端:
frontend/
(静态页托管,上传与问答控制台) - 依赖:
requirements.txt
- 环境配置:
.env
你可以直接克隆并跑起来,也可以按本文搭一套自己的。
1. 为什么选 LlamaIndex + Neo4j?
- LlamaIndex 提供从原始文本中抽取实体/关系(SchemaLLMPathExtractor)、构建图索引(PropertyGraphIndex)、问答与 Text2Cypher 能力。
- Neo4j 作为图数据库拥有成熟的 Cypher 查询生态,天然适合知识图谱存储与查询。
在这个组合中:
- 抽取:LLM 抽取实体-关系进图结构。
- 存储:LlamaIndex GraphStore 对接 Neo4j,持久化三元组。
- 问答:将自然语言转成 Cypher 查询(Text2Cypher),并从图中检索答案。
2. 运行示例工程(10 分钟上手)
- 安装依赖
pip install -r requirements.txt
- 配置
.env
OPENAI_API_KEY=sk-...
NEO4J_URI=neo4j+s://<your-instance>.databases.neo4j.io
NEO4J_USERNAME=neo4j
NEO4J_PASSWORD=********
NEO4J_DATABASE=neo4j # 可选,默认 neo4j
- 启动服务
uvicorn app:app --host 0.0.0.0 --port 8000 --reload
- 打开浏览器访问
-
http://localhost:8000
-
左侧上传文档,右侧提问(同时展示回答与生成的 Cypher)
-
生成的知识图谱
-
问答结果
-
Text2cypher结果测试
该控制台已经内置:
- 多格式解析:
.txt/.pdf/.docx
(PDF 用 pdfminer.six,DOCX 用 python-docx,其他按 utf-8/gbk/gb18030 解码) - Neo4j 持久化:上传即抽取,抽取即入库,可累积构建
3. 架构
4. 核心思路与关键代码
4.1 LLM / Embedding 初始化(app.py
)
from llama_index.llms.openai import OpenAI
from llama_index.embeddings.openai import OpenAIEmbedding
llm = OpenAI(model="o3", temperature=0)
embed_model = OpenAIEmbedding(model="text-embedding-3-small")
- LLM 负责抽取关系与问答生成。
- Embedding 用于索引构建与向量匹配(由 LlamaIndex 内部使用)。
4.2 图存与索引
from llama_index.graph_stores.neo4j import Neo4jPropertyGraphStore
from llama_index.core.indices import PropertyGraphIndex
graph_store = Neo4jPropertyGraphStore(
username=NEO4J_USERNAME,
password=NEO4J_PASSWORD,
url=NEO4J_URI,
database=NEO4J_DATABASE,
)
# 全局索引(首次上传时构建)
index: Optional[PropertyGraphIndex] = None
- GraphStore 将三元组写入 Neo4j。
PropertyGraphIndex
是查询与问答的核心入口。
4.3 Schema 与抽取器
from typing import Literal
from llama_index.core.extractors import SchemaLLMPathExtractor
entities = Literal["机构", "团队", "人物", "论文", "专利", "数据集", "模型", "技术", "任务", "会议", "期刊"]
relations = Literal[
"隶属于", "合作", "发表于", "发表于", "提出了", "基于", "应用于",
"引用了", "复现了", "训练于", "评测于", "授权于", "开发了"
]
validation_schema = {
"机构": ["隶属于", "合作", "开发了", "提出了"],
"团队": ["隶属于", "合作", "提出了", "开发了"],
"人物": ["隶属于", "合作", "提出了", "开发了"],
"论文": ["发表于", "引用了", "基于", "应用于", "复现了", "评测于"],
"专利": ["授权于", "基于", "应用于"],
"数据集": [],
"模型": ["训练于", "评测于", "基于", "应用于"],
"技术": ["基于", "应用于", "开发了"],
"任务": [],
"会议": [],
"期刊": [],
}
kg_extractor = SchemaLLMPathExtractor(
llm=llm,
possible_entities=entities,
possible_relations=relations,
kg_validation_schema=validation_schema,
strict=True,
)
- 用领域 Schema 限定“实体/关系”的范围与合法组合,提升质量与一致性。
- 可按你的领域改中文 Schema,例如“领域术语/设备/工艺/指标”等。
4.4 上传 → 抽取 → 入库(POST /upload
)
流程要点:
- 接收文件
UploadFile
,识别扩展名。 - 抽取文本:
.pdf
→ pdfminer.six.docx
→ python-docx- 其他 → 多编码解码
- 构建
Document
,调用PropertyGraphIndex.from_documents()
:
index = PropertyGraphIndex.from_documents(
documents,
kg_extractors=[kg_extractor],
property_graph_store=graph_store,
embed_model=embed_model,
show_progress=True,
)
- 底层 GraphStore 会将实体/关系写入 Neo4j。多次上传可持续扩充知识图谱。
测试 cURL:
curl -F "file=@data/a.txt" http://localhost:8000/upload
4.5 问答与 Text2Cypher(POST /query
)
- 确保索引已初始化(你至少上传过一次)。
- 通过
index.as_query_engine()
执行自然语言问答:
query_engine = index.as_query_engine(llm=llm, include_text=True, verbose=True)
resp = query_engine.query(req.query)
- 从
resp.metadata["cypher_query"]
或回答文本中提取 Cypher。 - 若没有,则用 Text2Cypher Retriever/LLM 兜底:提示模型只输出“单条可执行 Cypher”,并清理掉围栏/三引号。
- 返回 JSON:
{ answer: "...", cypher: "MATCH ... RETURN ..." }
测试 cURL:
curl -X POST http://localhost:8000/query \
-H "Content-Type: application/json" \
-d '{"query":"知识图谱和neo4j的关系?"}'
5. 在自己的项目中复用(最小改造指南)
- 复制
app.py
的核心段落:- OpenAI LLM / Embedding 初始化
Neo4jPropertyGraphStore
配置(读取.env
)- Schema
entities/relations/validation_schema
与SchemaLLMPathExtractor
POST /upload
文件解析、PropertyGraphIndex.from_documents(...)
POST /query
问答与 Cypher 解析
- 前端可选:
- 直接使用本项目
frontend/
(后端以静态资源方式托管)。 - 或者对接你自己的后台管理页面,仅需调用两个 API。
- 直接使用本项目
6. Text2Cypher 的最佳实践
- 提示工程(Prompting):
- 明确要求“只输出一条可执行 Cypher,不要包含```或其他围栏”。
- 根据你的图谱 Schema 提前提供实体-关系示例,降低幻觉。
- 结果清洗:
- 移除反引号/围栏/多余注释。
- 校验基本语法结构,例如必须有
MATCH
和RETURN
。
- 失败兜底:
- 首先尝试从 LlamaIndex 返回的
metadata
中取现成 Cypher。 - 若没有,再触发专用的 Text2Cypher 生成逻辑。
- 首先尝试从 LlamaIndex 返回的
示例清洗(伪代码):
cypher = raw.strip()
cypher = cypher.replace('```cypher','').replace('```','')
cypher = cypher.strip().strip('"').strip("'")
7. 常见问题(FAQ)
-
问:为什么
/query
提示“索引尚未初始化”?
答:需要先上传任意文件完成一次索引构建(服务重启后内存中的索引会丢失,需再次上传初始化)。 -
问:PDF 没抽出文本?
答:pdfminer.six 适用于文本型 PDF。扫描件需 OCR(可接入pytesseract
+pdf2image
或三方 OCR)。 -
问:生成的 Cypher 有三引号或 ```围栏?
答:后端已做清洗与兜底提示。如果仍出现,增强提示或在返回前再做一次正则清洗。 -
问:Neo4j 连接失败?
答:检查.env
的NEO4J_*
配置、数据库连通性与网络白名单。
8. 性能与质量优化建议
- 抽取质量:
- 收紧 Schema 与示例,让抽取更稳定、更贴合领域。
- 对大型文档分段处理,避免上下文过长影响抽取一致性。
- 存储设计:
- 为高频字段建立索引(Neo4j 节点/关系属性索引),提升查询性能。
- 问答可靠性:
- 对生成的 Cypher 做语法预检或小范围试跑,失败则回退更保守的模板。
- 对 Q&A 引擎启用检索证据片段返回,提高可解释性。
- 成本控制:
- 批量上传时合并调用,缓存已解析的片段,选择小 embedding 模型(已使用
text-embedding-3-small
)。
- 批量上传时合并调用,缓存已解析的片段,选择小 embedding 模型(已使用
结语
通过 LlamaIndex + Neo4j,你可以在很短时间里搭建一个可用的“文档→知识图谱→问答/Cypher”的端到端系统。示例工程已经实现了上传解析、抽取入库、问答与 Text2Cypher 的关键路径。拿去即用,按领域 Schema 扩展即可。
更多推荐
所有评论(0)