技术

agentic chat 图数据库的一些考量 LLM一些探索 Agent实践 LLM预训练 向量数据库的一些考量 fastapi+sqlalchemy进行项目开发 LLM微调实践 Python协程实现 Agent Functon Calling LLamaIndex入门 Multi-Agent探索 Python虚拟机 LLM工作流编排 Python实践 下一个平台Agent 激发LLM涌现——提示工程 LLM微调理论 大佬沉思 LLM外挂知识库 LLMOps 多模态LLM Python一些比较有意思的库 Transformers源码学习 LangChain源码学习 通用分布式计算引擎Ray Python并发 go依赖注入 go collection gc的基本原理 golang性能分析及优化 数据湖 高性能计算与存储 Linux2.1.13网络源代码学习 《大数据经典论文解读》 三驾马车学习 Spark 内存管理及调优 Yarn学习 从Spark部署模式开始讲源码分析 容器狂占内存资源怎么办? 多角度理解一致性 golang io使用及优化模式 Flink学习 c++学习 学习ebpf go设计哲学 ceph学习 学习mesh kvm虚拟化 学习MQ go编译器以及defer实现 学习go 为什么要有堆栈 汇编语言 计算机组成原理 运行时和库 Prometheus client mysql 事务 mysql 事务的隔离级别 mysql 索引 坏味道 学习分布式 学习网络 学习Linux go堆内存分配 golang 系统调用与阻塞处理 Goroutine 调度过程 重新认识cpu mosn有的没的 负载均衡泛谈 单元测试的新解读 《Redis核心技术与实现》笔记 《Prometheus监控实战》笔记 Prometheus 告警学习 calico源码分析 对容器云平台的理解 Prometheus 源码分析 并发的成本 基础设施优化 hashicorp raft源码学习 docker 架构 mosn细节 与微服务框架整合 Java动态代理 编程范式 并发通信模型 《网络是怎样连接的》笔记 go channel codereview gc分析 jvm 线程实现 go打包机制 go interface及反射 如何学习Kubernetes 《编译原理之美》笔记——后端部分 《编译原理之美》笔记——前端部分 Pilot MCP协议分析 go gc 内存管理玩法汇总 软件机制 istio流量管理 Pilot源码分析 golang io 学习Spring mosn源码浅析 MOSN简介 《datacenter as a computer》笔记 学习JVM Tomcat源码分析 Linux可观测性 学习存储 学计算 Gotty源码分析 kubernetes operator kaggle泰坦尼克问题实践 kubernetes扩缩容 神经网络模型优化 直觉上理解深度学习 如何学习机器学习 TIDB源码分析 什么是云原生 Alibaba Java诊断工具Arthas TIDB存储——TIKV 《Apache Kafka源码分析》——简介 netty中的线程池 guava cache 源码分析 Springboot 启动过程分析 Spring 创建Bean的年代变迁 Linux内存管理 自定义CNI IPAM 共识算法 spring redis 源码分析 kafka实践 spring kafka 源码分析 Linux进程调度 让kafka支持优先级队列 Codis源码分析 Redis源码分析 C语言学习 《趣谈Linux操作系统》笔记 docker和k8s安全访问机制 jvm crash分析 Prometheus 学习 Kubernetes监控 Kubernetes 控制器模型 容器日志采集 容器狂占资源怎么办? Kubernetes资源调度——scheduler 时序性数据库介绍及对比 influxdb入门 maven的基本概念 《Apache Kafka源码分析》——server Kubernetes类型系统 源码分析体会 《数据结构与算法之美》——算法新解 Kubernetes源码分析——controller mananger Kubernetes源码分析——apiserver Kubernetes源码分析——kubelet Kubernetes介绍 ansible学习 Kubernetes源码分析——从kubectl开始 jib源码分析之Step实现 线程排队 jib源码分析之细节 跨主机容器通信 jib源码分析及应用 为容器选择一个合适的entrypoint kubernetes yaml配置 《持续交付36讲》笔记 mybatis学习 程序猿应该知道的 无锁数据结构和算法 CNI——容器网络是如何打通的 为什么很多业务程序猿觉得数据结构和算法没用? 串一串一致性协议 当我在说PaaS时,我在说什么 《数据结构与算法之美》——数据结构笔记 PouchContainer技术分享体会 harbor学习 用groovy 来动态化你的代码 精简代码的利器——lombok 学习 《深入剖析kubernetes》笔记 编程语言那些事儿 rxjava3——背压 rxjava2——线程切换 spring cloud 初识 《深入拆解java 虚拟机》笔记 《how tomcat works》笔记 hystrix 学习 rxjava1——概念 Redis 学习 TIDB 学习 如何分发计算 Storm 学习 AQS1——论文学习 Unsafe Spark Stream 学习 linux vfs轮廓 《自己动手写docker》笔记 java8 实践 中本聪比特币白皮书 细读 区块链泛谈 比特币 大杂烩 总纲——如何学习分布式系统 hbase 泛谈 forkjoin 泛谈 看不见摸不着的cdn是啥 《jdk8 in action》笔记 程序猿视角看网络 bgp初识 calico学习 AQS——粗略的代码分析 我们能用反射做什么 web 跨域问题 《clean code》笔记 《Elasticsearch权威指南》笔记 mockito简介及源码分析 2017软件开发小结—— 从做功能到做系统 《Apache Kafka源码分析》——clients dns隐藏的一个坑 《mysql技术内幕》笔记 log4j学习 为什么netty比较难懂? 递归、回溯、动态规划 apollo client源码分析及看待面向对象设计 学习并发 docker运行java项目的常见问题 OpenTSDB 入门 spring事务小结 分布式事务 javascript应用在哪里 《netty in action》读书笔记 netty对http2协议的解析 ssl证书是什么东西 http那些事 苹果APNs推送框架pushy apple 推送那些事儿 编写java框架的几大利器 java内存模型和jvm内存布局 java exception Linux IO学习 netty内存管理 测试环境docker化实践 netty在框架中的使用套路 Nginx简单使用 《Linux内核设计的艺术》小结 Go并发机制及语言层工具 Linux网络源代码学习——数据包的发送与接收 《docker源码分析》小结 docker namespace和cgroup zookeeper三重奏 数据库的一些知识 Spark 泛谈 链式处理的那些套路 netty回顾 Thrift基本原理与实践(二) Thrift基本原理与实践(一) 回调 异步执行抽象——Executor与Future Docker0.1.0源码分析 java gc Jedis源码分析 深度学习泛谈 Linux网络命令操作 JTA与TCC 换个角度看待设计模式 Scala初识 向Hadoop学习NIO的使用 以新的角度看数据结构 并发控制相关的硬件与内核支持 systemd 简介 quartz 源码分析 基于docker搭建测试环境(二) spring aop 实现原理简述 自己动手写spring(八) 支持AOP 自己动手写spring(七) 类结构设计调整 分析log日志 自己动手写spring(六) 支持FactoryBean 自己动手写spring(九) 总结 自己动手写spring(五) bean的生命周期管理 自己动手写spring(四) 整合xml与注解方式 自己动手写spring(三) 支持注解方式 自己动手写spring(二) 创建一个bean工厂 自己动手写spring(一) 使用digester varnish 简单使用 关于docker image的那点事儿 基于docker搭建测试环境 分布式配置系统 JVM执行 git maven/ant/gradle/make使用 再看tcp kv系统 java nio的多线程扩展 《Concurrency Models》笔记 回头看Spring IOC IntelliJ IDEA使用 Java泛型 vagrant 使用 Go常用的一些库 Python初学 Goroutine 调度模型 虚拟网络 《程序员的自我修养》小结 Kubernetes存储 访问Kubernetes上的Service Kubernetes副本管理 Kubernetes pod 组件 Go基础 JVM类加载 硬币和扑克牌问题 LRU实现 virtualbox 使用 ThreadLocal小结 docker快速入门

架构

bert rerank微调 大模型推理tips RAG向量检索与微调 dddfirework源码分析 RAG与知识图谱 大模型推理服务框架vLLM 大模型推理服务框架 模型服务化(未完成) 大模型Post-Training 大模型训练 大模型推理 从Attention到Transformer k8s设备管理 ddd从理念到代码 如何应用LLM 小鼠如何驾驭大象(LLM)? 多类型负载协调员Koordinator controller-runtime细节分析 finops学习 kubevela多集群 kubevela中cue的应用 基于k8s的工作流 kubevela源码分析 容器和CPU那些事儿 数据集管理fluid 应用管理平台kubevela karmada支持crd 多集群管理 AutoML和AutoDL 特征平台 实时训练 分布式链路追踪 K8S YAML 资源清单管理方案 tensorflow原理——python层分析 如何学习tensorflow 数据并行——allreduce 数据并行——ps 推荐系统embedding原理及实践 机器学习中的python调用c 机器学习训练框架概述 tensornet源码分析 大模型训练和推理 X的生成——特征工程 tvm tensorflow原理——core层分析 模型演变 《深度学习推荐系统实战》笔记 keras 和 Estimator tensorflow分布式训练 分布式训练的一些问题 基于Volcano的弹性训练 图神经网络 pytorch弹性分布式训练 从混部到统一调度 从RNN到Attention pytorch分布式训练 CNN 《动手学深度学习》笔记 pytorch与线性回归 多活 volcano特性源码分析 推理服务 kubebuilder 学习 mpi 学习pytorch client-go学习 提高gpu 利用率 GPU与容器的结合 GPU入门 AI云平台梳理 tensorflow学习 tf-operator源码分析 k8s批处理调度/Job调度 喜马拉雅容器化实践 Kubernetes 实践 学习rpc BFF openkruise学习 可观察性和监控系统 基于Kubernetes选主及应用 《许式伟的架构课》笔记 Admission Controller 与 Admission Webhook 发布平台系统设计 k8s水平扩缩容 Scheduler如何给Node打分 Scheduler扩展 深入controller openkruise cloneset学习 controller-runtime源码分析 pv与pvc实现 csi学习 client-go informer源码分析 kubelet 组件分析 调度实践 Pod是如何被创建出来的? 《软件设计之美》笔记 mecha 架构学习 Kubernetes events学习及应用 CRI——kubelet与容器引擎之间的接口 资源调度泛谈 业务系统设计原则 grpc学习 元编程 以应用为中心 istio学习 下一代微服务Service Mesh 《实现领域驱动设计》笔记 概率论 serverless 泛谈 《架构整洁之道》笔记 处理复杂性 那些年追过的并发 服务器端编程 网络通信协议 架构大杂烩 如何学习架构 《反应式设计模式》笔记 项目的演化特点 反应式架构摸索 函数式编程的设计模式 服务化 ddd反模式——CRUD的败笔 研发效能平台 重新看面向对象设计 业务系统设计的一些体会 函数式编程 《左耳听风》笔记 业务程序猿眼中的微服务管理 DDD实践——CQRS 项目隔离——案例研究 《编程的本质》笔记 系统故障排查汇总及教训 平台支持类系统的几个点 代码腾挪的艺术 abtest 系统设计汇总 《从0开始学架构》笔记 初级权限系统设计 领域驱动理念 现有上传协议分析 移动网络下的文件上传要注意的几个问题 推送系统的几个基本问题 做配置中心要想好的几个基本问题 不同层面的异步 分层那些事儿 性能问题分析 用户认证问题 资源的分配与回收——池 消息/任务队列

标签

k8s设备管理 多类型负载协调员Koordinator controller-runtime细节分析 finops学习 kubevela多集群 kubevela中cue的应用 基于k8s的工作流 kubevela源码分析 容器和CPU那些事儿 数据集管理fluid 应用管理平台kubevela karmada支持crd 多集群管理 K8S YAML 资源清单管理方案 从混部到统一调度 volcano特性源码分析 kubebuilder 学习 client-go学习 tf-operator源码分析 k8s批处理调度/Job调度 喜马拉雅容器化实践 Kubernetes 实践 openkruise学习 基于Kubernetes选主及应用 Admission Controller 与 Admission Webhook k8s水平扩缩容 Scheduler如何给Node打分 Scheduler扩展 深入controller openkruise cloneset学习 controller-runtime源码分析 pv与pvc实现 csi学习 client-go informer源码分析 kubelet 组件分析 调度实践 Pod是如何被创建出来的? Kubernetes events学习及应用 CRI——kubelet与容器引擎之间的接口 资源调度泛谈 如何学习Kubernetes 以应用为中心 kubernetes operator kubernetes扩缩容 serverless 泛谈 什么是云原生 自定义CNI IPAM docker和k8s安全访问机制 Kubernetes监控 Kubernetes 控制器模型 Kubernetes资源调度——scheduler Kubernetes类型系统 Kubernetes源码分析——controller mananger Kubernetes源码分析——apiserver Kubernetes源码分析——kubelet Kubernetes介绍 Kubernetes源码分析——从kubectl开始 kubernetes yaml配置 CNI——容器网络是如何打通的 当我在说PaaS时,我在说什么 《深入剖析kubernetes》笔记 Kubernetes存储 访问Kubernetes上的Service Kubernetes副本管理 Kubernetes pod 组件
agentic chat bert rerank微调 大模型推理tips LLM一些探索 Agent实践 LLM预训练 RAG向量检索与微调 LLM微调实践 RAG与知识图谱 大模型推理服务框架vLLM Agent Functon Calling LLamaIndex入门 Multi-Agent探索 LLM工作流编排 大模型推理服务框架 模型服务化(未完成) 大模型Post-Training 大模型训练 大模型推理 从Attention到Transformer 下一个平台Agent 激发LLM涌现——提示工程 LLM微调理论 大佬沉思 LLM外挂知识库 LLMOps 多模态LLM Transformers源码学习 LangChain源码学习 如何应用LLM 小鼠如何驾驭大象(LLM)? AutoML和AutoDL 特征平台 实时训练 tensorflow原理——python层分析 如何学习tensorflow 数据并行——allreduce 数据并行——ps 推荐系统embedding原理及实践 机器学习中的python调用c 机器学习训练框架概述 tensornet源码分析 大模型训练和推理 X的生成——特征工程 tvm tensorflow原理——core层分析 模型演变 《深度学习推荐系统实战》笔记 keras 和 Estimator tensorflow分布式训练 分布式训练的一些问题 基于Volcano的弹性训练 图神经网络 pytorch弹性分布式训练 从RNN到Attention pytorch分布式训练 CNN 《动手学深度学习》笔记 pytorch与线性回归 推理服务 mpi 学习pytorch 提高gpu 利用率 GPU与容器的结合 GPU入门 AI云平台梳理 tensorflow学习 kaggle泰坦尼克问题实践 神经网络模型优化 概率论 直觉上理解深度学习 如何学习机器学习 深度学习泛谈

RAG与知识图谱

2024年07月12日

简介

传统的 RAG 技术仍然依赖于文本匹配,无法真正理解文本背后的语义和逻辑关系,在处理复杂查询、捕捉细微差别等方面依然存在不足。想象一下,如果只是把一堆文件扔给学生,而不教给他们如何理解和分析,他们真的能找到问题的答案吗?Graph RAG 将知识图谱(KG)引入 RAG 体系,就像为 AI 构建了一张清晰的“知识地图”。知识图谱能够表达实体之间的复杂关系,例如父子关系、朋友关系、因果关系等等,从而让 AI 不仅能够“查到”信息,更能够“理解”信息之间的逻辑,给出更准确、更智能的答案。从依赖自身记忆到检索外部信息,再到利用知识图谱进行深度理解,Graph RAG 代表着 AI 问答技术的一次重大飞跃。知识图谱是对现实世界实体及其关系的结构化表示。它们由两个主要部分组成:节点和边。节点表示独立的实体,例如人物、地点、物体或概念。而边则表示节点之间的关系,表示它们如何相互关联。这种结构使 LLM 能够访问精确且与上下文相关的的数据,从而极大地提高了其生成信息丰富答案的能力。Graph RAG 的创新之处在于它将图数据库与 LLM 相结合,在生成答案之前丰富了模型的上下文。

典型 RAG 的核心是向量搜索,它接收一段文本,并从候选书面材料中返回概念上相似的文本。然而,在 RAG 上下文中,它们只有在您需要确定一些单词与另一个单词的相似程度时才有价值。但是,如果您想了解向量内部的内容,了解其周围的内容,掌握文本中表示的内容,或者了解其中任何一个如何适应更大的上下文,那么向量作为一种表示就无法做到这一点。相比之下,知识图谱是世界的声明性(或者用人工智能术语来说,是符号性)表示。因此,人类和机器都可以理解和推理知识图谱。此外,您可以查询、可视化、注释、修复和扩展知识图谱。知识图谱代表您的世界模型 - 代表您正在处理的领域的世界的一部分。Graph通过建立结点之间的连接,可以极其有效的丰富搜索结果。

  1. 比如query:感冒了吃什么?db:1、感冒了可能会发烧和头痛。2、对乙酰氨基酚是一种退烧和止疼药物。相似度搜索极难查询到第2个结果,而Graph方法可以将对1和2连接在一起,从而在检索到1结点的时候将相邻结点一起检索出来。
  2. 当你问:“这个数据集的主题是什么?”这类高级别、概括性的问题时,传统的RAG可能就会束手无策。为什么呢?那是因为这本质上是一个聚焦于查询的总结性任务(Query-Focused Summarization,QFS),而不是一个明确的检索任务。

  3. GraphRAG 的第一个(也是最直接、最明显的)好处是更高质量的响应。
  4. 治理:可解释性、安全性等

全局性问题

在 RAG 中使用知识图谱主要解决在大型文档库上问答和理解困难的问题,特别是那些普通 RAG 方法难以处理的全局性问题。普通 RAG 在回答针对整个文档库的全局性问题时表现不佳,例如问题:请告诉我所有关于 XXX 的事情,这个问题涉及到的上下文可能分布在整个大型文档库中,普通 RAG 的向量检索方法很难得到这种分散、细粒度的文档信息,向量检索经常使用 top-k 算法来获取最相近的上下文文档,这种方式很容易遗漏关联的文档块,从而导致信息检索不完整。另外是 LLM 的上下文窗口限制问题,对于全局性问题往往涉及到非常多的上下文文档,如果要全部提交给 LLM 则很容易超出 LLM 的窗口限制,而知识图谱将文档提取成实体关系后,实际上大大压缩了文档块的大小,从而让所有相关文档提交给 LLM 成为可能。

与普通 RAG 的区别

  1. 知识图谱 RAG 使用图结构来表示和存储信息,捕捉实体间的复杂关系,而普通 RAG 通常使用向量化的文本数据
    1. 随着数据量的不断增加,GraphRAG 能够通过自然地新增节点与关系,将新信息无缝集成到现有的知识图谱中,无需对已有数据进行大规模重组。传统的 RAG 系统主要依赖于文本块来存储数据,这些数据块往往是非结构化或半结构化的。随着知识库的扩展,可能导致相似内容的多版本重复存储,面对复杂或模糊的查询时,容易受到噪音数据的干扰。
  2. 知识图谱 RAG 通过图遍历和子图检索来获取相关信息,普通 RAG 主要依赖向量相似度搜索
    1. 在 GraphRAG 中,“iPhone 15 Pro” 和 “三星S24” 可能被理解为是相关的,即使在任何文本块中没有直接将它们进行比较。因为它们都属于“高端智能手机”类别,并且在”手机摄影”特性上表现突出。传统的RAG如果没有明确符合这一需求的文本块,就需要依赖编程来进行查询分解与处理计划。
  3. 知识图谱 RAG 能更好地理解实体间的关系和层次结构,提供更丰富的上下文,普通 RAG 在处理复杂关系时能力有限

GraphRAG适合的应用场景下面我们来分析一下GraphRAG适合的场景,通常来说,具备如下特征的数据和场景更适合使用GraphRAG。

  1. 第一类是有较多相互关联实体与复杂关系,且结构较明确的数据。比如:
    1. 人物关系网络数据:社交网络中的用户关系、历史人物关系、家族图谱等。
    2. 企业级关系数据:公司结构、供应链、客户等之间的关系。
    3. 医学类数据:疾病、症状、治疗、药物、传播、病例等之间复杂关系。
    4. 法律法规数据:法律条款之间的引用关系、解释、判例与适用法律条款的关系。
    5. 推荐系统数据:产品、用户、浏览内容、产品之间的关联、用户之间的关系等。
  2. 第二类是涉及复杂关系、语义推理和多步逻辑关联的查询,比如:
    1. 多跳关系查询:在华东区所有的门店中,哪个导购的消费者客单价最高?
    2. 知识推理查询:根据患者的症状和病史,推断可能的疾病并提供治疗方案。
    3. 聚合统计查询:在《三国演义》中,出场次数最多的人是谁?
    4. 时序关联查询:过去一年都有哪些AI大模型的投资与并购事件?
    5. 跨多文档查询:在《三体3》中,有哪些人物在《三体1》中出现?

基于GraphRAG的特点,我们可以提炼出一些GraphRAG适合的应用场景

  1. 智能客服:将产品手册或说明文档映射到知识图谱中,解答消费者各式各样的售后问题,不需要提前准备QA对,也不需要提前对问题进行扩写,GraphRAG可以在精确理解用户意图的基础上进行查找。
  2. 智能检修:通常电气设备的产品说明短则几百页,长则数千页,即便是最资深的运维工程师也很难完全消化。因此,可以将设备的故障排除指南映射到知识图谱中,当设备出现故障时,方便工程师快速排查和解决问题。
  3. 智能问诊:在医学领域,病人的症状和病因之间存在着非常复杂的因果关系,同时医学领域对精准度的要求也非常高,因此非常适合GraphRag的使用,比如智能问诊
  4. 药物合成:在医药领域,存在非常多的分子化合物,每种分子化合物的特性都相差迥异,不同的分子还能结合成新的分子化合物,其中的排列组合不胜枚举。因此,可以将各种已经发现的分子化合物的结构、特征映射到知识图谱中,帮助生物学家更高效地发现有益的大分子结构。
  5. 报告总结:在金融领域,行业分析师每天都要阅读海量的市场信息或研究报告,过去都得完全依靠人工提炼和总结。而通过GraphRAG,构建智能投研、智能投顾等系统,帮助研究员快速提炼分析报告的核心内容。

大体思路

GraphRAG在整体架构与传统RAG并无更大区别,区别在于检索的知识采用图结构的方式进行构建、存储并检索。

非结构化数据入知识图谱

构建一个非结构化数据的GraphRAG应用,首要任务是把非结构化数据转换成以图结构表示的知识图谱,并存储到GraphDB如Neo4j,用来提供后续检索与生成的基础。

知识图谱 RAG 在入库过程中会将文档块进行实体和关系的提取,提取出实体和关系后再将它们保存到图数据库中。实体提取的传统方法是基于预定义的规则和词典、统计机器学习或者深度学习等技术,但进入到 LLM 时代后,实体提取更多的是使用 LLM 来进行,因为 LLM 能够更好地理解文本的语义,实现也更加简单。比如在 LlamaIndex 的 KnowledgeGraphIndex 类中的实体提取提示词是这样的:

DEFAULT_KG_TRIPLET_EXTRACT_TMPL = (
    "Some text is provided below. Given the text, extract up to "
    "{max_knowledge_triplets} "
    "knowledge triplets in the form of (subject, predicate, object). Avoid stopwords.\n"
    "---------------------\n"
    "Example:"
    "Text: Alice is Bob's mother."
    "Triplets:\n(Alice, is mother of, Bob)\n"
    "Text: Philz is a coffee shop founded in Berkeley in 1982.\n"
    "Triplets:\n"
    "(Philz, is, coffee shop)\n"
    "(Philz, founded in, Berkeley)\n"
    "(Philz, founded in, 1982)\n"
    "---------------------\n"
    "Text: {text}\n"
    "Triplets:\n"
)

在提示词中要求 LLM 将文档块 text 提取成实体-关系-实体这样的三元组,实体一般是名词,表示文档块中的实体,关系是动词或者介词,表示实体之间的关系,并给出了几个 Few Shot,让 LLM 能更好地理解实体抽取的任务。将实体提取出来后,通常是将实体和关系保存到图数据库中,但也有一些知识图谱 RAG 的实现会将这些数据保存到文件中,然后通过其特有的算法来进行检索,比如微软的 GraphRAG。图数据库是一种专门用来存储图结构数据的数据库,它能够高效地存储和查询图数据,常见的图数据库有 Neo4j、ArangoDB 等。不同的图数据库有不同的查询语言,比如 Neo4j 的查询语言使用的是 Cypher,如果想要在 RAG 中使用 Neo4j 来存储知识图谱数据,那么掌握一些基础的 Cypher 语法是有必要的。

检索知识图谱

知识图谱 RAG 在检索过程中会将问题进行实体提取,将提取出来的实体通过图数据库进行检索,这样可以获取到名称相同的实体,以及与实体相关的实体和关系,最后将检索到的所有实体和关系提交给 LLM 进行答案生成。PS:根据query查到相关的实体entity,再根据配置的depth bfs一个范围的entity,得到一个entity/三元组列表。三元组列表作为一段文本,增强llm生成时用到的context。

可以看下 LlamaIndex 的 KGTableRetriever 类中提取问题关键字的提示词:

DEFAULT_QUERY_KEYWORD_EXTRACT_TEMPLATE_TMPL = (
    "A question is provided below. Given the question, extract up to {max_keywords} "
    "keywords from the text. Focus on extracting the keywords that we can use "
    "to best lookup answers to the question. Avoid stopwords.\n"
    "---------------------\n"
    "{question}\n"
    "---------------------\n"
    "Provide keywords in the following comma-separated format: 'KEYWORDS: <keywords>'\n"
)

提示词要求 LLM 从问题中提取出多个关键字,并用逗号分隔,这些关键字通常是问题中的实体。将问题的实体提取出来后,再用实体名称去图数据库中进行检索, 检索的原理就是使用图数据库的查询语句对每个实体进行检索,获取对应的三元组。以 Neo4j 图数据库为例,下面是一个简单的 Cypher 查询语句:

MATCH (n {name: 'Alice'})-[r]-(m)
RETURN n, r, m

这个查询语句的意思是查找图数据库中所有与实体 Alice 相关的实体和关系,这样就可以获取到 Alice 相关的所有三元组。最后将得到的数据转换为文本,作为问题的上下文,提交给 LLM 进行答案生成。

GraphRAG

PS:GraphRAG 有点侧重于回答总结问题。 GraphRAG:复杂查询的知识图谱新框架

与 RAG 系统类似,整个 GraphRAG 管道可以分为两个核心功能组件:索引和查询。 索引阶段的主要过程如下所示:

  1. 文本块拆分: 将原始文档拆分成多个文本块,这个过程与经典RAG的处理过程一样。
  2. 实体与关系提取: 对文本块借助LLM分析,提取实体与关系,这个过程与普通的Graph RAG也类似。
  3. 生成实体与关系摘要: 为提取的实体与关系生成简单的描述性信息,这是区别普通Graph RAG的一个步骤。这些描述性信息,会作为一个属性存放在实体/关系的Graph节点中。这种摘要的好处是可以借助嵌入向量更准确的对这些实体与关系进行检索,带来的负面影响是由于需要为大量的实体与关系生成描述信息,将会产生很多的LLM调用,这可能带来过长的耗时与昂贵的大模型API开销。
  4. 检测与识别社区: 借助社区检测算法,在Graph中识别出多个社区。
  5. 生成社区摘要: 借助LLM生成每个社区的摘要信息,用来了解数据集的全局主题结构和语义,这也是微软GraphRAG的核心价值所在,也是回答全局性问题的关键。

查询引擎是 GraphRAG 库的检索模块,负责以下任务:

  1. 本地搜索方法通过将模型从知识图谱中提取的相关数据与原始文档的文本块相结合,生成准确的答案。这种方法特别适用于需要深入了解文档中提到的特定实体的问题,例如“洋甘菊的治疗特性是什么?”具体来说,本地搜索方法在给定用户查询和可选的对话历史记录的情况下,从知识图谱中识别出一组与用户输入语义相关的实体。这些实体作为访问知识图谱的切入点,可以进一步提取相关的细节信息,如关联实体、关系、实体协变量以及社区报告。此外,该方法还从与这些识别出的实体相关的原始文档中提取相关的文本块。接着,将这些候选数据源进行优先级排序和筛选,以适应预定义大小的单个上下文窗口,用于生成对用户查询的最终响应。
  2. 全局搜索方法通过以 Map-Reduce 方式搜索所有由 LLM 生成的社区报告来生成答案。这是一种资源密集型的方法,但对于需要了解整个数据集的问题,如“数据中排名前五的主题是什么?”,通常能提供较好的结果。LLM 生成的知识图谱结构揭示了整个数据集的组织方式和主题分布。这使得我们能够将私有数据集组织成预先总结的、有意义的语义集群。通过全局搜索方法,LLM 能够在响应用户查询时,利用这些集群来总结相关主题。具体而言,当接收到用户查询和(可选的)对话历史记录时,全局搜索方法使用从知识图谱社区层次结构中指定级别获取的社区报告集合作为上下文数据,以 Map-Reduce 方式生成响应。在 Map 步骤中,社区报告被分割成预定义大小的文本块。每个文本块用于生成包含要点列表的中间响应,并为每个要点附加表示其重要性的数字评分。在 Reduce 步骤中,筛选出的最重要要点被聚合,并用作生成最终响应的上下文。全局搜索的响应质量可能会受到选择的社区层次结构级别的显著影响。较低级别的层次结构及其详细报告往往能够生成更全面的响应,但由于报告量大,也可能增加生成最终响应所需的时间和 LLM 资源。

local模式的关键在于召回相关上下文,global模式关键在于map-reduce过程

local模式特别适合用于回答关于具体事实的问题,例如查询某个实体的特定信息或者探索实体间的关系等。大致流程如下:

  1. 根据query和对话历史,借助实体的description_embedding向量进行相似度检索从知识图谱中识别出最相关的实体(即在Neo4j库中标签为__Entity__的节点)
    1. 也有用llm 从query 中直接提取实体名称的 大模型在知识图谱问答上的核心算法详细思路及实践
  2. 找到这些实体更多的信息,包含:
    • 关联的原始文本块。提取其文本内容
    • 关联的社区。提取其社区报告
    • 关联的实体。提取其实体描述信息
    • 关联的关系。提取其关系描述信息
    • 关联的协变量。由于默认不生成,这里忽略
  3. 对这些提取的信息进行排序与筛选,最终形成参考的上下文
  4. 借助LLM和prompt,生成最终响应

global模式查询的架构相对简单,它采用了分布式计算中的Map-Reduce架构。可以简单概括为:

  1. Map: 查询指定community_level的所有社区报告,使用Query、conversation_history 和 这些社区报告 调用多次LLM生成多个带有评分的中间响应
  2. Reduce:对中间响应进行排序后,将这些中间响应给LLM作为参考,让LLM输出最终答案

GraphRAG 太过复杂,如果想看简单版,可以参考 Nano-graphrag

回头看

从GraphRAG看信息的重新组织沿着GraphRAG的思路,在LLM的时代,信息可能以什么样的方式被重新组织?在一个数字化的世界里,数据有两种:一种是给人看的,一种是给机器看的。

  1. 给人看的数据,比如新闻、网页、论文、专利文本。这些数据是由人生产的,生产出来的目的也是给人看的,用于传递信息或知识。所以,它们天然就是无结构的free text。在LLM出现之前,计算机系统对这些信息进行直接处理,是很困难的。
  2. 另一种给机器看的数据,指的是传统计算机程序可以直接处理的结构化数据,比如xml、json、关系表格,等等。它们在计算机程序之间传递信息或指令,支撑整个系统的运转。这些数据通常有严格的schema的约束,大部分由机器产生,也由机器消费。 现在,我们把关注点放在前一类数据上。由于这部分数据是为了给人看的,所以如果涉及到对信息进行处理,就需要大量的人力参与其中。具体需要做什么处理呢?对于一个严肃的信息获取场景来说,抽象来看,人们通常需要对数据进行三个阶段的处理:检索;提取;整合。以前的信息系统,只能辅助完成第一步的检索工作。后面的提取、整合,仍然主要是人的工作。但是LLM出现之后,后面这两步也有希望由计算机程序来处理了。

为了对海量的信息进行检索和处理,信息系统需要对数据做某种形式的提前组织。搜索引擎算是一种传统的技术,它出于检索的目的,基于关键词将文本数据组织成倒排索引的形式。这种数据组织形式,简单可解释。但是,这种关键词索引的数据组织形式,只能支撑粗粒度的「检索」任务。数据的存储单元,是一个完整的document。比如,一个网页是一个document,一篇新闻报道是一个document,一篇论文是一个document。我们可以根据关键词,定位到若干个document。但有两个遗留问题没有解决:

  1. 一个是广度的问题。在严肃的信息获取场景下,我们对于信息的完整性有比较高的要求。也就是说,用关键词检索出「某些」资料是不够的,还希望查到的资料要全。把散落到各处的信息,按照要求收集到一起,仅依靠关键词索引很难覆盖到。
  2. 另一个是深度的问题。通过关键词索引可以定位到document级别,但这些document内部更细粒度的信息,就需要人来提取、整合。

于是,有些人尝试从document中预先把有用的信息提取出来,做成结构化的数据。一般有两种形式:一种是知识图谱,按照实体和实体关系来组织信息,类似企查查、天眼查组织企业数据的形式;另一种是表格形式,常见的例子是金融领域的一些应用,把各个公司的历史财务信息(营业额、利润、资产负债、现金流量等)、分红派息行为、机构持股变动等信息,汇总成表格呈现给投资者。不管是知识图谱,还是表格数据,它们都属于「给人看」的结构化数据。把这些数据生产出来,需要耗费大量的人力(机器可以辅助一部分),因此,这种人工组织信息的方式,只能在商业价值高的一些领域内小范围使用。

LLM的出现,改变了这一切。它把整个互联网上公开可访问的文本信息,压缩进了模型当中。可以想象一下,LLM把信息重新组织、重新打散,按照某种难以理解的方式,存储在了数十亿甚至数百亿个参数中。它组织信息的粒度更细。前面讲的倒排索引,组织信息的粒度是document;知识图谱和表格,组织信息的粒度是人能够理解的实体概念和实体关系;而LLM组织信息的粒度,则是一个个的token。已经有不少研究人员在研究LLM内部的数据表示了,而且取得了一些进展,但是,LLM内部对于信息的组织形式,整体上看仍然是个黑盒。

本来人们对LLM的期望是很高的,认为它可能会颠覆知识产业。从逻辑上讲,这么想是有些道理的。既然LLM已经吃进去了互联网上所有的公开数据,将信息在内部做了重新组织,相当于学到了数据里面所表示的知识,自然可以解答信息或知识层面的「任何」问题。我们前面提到的检索、提取、整合,这三个阶段的信息处理过程,理论上LLM似乎是能端到端地做掉的。但是,LLM在能力上目前还有两个关键的不足:一个是推理能力 (reasoning) 还达不到真实业务场景的要求;另一个是幻觉问题,这是一个顽疾。这些不足让LLM难以单独成为端到端的解决方案。GraphRAG提供了一个新的思路。GraphRAG整个系统可以分成两个大的模块:Indexer和Query。Indexer负责从非结构化的文本中提取实体和实体关系,形成结构化的图谱,并支持层次化建图;Query模块则负责利用建好的图谱执行问答任务。GraphRAG仍然是按照实体和实体关系对世界进行建模的,但它按照这种图的模型对信息进行重新组织的时候,是充分利用了LLM学到的知识的。这相当于找到了一种实现路径,把关注的重点从LLM内部转移到了LLM外部。LLM内部是个黑盒,但它却有一个「人格化」的界面,输入和输出都是自然语言的。因此,利用LLM做信息的重新组织,LLM内部的知识就「外化」到了建图的过程中了,而且变成了人可以理解的形式。这里有两个关键点需要注意:

  1. GraphRAG更充分利用了LLM的知识。至少是比传统的RAG更多地利用了模型的知识。在传统的RAG中,LLM主要发挥作用的地方是最后一步。它在训练阶段所学到的——对于这个世界的理解——很可能并没有充分发挥出来。而在GraphRAG中,LLM对世界的理解,体现在了对实体和实体关系的识别过程中。
  2. 在LLM外部对信息进行重组,意味着更加可控,也意味着人可以理解和参与这个控制工程。 可以畅想一下,如果做得足够好的话,我们可能能够得到一种新型的知识库的组织形式。它以自然语言为界面来提供对信息的查询;内部则以一种全新的方式来组织数据。这种对数据的组织,不是基于关键词的浅层的关联,而是包含了语义上的关联,包含了LLM对于世界的理解。

GraphRAG提供了非常好的一种思路。但这种技术目前仍不成熟。有一些关键问题值得未来去仔细思考:

  1. 建模的合理性。GraphRAG的本质,还是在于应该如何对信息进行建模。它目前采取了图的方式,但不代表图这种方式就能对现实世界的信息进行全面的建模。比如历史数据、时间序列数据,如何融合到图当中?
  2. 图谱的规模。GraphRAG到底能支持多大规模的数据,还是未经验证的问题。它能应用到开放域,还是只能够用于特定领域?另一方面,如果数据规模很大,建图的成本也非常高。
  3. 如何在人为可控的方式下建图?只基于LLM来建图,会引入非常多的噪声。如何在充分利用LLM知识的基础上,把领域专家的经验也引入进来,也是非常重要的一个问题。

挑战

从数据到知识

  1. 是建成属性还是关系?
  2. 从提取上,不能放任llm 提取的三元组随意入库,这样的图谱价值有限,但是如何约束?
  3. 从检索上,如果不给llm 足够的context,说明图谱里有哪些实体(实体有哪些属性)、关系,单凭query 一句话十几个字有一个高质量的text2dsl 也不是很现实

其它

GraphRAG新增文件对已有知识库影响有多大?缓存又是何时失效?一文带你探究到底 未读。