第 13 期 | Routing 路由机制:复杂意图的分发中心
(申请发送: agentupdate)
🎯 本期学习目标
嘿,各位未来的 AI 架构师们!欢迎来到《LangChain 全栈大师课》的第一期。我是你们的导师,一个在 AI 领域摸爬滚打十年的老兵,也是一个对教育充满热情的技术导师。
今天,我们将踏上 LangChain 的征程,直捣其核心——大型语言模型 (LLM) 和 提示模板 (PromptTemplate)。这两位,就像你打造智能客服小助手的“大脑”和“说明书”,是所有复杂功能的基础。学完本期,你将:
- 透彻理解 LLM 和 ChatModel 在 LangChain 中的角色与差异,知道何时该请哪位“大佬”出场。
- 掌握 PromptTemplate 的艺术与科学,学会如何像顶级教练一样,用精准的指令激发 LLM 的潜能。
- 亲手构建智能客服的第一个对话基石,让你的 AI 小助手能根据指令进行初步交互。
- 识别并规避常见的 Prompt 工程陷阱,从一开始就培养高质量的 AI 应用开发习惯。
准备好了吗?让我们一起揭开 LangChain 的神秘面纱!
📖 原理解析
在我们的“智能客服知识库”项目中,最终目标是让一个 AI 小助手能准确、高效地回答用户问题,甚至主动提供帮助。要达到这个目标,我们首先需要给它一个“大脑”和一套“沟通规矩”。
1. LLM / ChatModel:智能客服的“大脑”
想象一下,你的智能客服小助手,需要能理解人类语言,并用人类语言进行交流。这个能力,就来自于 大型语言模型 (Large Language Model, LLM)。
LLM 是一种经过海量文本数据训练的深度学习模型,它能理解、生成、翻译、总结文本,甚至进行复杂的推理。在 LangChain 中,LLM 是所有智能应用的核心驱动力。
LangChain 对各种 LLM 提供了统一的接口,让你无需关心底层模型是 OpenAI 的 GPT-4 还是 Google 的 Gemini,甚至是你自己部署的 Llama 2。这种抽象能力,正是 LangChain 的魅力所在。
但这里有个小小的“坑”:LangChain 区分了两种主要的模型接口:LLM 和 ChatModel。
LLM(Completion API): 这种模型接口通常接收一个简单的字符串作为输入(例如,一个问题),然后返回一个字符串作为输出(例如,一个答案)。它更像是“文本补全”或“文本生成”的模式。- 适用场景: 简单的文本生成、摘要、翻译等。
- 举例:
text-davinci-003(OpenAI 早期模型)
ChatModel(Chat API): 这种模型接口更适合多轮对话。它接收一个“消息列表”作为输入,其中每条消息都有一个角色(System、Human、AI),然后返回一个“消息”作为输出。System消息可以用来设定 AI 的人设和行为准则,这在构建客服小助手时尤其重要!- 适用场景: 智能客服、聊天机器人、多轮对话系统等。
- 举例:
gpt-3.5-turbo,gpt-4(OpenAI 推荐的聊天模型)
为什么 ChatModel 对我们的智能客服项目如此重要?
因为客服小助手需要有明确的“人设”(比如:你是一个友好的、专业的客服),并且能够理解对话的上下文。System 消息能让我们清晰地定义小助手的行为,而消息列表则能更好地模拟真实的对话流程。所以,对于我们的智能客服项目,ChatModel 将是我们的首选。
2. PromptTemplate:智能客服的“说明书”和“角色设定”
有了“大脑”还不够,你还得告诉它怎么思考,怎么回答。这就引出了 提示模板 (PromptTemplate)。
Prompt Engineering,也就是“提示工程”,是与 LLM 交互的艺术和科学。你给 LLM 的指令(Prompt)质量越高,它给出的回答就越好。PromptTemplate 的作用,就是将这个“艺术”标准化、“工程化”。
一个好的 PromptTemplate 就像一份详细的说明书,它能:
- 设定角色 (Persona): “你是一个专业的智能客服。”
- 定义任务 (Task): “你的任务是解答用户关于产品的问题。”
- 提供上下文 (Context): “以下是用户的问题:
{user_query},以及相关的知识库片段:{knowledge_chunk}。” - 指定格式 (Format): “请用简洁的语言回答,并在末尾引导用户访问官网。”
PromptTemplate 最强大的地方在于它的变量占位符。你可以定义一些变量(例如 {user_query}),在运行时动态填充这些变量,从而生成针对特定场景的完整 Prompt。这大大提高了 Prompt 的复用性和可维护性。
同样,与 LLM 和 ChatModel 对应,LangChain 也提供了两种 PromptTemplate:
PromptTemplate: 适用于LLM模型,接收一个字符串模板,输出一个格式化的字符串。ChatPromptTemplate: 适用于ChatModel模型,它接收一个消息列表的模板,其中包含SystemMessagePromptTemplate、HumanMessagePromptTemplate、AIMessagePromptTemplate等,输出一个格式化的消息列表。这与ChatModel的输入格式完美契合。
在我们的智能客服项目中,ChatPromptTemplate 绝对是主角! 我们可以用 SystemMessagePromptTemplate 来设定客服小助手的性格和行为规则,用 HumanMessagePromptTemplate 来插入用户的具体问题。
3. LLM + PromptTemplate:协同工作流
现在,我们把大脑 (ChatModel) 和说明书 (ChatPromptTemplate) 结合起来,看看它们是如何协同工作的,为我们的智能客服小助手提供基础的对话能力。
用户提出问题后,这个流程是这样的:
- 用户提问: 比如:“如何重置我的密码?”
- PromptTemplate 填充: 用户的提问 (
{user_query}) 会被填入我们预设好的ChatPromptTemplate中。 - 生成完整 Prompt (消息列表):
ChatPromptTemplate会根据模板和填充的变量,生成一个包含System消息和Human消息的完整消息列表。 - 发送给 ChatModel: 这个消息列表会被发送给
ChatModel(例如gpt-3.5-turbo)。 - ChatModel 生成响应:
ChatModel根据消息列表,理解上下文和任务,生成一个 AI 的回复。 - 智能客服小助手回答: 最终,这个回复会作为智能客服小助手的答案返回给用户。
整个过程可以用下面的 Mermaid 图清晰地展示:
graph TD
A[用户提问] --> B{ChatPromptTemplate};
B -- 填充变量 (如: {user_query}) --> C[生成格式化消息列表];
C --> D[LangChain ChatModel];
D -- 调用大模型API (如: OpenAI GPT-4) --> E[大模型服务];
E -- 返回原始AI响应 --> D;
D -- 提取AI消息内容 --> F[智能客服小助手回答];
F --> G[用户接收回答];
subgraph 智能客服小助手核心
B
C
D
F
end这个图清晰地展示了数据流和各个组件的职责。ChatPromptTemplate 负责“翻译”用户输入和系统指令,ChatModel 负责“思考”和“生成”答案。这,就是你智能客服小助手的第一个简陋但强大的雏形!
💻 实战代码演练
好了,理论说再多,不如撸起袖子干一场!现在,让我们把这些概念转化为真实的代码,为我们的智能客服小助手打下坚实的基础。
我们将使用 OpenAI 的模型作为示例,所以请确保你已经安装了必要的库,并设置了 OPENAI_API_KEY 环境变量。
环境准备
如果你还没安装,请运行:
# Python
pip install langchain langchain-openai python-dotenv
# TypeScript (Node.js)
npm install langchain @langchain/openai dotenv
并在你的项目根目录创建一个 .env 文件,写入你的 OpenAI API Key:
OPENAI_API_KEY="sk-YOUR_OPENAI_API_KEY_HERE"
1. 初始化 LLM / ChatModel
首先,我们来初始化我们的“大脑”。记住,对于对话系统,我们优先选择 ChatOpenAI。
Python 代码:
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI, OpenAI
from langchain_core.prompts import PromptTemplate, ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate
from langchain_core.messages import HumanMessage, SystemMessage
# 加载环境变量
load_dotenv()
# 确保 OPENAI_API_KEY 已设置
if not os.getenv("OPENAI_API_KEY"):
raise ValueError("OPENAI_API_KEY 环境变量未设置。请在 .env 文件中配置。")
print("--- 初始化 LangChain LLM/ChatModel ---")
# 1. 初始化一个传统的 LLM (基于 Completion API)
# 注意:text-davinci-003 已被 OpenAI 弃用,这里仅作概念演示
# llm = OpenAI(temperature=0.7, model_name="text-davinci-003")
# print(f"初始化 LLM 模型: {llm.model_name}")
# 2. 初始化一个 ChatModel (基于 Chat API),这是我们智能客服的主力
# temperature: 控制模型输出的随机性。0.0表示确定性最高,1.0表示创造性最高。
# 客服场景通常需要较低的温度 (0.0 - 0.7),以保证回答的准确性和一致性。
chat_model = ChatOpenAI(temperature=0.7, model_name="gpt-3.5-turbo")
print(f"初始化 ChatModel 模型: {chat_model.model_name}")
# 你也可以尝试 gpt-4,但成本更高
# chat_model_gpt4 = ChatOpenAI(temperature=0.7, model_name="gpt-4")
# print(f"初始化 ChatModel 模型: {chat_model_gpt4.model_name}")
print("\n模型初始化完成。\n")
TypeScript 代码:
import 'dotenv/config'; // 确保在文件顶部导入,加载环境变量
import { ChatOpenAI, OpenAI } from '@langchain/openai';
import { PromptTemplate, ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate } from '@langchain/core/prompts';
import { HumanMessage, SystemMessage } from '@langchain/core/messages';
// 确保 OPENAI_API_KEY 已设置
if (!process.env.OPENAI_API_KEY) {
throw new Error("OPENAI_API_KEY 环境变量未设置。请在 .env 文件中配置。");
}
console.log("--- 初始化 LangChain LLM/ChatModel ---");
// 1. 初始化一个传统的 LLM (基于 Completion API)
// 注意:text-davinci-003 已被 OpenAI 弃用,这里仅作概念演示
// const llm = new OpenAI({ temperature: 0.7, modelName: "text-davinci-003" });
// console.log(`初始化 LLM 模型: ${llm.modelName}`);
// 2. 初始化一个 ChatModel (基于 Chat API),这是我们智能客服的主力
// temperature: 控制模型输出的随机性。0.0表示确定性最高,1.0表示创造性最高。
// 客服场景通常需要较低的温度 (0.0 - 0.7),以保证回答的准确性和一致性。
const chatModel = new ChatOpenAI({ temperature: 0.7, modelName: "gpt-3.5-turbo" });
console.log(`初始化 ChatModel 模型: ${chatModel.modelName}`);
// 你也可以尝试 gpt-4,但成本更高
// const chatModelGpt4 = new ChatOpenAI({ temperature: 0.7, modelName: "gpt-4" });
// console.log(`初始化 ChatModel 模型: ${chatModelGpt4.modelName}`);
console.log("\n模型初始化完成。\n");
2. 构建 PromptTemplate
接下来,我们来定义智能客服的“说明书”。我们将演示 PromptTemplate 和 ChatPromptTemplate 两种用法,并重点关注 ChatPromptTemplate。
Python 代码:
# --- 1. 经典 PromptTemplate 示例 (适用于 LLM 模型) ---
print("--- 经典 PromptTemplate 示例 ---")
# 定义一个简单的 Prompt 模板,包含一个变量 {question}
classic_template = "你是一个专业的客服助手。请根据以下问题提供简洁明了的答案:\n问题: {question}\n答案:"
classic_prompt = PromptTemplate.from_template(classic_template)
# 格式化 Prompt
formatted_classic_prompt = classic_prompt.format(question="如何重置我的账户密码?")
print(f"格式化后的经典 Prompt:\n{formatted_classic_prompt}\n")
# --- 2. ChatPromptTemplate 示例 (适用于 ChatModel 模型,我们智能客服的主力) ---
print("--- ChatPromptTemplate 示例 ---")
# 使用 ChatPromptTemplate 定义智能客服的“人设”和“任务”
# SystemMessagePromptTemplate: 用于设定 AI 的行为准则和角色
# HumanMessagePromptTemplate: 用于承载用户的输入
chat_cs_template = ChatPromptTemplate.from_messages([
SystemMessagePromptTemplate.from_template(
"你是一个友好的、专业的智能客服,专门解答关于我们产品的问题。你的回答应该简洁、准确,并引导用户访问官方文档获取更多信息。"
),
HumanMessagePromptTemplate.from_template("用户问题: {user_query}") # {user_query} 是我们将动态填充的变量
])
# 格式化 ChatPromptTemplate,生成一个消息列表
# 注意:这里返回的是一个列表,每个元素都是一个 Message 对象(SystemMessage, HumanMessage等)
formatted_chat_messages = chat_cs_template.format_messages(user_query="我的订单状态是什么?")
print("格式化后的 ChatPromptTemplate 消息列表:")
for msg in formatted_chat_messages:
print(f" - 角色: {msg.type}, 内容: {msg.content}")
print("\nChatPromptTemplate 构建完成。\n")
TypeScript 代码:
// --- 1. 经典 PromptTemplate 示例 (适用于 LLM 模型) ---
console.log("--- 经典 PromptTemplate 示例 ---");
// 定义一个简单的 Prompt 模板,包含一个变量 {question}
const classicTemplateString = "你是一个专业的客服助手。请根据以下问题提供简洁明了的答案:\n问题: {question}\n答案:";
const classicPrompt = PromptTemplate.fromTemplate(classicTemplateString);
// 格式化 Prompt
const formattedClassicPrompt = await classicPrompt.format({ question: "如何重置我的账户密码?" });
console.log(`格式化后的经典 Prompt:\n${formattedClassicPrompt}\n`);
// --- 2. ChatPromptTemplate 示例 (适用于 ChatModel 模型,我们智能客服的主力) ---
console.log("--- ChatPromptTemplate 示例 ---");
// 使用 ChatPromptTemplate 定义智能客服的“人设”和“任务”
// SystemMessagePromptTemplate: 用于设定 AI 的行为准则和角色
// HumanMessagePromptTemplate: 用于承载用户的输入
const chatCsTemplate = ChatPromptTemplate.fromMessages([
SystemMessagePromptTemplate.fromTemplate(
"你是一个友好的、专业的智能客服,专门解答关于我们产品的问题。你的回答应该简洁、准确,并引导用户访问官方文档获取更多信息。"
),
HumanMessagePromptTemplate.fromTemplate("用户问题: {user_query}") // {user_query} 是我们将动态填充的变量
]);
// 格式化 ChatPromptTemplate,生成一个消息列表
// 注意:这里返回的是一个列表,每个元素都是一个 Message 对象(SystemMessage, HumanMessage等)
const formattedChatMessages = await chatCsTemplate.formatMessages({ user_query: "我的订单状态是什么?" });
console.log("格式化后的 ChatPromptTemplate 消息列表:");
for (const msg of formattedChatMessages) {
console.log(` - 角色: ${msg._getType()}, 内容: ${msg.content}`);
}
console.log("\nChatPromptTemplate 构建完成。\n");
3. LLM + PromptTemplate 结合:第一个智能客服雏形
现在,我们将 ChatModel 和 ChatPromptTemplate 结合起来,构建我们的第一个智能客服小助手。它将根据我们设定的“人设”和用户的提问,生成一个专业的回答。
Python 代码:
# 确保 chat_model 和 chat_cs_template 已经从上面的代码块中定义
# 如果你是单独运行此块,请确保先运行上面的初始化和模板定义代码
print("--- 智能客服小助手对话模拟 ---")
async def ask_customer_service(question: str):
"""
智能客服小助手提问函数。
它将用户的提问填充到 ChatPromptTemplate 中,然后调用 ChatModel 获取答案。
"""
print(f"\n用户提问: {question}")
# 1. 格式化 PromptTemplate,生成 ChatModel 需要的消息列表
messages = await chat_cs_template.format_messages(user_query=question)
# 打印发送给模型的具体消息内容,方便调试
# print("--- 发送给 ChatModel 的消息 ---")
# for msg in messages:
# print(f" - {msg.type.capitalize()}: {msg.content}")
# print("-----------------------------")
# 2. 调用 ChatModel 获取响应
# .ainvoke() 是异步调用,如果你在 Jupyter 或顶级 await 环境外,需要用 asyncio.run() 包裹
response = await chat_model.ainvoke(messages)
# response 是一个 AIMessage 对象,它的 .content 属性就是 AI 生成的文本
return response.content
# 模拟几次智能客服的对话
# 场景1: 用户询问产品功能
answer1 = await ask_customer_service("你们的产品有什么特色功能?")
print(f"客服小助手: {answer1}")
# 场景2: 用户询问售后支持
answer2 = await ask_customer_service("如果我遇到技术问题,应该如何寻求帮助?")
print(f"客服小助手: {answer2}")
# 场景3: 用户询问退款政策 (即使知识库里没有,模型也会基于其通用知识和我们设定的角色进行合理回答)
answer3 = await ask_customer_service("请问你们的退款政策是怎样的?")
print(f"客服小助手: {answer3}")
print("\n--- 智能客服对话模拟结束 ---")
TypeScript 代码:
// 确保 chatModel 和 chatCsTemplate 已经从上面的代码块中定义
// 如果你是单独运行此块,请确保先运行上面的初始化和模板定义代码
console.log("--- 智能客服小助手对话模拟 ---");
async function askCustomerService(question: string): Promise<string> {
/**
* 智能客服小助手提问函数。
* 它将用户的提问填充到 ChatPromptTemplate 中,然后调用 ChatModel 获取答案。
*/
console.log(`\n用户提问: ${question}`);
// 1. 格式化 PromptTemplate,生成 ChatModel 需要的消息列表
const messages = await chatCsTemplate.formatMessages({ user_query: question });
// 打印发送给模型的具体消息内容,方便调试
// console.log("--- 发送给 ChatModel 的消息 ---");
// for (const msg of messages) {
// console.log(` - ${msg._getType()}: ${msg.content}`);
// }
// console.log("-----------------------------");
// 2. 调用 ChatModel 获取响应
const response = await chatModel.invoke(messages);
// response 是一个 AIMessage 对象,它的 .content 属性就是 AI 生成的文本
return response.content;
}
// 模拟几次智能客服的对话
(async () => {
// 场景1: 用户询问产品功能
const answer1 = await askCustomerService("你们的产品有什么特色功能?");
console.log(`客服小助手: ${answer1}`);
// 场景2: 用户询问售后支持
const answer2 = await askCustomerService("如果我遇到技术问题,应该如何寻求帮助?");
console