知识图谱+LLM:增强Agent的事实推理能力

副标题:从理论到实践,全方位解析如何构建具备精准事实推理能力的下一代智能Agent


第一部分:引言与基础 (Introduction & Foundation)

1. 摘要/引言 (Abstract / Introduction)

1.1 问题陈述

在人工智能技术飞速发展的今天,大语言模型(LLMs)如GPT-4、Claude、Llama等无疑是最耀眼的明星。它们展现出了令人惊叹的语言理解、生成和初步推理能力,仿佛让我们看到了通用人工智能(AGI)的曙光。然而,当我们深入使用这些模型时,会发现它们存在几个致命的缺陷:

  1. “幻觉”(Hallucination)问题:LLMs经常会一本正经地编造事实,给出听起来合理但实际上完全错误的信息。
  2. 知识的“静态性”:模型的知识截止到训练数据的时间点,对于训练后发生的事件或更新的信息一无所知。
  3. 可解释性差:当LLMs给出一个答案时,我们很难追溯它是如何得出这个结论的,缺乏透明度。
  4. 复杂事实推理能力有限:在需要多步逻辑推理、精确知识检索的任务上,LLMs往往表现不佳。

与此同时,知识图谱(Knowledge Graphs, KGs)作为一种结构化的知识表示方式,虽然在处理非结构化文本方面不如LLMs灵活,但却具有高度的可靠性、可解释性和可更新性。它以“实体-关系-实体”的三元组形式存储知识,能够支持复杂的查询和推理。

那么,一个自然的想法是:能否将知识图谱的“严谨”与LLMs的“灵活”结合起来,打造一个既懂语言又懂事实的智能体? 这正是本文要探讨的核心问题。

1.2 核心方案

本文提出的核心方案是构建一个知识图谱增强的大语言模型Agent。具体来说,我们将:

  1. 利用知识图谱作为外部“大脑”:存储结构化的、可验证的事实知识。
  2. 利用LLMs作为“中央控制器”:负责理解用户意图、生成查询计划、整合检索结果并生成最终回答。
  3. 设计高效的交互机制:让LLMs能够“访问”和“操作”知识图谱,将图谱知识无缝融入到推理过程中。

这种结合方式不仅能够减少LLMs的幻觉,提高回答的准确性,还能增强系统的可解释性和知识的实时更新能力。

1.3 主要成果/价值

读完本文,你将能够:

  1. 深刻理解知识图谱和LLMs各自的优缺点以及它们的互补性。
  2. 掌握知识图谱增强LLM的几种主流架构模式和技术路径。
  3. 亲手实践一个完整的项目:从构建一个简单的知识图谱,到开发一个能利用该图谱进行事实推理的Agent。
  4. 了解当前该领域的最佳实践、常见挑战以及未来发展方向。

无论你是一名想提升AI应用能力的开发者,还是一名对知识表示和推理感兴趣的研究者,本文都将为你提供有价值的参考。

1.4 文章导览

本文将分为四个部分,共16个章节,循序渐进地带你探索知识图谱与LLMs的结合之道:

  • 第一部分(第1-4章):奠定基础,介绍问题背景、核心概念和文章结构。
  • 第二部分(第5-9章):深入核心,讲解理论基础、环境搭建,并带你一步步实现项目。
  • 第三部分(第10-13章):扩展提升,展示结果、讨论优化、解答疑问并展望未来。
  • 第四部分(第14-16章):总结全文,列出参考资料和附录。

现在,就让我们开始这段激动人心的技术之旅吧!


2. 目标读者与前置知识 (Target Audience & Prerequisites)

2.1 目标读者

本文主要面向以下人群:

  1. AI/ML应用开发者:你已经有一些使用LLMs开发应用的经验,但希望解决幻觉问题,提升应用的事实准确性。
  2. NLP工程师:你对自然语言处理有一定了解,想探索知识增强的方法。
  3. 知识图谱从业者:你熟悉知识图谱的构建和应用,想知道如何与最新的LLMs技术结合。
  4. 对AGI和智能体感兴趣的技术人员:你好奇下一代AI系统会是什么样子,想了解知识与推理的结合之道。
2.2 前置知识

为了更好地理解本文内容,建议你具备以下基础知识:

  1. Python编程:我们将使用Python进行所有代码实现,你需要熟悉Python的基本语法和常用库。
  2. 大语言模型基础:你应该了解LLMs的基本概念,比如什么是提示词(Prompt)、什么是上下文窗口(Context Window),并且最好有使用OpenAI API或类似服务的经验。
  3. 数据库基础:虽然我们会详细介绍图数据库,但了解基本的数据库概念(如表、查询、索引)会有帮助。
  4. 基本的图论知识(可选):了解节点、边、路径等基本图论概念将有助于理解知识图谱的原理。

如果你在某些方面有所欠缺也没关系,我们会在相关章节对关键概念进行解释,你也可以通过参考资料进行补充学习。


3. 文章目录 (Table of Contents)

  • 第一部分:引言与基础
    1. 摘要/引言
    2. 目标读者与前置知识
    3. 文章目录
  • 第二部分:核心内容
    4. 问题背景与动机
    5. 核心概念与理论基础
    6. 环境准备
    7. 分步实现:构建知识图谱
    8. 分步实现:开发KG-LLM交互层
    9. 分步实现:构建智能Agent
    10. 关键代码解析与深度剖析
  • 第三部分:验证与扩展
    11. 结果展示与验证
    12. 性能优化与最佳实践
    13. 常见问题与解决方案
    14. 未来展望与扩展方向
  • 第四部分:总结与附录
    15. 总结
    16. 参考资料
    17. 附录

第二部分:核心内容 (Core Content)

4. 问题背景与动机 (Problem Background & Motivation)

4.1 LLM的局限性:繁荣背后的隐忧

在过去的几年里,大语言模型取得了令人瞩目的成就。从GPT-3的横空出世,到ChatGPT的全民热潮,再到GPT-4、Claude 3等模型的持续迭代,LLMs似乎无所不能:写代码、作文章、解数学题、进行多轮对话……然而,当我们将这些模型应用到需要高可靠性的场景(如医疗咨询、法律问答、金融分析)时,它们的局限性就暴露无遗了。

让我们通过一个具体的例子来看看LLMs的“幻觉”问题。假设我们问GPT-4一个关于电影的问题:“电影《盗梦空间》的导演是谁?他还导演过哪些电影?”GPT-4可能会给出正确的答案:“克里斯托弗·诺兰,他还导演过《星际穿越》、《蝙蝠侠:黑暗骑士》等。”但如果我们问一个更偏门的问题,或者故意设置一些陷阱,情况可能就不一样了。

更严重的是,LLMs的知识是“静态”的。如果一部电影是在2023年10月上映的,而GPT-4的训练数据截止到2023年6月,那么它就完全不知道这部电影的存在。此外,LLMs的推理过程是“黑盒”的,我们无法确认它的答案是基于真实的知识还是随机的猜测。

这些局限性使得纯LLM在处理事实密集型任务(Fact-Intensive Tasks)时显得力不从心。那么,有什么方法可以解决这些问题呢?

4.2 知识图谱的优势:结构化知识的力量

知识图谱的概念最早由Google在2012年提出,旨在提升搜索引擎的能力。但实际上,结构化知识表示的思想可以追溯到更早的语义网(Semantic Web)时代。

知识图谱的核心思想很简单:用图的结构来表示知识。在知识图谱中,我们有:

  1. 实体(Entities):代表现实世界中的具体事物或抽象概念,如“克里斯托弗·诺兰”、“盗梦空间”、“电影”。
  2. 关系(Relations):代表实体之间的联系,如“导演”、“上映时间”、“类型”。
  3. 属性(Attributes):代表实体的特性,如“出生日期”、“评分”。

这种表示方式有几个显著的优势:

  1. 高度可靠性:知识图谱中的知识通常是经过人工或半自动验证的,信息质量高。
  2. 可解释性:知识图谱的推理过程是透明的,我们可以沿着图中的边来追溯答案的来源。
  3. 可更新性:我们可以方便地向图谱中添加新的实体、关系或修改已有的知识。
  4. 支持复杂查询:利用图查询语言(如Cypher、SPARQL),我们可以进行复杂的多跳推理和模式匹配。

例如,在电影知识图谱中,我们可以很容易地查询:“找出所有由克里斯托弗·诺兰导演、且评分在8.5分以上的科幻电影。”这种查询对于纯LLM来说可能很困难,但对于知识图谱来说却是小菜一碟。

4.3 为什么要结合:1+1>2的可能性

既然LLMs和知识图谱各有优缺点,那么一个自然的想法就是将它们结合起来,取长补短。让我们来看看这种结合能带来什么好处:

特性纯LLM纯知识图谱结合后
自然语言理解
事实准确性中等/弱
知识时效性
可解释性
复杂推理中等
泛化能力
处理非结构化数据

正如上表所示,LLMs和知识图谱的特性具有很强的互补性。通过结合,我们可以获得一个既“聪明”又“可靠”的系统:

  1. 减少幻觉:LLMs可以从知识图谱中检索事实信息,作为回答的依据,而不是凭空编造。
  2. 实时知识更新:我们不需要重新训练LLMs,只需要更新知识图谱,系统就能获取最新的信息。
  3. 增强可解释性:系统在给出答案的同时,可以提供知识图谱中的证据路径,让用户知道答案是怎么来的。
  4. 提升复杂推理能力:结合LLMs的语义理解能力和知识图谱的结构化查询能力,可以处理更复杂的推理任务。

这种结合的潜力是巨大的。事实上,在学术界和工业界,已经有越来越多的工作在探索这一方向,这也正是我们撰写本文的动机所在。


5. 核心概念与理论基础 (Core Concepts & Theoretical Foundation)

在开始动手实践之前,我们需要先建立起扎实的理论基础。在这一章中,我们将详细介绍知识图谱、LLMs、Agent以及它们结合的核心概念和架构模式。

5.1 知识图谱(Knowledge Graph)的核心概念
5.1.1 定义与组成

虽然“知识图谱”这个术语已经被广泛使用,但在不同的语境下可能有不同的含义。在本文中,我们将知识图谱定义为:一种采用图结构来建模、存储和处理知识的数据结构,其中节点代表实体,边代表实体之间的关系。

更正式地说,一个知识图谱 G G G 可以表示为一个三元组的集合:

G = { ( h , r , t ) ∣ h , t ∈ E , r ∈ R } G = \{(h, r, t) \mid h, t \in E, r \in R\} G={(h,r,t)h,tE,rR}

其中:

  • E E E 是实体(Entity)的集合,
  • R R R 是关系(Relation)的集合,
  • ( h , r , t ) (h, r, t) (h,r,t) 表示“头实体 h h h 通过关系 r r r 与尾实体 t t t 相连”。

例如,(克里斯托弗·诺兰,导演,盗梦空间)就是一个这样的三元组。

除了三元组,很多知识图谱还包含实体的属性(Attributes),我们可以将其表示为(实体,属性名,属性值)的形式,例如(盗梦空间,评分,9.3)。

5.1.2 知识图谱的架构层次

一个完整的知识图谱系统通常包含以下几个层次:

  1. 数据源层:包含结构化数据(如数据库表)、半结构化数据(如XML、JSON)和非结构化数据(如文本、图片)。
  2. 信息抽取层:负责从数据源中提取实体、关系和属性,这通常涉及命名实体识别(NER)、关系抽取(RE)、实体链接(EL)等技术。
  3. 知识融合层:负责将不同来源的知识整合起来,解决实体对齐、冲突消解等问题。
  4. 知识存储层:负责将知识图谱存储起来,通常使用图数据库(如Neo4j、JanusGraph)或RDF存储(如Apache Jena)。
  5. 知识应用层:提供查询、推理、可视化等功能,支持上层应用。

在本文中,我们将重点关注知识存储层和知识应用层,尤其是如何将知识图谱与LLMs结合起来。

5.1.3 图查询语言

为了从知识图谱中检索信息,我们需要使用图查询语言。目前最流行的图查询语言有两种:

  1. Cypher:由Neo4j开发的声明式图查询语言,语法类似于SQL,但针对图结构进行了优化。例如,查询“克里斯托弗·诺兰导演的所有电影”可以写成:
    MATCH (director:Person {name: '克里斯托弗·诺兰'})-[:DIRECTED]->(movie:Movie)
    RETURN movie
    
  2. SPARQL:W3C推荐的RDF查询语言,适用于查询RDF格式的知识图谱。

在本文的项目中,我们将使用Neo4j图数据库和Cypher查询语言。

5.2 大语言模型(LLM)的核心概念
5.2.1 基本原理

大语言模型是一种基于Transformer架构的神经网络,通过在海量文本数据上进行自监督学习(预测下一个词)来训练。训练完成后,LLMs学会了语言的统计规律和世界的一些基本常识。

LLMs的核心能力是条件文本生成:给定一个提示词(Prompt),模型会根据训练数据中学到的模式,生成一个合理的续接文本。

5.2.2 关键特性

在与知识图谱结合时,LLMs的以下几个特性尤为重要:

  1. 上下文学习(In-Context Learning):LLMs可以在不更新参数的情况下,仅通过提示词中的几个示例就学会执行新任务。
  2. 思维链(Chain-of-Thought, CoT):当被要求逐步思考时,LLMs可以进行更复杂的推理。
  3. 工具使用(Tool Use):LLMs可以被训练或提示来使用外部工具,如搜索引擎、计算器,当然也包括知识图谱。
5.3 智能体(Agent)的核心概念
5.3.1 定义与组成

在人工智能领域,Agent可以被定义为一个能够感知环境、做出决策并采取行动的实体。一个典型的Agent包含以下几个部分:

  1. 感知模块:负责获取环境信息,如用户的输入。
  2. 推理模块:负责处理信息、制定计划、做出决策。
  3. 行动模块:负责执行决策,如调用工具、生成回答。
  4. 记忆模块:负责存储历史信息和知识。
5.3.2 LLM作为Agent的核心

近年来,一种趋势是将LLMs作为Agent的核心推理模块。这种Agent被称为LLM-based Agent,它利用LLMs强大的语言理解和生成能力来实现感知、推理和行动的统一。

在本文中,我们将构建一个这样的Agent,但不同的是,我们将为它配备知识图谱作为外部记忆和工具。

5.4 事实推理(Factual Reasoning)的核心概念
5.4.1 定义

事实推理是指基于已知的事实,通过逻辑推理得出新的事实或结论的过程。在知识图谱与LLM结合的场景下,事实推理通常涉及以下几种类型:

  1. 单跳推理:直接查询一个关系,如“谁导演了《盗梦空间》?”
  2. 多跳推理:需要经过多个关系才能得到答案,如“《盗梦空间》的导演还导演过哪些评分在8.5分以上的电影?”
  3. 比较推理:比较多个实体的属性,如“《盗梦空间》和《星际穿越》哪部评分更高?”
  4. 聚合推理:对多个结果进行聚合,如“克里斯托弗·诺兰导演的电影平均评分是多少?”
5.4.2 挑战

事实推理面临的主要挑战包括:

  • 如何将自然语言问题转换为结构化的查询?
  • 如何处理推理过程中的不确定性?
  • 如何处理知识图谱中缺失的信息?
  • 如何在推理效率和准确性之间取得平衡?
5.5 KG与LLM结合的架构模式

将知识图谱与大语言模型结合,目前主要有三种主流的架构模式,我们可以用一个图来表示它们之间的关系:

模式1: KG增强LLM (KG-augmented LLM)

生成检索查询

返回相关知识

模式3: 协同推理 (Synergistic Reasoning)

选择和调用

返回结果

综合结果

中央控制器/LLM

工具集

知识图谱工具

其他工具

最终答案

模式2: LLM增强KG (LLM-augmented KG)

自然语言

图查询

结果

自然语言回答

知识图谱

用户问题

自然语言接口

用户问题

大语言模型

知识图谱

最终答案

知识图谱作为信息源

信息检索

让我们详细介绍这三种模式:

5.5.1 模式1:KG增强LLM (KG-augmented LLM)

在这种模式中,知识图谱作为一个外部信息源,为LLMs提供额外的知识。具体流程如下:

  1. 问题理解:LLMs理解用户的问题。
  2. 查询生成:LLMs根据问题生成一个或多个检索查询(可能是关键词,也可能是结构化查询)。
  3. 知识检索:使用生成的查询从知识图谱中检索相关的知识。
  4. 知识整合:将检索到的知识作为上下文,与原始问题一起输入给LLMs。
  5. 回答生成:LLMs根据上下文和问题生成最终的回答。

这种模式的优点是实现相对简单,能够利用LLMs强大的语言能力。缺点是检索到的知识可能不完整或不相关,且LLMs可能仍然会忽略检索到的知识而产生幻觉。

典型应用:检索增强生成(RAG)系统,其中知识图谱是检索源之一。

5.5.2 模式2:LLM增强KG (LLM-augmented KG)

在这种模式中,LLMs主要作为知识图谱的自然语言接口,帮助用户更方便地与知识图谱交互。具体流程如下:

  1. 自然语言转查询:LLMs将用户的自然语言问题转换为结构化的图查询(如Cypher)。
  2. 查询执行:在知识图谱上执行生成的查询。
  3. 结果转自然语言:LLMs将查询结果转换为自然语言回答。

这种模式的优点是回答高度依赖知识图谱,准确性高,可解释性强。缺点是受限于知识图谱的覆盖范围,如果知识图谱中没有相关信息,系统就无法回答。此外,将自然语言准确转换为图查询也是一个挑战。

典型应用:基于知识图谱的问答系统(KBQA)。

5.5.3 模式3:协同推理 (Synergistic Reasoning)

这种模式是前两种模式的结合,也是最复杂但最强大的一种模式。在这种模式中,LLMs作为一个中央控制器(Agent),能够根据需要自主选择使用知识图谱还是其他工具(如搜索引擎、计算器)。具体流程如下:

  1. 问题分析:LLMs分析用户的问题,决定需要哪些信息。
  2. 工具选择:LLMs选择合适的工具(可能是知识图谱,也可能是其他工具)。
  3. 工具使用:LLMs生成工具调用指令,执行工具调用。
  4. 结果整合:LLMs整合工具返回的结果。
  5. 迭代:如果需要,LLMs可能会多次调用工具,直到获得足够的信息。
  6. 回答生成:LLMs生成最终的回答。

这种模式的优点是灵活性高,能够处理复杂的任务,结合了多种工具的优势。缺点是实现复杂,需要精心设计提示词和工具接口。

典型应用:高级Agent系统,如AutoGPT、Microsoft 365 Copilot。

5.6 概念对比与联系

为了帮助大家更好地理解这些概念,我们用一个表格来对比一下三种结合模式:

特性KG增强LLMLLM增强KG协同推理
核心思想KG作为LLM的外部知识源LLM作为KG的自然语言接口LLM作为控制器,协同使用KG和其他工具
LLM的角色回答生成者翻译器(NL⇄查询)决策者和协调者
KG的角色信息提供者核心知识库工具之一
准确性中等高(取决于工具选择)
灵活性低(受限于KG)最高
可解释性中等中等(可通过工具调用链解释)
实现难度低到中等中等
适用场景开放域问答封闭域事实查询复杂任务处理

这些模式之间并不是完全互斥的,在实际应用中,我们可能会根据具体需求混合使用它们。在本文的项目中,我们将主要实现模式2模式3的结合,因为它们能更好地展示知识图谱与LLMs结合的威力。


6. 环境准备 (Environment Setup)

在这一章中,我们将准备好开发环境,为后续的实践项目做好准备。我们将使用Python作为主要编程语言,Neo4j作为图数据库,LangChain作为开发框架,并使用OpenAI的GPT模型作为LLM。

6.1 所需软件与库

首先,让我们列出所需的软件和库:

  1. Python 3.9+:我们将使用Python的最新特性,建议使用3.9或更高版本。
  2. Neo4j图数据库:我们将使用Neo4j Community Edition,这是一个免费的开源版本。
  3. Python库
    • openai:用于访问OpenAI的API。
    • neo4j:用于连接和操作Neo4j数据库。
    • langchain:用于构建LLM应用的框架。
    • langchain-openai:LangChain与OpenAI的集成。
    • python-dotenv:用于管理环境变量。
6.2 安装步骤
6.2.1 安装Python

如果你还没有安装Python,可以从Python官网下载并安装。安装完成后,你可以在终端中运行以下命令来检查版本:

python --version

确保版本号大于等于3.9。

6.2.2 安装Neo4j

我们将使用Neo4j Desktop来方便地管理数据库,你也可以直接安装Neo4j Community Edition。

使用Neo4j Desktop(推荐):

  1. Neo4j官网下载并安装Neo4j Desktop。
  2. 打开Neo4j Desktop,创建一个新项目。
  3. 在项目中添加一个新的数据库,记住你设置的用户名和密码(默认用户名是neo4j)。
  4. 启动数据库。

直接安装Neo4j Community Edition:

  1. Neo4j下载中心下载适合你操作系统的版本。
  2. 按照官方文档进行安装和配置。
  3. 启动数据库。

安装完成后,你可以通过浏览器访问 http://localhost:7474 来打开Neo4j Browser,这是一个用于操作Neo4j的Web界面。

6.2.3 安装Python库

我们将使用pip来安装所需的Python库。为了避免依赖冲突,建议先创建一个虚拟环境:

# 创建虚拟环境
python -m venv kg-llm-env

# 激活虚拟环境
# Windows:
kg-llm-env\Scripts\activate
# macOS/Linux:
source kg-llm-env/bin/activate

然后,安装所需的库:

pip install openai neo4j langchain langchain-openai python-dotenv

你也可以创建一个requirements.txt文件,内容如下:

openai>=1.0.0
neo4j>=5.0.0
langchain>=0.1.0
langchain-openai>=0.0.5
python-dotenv>=1.0.0

然后运行:

pip install -r requirements.txt
6.3 配置环境变量

我们需要设置一些环境变量,包括OpenAI的API密钥和Neo4j的连接信息。创建一个名为.env的文件,内容如下:

# OpenAI配置
OPENAI_API_KEY=your_openai_api_key_here

# Neo4j配置
NEO4J_URI=bolt://localhost:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=your_neo4j_password_here

注意

  1. 请将your_openai_api_key_here替换为你的真实OpenAI API密钥。如果你没有,可以在OpenAI官网注册并获取。
  2. 请将your_neo4j_password_here替换为你设置的Neo4j密码。
  3. 确保.env文件不会被提交到版本控制系统中(你可以将其添加到.gitignore文件)。
6.4 测试环境

现在,让我们编写一个简单的脚本来测试环境是否配置正确。创建一个名为test_env.py的文件:

import os
from dotenv import load_dotenv
from neo4j import GraphDatabase
from openai import OpenAI

# 加载环境变量
load_dotenv()

def test_neo4j_connection():
    """测试Neo4j连接"""
    uri = os.getenv("NEO4J_URI")
    user = os.getenv("NEO4J_USER")
    password = os.getenv("NEO4J_PASSWORD")
    
    try:
        driver = GraphDatabase.driver(uri, auth=(user, password))
        driver.verify_connectivity()
        print("✅ Neo4j连接成功!")
        
        # 执行一个简单的查询
        with driver.session() as session:
            result = session.run("RETURN 1 AS num")
            record = result.single()
            print(f"✅ 执行查询成功,结果: {record['num']}")
        
        driver.close()
        return True
    except Exception as e:
        print(f"❌ Neo4j连接失败: {e}")
        return False

def test_openai_connection():
    """测试OpenAI连接"""
    api_key = os.getenv("OPENAI_API_KEY")
    
    try:
        client = OpenAI(api_key=api_key)
        
        # 发送一个简单的请求
        response = client.chat.completions.create(
            model="gpt-3.5-turbo",
            messages=[{"role": "user", "content": "你好,请回复'连接成功'"}],
            temperature=0
        )
        
        message = response.choices[0].message.content.strip()
        if message == "连接成功":
            print(f"✅ OpenAI连接成功!模型回复: {message}")
            return True
        else:
            print(f"⚠️ OpenAI连接成功,但回复不符合预期: {message}")
            return True
    except Exception as e:
        print(f"❌ OpenAI连接失败: {e}")
        return False

if __name__ == "__main__":
    print("开始测试环境...")
    neo4j_ok = test_neo4j_connection()
    openai_ok = test_openai_connection()
    
    if neo4j_ok and openai_ok:
        print("\n🎉 所有环境测试通过!")
    else:
        print("\n❌ 部分环境测试失败,请检查配置。")

运行这个脚本:

python test_env.py

如果一切正常,你应该会看到所有测试都通过的消息。如果有任何错误,请根据提示检查你的配置。


7. 分步实现:构建知识图谱 (Step-by-Step Implementation: Building the Knowledge Graph)

现在,我们的环境已经准备好了,接下来我们将开始构建一个知识图谱。为了让示例既有趣又实用,我们将构建一个电影知识图谱,包含电影、演员、导演、类型等实体以及它们之间的关系。

7.1 设计图谱Schema

在构建知识图谱之前,我们首先需要设计它的Schema(模式),也就是定义我们有哪些实体类型、哪些关系类型以及哪些属性。

我们的电影知识图谱将包含以下内容:

实体类型(Labels):

  1. Person:人物,包括演员和导演
  2. Movie:电影
  3. Genre:电影类型

关系类型(Relationship Types):

  1. ACTED_IN:演员参演电影
  2. DIRECTED:导演执导电影
  3. IN_GENRE:电影属于某个类型

属性:

  • Personname(姓名),birthYear(出生年份)
  • Movietitle(标题),releaseYear(上映年份),rating(评分)
  • Genrename(类型名称)
  • ACTED_INrole(角色名)

我们可以用一个ER图来表示这个Schema:

参演

执导

被参演

被执导

属于

包含

PERSON

string

name

int

birthYear

ACTED_IN

string

role

DIRECTED

MOVIE

string

title

int

releaseYear

float

rating

IN_GENRE

GENRE

string

name

7.2 准备数据

为了方便演示,我们将手动准备一些电影数据。在实际应用中,你可能需要从公开数据源(如IMDb、Wikidata)获取数据,或者使用信息抽取技术从文本中提取知识。

我们将选择几部克里斯托弗·诺兰的经典电影以及一些相关的演员和导演:

人物:

  • 克里斯托弗·诺兰(Christopher Nolan),1970年出生,导演
  • 莱昂纳多·迪卡普里奥(Leonardo DiCaprio),1974年出生,演员
  • 约瑟夫·高登-莱维特(Joseph Gordon-Levitt),1981年出生,演员
  • 艾伦·佩吉(Ellen Page),1987年出生,演员
  • 马修·麦康纳(Matthew McConaughey),1969年出生,演员
  • 安妮·海瑟薇(Anne Hathaway),1982年出生,演员
  • 克里斯蒂安·贝尔(Christian Bale),1974年出生,演员
  • 希斯·莱杰(Heath Ledger),1979年出生,演员

电影:

  • 《盗梦空间》(Inception),2010年上映,评分8.8
  • 《星际穿越》(Interstellar),2014年上映,评分8.6
  • 《蝙蝠侠:黑暗骑士》(The Dark Knight),2008年上映,评分9.0

类型:

  • 科幻(Sci-Fi)
  • 动作(Action)
  • 惊悚(Thriller)
  • 剧情(Drama)
  • 犯罪(Crime)

关系:

  • 克里斯托弗·诺兰执导了这三部电影
  • 莱昂纳多·迪卡普里奥在《盗梦空间》中饰演Cobb
  • 约瑟夫·高登-莱维特在《盗梦空间》中饰演Arthur
  • 艾伦·佩吉在《盗梦空间》中饰演Ariadne
  • 马修·麦康纳在《星际穿越》中饰演Cooper
  • 安妮·海瑟薇在《星际穿越》中饰演Brand
  • 克里斯蒂安·贝尔在《蝙蝠侠:黑暗骑士》中饰演Bruce Wayne / Batman
  • 希斯·莱杰在《蝙蝠侠:黑暗骑士》中饰演Joker
  • 《盗梦空间》属于科幻、动作、惊悚类型
  • 《星际穿越》属于科幻、剧情类型
  • 《蝙蝠侠:黑暗骑士》属于动作、剧情、犯罪类型
7.3 创建Cypher语句导入数据

现在,我们将编写Cypher语句来创建这些实体和关系。创建一个名为import_data.cypher的文件:

// 创建人物节点
CREATE (nolan:Person {name: 'Christopher Nolan', birthYear: 1970})
CREATE (dicaprio:Person {name: 'Leonardo DiCaprio', birthYear: 1974})
CREATE (gordon_levitt:Person {name: 'Joseph Gordon-Levitt', birthYear: 1981})
CREATE (page:Person {name: 'Ellen Page', birthYear: 1987})
CREATE (mcconaughey:Person {name: 'Matthew McConaughey', birthYear: 1969})
CREATE (hathaway:Person {name: 'Anne Hathaway', birthYear: 1982})
CREATE (bale:Person {name: 'Christian Bale', birthYear: 1974})
CREATE (ledger:Person {name: 'Heath Ledger', birthYear: 1979})

// 创建电影节点
CREATE (inception:Movie {title: 'Inception', releaseYear: 2010, rating: 8.8})
CREATE (interstellar:Movie {title: 'Interstellar', releaseYear: 2014, rating: 8.6})
CREATE (dark_knight:Movie {title: 'The Dark Knight', releaseYear: 2008, rating: 9.0})

// 创建类型节点
CREATE (sci_fi:Genre {name: 'Sci-Fi'})
CREATE (action:Genre {name: 'Action'})
CREATE (thriller:Genre {name: 'Thriller'})
CREATE (drama:Genre {name: 'Drama'})
CREATE (crime:Genre {name: 'Crime'})

// 创建导演关系
CREATE (nolan)-[:DIRECTED]->(inception)
CREATE (nolan)-[:DIRECTED]->(interstellar)
CREATE (nolan)-[:DIRECTED]->(dark_knight)

// 创建演员关系
CREATE (dicaprio)-[:ACTED_IN {role: 'Cobb'}]->(inception)
CREATE (gordon_levitt)-[:ACTED_IN {role: 'Arthur'}]->(inception)
CREATE (page)-[:ACTED_IN {role: 'Ariadne'}]->(inception)
CREATE (mcconaughey)-[:ACTED_IN {role: 'Cooper'}]->(interstellar)
CREATE (hathaway)-[:ACTED_IN {role: 'Brand'}]->(interstellar)
CREATE (bale)-[:ACTED_IN {role: 'Bruce Wayne / Batman'}]->(dark_knight)
CREATE (ledger)-[:ACTED_IN {role: 'Joker'}]->(dark_knight)

// 创建类型关系
CREATE (inception)-[:IN_GENRE]->(sci_fi)
CREATE (inception)-[:IN_GENRE]->(action)
CREATE (inception)-[:IN_GENRE]->(thriller)
CREATE (interstellar)-[:IN_GENRE]->(sci_fi)
CREATE (interstellar)-[:IN_GENRE]->(drama)
CREATE (dark_knight)-[:IN_GENRE]->(action)
CREATE (dark_knight)-[:IN_GENRE]->(drama)
CREATE (dark_knight)-[:IN_GENRE]->(crime)

现在,我们可以通过Neo4j Browser来执行这些Cypher语句,或者我们也可以编写一个Python脚本来自动导入数据。让我们选择后者,这样更方便自动化。

创建一个名为build_kg.py的文件:

import os
from dotenv import load_dotenv
from neo4j import GraphDatabase

# 加载环境变量
load_dotenv()

def clear_database(session):
    """清空数据库(谨慎使用!)"""
    print("正在清空数据库...")
    session.run("MATCH (n) DETACH DELETE n")
    print("✅ 数据库已清空")

def import_data(session):
    """导入数据"""
    print("正在导入数据...")
    
    # 读取Cypher文件
    with open("import_data.cypher", "r", encoding="utf-8") as f:
        cypher = f.read()
    
    # 执行Cypher语句
    session.run(cypher)
    print("✅ 数据导入完成")

def verify_data(session):
    """验证数据是否导入成功"""
    print("正在验证数据...")
    
    # 统计节点数量
    person_count = session.run("MATCH (p:Person) RETURN count(p) AS count").single()["count"]
    movie_count = session.run("MATCH (m:Movie) RETURN count(m) AS count").single()["count"]
    genre_count = session.run("MATCH (g:Genre) RETURN count(g) AS count").single()["count"]
    
    # 统计关系数量
    acted_in_count = session.run("MATCH ()-[r:ACTED_IN]->() RETURN count(r) AS count").single()["count"]
    directed_count = session.run("MATCH ()-[r:DIRECTED]->() RETURN count(r) AS count").single()["count"]
    in_genre_count = session.run("MATCH ()-[r:IN_GENRE]->() RETURN count(r) AS count").single()["count"]
    
    print(f"✅ 数据统计:")
    print(f"   - 人物节点: {person_count}")
    print(f"   - 电影节点: {movie_count}")
    print(f"   - 类型节点: {genre_count}")
    print(f"   - 参演关系: {acted_in_count}")
    print(f"   - 导演关系: {directed_count}")
    print(f"   - 类型关系: {in_genre_count}")
    
    # 验证几个具体的查询
    print(f"\n✅ 执行几个验证查询:")
    
    # 查询1: 谁导演了《盗梦空间》?
    result = session.run("""
        MATCH (p:Person)-[:DIRECTED]->(m:Movie {title: 'Inception'})
        RETURN p.name AS director
    """)
    director = result.single()["director"]
    print(f"   - 《盗梦空间》的导演是: {director}")
    
    # 查询2: 《盗梦空间》有哪些演员?
    result = session.run("""
        MATCH (p:Person)-[:ACTED_IN]->(m:Movie {title: 'Inception'})
        RETURN p.name AS actor, collect(r.role) AS roles
    """)
    actors = [record["actor"] for record in result]
    print(f"   - 《盗梦空间》的演员有: {', '.join(actors)}")
    
    # 查询3: 克里斯托弗·诺兰导演了哪些评分在8.5以上的电影?
    result = session.run("""
        MATCH (p:Person {name: 'Christopher Nolan'})-[:DIRECTED]->(m:Movie)
        WHERE m.rating >= 8.5
        RETURN m.title AS title, m.rating AS rating
        ORDER BY m.rating DESC
    """)
    movies = [f"{record['title']} ({record['rating']})" for record in result]
    print(f"   - 诺兰导演的评分8.5以上的电影: {', '.join(movies)}")

def main():
    # 连接数据库
    uri = os.getenv("NEO4J_URI")
    user = os.getenv("NEO4J_USER")
    password = os.getenv("NEO4J_PASSWORD")
    
    driver = GraphDatabase.driver(uri, auth=(user, password))
    
    with driver.session() as session:
        # 询问用户是否要清空数据库
        response = input("是否要清空数据库?这将删除所有数据!(y/n): ")
        if response.lower() == "y":
            clear_database(session)
        
        # 导入数据
        import_data(session)
        
        # 验证数据
        verify_data(session)
    
    driver.close()
    print("\n🎉 知识图谱构建完成!")

if __name__ == "__main__":
    main()

现在,让我们运行这个脚本来构建知识图谱:

python build_kg.py

当被询问是否要清空数据库时,如果你是第一次运行,可以选择n,因为数据库是空的。但如果你之前已经运行过,建议选择y来避免重复数据。

如果一切正常,你应该会看到数据导入和验证成功的消息。你也可以打开Neo4j Browser,运行以下查询来查看图谱:

MATCH (n) RETURN n

你应该能看到一个漂亮的图,展示了我们的电影、人物和类型之间的关系。


8. 分步实现:开发KG-LLM交互层 (Step-by-Step Implementation: Developing the KG-LLM Interaction Layer)

现在,我们已经有了一个知识图谱,接下来我们需要开发一个交互层,让LLMs能够与知识图谱进行交互。在这一章中,我们将实现两种关键的交互方式:

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐