Milvus

Milvus 是一个用于向量(Vector)存储和检索的特殊数据库,由国内的创业公司 Zilliz 开发。

所谓向量,可以看作一个长度为 N 的元组。很多 AI/ML 系统(例如推荐系统、图片相似度检测等)都有类似的需求:这些系统首先将海量数据集经过特征提取得到很多向量,使用时给定一个向量,从数据集向量中快速检索出和它最“相似”的 K 个向量。相似度的定义有多种,最常见的有余弦距离、欧几里得距离等。

sim_sear

为了做到这一点,最容易想到的方法就是让给定向量和所有数据库中的向量依次做比较,但显然这个做法太慢了。RDBMS 中有索引的概念,那我们能不能为向量的相似度也建立索引呢?当然是可以的!

这个问题称为向量相似度检索(vector similarity search),Facebook 开源的 Faiss 就是这样一个 C++ library,它内置了多种索引,例如 IVF_FLAT、IVF_FQ8、IVF_PQ 等(这些算法不是本文的重点)。Milvus 基于 Faiss 开发,Milvus 添加了存储组件,使之成为一个完整的数据库产品(而不仅是个 libaray),同时也做了很多工程上的优化。

存储格式

Milvus 的数据模型允许每行数据(文中称为 entity)包含 1 个或多个 vector 以及可选的数值属性(numeric attribute)。其中数值属性一般起到过滤作用,比如年龄、身高之类的,可以作为查询过滤条件的一部分。

每个 vector 本身显然是要连续排列的(vector 一定是以整体参与运算),而 vector 之间按列排列。比如一张表有 v1、v2 两个 vector 列、{A,B,C} 三行数据,那么在存储上的排列就是 {A.v1, B.v1, C.v1, A.v2, B.v2, C.v2} 。

索引选择

索引的原理超出本文的范畴,这里只介绍最基本的 idea:在构建索引时,会通过聚类算法选出几个中心点(v0v9 聚类得到图中 c0c2 三个中心点),当给定查询 q 时,算法能快速找到离 q 最近的 k 个中心点(k=2,得到 c0、c1),之后只要从 c0、c1 的邻居中(v0~v6)搜索即可。

index

索引选择的实现是基于代价的:

strategy

策略A(vector不走索引,数值条件走索引):先通过数值属性的倒排索引过滤,再在过滤出来的所有数据上扫描(逐个计算相似度,不依靠vector的索引)

策略B(vector走索引,数值条件走索引):通过数值属性的倒排索引拿到过滤结果 bitmap,然后在 vector 上利用相似度索引得所有相似的向量,根据 bitmap 只留下复合过滤条件的那些,再取 TopK

策略C(vector走索引,数值条件不走索引):在 vector 上利用相似度索引得所有相似的向量,然后按数值条件过滤

策略D:基于代价在 A/B/C 中选择一个,至于怎么选应该很容易想到吧 :)

策略E:是对 D 的进一步改进,也是 Milvus 使用的策略。具体来说,Milvus 首先根据某个数值属性将整个 dataset 分区(比如 price 可以分为 [1, 100], [101, 200], [201, 300], [301, 400] ),之后,如果查询条件带有分区键,则可以进行“分区裁剪”(比如对于 price in [50, 250],可以直接裁剪出 [1, 100], [101, 200], [201, 300] 这三个分区),并且对每个分区采取 cost-based 策略(比如中间的 [101, 200] 区间不需要对 price 进行过滤,因为一定满足条件)

LangChain

整体分为:基础层、能力层、应用层三部分。

LangChain

基础层

包括:Models、LLM、Index三层:

Models层

可以看出各家大模型的演进历史,其中Google、OpenAI、Meta、DeepMind领先优势非常大,国内大厂唯独腾讯缺席。有几个点:

目前最主要的几个大模型为: Google的Bard、Meta的LLaMa、OpenAI的GPT-4、DeepMind的Chinchilla

另外目前投入应用的智能终端基本都是基于模型的Fine-Tuning或者RLHF技术,并且需要大量的相关行业训练集,典型比如code领域、财经领域BloombergGPT、学术论文编写等,例如在Code细分领域,目前的模型有:OpenAI的Codex、DeepMind的AlphaCode以及Saleforce的CodeGen。

Models层的输出包括:

  • 文本自动生成
  • embeding向量化输出
  • 多模态输出

LLMS层

这一层主要强调对models层能力的封装以及服务化输出能力,主要有:

  • 各类LLM模型管理平台:强调的模型的种类丰富度以及易用性
  • 一体化服务能力产品:强调开箱即用
  • 差异化能力:比如聚焦于Promp管理、基于共享资源的模型运行模式等等

Index层

对用户私域文本、图片、PDF等各类文档进行存储和检索,这里有两个方案:

  • Vector方案:即对文件先切分为Chunks,在按Chunks分别编码存储并检索
  • KG方案:这部分利用LLM抽取文件中的三元组,将其存储为KG供后续检索

能力层

如果基础层提供了最核心的能力,能力层则给这些能力安装上手、脚、脑,让其具有记忆和触发万物的能力,包括:Chains、Memory、Tool三部分

Chains层

按照不同的需求抽象并定制化不同的执行逻辑,Chain可以相互嵌套并串行执行,通过这一层,让LLM的能力链接到各行各业比如面向私域数据的load_qa_with_sources_chain; 比如面向SQL数据源的SQLDatabaseChain;再比如能自动生成代码并执行的LLMMathChain等等。

Memory层

这层主要有两个核心点:

  • 对Chains的执行过程中的输入、输出进行记忆并结构化存储,为下一步的交互提供上下文,这部分简单存储在Redis即可
  • 根据交互历史构建知识图谱,根据关联信息给出准确结果

Tools层

其实Chains层可以根据LLM + Prompt执行一些特定的逻辑,但是如果要用Chain实现所有的逻辑不现实,可以通过Tools层也可以实现,Tools层理解为技能比较合理。典型的比如搜索、维基百科、天气预报、chatGPT服务等等。

应用层

有了基础层和能力层,我们可以构建各种各样好玩的,有价值的服务,这里就是Agent

Agent层

Agent层可以根据Tool和Chain组合出特定的服务来,最终实现以更自然的文本交互形态完成用户特定需求的目标,比如用自然语言的方式实现sql的操作等等。

项目架构

整体流程

1、将垂直行业的领域知识向量化并存入向量数据库

2、用户提问

3、用户问题向量化

4、查询向量数据库,得到TopN条匹配知识

5、构建Prompt,调用OpenAI API或其他大语言模型

6、返回回答

架构说明

1、Embeddgings model选择

要将领域知识向量化,需要有Embeddings model,最简单的方案是使用OpenAI的Embeddings API。

由于OpenAI的Embeddings model是通用模型,对垂直行业并不是最适合的,会出现回答不准确的情况。如果数据量较大,需要反复调用Embeddgins API,效率较低、成本较高。

可以考虑自己基于知识库自训练或基于一些现成的模型,HuggingFace上有很多Embeddings model可供参考使用。

2、向量数据库选择

向量数据库在相似文本搜索、个性化推荐、相似图片搜索等都有很好的应用场景。开源的向量数据库有qdrant,weaviate,milvus,elasticsearch等,看起来qdrant的性能最好。

qdrant对常用的向量数据库的测试报告:https://qdrant.tech/benchmarks/

3、LLM框架

LangChain及LlamaIndex (原GPT Index) 这样的LLM框架,封装了很多LLM的工具,可以极大程度提升与LLM的集成效率。

LlamaIndex (原GPT Index) 入门门槛更低,入门文档也写得比较详尽。LangChain更为强大灵活,qdrant对LangChain的集成更好。

可以参考 https://news.ycombinator.com/item?id=34568343

4、调用OpenAI,构建的Prompt模板

Answer the question as truthfully the question as truthfully as possible using the provided text, and if the answer is not contained within the text below, say “I don’t know”

Context:
向量数据库科搜索结果的TopN 知识的拼接

Q: 用户提问

architecture.png