- 知识图谱是对现实世界实体及其关系的组织化表示
- 知识图谱提供了一种结构化的方式来表示实体、它们的属性以及它们之间的关系,从而实现对信息的全面且相互关联的理解
- 知识图谱可以分解信息来源并将其整合,保存数据之间的关系
- 这种来自不同来源的集成使知识图谱具有更全面的视角,并促进了复杂查询、分析和洞察
- 知识图谱可以随着增长而轻松适应和演变,吸收新的信息和结构变化
- Neo4j 非常适合在知识图谱中表示和查询复杂且相互连接的数据,与使用表格和行的传统关系型数据库不同,Neo4j 使用基于图的模型,包含节点和关系
- 知识图谱不仅存储数据及其相互关系,还存储被称为“组织原则”的框架体系
- 组织原则是围绕数据定义的规则或类别,旨在为数据提供结构
- 组织原则的复杂程度各异
- 既可以是简单的数据描述(例如,将 GraphAcademy 课程描述为“课程 → 模块 → 课时”这种层级)
- 也可以是涵盖整个解决方案的复杂词汇体系
- 知识图谱具有内在的灵活性,并且随着数据增长和变化,你可以改变组织原则
- 描述 GraphAcademy 所能涵盖的课程内容类型的组织原则可能如下所示
- 组织原则作为节点存储在图中,并且可以与实际数据一起存储。例如,在 GraphAcademy 中将组织原则映射到课程内容可能看起来像这样
- 这种组织原则和数据的集成,使得可以执行复杂的查询和分析
- 创建知识图谱
- 从非结构化数据创建知识图谱可能很复杂,涉及多个数据查询、清洗和转换步骤
- 可以使用大型语言模型(LLMs)的文本分析功能来帮助自动化知识图谱的创建
- Benefits and challenges
- 知识图谱已成为极其重要的工具,这归功于其能够清晰呈现数据点之间复杂的关系与互联网络,它们通过对信息进行结构化组织,使人类和机器都能更高效、精准地从中提取 insights
- Benefits
- 增强数据集成和互操作性
- 知识图谱促进来自不同来源的数据集成,创建统一视图
- 优化搜索与发现体验
- 知识图谱通过将数据结构化为“实体”与“关系”,大幅提升了搜索能力
- 用户不再局限于简单的关键词检索,而是能够执行更精细的查询,这些查询能够准确反映真实世界的语境与关联
- 上下文语境理解
- 知识图谱通过精准捕捉实体间的关系,帮助人们更好地理解信息所处的上下文背景
- 提升决策能力
- 知识图谱通过提供互联数据的全局视角,为更优质的决策提供支撑
- 企业和组织能够据此识别出单凭孤立数据点难以察觉的潜在模式与发展趋势
- Challenges
- 数据收集与集成
- 数据往往分散在多种来源中,且格式、结构与标准各不相同
- 数据质量
- 确保数据的准确性、一致性与完整性是一项至关重要(且艰巨)的任务
- 数据建模与Schema(模式)设计
- 设计一个既灵活又强大、足以容纳海量实体与关系的Schema极具挑战性
- 该Schema不仅需要适应不断变化的数据需求,还必须具备处理大规模数据集的高可扩展性
- 实体消歧与对齐
- 需要识别并合并来自不同数据源的重复实体(例如,同一人物或企业在不同系统中的不同表述)
- 实体链接与关系抽取
- 精准识别实体,并将其与知识图谱中对应的节点正确关联,对于保证数据准确性及深度理解关系网络至关重要
- 借助大型语言模型(LLM)来辅助构建知识图谱,不仅能帮您充分发挥上述优势,还能有效缓解其中面临的各项挑战
- LLM能够自动分析数据、抽取实体与关系,并生成知识图谱Schema,从而让我们将精力集中于对数据和关系的深度理解上
Explore a Knowledge Graph
- 沙盒中包含一个预构建的知识图谱
- 该知识图谱仅提取并展示了3篇关于1976年美国总统大选的新闻报道(PDF)
- 1976-6:“杰克逊赢得马萨诸塞州民主党初选。”
- 1976-8:“卡特赢得宾夕法尼亚州民主党初选”
- 1976-22:“吉米·卡特赢得总统大选”
- 这3篇报道摘自NewsWire数据集,该数据集包含270万篇撰写于1878年至1977年间、属于公有领域且互不重复的美国新闻电讯稿
- 创建该数据集旨在为研究人员提供一个大规模、高质量的历史新闻语料库。这些文本为历史主题与事件——以及当年哪些报纸曾对其进行报道——提供了一个海量的信息资源库。该数据集将对广泛的研究群体大有裨益,其中包括历史学家、其他社会科学家以及自然语言处理(NLP)从业者
- 可以在
llm-knowledge-graph-construction代码仓库中,查看从该数据集中提取这些报道的Python代码
- 该知识图谱映射了报道中提及的以下实体类型之间的关系
Person 人物Location 地点Organization 组织Building 建筑Political party 党政State 州
- 运行此 Cypher 查询语句,以揭示报道中的实体之间是如何相互关联的:展现其底层数据与关系
- 节点 (Node):用圆括号 () 表示。看起来像一个圆圈。
- 例如:(p:Person) 表示一个标签为 Person 的节点,变量名为 p。
- 关系 (Relationship):用中括号 [] 和箭头 --> 表示。
- 例如:-[:FRIEND]-> 表示一个类型为 FRIEND 的有向关系。
- 属性 (Properties):用花括号 {} 表示,类似 JSON 格式。
- 例如:{name: 'Alice', age: 30}。
MATCH p=(a:Article)-[:HAS_ENTITY]->(e)-[r]-() RETURN p
- (文章 a) ----提到----> (实体 e) ----关系 r----> (未知节点)
Person 实体 Jimmy Carter 吉米·卡特 在图中相互连接MATCH (p:Person {id:"Jimmy Carter"})-[r]-(p2:Person) RETURN p, r, p2
Person 绿色 和 State 黄色实体MATCH (p:Person)-[r]-(s:State) RETURN p, r, s
MATCH (a:Article {id:"1976-8"})-[:HAS_ENTITY]->(e) MATCH (e)-[r]-(e2) WHERE (a)-[:HAS_ENTITY]->(e2) RETURN e, r, e2
- Knowledge Graph Use Cases
- 提升数据集成与互操作性: 知识图谱通过建立实体及其关系的结构化表示,将异构数据源统一起来,从而实现跨系统、跨领域的无缝数据集成与互操作
- 优化搜索与信息检索: 通过利用数据点之间的语义关系,知识图谱极大增强了搜索能力,能够实现更精准、更具语境感知能力的信息检索,从而提升搜索结果的相关性与准确度
- 深度分析与洞察生成: 知识图谱能够对互联数据进行复杂的查询与逻辑推理,从而实现高级分析。这有助于挖掘出隐藏的数据模式、深层洞察与发展趋势,进而为科学决策和预测性分析提供有力支持
- 个性化推荐与内容发现: 通过在知识图谱中精准捕捉用户的偏好、行为轨迹及所处情境,个性化推荐系统能够为用户量身定制内容、产品和服务,从而显著提升用户体验与用户粘性
使用 LLM 构建 knowledge graph
- 构建过程
- Gather the data
- 第一步是收集非结构化数据。这些数据可以是文本文档、PDF文件、公开数据或任何其他形式的信息源
- 根据原始格式的不同,您可能需要将数据转换为大语言模型(LLM)能够处理的格式(通常为纯文本)
- 这些数据源应当包含您希望纳入知识图谱的核心信息
- Chunk the data
- 下一步是将数据拆分为大小适中的片段。这一过程在业内被称为“分块”(Chunking)
- 分块的大小取决于您所使用的 LLM、数据的复杂程度,以及您希望从数据中提取的具体内容
- 如果 LLM 能够一次性处理整篇文档且符合您的需求,您也可以选择不对数据进行分块
- Vectorize the data
- 根据对数据查询和搜索的具体需求,您可能需要创建向量嵌入(Vector embeddings)。您可以使用任何嵌入模型为每个数据块生成嵌入,但必须确保所有嵌入均使用同一模型生成
- 将这些向量存入向量索引(Vector index)后,您便可以对数据进行语义搜索、相似度搜索以及聚类分析
- Pass the data to an LLM to extract nodes and relationships(将数据传递给 LLM 以提取节点和关系)
- 接下来,将非结构化文本数据输入到 LLM 中,以提取图谱所需的节点和关系
- 您应当设计一个恰当的提示词(Prompt),以指导 LLM 执行以下操作
- 识别文本中的实体
- 提取实体之间的关系
- 规范输出格式,以便直接用于生成图谱(例如将其输出为 JSON 或其他结构化格式)
- 此外,您还可以选择为提取过程提供补充上下文或限制条件,例如指定您希望提取的特定实体或关系类型
- Use the output to generate the graph
- 最后,您可以利用 LLM 的输出结果,在 Neo4j 图数据库中创建对应的节点与关系,从而生成最终的知识图谱
- 提取出的实体类型和关系类型将转化为图谱中的标签(Labels)和关系类型(Relationship types);而它们的名称则将作为节点和关系的标识符
- 假设您想基于维基百科的 Neo4j 页面构建一个知识图谱,您需要执行以下操作
- 从页面收集文本
- 将文本分割成块
- 为每个片段生成嵌入和向量
- 使用 LLM 提取实体和关系
- 将文本发送给 LLM,并使用适当的提示,例如
Neo4j is a graph database management system (GDBMS) developed by Neo4j Inc. The data elements Neo4j stores are nodes, edges connecting them and attributes of nodes and edges. Described by its developers as an ACID-compliant transactional database with native graph storage and processing...
Neo4j is a graph database management system (GDBMS) developed by Neo4j Inc.
The data elements Neo4j stores are nodes, edges connecting them and attributes of nodes and edges.
Described by its developers as an ACID-compliant transactional database with native graph storage and processing...
[0.21972137987, 0.12345678901, 0.98765432109, ...]
[0.34567890123, 0.23456789012, 0.87654321098, ...]
[0.45678901234, 0.34567890123, 0.76543210987, ...]
Your task is to identify the entities and relations requested with the user prompt from a given text. You must generate the output in a JSON format containing a list with JSON objects. Text: {text}
- 解析 LLM 输出的实体和关系
[ { "text": ( "Neo4j is a graph database management system (GDBMS) developed by Neo4j Inc." ), "head": "Neo4j", "head_type": "GraphDatabase", "relation": "DEVELOPED_BY", "tail": "Neo4j Inc", "tail_type": "Company", }, { "text": ( "Neo4j is implemented in Java" ), "head": "Neo4j", "head_type": "Graph Database", "relation": "IMPLEMENTED_IN", "tail": "Java", "tail_type": "ProgrammingLanguage", }, ... ]
5.生成图
- 使用数据在 Neo4j 中构建图,通过创建基于 LLM 提取的实体和关系来创建节点和关系
MERGE (neo4jInc:Company {id: 'Neo4j Inc'}) MERGE (neo4j:GraphDatabase {id: 'Neo4j'}) MERGE (java:ProgrammingLanguage {id: 'Java'}) MERGE (neo4j)-[:DEVELOPED_BY]->(neo4jInc) MERGE (neo4j)-[:IMPLEMENTED_IN]->(java)
LLM Graph Builder
- Neo4j LLM 图构建器是一个将非结构化数据转换为知识图谱的工具。它自动化了上一课中描述的相同过程,并从文本中构建图
- LLM 图形构建器包含一个聊天界面,用于与图形交互并探索数据。聊天机器人可以使用不同的检索增强生成(RAG)方法来回答问题,包括 GraphRAG、向量搜索和 Text2Cypher
- 你可以上传基于文本的文档,并直接从维基百科和 YouTube 导入文本
- LLM 图构建器允许你配置 LLM 模型、实体和关系的类型,以及预定义的模式
- Neo4j.io 凭证
username: 1895baf8 password: 53No-z8m8YLHnMu00rHbVfLHp5Gd9wfoPXy38hOKlSk # Wait 60 seconds before connecting using these details, or login to https://console.neo4j.io to validate the Aura Instance is available NEO4J_URI=neo4j+s://1895baf8.databases.neo4j.io NEO4J_USERNAME=1895baf8 NEO4J_PASSWORD=53No-z8m8YLHnMu00rHbVfLHp5Gd9wfoPXy38hOKlSk NEO4J_DATABASE=1895baf8 AURA_INSTANCEID=1895baf8 AURA_INSTANCENAME=Free instance
- Define a schema
- 模式(Schema)定义了一组旨在从文本中识别的图模式,由节点标签和关系类型(即 (节点)-[关系]→(节点))构成
- 核心技术画像 (Technology Profiling)
- 这部分模式用于定义“技术是什么”以及“它能做什么”
- (Technology)-[:RELATED_TO]→(Technology)
- 建立技术生态或技术栈的关联
- 识别竞品(如 PyTorch vs TensorFlow)、依赖关系(如 LangChain 依赖 Python)或同属关系(如 LLaMA 和 GPT-4 都属于 LLM)
- (Technology)-[:HAS]→(Capability)
- 抽取技术的功能特性
- 回答“这个技术能干什么?”(例如:ChatGPT -[:HAS]→ 文本生成)
- 价值与风险评估 (SWOT Analysis)
- 这部分是 Graph RAG 进行决策支持推理的关键,它将文本中的评价性信息结构化
- (Technology)-[:ENABLES]→(Benefit)
- 识别技术带来的正面价值或优势
- 回答“为什么要用它?”(例如:Docker -[:ENABLES]→ 快速部署)
- (Technology)-[:HAS_POTENTIAL]→(Risk)
- 识别潜在的负面影响、安全隐患或局限性
- 回答“使用它有什么坑?”(例如:LLM -[:HAS_POTENTIAL]→ 幻觉问题)
- 知识语境与溯源 (Context & Provenance)
- 这部分用于处理抽象概念,并连接原始数据来源,增强 RAG 的可解释性
- (Concept)-[:RELATED_TO]→(Concept)
- 构建领域概念的本体网络
- 帮助理解术语之间的关系(例如:深度学习 -[:RELATED_TO]→ 神经网络)
- (Concept)-[:EXPLAINED_IN]→(Resource)
- 知识溯源
- 在 RAG 生成答案时,可以精准引用来源(例如:Transformer机制 -[:EXPLAINED_IN]→ 《Attention Is All You Need》论文)
- 落地应用场景 (Implementation)
- 这部分连接了“工具”和“实际用途”
- (Application)-[:USES]→(Technology)
- 描述具体的应用案例或产品是如何构建的
- 展示技术的实际落地情况(例如:GitHub Copilot -[:USES]→ OpenAI Codex)
Explore the Knowledge Graph
- 每个
Document节点代表一个上传到 LLM 图构建器的文档或文本源
Document被分割成Chunk个节点,通过FIRST_CHUNK和PART_OF关系进行识别
- LLM 处理提取的 chunks 和
Entity节点。图使用HAS_ENTITY关系将Entity节点连接到Chunk节点
- LLM 还可能提取实体之间的关系,图将持有
Entity节点之间的关系
- 该模型允许你将知识图谱中的实体与它们所指的源文档关联起来
- LLM 差异性
- 数据是由 LLM 提取的,结果具有固有的差异性
- 调整路径深度
MATCH (d:Document {fileName:'genai-fundamentals_1-generative-ai_1-what-is-genai.pdf'} ) MATCH p = (d)-[*0..3]-(e) RETURN p
[*0..3] 模式匹配文档与实体之间最多三段关系路径。增加数字将返回更复杂的路径,但查询将花费更长时间Vectors
- 在传统的图数据库查询中,我们通常处理的是结构化数据
- 例子:查找“汤姆·汉克斯(Tom Hanks)出演的电影”
- 处理方式:这很简单,因为演员名字和电影标题都是精确的字符串或 ID,直接用 Cypher 查询匹配即可
MATCH (m:Movie {title: "Toy Story"}) RETURN m.title AS title, m.plot AS plot
- 但是:如何根据“电影剧情(Plot)”来查找电影?
- 数据现状:在你的图数据库中,Movie 节点有一个属性叫做 plot,它是一段很长的文本描述(例如:“一个小男孩的玩具在他不在的时候活过来了……”)
- 挑战:这是一段非结构化文本
- 传统关键词搜索的局限性
- 假设用户搜索:“关于**外星人(aliens)**进攻地球的电影”
- 如果某部电影的剧情简介里写的是:“**地外生物(extraterrestrial beings)**袭击了纽约……”
- 结果:传统的关键词匹配(如 CONTAINS 'aliens')会失败,因为文本中没有“aliens”这个词,尽管“地外生物”和“外星人”是一个意思
- 解决方案:语义搜索与向量嵌入 (Vector Embeddings)
- 我们不再匹配具体的单词,而是匹配含义(Meaning)
- 将电影的 plot(剧情文本)发送给一个 AI 模型
- 模型会理解这段文字的语义,并返回一串很长的数字列表(例如 [-0.012, 0.821, -0.442, ...])
- 这串数字就叫做 Vector Embedding(向量嵌入)
- 导入预先计算好的向量
LOAD CSV WITH HEADERS FROM 'https://data.neo4j.com/rec-embed/movie-plot-embeddings-1k.csv' # 预先计算了 1000 部电影的向量,存在一个 CSV 文件里。这行代码用于读取这个远程 CSV 文件 AS row MATCH (m:Movie {movieId: row.movieId}) # MATCH...: 根据 CSV 中的 movieId,在数据库中找到对应的电影节点 CALL db.create.setNodeVectorProperty(m, 'plotEmbedding', apoc.convert.fromJsonList(row.embedding));
- 它将 CSV 文件中的字符串格式的向量(如 "[0.1, 0.2]")通过 apoc.convert.fromJsonList 转换成 Neo4j 能够理解的真正的小数数组(List of Floats)
- 然后将这个数组保存到电影节点的 plotEmbedding 属性中
- 创建向量索引(Vector Index)
CREATE VECTOR INDEX moviePlots IF NOT EXISTS FOR (m:Movie) ON m.plotEmbedding OPTIONS {indexConfig: { `vector.dimensions`: 1536, `vector.similarity_function`: 'cosine' }};
- 将电影剧情的内容转换为向量索引,需要查询的时候就计算和向量的余弦相似度
- Graph Retrieval
import os from dotenv import load_dotenv load_dotenv() from langchain_core.documents import Document from langchain.chat_models import init_chat_model from langgraph.graph import START, StateGraph from langchain_core.prompts import PromptTemplate from typing_extensions import List, TypedDict from langchain_openai import OpenAIEmbeddings from langchain_neo4j import Neo4jGraph, Neo4jVector # Initialize the LLM model = init_chat_model("MiniMax/MiniMax-M2.5", model_provider="openai", base_url='https://api-inference.modelscope.cn/v1', api_key=os.getenv("MODELSCOPE_API_KEY"), ) # Create a prompt template = """Use the following pieces of context to answer the question at the end. If you don't know the answer, just say that you don't know, don't try to make up an answer. 回答使用中文 {context} Question: {question} Answer:""" prompt = PromptTemplate.from_template(template) # Define state for application class State(TypedDict): question: str context: List[Document] answer: str # Connect to Neo4j graph = Neo4jGraph( url=os.getenv("NEO4J_URI"), username=os.getenv("NEO4J_USERNAME"), password=os.getenv("NEO4J_PASSWORD"), database=os.getenv("NEO4J_DATABASE"), ) # end::graph[] # Create the embedding model embedding_model = OpenAIEmbeddings( model="text-embedding-ada-002", base_url=os.getenv("OPENAI_BASE_URL"), ) # Define the retrieval query retrieval_query = """ MATCH (node)<-[r:RATED]-() WITH node, score, avg(r.rating) AS userRating RETURN "Title: " + node.title + ", Plot: " + node.plot AS text, score, { title: node.title, genres: [ (node)-[:IN_GENRE]->(g) | g.name ], actors: [ (person)-[r:ACTED_IN]->(node) | [person.name, r.role] ], userRating: userRating } AS metadata ORDER BY userRating DESC """ # node(这是通过向量相似度搜索找到的那个具体的节点) 和 score(这是向量搜索计算出的相似度分数) 是保留关键字 # Create Vector plot_vector = Neo4jVector.from_existing_index( embedding_model, graph=graph, index_name="moviePlots", embedding_node_property="plotEmbedding", text_node_property="plot", # 节点中存储原始文本的属性名 retrieval_query=retrieval_query, ) # Define functions for each step in the application # Retrieve context def retrieve(state: State): # Use the vector to find relevant documents context = plot_vector.similarity_search( state["question"], k=6, ) return {"context": context} # Generate the answer based on the question and context def generate(state: State): messages = prompt.invoke({"question": state["question"], "context": state["context"]}) response = model.invoke(messages) return {"answer": response.content} # Define application steps workflow = StateGraph(State).add_sequence([retrieve, generate]) workflow.add_edge(START, "retrieve") app = workflow.compile() # Run the application question = "Who acts in movies about Love and Romance?" # question = "What are top user rated movies about a house haunted by ghosts?" response = app.invoke({"question": question}) print("Answer:", response["answer"]) print("Context:", response["context"])
Text to Cypher
- Cypher QA Chain
- 用户提问 -> LLM 自动写 Cypher -> 数据库执行 -> LLM 把结果总结成人类语言
当你问出:“谁导演了电影《阿凡达》?” 时,GraphCypherQAChain 在后台其实偷偷做了 5 步 1. 获取 Schema(图模式): Chain 会自动去 Neo4j 数据库里扫一眼,看看有哪些节点(如 Movie, Person)、哪些属性(如 title, name)以及哪些关系(如 DIRECTED) 2. 构建 Prompt(提示词): Chain 会把你的问题(“谁导演了阿凡达”)和刚刚获取到的 Schema 组合在一起,发给大模型(比如 GPT-4) 3. 生成 Cypher(Text2Cypher): 大模型根据 Schema,写出一句正确的 Cypher 语句,比如:MATCH (p:Person)-[:DIRECTED]->(m:Movie {title: 'Avatar'}) RETURN p.name 4. 执行查询: Chain 拿着这句 Cypher,去 Neo4j 数据库里真正执行它,拿到原始数据(比如返回了结果 James Cameron) 5. 生成最终回答: Chain 再把原始数据和你的原问题一起发给大模型,大模型组织语言,输出完美回答:“电影《阿凡达》是由詹姆斯·卡梅隆导演的。”
[human] What year was the movie Babe released? [system] Generate a Cypher query based on this question and this graph schema. [assistant] MATCH (m:Movie) WHERE m.title = 'Babe' RETURN m.released The Cypher query is the executed and the result returned. [system] Generate an answer based on these results [{m.released_year: 1995}]. [assistant] The movie Babe was released in 1995.
import os from dotenv import load_dotenv load_dotenv() from langchain.chat_models import init_chat_model from langchain_neo4j import Neo4jGraph # Initialize the LLM model = init_chat_model("MiniMax/MiniMax-M2.5", model_provider="openai", base_url='https://api-inference.modelscope.cn/v1', api_key=os.getenv("MODELSCOPE_API_KEY"), ) # Connect to Neo4j graph = Neo4jGraph( url=os.getenv("NEO4J_URI"), username=os.getenv("NEO4J_USERNAME"), password=os.getenv("NEO4J_PASSWORD"), database=os.getenv("NEO4J_DATABASE"), ) from langchain_neo4j import GraphCypherQAChain # Create the Cypher QA chain cypher_qa = GraphCypherQAChain.from_llm( graph=graph, llm=model, allow_dangerous_requests=True, verbose=True, # 打印生成的 Cypher 查询以及生成答案所使用的完整上下文 ) # allow_dangerous_requests=True # 将 Cypher 语句的生成工作交给大语言模型(LLM)。它可能会生成无效的 Cypher 查询,从而导致图数据库中的数据损坏,或导致敏感信息泄露 # 必须通过将 allow_dangerous_requests 参数设置为 True 来主动选择承担此风险 # 在生产环境中,你应该确保对数据的访问受到限制,并具备足够的安全措施以防止恶意查询。这可能包括使用只读用户或基于角色的访问控制(RBAC) # Invoke the chain question = "How many movies are in the Sci-Fi genre?" response = cypher_qa.invoke({"query": question}) print(response["result"])
temperature 设置为 0 。在生成 Cypher 查询时,能保证输出的稳定性- 可以向 GraphCypherQAChain 提供自定义的提示词。您可以根据具体的应用场景量身定制提示词,从而生成更准确的 Cypher 查询
from langchain_core.prompts.prompt import PromptTemplate # Cypher template cypher_template = """Task:Generate Cypher statement to query a graph database. Instructions: Use only the provided relationship types and properties in the schema. Do not use any other relationship types or properties that are not provided. Schema: {schema} Note: Do not include any explanations or apologies in your responses. Do not respond to any questions that might ask anything else than for you to construct a Cypher statement. Do not include any text except the generated Cypher statement. The question is: {question}""" cypher_prompt = PromptTemplate( input_variables=["schema", "question"], template=cypher_template )
- 将自定义提示词应用到 GraphCypherQAChain 中
cypher_qa = GraphCypherQAChain.from_llm( graph=graph, llm=model, cypher_llm=cypher_model, cypher_prompt=cypher_prompt, allow_dangerous_requests=True, verbose=True, )
- 特定指令 (Specific instructions)
- 为了处理特定的数据或业务规则,您可以在生成 Cypher 时向大语言模型(LLM)提供明确的指令
- 例如,以 "The" 开头的电影名称在图数据库中是存储为 "Matrix, The" 而不是 "The Matrix" 的
- 如果在不提供此背景信息的情况下让 LLM 生成 Cypher 查询,将导致查询结果为空(返回不到任何数据)
[用户] 谁出演了电影《黑客帝国》(The Matrix)? [助手] 我不知道
For movie titles that begin with "The", move "the" to the end, for example "The 39 Steps" becomes "39 Steps, The". (对于以 "The" 开头的电影标题,请将 "the" 移至末尾,例如将 "The 39 Steps" 转换为 "39 Steps, The"。
# Cypher template with additional instructions cypher_template = """Task:Generate Cypher statement to query a graph database. Instructions: Use only the provided relationship types and properties in the schema. Do not use any other relationship types or properties that are not provided. For movie titles that begin with "The", move "the" to the end, for example "The 39 Steps" becomes "39 Steps, The". Schema: {schema} Note: Do not include any explanations or apologies in your responses. Do not respond to any questions that might ask anything else than for you to construct a Cypher statement. Do not include any text except the generated Cypher statement. The question is: {question}"""
- 示例 (Examples)
- 可以提供一些“问题 -> 对应 Cypher”的示例(Few-shot prompting),以帮助 LLM 生成更精准的 Cypher 语句
- 与电影评分相关的问题常常会生成带有歧义或错误的 Cypher。这是因为在数据库中,用户评分是属于 RATED(已评分)关系上的一个属性,而 Movie(电影)节点本身也包含一个名为 imdbRating 的属性
Question: Get user ratings? Cypher: MATCH (u:User)-[r:RATED]->(m:Movie) WHERE u.name = "User name" RETURN r.rating AS userRating # 带有示例的 Cypher 模板 cypher_template = """Task:Generate Cypher statement to query a graph database. Instructions: Use only the provided relationship types and properties in the schema. Do not use any other relationship types or properties that are not provided. For movie titles that begin with "The", move "the" to the end, for example "The 39 Steps" becomes "39 Steps, The". Schema: {schema} Examples: 1. Question: Get user ratings? Cypher: MATCH (u:User)-[r:RATED]->(m:Movie) WHERE u.name = "User name" RETURN r.rating AS userRating 2. Question: Get average rating for a movie? Cypher: MATCH (m:Movie)<-[r:RATED]-(u:User) WHERE m.title = 'Movie Title' RETURN avg(r.rating) AS userRating Note: Do not include any explanations or apologies in your responses. Do not respond to any questions that might ask anything else than for you to construct a Cypher statement. Do not include any text except the generated Cypher statement. The question is: {question}"""
- 电影类型 (Genres)
- 数据库中包含有关电影类型(genres)的数据
- 在生成涉及电影类型等更复杂的 Cypher 查询时,LLM 可能无法直接生成正确的 Cypher 语句
- 这些查询可能需要在提示词中提供一个关于如何从图中检索类型的具体示例
What is the highest user rated movie in the Horror genre? (恐怖类型中用户评分最高的电影是哪一部?) How many Sci-Fi movies has Tom Hanks acted in? (汤姆·汉克斯出演过多少部科幻电影?) # Cypher template with examples cypher_template = """Task:Generate Cypher statement to query a graph database. Instructions: Use only the provided relationship types and properties in the schema. Do not use any other relationship types or properties that are not provided. For movie titles that begin with "The", move "the" to the end, for example "The 39 Steps" becomes "39 Steps, The". Schema: {schema} Examples: 1. Question: Get user ratings? Cypher: MATCH (u:User)-[r:RATED]->(m:Movie) WHERE u.name = "User name" RETURN r.rating AS userRating 2. Question: Get average rating for a movie? Cypher: MATCH (m:Movie)<-[r:RATED]-(u:User) WHERE m.title = 'Movie Title' RETURN avg(r.rating) AS userRating 3. Question: Get movies for a genre? Cypher: MATCH ((m:Movie)-[:IN_GENRE]->(g:Genre) WHERE g.name = 'Genre Name' RETURN m.title AS movieTitle Note: Do not include any explanations or apologies in your responses. Do not respond to any questions that might ask anything else than for you to construct a Cypher statement. Do not include any text except the generated Cypher statement. The question is: {question}"""
- Schema
- 大语言模型(LLM)是基于图模式(schema)来生成 Cypher 查询的。当提交查询时,系统会自动从数据库中读取该模式,并将其添加到提示词(prompt)中
- 如何限制模式(schema),使其仅包含特定的节点标签(node labels)或关系类型(relationship types)
- 限制模式可以通过以下方式帮助生成更好的 Cypher 查询
- 降低生成的 Cypher 查询的复杂度
- 帮助 LLM 专注于图谱中相关的部分
- 排除图谱中可能让 LLM 产生混淆的不相关或无用的部分
- 可以通过向 GraphCypherQAChain 提供一个列表,指定要在模式中包含或排除的节点标签和关系类型,从而实现对模式的限制
- 如果你只想包含有关电影(movies)及其导演(directors)的数据,你可以提供以下需要包含的节点标签和关系类型的列表
- Movie
- DIRECTED
- Director
- 可以将这些类型作为一个列表,传递给 GraphCypherQAChain 的 include_types 参数
# 创建 Cypher 问答链 cypher_qa = GraphCypherQAChain.from_llm( graph=graph, llm=model, include_types=["Movie", "ACTED_IN", "Person"], # Person 对应 Director allow_dangerous_requests=True, verbose=True, )
# 创建 Cypher 问答链 cypher_qa = GraphCypherQAChain.from_llm( graph=graph, llm=model, exclude_types=["User", "RATED"], allow_dangerous_requests=True, verbose=True, )
检索器 (Retriever)
- 将使用 GraphCypherQAChain 为 LangChain 智能体(agent)添加一个“文本转 Cypher”检索器
- 将 return_direct 参数设置为 True,以便直接返回 Cypher 查询的结果,而不是返回一段生成的答案
- 交给下一个智能体来分析答案
from langchain_neo4j import GraphCypherQAChain # 创建 Cypher 问答链 cypher_qa = GraphCypherQAChain.from_llm( graph=graph, llm=model, allow_dangerous_requests=True, return_direct=True, ) # 检索上下文 def retrieve(state: State): context = cypher_qa.invoke( {"query": state["question"]} ) return {"context": context}
- 提供自定义提示词和具体指令
- 包含示例问题和对应的 Cypher 查询(少样本提示)
- 使用不同的大语言模型来进行 Cypher 生成
- 限制数据库模式 (schema) 以提供更聚焦的结果
- 将 GraphCypherQAChain 的 verbose 参数设置为 True,以便查看生成的 Cypher 查询以及用于生成答案的完整上下文信息
- 向量检索 = 查字典找段落(精准命中局部文本细节,解决“从哪里开始”的问题)
- 图检索 = 看思维导图做联想(跨越不同文档,串联全局实体关系,解决“还漏掉了什么相关知识”的问题)
- 普通RAG的问题
- 普通 RAG 的死穴:Chunking 导致的“信息孤岛”与“上下文断层”
- 跨块连接断裂(代词指代丢失)
- Chunk 1:“张三是苹果公司的现任 CEO。他非常喜欢喝咖啡。”
- Chunk 2(几页之后):“这位 CEO 昨天在北京和李四签了一份价值百亿的合同。”
- 普通 RAG 的表现:系统把问题向量化,去搜索带有“张三”、“北京”的片段。它可能找回了 Chunk 1(因为有张三),但绝对找不回 Chunk 2(因为 Chunk 2 里没有“张三”这两个字,只有“这位 CEO”)。由于物理切割,上下文断裂了,普通 RAG 无法回答这个问题
- 缺乏全局视角(多跳推理失败)
- 普通 RAG 就像是用“管中窥豹”的方式看数据。如果一个问题的答案需要跨越 5 份不同的文档才能推理出来,普通向量检索是很难同时把这 5 个毫不相关的碎片精准召回的
- Graph RAG 的魔法:把“物理碎片”重组成“逻辑网络”
- 测试
- 多跳(multi-hop)推理能力
- 出现问题的原因
- 它被“引言”困住了(Chunking & 检索截断问题)
- 图谱构建(Graph Extraction)的边不够强
- 查询策略(Local Search vs. Global Search)
- Local Search(局部搜索)
- Global Search(全局搜索)
- 根据不同的场景设计不同的抽取 Schema
- 例如分析风险评估应该分析正面价值或优势,识别潜在的负面影响、安全隐患或局限性
- 例如做一个专业知识文档RAG(知识语境与溯源)处理抽象概念,并连接原始数据来源,增强 RAG 的可解释性
- 节点去重
