LangChain表达式 基础和实践(LCEL)
LCEL简介
LangChain表达式语言,或者LCEL,是一种声明式的方式,可以轻松地将链条组合在一起。 LCEL从第一天开始就被设计为支持将原型放入生产中,不需要改变任何代码,从最简单的“提示+LLM”链到最复杂的链(我们已经看到人们成功地在生产中运行了包含数百步的LCEL链)。
LCEL优点
流式支持 当你用LCEL构建你的链时,你可以得到最佳的首次到令牌的时间(输出的第一块内容出来之前的时间)。对于一些链,这意味着例如我们直接从LLM流式传输令牌到一个流式输出解析器,你可以以与LLM提供者输出原始令牌相同的速率得到解析后的、增量的输出块。
异步支持 任何用LCEL构建的链都可以通过同步API(例如在你的Jupyter笔记本中进行原型设计时)以及异步API(例如在LangServe服务器中)进行调用。这使得可以使用相同的代码进行原型设计和生产,具有很好的性能,并且能够在同一台服务器中处理许多并发请求。
优化的并行执行 无论何时,你的LCEL链有可以并行执行的步骤(例如,如果你从多个检索器中获取文档),我们都会自动执行,无论是在同步接口还是异步接口中,以获得最小可能的延迟。
重试和回退 为你的LCEL链的任何部分配置重试和回退。这是一种使你的链在大规模下更可靠的好方法。我们目前正在努力为重试/回退添加流式支持,这样你就可以在没有任何延迟成本的情况下获得增加的可靠性。
访问中间结果 对于更复杂的链,通常在最终输出产生之前就能访问中间步骤的结果是非常有用的。这可以用来让最终用户知道正在发生什么,甚至只是用来调试你的链。你可以流式传输中间结果,它在每个LangServe服务器上都可用。
输入和输出模式 输入和输出模式为每个LCEL链提供了从你的链的结构中推断出来的Pydantic和JSONSchema模式。这可以用于验证输入和输出,是LangServe的一个重要部分。
LCEL使得从基本组件构建复杂链条变得容易,并且支持诸如流式处理、并行处理和日志记录等开箱即用的功能。
LECL基本示例
最基本和常见的用例是将提示模板和模型链接在一起。为了了解这是如何工作的,让我们创建一个链条,它接受一个主题并生成一个笑话。首先我们需要安装相关的pip包:
pip install --upgrade --quiet langchain-core langchain-community langchain-openai
然后执行代码:
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
prompt = ChatPromptTemplate.from_template("tell me a short joke about {topic}")
model = ChatOpenAI(model="gpt-4")
output_parser = StrOutputParser()
chain = prompt | model | output_parser
chain.invoke({"topic": "ice cream"})
输出:
"为什么冰淇淋从不被邀请参加派对?\n\n因为当事情变热时,它们总是滴下来!"
请注意这行代码,我们使用LCEL将不同的组件组合成一个单一的链条:
chain = prompt | model | output_parser
| 符号类似于 [unix 管道操作符],它将不同的组件链接在一起,将一个组件的输出作为下一个组件的输入。
在这个链条中,用户输入被传递给提示模板,然后提示模板的输出被传递给模型,然后模型的输出被传递给输出解析器。让我们逐个组件地看一下,以真正理解发生了什么。
1. 提示
prompt 是一个 BasePromptTemplate,这意味着它接受一个模板变量的字典并生成一个 PromptValue。PromptValue 是一个包装完成的提示的包装器,可以传递给 LLM(它以字符串作为输入)或 ChatModel(它以消息序列作为输入)。它可以与任何语言模型类型一起使用,因为它定义了生成 BaseMessage 和生成字符串的逻辑。
prompt_value = prompt.invoke({"topic": "ice cream"})
prompt_value
ChatPromptValue(messages=[HumanMessage(content='tell me a short joke about ice cream')])
prompt_value.to_messages()
[HumanMessage(content='tell me a short joke about ice cream')]
prompt_value.to_string()
'Human: tell me a short joke about ice cream'
2. 模型
然后将 PromptValue 传递给 model。在这种情况下,我们的 model 是一个 ChatModel,这意味着它将输出一个 BaseMessage。
message = model.invoke(prompt_value)
message
AIMessage(content="为什么冰淇淋从不被邀请参加派对?\n\n因为当事情变热时,它们总是滴下来!")
如果我们的 model 是一个 LLM,它将输出一个字符串。
from langchain_openai.llms import OpenAI
llm = OpenAI(model="gpt-3.5-turbo-instruct")
llm.invoke(prompt_value)
'\n\nRobot: 冰淇淋车为什么坏了?因为它融化了!'
3. 输出解析器
最后,我们将 model 的输出传递给 output_parser,它是一个 BaseOutputParser,意味着它可以接受字符串或 BaseMessage 作为输入。StrOutputParser 简单地将任何输入转换为字符串。
output_parser.invoke(message)
"冰淇淋为什么去看心理医生?\n\n因为它有太多的配料,找不到自己的冰淇淋锥自信!"
4. 整个流程
按照以下步骤进行操作:
- 我们将用户输入的主题作为
{"topic": "ice cream"}传入。 prompt组件接受用户输入,然后使用主题构建提示,生成PromptValue。model组件接受生成的提示,并将其传递给 OpenAI LLM 模型进行评估。模型生成的输出是一个ChatMessage对象。- 最后,
output_parser组件接受一个ChatMessage,将其转换为 Python 字符串,并从invoke方法返回。
graph LR
A(输入:topic=ice cream) --> |字典| B(PromptTemplate)
B -->|PromptValue| C(ChatModel)
C -->|ChatMessage| D(StrOutputParser)
D --> |字符串| F(结果)