在LangChain的学习小册中,按照课程要求使用LLMChain会出现弃用提示。后来通过官方文档和网上资料学习到了如何使用Runnable来实现课程中的各种链任务。
LangChainDeprecationWarning: The class `LLMChain` was deprecated in LangChain 0.1.17 and will be removed in 1.0. Use RunnableSequence, e.g., `prompt | llm` instead.
Runnable
LangChain中的Runnable是一个强大的抽象层,它允许开发者以一种统一的方式定义和执行各种类型的任务。以下是Runnable的一些主要优势:
- 统一接口:
Runnable提供了一个统一的接口来定义和执行任务,无论这些任务是简单的函数调用、复杂的链式操作,还是与其他系统的集成。这使得代码更加模块化和可重用。 - 灵活性:
Runnable可以组合和嵌套,允许你构建复杂的任务流程。你可以将多个Runnable对象组合成一个链(Chain),或者将它们嵌套在其他Runnable中,从而实现高度灵活的任务编排。 - 异步支持:
Runnable天然支持异步操作,这意味着你可以在任务中使用异步函数,并在需要时并发执行多个任务。 - 可扩展性:
Runnable的设计允许你轻松地扩展和自定义任务的执行逻辑。你可以通过继承或组合现有的Runnable来创建新的任务类型。 - 调试和监控:
Runnable提供了丰富的调试和监控功能,你可以轻松地跟踪任务的执行流程,捕获中间结果,并在需要时进行干预。
简单示例
以下是一个简单的示例,展示了如何使用Runnable来定义和执行一个任务链。
from langchain import Runnable, RunnablePassthrough
# 定义一个简单的Runnable,它将输入字符串转换为大写
class UppercaseRunnable(Runnable):
def run(self, input):
return input.upper()
# 定义另一个Runnable,它将输入字符串反转
class ReverseRunnable(Runnable):
def run(self, input):
return input[::-1]
# 创建一个任务链,将输入字符串先转换为大写,然后反转
chain = UppercaseRunnable() | ReverseRunnable()
# 执行任务链
result = chain.run("hello")
print(result) # 输出: "OLLEH"
RunnableBranch 和 RunnableLambda
RunnableBranch 的用法
RunnableBranch 是 LangChain 中用于实现条件分支逻辑的组件。它允许你根据不同的条件选择不同的 Runnable 来执行。RunnableBranch 的核心思想是根据输入数据的不同条件,动态选择并执行相应的 Runnable。
示例
from langchain.prompts import PromptTemplate
from langchain_core.runnables import RunnableBranch
# 定义两个不同的提示模板
template1 = PromptTemplate.from_template("这是模板1,输入是: {input}")
template2 = PromptTemplate.from_template("这是模板2,输入是: {input}")
# 定义一个 RunnableBranch,根据输入的条件选择不同的模板
branch = RunnableBranch(
(lambda x: x["condition"] == "case1", template1),
(lambda x: x["condition"] == "case2", template2),
)
# 执行 RunnableBranch
result = branch.invoke({"condition": "case1", "input": "Hello"})
print(result) # 输出: "这是模板1,输入是: Hello"
result = branch.invoke({"condition": "case2", "input": "Hello"})
print(result) # 输出: "这是模板2,输入是: Hello"
解释
- 定义条件和对应的
Runnable:在RunnableBranch中,我们定义了两个条件和对应的Runnable。每个条件是一个函数,它接受输入数据并返回一个布尔值。如果条件为真,则选择对应的Runnable执行。 - 执行
RunnableBranch:通过调用branch.invoke,我们传递一个包含条件和输入数据的字典。RunnableBranch会根据条件选择合适的Runnable并执行。
RunnableLambda 的用法
RunnableLambda 是 LangChain 中用于包装普通函数或 lambda 表达式的组件。它允许你将一个函数或 lambda 表达式转换为一个 Runnable,从而可以在任务链中使用。
示例
from langchain_core.runnables import RunnableLambda
# 定义一个简单的函数
def print_info(info: str):
print(f"info: {info}")
return info
# 将函数包装为 RunnableLambda
lambda_runnable = RunnableLambda(print_info)
# 执行 RunnableLambda
result = lambda_runnable.invoke("Hello")
print(result) # 输出: "Hello"
解释
- 定义函数:我们定义了一个简单的函数
print_info,它接受一个字符串并打印出来,然后返回该字符串。 - 包装为
RunnableLambda:通过RunnableLambda,我们将print_info函数包装为一个Runnable。 - 执行
RunnableLambda:通过调用lambda_runnable.invoke,我们传递一个字符串给print_info函数,并执行它。
结合使用 RunnableBranch 和 RunnableLambda
在实际应用中,RunnableBranch 和 RunnableLambda 可以结合使用,以实现更复杂的任务链。例如,我们可以在 RunnableBranch 中使用 RunnableLambda 来处理不同的条件分支。
示例
from langchain.prompts import PromptTemplate
from langchain_core.runnables import RunnableBranch, RunnableLambda
# 定义两个不同的提示模板
template1 = PromptTemplate.from_template("这是模板1,输入是: {input}")
template2 = PromptTemplate.from_template("这是模板2,输入是: {input}")
# 定义一个简单的函数
def print_info(info: str):
print(f"info: {info}")
return info
# 定义一个 RunnableBranch,根据输入的条件选择不同的模板,并在每个分支中使用 RunnableLambda
branch = RunnableBranch(
(lambda x: x["condition"] == "case1", RunnableLambda(print_info) | template1),
(lambda x: x["condition"] == "case2", RunnableLambda(print_info) | template2),
)
# 执行 RunnableBranch
result = branch.invoke({"condition": "case1", "input": "Hello"})
print(result) # 输出: "这是模板1,输入是: Hello"
result = branch.invoke({"condition": "case2", "input": "Hello"})
print(result) # 输出: "这是模板2,输入是: Hello"
解释
- 定义条件和对应的
Runnable:在RunnableBranch中,我们定义了两个条件和对应的Runnable。每个条件是一个函数,它接受输入数据并返回一个布尔值。如果条件为真,则选择对应的Runnable执行。 - 使用
RunnableLambda:在每个分支中,我们使用RunnableLambda包装print_info函数,并在每个分支中执行它。 - 执行
RunnableBranch:通过调用branch.invoke,我们传递一个包含条件和输入数据的字典。RunnableBranch会根据条件选择合适的Runnable并执行。
通过这种方式,我们可以灵活地组合和使用 RunnableBranch 和 RunnableLambda,以实现复杂的任务链和条件分支逻辑。
RouterChain实现
完整代码
from langchain.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser, JsonOutputParser
from langchain_core.runnables import RunnableBranch,RunnableLambda
import os
from operator import itemgetter
from langchain.chains.router.llm_router import RouterOutputParser
from langchain.chains.router.multi_prompt_prompt import MULTI_PROMPT_ROUTER_TEMPLATE as RounterTemplate
# 构建两个场景的模板
flower_care_template = """你是一个经验丰富的园丁,擅长解答关于养花育花的问题。
下面是需要你来回答的问题:
{input}"""
flower_deco_template = """你是一位网红插花大师,擅长解答关于鲜花装饰的问题。
下面是需要你来回答的问题:
{input}
"""
# 一个默认模板
other_template = """
你是一个AI助手,你会回答一下问题。
具体问题如下:
{input}
"""
# 构建提示信息
prompt_infos = [
{
"key": "flower_care",
"description": "适合回答关于鲜花护理的问题",
"template": flower_care_template,
},
{
"key": "flower_decoration",
"description": "适合回答关于鲜花装饰的问题",
"template": flower_deco_template,
}
]
llm = ChatOpenAI(model=os.environ.get("LLM_MODELEND"))
destinations = [f"{p['key']}: {p['description']}" for p in prompt_infos]
router_template = RounterTemplate.format(destinations="\n".join(destinations))
router_prompt = PromptTemplate.from_template(router_template,output_parser=RouterOutputParser())
# print("路由提示:\n",router_prompt)
router_chain = router_prompt | llm | JsonOutputParser()
def print_info(info: str):
print(f"info: {info}")
return info
prompt = RunnableBranch(
(lambda x: "flower_care" == x["topic"]['destination'], PromptTemplate.from_template(flower_care_template)),
(lambda x: "flower_decoration" == x["topic"]['destination'], PromptTemplate.from_template(flower_deco_template)),
PromptTemplate.from_template(other_template)
)
chain = ({"topic": router_chain, "input": itemgetter('input')} |
RunnableLambda(print_info) |
prompt | RunnableLambda(print_info)| llm | StrOutputParser())
print(chain.invoke({"input": "如何为婚礼场地装饰花朵?"}))
print(chian.invoke({"input": "如何学习好LangChian?"}))
代码解释
我使用LangChain的Runnable来实现一个类似于RouterChain的功能,通过动态选择合适的提示模板来处理不同类型的问题。
首先,我定义了三个不同的提示模板:
flower_care_template:用于回答关于鲜花护理的问题。flower_deco_template:用于回答关于鲜花装饰的问题。other_template:作为默认模板,用于处理其他类型的问题。
接下来,我创建了一个包含这些提示模板的列表prompt_infos,每个模板都有一个唯一的键(key)和描述(description)。
然后,我初始化了一个ChatOpenAI模型,并使用这些提示模板的描述来构建一个路由提示模板。这个路由提示模板的作用是根据输入的问题类型,选择合适的提示模板。
为了实现路由功能,我定义了一个router_chain,它由路由提示模板、语言模型(LLM)和输出解析器组成。这个链的作用是根据输入的问题,动态选择合适的提示模板。
接下来,我定义了一个RunnableBranch,它根据路由链的输出(即选择的提示模板)来选择合适的提示模板。具体来说,如果路由链选择的是flower_care,则使用flower_care_template;如果选择的是flower_decoration,则使用flower_deco_template;否则,使用默认的other_template。
最后,我构建了一个完整的任务链chain,它首先通过路由链选择合适的提示模板,然后将输入传递给选择的提示模板,并最终通过语言模型生成回答。在任务链的执行过程中,我还添加了一个打印信息的步骤,以便在执行过程中输出一些调试信息。
通过这种方式,我可以根据输入的问题类型,动态选择合适的提示模板,并生成相应的回答。例如,当输入的问题是“如何为婚礼场地装饰花朵?”时,系统会选择flower_deco_template,并生成关于鲜花装饰的回答;而当输入的问题是“如何学习好LangChain?”时,系统会选择默认的other_template,并生成关于学习LangChain的回答。