第 11 期 | RAG 架构实战:检索增强生成的完美闭环
(申请发送: agentupdate)
🎯 本期学习目标
各位未来的 AI 大师们,系好安全带!在「LangChain 全栈大师课」的第一站,我们将直捣黄龙,触及 LangChain 的核心——大型语言模型(LLM)和提示模板(PromptTemplate)。这不只是一次理论学习,更是我们构建「智能客服知识库」项目基石的第一步。学完本期,你将:
- 透彻理解 LLM 在 AI 应用中的核心地位: 洞察智能客服小助手“大脑”的运作原理,知道它如何“思考”和“表达”。
- 精通 LangChain 中 LLM 和 ChatModel 的加载与调用: 学会如何让你的代码与顶尖的 AI 模型“对话”,并为客服项目注入智能。
- 掌握 PromptTemplate 的构建与高级技巧: 深入理解提示工程的艺术,学会为 LLM 编写清晰、高效、具备客服“人设”的指令。
- 能够结合 LLM 与 PromptTemplate: 为你的智能客服小助手搭建一个初步的、能理解用户意图并给出基础回应的“智能大脑”。
📖 原理解析
好了,各位,请允许我这个老兵先给大家泼一盆冷水,再奉上一碗热鸡汤。AI 领域日新月异,但万变不离其宗。今天,我们要聊的 LLM 和 PromptTemplate,就是这个“宗”里的“核心驱动力”。想象一下,你的智能客服小助手,它不是一个冰冷的机器,而是一个有思想、有逻辑、能沟通的“数字生命”。而这期,我们就是要为它安上“大脑”和“语言”。
1. LLM:智能客服的“超级大脑”
什么是 LLM? LLM,即 Large Language Model,大型语言模型。简单来说,它就是一个被海量文本数据训练过的、能理解和生成人类语言的巨型神经网络。你可以把它想象成一个阅读了人类所有已知书籍、网页、对话的超级学霸,它不仅记住了海量知识,更重要的是,它学会了语言的“模式”和“逻辑”。
在智能客服中,LLM 的作用是什么? 它是我们客服小助手的“大脑”。当用户提出问题时,LLM 负责:
- 理解用户意图: 无论用户用多么复杂的语言描述问题,LLM 都能尝试理解其核心诉求。
- 生成自然语言回复: 根据理解到的意图和提供的知识,生成流畅、准确、符合人类沟通习惯的回复。
- 知识推理: 在一定程度上,LLM 还能根据其通用知识进行推理,甚至在没有直接匹配的答案时,给出合理的推断。
LangChain 如何封装 LLM? LangChain 的设计哲学之一就是“抽象”。它把各种 LLM(无论是 OpenAI 的 GPT 系列、Google 的 Gemini、Anthropic 的 Claude 还是开源模型)都封装成统一的接口。这意味着,你只需要改动几行代码,就能轻松切换不同的 LLM,而不用重写整个应用逻辑。这对于我们构建一个灵活、可扩展的智能客服系统来说,简直是福音!
- LLMs: 传统的文本补全模型,输入文本,输出文本。
- ChatModels: 更专注于对话场景,输入一系列消息(System, Human, AI),输出 AI 消息。这正是我们客服项目所需的核心组件!它能更好地理解对话上下文和角色设定。
2. PromptTemplate:与 AI 沟通的“魔法咒语”
如果说 LLM 是大脑,那么 PromptTemplate 就是我们与这个大脑沟通的“语言”和“指令”。你不能指望 LLM 凭空知道你要它做什么。它需要清晰、明确、有结构的指引。
为什么 PromptTemplate 如此重要?
- 设定角色与行为: 你希望客服小助手是友好的?专业的?还是幽默的?PromptTemplate 可以为它设定一个“人设”。
- 提供上下文信息: 用户的问题往往需要结合具体的背景才能给出准确答案。例如,用户问“我的订单什么时候发货?”,小助手需要知道“哪个订单号?”。PromptTemplate 允许我们动态注入这些上下文。
- 指导输出格式: 你希望小助手回复一个总结?一个步骤列表?还是一个 JSON 对象?PromptTemplate 可以明确指导 LLM 的输出形式。
- 可复用性与标准化: 想象一下,每次都要手动拼接长长的指令,既容易出错又低效。PromptTemplate 允许我们定义可复用的模板,只需填充变量即可。
提示工程(Prompt Engineering)的核心思想: 这不仅仅是写几个字那么简单,它是一门艺术,也是一门科学。它的核心在于:如何用最简洁、最清晰、最有效的方式,引导 LLM 给出我们期望的、高质量的输出。 在智能客服场景中,这意味着我们需要精心设计提示,让小助手能够:
- 准确理解用户问题。
- 结合知识库(后续会讲)给出正确答案。
- 以友善、专业的客服语气进行回复。
- 避免“幻觉”和不负责任的回答。
LangChain 中的 PromptTemplate: LangChain 提供了 PromptTemplate 和 ChatPromptTemplate 两种核心组件。
PromptTemplate: 适用于简单的文本到文本生成。ChatPromptTemplate: 更适合多轮对话和角色扮演。它允许你定义不同角色的消息(系统消息、用户消息、AI 消息),这对于模拟客服对话至关重要。我们可以用SystemMessagePromptTemplate来设定客服小助手的“行为准则”和“专业素养”。
Mermaid 图解:智能客服小助手的工作流(初版)
让我们用一张图来直观地看看,LLM 和 PromptTemplate 是如何在智能客服项目中协同工作的。这好比是你的用户与客服小助手之间的“信息桥梁”。
graph TD
A[用户提出问题] --> B{PromptTemplate}
B -- 注入动态数据 (用户问题, 客服角色设定) --> C[构建完整的Prompt]
C --> D[LangChain ChatModel (LLM)]
D -- 理解并生成回复 --> E[智能客服小助手回复]
E --> F[将回复呈现给用户]
subgraph LangChain 核心
B
D
end
style A fill:#f9f,stroke:#333,stroke-width:2px
style F fill:#f9f,stroke:#333,stroke-width:2px
style B fill:#bbf,stroke:#333,stroke-width:2px
style C fill:#ccf,stroke:#333,stroke-width:2px
style D fill:#ddf,stroke:#333,stroke-width:2px
style E fill:#eef,stroke:#333,stroke-width:2px从图中可以看到,用户的问题并不是直接扔给 LLM 就完事了。它首先要经过 PromptTemplate 的“包装”和“引导”,被转换成 LLM 能够理解并高效处理的指令。这个“包装”的过程,就是我们赋予智能客服小助手“智慧”和“灵魂”的关键一步。
💻 实战代码演练
理论说得再天花乱坠,不如亲手敲几行代码来得实在!现在,让我们一起动手,为我们的「智能客服知识库」项目打下第一个地基。我们将从最基础的 LLM 调用开始,逐步引入 PromptTemplate,最终构建一个初步具备“智能”的客服小助手。
1. 环境准备
首先,确保你已经安装了 langchain 和 openai 库。如果你还没有,请运行:
pip install langchain langchain-openai
或者对于 TypeScript 项目:
npm install langchain @langchain/openai
并且,你需要设置你的 OpenAI API Key。强烈建议你使用环境变量来管理你的密钥,而不是直接写在代码里。
Python 设置环境变量:
export OPENAI_API_KEY="your_openai_api_key_here"
TypeScript 设置环境变量:
在项目根目录创建 .env 文件:
OPENAI_API_KEY="your_openai_api_key_here"
并在代码中加载:import 'dotenv/config';
2. 加载并与 ChatModel 交互 (Python & TypeScript)
我们首先让客服小助手拥有“开口”的能力。这里我们使用 ChatOpenAI,它是 LangChain 封装的 OpenAI 对话模型,非常适合我们的客服场景。
Python 代码示例
import os
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage
# 确保你的 OPENAI_API_KEY 环境变量已设置
# os.environ["OPENAI_API_KEY"] = "sk-..." # 不推荐直接写在这里,请使用环境变量
print("--- Python: ChatModel 基本交互 ---")
# 实例化 ChatModel
# temperature 参数控制模型输出的随机性,0 表示更确定性,1 表示更有创造性。
# 对于客服场景,我们通常希望回复准确且稳定,所以会设置较低的 temperature。
chat_model = ChatOpenAI(temperature=0.7, model_name="gpt-3.5-turbo")
# 模拟用户提问
user_query = "你们公司的退货政策是怎样的?"
print(f"\n用户提问: {user_query}")
# 调用 ChatModel,传入一个消息列表
# SystemMessage 用于设定模型的行为或角色,HumanMessage 是用户的输入。
messages = [
SystemMessage(content="你是一个友善且专业的智能客服助理。"),
HumanMessage(content=user_query),
]
# invoke 方法用于同步调用模型并获取回复
response = chat_model.invoke(messages)
print(f"客服小助手回复: {response.content}")
# 演示另一个问题,看看模型的连贯性(虽然这里没有历史记忆,但SystemMessage是持续的)
user_query_2 = "那如果商品有质量问题呢?"
print(f"\n用户提问: {user_query_2}")
messages_2 = [
SystemMessage(content="你是一个友善且专业的智能客服助理。"),
HumanMessage(content=user_query_2),
]
response_2 = chat_model.invoke(messages_2)
print(f"客服小助手回复: {response_2.content}")
TypeScript 代码示例
import 'dotenv/config'; // 加载 .env 文件中的环境变量
import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage, SystemMessage } from '@langchain/core/messages';
console.log("--- TypeScript: ChatModel 基本交互 ---");
// 实例化 ChatModel
// temperature 参数控制模型输出的随机性,0 表示更确定性,1 表示更有创造性。
// 对于客服场景,我们通常希望回复准确且稳定,所以会设置较低的 temperature。
const chatModel = new ChatOpenAI({
temperature: 0.7,
modelName: "gpt-3.5-turbo",
});
// 模拟用户提问
const userQuery = "你们公司的退货政策是怎样的?";
console.log(`\n用户提问: ${userQuery}`);
// 调用 ChatModel,传入一个消息列表
// SystemMessage 用于设定模型的行为或角色,HumanMessage 是用户的输入。
const messages = [
new SystemMessage("你是一个友善且专业的智能客服助理。"),
new HumanMessage(userQuery),
];
// invoke 方法用于同步调用模型并获取回复
chatModel.invoke(messages).then((response) => {
console.log(`客服小助手回复: ${response.content}`);
});
// 演示另一个问题
const userQuery2 = "那如果商品有质量问题呢?";
console.log(`\n用户提问: ${userQuery2}`);
const messages2 = [
new SystemMessage("你是一个友善且专业的智能客服助理。"),
new HumanMessage(userQuery2),
];
chatModel.invoke(messages2).then((response) => {
console.log(`客服小助手回复: ${response.content}`);
});
运行效果: 你会看到智能客服小助手针对你的问题给出了初步的、通用的回答。此时,它还没有我们的具体知识库,所以回答可能比较通用。但它已经有了“沟通”的能力!
3. PromptTemplate 的引入与优化 (Python & TypeScript)
现在,我们让客服小助手变得更“聪明”和“专业”。我们将使用 ChatPromptTemplate 来定义一个结构化的提示,让小助手在回答问题时带上特定的“人设”和“指令”。
Python 代码示例
import os
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate
print("\n--- Python: ChatPromptTemplate 引入与优化 ---")
chat_model = ChatOpenAI(temperature=0.7, model_name="gpt-3.5-turbo")
# 定义客服小助手的 System Prompt,设定其角色和行为准则
# 这是一个非常关键的步骤,它定义了小助手的“人格”
system_template = (
"你是一个专业的智能客服助理,负责解答客户关于公司产品和服务的疑问。"
"你的回答必须准确、简洁、友好,并且尽可能地帮助客户解决问题。"
"如果遇到你无法回答的问题,请礼貌地告知客户你将转接人工服务。"
"你的回答应以中文进行。"
)
system_message_prompt = SystemMessagePromptTemplate.from_template(system_template)
# 定义用户提问的 Prompt,其中 {user_question} 是一个变量,用于动态注入用户输入
human_message_prompt = HumanMessagePromptTemplate.from_template("{user_question}")
# 组合成一个 ChatPromptTemplate
# from_messages 方法接收一个消息模板列表
chat_prompt = ChatPromptTemplate.from_messages([
system_message_prompt,
human_message_prompt,
])
# 模拟用户提问
user_query = "请问你们支持哪些支付方式?"
print(f"\n用户提问: {user_query}")
# 将用户问题填充到 PromptTemplate 中,生成最终的 Prompt
# format_messages 方法会返回一个消息列表,可以直接传递给 ChatModel
formatted_prompt_messages = chat_prompt.format_messages(user_question=user_query)
# 调用 ChatModel
response = chat_model.invoke(formatted_prompt_messages)
print(f"客服小助手回复: {response.content}")
# 演示另一个问题,使用相同的 PromptTemplate 结构
user_query_2 = "我的订单号是 12345,请帮我查询一下物流信息。"
print(f"\n用户提问: {user_query_2}")
formatted_prompt_messages_2 = chat_prompt.format_messages(user_question=user_query_2)
response_2 = chat_model.invoke(formatted_prompt_messages_2)
print(f"客服小助手回复: {response_2.content}")
# 注意:这里小助手可能无法真正查询物流,因为我们还没有集成外部工具或知识库。
# 但它的回答会遵循我们设定的“无法回答时转接人工”的规则,体现了 Prompt Engineering 的作用。
TypeScript 代码示例
import 'dotenv/config';
import { ChatOpenAI } from '@langchain/openai';
import { ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate } from '@langchain/core/prompts';
console.log("\n--- TypeScript: ChatPromptTemplate 引入与优化 ---");
const chatModel = new ChatOpenAI({
temperature: 0.7,
modelName: "gpt-3.5-turbo",
});
// 定义客服小助手的 System Prompt,设定其角色和行为准则
// 这是一个非常关键的步骤,它定义了小助手的“人格”
const systemTemplate = (
"你是一个专业的智能客服助理,负责解答客户关于公司产品和服务的疑问。"
+ "你的回答必须准确、简洁、友好,并且尽可能地帮助客户解决问题。"
+ "如果遇到你无法回答的问题,请礼貌地告知客户你将转接人工服务。"
+ "你的回答应以中文进行。"
);
const systemMessagePrompt = SystemMessagePromptTemplate.fromTemplate(systemTemplate);
// 定义用户提问的 Prompt,其中 {user_question} 是一个变量,用于动态注入用户输入
const humanMessagePrompt = HumanMessagePromptTemplate.fromTemplate("{user_question}");
// 组合成一个 ChatPromptTemplate
// fromMessages 方法接收一个消息模板列表
const chatPrompt = ChatPromptTemplate.fromMessages([
systemMessagePrompt,
humanMessagePrompt,
]);
// 模拟用户提问
const userQuery = "请问你们支持哪些支付方式?";
console.log(`\n用户提问: ${userQuery}`);
// 将用户问题填充到 PromptTemplate 中,生成最终的 Prompt
// formatMessages 方法会返回一个消息列表,可以直接传递给 ChatModel
chatPrompt.formatMessages({ user_question: userQuery }).then((formattedPromptMessages) => {
// 调用 ChatModel
chatModel.invoke(formattedPromptMessages).then((response) => {
console.log(`客服小助手回复: ${response.content}`);
});
});
// 演示另一个问题,使用相同的 PromptTemplate 结构
const userQuery2 = "我的订单号是 12345,请帮我查询一下物流信息。";
console.log(`\n用户提问: ${user_query2}`);
chatPrompt.formatMessages({ user_question: userQuery2 }).then((formattedPromptMessages2) => {
chatModel.invoke(formattedPromptMessages2).then((response) => {
console.log(`客服小助手回复: ${response.content}`);
});
});
运行效果: 你会发现,这次客服小助手的回复更具“人情味”和“专业性”了。即使它不知道具体的物流信息,它也会按照我们 system_template 中设定的规则,礼貌地告知客户将转接人工服务。这就是 PromptTemplate 的魔力!它让你的 AI 不再是冷冰冰的机器,而是有“个性”和“行为规范”的智能体。
4. 链式操作 (Chains) 的初步体验 (Python & TypeScript)
LangChain 的核心思想之一就是“链 (Chain)”。我们可以将不同的组件(如 PromptTemplate 和 LLM)像乐高积木一样连接起来,形成一个处理流程。
Python 代码示例
import os
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate
from langchain.chains import LLMChain # 尽管LLMChain是旧版,但用于演示概念仍可
print("\n--- Python: Chains 初步体验 ---")
chat_model = ChatOpenAI(temperature=0.7, model_name="gpt-3.5-turbo")
# 仍然使用之前定义的 PromptTemplate
system_template = (
"你是一个专业的智能客服助理,负责解答客户关于公司产品和服务的疑问。"
"你的回答必须准确、简洁、友好,并且尽可能地帮助客户解决问题。"
"如果遇到你无法回答的问题,请礼貌地告知客户你将转接人工服务。"
"你的回答应以中文进行。"
)
system_message_prompt = SystemMessagePromptTemplate.from_template(system_template)
human_message_prompt = HumanMessagePromptTemplate.from_template("{user_question}")
chat_prompt = ChatPromptTemplate.from_messages([
system_message_prompt,
human_message_prompt,
])
# 创建一个简单的 Chain:将 chat_prompt 和 chat_model 连接起来
# 现代LangChain推荐使用LCEL (LangChain Expression Language) 的管道操作符 |
# 但这里为了演示旧版Chain概念,我们先用LLMChain
# 实际上,chat_prompt | chat_model 就能达到类似效果
chain = chat_prompt | chat_model
# 模拟用户提问
user_query = "你们最新的产品发布计划是什么?"
print(f"\n用户提问: {user_query}")
# 调用 Chain,传入变量
# invoke 方法会自动将 user_question 填充到 prompt 中,然后将结果传递给 LLM
response = chain.invoke({"user_question": user_query})
print(f"客服小助手回复: {response.content}")
user_query_2 = "我需要办理退款,流程是怎样的?"
print(f"\n用户提问: {user_query_2}")
response_2 = chain.invoke({"user_question": user_query_2})
print(f"客服小助手回复: {response_2.content}")
TypeScript 代码示例
import 'dotenv/config';
import { ChatOpenAI } from '@langchain/openai';
import { ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate } from '@langchain/core/prompts';
console.log("\n--- TypeScript: Chains 初步体验 ---");
const chatModel = new ChatOpenAI({
temperature: 0.7,
modelName: "gpt-3.5-turbo",
});
// 仍然使用之前定义的 PromptTemplate
const systemTemplate = (
"你是一个专业的智能客服助理,负责解答客户关于公司产品和服务的疑问。"
+ "你的回答必须准确、简洁、友好,并且尽可能地帮助客户解决问题。"
+ "如果遇到你无法回答的问题,请礼貌地告知客户你将转接人工服务。"
+ "你的回答应以中文进行。"
);
const systemMessagePrompt = SystemMessagePromptTemplate.fromTemplate(systemTemplate);
const humanMessagePrompt = HumanMessagePromptTemplate.fromTemplate("{user_question}");
const chatPrompt = ChatPromptTemplate.fromMessages([
systemMessagePrompt,
humanMessagePrompt,
]);
// 创建一个简单的 Chain:将 chatPrompt 和 chatModel 连接起来
// 在 LangChain JS 中,可以直接使用 .pipe() 方法或者 LCEL 的管道操作符 |
const chain = chatPrompt.pipe(chatModel);
// 模拟用户提问
const userQuery = "你们最新的产品发布计划是什么?";
console.log(`\n用户提问: ${userQuery}`);
// 调用 Chain,传入变量
// invoke 方法会自动将 user_question 填充到 prompt 中,然后将结果传递给 LLM
chain.invoke({ user_question: userQuery }).then((response) => {
console.log(`客服小助手回复: ${response.content}`);
});
const userQuery2 = "我需要办理退款,流程是怎样的?";
console.log(`\n用户提问: ${userQuery2}`);
chain.invoke({ user_question: userQuery2 }).then((response) => {
console.log(`客服小助手回复: ${response.content}`);
});
运行效果: 你会发现,使用 Chain 后,代码更加简洁、流程更加清晰。我们只需定义输入变量,Chain 会自动处理 Prompt 的格式化和 LLM 的调用。这正是 LangChain 致力于简化复杂 AI 应用开发的核心体现。
坑与避坑指南
好了,代码跑起来了,是不是感觉自己离 AI 大师又近了一步?别急着庆祝,作为一名经验丰富的 AI 架构师,我有义务提醒你,这条路上坑不少,但提前知道,就能少踩!
1. Prompt Engineering 的陷阱:别把 AI 当“读心术大师”
- 坑:模糊不清的指令。 你以为 LLM 能理解你的“言外之意”?想多了!“帮我解决问题”这种宽泛的指令,只会让 LLM 给出泛泛而谈的废话。
- 避坑指南: 具体、明确、可衡量! 告诉 LLM 你的目标、它扮演的角色、需要关注的重点、甚至期望的输出格式。例如,不是“帮我写个邮件”,而是“请你以公司客服代表的身份,给客户回复一封关于订单延迟的邮件,语气要诚恳,并提供解决方案和联系方式”。
- 坑:上下文不足导致“一本正经地胡说八道”。 LLM 缺乏我们人类的常识和实时信息,如果 Prompt 没有提供足够的背景,它很可能“幻觉”