第 05 期 | 流式输出与可观测性:Callbacks 与打字机效果
🎯 本期学习目标
嘿,各位未来的 AI 大师们!欢迎来到《LangChain 全栈大师课》的第一期。我知道你们都迫不及待想撸袖子干活了,但别急,罗马不是一天建成的,智能客服小助手也不是凭空出现的。本期,我们将深入探索 LangChain 的两大基石——LLM (Large Language Model) 和 Prompt Template (提示模板)。学完这期,你将:
- 透彻理解 LangChain 如何封装 LLM,让你能轻松驾驭各种大模型,告别繁琐的 API 调用。
- 掌握 Prompt Template 的精髓,学会如何“编程”LLM,让它听话、高效、输出你想要的结果。
- 亲手搭建一个最基础的智能客服问答机器人,体验从零到一的快感,为我们的“智能客服知识库”项目打下坚实基础。
- 识别并规避初期常见的“坑”,少走弯路,直抵成功彼岸。
准备好了吗?让我们一起开启 LangChain 的魔法之旅!
📖 原理解析
LLM:LangChain 的“大脑”
想象一下,我们的“智能客服知识库”项目需要一个能理解用户问题、给出专业答案的“大脑”。这个大脑就是 大型语言模型 (LLM)。它们是深度学习的奇迹,经过海量文本训练,具备了惊人的语言理解和生成能力。
然而,市面上的 LLM 五花八门:OpenAI 的 GPT 系列、Google 的 Gemini、Anthropic 的 Claude,还有各种开源模型。每个模型的 API 调用方式、参数设置都可能不同,这对于开发者来说简直是噩梦。
LangChain 的 LLM 模块就像一个“万能适配器”。它提供了一套统一的接口,让你无论使用哪家厂商的 LLM,都能以几乎相同的方式进行调用和交互。这大大降低了学习成本和切换模型的难度,让你可以专注于业务逻辑,而不是底层 API 的适配。
在 LangChain 中,LLM 主要分为两类:
LLM(传统文本补全模型):这类模型通常接收一个字符串作为输入,然后生成一个字符串作为输出。例如,早期的 GPT-3。它们更像是一个智能的文本补全器。ChatModel(聊天模型):这是当前主流的模型类型,它们接收一系列“消息”作为输入(例如,用户消息、AI 助手消息、系统消息),然后生成一个“AI 消息”作为输出。这种基于消息的交互方式更符合对话场景,也更容易控制模型的行为和角色。我们的智能客服项目,毫无疑问会大量使用ChatModel。
Prompt Template:LLM 的“指令集”
有了强大的 LLM 大脑,我们还需要一套清晰的“指令集”,告诉它该做什么,怎么做。这个指令集就是 Prompt Template (提示模板)。
你有没有遇到过这样的情况:给 LLM 一个问题,它回答得驴唇不对马嘴,或者语气完全不对?这就是因为你没有给它一个好的“提示”。Prompt Engineering (提示工程) 就是一门艺术,通过精心设计的提示,引导 LLM 按照我们的意图输出。
Prompt Template 的核心思想是:将固定的指令和动态的用户输入结合起来,生成一个完整、清晰、富有上下文的提示。
例如,我们的智能客服小助手,不能只是简单地回答用户问题,它需要:
- 扮演客服角色:语气要专业、友好。
- 聚焦知识库内容:不能胡编乱造,也不能跑题。
- 处理特定格式:比如,如果用户问“退款政策”,它需要知道去哪里找,并以简洁明了的方式呈现。
Prompt Template 允许我们预定义这些“固定指令”,然后在运行时填入用户的问题、从知识库中检索到的相关信息等“动态内容”。这不仅保证了提示的一致性,也大大提高了开发效率和模型的响应质量。
LLM 与 Prompt Template 的魔法组合
当 LLM 这个强大的“大脑”遇到 Prompt Template 这个精密的“指令集”,就产生了奇妙的化学反应。它们是 LangChain 中最基础、也是最核心的组合。
整个流程可以概括为:
- 用户提出问题。
- 我们使用 Prompt Template,将用户的原始问题、预设的客服角色、以及(未来会加入的)知识库上下文等信息,格式化成一个清晰、结构化的提示。
- 这个格式化后的提示被发送给 LLM (或 ChatModel)。
- LLM 根据提示的内容进行推理和生成,返回一个答案。
- 这个答案就是我们智能客服小助手的回复。
下面用一张 Mermaid 图来直观地展示这个核心工作流:
graph TD
A[用户提问] --> B{Prompt Template};
B -- 填充动态变量 --> C[完整格式化提示];
C --> D(LLM / ChatModel);
D -- 生成回复 --> E[AI 助手回复];
subgraph LangChain 核心
B;
D;
end
style A fill:#f9f,stroke:#333,stroke-width:2px;
style B fill:#bbf,stroke:#333,stroke-width:2px;
style C fill:#fcc,stroke:#333,stroke-width:2px;
style D fill:#afa,stroke:#333,stroke-width:2px;
style E fill:#f9f,stroke:#333,stroke-width:2px;这张图清晰地展示了,用户的问题如何经过 Prompt Template 的“加工”,再由 LLM 的“思考”,最终变成智能客服的专业回复。理解这个基础流程,你就抓住了 LangChain 的“魂”。
💻 实战代码演练
是时候把理论转化为代码了!我们将使用 OpenAI 的 gpt-3.5-turbo 作为我们的 ChatModel,并结合 ChatPromptTemplate 来构建一个最简单的智能客服问答机器人。
首先,你需要安装 LangChain 和 OpenAI 库:
pip install langchain langchain-openai python-dotenv
然后,确保你的 .env 文件中有 OPENAI_API_KEY:
OPENAI_API_KEY="sk-..."
Python 实现
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate
# 1. 加载环境变量
load_dotenv()
# 2. 初始化 ChatModel
# 这里我们选用 gpt-3.5-turbo,它是目前性价比很高的模型
# temperature 参数控制模型的创造性,0 表示更确定和保守,1 表示更富有想象力
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.7)
# 3. 定义 Prompt Template
# 对于智能客服,我们需要一个系统消息来设定其角色和行为
# 以及一个人类消息来接收用户的问题
customer_service_template = ChatPromptTemplate.from_messages(
[
# SystemMessagePromptTemplate 用于设定 AI 的角色和全局行为
SystemMessagePromptTemplate.from_template(
"你是一个友好、专业且乐于助人的智能客服助手。你的目标是清晰、准确地回答用户的问题,并尽可能提供有用的信息。请保持礼貌和耐心。"
),
# HumanMessagePromptTemplate 用于接收用户输入
HumanMessagePromptTemplate.from_template(
"用户的问题是:{user_question}"
),
]
)
# 4. 组合 Prompt Template 和 LLM
# LangChain 的链式调用非常优雅,这里我们使用 .pipe() 方法连接
# 这种方式是 LangChain Expression Language (LCEL) 的基础,非常强大和灵活
# 它意味着:先用 customer_service_template 处理输入,然后将结果传递给 llm
customer_service_chain = customer_service_template | llm
# 5. 模拟用户提问并获取回复
print("--- 智能客服助手启动 ---")
# 场景 1: 常见问题
user_query_1 = "请问你们的退款政策是什么?"
print(f"\n用户: {user_query_1}")
response_1 = customer_service_chain.invoke({"user_question": user_query_1})
print(f"客服助手: {response_1.content}")
# 预期输出:一个关于退款政策的通用性回答,语气专业。
# 场景 2: 寻求帮助
user_query_2 = "我的订单号是 #12345,我该如何查询物流进度?"
print(f"\n用户: {user_query_2}")
response_2 = customer_service_chain.invoke({"user_question": user_query_2})
print(f"客服助手: {response_2.content}")
# 预期输出:指导用户如何查询物流,并可能提示需要更多信息。
# 场景 3: 简单问候
user_query_3 = "你好,请问你是谁?"
print(f"\n用户: {user_query_3}")
response_3 = customer_service_chain.invoke({"user_question": user_query_3})
print(f"客服助手: {response_3.content}")
# 预期输出:自我介绍为智能客服助手,并询问用户有何需求。
print("\n--- 智能客服助手已关闭 ---")
TypeScript 实现
import 'dotenv/config'; // 加载 .env 文件
import { ChatOpenAI } from '@langchain/openai';
import { ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate } from '@langchain/core/prompts';
import { BaseMessage } from '@langchain/core/messages';
// 1. 确保 OPENAI_API_KEY 环境变量已设置
if (!process.env.OPENAI_API_KEY) {
console.error("OPENAI_API_KEY is not set in environment variables.");
process.exit(1);
}
// 2. 初始化 ChatModel
// 这里的 modelName 对应 Python 中的 model 参数
const llm = new ChatOpenAI({
modelName: "gpt-3.5-turbo",
temperature: 0.7, // 控制模型的创造性
});
// 3. 定义 Prompt Template
const customerServiceTemplate = ChatPromptTemplate.fromMessages([
// SystemMessagePromptTemplate 用于设定 AI 的角色和全局行为
SystemMessagePromptTemplate.fromTemplate(
"你是一个友好、专业且乐于助人的智能客服助手。你的目标是清晰、准确地回答用户的问题,并尽可能提供有用的信息。请保持礼貌和耐心。"
),
// HumanMessagePromptTemplate 用于接收用户输入
HumanMessagePromptTemplate.fromTemplate(
"用户的问题是:{user_question}"
),
]);
// 4. 组合 Prompt Template 和 LLM
// 在 TypeScript 中,我们可以使用 .pipe() 方法进行链式调用,与 Python 类似
// 或者直接 await prompt.formatMessages(input) 后再 await llm.invoke(messages)
const customerServiceChain = customerServiceTemplate.pipe(llm);
// 5. 模拟用户提问并获取回复
async function runCustomerServiceDemo() {
console.log("--- 智能客服助手启动 ---");
// 场景 1: 常见问题
const userQuery1 = "请问你们的退款政策是什么?";
console.log(`\n用户: ${userQuery1}`);
const response1 = await customerServiceChain.invoke({ user_question: userQuery1 });
console.log(`客服助手: ${response1.content}`);
// 预期输出:一个关于退款政策的通用性回答,语气专业。
// 场景 2: 寻求帮助
const userQuery2 = "我的订单号是 #12345,我该如何查询物流进度?";
console.log(`\n用户: ${userQuery2}`);
const response2 = await customerServiceChain.invoke({ user_question: userQuery2 });
console.log(`客服助手: ${response2.content}`);
// 预期输出:指导用户如何查询物流,并可能提示需要更多信息。
// 场景 3: 简单问候
const userQuery3 = "你好,请问你是谁?";
console.log(`\n用户: ${userQuery3}`);
const response3 = await customerServiceChain.invoke({ user_question: userQuery3 });
console.log(`客服助手: ${response3.content}`);
// 预期输出:自我介绍为智能客服助手,并询问用户有何需求。
console.log("\n--- 智能客服助手已关闭 ---");
}
runCustomerServiceDemo();
通过上面的代码,我们成功地创建了一个最基础的智能客服问答机器人。它能够:
- 扮演客服角色:通过
SystemMessagePromptTemplate设定了其友好、专业的基调。 - 接收用户问题:通过
HumanMessagePromptTemplate动态接收用户输入。 - 利用 LLM 生成回复:将格式化后的提示发送给
gpt-3.5-turbo,获得智能回复。
这只是万里长征的第一步!你已经看到了 LLM 和 Prompt Template 的强大威力,它们是构建任何复杂 AI 应用的基石。在后续的课程中,我们将在此基础上,逐步加入知识库检索、记忆、工具使用等高级功能,让我们的智能客服小助手变得越来越聪明。
坑与避坑指南
作为一名老兵,我见过太多新手在这里摔跟头。别担心,我来给你们提前排雷:
- API Key 泄露的坑:
- 现象:你的
OPENAI_API_KEY硬编码在代码里,或者直接上传到了 GitHub。 - 危害:你的 API Key 被盗用,账单暴涨,甚至可能被用于非法活动。
- 避坑指南:永远不要将敏感信息(如 API Key)直接写入代码或提交到版本控制系统。 务必使用环境变量 (
.env文件配合python-dotenv或dotenv库) 来管理。你的.gitignore文件里一定要有.env!
- 现象:你的
- Prompt 工程不足的坑 (Garbage In, Garbage Out):
- 现象:你直接把用户问题扔给 LLM,结果 LLM 回答得南辕北辙,或者语气不符。
- 危害:用户体验差,模型能力没有被充分发挥,甚至可能产生误导性信息。
- 避坑指南:
- 明确角色:通过
SystemMessage清晰地告诉 LLM 它的身份(例如:“你是一个专业的客服助手”)。 - 明确指令:告诉 LLM 你希望它做什么(例如:“请根据提供的信息回答问题,如果信息不足,请礼貌告知”)。
- 提供上下文:未来我们会学习如何将知识库内容作为上下文提供给 LLM。
- 测试迭代:Prompt Engineering 是一个迭代的过程,不断尝试不同的措辞和结构,直到获得满意的结果。
- 明确角色:通过
- Token 限制的坑:
- 现象:你的输入或输出太长,LLM 报错
max_token_limit_exceeded。 - 危害:程序崩溃,用户请求无法处理。
- 避坑指南:
- 理解模型限制:不同的 LLM 有不同的上下文窗口大小(例如,
gpt-3.5-turbo通常是 4k 或 16k tokens)。 - 精简 Prompt:尽量用简洁的语言表达指令,避免冗余。
- 分块处理:对于超长文本,需要考虑分块处理或使用摘要技术。
- 成本考量:Token 越多,费用越高。
- 理解模型限制:不同的 LLM 有不同的上下文窗口大小(例如,
- 现象:你的输入或输出太长,LLM 报错
- 幻觉 (Hallucination) 的坑:
- 现象:LLM 煞有介事地编造信息,一本正经地胡说八道。
- 危害:对于客服系统而言,这绝对是致命的!会误导用户,损害公司信誉。
- 避坑指南:
- 系统消息强调真实性:在
SystemMessage中明确要求 LLM “只根据提供的信息回答,如果无法确定,请不要猜测”。 - 引入检索增强生成 (RAG):这是未来几期的重点!通过外部知识库检索,限制 LLM 只能在给定事实范围内回答。
- 事实核查:在关键场景,可能需要人工或自动化工具对 LLM 的回答进行二次核查。
- 系统消息强调真实性:在
这些“坑”都是血的教训,希望你们能牢记在心,少走弯路!
📝 本期小结
恭喜你!在本期的《LangChain 全栈大师课》中,你已经掌握了 LangChain 的两大核心组件:LLM (或 ChatModel) 和 Prompt Template。我们了解了 LangChain 如何通过统一接口封装不同的 LLM,以及 Prompt Template 如何成为我们与 LLM 沟通的桥梁。更重要的是,我们亲手搭建了一个最基础的智能客服问答机器人,迈出了构建生产级 AI 应用的第一步。
你现在应该对:
- LLM 作为 AI 大脑的角色。
- Prompt Template 作为 AI 指令集的重要性。
- 如何将二者结合,构建一个简单的对话系统。
- 以及在实践中可能遇到的关键问题和解决方案。
有了这些基础,你已经站在了巨人的肩膀上。在接下来的课程中,我们将逐步解锁 LangChain 更多强大的功能,让我们的智能客服小助手从一个“初级练习生”成长为真正的“全栈大师”!
下一期,我们将深入探索 Output Parsers (输出解析器),学会如何从 LLM 自由流动的文本回复中,提取出结构化的数据,让 AI 的输出不仅仅是“说”,更是“可理解、可操作”的!敬请期待!