「LangChain 学习笔记」LangChain大模型应用开发:模型链(Chains)
目录
1. 大语言模型链
2. 简单顺序链
3. 多输入/输出顺序链
4. 路由链(Router Chain)
4.1 路由链流程
链(Chains)通常将大语言模型(LLM)与提示(Prompt)结合在一起,基于此,我们可以对文本或数据进行一系列操作。链(Chains)可以一次性接受多个输入。
例如,我们可以创建一个链,该链接受用户输入,使用提示模板对其进行格式化,然后将格式化的响应传递给 LLM 。我们可以通过将多个链组合在一起,或者通过将链与其他组件组合在一起来构建更复杂的链。
1. 大语言模型链
LLMChain 类已在 LangChain 0.1.17 弃用,未来将被移除。所以这里使用 prompt | llm 替换 LLMChain,主要是管道符" | "。详细请参考:【LangChain】LLMChain 迁移至 RunnableSequence_langchaindeprecationwarning: the class `llmchain` -CSDN博客
大语言模型链(LLMChain)是一个简单但非常强大的链,也是后面我们将要介绍的许多链的基础。
将大语言模型(LLM)和提示(Prompt)组合成链。这个大语言模型链非常简单,可以让我们以一种顺序的方式去通过运行提示并且结合到大语言模型中。通过使用chain.invoke将其通过这个链运行。下面是一个简单的例子
from langchain_openai import ChatOpenAI # OpenAI 的语言模型支持
from langchain.prompts import ChatPromptTemplatellm = ChatOpenAI(api_key="你的API key",base_url="https://qianfan.baidubce.com/v2",model="ernie-3.5-8k",temperature=0
)prompt = ChatPromptTemplate.from_template( "描述制造{product}的一个公司的最佳名称是什么?"
)
chain = prompt | llm
product = "大号床单套装"result = chain.invoke(product)
print(result.content)
为一家制造大号床单套装的公司起名时,我们需要考虑名称的易记性、与产品特性的关联性、市场定位以及潜在的文化含义。以下是我为这类公司精心挑选的五个名称,每个都附有起名的理由:1. **巨梦家纺**:结合了“巨大”和“梦境”两个概念,暗示着大号床单能带来宽敞舒适的睡眠体验。
2. **宽逸居品**:“宽”字直接体现了床单套装的尺寸特点,而“逸居”则传达了家居生活的轻松惬意。
3. **悦眠巨幕**:将床单比作“巨幕”,形象地表达了其大尺寸的特点,同时“悦眠”强调了优质的睡眠体验。
4. **舒域家纺**:“舒域”结合了舒适和领域两个词汇,寓意着公司在大号床单领域的专业性和顾客的舒适感受。
5. **梦澜宽庭**:“梦澜”寓意着梦境中的波澜,象征着美好的睡眠体验,“宽庭”则强调了空间的宽敞感。这些名称都旨在体现公司产品的核心特点——大号和舒适,同时易于记忆和发音,符合中文文化背景。请注意,这些建议是基于创意和通用性,建议在最终确定前进行商标和名称的可用性检查,以确保不与现有公司或品牌重名。
2. 简单顺序链
顺序链(SequentialChains)是按预定义顺序执行其链接的链。具体来说,我们将使用管道操作符(" | ")构建简单顺序链,这是顺序链的最简单类型,其中每个步骤都有一个输入/输出,一个步骤的输出是下一个步骤的输入。
first_prompt = ChatPromptTemplate.from_template( "为一家主营{product}的公司起一个最好的中文品牌名称,只要一个名称不需要多余的,也不需要任何描述。"
)
chain_one = first_prompt | llmsecond_prompt = ChatPromptTemplate.from_template( "写一个20字的描述对于下面这个\公司的品牌:{company_name}"
)chain_two = second_prompt | llmsimple_chain = chain_one | chain_twoproduct = "大号床单套装"
result1 = chain_one.invoke(product)
result2 = chain_two.invoke(result1)
print("第一条链的结果:" + result1.content)
print("第二条链的结果:" + result2.content)result = simple_chain.invoke(product)
print("整体链的结果:" + result.content)
第一条链的结果:巨梦床单
第二条链的结果:巨梦床单,编织梦幻家居的温馨之选。
整体链的结果:巨梦床单,编织梦幻家居的温馨之选。
3. 多输入/输出顺序链
当只有一个输入和一个输出时,简单顺序链即可实现。当有多个输入或多个输出时,我们则需要使用 RunnableSequence来实现。
from langchain_core.runnables import RunnableSequence
from langchain_core.prompts import ChatPromptTemplate# 假设 llm 已定义
# 步骤 1:分析用户输入的餐厅偏好
gather_preferences_prompt = ChatPromptTemplate.from_template("用户输入了一些餐厅偏好:{preferences}\n""请将用户的偏好总结为清晰的需求:"
)
gather_preferences_chain = gather_preferences_prompt | llm | (lambda x: {"summarized_preferences": x.content})# 步骤 2:根据需求推荐餐厅
recommend_restaurants_prompt = ChatPromptTemplate.from_template("基于用户需求:{summarized_preferences}\n""请推荐 3 家适合的餐厅,并说明推荐理由:"
)
recommend_restaurants_chain = recommend_restaurants_prompt | llm | (lambda x: {"recommendations": x.content})# 步骤 3:总结推荐内容供用户快速参考
summarize_recommendations_prompt = ChatPromptTemplate.from_template("以下是餐厅推荐和推荐理由:\n{recommendations}\n""请总结成 2-3 句话,供用户快速参考:"
)
summarize_recommendations_chain = summarize_recommendations_prompt | llm# 创建 RunnableSequence
restaurant_chain = RunnableSequence(gather_preferences_chain,recommend_restaurants_chain,summarize_recommendations_chain
)
restaurant_chain2 = gather_preferences_chain | recommend_restaurants_chain | summarize_recommendations_chain# 示例输入数据
input_data = {"preferences": "我喜欢安静的环境,最好有素食选项,并且价格适中。"}# 执行链条
result = restaurant_chain.invoke(input_data)
result2 = restaurant_chain2.invoke(input_data)# 输出最终结果
print(result.content)
print(result2.content)
推荐三家安静且提供丰富素食选项的餐厅:静谧素食轩、悠然素食馆和绿野仙踪素食餐厅。这些餐厅均拥有适中的价格定位,是性价比很高的用餐选择,适合寻求安静用餐环境的素食爱好者。
推荐三家安静且提供丰富素食选项的餐厅:静谧素食轩、悠然素食馆和绿野仙踪素食餐厅。这些餐厅均拥有适中的价格定位,是性价比很高的用餐选择,适合寻求安静用餐环境的素食爱好者。
gather_preferences_chain = gather_preferences_prompt | llm | (lambda x: {"summarized_preferences": x.content})代码解释:
- gather_preferences_prompt:一个 ChatPromptTemplate,用于格式化用户输入(如“我喜欢安静的环境...”)。
- | llm:把格式化后的提示传给大语言模型(如 ChatOpenAI),让模型生成总结。
- | (lambda x: {"summarized_preferences": x.content}):对模型的输出做后处理,把输出内容包装成字典,key 为 "summarized_preferences",value 为模型输出的内容(x.content)。
RunnableSequence方法:显式方式组织链路;等价于使用管道符 |,可以看出上面结果一致。
4. 路由链(Router Chain)
到目前为止,我们已经学习了大语言模型链和顺序链。但是,如果我们想做一些更复杂的事情怎么办?
一个相当常见但基本的操作是根据输入将其路由到一条链,具体取决于该输入到底是什么(也就是不同的输入对应不同的链)。如果你有多个子链,每个子链都专门用于特定类型的输入,那么可以组成一个路由链,它首先决定将它传递给哪个子链,然后将它传递给那个链。
from langchain_core.runnables import RouterRunnable, RunnableLambda, RunnableSequence# Step 1: 定义各领域的任务模板
# 定义物理任务模板
physics_template = ChatPromptTemplate.from_template("你是一位物理学教授,擅长用简洁易懂的方式回答物理问题。以下是问题内容:{input}"
)# 定义数学任务模板
math_template = ChatPromptTemplate.from_template("你是一位数学家,擅长分步骤解决数学问题,并提供详细的解决过程。以下是问题内容:{input}"
)# 定义历史任务模板
history_template = ChatPromptTemplate.from_template("你是一位历史学家,对历史事件和背景有深入研究。以下是问题内容:{input}"
)# 定义计算机科学任务模板
computerscience_template = ChatPromptTemplate.from_template("你是一位计算机科学专家,擅长算法、数据结构和编程问题。以下是问题内容:{input}"
)# Step 2: 将模板与语言模型结合,形成任务链
physics_chain = physics_template | llm
math_chain = math_template | llm
history_chain = history_template | llm
computerscience_chain = computerscience_template | llm# Step 3: 定义动态路由逻辑
def route(input):if "物理" in input["input"]:print("路由到物理任务")return {"key": "physics", "input": input["input"]}elif "数学" in input["input"]:print("路由到数学任务")return {"key": "math", "input": input["input"]}elif "历史" in input["input"]:print("路由到历史任务")return {"key": "history", "input": input["input"]}elif "计算机" in input["input"]:print("路由到计算机任务") return {"key": "computer_science", "input": input["input"]}else:print("路由到默认任务")return {"key": "default", "input": input["input"]}# 创建路由逻辑的 Runnable
route_runnable = RunnableLambda(route)# 定义默认处理链
default_chain = ChatPromptTemplate.from_template("输入内容无法归类,请直接回答:{input}"
) | llm# Step 4: 创建 RouterRunnable
router = RouterRunnable(runnables={"physics": physics_chain,"math": math_chain,"history": history_chain,"computer_science": computerscience_chain,"default": default_chain}
)# Step 5: 创建完整的 Router Chain
router_chain = RunnableSequence(route_runnable, router)# Step 6: 测试 Router Chain
# 定义示例输入
inputs = [{"input": "物理:什么是黑体辐射?"}, # 物理问题{"input": "数学:计算 2 + 2 的结果。"}, # 数学问题{"input": "历史:介绍一次世界大战的背景。"}, # 历史问题{"input": "计算机:如何实现快速排序算法?"} # 计算机科学问题
]# 执行 Router Chain 并输出结果
for inp in inputs:result = router_chain.invoke(inp)print(f"问题:{inp['input']}\n回答:{result.content}\n")
以上代码参考 :全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南_routerrunnable-CSDN博客
,虽然代码流程看懂了但是我觉得还是自己沉淀一下,等沉淀完了重新回来补上心得。
4.1 路由链流程
流程步骤 | 描述 | 使用的 API |
---|---|---|
1️⃣ 定义任务模板 | 为各领域设计 prompt | ChatPromptTemplate.from_template |
2️⃣ 创建任务链 | 模板 + LLM → 可执行链 | 管道符 " | " |
3️⃣ 定义路由逻辑 | 判断输入所属领域 | Python 函数 + RunnableLambda |
4️⃣RouterRunnable | 根据 key 选择链执行 | RouterRunnable |
5️⃣Router Chain | 路由逻辑 + 多分支路由串联 | RunnableSequence |
6️⃣ 执行测试 | 调用整个链生成回答 | router_chain.invoke(input) |