️ LangChain学习整理

🦜️🔗 LangChain

from dotenv import load_dotenv
import os
load_dotenv(dotenv_path = ".env",override = True)
os.environ["OPENAI_API_BASE"] = os.environ.get("OPENAI_API_BASE")
os.environ["OPENAI_API_KEY"] = os.environ.get("OPENAI_API_KEY")

1.LLMs vs Chatmodels

LangChain框架中,LLMs(Large Language Models)和Chat Models是两种不同类型的语言模型接口,它们各自有不同的特点和用途。

1.1.LLMs (Large Language Models)

  • 定义:LLMs 是以文本字符串作为输入并返回文本字符串的模型。它们封装的API接受一个字符串提示作为输入,并输出一个字符串补全。
  • 使用场景:LLMs 适用于需要文本生成、文本补全、问答系统等场景,它们可以处理广泛的语言任务。
  • 特点:LLMs 通常是基于纯文本的输入和输出,不涉及对话上下文或消息类型。
from langchain_openai import OpenAI
llm = OpenAI(temperature=0.5)
response = llm("你好")
response
llm.model_name

1.2.Chat Models

  • 定义:Chat Models 是由语言模型支持的,专门为对话场景调优的模型。它们的API接受一个聊天消息列表作为输入,这些消息通常包含文本和消息类型(如"System"、“Human”、“AI”),这种message类型是langchain的一个类,包含内容和角色。
  • 使用场景:Chat Models 适合构建聊天机器人或对话系统,它们能够处理多轮对话并维持上下文连贯性。
  • 特点:Chat Models 能够根据对话历史和当前消息生成回复,通常用于交互式应用程序。
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage
chat = ChatOpenAI(temperature=0.5, model="gpt-3.5-turbo")
messages = [
    SystemMessage(content="You are an expert on large language models."),
    HumanMessage(content="你是chatgpt吗")
]
response = chat(messages)
response
print(type(response.content))
print('-'*100)
print(response.content)

1.3.主要区别

  1. 输入输出格式:LLMs 处理的是纯文本字符串,而 Chat Models 处理的是带有角色标签的聊天消息列表。
  2. 上下文理解:Chat Models 能够更好地理解和利用对话上下文,因为它们的输入包含了消息类型,这有助于模型区分对话中的不同角色和环节。
  3. 应用场景:LLMs 适合广泛的文本生成任务,Chat Models 更专注于对话和交互式场景。

在这个对比中,LLMs 通过 OpenAI 类使用,而 Chat Models 通过 ChatOpenAI 类使用。Chat Models 需要提供消息列表,其中包含了对话的角色信息。

注意事项

  • LangChain 设计了统一的接口,使得在不同模型之间切换变得容易。例如,它们都实现了 Base Language Model 接口,这包括 predict 方法(用于 LLMs)和 predict messages 方法(用于 Chat Models)。
  • 开发者应根据应用程序的具体需求选择使用 LLMs 或 Chat Models。
print(chat.predict('hello'))

print('-'*100)

print(chat.predict_messages([ 
    HumanMessage(content="hello")
    ]))

2.Message class

在LangChain中,Message 是对话系统交互的基本单元,用于在对话的不同参与者之间传递信息。

每个 Message 通常包含文本内容、发送者信息和可能的其他上下文数据。

Message 的重要性:

  • 上下文管理:消息允许模型跟踪对话的上下文,这对于生成连贯且相关的回复至关重要。

  • 角色区分:消息可以包含发送者的角色信息,如用户、系统或AI,这有助于模型理解每条消息的意图和上下文。

  • 交互式对话:消息是实现交互式对话的基础,允许用户和模型之间进行多轮交流

LangChain 定义了几种消息类型,每种类型有特定的用途:

  • SystemMessage: 包含系统级别的指令或初始化信息,为AI的后续回复设定上下文。

  • HumanMessage: 代表用户的输入,可以是问题、陈述或其他形式的对话。

  • AIMessage: AI系统生成的回复,基于对 SystemMessage 或 HumanMessage 的理解。

from langchain_core.messages import (
    SystemMessage,
    AIMessage,
    HumanMessage
)

2.1.Message的常见属性

  • Content: 消息的文本内容。
  • construct(): 返回message的具体类型,如SystemMessageHumanMessageAIMessage
  • type(): 返回message的类型,如SystemHumanAI
  • dict: 将列表的各种信息以dict形式返回
message1 = SystemMessage(content="You are an expert on large language models.")
message1.dict()
print(type(message1.construct()), message1.construct())
print(type(message1.type), message1.type)

2.2. Message的类型转换

# 内容转为str
message_str = message1.content
# 转为json
message_jason = message1.to_json()

print(message_str)
print(message_jason)
message2 = SystemMessage(content=message_str)
message2

3.Prompt Class

在LangChain中,Prompt 组件用于构建和格式化输入,以便有效地与语言模型交互。

3.1.PromptTemplate

  • 这时一个最基本的Prompt类,允许开发者定义一个文本模板,该模板包含静态文本和占位符。

  • 目的:PromptTemplate 允许开发者定义一个文本模板,该模板包含静态文本和占位符。这些占位符在生成最终提示时会被具体的值替换。

  • 使用场景:适用于需要根据不同输入动态生成提示的场景。

from langchain.prompts import PromptTemplate

# 定义一个提示模板,包含一个占位符 {input}
template = "What is the capital of {input}?"

# 创建 PromptTemplate 实例
prompt = PromptTemplate.from_template(template)

# 使用具体的值填充模板,生成最终的提示
final_prompt = prompt.format(input="China")
print(type(prompt))
print(prompt)
print('-'*100)
print(type(final_prompt))
print(final_prompt)

3.2.其他PromptTemplate

3.2.1.(System/Human/AI)MessagePromptTemplate

这几个prompt会生成相应的message类型的数据

  • 基本用法

    • SystemMessagePromptTemplate用于创建系统级别的消息模板,通常包含初始化信息或指令。

    • HumanMessagePromptTemplate用于创建用户级别的消息模板,通常包含问题或陈述。

    • AIMessagePromptTemplate用于创建AI级别的消息模板,通常包含回复或响应。

  • 应用场景

    • SystemMessagePromptTemplate 适用于初始化对话或发送指令,设定AI的角色、提供对话的背景信息

    • HumanMessagePromptTemplate 在对话系统中模拟用户提问或陈述,用于测试AI的响应或构建交互式对话

    • AIMessagePromptTemplate 生成AI的回复,与HumanMessagePromptTemplate配合使用构建对话历史

from langchain.prompts import (
    SystemMessagePromptTemplate,
    HumanMessagePromptTemplate,
    AIMessagePromptTemplate
)

# 创建一个系统消息提示模板
system_template = "You are a helpful assistant that translates {input_language} to {output_language}."
system_prompt = SystemMessagePromptTemplate.from_template(system_template)
# 使用具体的值填充模板
system_message = system_prompt.format(input_language="English", output_language="French")

human_template = "{text}"  # 这里的文本将由用户输入
human_prompt = HumanMessagePromptTemplate.from_template(human_template)
# 假设用户输入了一句话
user_input = "What is the weather like today?"
human_message = human_prompt.format(text=user_input)

# 创建一个AI消息提示模板
ai_template = "The weather today is {weather_description} with a high of {high_temperature}."
ai_prompt = AIMessagePromptTemplate.from_template(ai_template)
# AI生成的回复
ai_response = ai_prompt.format(weather_description="sunny", high_temperature="75 degrees")

print('-'*25+'system'+'-'*25)
print(type(system_prompt))
print(system_prompt)
print('-'*56)
print(type(system_message))
print(system_message)
print('-'*25+'human'+'-'*25)
print(type(human_prompt))
print(human_prompt)
print('-'*56)
print(type(human_message))
print(human_message)
print('-'*25+'human'+'-'*25)
print(type(ai_prompt))
print(ai_prompt)
print('-'*56)
print(type(ai_response))
print(ai_response)
3.2.2.ChatPromptTemplate
  • 基本用法:专门用于对话系统,可以处理对话消息列表,每个消息都与一个角色相关联。
  • 应用场景:用于构建聊天机器人或对话代理,其中包含用户和AI的多轮对话。
from langchain.prompts import ChatPromptTemplate

# 使用前面定义的系统、人类和AI提示模板创建对话提示
chat_prompt = ChatPromptTemplate.from_messages([
    system_prompt,
    human_prompt,
    ai_prompt
])

# 格式化整个对话提示
dialogue = chat_prompt.format(
    input_language="English",
    output_language="French",
    text="What is the weather like today?",
    weather_description="sunny",
    high_temperature="75"
)

# 输出可能是一个包含了系统消息、用户消息和AI回复的消息列表
print('-'*25+'chat'+'-'*25)
print(type(chat_prompt))
print(chat_prompt)
print('-'*56)
print(type(dialogue))
print(dialogue)
len(dialogue)
3.2.3.FewshotPromptTemplate:

输入参数:

  • examples: 一个示例列表,每个示例是一个包含输入和输出的字典。

  • example_prompt: 一个 PromptTemplate 对象,用于格式化每个示例。

  • prefix: 生成提示时附加在示例前面的字符串。

  • suffix: 生成提示时附加在示例后面的字符串。

  • input_variables: 一个包含用于格式化最终提示的输入变量的列表。

基本用法:

  • 准备示例:创建一个示例列表,每个示例包含问题和答案。

  • 创建格式化程序:配置一个PromptTemplate对象,用于将示例格式化为字符串。

  • 创建FewShotPromptTemplate对象:使用示例和格式化程序创建FewShotPromptTemplate实例。

  • 生成最终提示:使用format方法和新的问题生成最终的提示。

from langchain.prompts.few_shot import FewShotPromptTemplate
from langchain.prompts.prompt import PromptTemplate

# 准备示例
examples = [
    {"question": "Who lived longer, Muhammad Ali or Alan Turing?", "answer": "Muhammad Ali"},
    # 可以添加更多示例
]

# 创建示例格式化程序
example_prompt = PromptTemplate(
    input_variables=["question", "answer"],
    template="Question: {question}\nAnswer: {answer}"
)

# 创建 FewShotPromptTemplate 对象
few_shot_prompt = FewShotPromptTemplate(
    examples=examples,
    example_prompt=example_prompt,
    prefix = "Few-shot learning example:",
    suffix="\nQuestion: {input}",
    input_variables=["input"]
)

# 生成最终提示
final_prompt = few_shot_prompt.format(input="Who was the maternal grandfather of George Washington?")
print('-'*25+'few-shot'+'-'*25)
print(type(few_shot_prompt))
print(few_shot_prompt)
print('-'*56)
print(type(final_prompt))
print(final_prompt)
3.2.4.MessagesPlaceholder:

基本用法:

  • 定义消息占位符:在创建提示模板时,使用 MessagesPlaceholder 来定义一个占位符,该占位符将在运行时被实际的消息列表替换。

  • 插入消息:在提示模板中,通过 MessagesPlaceholder 预留的位置,可以在生成最终提示时动态地插入一系列消息。

  • 生成提示:使用 format_prompt 方法(或类似的机制),传入一个消息列表,这些消息将替换提示模板中的占位符。

from langchain.prompts import MessagesPlaceholder, HumanMessagePromptTemplate
from langchain.prompts.chat import ChatPromptTemplate

# 定义一个人类消息提示模板
human_prompt = "What is the best way to learn programming?"
human_message_template = HumanMessagePromptTemplate.from_template(human_prompt)

# 创建一个消息占位符
messages_placeholder = MessagesPlaceholder(variable_name="conversation")

# 创建一个聊天提示模板,并使用消息占位符
chat_prompt = ChatPromptTemplate.from_messages([
    messages_placeholder,
    human_message_template
])

# 定义一些消息
human_message = "What is the best way to learn programming?"
ai_message = """1. Choose a programming language: Decide on a programming language that you want to learn.
2. Start with the basics: Familiarize yourself with the basic programming concepts such as variables, data types and control structures.
3. Practice, practice, practice: The best way to learn programming is through hands-on experience."""

# 格式化提示,将消息列表插入占位符
final_prompt = chat_prompt.format_prompt(conversation=[human_message, ai_message], word_count="10")

# 输出最终的提示消息列表
print(final_prompt.to_messages())

4.Memory

LangChain 的 Memory 组件是构建对话系统中的一个关键部分,它允许模型在对话过程中存储和检索数据。

这个组件使得语言模型能够记住之前的交互,从而提供更加连贯和上下文相关的回复

4.1.ConversationBufferMemory

这个组件就是会一直记住对话的历史,可以用于构建对话系统,实现多轮对话。

4.2.ConversationSummaryMemory

这个组件用于存储对话的摘要信息,可以用于生成对话总结或提取对话中的关键信息。

4.2.ConversationSummaryBufferMemory

这个结合了buffersummary的功能,可以用于存储对话的历史和摘要信息,提供更全面的对话管理功能。

summary会将总结保存为SysteMessage,buffer则会将对话保存为HumanMessage和AIMessage。

基本用法:

  • buffer ConversationSummaryBufferMemory 在内存中保留最近的交互缓冲区。

  • summary 当buffer超过一定长度,它不仅清除旧的交互,而是将它们编译成摘要,并同时使用缓冲区和摘要。

适用场景:

  • 长对话:适合于需要记忆长对话的场景,如聊天机器人或多轮对话系统。

  • 上下文理解:在生成回答时需要考虑对话的上下文信息。

总的来说,摘要以及上下文的信息的variables_keyhistory,可以通过load_memory_variables的方法来获取一个记忆字典,其中包含了buffersummary的信息。

from langchain.memory import ConversationSummaryBufferMemory
from langchain_openai import ChatOpenAI

# 初始化 OpenAI 模型
llm = ChatOpenAI()

# 创建 ConversationSummaryBufferMemory 实例,设置最大令牌限制
memory = ConversationSummaryBufferMemory(
    llm=llm,
    max_token_limit=40,
    return_messages=True  # 返回消息列表
)

# 模拟对话交互
memory.save_context({"input": "嗨"}, {"output": "最近怎么样?"})
memory.save_context({"input": "没什么特别的,你呢?"}, {"output": "没什么特别的。"})

dialogue = memory.moving_summary_buffer
context = memory.chat_memory.messages
memory_data = memory.load_memory_variables({})
print(type(dialogue))
print(dialogue)
print('-'*100)
print(type(context))
print(context)
print('-'*100)
print(type(memory_data))
print(memory_data)

5.RAG

5.0.Summary

A typical RAG application has two main components:

Indexing: a pipeline for ingesting data from a source and indexing it. This usually happens offline.

Retrieval and generation: the actual RAG chain, which takes the user query at run time and retrieves the relevant data from the index, then passes that to the model.

The most common full sequence from raw data to answer looks like:

Indexing

  • Load: First we need to load our data. This is done with DocumentLoaders.

  • Split: Text splitters break large Documents into smaller chunks. This is useful both for indexing data and for passing it in to a model, since large chunks are harder to search over and won’t fit in a model’s finite context window.

  • Store: We need somewhere to store and index our splits, so that they can later be searched over. This is often done using a VectorStore and Embeddings model.

在这里插入图片描述

Retrieval and generation

  • Retrieve: Given a user input, relevant splits are retrieved from storage using a Retriever.

  • Generate: A ChatModel / LLM produces an answer using a prompt that includes the question and the retrieved data

在这里插入图片描述

# print出document class的内容
def pretty_print_docs(docs):
    print(
        f"\n{'-' * 100}\n".join([
            f"Document {i+1}:\n\n" + 
            d.page_content for i, d in enumerate(docs)]))

5.1.Indexing

5.1.1.Load

DocumentLoader: Object that loads data from a source as list of Documents.

Document:Contains text and metadata.

  • Docs: Detailed documentation on how to use DocumentLoaders.

  • Integrations: 160+ integrations to choose from.

  • Interface: API reference for the base interface.

Subclass from DocumentLoader:

LangChain提供了多种文档加载器(loaders),这些加载器可以读取和处理不同格式的文档,并将它们转换成统一的文档对象(Document),以便于后续处理。以下是 LangChain 中一些文档加载器的介绍:

  1. UnstructuredFileLoader:用于加载非结构化文本文件,如 TXT、Word 文档等。

  2. UnstructuredMarkdownLoader:专门用于加载 Markdown 格式的文档。

  3. UnstructuredPDFLoader:加载 PDF 格式的文档。

  4. UnstructuredImageLoader:用于加载图像文件,如 JPG,并从中提取文本。

  5. CSVLoader:加载 CSV 文件,并将每一行数据转换成一个文档对象。

  6. DirectoryLoader:可以加载一个目录下所有匹配特定模式的文件,适用于批量处理文件。

  7. TextLoader:加载纯文本文件,并将文件内容作为单个文档对象加载。

  8. BSHTMLLoader:使用 BeautifulSoup4 加载 HTML 文档,提取文本内容。

  9. JSONLoader:加载 JSON 格式的文件,可以使用 jq 模式解析 JSON 文件。

  10. PyPDFLoaderPyPDFDirectoryLoader:加载 PDF 文档,PyPDFDirectoryLoader 用于从目录加载多个 PDF 文档。

  11. PDFMinerLoader:另一种加载 PDF 文件的加载器。

  12. UnstructuredWordDocumentLoader:加载 Word 文档(包括 .doc 和 .docx 格式)。

这些加载器通过 load 方法读取文件内容,并将其转换为 LangChain 框架中的 Document 对象。Document 对象通常包含文本内容、元数据(如文件路径、名称等)和可能的文本分割信息。这些加载器可以单独使用,也可以与其他 LangChain 组件结合使用,以构建更复杂的数据处理流程。

CSVLoader

CSVLoader是一个用于从CSV文件中加载数据的类,它将CSV文件中的数据转化为Document对象,以便于后续处理。以下是CSVLoader的关键参数及其用法的总结:

  1. file_path:这是要加载的CSV文件的路径。这是必须提供的参数,指定了要加载数据的文件位置。

  2. source_column:(可选)一个字符串,指定CSV文件中的一个列名,该列的值将被用作每个Document对象的source属性。如果不指定,将使用文件路径作为source

  3. metadata_columns:(可选)一个字符串序列,指定CSV文件中应该作为元数据加载的列。这些列的值将被添加到每个Document对象的metadata属性中。

  4. csv_args:(可选)一个字典,包含传递给Python内置csv.DictReader的参数,如delimiter(定界符)、quotechar(引用字符)等。这允许你自定义CSV文件的解析方式,以适应不同的CSV格式。

  5. mode:(可选)加载CSV文件时使用的模式。可以是"single"或"elements"。在"single"模式下,整个CSV文件被加载为单个Document对象。在"elements"模式下,CSV文件的每一行被加载为一个单独的Document对象。

  6. column:(在某些实现中提到)指定要从CSV文件中提取的单个列的名称。当指定时,将为CSV文件中的每一行创建一个Document,并将指定列的值用作该DocumentpageContent

from langchain_community.document_loaders.csv_loader import CSVLoader

loader1 = CSVLoader(file_path='./data/Data.csv', source_column="Review")
loader2 = CSVLoader(file_path='./data/Data.csv', source_column="Product")
loader3 = CSVLoader(
    file_path='./data/Data.csv',
    csv_args={
        'delimiter': ',',
        'quotechar': '"', # 指定引号字符,将引号内的内容识别为一个整体
        'fieldnames': ['Review', 'Product'], # 指定列名
    }
)
data1 = loader1.load()
data2 = loader2.load()
data3 = loader3.load()
print(data1[0].metadata)
data1
print(data2[0].metadata)
data2
print(data3[0].metadata)
data3
PyPDFLoader

PyPDFLoader 是 LangChain 中用于加载 PDF 文件的类。它使用 pypdf 库来读取 PDF 文件,并将文件内容分割成单独的页面或部分。以下是 PyPDFLoader 的关键参数及其用法的总结:

  1. file_path: 这是要加载的 PDF 文件的路径。这是必须提供的参数,指定了要加载数据的文件位置。

  2. password: (可选)如果 PDF 文件被密码保护,此参数用于提供密码以解锁文件。它可以是一个字符串或者字节类型。

  3. headers: (可选)如果 PDF 文件是从网络资源加载的,此参数可以提供 HTTP 请求的头部信息。

  4. extract_images: (可选)一个布尔值,如果设置为 True,则从 PDF 文件中提取图像。

  5. text_splitter: (可选)当使用 load_and_split 方法时,此参数可以指定一个 TextSplitter 实例,用于将加载的文档进一步分割成更小的块。

  6. mode: (可选)在某些实现中,mode 参数可以指定加载 PDF 文件的方式。例如,使用 “single” 模式时,整个 PDF 文档将作为一个单独的 Document 对象返回;而 “elements” 模式则可能会根据文档的结构(如标题和描述文本)进行分割。

  7. web_path: (可选)如果 PDF 是通过 Web 路径提供的,此参数可用于指定该路径。

from langchain_community.document_loaders import PyPDFLoader

loader = PyPDFLoader("./data/pdf/避开误区清黑头.pdf")
pages = loader.load_and_split()
pretty_print_docs(pages)
Web
import bs4
from langchain_community.document_loaders import WebBaseLoader

# Only keep post title, headers, and content from the full HTML.
bs4_strainer = bs4.SoupStrainer(class_=("post-title", "post-header", "post-content"))
loader = WebBaseLoader(
    web_paths=("https://lilianweng.github.io/posts/2023-06-23-agent/",),
    bs_kwargs={"parse_only": bs4_strainer},
)
docs = loader.load()
print(len(docs[0].page_content))
print(type(docs[0].page_content))
print(docs[0].page_content[0:500])
5.1.2.Split

SentenceTransformersSplitter还不太懂

This is an import step, it needs to keep the semantically related pieces of text together.

TextSplitter: Object that splits a list of Documents into smaller chunks. Subclass of DocumentTransformers.

DocumentTransformer: Object that performs a transformation on a list of Documents.

  • Docs: Detailed documentation on how to use DocumentTransformers

  • Integrations

  • Interface: API reference for the base interface.

Splitter的分类:

文本分割器的工作原理大致如下:

  1. 将文本分割成小的、语义上有意义的块(通常是句子)。

  2. 开始将这些小块组合成更大的块,直到达到某个特定大小(通过某种函数来衡量)。

  3. 一旦达到该大小,就将该块作为自己的文本块,并开始创建一个新的文本块,同时保持一些重叠(以保持块之间的上下文联系)。

这意味着可以沿着两个不同的轴定制你的文本分割器:

  • 文本是如何被分割的

  • 块大小是如何被衡量的

  • 名称:文本分割器的名称

  • 分割依据:这个文本分割器是如何分割文本的

  • 添加元数据:这个文本分割器是否添加有关每个文本块来源的元数据

  • 描述:对分割器的描述,包括推荐使用该分割器的情境

名称分割方式添加元数据描述
Recursive用户定义的字符列表递归地分割文本。递归分割的目的是尝试保持相关文本片段在一起。这是开始分割文本的推荐方式。
HTMLHTML特定字符基于HTML特定字符分割文本。显著地,它添加了有关该文本块来源的相关信息(基于HTML)。
MarkdownMarkdown特定字符基于Markdown特定字符分割文本。显著地,它添加了有关该文本块来源的相关信息(基于Markdown)。
Code编程语言(Python, JS等)特定字符基于编程语言特定字符分割文本。提供15种不同的语言可供选择。
Token标记基于标记分割文本。存在几种不同的方法来衡量标记。
Character用户定义的字符基于用户定义的字符分割文本。这是更简单的方法之一。
[Experimental] Semantic Chunker句子首先按句子分割,然后将相邻的句子组合在一起,如果它们在语义上足够相似。来源于Greg Kamradt。
AI21 Semantic Text Splitter语义识别形成连贯文本片段的不同主题,并沿着这些主题进行分割。
Subclass of splitter

LangChain提供了多种文本分割器(Splitters),用于将大型文本文档分割成更易于处理的小块。以下是LangChain中一些文本分割器的子类,根据搜索结果整理得出:

  1. RecursiveCharacterTextSplitter:递归地使用不同的字符,如换行符、空行和空格进行文本分割,创建语义上有意义的文本块。

  2. MarkdownHeaderTextSplitter:根据Markdown文档中的标题进行分割,允许用户指定要分割的标题。

  3. RecursiveCharacterSplitter:用于根据LaTeX特定标记列表分割文本。

  4. MarkdownTextSplitter:用于将文本沿Markdown标题、代码块或水平线分割。

  5. CharacterTextSplitter:根据给定的分隔符将文本切割成片段,可以设置分块大小和分块重叠。

  6. SemanticChunker:会在嵌入空间根据语义的相似性来切割文本

  7. LatexTextSplitter:特定于LaTeX文本的分割器,根据LaTeX的特定语法进行分割。

  8. Sentence Transformers Token Text Splitter:为sentence-transformer模型设计的文本分割器,将文本分割为适合该模型使用的token窗口。

  9. Custom Text Splitter:自定义的文本分割器,通过继承TextSplitter类并实现splitText方法来创建。

这些分割器可以应用于不同的场景,如文本分类、信息检索、机器翻译和语义分析等任务。选择合适的分割器取决于具体的应用场景和需求。LangChain还允许用户根据具体需求自定义文本分割器,以实现更精确的文本分割。

RecursiveCharacterTextSplitter

基本参数:

  1. separators: 一个字符列表,用于尝试文本分割。默认字符包括["\n\n", "\n", " ", ""],即换行符、空格等。

  2. chunk_size: 分割后文本块的目标大小。在实际应用中,可以通过这个参数控制最终文档的最大大小(以字符数为单位)。

  3. chunk_overlap: 文本块之间的重叠字符数。这有助于确保文本不会被不恰当地分割,同时提供上下文信息。

  4. keep_separator: 一个布尔值,指示是否在分割后的文本块中保留分隔符,默认为True

  5. add_start_index: 一个布尔值,指示是否在分割后的文本块的meta信息中添加起始索引,默认为True

类方法:
RecursiveCharacterTextSplitter提供了以下几种方法:

  1. __init__: 构造函数,用于创建一个新的TextSplitter实例。

  2. split_text: 接受一个字符串输入,表示要分割的文本,并返回一个字符串数组,每个字符串代表分割后的一个块。

  3. create_documents: 从文本列表创建文档。

  4. split_documents: 接受一个文档序列,并将它们分割成更小的文档。

  5. transform_documents: 通过分割文档序列来转换它们。

RecursiveCharacterTextSplitter是处理通用文本时推荐的分割器,它通过字符列表参数化,并尝试按顺序使用这些字符进行分割,直到块大小足够小。

from langchain_text_splitters import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000, chunk_overlap=200, add_start_index=True
)
all_splits = text_splitter.split_documents(docs)
print(type(docs),len(docs))
print(type(all_splits),len(all_splits))
print(len(all_splits[0].page_content))
print(all_splits[0].page_content)
print(all_splits[65].metadata)

在处理没有单词边界的书写系统(如中文、日文和泰文)时,

使用默认的分隔符列表 ["\n\n", "\n", " ", ""] 可能会导致单词在块之间被分割。

为了保持单词的完整性,可以覆盖分隔符列表以包含额外的标点符号。

推荐添加的标点符号:

  • ASCII句号.”,用于英文文本。

  • 全角句号 “.”,在中文文本中使用。

  • 汉字句号 “。”,在日语和中文中使用。

  • 零宽度空格,在泰文、缅甸文、高棉语和日文中使用。

  • ASCII逗号,”,用于英文文本。

  • 全角逗号 “,”,在中文文本中使用。

  • 汉字逗号 “、”,在中文和日文中使用。

    text_splitter = RecursiveCharacterTextSplitter(
    separators=[
        "\n\n",
        "\n",
        " ",
        ".",
        ",",
        "\u200B",  # Zero-width space
        "\uff0c",  # Fullwidth comma
        "\u3001",  # Ideographic comma
        "\uff0e",  # Fullwidth full stop
        "\u3002",  # Ideographic full stop
        "",
    ],
    # Existing args
    )
    
SemanticChunker

SemanticChunker 是 LangChain 中用于基于语义相似性分割文本的组件。它通过将文本首先分割成句子,然后将这些句子分组,最后合并那些在嵌入空间中语义上相似的句子。以下是 SemanticChunker 的一些基本参数和类方法的概述:

基本参数:

  1. embeddings: 用于生成文本嵌入的嵌入模型。

  2. buffer_size: 缓冲区大小,用于处理文本时的批次大小。

  3. add_start_index: 是否在每个句子的开始添加索引。

  4. breakpoint_threshold_type: 确定句子之间差异阈值的方法,可以是“percentile”(百分位)、“standard_deviation”(标准差)或“interquartile”(四分位)。

  5. breakpoint_threshold_amount: 阈值量,与 breakpoint_threshold_type 一起使用。

  6. number_of_chunks: 分割成的块的数量。

  7. sentence_split_regex: 用于分割文本为句子的正则表达式。

类方法:
SemanticChunker 提供了以下方法:

  • __init__: 构造函数,用于初始化 SemanticChunker 实例。

  • atransform_documents: 异步转换文档列表。

  • create_documents: 从文本列表创建文档。

  • split_documents: 分割文档。

  • split_text: 分割文本。

  • transform_documents: 通过分割它们来转换文档序列。

    from langchain_experimental.text_splitter import SemanticChunker
    from langchain_openai.embeddings import OpenAIEmbeddings
    
    # 初始化 SemanticChunker 实例
    text_splitter = SemanticChunker(
        OpenAIEmbeddings(),
        breakpoint_threshold_type="percentile"
    )
    
    # 使用文本分割器处理文本
    docs = text_splitter.create_documents(["Your text here"])
    print(docs[0].page_content)
    

SemanticChunker 特别适用于需要基于文本的语义内容进行分割的场景,例如在构建检索系统或处理长文本时保持语义连贯性。通过调整不同的阈值类型和阈值量,可以控制文本块的划分方式,以达到预期的语义分割效果。

from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai.embeddings import OpenAIEmbeddings

text_splitter = SemanticChunker(OpenAIEmbeddings())
text_splitter1 = SemanticChunker(
    OpenAIEmbeddings(), breakpoint_threshold_type="percentile"
)
text_splitter2 = SemanticChunker(
    OpenAIEmbeddings(), breakpoint_threshold_type="standard_deviation"
)
text_splitter3 = SemanticChunker(
    OpenAIEmbeddings(), breakpoint_threshold_type="interquartile"
)

with open("./data/state_of_the_union.txt") as f:
    state_of_the_union = f.read()
len(state_of_the_union)
docs = text_splitter.create_documents([state_of_the_union])

docs_1 = text_splitter1.create_documents([state_of_the_union])
docs_2 = text_splitter2.create_documents([state_of_the_union])
docs_3 = text_splitter3.create_documents([state_of_the_union])

print(len(docs[0].page_content))

print(len(docs_1[0].page_content))
print(len(docs_2[0].page_content))
print(len(docs_3[0].page_content))

Spilt by tokens
  • tiktoken: a fast BPE(Byte Pair Encoding) tokenizer created by OpenAI.

    %pip install --upgrade --quiet langchain-text-splitters tiktoken
    
    text_splitter = CharacterTextSplitter.from_tiktoken_encoder(
        encoding="cl100k_base", 
        chunk_size=100, 
        chunk_overlap=0
    )
    texts = text_splitter.split_text(state_of_the_union)
    
  • SentenceTransformersTokenTextSplitter

  • Hugging Face Tokenizer

    from transformers import GPT2TokenizerFast
    tokenizer = GPT2TokenizerFast.from_pretrained("gpt2")
    
    text_splitter = CharacterTextSplitter.from_huggingface_tokenizer(
        tokenizer, 
        chunk_size=100, 
        chunk_overlap=0
        )
    texts = text_splitter.split_text(state_of_the_union)
    
    
Chunkviz utility : 一个用于评估评估效果的工具
5.1.3.Store

Embeddings: Wrapper around a text embedding model, used for converting text to embeddings.OpenAI,Cohere

  • Docs: Detailed documentation on how to use embeddings.

  • Integrations: 30+ integrations to choose from.

  • Interface: API reference for the base interface.

  • Cache:储存或临时缓存嵌入以避免需要重新计算它们

VectorStore: Wrapper around a vector database, used for storing and querying embeddings.chroma,FAISS,Lance

  • Docs: Detailed documentation on how to use vector stores.

  • Integrations: 40+ integrations to choose from.

  • Interface: API reference for the base interface.

from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
# Chroma 是一个人工智能原生的开源向量数据库,专注于提高开发者的生产力和幸福感。
# 它被设计用来与嵌入模型一起使用,非常适合用于构建基于大型语言模型(LLMs)的应用程序
vectorstore = Chroma.from_documents(documents=all_splits, embedding=OpenAIEmbeddings())
向量数据库的不同检索方法
  • Similarity search

    # 直接用文本输入
    query = "What did the president say about Ketanji Brown Jackson"
    docs = vectorstore.similarity_search(query)
    
    # 先转入到嵌入空间后再检索
    embedding_vector = OpenAIEmbeddings().embed_query(query)
    docs = vectorstore.similarity_search_by_vector(embedding_vector)
    
  • Maximum marginal relevance search (MMR)

    query = "What did the president say about Ketanji Brown Jackson"
    docs = vectorstore.max_marginal_relevance_search(query)
    
Cache for embedding
from langchain.embeddings import CacheBackedEmbeddings
from langchain_openai import OpenAIEmbeddings
from langchain.storage import LocalFileStore

store = LocalFileStore("./cache/")

underlying_embeddings = OpenAIEmbeddings()

cached_embedder = CacheBackedEmbeddings.from_bytes_store(
    underlying_embeddings, store, namespace=underlying_embeddings.model
)
list(store.yield_keys())
embeddings = cached_embedder.embed_documents(
    [
        "Hi there!",
        "Oh, hello!",
        "What's your name?",
        "My friends call me World",
        "Hello World!"
    ]
)
list(store.yield_keys())
# 可以看到下边再次运行的时候都不用时间了,因为存在./cache中,直接根据索引拿结果就行了
embeddings = cached_embedder.embed_documents(
    [
        "Hi there!",
        "Oh, hello!",
        "What's your name?",
        "My friends call me World",
        "Hello World!"
    ]
)

5.2.Retrieval

A retriever is an interface that returns documents given an unstructured query. It is more general than a vector store. A retriever does not need to be able to store documents, only to return (or retrieve) them. Vector stores can be used as the backbone of a retriever, but there are other types of retrievers as well.

Retrievers accept a string query as input and return a list of Document’s as output.

Retriever: An object that returns Documents given a text query

  • Docs: Further documentation on the interface and built-in retrieval techniques. Some of which include:

  • Integrations: Integrations with retrieval services.

  • Interface: API reference for the base interface.

5.2.1.Retriever的分类
  • Name: 检索算法的名称。

  • Index Type: 该算法依赖的索引类型(如果有的话)。

  • Uses an LLM: 此检索方法是否使用大型语言模型(LLM)。

  • When to Use: 我们关于何时考虑使用这种检索方法的评论。

  • Description: 描述这种检索算法的工作原理。

NameIndex TypeUses an LLMWhen to UseDescription
VectorstoreVectorstoreNo如果你刚开始并且寻找快速且简单的东西。这是最简单的方法,也是最容易上手的。它涉及为每段文本创建嵌入。
ParentDocumentVectorstore + Document StoreNo如果你的页面有很多小的独立信息块,最好单独索引,但一起检索。这涉及为每个文档索引多个块。然后你找到嵌入空间中相似度最高的块,但你检索整个父文档并返回它(而不是单独的块)。
Multi VectorVectorstore + Document StoreSometimes during indexing如果你能从文档中提取出比文本本身更相关的信息来索引。这涉及为每个文档创建多个向量。每个向量可以以多种方式创建 - 例如包括文本的摘要和假设性问题。
Self QueryVectorstoreYes如果用户提出的问题更适合通过基于元数据检索文档而不是基于文本相似性来解答。使用大型语言模型将用户输入转换为两件事:(1) 语义查找的字符串,(2) 伴随的元数据过滤器。这很有用,因为通常问题涉及文档的元数据(而不是内容本身)。
Contextual CompressionAnySometimes如果你发现检索到的文档包含太多不相关的信息,并且会分散LLM的注意力。这是在另一个检索器之上的后处理步骤,只从检索到的文档中提取最相关的信息。这可以用嵌入或LLM完成。
Time-Weighted VectorstoreVectorstoreNo如果你有与文档相关的时间戳,并希望检索最新的文档。这基于语义相似性(如常规向量检索)和时效性(查看索引文档的时间戳)的组合来获取文档。
Multi-Query RetrieverAnyYes如果用户提出的问题复杂,需要多块独立信息才能恰当回答。使用LLM从原始查询生成多个查询。当原始查询需要关于多个主题的信息片段才能正确回答时,这很有用。通过生成多个查询,我们就可以为它们各自检索文档。
EnsembleAnyNo如果你有多种检索方法,并希望尝试将它们结合起来。从多个检索器获取文档,然后将它们组合起来。
Long-Context ReorderAnyNo如果你正在使用长上下文模型,并注意到它没有关注检索文档中间的信息。这从底层检索器获取文档,然后重新排序,使最相似的文档靠近开头和结尾。这对于长上下文模型很有用,因为有时它们不会关注上下文窗口中间的信息。
  • ParentDocument 分成父子文档,匹配时是匹配子文档,返回父文档,使得信息更加完整

  • Self Query 会通过一段fewshot示例来引导大模型把问题转换成结构化的数据,这一段的prompt也是会很废token的

  • Multi Vector 会为每个文档生成多个向量,这些向量可以是文档的摘要、假设性问题等,这样可以提高检索的准确性,也是会到大模型

  • Contextual Compression 这里需要一个基础的检索器还有一个压缩器,压缩器也会是一个LLM是

5.2.2.as_retriever

as_retriever 是 Chroma 类的一个方法,它初始化并返回一个 VectorStoreRetriever 对象,该对象可以用于检索操作。

以下是一些 as_retriever 方法的常用参数:

  1. search_type (str, optional): 定义检索器执行的搜索类型。可以是 “similarity”(默认)、“mmr”(最大边际相关性)或 “similarity_score_threshold”。

  2. search_kwargs (dict, optional): 传递给搜索函数的关键字参数。可以包含:

    • k (int): 返回的文档数量,默认为 4。
    • score_threshold (float): 用于 “similarity_score_threshold” 搜索类型的最小相关性阈值。
    • fetch_k (int): 传递给 MMR 算法的文档数量,默认为 20。
    • lambda_mult (float): MMR 算法返回结果的多样性,1 表示最小多样性,0 表示最大多样性,默认为 0.5。
    • filter (dict): 根据文档元数据进行过滤的条件。
  3. **kwargs (Any, optional): 其他传递给检索器的关键字参数。

retriever = vectorstore.as_retriever(search_type="similarity", search_kwargs={"k": 6})
retrieved_docs = retriever.invoke("What are the approaches to Task Decomposition?")
print(len(retrieved_docs))
print(retrieved_docs[0].page_content)

5.2.Generate

Choosing a model:

ChatModel: An LLM-backed chat model. Takes in a sequence of messages and returns a message.

LLM: A text-in-text-out LLM. Takes in a string and returns a string.

See a guide on RAG with locally-running models here.

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-3.5-turbo-0125")
from langchain_core.prompts import PromptTemplate

template = """Use the following pieces of context to answer the question at the end.
If you don't know the answer, just say that you don't know, don't try to make up an answer.
Use three sentences maximum and keep the answer as concise as possible.
Always say "thanks for asking!" at the end of the answer.

{context}

Question: {question}

Helpful Answer:"""

custom_rag_prompt = PromptTemplate.from_template(template)
def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough

rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | custom_rag_prompt
    | llm
    | StrOutputParser()
)
rag_chain.invoke("What is Task Decomposition?")

6.Chain Class

类似于 huggingface😊pipelineChain 类是LangChain的核心组件之一,它允许用户将多个模型和组件链接在一起。

7.Agent

from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType
from langchain.llms import OpenAI
llm = OpenAI(temperature=0)

-----------------------未完待续-----------------------

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值