第 26 期 | 本地模型替身:结合 Ollama 与 Llama3
(申请发送: agentupdate)
本期副标题:从 LangChain 架构到第一个智能客服问答机器人
同学们,欢迎来到《LangChain 全栈大师课:从零基础到生产级 AI 应用》的第一期!我是你们的导师,一个在 AI 领域摸爬滚打十年,对技术和教育都充满热情的“老兵”。今天,我们将一起踏上征途,揭开 LangChain 这把“AI 应用开发瑞士军刀”的神秘面纱,并亲手打造我们智能客服小助手的第一个“大脑”!
🎯 本期学习目标
经过本期的洗礼,你将能够:
- 洞悉 LangChain 的核心价值与设计哲学:理解为什么 LangChain 会成为构建 LLM 应用的事实标准,它解决了哪些痛点。
- 掌握 LangChain 的基础架构与关键模块:对 LangChain 的组件有一个清晰的认知,知道它们各自扮演的角色。
- 亲手搭建一个简单的智能客服问答机器人:从零开始,利用 LangChain 快速实现一个基于大模型的问答功能。
- 初步理解 LangChain 在智能客服项目中的应用:明确本期所学知识如何奠定我们“智能客服知识库”项目的基石。
📖 原理解析
LangChain:AI 应用开发的瑞士军刀,真的不夸张!
你可能会问,我们有了 OpenAI、Anthropic 这样强大的 LLM,为什么还需要 LangChain?这就像你有了最顶级的发动机,但你还需要一套完整的车架、传动系统、刹车和方向盘才能造出一辆真正的汽车。
LLM 固然强大,但它本身只是一个“黑箱”式的文本生成器。在实际的 AI 应用中,我们需要的远不止是简单的问答。想象一下我们的“智能客服知识库”项目:
- 它需要从海量文档中检索信息。
- 它需要理解用户的复杂意图。
- 它需要记住用户的历史对话。
- 它需要调用外部工具(比如查询订单状态、重置密码接口)。
- 它还需要将这些复杂的步骤有逻辑地串联起来。
如果每次都从头开始构建这些逻辑,那简直是重复造轮子的噩梦!LangChain 应运而生,它提供了一套标准化的、模块化的、可组合的工具集,极大地简化了 LLM 应用的开发流程。它就像一把瑞士军刀,把各种实用的工具(模块)集成在一起,让你能够高效地处理各种复杂的任务。
LangChain 的核心理念:模块化与可组合性
LangChain 的设计哲学非常清晰:将构建 LLM 应用所需的各种能力抽象成独立的模块,然后通过“链 (Chain)”的机制将这些模块灵活地组合起来,以完成更复杂的任务。
它的主要模块包括:
- LLMs (Language Models):这是 LangChain 的“心脏”,负责与各种大模型(OpenAI, Google Gemini, Anthropic Claude 等)进行交互。它提供统一的接口,让你轻松切换不同的模型。
- Prompt Templates (提示词模板):这是 LangChain 的“大脑皮层”,负责生成结构化的、针对特定任务的提示词。好的提示词是 LLM 应用成功的关键,LangChain 让提示词的管理和复用变得异常简单。
- Chains (链):这是 LangChain 的“神经系统”,负责将多个 LLM 调用或其他工具按顺序或逻辑组合起来。它定义了信息流的走向和处理逻辑。
- Retrievers (检索器):这是 LangChain 的“记忆库”,负责从外部知识源(如向量数据库、文档)中检索相关信息,以增强 LLM 的知识。
- Agents (智能体):这是 LangChain 的“决策者”,赋予 LLM 使用工具的能力。Agent 能够根据用户的输入,自主决定需要执行哪些步骤、调用哪些工具来解决问题。
- Memory (记忆):这是 LangChain 的“短期记忆”,让 LLM 能够记住对话历史,从而实现多轮对话的连贯性。
- Document Loaders (文档加载器):这是 LangChain 的“资料员”,负责从各种格式(PDF, CSV, HTML 等)加载数据。
- Vector Stores (向量数据库):这是 LangChain 的“长期记忆”,用于存储和检索向量化的文本数据,常与检索器配合使用。
在本期,我们先聚焦最核心的几个模块:LLMs、Prompt Templates 和 Chain,来构建我们智能客服的第一个简单问答功能。
智能客服的“大脑”雏形:问答工作流解析
设想一下,我们的智能客服小助手,最基本的能力是什么?不就是用户问一个问题,它能给出一个合理的答案吗?
这个过程在 LangChain 中,可以简化为以下工作流:
- 用户输入问题:比如“如何重置我的密码?”
- 提示词模板加工:将用户的问题嵌入到一个预设的模板中,给 LLM 明确的指示,例如“你是一个专业的客服助手,请用简洁友好的语言回答用户的问题:[用户问题]”。
- 调用大型语言模型 (LLM):将加工后的提示词发送给大模型。
- 模型生成答案:LLM 根据提示词和其内部知识生成回复。
- 输出答案:将 LLM 的回复呈现给用户。
这个工作流,就是我们智能客服的“大脑”雏形。它虽然简单,但却是所有复杂功能的基础。
graph TD
A[用户] --> B{输入问题};
B --> C[PromptTemplate: 构造指令];
C --> D[LLM: 大语言模型];
D --> E[输出解析器 (可选): 格式化回答];
E --> F[智能客服小助手];
F --> G[回复用户];
subgraph LangChain 核心流程
C --- D --- E
end
style A 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;
style F fill:#fcf,stroke:#333,stroke-width:2px;
style G fill:#9f9,stroke:#333,stroke-width:2px;上图清晰地展示了从用户提问到智能客服回复的简化流程。LangChain 在其中扮演了连接各个模块、串联逻辑的核心角色。
💻 实战代码演练 (客服项目中的具体应用)
好了,原理讲明白了,是时候撸起袖子干活了!我们将用 LangChain 来实现上述的简单问答流程,让我们的智能客服小助手能够回答一些通用问题。
准备工作
首先,我们需要安装 LangChain 库和 OpenAI 相关的集成库。我们选择 OpenAI 作为本期的 LLM 提供方,因为它目前是行业标杆,且接入方便。
# Python 环境
pip install langchain langchain-openai python-dotenv
# TypeScript / JavaScript 环境
npm install langchain @langchain/openai dotenv
然后,为了安全管理 API Key,我们通常使用 .env 文件。在项目根目录下创建一个名为 .env 的文件,并填入你的 OpenAI API Key:
OPENAI_API_KEY="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Python 实战代码
让我们用 Python 来构建第一个智能客服问答机器人。
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableSequence
# 1. 加载环境变量
# 这一步是读取 .env 文件中的 OPENAI_API_KEY
load_dotenv()
# 检查 API Key 是否已加载
if not os.getenv("OPENAI_API_KEY"):
raise ValueError("OPENAI_API_KEY not found in environment variables. Please set it in a .env file.")
print("--- 智能客服小助手启动中 ---")
# 2. 初始化大语言模型 (LLM)
# 我们使用 ChatOpenAI,它封装了 OpenAI 的聊天模型接口
# temperature 参数控制模型的创造性,0 表示更确定性,1 表示更有创造性
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.7)
print(f"✅ 已连接到大模型: {llm.model_name}")
# 3. 定义提示词模板 (Prompt Template)
# 这是我们给 LLM 的指令,告诉它如何扮演角色和回答问题
# {question} 是一个占位符,用户的问题会填充到这里
prompt_template = ChatPromptTemplate.from_messages(
[
("system", "你是一个友好的智能客服助手,你的任务是简洁、准确地回答用户的问题。"),
("human", "{question}"),
]
)
print("✅ 已定义智能客服的提示词模板。")
# 4. 构建链 (Chain)
# LangChain Expression Language (LCEL) 是构建链的现代化方式,简洁且强大
# 这里的链表示:用户问题 -> 提示词模板 -> LLM -> 字符串解析器
# StrOutputParser 将 LLM 的输出(通常是 AIMessage 对象)转换为纯字符串
qa_chain = RunnableSequence(prompt_template | llm | StrOutputParser())
print("✅ 已构建问答链。")
# 5. 模拟智能客服问答
print("\n--- 开始模拟问答 ---")
# 场景1: 用户询问如何重置密码 (客服常见问题)
user_question_1 = "我忘记了我的账户密码,请问如何重置?"
print(f"\n🙋♂️ 用户提问: {user_question_1}")
# 调用链来获取答案
response_1 = qa_chain.invoke({"question": user_question_1})
print(f"🤖 智能客服: {response_1}")
# 场景2: 用户询问产品功能 (客服常见问题)
user_question_2 = "你们的产品支持哪些文件格式的上传?"
print(f"\n🙋♂️ 用户提问: {user_question_2}")
response_2 = qa_chain.invoke({"question": user_question_2})
print(f"🤖 智能客服: {response_2}")
# 场景3: 用户询问一个通用知识 (LLM 的通识能力)
user_question_3 = "请简要解释一下人工智能是什么?"
print(f"\n🙋♂️ 用户提问: {user_question_3}")
response_3 = qa_chain.invoke({"question": user_question_3})
print(f"🤖 智能客服: {response_3}")
print("\n--- 模拟问答结束 ---")
print("🎉 恭喜,你的第一个智能客服问答机器人已成功运行!")
代码解析:
load_dotenv(): 从.env文件加载环境变量,确保你的 API Key 不会硬编码在代码中,这是生产级应用的基本要求。ChatOpenAI: 初始化一个聊天模型实例。我们指定了gpt-3.5-turbo模型。temperature参数很重要,它控制了模型回复的“随机性”或“创造性”。对于客服场景,我们通常希望回复更稳定、更确定,所以0.7是一个比较折中的选择。ChatPromptTemplate.from_messages(): 定义了我们与 LLM 沟通的“剧本”。("system", ...):系统消息,用于设定 LLM 的角色和行为准则。在这里,我们让它扮演一个“友好的智能客服助手”。这是进行提示词工程(Prompt Engineering)的关键一环。("human", "{question}"):用户消息,{question}是一个占位符,LangChain 会自动将我们传入的实际问题替换进去。
RunnableSequence(LCEL): 这是 LangChain 0.1.0+ 版本推荐的链式调用方式,非常简洁直观。prompt_template | llm | StrOutputParser():这表示一个管道操作。用户输入首先经过prompt_template加工成完整的提示词,然后这个提示词被发送给llm,LLM 返回的结果再经过StrOutputParser转换为纯文本字符串。
qa_chain.invoke(): 调用我们构建好的链,传入一个字典,其中question的值就是用户实际的问题。
通过这段代码,我们的智能客服小助手就拥有了初步的问答能力。它能理解用户的问题,并基于 OpenAI 模型的通用知识和我们设定的“客服助手”角色,给出相应的回复。
TypeScript 实战代码
对于前端或 Node.js 开发者,我们也提供 TypeScript 版本的实现。
import * as dotenv from 'dotenv';
import { ChatOpenAI } from '@langchain/openai';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { StringOutputParser } from '@langchain/core/output_parsers';
import { RunnableSequence } from '@langchain/core/runnables';
// 1. 加载环境变量
// 这一步是读取 .env 文件中的 OPENAI_API_KEY
dotenv.config();
// 检查 API Key 是否已加载
if (!process.env.OPENAI_API_KEY) {
throw new Error("OPENAI_API_KEY not found in environment variables. Please set it in a .env file.");
}
console.log("--- 智能客服小助手启动中 ---");
// 2. 初始化大语言模型 (LLM)
// 我们使用 ChatOpenAI,它封装了 OpenAI 的聊天模型接口
// temperature 参数控制模型的创造性,0 表示更确定性,1 表示更有创造性
const llm = new ChatOpenAI({
model: "gpt-3.5-turbo",
temperature: 0.7,
});
console.log(`✅ 已连接到大模型: ${llm.modelName}`);
// 3. 定义提示词模板 (Prompt Template)
// 这是我们给 LLM 的指令,告诉它如何扮演角色和回答问题
// {question} 是一个占位符,用户的问题会填充到这里
const promptTemplate = ChatPromptTemplate.fromMessages([
["system", "你是一个友好的智能客服助手,你的任务是简洁、准确地回答用户的问题。"],
["human", "{question}"],
]);
console.log("✅ 已定义智能客服的提示词模板。");
// 4. 构建链 (Chain)
// LangChain Expression Language (LCEL) 是构建链的现代化方式,简洁且强大
// 这里的链表示:用户问题 -> 提示词模板 -> LLM -> 字符串解析器
// StringOutputParser 将 LLM 的输出(通常是 AIMessage 对象)转换为纯字符串
const qaChain = RunnableSequence.from([
promptTemplate,
llm,
new StringOutputParser(),
]);
console.log("✅ 已构建问答链。");
// 5. 模拟智能客服问答
async function runDemo() {
console.log("\n--- 开始模拟问答 ---");
// 场景1: 用户询问如何重置密码 (客服常见问题)
const userQuestion1 = "我忘记了我的账户密码,请问如何重置?";
console.log(`\n🙋♂️ 用户提问: ${userQuestion1}`);
// 调用链来获取答案
const response1 = await qaChain.invoke({ question: userQuestion1 });
console.log(`🤖 智能客服: ${response1}`);
// 场景2: 用户询问产品功能 (客服常见问题)
const userQuestion2 = "你们的产品支持哪些文件格式的上传?";
console.log(`\n🙋♂️ 用户提问: ${userQuestion2}`);
const response2 = await qaChain.invoke({ question: userQuestion2 });
console.log(`🤖 智能客服: ${response2}`);
// 场景3: 用户询问一个通用知识 (LLM 的通识能力)
const userQuestion3 = "请简要解释一下人工智能是什么?";
console.log(`\n🙋♂️ 用户提问: ${userQuestion3}`);
const response3 = await qaChain.invoke({ question: userQuestion3 });
console.log(`🤖 智能客服: ${response3}`);
console.log("\n--- 模拟问答结束 ---");
console.log("🎉 恭喜,你的第一个智能客服问答机器人已成功运行!");
}
runDemo();
TypeScript 代码解析:
与 Python 版本逻辑完全一致,只是语法上有所区别:
dotenv.config(): 加载.env文件。new ChatOpenAI(): 初始化聊天模型。ChatPromptTemplate.fromMessages(): 定义提示词模板,这里同样使用[string, string]的数组形式表示消息。RunnableSequence.from([...]): 构建链,同样通过数组顺序定义管道。await qaChain.invoke({ question: userQuestion }): 调用链是异步的,所以需要使用await。
至此,我们的智能客服小助手已经具备了最基础的问答能力。它能够接收用户的问题,并利用大模型的通用知识和我们设定的角色进行回复。虽然它还不能访问我们公司内部的知识库,但这是一个坚实的第一步!
坑与避坑指南
作为一名资深开发者,我见过太多的坑,也总结了一些避坑经验,今天就先分享几个初学者常犯的错误和注意事项:
- API Key 安全性:
- 坑: 直接把
OPENAI_API_KEY硬编码在代码里,或者上传到公共仓库(如 GitHub)。这等于把你的银行卡密码贴在脸上! - 避坑指南: 永远使用环境变量(如
.env文件配合python-dotenv或dotenv)来管理敏感信息。在部署到生产环境时,也要确保这些变量是通过安全的方式(如 Kubernetes Secrets, AWS Secrets Manager, Vault 等)注入的。
- 坑: 直接把
- 提示词工程 (Prompt Engineering) 的重要性:
- 坑: 觉得 LLM 无所不能,随便给个问题就能得到完美答案。或者提示词模糊不清,导致模型“胡言乱语”(Hallucination)。
- 避坑指南: “Garbage in, garbage out” 在 LLM 世界里尤其适用。本期我们用的
system消息就是最基础的提示词工程。请记住,清晰、明确、有上下文的指令,比你花几小时调参可能都管用。多尝试不同的措辞,限定模型的回复格式、长度、语气等。
- LLM 的“幻觉” (Hallucination):
- 坑: 过于信任 LLM 的回答,认为它说的都是真理。
- 避坑指南: 现阶段的 LLM 并不是一个严谨的知识库,它可能会生成听起来合理但实际上是错误或虚构的信息。在我们的智能客服项目中,这意味着它可能会“编造”公司政策或产品功能。这是这个简单问答模型的最大局限性。未来我们将通过 RAG(检索增强生成)等技术来解决这个问题,让 LLM 能够基于我们提供的真实知识进行回答。
- 成本管理与模型选择:
- 坑: 不加思索地使用最新最强的模型(如
gpt-4),导致账单爆炸。 - 避坑指南: 不同的模型有不同的价格和性能。对于简单的问答或测试,
gpt-3.5-turbo通常是性价比最高的选择。只有在确实需要更高级推理能力时,才考虑使用更昂贵的模型。LangChain 的统一接口让你切换模型变得轻而易举,善用这个特性。
- 坑: 不加思索地使用最新最强的模型(如
- 异步操作 (Async) 与并发:
- 坑: 在处理大量请求时,同步调用 LLM 导致性能瓶颈。
- 避坑指南: LangChain 的许多方法都提供了异步版本(如
ainvoke)。在生产环境中,尤其是在 Web 服务中,务必利用异步编程来提升并发处理能力和响应速度。我们本期为了简化,使用了同步调用,但心中要有这根弦。
📝 本期小结
恭喜你,同学们!通过本期的学习,我们不仅深入理解了 LangChain 作为 AI 应用开发“瑞士军刀”的核心价值和模块化设计理念,更亲手搭建了我们“智能客服知识库”项目的第一个里程碑——一个基于大模型的简单问答机器人。
我们学会了如何:
- 初始化并连接到 OpenAI 大模型。
- 利用
ChatPromptTemplate进行基础的提示词工程,赋予模型角色。 - 通过 LangChain Expression Language (LCEL) 构建简洁高效的问答链。
- 在实际项目中,让智能客服小助手能够响应用户的通用问题。
虽然这个小助手还很“稚嫩”,它的知识仅限于 LLM 的通用训练数据,无法访问我们公司的内部文档,但这正是 LangChain 的魅力所在——它为我们后续扩展功能打下了坚实的基础。
在接下来的课程中,我们将逐步解锁 LangChain 更强大的能力,比如如何让我们的智能客服能够“阅读”公司的产品手册和 FAQ 文档,如何记住用户的多轮对话,甚至如何调用外部 API 来执行具体操作。
下期预告:我们将深入探索 LangChain 的 Document Loaders 和 Text Splitters,学习如何将海量的非结构化文档转化为 LLM 可以理解的知识片段,为我们的智能客服构建专属的“记忆库”!
加油,AI 架构师们,我们的征途才刚刚开始!