🧠 让 AI 像乐高一样搭建:LangChain 入门与实战指南
在 AI 时代,大模型(LLM)就像一个拥有海量知识但性格古怪的“超级大脑”。它知道所有答案,但你很难直接指挥它完成复杂的任务。LangChain 就是那个能让你指挥这个“超级大脑”的指挥棒,它是一个用于开发由语言模型驱动的应用程序的开源框架。
🍳 第一部分:什么是 LangChain?—— 让 AI 变得“工程化”
LangChain 不仅仅是一个调用 API 的库,它是一个应用开发框架。它的核心理念是将复杂的 AI 任务拆解为可复用的模块,就像搭乐高积木一样。
核心组件概念:
- Model (模型): 即 LLM,如 DeepSeek、GPT 等,负责生成文本。
- Prompt (提示词): 引导模型如何回答的模板,决定了模型的“人设”和“任务”。
- Chain (链): 将多个步骤(如:格式化提示词 -> 调用模型 -> 后处理)串联起来,形成一个工作流。
🧱 第二部分:代码实战详解 —— 从“单次调用”到“复杂流水线”
🟢 阶段一:基础调用
场景: 就像直接问主厨:“RAG 是什么?” 核心概念: 初始化模型与单次交互。
import 'dotenv/config';
import { ChatDeepSeek } from '@langchain/deepseek';
// 1. 初始化模型
// 这里使用了适配器模式(Adapter Pattern)。
// 无论底层是 OpenAI 还是 DeepSeek,LangChain 都提供了统一的接口。
const model = new ChatDeepSeek({
model: 'deepseek-reasoner', // 指定模型名称
temperature: 0, // 严谨模式,不加随机性
});
// 2. 调用 (Invoke)
// 直接向模型发送纯文本指令。
const res = await model.invoke('一句话解释什么是RAG');
console.log(res.content);
💡 解析: 这是最原子的操作。优点是简单,缺点是如果逻辑复杂,代码会变得难以维护,且提示词和逻辑混杂。
🟡 阶段二:提示词工程
场景: 你不仅想问问题,还想规定主厨的“身份”和“回答长度”。
核心概念: PromptTemplate(提示词模板)。
import { PromptTemplate } from '@langchain/core/prompts';
// 1. 定义模板
// 使用 {variable} 占位符,就像做菜前先准备好填空题。
const prompt = PromptTemplate.fromTemplate(`
你是一个{role}. 请用不超过{limit}字回答以下问题: {question}
`);
// 2. 格式化 (Format)
// 将具体的变量值填入模板。
// 这一步是手动的:先填空,再把填好后的字符串给模型。
const promptStr = await prompt.format({
role: '前端面试官',
limit: 50,
question: '什么是闭包?'
});
// 3. 调用模型
const res = await model.invoke(promptStr);
console.log(res.content);
💡 解析: 这里展示了最基础的逻辑分离:模板与数据分离。但代码流程是割裂的(先处理字符串,再调用模型),LangChain 的强大之处在于能将这两步合并。
🔵 阶段三:管道连接 —— 什么是 Pipe?
场景: 你不想手动把填好的菜谱写下来再递给主厨,你希望“填空”和“烹饪”自动完成。
核心概念: pipe (管道操作符)。
const model = new ChatDeepSeek({ model:'deepseek-reasoner', temperature:0.7 });
const prompt = PromptTemplate.fromTemplate(` 你是一个前端专家,用一句话解释:{topic} `);
// 核心:使用 pipe 连接
// pipe 的意思是“流向”。它将两个 Runnable 对象连接起来。
// 数据流向:Input (输入) -> Prompt (填空) -> Model (生成) -> Output (输出)
const chain = prompt.pipe(model);
// 执行
// 你只需要给 chain 一个输入对象,它内部会自动:
// 1. 拿着输入对象去填充 prompt 模板
// 2. 拿着填充好的字符串去调用 model
const response = await chain.invoke({ topic: '闭包' });
console.log(response.content);
❓ 为什么要用 Pipe?
- 声明式编程: 你只需要声明“流程是什么”(先提示词,后模型),而不需要写“过程怎么执行”(先调 format,再调 invoke)。
- 复用性: 这个
chain可以被多次调用,甚至作为更大流程的一个环节。 - 简洁性: 它消除了中间变量的繁琐处理,让代码逻辑一目了然。
🟣 阶段四:复杂工作流 —— RunnableSequence
场景: 你需要做一个“解释 + 总结”的复杂任务。先让主厨写一篇长文,然后把长文拿给助厨总结成要点。
核心概念: RunnableSequence (可运行序列)。
// 定义两个独立的链
const explainChain = explainPrompt.pipe(model); // 链1: 负责详细解释
const summaryChain = summaryPrompt.pipe(model); // 链2: 负责总结
// 组装复杂流水线
const fullChain = RunnableSequence.from([
// 步骤 1: 接收输入,调用 explainChain
// 注意:这里 explainChain.invoke 返回的是一个 Promise,所以我们用 then 获取结果
(input) => explainChain.invoke({topic: input.topic}).then(res => res.content),
// 步骤 2: 接收步骤1的输出(即详细解释),调用 summaryChain
// 这体现了数据的“流式处理”:前一步的输出是后一步的输入
(explanation) => summaryChain.invoke({explanation})
]);
// 执行
// 输入一个简单的主题,输出一个经过两步处理的复杂结果
const response = await fullChain.invoke({ topic:'闭包' });
console.log(response);
💡 解析:
这是 LangChain 的高阶用法。RunnableSequence 允许你定义任意复杂的逻辑。在这个例子中,我们没有使用简单的 A.pipe(B),而是使用 RunnableSequence 来组合两个已经存在的 Chain。这模拟了真实业务中的复杂 Agent(智能体)逻辑:规划 -> 执行 -> 总结。
📊 总结:LangChain 的核心价值
通过这四个文件的演进,我们可以清晰地看到 LangChain 解决了什么问题:
| 阶段 | 关键技术 | 代码特征 | 适用场景 |
|---|---|---|---|
| 基础 | .invoke() | 直接调用,硬编码 | 简单的问答测试 |
| 模板 | PromptTemplate | 模板与数据分离 | 需要动态填充变量的场景 |
| 链式 | .pipe() | 声明式流水线 | 绝大多数标准的 LLM 任务 |
| 编排 | RunnableSequence | 复杂异步流程 | 需要多步推理、循环或条件判断的 Agent 应用 |
LangChain 的本质,就是将不可控的黑盒模型,变成了可控的、可调试的、工程化的软件系统。