用 LangChain 构建 AI 应用:从框架理解到工程实践
第一部分:LangChain 是什么?—— AI 应用的“乐高底板”
在大语言模型(LLM)如 ChatGPT、DeepSeek 爆火之后,开发者很快发现一个问题:直接调用模型 API 只能做“一次性问答” ,而真实场景往往需要多轮交互、外部工具调用、记忆上下文、流程编排……这就像只有一块砖,却想盖一栋楼。
于是,LangChain 应运而生——它不是一个模型,而是一个专为 LLM 应用打造的开发框架。
🔧 LangChain = Language(语言) + Chain(链)
它的核心思想是:把复杂的 AI 任务拆解成一个个可组合的“节点”,再用“链”把它们串起来,形成可运行、可调试、可复用的工作流。
这听起来是不是很像自动化工具 n8n 或 Coze?没错!LangChain 本质上就是用代码实现的 AI 工作流引擎,只不过它是基于 Node.js(或 Python)开发的,更灵活、更贴近工程实际。
本文将带你从零开始,用 Node.js + DeepSeek 模型,亲手搭建一个可扩展的 AI 应用骨架。
为什么需要 LangChain?
- 模型太多,接口不一
OpenAI、DeepSeek、Claude、通义千问……每个厂商的 API 参数、格式都不同。LangChain 通过 “适配器模式” 提供统一接口,让你用同一套代码切换模型,就像换电池一样简单。 - Prompt 需要工程化管理
硬编码字符串"请解释闭包"很脆弱。LangChain 提供PromptTemplate,让提示词变成带变量的模板,支持版本控制、测试和复用。 - 任务天然具有流程性
比如:“查天气 → 判断是否下雨 → 生成穿衣建议”。这种多步骤任务,LangChain 用 Chain 组织成管道(pipe),每一步输出自动作为下一步输入。 - 模型即服务,可插拔
你可以根据性价比、响应速度、功能特性,在不同环节使用不同模型。比如用便宜模型做初筛,用高性能模型做最终生成——LangChain 让这种“混合调度”变得轻而易举。
🧱
如果把 LLM 比作“智能积木”,那 LangChain 就是带卡扣的乐高底板——它不提供积木本身,但让你能把各种积木(模型、工具、数据)稳稳拼在一起,搭出复杂结构。
第二部分:动手实践 —— 从一行代码到智能工作流
有了上述认知,我们来看如何用代码一步步实现 LangChain 的核心能力。以下示例基于 Node.js + DeepSeek 模型。
✅ 步骤 0:环境准备 —— 搭好舞台,才能登台表演
在编写任何 AI 应用之前,我们需要先准备好“工具箱”和“通行证”。
pnpm i langchain @langchain/deepseek dotenv
langchain:LangChain 的核心包,提供 Chain、Prompt、Runnable 等基础能力。@langchain/deepseek:DeepSeek 模型的官方适配器。LangChain 把每个大模型都做成独立插件,实现“可拔插”设计。dotenv:用于加载.env文件中的环境变量,避免把 API 密钥硬编码在代码里(安全最佳实践)。
接着创建 .env 文件:
DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxxxxxx
并在主文件顶部引入:
import 'dotenv/config';
🔒 为什么重要?
这行代码会在程序启动时自动读取.env,并将其中的变量注入到process.env。后续 LangChain 初始化 DeepSeek 时,会自动从process.env.DEEPSEEK_API_KEY读取密钥,你无需手动传参。
✅ 步骤1:基础调用 + 提示模板 —— 让 AI 听懂你的“人话”
第一步:实例化模型
import { ChatDeepSeek } from '@langchain/deepseek';
const model = new ChatDeepSeek({
model: 'deepseek-reasoner',
temperature: 0.7
});
-
ChatDeepSeek是 LangChain 为 DeepSeek 提供的聊天接口封装。 -
model:指定使用哪个具体模型(如 reasoning 能力更强的deepseek-reasoner)。 -
temperature:控制输出的“随机性”。0→ 确定性回答(适合事实查询)1→ 创意性强但可能跑偏(适合写故事)0.7是通用平衡值。
⚠️ 注意:不同模型支持的参数不同,LangChain 会自动映射到对应 API 字段。
第二步:使用 PromptTemplate 构造提示
import { PromptTemplate } from '@langchain/core/prompts';
const prompt = PromptTemplate.fromTemplate(`
你是一个{role}.
请用不超过 {limit} 字回答以下问题:
{question}
`)
fromTemplate是一个静态方法,用于从字符串模板创建可复用的 Prompt 对象。{role}、{limit}、{question}是占位符,后续通过.format()填入真实值。
const promptStr = await prompt.format({
role: '前端面试官',
limit: '50',
question: '什么是闭包'
})
format() 是 LangChain 中 PromptTemplate 类的一个核心方法,用于将模板中的占位符(如 {role}、{question})替换为实际的值,从而生成一个完整的、可直接用于大模型的提示字符串(prompt string) 。
这会生成一段结构清晰、语义明确的提示文本
💡 为什么不用字符串拼接?
因为直接拼接容易出错(比如忘记空格、引号冲突),而PromptTemplate支持:
- 自动转义特殊字符
- 多语言支持
- 与 LangChain 其他组件(如 Memory、OutputParser)无缝集成
第三步:调用模型并获取结果
const response = await model.invoke(formatted);
console.log(response.content);
invoke()是 LangChain 统一的执行方法,无论底层是 OpenAI 还是 DeepSeek,调用方式一致。- 返回值是一个
AIMessage对象,其.content属性才是真正的文本回答。
🎯 关键收获:
通过PromptTemplate + Model.invoke,我们实现了结构化输入 → 标准化输出,这是所有 AI 应用的最小闭环。
输出结果:
步骤1完整代码:
// pnpm i @langchain/deepseek
// pnpm i @langchain/core
import 'dotenv/config';
import { ChatDeepSeek } from '@langchain/deepseek';
import { PromptTemplate } from '@langchain/core/prompts';
const prompt = PromptTemplate.fromTemplate(`
你是一个{role}.
请用不超过 {limit} 字回答以下问题:
{question}
`);
const promptStr = await prompt.format({
role: '前端面试官',
limit: '50',
question: '什么是闭包'
});
const model = new ChatDeepSeek({
model: 'deepseek-reasoner',
temperature: 0.7
});
const res = await model.invoke(promptStr);
console.log(res.content);
✅ 步骤2:构建第一个 Chain —— 把“提示+模型”打包成流水线
当你的应用需要反复执行“填模板 → 调模型”这个流程时,每次都写两行代码太啰嗦。LangChain 提供 Chain 来封装这一组合。
🔍 什么是 Chain?
在 LangChain 中,Chain 并不是一个具体的类,而是一种“可运行单元(Runnable)的组合模式” 。
它的核心思想是:
把 AI 应用拆解为若干个独立、可测试、可替换的步骤(如:输入处理 → 提示生成 → 模型调用 → 输出解析),然后像搭积木一样把它们连起来。
- 每个步骤都是一个
Runnable(实现了.invoke(),.stream()等方法的对象) Chain就是这些Runnable的顺序组合- 最终形成的 Chain 本身也是一个
Runnable,可以被其他 Chain 再次组合
这使得复杂 AI 应用的开发变得模块化、可维护、可测试。
🧪 .pipe() 是如何工作的?
.pipe() 是 LangChain 中用于连接 Runnable 的核心操作符,语法为:
const workflow = step1.pipe(step2).pipe(step3);
它会自动将前一个步骤的输出作为后一个步骤的输入,形成数据流:
输入 → step1 → step2 → step3 → 输出
const chain = prompt.pipe(model);
.pipe()是 LangChain 的核心组合操作符,它将两个“可运行单元”(Runnable)连接起来。prompt是一个 Runnable(能接收输入并输出字符串),model也是一个 Runnable(能接收字符串并输出 AI 回答),- 所以
prompt.pipe(model)生成一个新的 Runnable:输入 → Prompt → LLM → 输出
然后直接调用:
const prompt = PromptTemplate.fromTemplate(`
你是一个前端专家,用一句话解释:{topic}
`);
const response = await chain.invoke({
topic: '闭包'
})
console.log(response.text);// // 注意:这里用 .text 而不是 .content
❓ 为什么是
.text?
当你直接使用prompt.pipe(model)时,最终输出类型是AIMessage(LangChain 的消息对象)。
为了方便,LangChain 在AIMessage上定义了.text属性作为.content的别名(尤其在较新版本中统一接口),两者通常等价。
但更推荐的做法是:显式添加StringOutputParser,让输出直接是字符串,避免字段混淆。
✅ Chain 的核心优势
| 优势 | 说明 |
|---|---|
| 简洁性 | 一行代码封装完整流程 |
| 复用性 | 同一个 chain 可用于不同输入(如 topic: '原型链') |
| 组合性 | chain 可嵌套、可拼接,构建复杂 Agent |
| 可测试性 | 每个步骤可单独 mock 或验证 |
| 一致性 | 所有组件遵循 Runnable 接口,无缝集成 |
输出结果:
步骤二完整代码:
import 'dotenv/config';
import { ChatDeepSeek } from '@langchain/deepseek';
import { PromptTemplate } from '@langchain/core/prompts';
const model = new ChatDeepSeek({
model: 'deepseek-reasoner',
temperature: 0.7
});
const prompt = PromptTemplate.fromTemplate(`
你是一个前端专家,用一句话解释:{topic}
`);
const chain = prompt.pipe(model);
const response = await chain.invoke({
topic: '闭包'
});
console.log(response.text);
✅ 步骤3:多阶段 Chain —— 让多个 AI “角色”协同工作
现实中的智能任务往往不是一步到位的。例如:先让领域专家详细讲解一个概念,再让编辑将其提炼为简洁要点。这种“分阶段、多角色”的协作模式,正是 LangChain 中 多阶段 Chain(Multi-step Chain) 的用武之地。
LangChain 提供了 RunnableSequence 来显式定义串行处理流程,让多个 AI 步骤像流水线一样协同工作。
🔧 什么是 RunnableSequence?
✅
RunnableSequence是 LangChain 中用于构建顺序执行工作流的核心工具类。
- 它接收一个 按顺序执行的函数或 Runnable 列表;
- 每个步骤的输出自动作为下一个步骤的输入;
- 所有步骤支持 异步执行(async/await) ;
- 整个序列本身也是一个
Runnable,可被其他链复用或嵌套; - 特别适合实现 “思考 → 回答 → 校验 → 总结” 等复杂推理流程。
💡 你可以把它理解为 JavaScript 中的
.then().then().then()链,但专为 AI 工作流设计,并具备类型安全和 LangChain 生态集成能力。
第一阶段:深度解释
const explainPrompt = PromptTemplate.fromTemplate(`
你是一个前端专家,请详细介绍以下概念:{topic}
要求:覆盖定义、原理、使用方式,不超过300字.
`);
const explainChain = explainPrompt.pipe(model);
这个 Chain 接收 {topic},调用模型生成一段专业、详细的解释。
第二阶段:提炼要点
const summaryPrompt = PromptTemplate.fromTemplate(`
请将以下前端概念解释总结为3个核心要点(每点不超过20字):
{explanation}
`);
const summaryChain = summaryPrompt.pipe(model);
该 Chain 接收上一阶段生成的解释文本(作为 {explanation}),输出高度凝练的摘要。
🧩 组合成完整工作流:使用 RunnableSequence
import { RunnableSequence } from '@langchain/core/runnables';
const fullChain = RunnableSequence.from([
(input) => explainChain.invoke({topic: input.topic}).then( res => res.text),
// 返回纯文本,作为下一阶段输入
(explanation) => summaryChain.invoke({explanation })
// 基于解释生成摘要
.then( res => `知识点:${explanation}总结:${res.text}`),
])
const response = await fullChain.invoke({
topic: '闭包'
});
console.log(response);
RunnableSequence.from()接收一个处理函数数组,每个函数代表一个逻辑阶段;- 函数可以是普通函数、异步函数,也可以是
Runnable对象(如 Chain); - 数据在阶段间自动传递,无需手动管理中间变量;
- 你可以轻松插入日志、校验、缓存等中间逻辑。
总结:从“调用一次”到“构建系统”
| 阶段 | 能力 | 类比 |
|---|---|---|
| 步骤0 | 环境与认证 | 办理入场证 |
| 步骤1 | 结构化提示 + 单次调用 | 学会说清楚需求 |
| 步骤2 | 封装提示+模型为 Chain | 把常用操作做成快捷按钮 |
| 步骤3 | 多 Chain 串联为工作流 | 组建自动化流水线 |
LangChain 的真正威力,不在于它能让你“更快地问一个问题”,而在于它提供了工程化构建复杂 AI 应用的骨架。当你开始用 Chain 思维思考问题,你就已经从“AI 用户”迈向了“AI 工程师”。