无限可能Langchain——向量存储和检索

递归蝉翼
• 阅读 281

欢迎关注微信公众号【千练极客】,尽享更多干货文章!
无限可能Langchain——向量存储和检索

本教程将使您熟悉LangChain的向量存储和检索器抽象。这些抽象旨在支持从(向量)数据库和其他来源检索数据,以便与LLM工作流集成。它们对于获取数据作为模型推理的一部分进行推理的应用程序很重要,例如检索增强生成或RAG(请参阅我们的RAG教程这里)。

概念

这个指南专注于文本数据的检索。我们将涵盖以下概念:

  • 文档;
  • 向量存储;
  • 检索器。

安装

参考前面文档:
《无限可能LangChain——开启大模型世界》
《无限可能LangChain——构建一个简单的LLM应用程序》

本教程还需要 langchain-chroma 包

pip install langchain langchain-chroma langchain-openai

请参阅官网的安装指南以获取更多详细信息

Documents

LangChain 实现了一个 Document 抽象,旨在表示一个文本单元和相关的元数据。它有两个属性:

  • page_content:表示内容的字符串;
  • metadata:包含任意的元数据。

metadata 属性可以捕获有关文档源、与其他文档的关系以及其他信息的信息。请注意,单个 Document 对象通常代表较大文档的一大块。

让我们生成一些示例文档:

from langchain_core.documents import Document

documents = [
    Document(
        page_content="狗是很好的伴侣,以忠诚和友善而闻名。",
        metadata={"source": "mammal-pets-doc"},
    ),
    Document(
        page_content="猫是独立的宠物,经常享受自己的空间。",
        metadata={"source": "mammal-pets-doc"},
    ),
    Document(
        page_content="金鱼是深受初学者欢迎的宠物,需要相对简单的护理。",
        metadata={"source": "fish-pets-doc"},
    ),
    Document(
        page_content="鹦鹉是一种聪明的鸟类,能够模仿人类的语言。",
        metadata={"source": "bird-pets-doc"},
    ),
    Document(
        page_content="兔子是群居动物,需要足够的空间来跳跃。",
        metadata={"source": "mammal-pets-doc"},
    ),
]

API Reference:Document

在这里,我们生成了五个文档,其中包含指示三个不同“来源”的元数据(mammal-pets-doc、fish-pets-doc、bird-pets-doc)。

VectorStore

向量搜索是存储和搜索非结构化数据(例如非结构化文本)的常用方法。这个想法是存储与文本关联的数字向量。给定一个查询,我们可以将嵌入为相同维度的向量,并使用向量相似度度量来识别存储中的相关数据。

LangChain VectorStore 对象包含用于添加文本和 Document对象记录到存储中,并使用各种相似度指标查询它们。它们通常用 嵌入模型,它确定如何将文本数据转换为数字向量。

LangChain 包括一套与不同矢量存储技术的集成。一些矢量存储由提供商(例如,各种云提供商)托管,需要特定的凭据才能使用;一些(例如 Postgres)在单独的基础架构中运行,可以在本地或通过第三方运行;其他人可以在内存中运行以实现轻量级工作负载。

在这里,我们将使用 ChromaChroma 是一个AI原生开源矢量数据库,专注于开发人员的生产力和幸福感。其中包括一个内存实现。

OpenAI 嵌入

要实例化向量存储,我们通常需要提供一个嵌入模型以指定如何将文本转换为数字向量。这里我们将使用 OpenAI嵌入

from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings

vectorstore = Chroma.from_documents(
    documents,
    embedding=OpenAIEmbeddings(),
)

API Reference: OpenAIEmbeddings

DashScope 嵌入(灵积模型)

from langchain_chroma import Chroma
from langchain_community.embeddings import DashScopeEmbeddings

vectorstore = Chroma.from_documents(
    documents,
    embedding=DashScopeEmbeddings(),
)

调用 .from_documents 将文档添加到矢量存储中。VectorStore 实现了添加文档的方法,这些文档也可以在对象实例化后调用。大多数实现将允许您连接到现有的向量存储——例如,通过提供客户端、索引名称或其他信息。有关特定的 集成了解更多细节。

一旦我们实例化了一个 VectorStore包含文档,我们可以查询它。 VectorStore包括查询方法:

  • 同步和异步;
  • 通过字符串查询和向量;
  • 有和没有返回相似度分数;
  • 通过相似性和最大边际相关性(平衡相似性与查询到检索结果的多样性)。

这些方法通常会在其输出中包含 Document 对象的列表。

搜索实例

根据与字符串查询的相似性返回文档:

from langchain_core.documents import Document

# 构造文档
documents = [
    Document(
        page_content="狗是很好的伴侣,以忠诚和友善而闻名。",
        metadata={"source": "mammal-pets-doc"},
    ),
    Document(
        page_content="猫是独立的宠物,经常享受自己的空间。",
        metadata={"source": "mammal-pets-doc"},
    ),
    Document(
        page_content="金鱼是深受初学者欢迎的宠物,需要相对简单的护理。",
        metadata={"source": "fish-pets-doc"},
    ),
    Document(
        page_content="鹦鹉是一种聪明的鸟类,能够模仿人类的语言。",
        metadata={"source": "bird-pets-doc"},
    ),
    Document(
        page_content="兔子是群居动物,需要足够的空间来跳跃。",
        metadata={"source": "mammal-pets-doc"},
    ),
]



from langchain_chroma import Chroma
from langchain_community.embeddings import DashScopeEmbeddings

# 创建 Chroma 向量存储
vectorstore = Chroma.from_documents(
    documents,
    embedding=DashScopeEmbeddings(),
)

# 查询向量存储
result = vectorstore.similarity_search("猫")
print(result)

运行结果:

[Document(page_content='猫是独立的宠物,经常享受自己的空间。', metadata={'source': 'mammal-pets-doc'
    }), Document(page_content='狗是很好的伴侣,以忠诚和友善而闻名。', metadata={'source': 'mammal-pets-doc'
    }), Document(page_content='兔子是群居动物,需要足够的空间来跳跃。', metadata={'source': 'mammal-pets-doc'
    }), Document(page_content='鹦鹉是一种聪明的鸟类,能够模仿人类的语言。', metadata={'source': 'bird-pets-doc'
    })
]

无限可能Langchain——向量存储和检索

异步查询

from langchain_core.documents import Document

# 构造文档
documents = [
    Document(
        page_content="狗是很好的伴侣,以忠诚和友善而闻名。",
        metadata={"source": "mammal-pets-doc"},
    ),
    Document(
        page_content="猫是独立的宠物,经常享受自己的空间。",
        metadata={"source": "mammal-pets-doc"},
    ),
    Document(
        page_content="金鱼是深受初学者欢迎的宠物,需要相对简单的护理。",
        metadata={"source": "fish-pets-doc"},
    ),
    Document(
        page_content="鹦鹉是一种聪明的鸟类,能够模仿人类的语言。",
        metadata={"source": "bird-pets-doc"},
    ),
    Document(
        page_content="兔子是群居动物,需要足够的空间来跳跃。",
        metadata={"source": "mammal-pets-doc"},
    ),
]



from langchain_chroma import Chroma
from langchain_community.embeddings import DashScopeEmbeddings

# 创建 Chroma 向量存储
vectorstore = Chroma.from_documents(
    documents,
    embedding=DashScopeEmbeddings(),
)

# 查询向量存储
# result = vectorstore.similarity_search("猫")

async def asnc_search():
    result = await vectorstore.asimilarity_search("猫")
    print(result)

if __name__ == "__main__":
    import asyncio
    asyncio.run(asnc_search())
[Document(page_content='猫是独立的宠物,经常享受自己的空间。', metadata={'source': 'mammal-pets-doc'
    }), Document(page_content='狗是很好的伴侣,以忠诚和友善而闻名。', metadata={'source': 'mammal-pets-doc'
    }), Document(page_content='兔子是群居动物,需要足够的空间来跳跃。', metadata={'source': 'mammal-pets-doc'
    }), Document(page_content='鹦鹉是一种聪明的鸟类,能够模仿人类的语言。', metadata={'source': 'bird-pets-doc'
    })
]

返回分数

from langchain_core.documents import Document

# 构造文档
documents = [
    Document(
        page_content="狗是很好的伴侣,以忠诚和友善而闻名。",
        metadata={"source": "mammal-pets-doc"},
    ),
    Document(
        page_content="猫是独立的宠物,经常享受自己的空间。",
        metadata={"source": "mammal-pets-doc"},
    ),
    Document(
        page_content="金鱼是深受初学者欢迎的宠物,需要相对简单的护理。",
        metadata={"source": "fish-pets-doc"},
    ),
    Document(
        page_content="鹦鹉是一种聪明的鸟类,能够模仿人类的语言。",
        metadata={"source": "bird-pets-doc"},
    ),
    Document(
        page_content="兔子是群居动物,需要足够的空间来跳跃。",
        metadata={"source": "mammal-pets-doc"},
    ),
]



from langchain_chroma import Chroma
from langchain_community.embeddings import DashScopeEmbeddings

# 创建 Chroma 向量存储
vectorstore = Chroma.from_documents(
    documents,
    embedding=DashScopeEmbeddings(),
)

# 查询向量存储
result = vectorstore.similarity_search_with_score("猫")
print(result)
[
    (Document(page_content='猫是独立的宠物,经常享受自己的空间。', metadata={'source': 'mammal-pets-doc'}),6132.1220703125), 
    (Document(page_content='狗是很好的伴侣,以忠诚和友善而闻名。', metadata={'source': 'mammal-pets-doc'}),9821.0986328125), 
    (Document(page_content='兔子是群居动物,需要足够的空间来跳跃。', metadata={'source': 'mammal-pets-doc'}),12847.712890625), 
    (Document(page_content='鹦鹉是一种聪明的鸟类,能够模仿人类的语言。', metadata={'source': 'bird-pets-doc'}),13557.1650390625)
]

根据与嵌入式查询的相似性返回文档:

from langchain_core.documents import Document

# 构造文档
documents = [
    Document(
        page_content="狗是很好的伴侣,以忠诚和友善而闻名。",
        metadata={"source": "mammal-pets-doc"},
    ),
    Document(
        page_content="猫是独立的宠物,经常享受自己的空间。",
        metadata={"source": "mammal-pets-doc"},
    ),
    Document(
        page_content="金鱼是深受初学者欢迎的宠物,需要相对简单的护理。",
        metadata={"source": "fish-pets-doc"},
    ),
    Document(
        page_content="鹦鹉是一种聪明的鸟类,能够模仿人类的语言。",
        metadata={"source": "bird-pets-doc"},
    ),
    Document(
        page_content="兔子是群居动物,需要足够的空间来跳跃。",
        metadata={"source": "mammal-pets-doc"},
    ),
]


from langchain_chroma import Chroma
from langchain_community.embeddings import DashScopeEmbeddings

# 创建 Chroma 向量存储
vectorstore = Chroma.from_documents(
    documents,
    embedding=DashScopeEmbeddings(),
)

# 查询向量存储
embedding = DashScopeEmbeddings().embed_query("猫")
result = vectorstore.similarity_search_by_vector(embedding)
print(result)
[Document(page_content='猫是独立的宠物,经常享受自己的空间。', metadata={'source': 'mammal-pets-doc'
    }), Document(page_content='狗是很好的伴侣,以忠诚和友善而闻名。', metadata={'source': 'mammal-pets-doc'
    }), Document(page_content='兔子是群居动物,需要足够的空间来跳跃。', metadata={'source': 'mammal-pets-doc'
    }), Document(page_content='鹦鹉是一种聪明的鸟类,能够模仿人类的语言。', metadata={'source': 'bird-pets-doc'
    })
]

了解更多:

Retrievers

LangChain VectorStore 对象不是子类 可运行,因此不能立即集成到 LangChain 表达式语言中

朗链 检索器是Runnables,因此它们实现了一组标准方法(例如,同步和异步 invoke 和 batch 操作)并被设计为合并到LCEL链中。

我们可以自己创建一个简单的版本,无需子类化 Retriever。如果我们选择希望使用什么方法来检索文档,我们可以轻松创建一个可运行的。下面我们将围绕 similarity_search 方法:

from langchain_core.documents import Document

# 构造文档
documents = [
    Document(
        page_content="狗是很好的伴侣,以忠诚和友善而闻名。",
        metadata={"source": "mammal-pets-doc"},
    ),
    Document(
        page_content="猫是独立的宠物,经常享受自己的空间。",
        metadata={"source": "mammal-pets-doc"},
    ),
    Document(
        page_content="金鱼是深受初学者欢迎的宠物,需要相对简单的护理。",
        metadata={"source": "fish-pets-doc"},
    ),
    Document(
        page_content="鹦鹉是一种聪明的鸟类,能够模仿人类的语言。",
        metadata={"source": "bird-pets-doc"},
    ),
    Document(
        page_content="兔子是群居动物,需要足够的空间来跳跃。",
        metadata={"source": "mammal-pets-doc"},
    ),
]

from langchain_chroma import Chroma
from langchain_community.embeddings import DashScopeEmbeddings

# 创建 Chroma 向量存储
vectorstore = Chroma.from_documents(
    documents,
    embedding=DashScopeEmbeddings(),
)

# 查询向量存储
from langchain_core.runnables import RunnableLambda
retriever = RunnableLambda(vectorstore.similarity_search).bind(k=1)  # 选择顶部结果

result = retriever.batch(["猫", "鲨鱼"])
print(result)
API Reference:Document | RunnableLambda

运行结果:
无限可能Langchain——向量存储和检索

[
    [Document(page_content='猫是独立的宠物,经常享受自己的空间。', metadata={'source': 'mammal-pets-doc'
        })
    ],
    [Document(page_content='金鱼是深受初学者欢迎的宠物,需要相对简单的护理。', metadata={'source': 'fish-pets-doc'
        })
    ]
]

Vectorstore 实现了一个 as_retriever 将生成 Retriever 的方法,特别是 VectorStoreRetriever。这些检索器包括特定的 search_type 和 search_kwargs 属性,用于标识要调用的底层向量存储的哪些方法,以及如何参数化它们。例如,我们可以使用以下内容复制上述内容:

from langchain_core.documents import Document

# 构造文档
documents = [
    Document(
        page_content="狗是很好的伴侣,以忠诚和友善而闻名。",
        metadata={"source": "mammal-pets-doc"},
    ),
    Document(
        page_content="猫是独立的宠物,经常享受自己的空间。",
        metadata={"source": "mammal-pets-doc"},
    ),
    Document(
        page_content="金鱼是深受初学者欢迎的宠物,需要相对简单的护理。",
        metadata={"source": "fish-pets-doc"},
    ),
    Document(
        page_content="鹦鹉是一种聪明的鸟类,能够模仿人类的语言。",
        metadata={"source": "bird-pets-doc"},
    ),
    Document(
        page_content="兔子是群居动物,需要足够的空间来跳跃。",
        metadata={"source": "mammal-pets-doc"},
    ),
]



from langchain_chroma import Chroma
from langchain_community.embeddings import DashScopeEmbeddings

# 创建 Chroma 向量存储
vectorstore = Chroma.from_documents(
    documents,
    embedding=DashScopeEmbeddings(),
)

# 查询向量存储
retriever = vectorstore.as_retriever(
    search_type="similarity",
    search_kwargs={"k": 1},
)

result = retriever.batch(["猫", "鲨鱼"])
print(result)

运行结果:
无限可能Langchain——向量存储和检索

[
    [Document(page_content='猫是独立的宠物,经常享受自己的空间。', metadata={'source': 'mammal-pets-doc'
        })
    ],
    [Document(page_content='金鱼是深受初学者欢迎的宠物,需要相对简单的护理。', metadata={'source': 'fish-pets-doc'
        })
    ]
]

VectorStoreRetriever 支持搜索类型 "similarity"(默认), "mmr"(最大边际相关性,如上所述),以及
"similarity_score_threshold"。我们可以使用后者根据相似度分数对检索器输出的文档进行阈值。

检索器可以很容易地合并到更复杂的应用程序中,例如检索增强生成(RAG)应用程序,它将给定问题与检索到的上下文组合成LLM的提示。下面我们展示一个最小的例子。

from langchain_core.documents import Document

# 构造文档
documents = [
    Document(
        page_content="狗是很好的伴侣,以忠诚和友善而闻名。",
        metadata={"source": "mammal-pets-doc"},
    ),
    Document(
        page_content="猫是独立的宠物,经常享受自己的空间。",
        metadata={"source": "mammal-pets-doc"},
    ),
    Document(
        page_content="金鱼是深受初学者欢迎的宠物,需要相对简单的护理。",
        metadata={"source": "fish-pets-doc"},
    ),
    Document(
        page_content="鹦鹉是一种聪明的鸟类,能够模仿人类的语言。",
        metadata={"source": "bird-pets-doc"},
    ),
    Document(
        page_content="兔子是群居动物,需要足够的空间来跳跃。",
        metadata={"source": "mammal-pets-doc"},
    ),
]

# 构造 prompt
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough

message = """
仅使用提供的上下文回答此问题。
{question}

上下文:
{context}
"""
prompt = ChatPromptTemplate.from_messages([("human", message)])


from langchain_chroma import Chroma
from langchain_community.embeddings import DashScopeEmbeddings
# 创建 Chroma 向量存储
vectorstore = Chroma.from_documents(
    documents,
    embedding=DashScopeEmbeddings(),
)

# 查询向量存储
retriever = vectorstore.as_retriever(
    search_type="similarity",
    search_kwargs={"k": 1},
)

# 使用 Tongyi LLM,并设置温度为 1,代表模型会更加随机,但也会更加不确定
from langchain_community.llms import Tongyi
llm = Tongyi(temperature=1)

# 构建 RAG 链
rag_chain = {"context": retriever, "question": RunnablePassthrough()} | prompt | llm

# 使用 RAG 链并打印结果
response = rag_chain.invoke("告诉我关于猫的事")
print(response)
API Reference:ChatPromptTemplate | RunnablePassthrough

运行结果:
无限可能Langchain——向量存储和检索

langsmith 日志:搜索日志
无限可能Langchain——向量存储和检索

无限可能Langchain——向量存储和检索

小结

本文我们了解了 Documents、VectorStoreRetrievers 的简单用法,作为目前最火的 RAG 应用方向,值得我们深入去了解学习。

如果想继续深入,我们可以了解以下内容,比如检索策略可能丰富而复杂。

操作指南的检索器部分涵盖了这些和其他内置检索策略。扩展BaseRetriever类以实现自定义检索器也很简单。请参阅我们的操作指南 此处

本文由博客一文多发平台 OpenWrite 发布!
点赞
收藏
评论区
推荐文章
待兔 待兔
4年前
Flutter开发必备Dart基础:Dart快速入门
本文首发于微信公众号「Android开发之旅」,欢迎关注,获取更多技术干货Jetpack版WanAndroid项目地址:AndroidJetpack架构开发组件化应用实战(https://links.jianshu.com/go?tohttps%3A%2F%2Fgithub.com%2Fwinlee28%2FJetpackWanAndroi
梦
4年前
微信小程序new Date()转换时间异常问题
微信小程序苹果手机页面上显示时间异常,安卓机正常问题image(https://imghelloworld.osscnbeijing.aliyuncs.com/imgs/b691e1230e2f15efbd81fe11ef734d4f.png)错误代码vardate'2021030617:00:00'vardateT
Wesley13 Wesley13
3年前
MySQL高可用方案选型参考
本文由「MySQL中文网」原创,“MySQL中文”公众号是http://imysql.com的官方唯一公众号,微信首发。欢迎关注「MySQL中文」公众号(ID:imysql_wx),我们会不定期推送MySQL相关原创干货。本次专题是MySQL高可用方案选型,这个专题想必有很多同学感兴趣。高可用的意义以及各种不同高可用
Stella981 Stella981
3年前
Django 中如何优雅的记录日志
技术博客:https://github.com/yongxinz/techblog(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2Fyongxinz%2Ftechblog)同时,也欢迎关注我的微信公众号AlwaysBeta,更多精彩内容等你来。
Stella981 Stella981
3年前
Markdown 简明语法
关注我,每天都有优质技术文章推送,工作,学习累了的时候放松一下自己。本篇文章同步微信公众号欢迎大家关注我的微信公众号:「醉翁猫咪」!(https://oscimg.oschina.net/oscnet/up1d5ae683f66a54eb857ab1a178a657b6.jpg)今天我们了解一下什么是Markdow
Stella981 Stella981
3年前
RabbitMQ集群搭建
!(https://oscimg.oschina.net/oscnet/ddd81f190f1a43418b5aec950f99332a.png)欢迎扫描文末二维码关注本人微信公众号,持续输出原创,永不停步!推荐相关文章阅读:入门RabbitMQ(https://www.oschin
Wesley13 Wesley13
3年前
Java 8 创建 Stream 的 10 种方式,我保证你受益无穷!
之前栈长分享过Java8一系列新特性的文章,其中重点介绍了Stream.!(http://img.javastack.cn/20190613135450.png)!(http://img.javastack.cn/20190613135537.png)获取上面这份Java8~12系列新特性干货文章,请在微信搜索关注微信公众号:
Stella981 Stella981
3年前
Python编程思想(22):Lambda表达式
李宁老师已经在「极客起源」微信公众号推出《Python编程思想》电子书,囊括了Python的核心技术,以及Python的主要函数库的使用方法。读者可以在「极客起源」公众号中输入160442开始学习。\正文现在几乎所有的现代编程语言都支持lambda表达式,如果说函数是命名的、
Stella981 Stella981
3年前
200的大额人民币即将面世?央行:Yes!
点击上方蓝字关注我们!(https://oscimg.oschina.net/oscnet/2a1c2ac00bf54458a78c48a6c2e547d5.png)点击上方“印象python”,选择“星标”公众号重磅干货,第一时间送达!!(
Wesley13 Wesley13
3年前
IT人必备的14个优质科研类公众号
希望这些NB的公众号能够给你在学习的道路上增光添彩极客视界!(https://oscimg.oschina.net/oscnet/91007182c15a42bb92f666622a9cd544.png)ID:geekview哈喽~这里是「极客
可莉 可莉
3年前
200的大额人民币即将面世?央行:Yes!
点击上方蓝字关注我们!(https://oscimg.oschina.net/oscnet/2a1c2ac00bf54458a78c48a6c2e547d5.png)点击上方“印象python”,选择“星标”公众号重磅干货,第一时间送达!!(