第 04 期 | 记忆系统:赋予客服机器人的上下文记忆

更新于 2026/4/18

🎯 本期学习目标

嘿,各位未来的 AI 架构师们!我是你们的老朋友,一个在 AI 圈子里摸爬滚打十年的老兵,也是你们最热心的导师。今天,我们将正式开启这场 LangChain 的硬核之旅。别紧张,我们会从最基础的地方开始,手把手带你搭建起你的第一个生产级 AI 应用——我们的“智能客服知识库”。

通过本期学习,你将达成以下目标:

  1. 洞悉 LangChain 的核心价值:理解为什么 LangChain 是构建复杂 LLM 应用的“瑞士军刀”,它解决了哪些痛点。
  2. 掌握 LangChain 的基本组件:熟悉 LLMPromptTemplate,这两个是你与大模型对话的“嘴巴”和“剧本”。
  3. 构建智能客服原型:亲手搭建一个能接收用户问题并生成初步回复的客服助手,感受从零到一的快感。
  4. 初步建立开发心智:了解如何将抽象的 AI 能力具象化为我们客服项目中的特定功能,为后续复杂功能打下基础。

📖 原理解析

好了,废话不多说,我们直接上“道”。

想象一下,你现在要开发一个智能客服,用户问:“我的订单状态是什么?”或者“如何申请退货?”。你不能直接把用户的问题扔给大模型,然后指望它能立刻给出精准、符合公司政策的答案,对吧?大模型虽然强大,但它没有你的业务知识,也没有扮演客服角色的意识。

这就是 LangChain 大显身手的地方。它不是一个大模型本身,而是一个编排框架,一个帮你把大模型、数据源、外部工具等等这些“乐器”组织起来,共同演奏一曲美妙乐章的“指挥家”。

在我们的“智能客服知识库”项目中,LangChain 的核心价值在于:

  1. 结构化输入:用户的问题千奇百怪,LangChain 帮助我们把这些问题“翻译”成大模型能理解、能有效处理的指令。
  2. 统一接口:无论是 OpenAI 的 GPT 系列,还是 Google 的 Gemini,抑或是本地部署的 Llama 2,LangChain 都能用一套统一的接口去调用它们,让你随时切换,无需重构代码。
  3. 模块化设计:它把大模型应用的开发过程拆解成一个个独立的、可复用的组件(比如 LLMPromptTemplateChainMemoryTool 等),就像乐高积木一样,你可以自由组合,构建出无限可能。

本期,我们聚焦两个最基础、但也最重要的组件:

  • 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[智能客服回复用户]

原理拆解

  1. 用户输入:用户在客服界面提出问题。
  2. PromptTemplate 加工:我们的 PromptTemplate 会预先定义好客服助手的“人设”(比如“你是一个专业的智能客服助手,你的职责是解答用户关于产品和服务的疑问,并提供解决方案。”)以及输出格式要求。然后,它会把用户的具体问题填充到这个预设的模板中。
  3. 生成完整 Prompt:经过 PromptTemplate 处理后,一个结构化、包含角色设定和具体问题的完整提示词就诞生了。
  4. LLM 调用:这个完整的提示词会被传递给 LLM 组件。LLM 负责与实际的大模型(例如 OpenAI 的 gpt-3.5-turbo)进行通信,发送请求并接收响应。
  5. 大模型输出:大模型根据接收到的提示词,生成相应的回复。
  6. 智能客服回复:智能客服将大模型的回复展示给用户。

看到没?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 的 ChatOpenAIPromptTemplate 生成回复。

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}")

代码解析

  1. ChatOpenAI: 我们实例化了一个 ChatOpenAI 对象,指定了模型名称 (gpt-3.5-turbo-0125 是一个较新的版本,推荐使用) 和 temperaturetemperature 决定了模型回复的“随机性”或“创造性”,对于客服场景,我们通常希望它更稳定、更准确,所以设为 0.5 或更低。
  2. SystemMessagePromptTemplate: 这是定义客服助手“人格”的关键。我们通过一个模板,告诉大模型它是一个“专业的智能客服助手”,并且定义了它的职责和回复风格。这就像给客服助手穿上了工作服,明确了它的岗位职责。
  3. HumanMessagePromptTemplate: 这是用户输入内容的占位符。{user_question} 会在实际调用时被用户的具体问题替换。
  4. ChatPromptTemplate.from_messages: 将系统提示词和用户提示词组合起来,形成一个完整的对话流程。LangChain 会自动处理这些消息的格式,使其符合大模型 API 的要求。
  5. chat_prompt.format_messages: 在 get_response 方法中,我们使用这个方法将用户的问题填充到模板中,生成最终要发送给大模型的 Message 对象列表。
  6. self.llm.invoke(formatted_prompt): 这是真正调用大模型的地方。invoke 方法接收格式化后的 prompt,发送给 ChatOpenAI 实例背后的大模型,并返回大模型的响应。
  7. 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 应用。

坑与避坑指南

作为一名老司机,我见过太多新手在这些地方栽跟头了,所以提前给你打个预防针:

  1. API Key 配置问题:这是最常见、最基础的错误。OPENAI_API_KEY 环境变量一定要正确设置!如果你在 IDE 中运行,确保 IDE 的运行环境也加载了这个变量。有时直接在终端 export 后,IDE 启动的进程可能没继承到。最稳妥的方式是在代码中通过 dotenv 库加载(生产环境不推荐),或者确保你的部署环境正确配置。
  2. 模型选择与成本gpt-3.5-turbo 是一个性价比很高的选择,但如果你追求极致的性能和最新的能力,可以尝试 gpt-4-turbo 或其他更强大的模型。但请注意,模型越强大,通常费用也越高。在开发初期,从 gpt-3.5-turbo 开始,逐步升级是明智之举。
  3. Prompt Engineering 的初步思考
    • 角色设定清晰:你希望 AI 扮演什么角色?客服、技术专家、销售?越清晰,AI 的回复越符合预期。
    • 指令明确:你希望 AI 怎么做?回答问题、提供建议、总结?给出明确的指令。
    • 限制范围:告诉 AI 它不能做什么,或者在什么情况下应该如何响应(比如“如果问题超出知识范围,请礼貌告知”)。这对于避免 AI “胡说八道”或给出不负责任的答案至关重要。
    • 迭代优化:Prompt 不是一蹴而就的,需要不断测试、调整,才能达到最佳效果。把它当作你与 AI 沟通的艺术,而非一次性的任务。
  4. 同步/异步调用:在 Python 中,llm.invoke() 是同步调用。如果你的应用需要处理大量并发请求,或者不想阻塞主线程,你需要考虑使用异步调用 llm.ainvoke()。TypeScript/JavaScript 天然支持异步,所以 await agent.getResponse() 是标准做法。
  5. 输出格式的不可控性:尽管我们通过 SystemMessagePromptTemplate 设定了回复风格,但大模型毕竟不是程序,它仍然会有一定的自由度。不要期望它每次都百分百按照你的精确格式来。如果需要严格的结构化输出,我们后续会学习更高级的技巧(比如使用 Pydantic 输出解析器)。

📝 本期小结

恭喜你,迈出了 LangChain 学习的第一步!

在本期中,我们:

  • 深入理解了 LangChain 作为 LLM 编排框架的核心价值,以及它如何让我们的“智能客服知识库”项目开发变得更简单、更高效。
  • 掌握了 LangChain 最基础但最重要的两个组件:LLM (特别是 ChatOpenAI) 和 PromptTemplate
  • 亲手构建了一个能够接收用户问题,并根据预设客服角色生成回复的智能客服原型。
  • 通过实战代码,直观感受了如何将抽象的 AI 能力转化为具体的应用功能。
  • 提前了解了在开发过程中可能遇到的“坑”以及相应的“避坑指南”。

这只是冰山一角!目前的客服助手还很“傻”,它没有任何记忆,每次提问都像第一次见面。它也没有外部知识,只能凭借大模型本身的通用知识进行回复。

在接下来的课程中,我们将逐步为它添加“记忆”,让它能记住用户的上下文;为它连接“知识库”,让它能回答我们公司特有的产品和服务问题;甚至为它赋予“工具”,让它能查询订单、发送邮件等等。

准备好了吗?下一期,我们将深入探索 LangChain 的 Chain 机制,开始编织我们客服助手的复杂逻辑!Stay curious, stay hungry!