01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南
02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖
03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南
04-玩转 LangChain:从文档加载到高效问答系统构建的全程实战
在自然语言处理(NLP)的世界里,任务需求的多样性和复杂性不断给开发者提出挑战。一个高效的 NLP 系统需要同时胜任多个任务,例如动态生成广告语、个性化推荐,甚至能回答跨学科的问题。然而,传统开发方法通常需要独立为每个任务设计逻辑模块,不仅耗时耗力,还极易导致代码的复杂性失控。
LangChain 以其模块化的设计为这一问题提供了解决方案。它不仅简化了开发流程,还通过强大的链条组合能力,使开发者能够快速搭建从单一任务到多任务分配的完整逻辑流。本篇文章将围绕 LangChain 的核心功能模块展开,从理论到实战,全面展示如何通过它实现自然语言处理任务的高效管理。
在本文中,您将学到以下内容:
ChatPromptTemplate
和 Pipe Operator
)入手,学习如何构建单任务处理链条,逐步扩展到多步骤的复杂任务链。特别是通过 RunnableSequence
,我们将探讨如何在多步骤任务中实现数据流转和结果汇总。不仅如此,文章还提供了详细的代码示例,涵盖从单一任务到复杂任务链条的完整实现过程,助力您快速上手并将其应用于实际项目。
为什么你需要关注这篇文章?
本文不是一个纯理论的解读,而是一个贯穿实践的指南。无论是生成广告语、推荐餐厅,还是动态解决跨学科问题,LangChain 都能为您提供强大的技术支持。而本文正是您解锁这些潜能的第一步。
LangChain 提供了丰富的模块,帮助开发者高效构建自然语言处理任务的逻辑流。本章将介绍如何通过新的方法直接结合 ChatPromptTemplate
和 ChatOpenAI
来实现核心功能,并利用 Pipe Operator
和 RunnableSequence
实现从简单到复杂的任务链。
通过 ChatPromptTemplate
和 ChatOpenAI
的结合,可以快速实现单一任务,例如生成广告语、回答问题或其他简单任务。
以下示例展示了如何结合提示模板与语言模型,生成一条针对产品的中文广告语:
import os
from dotenv import load_dotenv # 加载环境变量
from langchain_openai import ChatOpenAI # OpenAI 的语言模型支持
from langchain.prompts import ChatPromptTemplate
# 加载 .env 文件中的环境变量
load_dotenv()
# 从环境变量中获取 API Key 和 Base URL
api_key = os.getenv("ALIYUN_API_KEY")
base_url = os.getenv("ALIYUN_API_URL")
# 初始化语言模型
llm = ChatOpenAI(
openai_api_key=api_key,
model_name="qwen-plus", # 使用 Qwen-Plus 模型
base_url=base_url,
)
# 定义提示模板
prompt = ChatPromptTemplate.from_template(
"请为以下产品设计一句吸引人的广告语:\n\n产品:{product}"
)
# 创建链条
chain = prompt | llm
# 输入产品信息并运行链条
product = "健康无糖茶饮"
result = chain.invoke({"product": product})
print(f"生成的广告语:{result.content}")
{product}
是占位符,运行时会被实际的输入内容替换。"一口茶香,一份健康,享受无糖新生活!"
temperature
参数可控制生成结果的多样性。管道组合(Pipe Operator
,即 |
)是一种高效的方式,可以将多个任务串联起来,每个任务的输出作为下一个任务的输入。这种方法适用于线性的多步骤任务。
以下示例展示如何通过两步链条,生成一个中文品牌名称并为其撰写品牌简介:
# 定义第一个任务:生成品牌名称
first_prompt = ChatPromptTemplate.from_template(
"为一家主营{product}的公司起一个中文品牌名称。"
)
chain_one = first_prompt | llm
# 定义第二个任务:生成品牌简介
second_prompt = ChatPromptTemplate.from_template(
"为以下品牌撰写一段简介:\n\n品牌名称:{brand_name}"
)
chain_two = second_prompt | llm
# 通过管道组合两个任务
simple_chain = chain_one | chain_two
# 输入数据并运行链条
product = {"product": "智能家居设备"}
result = simple_chain.invoke(product)
# 输出最终结果
print(f"生成结果:{result.content}")
"智家优选"
"智家优选致力于提供智能化的家居解决方案,为家庭带来便捷与高科技体验。"
RunnableSequence
是 LangChain 的高级功能模块,支持复杂任务链,例如需要多输入、多输出以及多步骤的数据流转。
以下示例展示如何通过多步骤链条,处理用户餐厅推荐任务,包括分析用户偏好、推荐餐厅并总结结果:
from langchain_core.runnables import RunnableSequence
# 步骤 1:分析用户输入的餐厅偏好
gather_preferences_prompt = ChatPromptTemplate.from_template(
"用户输入了一些餐厅偏好:{preferences}\n"
"请将用户的偏好总结为清晰的需求:"
)
# 步骤 2:根据需求推荐餐厅
recommend_restaurants_prompt = ChatPromptTemplate.from_template(
"基于用户需求:{summarized_preferences}\n"
"请推荐 3 家适合的餐厅,并说明推荐理由:"
)
# 步骤 3:总结推荐内容供用户快速参考
summarize_recommendations_prompt = ChatPromptTemplate.from_template(
"以下是餐厅推荐和推荐理由:\n{recommendations}\n"
"请总结成 2-3 句话,供用户快速参考:"
)
# 将每个任务组合成链条
gather_preferences_chain = gather_preferences_prompt | llm
recommend_restaurants_chain = recommend_restaurants_prompt | llm
summarize_recommendations_chain = summarize_recommendations_prompt | llm
# 创建 RunnableSequence
restaurant_chain = RunnableSequence(
gather_preferences_chain,
recommend_restaurants_chain,
summarize_recommendations_chain
)
# 示例输入数据
input_data = {"preferences": "我喜欢安静的环境,最好有素食选项,并且价格适中。"}
# 执行链条
result = restaurant_chain.invoke(input_data)
# 输出最终结果
print(result.content)
"用户偏好安静的餐厅,提供素食选项,价格适中。"
"推荐素心小馆、自然味道和绿叶咖啡馆,均符合您的素食和安静需求。"
在复杂的自然语言处理场景中,不同输入内容可能需要不同的处理逻辑。例如,物理问题、数学问题和历史问题的解决方式可能完全不同。Router Chain
提供了动态路由解决方案,可以根据输入内容自动选择适合的处理链条。以下内容将结合您修改后的代码,详细讲解如何实现动态路由及其应用场景。
Router Chain
是 LangChain 提供的一种动态任务分配机制。它通过路由逻辑分析输入内容,将其动态分配到最合适的任务链。如果输入无法归类,则会调用默认处理链。Router Chain
的核心优势是灵活适配多领域任务处理需求。
以下是基于您的代码实现的完整动态路由逻辑及任务链的步骤。
首先为不同领域(物理、数学、历史、计算机科学)的问题定义任务模板。这些模板会根据输入内容动态生成回答。
from langchain.prompts import ChatPromptTemplate
# 定义物理任务模板
physics_template = ChatPromptTemplate.from_template(
"你是一位物理学教授,擅长用简洁易懂的方式回答物理问题。以下是问题内容:{input}"
)
# 定义数学任务模板
math_template = ChatPromptTemplate.from_template(
"你是一位数学家,擅长分步骤解决数学问题,并提供详细的解决过程。以下是问题内容:{input}"
)
# 定义历史任务模板
history_template = ChatPromptTemplate.from_template(
"你是一位历史学家,对历史事件和背景有深入研究。以下是问题内容:{input}"
)
# 定义计算机科学任务模板
computerscience_template = ChatPromptTemplate.from_template(
"你是一位计算机科学专家,擅长算法、数据结构和编程问题。以下是问题内容:{input}"
)
将上述模板与语言模型(ChatOpenAI
)结合,形成任务链条。
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
load_dotenv()
# 从环境变量中获取 API Key 和 Base URL
api_key = os.getenv("ALIYUN_API_KEY")
base_url = os.getenv("ALIYUN_API_URL")
# 初始化语言模型
llm = ChatOpenAI(
openai_api_key=api_key,
model_name="qwen-plus", # 使用 Qwen-Plus 模型
base_url=base_url
)
# 将模板与语言模型结合,形成各领域的任务链
physics_chain = physics_template | llm
math_chain = math_template | llm
history_chain = history_template | llm
computerscience_chain = computerscience_template | llm
通过自定义的 RunnableLambda
定义路由逻辑,根据输入内容分配到不同任务链。
from langchain_core.runnables import RunnableLambda
# 定义动态路由逻辑
def route(input):
if "物理" in input["input"]:
return {"key": "physics", "input": input["input"]}
elif "数学" in input["input"]:
return {"key": "math", "input": input["input"]}
elif "历史" in input["input"]:
return {"key": "history", "input": input["input"]}
elif "计算机" in input["input"]:
return {"key": "computer_science", "input": input["input"]}
else:
return {"key": "default", "input": input["input"]}
# 创建路由逻辑的 Runnable
route_runnable = RunnableLambda(route)
当输入内容无法匹配任何任务链时,使用默认处理链处理问题。
# 定义默认处理链
default_chain = ChatPromptTemplate.from_template(
"输入内容无法归类,请直接回答:{input}"
) | llm
将所有任务链和默认链条整合到 RouterRunnable
中,用于动态分配任务。
from langchain_core.runnables import RouterRunnable
# 创建 RouterRunnable
router = RouterRunnable(
runnables={
"physics": physics_chain,
"math": math_chain,
"history": history_chain,
"computer_science": computerscience_chain,
"default": default_chain
}
)
通过 RunnableSequence
将路由逻辑与任务链整合为一个完整的 Router Chain
。
from langchain_core.runnables import RunnableSequence
# 创建完整的 Router Chain
router_chain = RunnableSequence(route_runnable, router)
为 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")
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain_core.runnables import RouterRunnable, RunnableLambda, RunnableSequence
# 加载 .env 文件中的环境变量
load_dotenv()
# 从环境变量中获取 API Key 和 Base URL
api_key = os.getenv("ALIYUN_API_KEY")
base_url = os.getenv("ALIYUN_API_URL")
# 初始化语言模型
llm = ChatOpenAI(
openai_api_key=api_key,
model_name="qwen-plus", # 使用 Qwen-Plus 模型
base_url=base_url
)
# 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"]:
return {"key": "physics", "input": input["input"]}
elif "数学" in input["input"]:
return {"key": "math", "input": input["input"]}
elif "历史" in input["input"]:
return {"key": "history", "input": input["input"]}
elif "计算机" in input["input"]:
return {"key": "computer_science", "input": input["input"]}
else:
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")
根据输入内容,Router Chain 将问题分配到合适的任务链,并生成对应的答案。例如:
输入:"什么是黑体辐射?"
输出:"黑体辐射是一个理想黑体发出的电磁辐射,其能量分布与温度相关。"
输入:"计算 2 + 2 的结果。"
输出:"2 + 2 的结果是 4。"
输入:"介绍一次世界大战的背景。"
输出:"一次世界大战的爆发是由于帝国主义竞争、民族主义以及萨拉热窝事件的直接影响。"
输入:"如何实现快速排序算法?"
输出:"快速排序是一种分治算法,核心步骤包括选择基准值、分区操作和递归调用子序列排序。"
路由逻辑的扩展性:
route
函数中添加相应的逻辑,并新增任务链条。默认处理链的重要性:
任务描述的清晰性:
本文全面解析了 LangChain 的两大核心功能模块:基础链条构建与动态任务分配,并通过具体示例演示了它们的强大应用。以下是本文的核心要点总结:
单任务 Prompt 模型结合:
ChatPromptTemplate
和 ChatOpenAI
快速构建单任务处理链条,例如生成广告语和回答简单问题。管道组合实现多步骤任务:
Pipe Operator
(|
)将多个任务串联,构建线性任务链,实现如品牌命名与简介生成等复杂逻辑。RunnableSequence 构建复杂链条:
RunnableSequence
模块,将多步骤任务串联为更高级的复杂链条。动态任务分配的核心机制:
RouterRunnable
和 RunnableLambda
,实现输入内容的智能分类和任务分配。完整代码实现:
实际应用场景: