第 04 期 | 记忆系统:赋予客服机器人的上下文记忆
🎯 本期学习目标
嘿,各位未来的 AI 架构师们!我是你们的老朋友,一个在 AI 圈子里摸爬滚打十年的老兵,也是你们最热心的导师。今天,我们将正式开启这场 LangChain 的硬核之旅。别紧张,我们会从最基础的地方开始,手把手带你搭建起你的第一个生产级 AI 应用——我们的“智能客服知识库”。
通过本期学习,你将达成以下目标:
- 洞悉 LangChain 的核心价值:理解为什么 LangChain 是构建复杂 LLM 应用的“瑞士军刀”,它解决了哪些痛点。
- 掌握 LangChain 的基本组件:熟悉
LLM和PromptTemplate,这两个是你与大模型对话的“嘴巴”和“剧本”。 - 构建智能客服原型:亲手搭建一个能接收用户问题并生成初步回复的客服助手,感受从零到一的快感。
- 初步建立开发心智:了解如何将抽象的 AI 能力具象化为我们客服项目中的特定功能,为后续复杂功能打下基础。
📖 原理解析
好了,废话不多说,我们直接上“道”。
想象一下,你现在要开发一个智能客服,用户问:“我的订单状态是什么?”或者“如何申请退货?”。你不能直接把用户的问题扔给大模型,然后指望它能立刻给出精准、符合公司政策的答案,对吧?大模型虽然强大,但它没有你的业务知识,也没有扮演客服角色的意识。
这就是 LangChain 大显身手的地方。它不是一个大模型本身,而是一个编排框架,一个帮你把大模型、数据源、外部工具等等这些“乐器”组织起来,共同演奏一曲美妙乐章的“指挥家”。
在我们的“智能客服知识库”项目中,LangChain 的核心价值在于:
- 结构化输入:用户的问题千奇百怪,LangChain 帮助我们把这些问题“翻译”成大模型能理解、能有效处理的指令。
- 统一接口:无论是 OpenAI 的 GPT 系列,还是 Google 的 Gemini,抑或是本地部署的 Llama 2,LangChain 都能用一套统一的接口去调用它们,让你随时切换,无需重构代码。
- 模块化设计:它把大模型应用的开发过程拆解成一个个独立的、可复用的组件(比如
LLM、PromptTemplate、Chain、Memory、Tool等),就像乐高积木一样,你可以自由组合,构建出无限可能。
本期,我们聚焦两个最基础、但也最重要的组件:
LLM(Large Language Model):这是你与大模型交互的基石。它封装了对各种大模型的调用逻辑,让你不必关心底层的 API 请求细节,只需要告诉它你要用哪个模型,然后传入你的输入就行。PromptTemplate(提示词模板):这是你给大模型“写剧本”的地方。它允许你定义一个带有占位符的模板,然后动态地填充内容,生成最终发送给大模型的提示词。这对于确保大模型扮演特定角色、遵循特定输出格式至关重要,尤其是在我们的客服场景中,我们希望客服助手能以专业、友好的语气回答问题。
现在,让我们通过一个简化的 Mermaid 图来理解,在我们的智能客服项目中,这两个组件是如何协作,完成一次最基本的问答流程的。
graph TD
A[用户输入: "我有一个关于订单的问题"] --> B{PromptTemplate: 设定客服角色与问题格式}
B --> C[生成完整Prompt: "你是一个专业的智能客服。请根据用户的问题,提供帮助。用户问题:'我有一个关于订单的问题'"]
C --> D[LLM: 调用大模型 (e.g., GPT-3.5-turbo)]
D --> E[大模型输出: "您好!请问您具体想了解订单的哪方面信息?"]
E --> F[智能客服回复用户]原理拆解:
- 用户输入:用户在客服界面提出问题。
- PromptTemplate 加工:我们的
PromptTemplate会预先定义好客服助手的“人设”(比如“你是一个专业的智能客服助手,你的职责是解答用户关于产品和服务的疑问,并提供解决方案。”)以及输出格式要求。然后,它会把用户的具体问题填充到这个预设的模板中。 - 生成完整 Prompt:经过
PromptTemplate处理后,一个结构化、包含角色设定和具体问题的完整提示词就诞生了。 - LLM 调用:这个完整的提示词会被传递给
LLM组件。LLM负责与实际的大模型(例如 OpenAI 的gpt-3.5-turbo)进行通信,发送请求并接收响应。 - 大模型输出:大模型根据接收到的提示词,生成相应的回复。
- 智能客服回复:智能客服将大模型的回复展示给用户。
看到没?LangChain 就像一个熟练的厨师,把用户杂乱无章的“食材”(问题)经过“菜谱”(PromptTemplate)的指导,再送入“烤箱”(LLM),最终端出一道美味的“菜肴”(答案)。这比你直接把生肉扔进烤箱要强太多了!
💻 实战代码演练 (客服项目中的具体应用)
好了,原理讲得再天花乱坠,不如亲手敲代码来得实在。现在,我们来搭建我们智能客服的“Hello World”版本。我们将用 Python 来演示,因为它在 AI 领域更流行,但稍后我也会给出 TypeScript 的代码示例,让你感受一下 LangChain 的跨语言魅力。
环境准备
首先,你需要安装 LangChain 和你选择的大模型提供商的客户端。这里我们以 OpenAI 为例。
# Python 环境
pip install langchain-openai
# TypeScript / JavaScript 环境
npm install langchain @langchain/openai
别忘了设置你的 OpenAI API Key。最安全的方式是将其设置为环境变量。
# 在你的终端中执行 (macOS/Linux)
export OPENAI_API_KEY="sk-YOUR_OPENAI_API_KEY"
# 在你的终端中执行 (Windows PowerShell)
$env:OPENAI_API_KEY="sk-YOUR_OPENAI_API_KEY"
Python 代码实战
我们将构建一个简单的 SimpleSupportAgent 类,它能接收用户问题,并通过 LangChain 的 ChatOpenAI 和 PromptTemplate 生成回复。
import os
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, HumanMessagePromptTemplate, SystemMessagePromptTemplate
from langchain_core.messages import HumanMessage, SystemMessage
class SimpleSupportAgent:
"""
一个基础的智能客服代理,用于演示LangChain的LLM和PromptTemplate组件。
它能够接收用户提问,并根据预设的客服角色生成回复。
"""
def __init__(self, model_name: str = "gpt-3.5-turbo-0125", temperature: float = 0.7):
"""
初始化智能客服代理。
:param model_name: 使用的OpenAI模型名称。
:param temperature: 模型的创造性程度(0-1之间,越高越有创造性)。
"""
# 检查API Key是否已设置
if not os.getenv("OPENAI_API_KEY"):
raise ValueError("OPENAI_API_KEY 环境变量未设置。请先设置您的OpenAI API Key。")
# 初始化ChatOpenAI模型实例
# 这是我们客服助手的“大脑”,负责理解和生成文本
self.llm = ChatOpenAI(model_name=model_name, temperature=temperature)
# 定义客服助手的系统级提示词模板
# 这就像给客服助手设定了一个“剧本”,告诉它应该扮演什么角色
self.system_prompt = SystemMessagePromptTemplate.from_template(
"你是一个专业的智能客服助手,你的职责是解答用户关于产品和服务的疑问,并提供解决方案。 "
"你的回答应该友好、清晰、准确,并尽量简洁。如果问题超出你的知识范围,请礼貌地告知用户。"
)
# 定义用户消息的提示词模板
# 这是用户输入的部分,会动态填充到完整的Prompt中
self.human_prompt = HumanMessagePromptTemplate.from_template("{user_question}")
# 将系统提示词和用户提示词组合成一个完整的聊天提示词模板
# 这是我们给大模型看的完整“剧本”,包含角色设定和用户具体问题
self.chat_prompt = ChatPromptTemplate.from_messages([
self.system_prompt,
self.human_prompt
])
def get_response(self, user_question: str) -> str:
"""
根据用户问题生成客服回复。
:param user_question: 用户提出的问题。
:return: 客服助手的回复。
"""
print(f"\n--- 用户提问 ---\n{user_question}")
# 使用chat_prompt模板格式化用户问题,生成最终的Prompt
# 这一步是把用户问题填充到我们预设的“剧本”里
formatted_prompt = self.chat_prompt.format_messages(user_question=user_question)
print(f"\n--- 发送给大模型的Prompt (格式化后) ---\n{formatted_prompt}")
# 调用LLM模型获取回复
# 大模型根据格式化后的Prompt生成答案
response = self.llm.invoke(formatted_prompt)
# 提取回复内容
ai_response_content = response.content
print(f"\n--- 智能客服回复 ---\n{ai_response_content}")
return ai_response_content
# --- 模拟运行我们的智能客服 ---
if __name__ == "__main__":
try:
# 实例化我们的智能客服代理
# 你可以尝试不同的模型或temperature
agent = SimpleSupportAgent(model_name="gpt-3.5-turbo", temperature=0.5)
# 模拟用户提问
agent.get_response("我的订单号是 123456789,请问什么时候能发货?")
agent.get_response("你们公司最新的产品是什么?有什么特点?")
agent.get_response("如何申请退货?需要提供哪些资料?")
agent.get_response("请给我讲一个笑话。") # 测试超出知识范围的情况
except ValueError as e:
print(f"错误:{e}")
print("请确保已设置 OPENAI_API_KEY 环境变量。")
except Exception as e:
print(f"发生未知错误:{e}")
代码解析:
ChatOpenAI: 我们实例化了一个ChatOpenAI对象,指定了模型名称 (gpt-3.5-turbo-0125是一个较新的版本,推荐使用) 和temperature。temperature决定了模型回复的“随机性”或“创造性”,对于客服场景,我们通常希望它更稳定、更准确,所以设为0.5或更低。SystemMessagePromptTemplate: 这是定义客服助手“人格”的关键。我们通过一个模板,告诉大模型它是一个“专业的智能客服助手”,并且定义了它的职责和回复风格。这就像给客服助手穿上了工作服,明确了它的岗位职责。HumanMessagePromptTemplate: 这是用户输入内容的占位符。{user_question}会在实际调用时被用户的具体问题替换。ChatPromptTemplate.from_messages: 将系统提示词和用户提示词组合起来,形成一个完整的对话流程。LangChain 会自动处理这些消息的格式,使其符合大模型 API 的要求。chat_prompt.format_messages: 在get_response方法中,我们使用这个方法将用户的问题填充到模板中,生成最终要发送给大模型的Message对象列表。self.llm.invoke(formatted_prompt): 这是真正调用大模型的地方。invoke方法接收格式化后的prompt,发送给ChatOpenAI实例背后的大模型,并返回大模型的响应。response.content: 从大模型的响应对象中提取出我们需要的文本内容。
运行这段代码,你会看到智能客服如何根据你设定的角色,对不同的问题给出相应的回复。即使是超出知识范围的问题,它也会礼貌地回应,这正是 SystemMessagePromptTemplate 发挥作用的地方!
TypeScript / JavaScript 代码实战 (可选)
如果你是前端开发者或者喜欢 TypeScript,这里是对应的实现:
import { ChatOpenAI } from "@langchain/openai";
import { ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate } from "@langchain/core/prompts";
import { BaseMessage } from "@langchain/core/messages";
// 确保你已经设置了 OPENAI_API_KEY 环境变量
// 例如:process.env.OPENAI_API_KEY = "sk-YOUR_OPENAI_API_KEY";
class SimpleSupportAgent {
private llm: ChatOpenAI;
private chatPrompt: ChatPromptTemplate;
constructor(modelName: string = "gpt-3.5-turbo-0125", temperature: number = 0.7) {
if (!process.env.OPENAI_API_KEY) {
throw new Error("OPENAI_API_KEY environment variable is not set. Please set your OpenAI API Key.");
}
this.llm = new ChatOpenAI({
modelName: modelName,
temperature: temperature,
});
// 定义客服助手的系统级提示词模板
const systemPrompt = SystemMessagePromptTemplate.fromTemplate(
"你是一个专业的智能客服助手,你的职责是解答用户关于产品和服务的疑问,并提供解决方案。 " +
"你的回答应该友好、清晰、准确,并尽量简洁。如果问题超出你的知识范围,请礼貌地告知用户。"
);
// 定义用户消息的提示词模板
const humanPrompt = HumanMessagePromptTemplate.fromTemplate("{user_question}");
// 将系统提示词和用户提示词组合成一个完整的聊天提示词模板
this.chatPrompt = ChatPromptTemplate.fromMessages([
systemPrompt,
humanPrompt,
]);
}
async getResponse(userQuestion: string): Promise<string> {
console.log(`\n--- 用户提问 ---\n${userQuestion}`);
// 使用chatPrompt模板格式化用户问题,生成最终的Prompt
const formattedPrompt: BaseMessage[] = await this.chatPrompt.formatMessages({
user_question: userQuestion,
});
console.log(`\n--- 发送给大模型的Prompt (格式化后) ---\n`, formattedPrompt);
// 调用LLM模型获取回复
const response = await this.llm.invoke(formattedPrompt);
// 提取回复内容
const aiResponseContent = response.content;
console.log(`\n--- 智能客服回复 ---\n${aiResponseContent}`);
return String(aiResponseContent); // 确保返回字符串类型
}
}
// --- 模拟运行我们的智能客服 ---
async function main() {
try {
// 实例化我们的智能客服代理
const agent = new SimpleSupportAgent("gpt-3.5-turbo", 0.5);
// 模拟用户提问
await agent.getResponse("我的订单号是 123456789,请问什么时候能发货?");
await agent.getResponse("你们公司最新的产品是什么?有什么特点?");
await agent.getResponse("如何申请退货?需要提供哪些资料?");
await agent.getResponse("请给我讲一个笑话。"); // 测试超出知识范围的情况
} catch (e: any) {
console.error(`错误:${e.message}`);
console.error("请确保已设置 OPENAI_API_KEY 环境变量。");
}
}
main();
TypeScript 的代码逻辑与 Python 版本几乎完全一致,只是语法上有所差异。这正是 LangChain 设计的精妙之处:它提供了跨语言的统一抽象,让你无论使用哪种语言,都能以相似的思维模式去构建 AI 应用。
坑与避坑指南
作为一名老司机,我见过太多新手在这些地方栽跟头了,所以提前给你打个预防针:
- API Key 配置问题:这是最常见、最基础的错误。
OPENAI_API_KEY环境变量一定要正确设置!如果你在 IDE 中运行,确保 IDE 的运行环境也加载了这个变量。有时直接在终端export后,IDE 启动的进程可能没继承到。最稳妥的方式是在代码中通过dotenv库加载(生产环境不推荐),或者确保你的部署环境正确配置。 - 模型选择与成本:
gpt-3.5-turbo是一个性价比很高的选择,但如果你追求极致的性能和最新的能力,可以尝试gpt-4-turbo或其他更强大的模型。但请注意,模型越强大,通常费用也越高。在开发初期,从gpt-3.5-turbo开始,逐步升级是明智之举。 - Prompt Engineering 的初步思考:
- 角色设定清晰:你希望 AI 扮演什么角色?客服、技术专家、销售?越清晰,AI 的回复越符合预期。
- 指令明确:你希望 AI 怎么做?回答问题、提供建议、总结?给出明确的指令。
- 限制范围:告诉 AI 它不能做什么,或者在什么情况下应该如何响应(比如“如果问题超出知识范围,请礼貌告知”)。这对于避免 AI “胡说八道”或给出不负责任的答案至关重要。
- 迭代优化:Prompt 不是一蹴而就的,需要不断测试、调整,才能达到最佳效果。把它当作你与 AI 沟通的艺术,而非一次性的任务。
- 同步/异步调用:在 Python 中,
llm.invoke()是同步调用。如果你的应用需要处理大量并发请求,或者不想阻塞主线程,你需要考虑使用异步调用llm.ainvoke()。TypeScript/JavaScript 天然支持异步,所以await agent.getResponse()是标准做法。 - 输出格式的不可控性:尽管我们通过
SystemMessagePromptTemplate设定了回复风格,但大模型毕竟不是程序,它仍然会有一定的自由度。不要期望它每次都百分百按照你的精确格式来。如果需要严格的结构化输出,我们后续会学习更高级的技巧(比如使用 Pydantic 输出解析器)。
📝 本期小结
恭喜你,迈出了 LangChain 学习的第一步!
在本期中,我们:
- 深入理解了 LangChain 作为 LLM 编排框架的核心价值,以及它如何让我们的“智能客服知识库”项目开发变得更简单、更高效。
- 掌握了 LangChain 最基础但最重要的两个组件:
LLM(特别是ChatOpenAI) 和PromptTemplate。 - 亲手构建了一个能够接收用户问题,并根据预设客服角色生成回复的智能客服原型。
- 通过实战代码,直观感受了如何将抽象的 AI 能力转化为具体的应用功能。
- 提前了解了在开发过程中可能遇到的“坑”以及相应的“避坑指南”。
这只是冰山一角!目前的客服助手还很“傻”,它没有任何记忆,每次提问都像第一次见面。它也没有外部知识,只能凭借大模型本身的通用知识进行回复。
在接下来的课程中,我们将逐步为它添加“记忆”,让它能记住用户的上下文;为它连接“知识库”,让它能回答我们公司特有的产品和服务问题;甚至为它赋予“工具”,让它能查询订单、发送邮件等等。
准备好了吗?下一期,我们将深入探索 LangChain 的 Chain 机制,开始编织我们客服助手的复杂逻辑!Stay curious, stay hungry!