五分钟带你学习LangChain 框架

LangChain是一个强大的框架,旨在帮助开发人员使用语言模型构建端到端的应用程序或者是各类Agent。它提供了一套工具、组件和接口,可简化创建应用程序的过程。LangChain 可以轻松管理与LLM的交互,将多个组件链接在一起,并集成额外的资源,以下LangChain 框架提供的几个主要模块,这些模块按照逐渐增加的复杂性排列如下:

  • 模型(models) : LangChain 支持的各种模型类型和模型集成。
  • 提示(prompts) : 包括提示管理、提示优化和提示序列化。
  • 内存(memory) : 内存是在链/代理调用之间保持状态的概念。LangChain 提供了一个标准的内存接口、一组内存实现及使用内存的链/代理示例。
  • 索引(indexes) : 为了使LLM与文档更好地进行交互而对其进行结构化的方式。在链中,索引最常用于“检索”步骤中,该步骤指的是根据用户的查询返回最相关的文档,并将文档返回给大模型进行交互。
  • 链(chains): 链不仅仅是单个 LLM 调用,还包括一系列调用(无论是调用 LLM 还是不同的实用工具)。LangChain 提供了一种标准的链接口、许多与其他工具的集成。LangChain 提供了用于常见应用程序的端到端的链调用。
  • 代理(agents) : 代理通 过调用LLM的能力做出行动决策、执行该行动、查看一个观察结果,并重复该过程直到完成。LangChain 提供了一个标准的代理接口,一系列可供选择的代理,以及端到端代理的示例。
    下面逐一介绍各个模块,并提供一些相应的简化版的代码示例。

模型Models

LangChain支持三种大类模型:

  • LLMs
    大型语言模型(LLMs),将文本字符串作为输入,并返回文本字符串作为输出。

  • 聊天模型
    聊天模型通常由语言模型支持,但它们的API更加结构化。 具体来说,这些模型将聊天消息列表作为输入,并返回聊天消息。

  • 文本嵌入模型
    文本嵌入模型将文本作为输入,并返回一个浮点数列表(向量组)。

LLM模型的调用示例

llm = OpenAI(model_name="text-ada-001", n=2, best_of=2)
**生成文本:**LLM最基本的功能就是能够调用它,传入一个字符串并返回一个字符串。
llm("Tell me a joke")
##大模型返回文本
'  Why did the chicken cross the road?  To get to the other side.'
##类似的还可以传入数组,得到llm_result.generations[]的各种信息

chat模型的调用示例

chat = ChatOpenAI(temperature=0)
chat([HumanMessage(content="Translate this sentence from English to French. I love programming.")])
##通过向聊天模型传递一个或多个消息,您可以获得聊天完成。响应将是一条消息LangChain目前支持的消息类型AIMessage、HumanMessage、SystemMessage和ChatMessage - ChatMessage接受任意角色参数。
AIMessage(content="J'aime programmer.", additional_kwargs={})

文本嵌入模型 text-embedding-model调用示例

Embedding类是一个用于与嵌入进行交互的类。有许多嵌入提供商(OpenAI、Cohere、Hugging Face等)- 这个类旨在为所有这些提供商提供一个标准接口。
嵌入会创建文本的向量表示。这使我们可以在向量空间中执行诸如语义搜索之类的操作,寻找最相似的文本片段。

LangChain中的基本Embedding类公开了两种方法:embed_documents和embed_query。最大的区别在于这两种方法具有不同的接口:一个适用于多个文档,而另一个适用于单个文档。

提示工程Prompts

Prompt是一种向语言模型提供特定的输入或指令,以此来引导模型生成用户期望的特定输出的提示词。 通常这个提示词不仅仅是一个硬编码的字符串,而是一个模板、一些例子和用户输入的组合。
提示词模板可能包含:

  • 对语言模型的指导,

  • 一组少量示例,以帮助语言模型生成更好的响应,

  • 对语言模型的提问。

LangChain提供了几个相关的提示模板,以便轻松构建和处理提示。包括ChatPromptTemplate,PromptTemplate,SystemMessagePromptTemplate,AIMessagePromptTemplate,HumanMessagePromptTemplate等。
官方文档提供的示例可以使用from_template方法直接将自定义template文本作为参数建立提示词模板, 也可以将PromptTemplate实例化后作为参数传递给MessagePromptTemplates。

template="You are a helpful assistant that translates {input_language} to {output_language}."
system_message_prompt = SystemMessagePromptTemplate.from_template(template)
human_template="{text}"
human_message_prompt = HumanMessagePromptTemplate.from_template(human_template)
 
prompt=PromptTemplate(
    template="You are a helpful assistant that translates {input_language} to {output_language}.",
    input_variables=["input_language", "output_language"],
)
system_message_prompt_2 = SystemMessagePromptTemplate(prompt=prompt)
 
assert system_message_prompt == system_message_prompt_2
 chat_prompt = ChatPromptTemplate.from_messages([system_message_prompt, human_message_prompt])
 
##get a chat completion from the formatted messages
chat_prompt.format_prompt(input_language="English", output_language="French", text="I love programming.").to_messages()

few shot examples 是一组示例,可用于帮助语言模型生成更好的响应。要使用 few shot examples 生成提示,可以使用 FewShotPromptTemplate。这个类接受一个 PromptTemplate 和一个 few shot examples 列表。然后,它将用 few shot examples 格式化提示模板。
如果在构建prompt过程中,如果有很多example示例,可以使用ExampleSelector类进行select_examples,该方法接受输入变量,然后返回一个示例列表。该方法根据示例example与输入的相似度等选择示例。它通过查找嵌入与输入的余弦相似度最大的示例来实现此目的。使用方法示例如下:

example_selector = SemanticSimilarityExampleSelector.from_examples(
    # 该变量是一个示例的数组
    examples, 
    # This is the embedding class used to produce embeddings which are used to measure semantic similarity.
    OpenAIEmbeddings(), 
    # This is the VectorStore class that is used to store the embeddings and do a similarity search over.
    Chroma, 
    # This is the number of examples to produce.
    k=1
)
similar_prompt = FewShotPromptTemplate(
    # We provide an ExampleSelector instead of examples.
    example_selector=example_selector,
    example_prompt=example_prompt,
    prefix="Give the antonym of every input",
    suffix="Input: {adjective}\nOutput:", 
    input_variables=["adjective"],
)

输出解析器也是提示工程中的重要一环,一般来说LLM语言模型输出文本。但是很多时候,需要获得比文本更结构化的信息。这就是输出解析器的作用。
输出解析器是帮助结构化语言模型响应的类。如逗号分隔列表:

['Vanilla',
 'Chocolate',
 'Strawberry',
 'Mint Chocolate Chip',
 'Cookies and Cream']

或者定义好我们想要接受的响应格式,并将模式作为参数传递给prompt模板,最终得到的大模型响应结果就可以按照预先定义的格式来,例如:

response_schemas = [
    ResponseSchema(name="answer", description="answer to the user's question"),
    ResponseSchema(name="source", description="source used to answer the user's question, should be a website.")
]
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)
**进行调用后**
 output_parser.parse(output.content)
 **结果**
 {'answer': 'Paris', 'source': 'https://en.wikipedia.org/wiki/Paris'}

序列化提示模板,可以将PromptTemplate保存到本地文件系统中。 langchain会自动通过文件扩展名推断文件格式。当前,langchain支持将模板保存为YAML和JSON文件。

内存 Memory

底层的LLMs和聊天模型是无状态的,因此每个请求都是独立地处理。但在某些应用程序中(如聊天机器人),记住以前的交互非常重要,无论是在短期还是长期层面上。因此需要一定的memory来存储过程中的交互数据。
Memory中可以保存最近n个对话和以前对话的消息摘要。
缓存窗口内存:ConversationBufferWindowMemory函数,保留了对话随时间推移的交互列表。它仅使用最后K个交互。这可以用于保持最近交互的滑动窗口,以便缓冲区不会过大。K值作为参数可以自定义。
实体摘要内存/对话摘要内存:ConversationEntityMemory,通过该函数来总结并记住特定实体的信息。它使用LLMs提取实体的信息,并移逐渐建立对实体的了解(通过LLMs的能力)。ConversationSummaryMemory 这种记忆类型可以创建关于对话的摘要,有助于从对话中概括信息。概括能力仍然需要llm。
对话摘要缓存内存:ConversationSummaryBufferMemory结合了前两个想法。它将最近的交互记录缓存在内存中,但不仅仅是完全清除旧的交互,而是将它们编译成一份摘要并同时使用。不过,与之前的实现不同,它使用token长度而不是交互数量K来确定何时清除交互。
对话缓冲内存:ConversationTokenBufferMemory 会在内存中保留最近的对话内容,并使用token长度而不是对话数量来决定何时刷新对话。可以将历史记录作为消息列表。
基于向量存储的记忆:VectorStoreRetrieverMemory将记忆存储在VectorDB中,并在每次调用时查询最重要的K个文档。
与大多数其他记忆类不同的是,它不明确跟踪交互的顺序。
在这种情况下,“文档”是先前的对话片段。这可以用来提到AI在对话中早期被告知的相关信息。

索引 Indexes:

主要关注于构建索引,目标是使用它们作为检索器。我们主要关注的检索器类型是 Vectorstore 检索器。它提供一系列能力讲文件等非结构化数据存储起来并提供索引以供检索,更好的与大模型交互。
通过文件回答问题包括四个步骤:
1.创建索引
2.从该索引创建检索器
3.创建一个问题回答链
4.问问题!
首先可以使用VectorstoreIndexCreator创建索引,然后用它来询问数据问题,示例代码如下:

from langchain.indexes import VectorstoreIndexCreator
index = VectorstoreIndexCreator().from_loaders([loader])
query = "What did the president say about Ketanji Brown Jackson"
index.query(query)
#下述方法可以返回查询的数据来源sources地址
index.query_with_sources(query)

其中这个 VectorstoreIndexCreator 背后的主要功能如下:
加载文件后有三个主要步骤:
1.将文档分割成块
2.为每个文档创建嵌入
3.在向量库中存储文档和嵌入
VectorstoreIndexCreator 只是所有这些逻辑的包装器。它可以在它使用的文本分割器、它使用的嵌入以及它使用的向量存储中进行配置。配置方法如下:

index_creator = VectorstoreIndexCreator(
    vectorstore_cls=Chroma, 
    embedding=OpenAIEmbeddings(),
    text_splitter=CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
)
与向量存储相关的高级功能如下:
docsearch = Chroma.from_texts(texts, embeddings)
 
query = "What did the president say about Ketanji Brown Jackson"
#搜索
docs = docsearch.similarity_search(query)

#添加文本
docsearch.add_texts(["Ankush went to Princeton"])
 #从文档初始化向量存储。使用文本拆分器方法直接获取文档
documents = text_splitter.create_documents([state_of_the_union], metadatas=[{"source": "State of the Union"}])

向量存储完成后,需要使用检索器来进行下一步查询。LangChain最支持的索引,因此也是最支持的检索器是VectorStoreRetriever。正如其名称所示,此检索器主要由VectorStore支持。

#从向量数据库db中初始化一个检索器
retriever = db.as_retriever()
#默认情况下,vectorstore检索器使用相似性搜索。如果底层的vectorstore支持最大边际相关性搜索,则可以指定该搜索类型。
retriever = db.as_retriever(search_type="mmr")
docs = retriever.get_relevant_documents("what did he say about ketanji brown jackson")

链 Chains

使用单独的LLM对于一些简单的应用程序来说是可以的,但许多更复杂的应用程序需要链接LLM——无论是相互链接还是与其他专家链接。LangChain为链提供了标准接口,以及一些常见的链实现,以便于使用。链允许我们将多个组件组合在一起,创建一个单一的、一致的应用程序。例如,我们可以创建一个链,该链接接受用户输入,使用 PromptTemplate 对其进行格式化,然后将格式化后的响应传递给 LLM。我们可以通过将多个链组合在一起,或者通过将链与其他组件组合在一起,来构建更复杂的链。
LLMChain 是一个简单的链,它接受一个大模型+一个提示模板(可以是自定义的PromptTemplate),也可以使用chatllm+ChatPromptTemplate;然后将信息发个大模型并得到返回。
调用Chain的方式
可以将chain作为一个function集成在Tool中,从而构建代理。代码示例如下:

tools.append(
    Tool(        
        name="Calculator",        
        func=llm_math_chain.run,
        description="在需要回答数学问题时非常有用",
        args_schema=CalculatorInput
         )
 )

向链中添加内存
Chain 支持将 BaseMemory 对象作为其内存参数,允许 Chain 对象跨多个调用持久存储数据。换句话说,它使 Chain 成为一个有状态对象。
调试链
因为大多数 Chain 对象都涉及大量的输入提示预处理和 LLM 输出后处理,所以很难单独从它的输出调试 Chain 对象。如果详细设置为 True,将在 Chain 对象运行时打印出它的一些内部状态。
组合Chain:
我们可以组合这两个 LLMChains,这样我们就可以在一个步骤中创建完成两个不同的任务,示例代码如下:

from langchain.chains import SimpleSequentialChain
overall_chain = SimpleSequentialChain(chains=[chain, chain_two], verbose=True)

#Run the chain specifying only the input variable for the first chain.
catchphrase = overall_chain.run("colorful socks")
print(catchphrase)

问答与来源
如果我们想通过chain对一系列文档进行带来源的问答,首先,我们需要准备数据。在此示例中,我们在向量数据库上进行相似性搜索,但这些文档可以以任何方式获取(本教程的重点是强调在获取文档后要做什么)。可以使用如下代码思路:

#准备问题
query = "What did the president say about Justice Breyer"
#准备文档数据源&向量化存储
docsearch = Chroma.from_texts(texts, embeddings, metadatas=[{"source": str(i)} for i in range(len(texts))])
 
docs = docsearch.similarity_search(query)

#使用链
chain = load_qa_with_sources_chain(OpenAI(temperature=0), chain_type="stuff")

chain({"input_documents": docs, "question": query}, return_only_outputs=True)

进一步的,可以使用向量数据进行索引对问题进行基于来源的问答。它通过使用RetrievalQAWithSourcesChain来完成从索引中查找文档的工作。代码如下:

docsearch = Chroma.from_texts(texts, embeddings, metadatas=[{"source": f"{i}-pl"} for i in range(len(texts))])

from langchain import OpenAI
 
chain = RetrievalQAWithSourcesChain.from_chain_type(OpenAI(temperature=0), chain_type="stuff", retriever=docsearch.as_retriever())

chain({"question": "What did the president say about Justice Breyer"}, return_only_outputs=True)

代理 Agent:

Agent也可以称为“智能体”或“智能业务助理”,在大模型技术驱动下,它通过"大模型 + 插件 + 执行流程 / 思维链"的方式,对应控制端 (Brain / 大脑)、感知端 (Preception)、执行端 (Action) 环节,实现了复杂的功能。
一个代理 (Agent)至少 由三个部分组成:

  • LLM:作为Agent的大脑,用于核心控制器,例如规划,反馈,学习等。
  • 工具 tool:代理可以使用的工具。工具是代理可以用来与世界互动的功能。这些工具可以是通用工具(例如搜索),其他链,甚至是其他代理。
  • 代理执行器 :这决定了采取哪些行动。
    详细如何创建代理在上一篇文章《以AutoGPT为例浅谈智能体Agent》讲过,可以参考。

以上使LangChain框架中的6大组件,本文摘抄、总结了官方文档中的使用思路,希望能进一步加深大家对LangChain构建智能体应用或其他大模型应用提供思路和方法。

  • 44
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值