大模型笔记(四):向量库/检索召回的不同方式-Chroma/FAISS

目录

一、llm接口的一般形式

二、token、chunk

2.1 文本拆分器的类型

2.2 按特定字符拆分

2.3 语义组块 Semantic Chunking

2.4 按token拆分 

三、向量库→检索器/检索方式

3.1 Chroma读取文件后返回类对象的方法 as_retriever

3.2 FAISS读取文件后返回类对象的方法 as_retriever

3.3 高级检索方法

四、embedding模型


一、llm接口的一般形式

填入你的api_key,如果是中转api,还需要base_url。

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    openai_api_key = "sk-JIs8b5bDG2CJezw4295f8287512245F2BdC45fCeAb6d85Cd",
    openai_api_base = "http://192.2.22.55:3001/v1",
    model="deepseek-chat")

不同类型文件导入

# URL 网页加载器生成文档
def load_docs_url():
    from langchain_community.document_loaders import WebBaseLoader
    loader = WebBaseLoader(
        web_paths=("https://blogs.nvidia.com/blog/what-is-retrieval-augmented-generation/",),
        bs_kwargs=dict(
            parse_only=bs4.SoupStrainer(
                class_=("entry-content", "entry-header", "entry-title")
            )
        ),
    )
    docs = loader.load()
    return docs


# TXT文本文件加载器加载生成文档
def load_docs_csv():
    from langchain_community.document_loaders.csv_loader import CSVLoader
    loader = CSVLoader(file_path='data/专业描述.csv', csv_args={
        'delimiter': ',',
        'quotechar': '"',
        'fieldnames': ['专业', '描述']
    }, encoding='utf8', source_column='专业')
    docs = loader.load()
    print(docs)
    return docs


# Word文件加载器加载生成文档
def load_docs_word():
    from langchain_community.document_loaders.word_document import UnstructuredWordDocumentLoader
    loader = UnstructuredWordDocumentLoader(file_path="data/demo.docx")
    # loader = UnstructuredWordDocumentLoader(file_path='data/demo.docx', mode="elements",strategy="fast", )
    docs = loader.load()
    print(docs)
    return docs

# PDF文件加载器加载生成文档
def load_docs_pdf():
    from langchain_community.document_loaders.pdf import UnstructuredPDFLoader
    loader = UnstructuredPDFLoader(file_path="data/demo.pdf")
    # loader = UnstructuredPDFLoader(file_path='data/demo.pdf', mode="elements",strategy="fast", )
    docs = loader.load()
    print(docs)
    return docs

# PowerPoint文件加载器加载生成文档
def load_docs_ppt():
    from langchain_community.document_loaders.powerpoint import UnstructuredPowerPointLoader
    loader = UnstructuredPowerPointLoader(file_path="data/demo.ppt")
    # loader = UnstructuredPowerPointLoader(file_path='data/demo.pptx', mode="elements",strategy="fast", )
    docs = loader.load()
    print(docs)
    return docs

二、token、chunk

transformers库里面一些bert_base模型的输入是tokenizer之后的句子;目前大模型都是一次性可以输入几千token,所以使用textspliter分chunk就可以

而常用的分词token方法主要有:

  1. 基于词典匹配
  2. 基于统计
  3. 基于深度学习

Tokenization(分词) 在自然语言处理(NLP)的任务中是最基本的一步,把文本内容处理为最小基本单元即token(标记,令牌,词元,没有准确的翻译),基本思想是构建一个词表通过词表一一映射进行分词,但如何构建合适的词表呢?以分词粒度的角度有:word(词)粒度、char(字符)粒度、subword(子词)粒度【WordPiece、Byte-Pair Encoding (BPE)、Byte-level BPE(BBPE)】

将一个长文档分割成更小的块,以便适合模型的上下文窗口。LangChain有许多内置的文档转换器,可以轻松地拆分、组合、过滤和操作文档。

2.1 文本拆分器的类型

  • Name:文本拆分器的名称
  • Splits on:此文本拆分器如何拆分文本
  • Adds Metadata:此文本拆分器是否添加关于每个块来源的元数据。
  • Description:拆分器的描述,包括何时使用它的建议。
NameSplits OnAdds MetadataDescription
RecursiveA list of user defined characters递归拆分文本。递归分割文本的目的是试图将相关的文本片段保持在一起。这是开始拆分文本的推荐方式。
HTMLHTML specific characters基于特定于HTML的字符拆分文本。值得注意的是,这添加了关于该块来自何处的相关信息(基于HTML)
MarkdownMarkdown specific characters基于特定于Markdown的字符拆分文本。值得注意的是,这增加了关于该块来自哪里的相关信息(基于降价)
CodeCode (Python, JS) specific characters基于特定于编码语言的字符拆分文本。有15种不同的语言可供选择。
TokenTokens拆分令牌上的文本。有几种不同的方法来衡量token。
CharacterA user defined character基于用户定义的字符拆分文本。一种更简单的方法。
[Experimental] Semantic ChunkerSentences首先对句子进行拆分。然后,如果它们在语义上足够相似,就将它们组合在一起。取自Greg Kamradt

2.2 按特定字符拆分

from langchain_text_splitters import CharacterTextSplitter
 
text_splitter = CharacterTextSplitter(
    separator="\n\n",
    chunk_size=1000,
    chunk_overlap=200,
    length_function=len,
    is_separator_regex=False,
)
 
texts = text_splitter.create_documents([state_of_the_union])
print(texts[0])

2.2 按字符递归拆分

from langchain_text_splitters import RecursiveCharacterTextSplitter
 
# This is a long document we can split up.
with open("./demo_static/splitters_test.txt") as f:
    state_of_the_union = f.read()
text_splitter = RecursiveCharacterTextSplitter(
    # Set a really small chunk size, just to show.
    chunk_size=100,
    chunk_overlap=20,
    length_function=len,
    is_separator_regex=False,
)
 
texts = text_splitter.create_documents([state_of_the_union])
print(texts[0])
print(texts[1])

2.3 语义组块 Semantic Chunking

基于语义相似性拆分文本。

# This is a long document we can split up.
with open("./demo_static/splitters_test.txt") as f:
    state_of_the_union = f.read()
from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai.embeddings import OpenAIEmbeddings
 
text_splitter = SemanticChunker(OpenAIEmbeddings())
docs = text_splitter.create_documents([state_of_the_union])
print(docs[0].page_content)

2.4 按token拆分 

1、tiktoken

from langchain_text_splitters import CharacterTextSplitter
# This is a long document we can split up.
with open("./demo_static/splitters_test.txt") as f:
    state_of_the_union = f.read()
使用from_tiktoken_encoder()方法采用model_name作为一个参数(例如gpt-4)。所有附加参数,如chunk_size, chunk_overlap,以及separators用于实例化CharacterTextSplitter:

text_splitter = CharacterTextSplitter.from_tiktoken_encoder(
    model_name="gpt-4", chunk_size=100, chunk_overlap=0
)
texts = text_splitter.split_text(state_of_the_union)
   

from langchain_text_splitters import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
    model_name="gpt-4",
    chunk_size=100,
    chunk_overlap=0,
)
        

from langchain_text_splitters import TokenTextSplitter
text_splitter = TokenTextSplitter(chunk_size=10, chunk_overlap=0)
 
texts = text_splitter.split_text(state_of_the_union)
print(texts[0])

2、spaCy:如何测量块大小:通过字符数。

from langchain_text_splitters import SpacyTextSplitter
# This is a long document we can split up.
with open("./demo_static/splitters_test.txt") as f:
    state_of_the_union = f.read()
text_splitter = SpacyTextSplitter(chunk_size=1000)
 
texts = text_splitter.split_text(state_of_the_union)
print(texts[0])

3、SentenceTransformers
默认行为是将文本分割成适合您想要使用的句子转换器模型的标记窗口的块。

from langchain_text_splitters import SentenceTransformersTokenTextSplitter
splitter = SentenceTransformersTokenTextSplitter(chunk_overlap=0)
text = "Lorem "
count_start_and_stop_tokens = 2
text_token_count = splitter.count_tokens(text=text) - count_start_and_stop_tokens
print(text_token_count)
token_multiplier = splitter.maximum_tokens_per_chunk // text_token_count + 1
 
# `text_to_split` does not fit in a single chunk
text_to_split = text * token_multiplier
 
print(f"tokens in text to split: {splitter.count_tokens(text=text_to_split)}")
text_chunks = splitter.split_text(text=text_to_split)
 
print(text_chunks[1])

4、NLTK如何测量块大小:通过字符数。

from langchain_text_splitters import NLTKTextSplitter
 
# This is a long document we can split up.
with open("./demo_static/splitters_test.txt") as f:
    state_of_the_union = f.read()
text_splitter = NLTKTextSplitter(chunk_size=1000)
 
texts = text_splitter.split_text(state_of_the_union)
print(texts[0])

三、向量库→检索器/检索方式

嵌入向量需要高效索引器

3.1 Chroma读取文件后返回类对象的方法 as_retriever

在 client = chromadb.Client()中,client 是一个 chromadb.Client 类的实例化对象。

client 对象通常用于执行以下操作:连接到数据库、对数据库执行各种查询操作,如插入数据、检索数据、更新数据、删除数据等、管理集合:你可以使用 client 对象来创建、删除或操作集合(类似于表格的概念)。

vectorstore = Chroma.from_documents(documents=splits, embedding=embeddings)
retriever = vectorstore.as_retriever()

Chroma.from_documents:会创建一个向量存储,将文档转换为嵌入向量,并将它们存储在向量数据库中。

as_retriever:将 vectorstore 转换为一个检索器对象(retriever),该对象可以用于从存储的向量中检索最相似的向量。使用了 基于相似度的检索方法,具体来说,用余弦相似度来召回。

retriever = vectorstore.as_retriever(search_type="similarity", search_kwargs={"k": 5})

召回过程:当你使用 retriever 来检索时,通常会指定 k 的值,即返回前 k 个最相似的向量。然后,retriever 会根据余弦相似度或其他可能的距离度量方法(如欧氏距离、内积等),返回与查询向量最相似的文档或片段。如果你需要具体调整检索方法或使用不同的相似度度量,可以在调用 as_retriever() 时通过 search_typesearch_kwargs 参数进行配置。

3.2 FAISS读取文件后返回类对象的方法 as_retriever

FAISS(Facebook AI Similarity Search)来创建和保存一个向量存储库

  • 这一步将 FAISS 向量索引保存到本地磁盘,保存的路径为 "faiss_db".
  • 这个保存的索引可以稍后重新加载,以便你在不同的会话或程序中重复使用这个向量索引,而不必重新计算所有的嵌入向量。
  • 通过这种方式,你可以在不同的会话中复用已构建的向量索引,而无需重新处理所有数据。
from langchain.agents import AgentExecutor, create_tool_calling_agent
text_chunks = ["Text chunk 1", "Text chunk 2", "Text chunk 3"]
embeddings = YourEmbeddingModel()  # 用于生成嵌入向量的模型
vector_store = FAISS.from_texts(text_chunks, embedding=embeddings)
vector_store.save_local("faiss_db")
loaded_vector_store = FAISS.load_local("faiss_db", embedding=embeddings)
retriever = loaded_vector_store.as_retriever(search_type="similarity", search_kwargs={"k": 5})

也可以没有save和load的操作。

使用检索器查询相关文档:

docs = retriever.get_relevant_documents("知乎是什么") 
prine(docs)

3.3 高级检索方法

  • 最大边际相关性检索

最大边际相关性检索(Maximum Marginal Relevance, MMR)是一种平衡相关性(relevance)与新颖性(novelty)的文档检索方法。该方法旨在解决在检索过程中可能出现的结果冗余问题,即避免返回给用户过多相似或重复的信息。通过同时考虑待检索文档与查询的相关性以及待检索文档之间的相异度,MMR能够在保证信息相关性的同时,增加检索结果的多样性。

默认情况下,Vector Store-backed Retriever 使用相似性搜索。如果底层向量存储支持最大边际相关性搜索,你可以指定这种搜索类型:

retriever = db.as_retriever(search_type="mmr")
docs = retriever.get_relevant_documents("知乎是什么") 
  • 相似度分数阈值检索

相似度分数阈值检索是一种基于相似度分数设定阈值的检索方法。在这种检索策略下,只有当文档与查询的相似度分数高于某个预设的阈值时,该文档才会被检索并返回给用户。这种方法有利于过滤掉与查询相关性较低的文档,确保返回的结果具有较高的相关性。

相似度分数阈值检索适用于对结果质量要求较高的场景,通过调整阈值的大小,可以灵活控制检索结果的精确度和召回率之间的平衡。阈值设置较高时,可以获得更为精确但数量可能较少的结果;阈值设置较低时,则可以获得更多但可能包含一些相关性较低的结果:

retriever = db.as_retriever(
    search_type="similarity_score_threshold", search_kwargs={"score_threshold": 0.5}
)
docs = retriever.get_relevant_documents("知乎是什么") 
  • 指定Top K

指定Top K检索是一种简单直观的检索方法,即在所有检索结果中,仅返回相似度分数最高的前K个文档。这种方法非常适用于需要快速获取最相关信息的场景,比如用户仅关注最相关的几条信息,而不需要查看大量可能相关的文档。

通过指定K的值,可以直接控制返回结果的数量,使得检索结果更加精简和专注。这种方法的优势在于操作简单、执行效率高,尤其适合在大规模文档集合中快速定位最相关的信息:

retriever = db.as_retriever(search_kwargs={"k": 1})
docs = retriever.get_relevant_documents("知乎是什么")
print(len(docs))

总结来说,这三种检索策略各有特点和适用场景。最大边际相关性检索强调结果的相关性和多样性,适用于需要综合考虑信息全面性和避免重复的场景;相似度分数阈值检索侧重于过滤掉低相关性结果,适用于对结果质量要求较高的情况;而指定Top K检索则提供了一种快速获取最相关结果的简便方法,适用于需要迅速获取关键信息的场景。

四、embedding模型

bge-base-zh-v1.5

参考

LangChain学习笔记:检索器-矢量存储检索器

LangChain教程 | langchain 文本拆分器 | Text Splitters全集

<think>好的,用户想开发一个多模块的Python应用,集成阿里云DeepSeek-R1模型的API调用,并且实现与用户交互的功能,包括参数设置、文件处理和知识库管理。首先,我需要理清用户的具体需求,然后分步骤思考如何实现。 首先,用户提到要调用阿里云DeepSeek-R1的API。这需要先了解阿里云API的调用方式,通常需要API密钥、服务地址和正确的请求头。可能需要查阅阿里云的文档,确认API的端点、请求参数和返回格式。然后,如何用Python发送HTTP请求,这里可以使用requests库,构造POST请求,处理响应。 接下来是自定义模型参数,比如温度和最大输出长度。这些参数通常作为请求的JSON体中的参数传递。需要设计一个配置模块,允许用户设置这些参数,可能通过配置文件或命令行参数,这里可能需要一个单独的模块来处理配置的加载和保存。 然后是文件上传和知识库管理。用户需要上传文件来创建知识库,并在回复中引用这些内容。这部分可能需要文档加载和向量存储,比如使用LangChain的文档加载器来处理不同格式的文件,然后进行文本分割,使用嵌入模型生成向量,并存储到向量数据库中。当用户提问时,需要先检索知识库中的相关内容,再结合这些内容生成回答。可能还需要处理临时文件的上传,作为输入token,这部分可能需要文件处理模块,比如读取文件内容,分块处理,并临时存储。 关于代码结构,用户希望拆分为多个Python文件。常见的模块划分可能包括:API调用模块、配置管理模块、文件处理模块、知识库管理模块、主程序模块。这样便于维护和扩展。 接下来,我需要考虑各个模块之间的交互。比如,主程序调用API模块发送请求,配置模块提供参数,文件处理模块处理上传的文件,知识库模块负责存储和检索。同时,需要处理错误情况,比如API调用失败、文件格式不支持等,可能需要异常处理。 在具体实现步骤上,首先设置API的配置,比如在config.py中存储API密钥和端点。然后创建api_client.py,处理请求的构造和发送,包括生成正确的headers和json数据。对于知识库管理,可能需要使用向量数据库,比如FAISS,结合LangChain的文本分割器,将文档分块存储。文件处理模块需要支持多种格式,如PDF、Word、TXT,使用相应的库进行读取,比如PyPDF2、python-docx等。 临时文件作为输入token,可能需要将文件内容读取后,分割成适当的块,并添加到请求的上下文中。这里可能需要限制文件大小,处理大文件的分块上传。 测试方面,需要确保各个模块能正确协同工作,API响应被正确解析,知识库内容能被正确检索和引用。可能需要编写单元测试,或者手动测试各个功能点。 最后,用户可能需要部署这个应用,考虑如何打包,或者提供命令行接口或简单的Web界面。但根据当前问题,可能只需要控制台交互即可。 总结一下,关键步骤包括:配置API访问、实现API调用模块、处理参数配置、文件上传与处理、知识库的构建与检索,以及模块化代码结构。需要确保各部分之间的数据流正确,并处理可能的异常情况。</think>以下是如何构建该系统的分模块实现方案: 1. **基础配置模块 (config.py)** ```python # config.py DEEPSEEK_CONFIG = { "api_key": "your_api_key", "endpoint": "https://api.deepseek.com/v1/chat/completions", "temperature": 0.7, "max_tokens": 2000, "knowledge_base_path": "./knowledge_base" } ``` 2. **API调用核心模块 (api_client.py)** ```python # api_client.py import requests import json from config import DEEPSEEK_CONFIG class DeepSeekClient: def __init__(self): self.headers = { "Authorization": f"Bearer {DEEPSEEK_CONFIG['api_key']}", "Content-Type": "application/json" } def generate_response(self, prompt, context=None, **kwargs): payload = { "model": "deepseek-r1", "messages": self._build_messages(prompt, context), "temperature": kwargs.get('temperature', DEEPSEEK_CONFIG['temperature']), "max_tokens": kwargs.get('max_tokens', DEEPSEEK_CONFIG['max_tokens']) } response = requests.post( DEEPSEEK_CONFIG['endpoint'], headers=self.headers, json=payload ) return self._handle_response(response) def _build_messages(self, prompt, context): messages = [{"role": "user", "content": prompt}] if context: messages.insert(0, {"role": "system", "content": context}) return messages def _handle_response(self, response): if response.status_code == 200: return json.loads(response.text)['choices'][0]['message']['content'] raise Exception(f"API请求失败: {response.status_code} - {response.text}") ``` 3. **知识库管理模块 (knowledge_base.py)** ```python # knowledge_base.py import os from langchain.document_loaders import DirectoryLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from config import DEEPSEEK_CONFIG class KnowledgeManager: def __init__(self): self.splitter = RecursiveCharacterTextSplitter( chunk_size=1000, chunk_overlap=200 ) def process_uploaded_file(self, file_path): # 支持PDF/TXT/DOCX文件处理 loader = DirectoryLoader(os.path.dirname(file_path), glob=f"**/{os.path.basename(file_path)}") docs = loader.load() chunks = self.splitter.split_documents(docs) self._save_to_knowledge_base(chunks) def _save_to_knowledge_base(self, chunks): # 实现向量存储和检索逻辑 # 可使用FAISSChroma等向量数据库[^1] pass def retrieve_context(self, query): # 实现语义搜索逻辑 return "相关上下文内容" ``` 4. **主程序模块 (main.py)** ```python # main.py from api_client import DeepSeekClient from knowledge_base import KnowledgeManager class Application: def __init__(self): self.client = DeepSeekClient() self.knowledge = KnowledgeManager() def process_input(self, prompt, temp_file=None): context = self._build_context(prompt, temp_file) return self.client.generate_response(prompt, context) def _build_context(self, prompt, temp_file): # 结合知识库和临时文件构建上下文 kb_context = self.knowledge.retrieve_context(prompt) file_context = self._process_temp_file(temp_file) if temp_file else "" return f"{kb_context}\n{file_context}" def _process_temp_file(self, file_path): # 处理临时文件内容 with open(file_path, 'r') as f: return f.read() ``` **实现说明:** 1. 模块化架构将功能拆分为API交互、知识库管理、配置设置等独立模块 2. 支持通过`generate_response`的参数动态调整模型参数 3. 使用LangChain的文档加载器和文本分割器处理文件 4. 知识库管理模块预留了向量存储接口,可根据需要集成FAISS等数据库 5. 主程序协调各模块工作流,实现上下文构建和响应生成
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值