LangChain组件 vector stores向量存储

------------恢复内容开始------------

Vector Store向量存储

基于LangChain的向量存储,存储嵌入数据,并执行相似性搜索。
image

这是一个典型的向量存储应用,也即是典型的RAG流程

这部分开发主要涉及到:

  • 如何文本转向量
  • 创建向量存储,基于向量存储完成(LangChain为向量存储提供了统一接口):
    • 存入向量 add_document
    • 删除向量 delete
    • 向量检索 similarity_search

内置向量存储

from langchain_core.vectorstores import InMemoryVectorStore
from langchain_community.embeddings import DashScopeEmbeddings

vector_store = InMemoryVectorStore(embedding=DashScopeEmbeddings())

# 添加文档到向量存储中,并指定ID
vector_store.add_documents(documents=["Hello World!", "你好,世界!"], ids=["doc1", "doc2"])

# 删除文档 (通过指定的ID删除)
vector_store.delete(ids=["doc1"])

# 相似性搜索 返回最相似的1个文档结果
similar_docs = vector_store.similarity_search("Hello", 1)

add_documents结合文档解析器使用

from langchain_core.vectorstores import InMemoryVectorStore
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.document_loaders import CSVLoader

vector_store = InMemoryVectorStore(embedding=DashScopeEmbeddings())

csv_loader = CSVLoader(
    file_path="./data/sample.csv",
    encoding="utf-8",
    csv_args={
        "delimiter": ",",
        "quotechar": '"',
        "fieldnames": ["id", "name", "age", "hobby"],
    },
    source_column="name"
)

docs = csv_loader.load()
print("文档个数:", len(docs))
# 向量存储的 新增、删除、检索
vector_store.add_documents(
    documents=docs,
    ids=[f"doc_{i}" for i in range(1, len(docs) + 1)],
)

# 删除 传入[id, id]
vector_store.delete(["doc_1", "doc_2"])

# 检索 传入文本 返回相关文档
result = vector_store.similarity_search(
    "喜欢球类运动的人",
    k=2   # 返回最相关的文档数量
)

print("检索结果:", result)

外部(Chroma)向量存储

from langchain_community.embeddings import DashScopeEmbeddings
from langchain_chroma import Chroma

vector_store = Chroma(
    collection_name="example_collection",
    embedding_function=DashScopeEmbeddings(),
    persist_directory="./chroma_db"
)

综合应用 基于向量检索构建提示词提问

from langchain_community.chat_models import ChatTongyi
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.vectorstores import InMemoryVectorStore

model = ChatTongyi(model="qwen-plus")
prompt = ChatPromptTemplate(
    [
        ("system", "根据已提供的参考资料为主,简洁和专业地回答用户问题。参考资料:{context}"),
        ("human", "{question}"),
    ]
)

vector_store = InMemoryVectorStore(embedding=DashScopeEmbeddings())

# 准备资料(向量数据库数据)
vector_store.add_texts([
    "SFR是用来量化摄像头成像系统清晰度(解析力、锐度)的一个关键指标",
    "Yshading是评估成像系统在均匀光照下,画面从中心到边缘/四角的亮度衰减程度",
    "SFR取代了早期更粗糙的 TV Line / 线对数读数法,现在几乎所有模组厂、手机厂商、第三方评测(如 DXOMARK、Image Engineering)都用 SFR 作为核心清晰度打分项。"
])

input_text = "SFR与Yshading区别"

result = vector_store.similarity_search(input_text, k=2)
reference_text = "["
for doc in result:
    reference_text += f"{doc.page_content},"
reference_text += "]"

def print_prompt(prompt):
    print(prompt.to_string())
    print("="*20)
    return prompt

chain = prompt | print_prompt | model | StrOutputParser()

res = chain.invoke({"context": reference_text, "question": input_text})

print(res)

RunnablePassthrough的使用 向量检索构建提示词入链

之前的代码中创建了vector_store的向量库,但是它不是Runnable接口的子类实例,无法入链;

在实际需求中,向量检索入链也是比较常用的方式

LangChain为向量存储对象提供了一个as_retriever方法,可以返回一个Runnable接口子类实例对象,以达到入链的目的

retriever = vector_store.as_retriever(search_kwargs={"k": 2})

其中的参数search_kwargs中的k表示之前的代码中result = vector_store.similarity_search(input_text, k=2)对应的k=2,最大匹配结果

retriever 入链输入输出问题

chain = retriever | prompt | print_prompt | model | StrOutputParser()

对于入链,还要考虑每个组件的输入输出问题

retriever

  • 输入:用户提问 str
  • 输出:向量库检索结果 list[Document]

prompt

  • 输入:用户提问 + 向量库的检索结果 dict
  • 输出:完整的提示词 PromptValue

如果这样入链,发现输入输出类型完全对应不上,链条会失败,同时用户提问在经过retriever后被丢弃,仅输出了list[Document]

此时就需要用到RunnablePassthrough()功能类似于占位符,在链中,它会复制一份链输入内容给自己,而prompt需要输入字典,retriever输入为字符串

那么我们可以构造一个字典入链,如下

chain = (
    {"question": RunnablePassthrough(), "context": retriever | format_func } | prompt | print_prompt | model | StrOutputParser()
)

这样链的输入同时给到了字典中的inputcontextretriever | format_func则是一条子链,将retriever输出的list[Document]转为字符串接入字典中

这样就完成了向量检索的入链,关键在于RunnablePassthrough()的使用, 下面是完整实现

from langchain_community.chat_models import ChatTongyi
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.vectorstores import InMemoryVectorStore

model = ChatTongyi(model="qwen-plus")
prompt = ChatPromptTemplate(
    [
        ("system", "根据已提供的参考资料为主,简洁和专业地回答用户问题。参考资料:{context}"),
        ("human", "{question}"),
    ]
)

vector_store = InMemoryVectorStore(embedding=DashScopeEmbeddings())

# 准备资料(向量数据库数据)
vector_store.add_texts([
    "SFR是用来量化摄像头成像系统清晰度(解析力、锐度)的一个关键指标",
    "Yshading是评估成像系统在均匀光照下,画面从中心到边缘/四角的亮度衰减程度",
    "SFR取代了早期更粗糙的 TV Line / 线对数读数法,现在几乎所有模组厂、手机厂商、第三方评测(如 DXOMARK、Image Engineering)都用 SFR 作为核心清晰度打分项。"
])

input_text = "SFR是什么?"

# `as_retriever`方法,可以返回一个Runnable接口子类实例对象,以达到入链的目的
retriever = vector_store.as_retriever(search_kwargs={"k": 2})

def print_prompt(prompt):
    print(prompt.to_string())
    print("="*20)
    return prompt

def format_func(docs):
    if not docs:
        return "没有找到相关资料"
    reference_text = "["
    for doc in docs:
        reference_text += f"{doc.page_content},"
    reference_text += "]"
    return reference_text
chain = (
    {"question": RunnablePassthrough(), "context": retriever | format_func } | prompt | print_prompt | model | StrOutputParser()
)

res = chain.invoke(input_text)

print(res)
posted @ 2026-03-09 11:30  风陵南  阅读(97)  评论(0)    收藏  举报