这篇教程带你从零跑通一个“上传文档→抽取三元组→入库 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 分钟上手)

  1. 安装依赖
pip install -r requirements.txt
  1. 配置 .env
OPENAI_API_KEY=sk-...
NEO4J_URI=neo4j+s://<your-instance>.databases.neo4j.io
NEO4J_USERNAME=neo4j
NEO4J_PASSWORD=********
NEO4J_DATABASE=neo4j        # 可选,默认 neo4j
  1. 启动服务
uvicorn app:app --host 0.0.0.0 --port 8000 --reload
  1. 打开浏览器访问
  • http://localhost:8000

  • 左侧上传文档,右侧提问(同时展示回答与生成的 Cypher)
    在这里插入图片描述

  • 生成的知识图谱
    在这里插入图片描述

  • 问答结果
    在这里插入图片描述

  • Text2cypher结果测试
    在这里插入图片描述

该控制台已经内置:

  • 多格式解析:.txt/.pdf/.docx(PDF 用 pdfminer.six,DOCX 用 python-docx,其他按 utf-8/gbk/gb18030 解码)
  • Neo4j 持久化:上传即抽取,抽取即入库,可累积构建

3. 架构

问答
上传/构建
静态托管
OpenAI API
LlamaIndex
GraphStore
/upload
抽取文本, 构建索引
/query
生成/解析 Cypher
读写
Neo4j Database
PropertyGraphIndex
FastAPI 后端 app.py
用户浏览器
前端页面 frontend/
LLM/Embedding (OpenAI)

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

流程要点:

  1. 接收文件 UploadFile,识别扩展名。
  2. 抽取文本:
    • .pdf → pdfminer.six
    • .docx → python-docx
    • 其他 → 多编码解码
  3. 构建 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,
)
  1. 底层 GraphStore 会将实体/关系写入 Neo4j。多次上传可持续扩充知识图谱。

测试 cURL:

curl -F "file=@data/a.txt" http://localhost:8000/upload

4.5 问答与 Text2Cypher(POST /query

  1. 确保索引已初始化(你至少上传过一次)。
  2. 通过 index.as_query_engine() 执行自然语言问答:
query_engine = index.as_query_engine(llm=llm, include_text=True, verbose=True)
resp = query_engine.query(req.query)
  1. resp.metadata["cypher_query"] 或回答文本中提取 Cypher。
  2. 若没有,则用 Text2Cypher Retriever/LLM 兜底:提示模型只输出“单条可执行 Cypher”,并清理掉围栏/三引号。
  3. 返回 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_schemaSchemaLLMPathExtractor
    • POST /upload 文件解析、PropertyGraphIndex.from_documents(...)
    • POST /query 问答与 Cypher 解析
  • 前端可选:
    • 直接使用本项目 frontend/(后端以静态资源方式托管)。
    • 或者对接你自己的后台管理页面,仅需调用两个 API。

6. Text2Cypher 的最佳实践

  • 提示工程(Prompting):
    • 明确要求“只输出一条可执行 Cypher,不要包含```或其他围栏”。
    • 根据你的图谱 Schema 提前提供实体-关系示例,降低幻觉。
  • 结果清洗:
    • 移除反引号/围栏/多余注释。
    • 校验基本语法结构,例如必须有 MATCHRETURN
  • 失败兜底:
    • 首先尝试从 LlamaIndex 返回的 metadata 中取现成 Cypher。
    • 若没有,再触发专用的 Text2Cypher 生成逻辑。

示例清洗(伪代码):

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 连接失败?
    答:检查 .envNEO4J_* 配置、数据库连通性与网络白名单。


8. 性能与质量优化建议

  • 抽取质量:
    • 收紧 Schema 与示例,让抽取更稳定、更贴合领域。
    • 对大型文档分段处理,避免上下文过长影响抽取一致性。
  • 存储设计:
    • 为高频字段建立索引(Neo4j 节点/关系属性索引),提升查询性能。
  • 问答可靠性:
    • 对生成的 Cypher 做语法预检或小范围试跑,失败则回退更保守的模板。
    • 对 Q&A 引擎启用检索证据片段返回,提高可解释性。
  • 成本控制:
    • 批量上传时合并调用,缓存已解析的片段,选择小 embedding 模型(已使用 text-embedding-3-small)。

结语

通过 LlamaIndex + Neo4j,你可以在很短时间里搭建一个可用的“文档→知识图谱→问答/Cypher”的端到端系统。示例工程已经实现了上传解析、抽取入库、问答与 Text2Cypher 的关键路径。拿去即用,按领域 Schema 扩展即可。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐