第 10 期 | 检索技巧 (Retrievers):让知识触手可及

⏱ 预计阅读 20 分钟 更新于 2026/5/7
💡 进群学习加 wx: agentupdate
(申请发送: agentupdate)

各位未来的 AI 大师们,大家好!我是你们的导师。很高兴能和大家一起踏上这段激动人心的 LangChain 全栈开发之旅。十年磨一剑,我见证了 AI 从实验室走向千家万户,而现在,是时候让你亲手去创造了!

我们这门《LangChain 全栈大师课》不是那种只讲理论不落地的“纸上谈兵”。我们将以一个贯穿始终的**「智能客服知识库 (Intelligent Support Copilot)」**项目为主线,从零开始,手把手带你打造一个生产级的 AI 应用。每一期,我们都会围绕这个项目,将最前沿的 LangChain 技术,像搭乐高一样,一块一块地拼接到我们的客服小助手身上,让它从一个简单的“应声虫”逐步进化为能读懂人心、解决问题的“超级大脑”。

本期的副标题是:深入理解 LangChain 核心组件与智能客服的基石。我们将奠定整个项目的基石,让你对 LangChain 有一个鸟瞰式的理解,并构建我们智能客服助手的第一个原型——一个能理解并总结客户问题的“倾听者”。


🎯 本期学习目标

经过本期的洗礼,你将能够:

  1. 洞察 LangChain 核心价值:理解 LangChain 如何简化 LLM 应用开发,以及它在构建复杂 AI 系统中的不可替代性。
  2. 掌握 LangChain 基石组件:熟练运用 LLM(语言模型)、PromptTemplate(提示词模板)和 OutputParser(输出解析器)这些核心构件。
  3. 搭建智能客服原型:亲手构建一个能接收客户问题并进行精准总结的智能客服小助手,迈出实战的第一步。
  4. 识别潜在开发陷阱:提前了解 LangChain 开发中的常见误区与最佳实践,少走弯路,直抵成功。

📖 原理解析

好了,少年们,坐稳了!我们现在要深入 LangChain 的核心世界。你可能会问,市面上那么多 LLM 库,为什么偏偏要学 LangChain?我的答案很简单:LangChain 不仅仅是一个库,它是一个框架,一个生态系统,一个能让你把 LLM 的强大能力真正工程化、产品化的利器。

想象一下,你的智能客服小助手,未来不仅要回答问题,还要查询订单、生成邮件、甚至与外部系统交互。如果直接用原始的 LLM API,你很快就会陷入“意大利面条式代码”的泥潭。而 LangChain,就像一个精密的管弦乐队指挥,它能帮你把各种复杂的 LLM 调用、数据处理、外部工具集成,优雅地编排起来。

对于我们的智能客服知识库项目来说,LangChain 的核心价值体现在:

  • 模块化与可组合性:把 LLM 应用拆解成一个个可复用的组件(比如,一个组件负责理解用户意图,另一个组件负责查询知识库,再一个负责生成回复),然后像乐高一样自由组合。
  • 链式思维 (Chains):将这些组件串联起来,形成一个有序的工作流,一步步处理用户的请求。
  • 数据流管理:在各个组件之间高效、结构化地传递数据,确保信息不丢失、不混乱。

LangChain 的三大基石

在构建我们的第一个智能客服原型时,我们将主要接触 LangChain 的三个最基础,但也是最重要的概念:

  1. LLM (Language Models) - 语言模型

    • 道 (The Way):这是我们智能客服的“大脑”。它负责理解、生成自然语言。无论是 OpenAI 的 GPT 系列,还是 Anthropic 的 Claude,亦或是本地部署的 Llama 2,它们都是你这个 AI 系统的核心计算单元。LangChain 提供了一层抽象,让你能轻松切换不同的 LLM,而无需修改太多业务逻辑。
    • 术 (The Art):在 LangChain 中,LLM 被封装成一个接口,你只需要实例化它,然后就可以像调用函数一样给它输入文本,它就会给你返回文本。
  2. PromptTemplate - 提示词模板

    • 道 (The Way):如果说 LLM 是大脑,那 PromptTemplate 就是给大脑下的“指令书”。你不能指望 LLM 凭空知道你要它做什么。你需要清晰、明确、结构化地告诉它。PromptTemplate 允许你预定义好指令的骨架,然后动态地填充变量,就像填空题一样。这对于确保智能客服的回答风格、准确性、一致性至关重要。
    • 术 (The Art):它接收用户输入(比如客户的问题),结合预设的系统角色和指令,生成一个完整的、准备喂给 LLM 的提示词。
  3. OutputParser - 输出解析器

    • 道 (The Way):LLM 的原始输出通常是自由格式的文本,而我们的应用程序往往需要结构化的数据来进一步处理(比如,从客服总结中提取订单号、问题类型等)。OutputParser 的作用就是把 LLM 生成的“大白话”翻译成机器能理解的 JSON、列表或其他特定格式。这是连接 LLM 与后续业务逻辑的关键桥梁。
    • 术 (The Art):它接收 LLM 的文本输出,并尝试按照预设的规则将其解析成我们需要的格式。

智能客服原型工作流 (Mermaid 图解)

现在,让我们用一张图来清晰地展示,当一个客户提出问题时,我们的第一个智能客服小助手是如何工作的:

graph TD
    A[用户提问: "我的订单#123还没发货"] --> B(PromptTemplate: 构造指令)
    B -- 填充变量 {customer_query} --> C{完整提示词: "你是一个客服助手...总结问题: 我的订单#123还没发货"}
    C --> D[LLM (e.g., GPT-3.5-Turbo): 理解并生成总结]
    D -- 原始文本输出 --> E(OutputParser: 解析为结构化数据或纯文本)
    E -- 总结后的问题 --> F[智能客服回复: "客户的问题是关于订单#123未发货"]

    style A fill:#f9f,stroke:#333,stroke-width:2px,color:#333
    style F fill:#bbf,stroke:#333,stroke-width:2px,color:#333
    style B fill:#e0f7fa,stroke:#0097a7,stroke-width:1px,color:#333
    style C fill:#fff3e0,stroke:#ff8f00,stroke-width:1px,color:#333
    style D fill:#e8f5e9,stroke:#4caf50,stroke-width:1px,color:#333
    style E fill:#ffebee,stroke:#d32f2f,stroke-width:1px,color:#333

图解说明:

  1. 用户提问:客户输入他们的疑问,这是我们流程的起点。
  2. PromptTemplate (构造指令):这个组件接收用户的原始问题,并将其嵌入到我们预设的提示词模板中。这个模板会告诉 LLM 它的角色(智能客服助手)、任务(总结问题)以及如何输出。
  3. 完整提示词:经过 PromptTemplate 处理后,一个包含所有指令和用户问题的完整提示词被生成,准备发送给 LLM。
  4. LLM (语言模型):我们的“大脑”接收到提示词后,开始处理。它会根据指令和自身强大的语言理解能力,生成对客户问题的总结。
  5. OutputParser (解析输出):LLM 的输出是纯文本。OutputParser 负责对其进行处理。在本期,我们可能只是简单地返回纯文本,但在后续课程中,它会变得更加智能,将文本解析成 JSON 等结构化数据。
  6. 智能客服回复:最终,经过解析的总结文本被呈现给用户或内部客服人员,完成了本次交互。

这个简单的链式结构,就是 LangChain 的精髓。它将复杂的 AI 任务分解成可管理、可组合的步骤,让你能清晰地看到数据流向和处理逻辑。


💻 实战代码演练 (客服项目中的具体应用)

理论讲完了,是时候撸起袖子,把这些概念变成我们智能客服小助手的第一行代码了!

项目需求:智能客服问题总结器

我们的智能客服知识库项目的第一步,是让小助手能够准确、简洁地总结客户提出的问题。这听起来简单,但却是后续所有复杂功能(比如意图识别、知识库检索、工单创建)的基础。一个好的总结能帮助客服人员快速理解问题核心,也能为后续的自动化处理提供清晰的输入。

我们将使用 Python 来进行实战演练,并提供 TypeScript 的代码片段作为参考。

环境准备

在开始之前,请确保你已经安装了 Python (推荐 3.9+) 和 npm/yarn (如果使用 TypeScript)。

1. 创建项目目录并安装依赖:

# 创建项目文件夹
mkdir intelligent-support-copilot
cd intelligent-support-copilot

# 创建并激活虚拟环境 (Python 最佳实践)
python -m venv venv
source venv/bin/activate # macOS/Linux
# 或 venv\Scripts\activate # Windows

# 安装 LangChain 核心库、OpenAI 接口库和 dotenv
pip install langchain langchain-openai python-dotenv

# 如果你同时想看 TypeScript 示例 (可选)
# mkdir ts-example
# cd ts-example
# npm init -y
# npm install langchain @langchain/openai @langchain/core dotenv
# npm install -D typescript @types/node ts-node
# cd .. # 返回项目根目录

2. 设置 OpenAI API Key:

在项目根目录下创建一个 .env 文件,并填入你的 OpenAI API Key。切记不要将 API Key 直接写在代码中或提交到版本控制!

OPENAI_API_KEY="sk-YOUR_OPENAI_API_KEY_HERE"

Python 实战代码

创建一个 main.py 文件:

import os
from dotenv import load_dotenv

# 从 langchain_openai 导入 ChatOpenAI,用于与 OpenAI 模型交互
from langchain_openai import ChatOpenAI
# 从 langchain_core.prompts 导入 ChatPromptTemplate,用于创建提示词模板
from langchain_core.prompts import ChatPromptTemplate
# 从 langchain_core.output_parsers 导入 StrOutputParser,用于将 LLM 输出解析为字符串
from langchain_core.output_parsers import StrOutputParser
# 从 langchain_core.runnables 导入 RunnablePassthrough,用于简单地传递输入
# 这是一个非常重要的概念,代表了 LangChain 表达式语言 (LCEL) 的基础构件

# 加载环境变量 (包括 OPENAI_API_KEY)
load_dotenv()

print("--- 智能客服小助手:问题总结器启动 ---")

# --- 1. 初始化语言模型 (LLM) ---
# 我们选择 ChatOpenAI,因为它更适合对话和指令遵循任务
# model="gpt-3.5-turbo" 是一个经济且性能不错的选择
# temperature 参数控制模型的创造性 (0.0 表示更确定性,1.0 表示更有创造性)
# 对于客服问题总结,我们追求准确和稳定,所以温度设置较低
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.3)
print("✅ LLM (GPT-3.5-Turbo) 初始化完成。")

# --- 2. 定义 PromptTemplate (提示词模板) ---
# 这是我们智能客服的“说明书”。它告诉 LLM 它的角色和任务。
# from_messages 允许我们定义对话式的提示词,包括系统消息和用户消息。
# "system" 消息设定了 LLM 的身份和行为准则。
# "user" 消息是实际的用户输入,其中 {customer_query} 是一个占位符,将在运行时填充。
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个经验丰富且专业的智能客服助手。你的任务是简洁、准确、客观地总结客户提出的问题,提取核心要点,不添加任何额外信息或猜测。"),
    ("user", "客户原始问题:\n{customer_query}\n\n请严格按照上述要求总结客户问题:")
])
print("✅ PromptTemplate 定义完成。")

# --- 3. 定义 OutputParser (输出解析器) ---
# 对于本期的简单总结任务,我们只需要将 LLM 的文本输出直接作为字符串返回即可。
# StrOutputParser 是最基础的解析器。
output_parser = StrOutputParser()
print("✅ OutputParser (StrOutputParser) 定义完成。")

# --- 4. 组装成一个 LangChain 链 (Chain) ---
# LangChain 的核心概念之一就是“链”。
# 我们使用 LangChain 表达式语言 (LCEL) 来组装这些组件。
# 管道操作符 "|" 意味着将前一个组件的输出作为后一个组件的输入。
# 流程:用户输入 -> prompt 填充 -> LLM 处理 -> output_parser 解析 -> 最终输出
customer_problem_summarizer_chain = prompt | llm | output_parser
print("✅ LangChain 链组装完成:Prompt -> LLM -> OutputParser。")

# --- 5. 实战演练:模拟客户提问并获取总结 ---

print("\n--- 模拟客户提问场景 ---")

# 场景一:简单的订单查询
customer_issue_1 = "我的订单号是 #20230815-001,我已经付款了,但是到现在还没有收到发货通知。网站上显示还在处理中,请问我的包裹什么时候能发货?我急着用!"
print(f"\n[客户原始问题 1]:\n{customer_issue_1}\n")
print("📞 智能客服助手正在总结中...")
summary_1 = customer_problem_summarizer_chain.invoke({"customer_query": customer_issue_1})
print(f"[智能客服总结 1]:\n{summary_1}\n")
# 预期输出示例:客户询问订单 #20230815-001 的发货状态,该订单已付款但未收到发货通知,希望能尽快发货。

# 场景二:包含多个意图的复杂问题
customer_issue_2 = "我昨天在你们网站购买了一个智能音箱,订单号是 #20230816-002。我发现我选错了颜色,我想把黑色换成白色。另外,我看到你们最近有活动,满500减50,我的订单是499元,能申请优惠吗?"
print(f"\n[客户原始问题 2]:\n{customer_issue_2}\n")
print("📞 智能客服助手正在总结中...")
summary_2 = customer_problem_summarizer_chain.invoke({"customer_query": customer_issue_2})
print(f"[智能客服总结 2]:\n{summary_2}\n")
# 预期输出示例:客户关于订单 #20230816-002 有两个问题:一是希望将智能音箱的颜色从黑色更换为白色;二是询问订单金额 499 元是否能申请满 500 减 50 的活动优惠。

# 场景三:模糊的问题
customer_issue_3 = "你们的产品是不是不太好用啊?我买了一个,感觉老是卡顿,是不是质量问题啊?"
print(f"\n[客户原始问题 3]:\n{customer_issue_3}\n")
print("📞 智能客服助手正在总结中...")
summary_3 = customer_problem_summarizer_chain.invoke({"customer_query": customer_issue_3})
print(f"[智能客服总结 3]:\n{summary_3}\n")
# 预期输出示例:客户反馈购买的产品存在卡顿现象,怀疑是质量问题。

print("--- 智能客服小助手:问题总结器运行完毕 ---")

运行代码:

python main.py

你将看到类似以下的输出(具体总结内容可能因 LLM 模型和温度设置略有差异):

--- 智能客服小助手:问题总结器启动 ---
✅ LLM (GPT-3.5-Turbo) 初始化完成。
✅ PromptTemplate 定义完成。
✅ OutputParser (StrOutputParser) 定义完成。
✅ LangChain 链组装完成:Prompt -> LLM -> OutputParser。

--- 模拟客户提问场景 ---

[客户原始问题 1]:
我的订单号是 #20230815-001,我已经付款了,但是到现在还没有收到发货通知。网站上显示还在处理中,请问我的包裹什么时候能发货?我急着用!

📞 智能客服助手正在总结中...
[智能客服总结 1]:
客户询问订单号 #20230815-001 的发货状态,该订单已付款但尚未收到发货通知,目前网站显示仍在处理中,客户急需了解包裹何时能发货。

[客户原始问题 2]:
我昨天在你们网站购买了一个智能音箱,订单号是 #20230816-002。我发现我选错了颜色,我想把黑色换成白色。另外,我看到你们最近有活动,满500减50,我的订单是499元,能申请优惠吗?

📞 智能客服助手正在总结中...
[智能客服总结 2]:
客户关于订单号 #20230816-002 有两点需求:一是希望将购买的智能音箱颜色从黑色更换为白色;二是询问其订单金额 499 元是否能参与满 500 减 50 的优惠活动。

[客户原始问题 3]:
你们的产品是不是不太好用啊?我买了一个,感觉老是卡顿,是不是质量问题啊?

📞 智能客服助手正在总结中...
[智能客服总结 3]:
客户反映购买的产品存在卡顿问题,并质疑产品质量。

--- 智能客服小助手:问题总结器运行完毕 ---

TypeScript 示例 (简要展示,与 Python 逻辑一致)

如果你是 TypeScript 爱好者,LangChain 也提供了非常完善的 TS 支持。核心逻辑与 Python 几乎完全一致,只是语法有所不同。

创建一个 ts-example/src/main.ts 文件:

// ts-example/src/main.ts
import { ChatOpenAI } from "@langchain/openai";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
import * as dotenv from "dotenv";

// 加载环境变量
dotenv.config();

async function main() {
    console.log("--- 智能客服小助手:问题总结器启动 (TypeScript) ---");

    // 1. 初始化语言模型
    const llm = new ChatOpenAI({
        modelName: "gpt-3.5-turbo",
        temperature: 0.3,
    });
    console.log("✅ LLM (GPT-3.5-Turbo) 初始化完成。");

    // 2. 定义PromptTemplate
    const prompt = ChatPromptTemplate.fromMessages([
        ["system", "你是一个经验丰富且专业的智能客服助手。你的任务是简洁、准确、客观地总结客户提出的问题,提取核心要点,不添加任何额外信息或猜测。"],
        ["user", "客户原始问题:\n{customer_query}\n\n请严格按照上述要求总结客户问题:"],
    ]);
    console.log("✅ PromptTemplate 定义完成。");

    // 3. 定义OutputParser
    const outputParser = new StringOutputParser();
    console.log("✅ OutputParser (StringOutputParser) 定义完成。");

    // 4. 组装成一个 LangChain 链
    // 在 TypeScript 中,我们使用 .pipe() 方法来连接组件
    const customerProblemSummarizerChain = prompt.pipe(llm).pipe(outputParser);
    console.log("✅ LangChain 链组装完成:Prompt -> LLM -> OutputParser。");

    // 5. 实战演练:模拟客户提问
    const customerIssue = "我的订单号是 #20230815-001,我已经付款了,但是到现在还没有收到发货通知。网站上显示还在处理中,请问我的包裹什么时候能发货?我急着用!";
    console.log(`\n[客户原始问题]:\n${customerIssue}\n`);
    console.log("📞 智能客服助手正在总结中...");
    const summary = await customerProblemSummarizerChain.invoke({ customer_query: customerIssue });
    console.log(`[智能客服总结]:\n${summary}\n`);

    console.log("--- 智能客服小助手:问题总结器运行完毕 (TypeScript) ---");
}

main().catch(console.error);

运行 TypeScript 代码:

# 在 ts-example 目录下
cd ts-example
npx ts-node src/main.ts

你会看到与 Python 类似的输出。这充分体现了 LangChain 在