【大语言模型】LangChain 核心模块
- 一、LangChain 核心模块 Chains
- 1、简介
- 2、应用场景
- 3、使用技巧
- 3.1、LCEL Chains
- 3.2、Legacy Chains
- 4、实践演练
- 二、LangChain 核心模块 Retrieval
- 1、简介
- 2、应用场景
- 2.1、需求说明
- 2.2、实现思路
- 三、LangChain 核心组件 Tools
- 1、应用场景
- 2、使用原理
- 3、Tools 需要包含的信息
- 4、实践应用
- 4.1、Tools 的常见类型
- 4.2、默认工具
- 4.3、自定义工具
- 4.3.1、@TOOL 装饰器使用说明
- 4.3.2、工具的异常处理
- 4.3.3、解决的问题:异常忽略
一、LangChain 核心模块 Chains
1、简介
Chains 是指调用的序列,这个序列包含 LLM、工具、数据预处理等多个不同类型的步骤。Chains 的主要实现方式有两种:
- 1、使用LangChain LCEL 表达式语言,构建 Chains。
- 2、通过继承旧版 Chain 类的子类而构造的链(不推荐)。
其中主要推荐大家使用 LCEL 进行 Chains 的使用。原因是因为:
- 1、使用简单。
- 2、支持多种方式,包含 streaming(流),async(异步),而且开箱即用。
- 3、非常易于观察每个实现步骤。
2、应用场景
Chains 的应用场景非常广,简直就是 LangChain 最核心的能力,基本上所有的 Retrieval 或则是 RAG 甚至是 ReaAct 都需要结合 Chains 去做实现。
3、使用技巧
3.1、LCEL Chains
官方文档对于不同的 Chain 的使用有非常清晰的表述,而官网还在不断增加不同类型的 Chain。每个 Chain 包含这些信息:
- Chain 构造器: 主要为这个 Chain 对应的构造函数,以及 api 文档说明,这些构造函数的返回值均为 LCEL 可运行对象。
- Function Calling:是否支持 OpenAI 的 function calling 功能。function calling 的介绍
- 其他工具:在这个 Chain 中是否有使用其他工具
- 何时使用:这个 Chain 的应用场景。
我们在使用不同的 Chain 的过程中,只需要注意根据不同的应用场景以及参数信息去进行使用即可。
3.2、Legacy Chains
由于官方更建议使用 LCEL Chains 而不是 Legacy Chains ,所以关于 Legacy Chains 不作为必须要学习的内容,如果想要了解相关的具体说明可以参考官方文档。
4、实践演练
在前面的案例实践中,就已经有Chains的使用,主要在:
- LangChain提示词+大语言模型应用
Chains 这块的知识基本在前面的章节都有所应用。在后面的练习中,基本每个模块也都会涉及到Chains的使用,以及LangChain官方提到的其他的构造器的使用,例如LangChain核心模块Retrieval使用到了create_stuff_documents_chain与create_retrieval_chain构造器。
二、LangChain 核心模块 Retrieval
1、简介
许多 LLM 应用程序需要使用用户的数据而这些数据不属于 LLM 的一部分。所以则需要 RAG检索增强生成解决此问题,LangChain 可以很好的提供此能力。
从图中可以得知,LangChain 在实现 RAG 的时候,主要就是通过以下几个步骤完成:
- Document loaders:LangChain 提供 100 多种不同的文档加载能力 L 其中包含多个类型的文档加载:HTML、PDF、代码的集成。
- Text Splitting:检索的关键部分是仅获取文档的相关部分。 这涉及几个转换步骤来准备文档以供检索。这里的主要任务之一是将大文档分割(或分块)为更小的块。
- Text embedding models:嵌入捕获文本的语义,使您能够快速有效地找到文本的其他相似部分。
- Vector stores:向量数据库。
- Retrievers:检索器包含父文档检索器、自查询检索器、集成检索器。
- Indexing:将其他数据来源的数据同步到向量数据库中。
2、应用场景
LangChain 的 Retrieval 就是非常经典的 RAG 的应用场景:
- 知识库检索。
- 问答机器人。
等多种与检索相关的应用场景。
2.1、需求说明
加载网页中的内容,然后针对此网页的内容进行检索。
2.2、实现思路
1、数据导入:从第三方的信息源导入数据信息。
2、数据切分:如果数据比较庞大,则可以对数据进行切分处理。
3、创建向量数据库与 embedding。
4、查询检索操作:
- create_stuff_documents_chain: 获取文档,并将其格式化到提示词中。
- create_retrieval_chain: 检索 chain。
from langchain_community.document_loaders import WebBaseLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
# 数据导入
loader = WebBaseLoader("https://docs.smith.langchain.com/overview")
docs = loader.load()
# 数据切分
text_splitter = RecursiveCharacterTextSplitter()
documents = text_splitter.split_documents(docs)
# 创建embedding
embeddings = OpenAIEmbeddings()
# 通过向量数据库存储
vector = FAISS.from_documents(documents, embeddings)
# 查询检索
# 创建 prompt
prompt = ChatPromptTemplate.from_template("""Answer the following question based only on the provided context:
<context>
{context}
</context>
Question: {input}""")
# 创建模型
llm = ChatOpenAI()
# 创建 document 的chain, 查询
document_chain = create_stuff_documents_chain(llm, prompt)
from langchain_core.documents import Document
document_chain.invoke({"input": "how can langsmith help with testing?",
"context":[Document(page_content="langsmith can let you visualize test results")]})
from langchain.chains import create_retrieval_chain
# # 创建搜索chain 返回值为 VectorStoreRetriever
retriever = vector.as_retriever()
retrieval_chain = create_retrieval_chain(retriever, document_chain)
# # 执行请求
response = retrieval_chain.invoke({"input": "how can langsmith help with testing?"})
print(response["answer"])
三、LangChain 核心组件 Tools
1、应用场景
在没有 Tools 之前,无论是 Agents、Chains 或 LLM 都只具备生成一些数据或者信息,但是不具备任何的操作能力。比如让大模型直接生成某个文件,让大模型执行某些代码等操作,均无法实现。
而 Tools 完美的填补了这一片空白,如果把 LLM 比喻做大脑,Agents Chains 是中枢神经,那么 Tools 就是一个人的手和脚。
所以 Tools 的学习,是为了后面 Agent 的使用而打基础。
2、使用原理
大语言模型根据当下应用场景进行判断,是否应该使用此工具。所以工具的描述至关重要,如果工具描述不清晰,则可能会导致大语言模型无法很好的将这些工具应用在适用的场景之上。
3、Tools 需要包含的信息
作用 | 对应字段名 |
---|---|
工具名称。 | name |
该工具是什么的描述。 | description |
工具输入内容的 JSON 架构。 | args_schema |
工具的结果是否应直接返回给用户。 | return_direct |
要调用的函数。 | 无 |
4、实践应用
4.1、Tools 的常见类型
- Built-In Tools(默认工具)
- Custom Tools(自定义工具)
- Toolkits(工具包)
- Tools as OpenAI Functions(工具作为 OpenAI 函数)
4.2、默认工具
- 使用内置的维基百科工具进行搜索操作
from langchain_community.tools import WikipediaQueryRun
from langchain_community.utilities import WikipediaAPIWrapper
api_wrapper = WikipediaAPIWrapper(top_k_results=1, doc_content_chars_max=100)
tool = WikipediaQueryRun(api_wrapper=api_wrapper)
# 工具的名称
# tool.name
# 描述
# tool.description
# 输入参数
# tool.args
# 我们可以看到该工具是否应该直接返回给用户
# tool.return_direct
print(tool.run({"query": "langchain"}))
4.3、自定义工具
定制工具主要的使用方式有 3 种:
工具类型 | 应用场景 |
---|---|
@tool 装饰器 | @tool 装饰器是定义自定义工具的最简单方法 |
继承 BaseTool | 使用更为复杂,定制性更强 |
StructuredTool | 前两种方法的结合 |
4.3.1、@TOOL 装饰器使用说明
tools 的定义:
@tool
def search(query: str) -> str:
"""Look up things online."""
return "LangChain"
print(search.name)
print(search.description)
print(search.args)
通过打印 search 函数的属性,可以了解到:
- 默认函数名为工具的 name。
- 默认函数的注释为工具的 description。
- 默认函数的参数为工具的 args。
如果想修改默认的函数的属性,可以通过如下的方法进行修改:
from langchain_core.tools import tool, ToolException, StructuredTool
from pydantic import BaseModel, Field
class SearchInput(BaseModel):
# 定义参数的描述
# 参数名 = Field(description="参数的描述")
query: str = Field(description="should be a search query")
# return_direct=True 是否直接从工具返回而不是继续Agent的循环。若设为True,则会中断Agent的执行
@tool("search-tool", args_schema=SearchInput, return_direct=True)
def search(query: str) -> str:
"""Look up things online."""
return "LangChain"
print(search.name)
print(search.description)
print(search.args)
print(search.return_direct)
search-tool
Look up things online.
{'query': {'description': 'should be a search query', 'title': 'Query', 'type': 'string'}}
True
return_direct=True:表示是否直接从工具返回而不是继续Agent的循环。若设为True,则会中断Agent的执行。
4.3.2、工具的异常处理
以下这段代码定义了一个 tools 的构造器,在代码中,我们添加了一个抛出异常的操作:
def search_tool1(s: str):
raise ToolException("The search tool1 is not available.")
# 效果等同于给 search_tool1 加装饰器
search = StructuredTool.from_function(
func=search_tool1,
name="Search_tool1",
description="A bad tool",
)
print(search.run("test"))
raise ToolException("The search tool1 is not available.")
langchain_core.tools.base.ToolException: The search tool1 is not available.
4.3.3、解决的问题:异常忽略
如果没有加异常处理,那么工具的使用效果为,直接抛出异常。如果在工具构造的时候给它添加了异常处理:
def search_tool1(s: str):
raise ToolException("The search tool1 is not available.")
# 效果等同于给 search_tool1 加装饰器
search = StructuredTool.from_function(
func=search_tool1,
name="Search_tool1",
description="A bad tool",
# 捕获异常错误
handle_tool_error=True,
)
print(search.run("test"))
那么执行之后,则不会抛出异常信息。