LCEL介绍
LangChain 表达式语言(LCEL)是一种声明式的方法,可以轻松地将多个链条组合在一起。
LCEL 从第一天开始设计就支持将原型投入生产,无需进行代码更改,从最简单的“提示 + LLM”链条到最复杂的链条(我们见过人们在生产中成功运行包含数百个步骤的 LCEL 链条)
- 支持流式
- 支持异步
- 支持并行
- 无缝 LangSmith 跟踪
- 无缝 LangServe 部署
LCEL使用
基本示例:提示(Prompt) + 模型(Model) + 输出解析器(OutputParser)
简单示例
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate # 模型
model = ChatOpenAI(参数省略...)
# 模板
prompt = ChatPromptTemplate.from_template("你是历史专家,请讲一个关于{topic}的历史故事。")
# 输出解析器
output_parser = StrOutputParser()
# 链
chain = prompt | model | output_parser
# 执行
chain.invoke({"topic": "刘彻"})
chain = prompt | model | output_parser:类似于unix管道运算符,其中链将不同的组件组合在一起,从一个组件提供输出作为下一个组件的输入。|
在此链中,用户输入被传递到提示模板,然后提示模板输出传递给模型,然后模型输出为传递给输出解析器。
元素解读
- prompt :是一个BasePromptTemplate,这意味着它接受模板变量的字典并生成PromptValue,是提示词包装
- model :将PromptValue传递给模型。在这种情况下,我们的模型是ChatModel,这意味着它将输出BaseMessage
- output_parser
:最后,我们将模型输出传递给output_parser,它是一个BaseOutputParser,这意味着它接受字符串或BaseMessage作为输入。指定的StrOutputParser只需将任何输入转换为字符串
完整步骤
- 我们将用户的主题以 {“topic”:“刘彻”} 形式输入
- 提示组件(Prompt)接受用户输入,然后在使用主题构造提示后使用该输入构造PromptValue。
- 模型组件(Model)接受生成的提示,并传递到OpenAI LLM模型中进行评估。模型生成的输出是一个ChatMessage对象。
- 最后,output_parser组件接收ChatMessage,并将其转换为从invoke方法返回的Python字符串。
知道具体步骤后,我们可以对其拆分,查看中间过程结果
# 模型
model = ChatOpenAI(参数省略...)
# 模板
prompt = ChatPromptTemplate.from_template("你是历史专家,请讲一个关于{topic}的历史故事。")
input = {"topic": "刘彻"}
# prompt执行invoke方法的输出
prompt.invoke(input)
# 输出
ChatPromptValue(messages=[HumanMessage(content='你是历史专家,请讲一个关于刘彻的历史故事。')]) # prompt+model执行invoke的输出
(prompt | model).invoke(input)
# 输出
AIMessage(content='刘彻在小时候。。。 ', ...其他参数省略)
多链
在上述LCEL表达式的了解过程中,我们了解了链的简单使用,后面我们看一下多链的使用
两条Chain
from operator import itemgetterfrom langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAIprompt1 = ChatPromptTemplate.from_template("{person} 来自于哪个城市?")
prompt2 = ChatPromptTemplate.from_template("城市 {city} 在哪个国家? 用{language} 回答"
)model = ChatOpenAI()chain1 = prompt1 | model | StrOutputParser()chain2 = ({"city": chain1, "language": itemgetter("language")}| prompt2| model| StrOutputParser()
)chain2.invoke({"person": "胡歌", "language": "English"})
多条Chain
from langchain_core.runnables import RunnablePassthroughprompt1 = ChatPromptTemplate.from_template("生成一种 {attribute} 的颜色。只返回颜色的名称:"
)
prompt2 = ChatPromptTemplate.from_template("颜色 {color} 的水果是什么?只返回水果的名称:"
)
prompt3 = ChatPromptTemplate.from_template("国旗的颜色中有颜色 {color} 的国家是哪个?只返回国家的名称:"
)
prompt4 = ChatPromptTemplate.from_template("{fruit} 的颜色和 {country} 的国旗是什么?"
)model_parser = model | StrOutputParser()color_generator = ({"attribute": RunnablePassthrough()} | prompt1 | {"color": model_parser}
)
color_to_fruit = prompt2 | model_parser
color_to_country = prompt3 | model_parser
question_generator = (
color_generator | {"fruit": color_to_fruit, "country": color_to_country} | prompt4
)prompt = question_generator.invoke("温暖的")
model.invoke(prompt)
多链的分支和合并
您可能希望将一个组件的输出结果处理为两个或多个其他组件的输入。RunnableParallels(opens in a new tab) 使您可以将链分割或分叉,以便多个组件可以并行处理输入。稍后,其他组件可以合并结果,以合成最终的响应。这种类型的链创建了一个计算图,如下所示:
输入/ \/ \分支1 分支2\ /\ /合并
planner = (ChatPromptTemplate.from_template("关于 {input} 生成一个论点")| ChatOpenAI()| StrOutputParser()| {"base_response": RunnablePassthrough()}
)arguments_for = (ChatPromptTemplate.from_template("列举 {base_response} 的优点或正面方面")| ChatOpenAI()| StrOutputParser()
)
arguments_against = (ChatPromptTemplate.from_template("列举 {base_response} 的缺点或负面方面")| ChatOpenAI()| StrOutputParser()
)final_responder = (ChatPromptTemplate.from_messages([("ai", "{original_response}"),("human", "优点:\n{results_1}\n\n缺点:\n{results_2}"),("system", "根据批评生成最终响应"),])| ChatOpenAI()| StrOutputParser()
)chain = (planner| {"results_1": arguments_for,"results_2": arguments_against,"original_response": itemgetter("base_response"),}| final_responder
)
chain.invoke({"input": "scrum"})
多链的函数使用
from operator import itemgetterfrom langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableLambdadef length_function(text):print("计算text长度")return len(text)def _multiple_length_function(text1, text2):print("计算text1乘text2长度")return len(text1) * len(text2)def multiple_length_function(_dict):print("计算字典内text1乘text2长度")return _multiple_length_function(_dict["text1"], _dict["text2"])def output_function(_dict):print("输出文字"+str(_dict["a"])+" + "+str(_dict["b"]))return "what is "+str(_dict["a"])+" + "+str(_dict["b"]);
prompt = ChatPromptTemplate.from_template("what is {a} + {b}")chain1 = prompt | modelchain = ({"a": itemgetter("foo") | RunnableLambda(length_function),"b": {"text1": itemgetter("foo"), "text2": itemgetter("bar")}| RunnableLambda(multiple_length_function),}| RunnableLambda(output_function)
)re = chain.invoke({"foo": "bar", "bar": "gah"})print(re)