LangChain 1.0 实战指南:从零构建你的第一个 AI 工作流
今天我们来聊聊一个让 AI 开发变得更优雅、更工程化的神器——LangChain。经过几年的打磨,LangChain 终于推出了稳定、强大的 1.0 版本,是时候把它加入你的工具箱了!
一、LangChain 是什么?为什么要用它?
LangChain是一个开源框架,旨在简化大语言模型(LLM)应用的开发。它的核心思想是将复杂的AI任务拆分为可组合、可重用的组件,通过标准化接口实现"乐高式"的搭建体验。LangChain 1.0版本已经具备了成熟的架构设计和丰富的生态系统。
简单来说,LangChain 是一个用于构建基于大语言模型(LLM)应用程序的框架,它帮你:
- 统一接口:不管用 OpenAI、DeepSeek 还是其他模型,调用方式都一样
- 组件化设计:提示词、模型、输出解析器等都可以像乐高一样拼接
- 工作流管理:支持链式调用、智能体(Agent)等复杂流程
二、快速上手:安装与环境配置
首先,创建项目并安装依赖:
npm init -y
npm install @langchain/deepseek @langchain/core dotenv
环境变量管理对于AI应用至关重要。我们通常使用dotenv包来加载.env文件中的配置,避免将敏感信息(如API密钥)硬编码在源码中。
三、你的第一个 LangChain 程序
让我们从最简单的示例开始,展示如何使用LangChain接入大模型:
import 'dotenv/config';
import { ChatDeepSeek } from '@langchain/deepseek';
const model = new ChatDeepSeek({
model: 'deepseek-reasoner',
temperature: 0
});
const res = await model.invoke('用一句话解释什么是RAG');
console.log(res.content);
执行结果:
RAG(检索增强生成)是通过检索外部知识库来增强大模型回答准确性的一种技术,让AI不仅能“胡说”还能“有据可查”。
这段代码简洁明了:
- 我们创建了一个DeepSeek模型实例,指定使用
deepseek-reasoner模型,temperature参数控制输出的随机性 - 通过
invoke方法调用模型,这是LangChain提供的统一接口,无论底层使用哪种LLM,调用方式都保持一致 - 最终获取模型返回的内容
invoke是LangChain的核心方法之一,它是所有LLM调用的标准化接口。无论你使用OpenAI、Anthropic还是DeepSeek,调用模式都是一致的,这大大降低了切换模型的成本。
四、提示模板:PromptTemplate
如果你每次都手动拼接提示词,很快就会发现自己在一堆字符串模板里挣扎。硬编码提示词不仅难以维护,也无法动态适应不同场景。LangChain提供了PromptTemplate类来解决这一问题:
import { PromptTemplate } from '@langchain/core/prompts';
const prompt = PromptTemplate.fromTemplate(
`你是一个{role}.请用不超过{limit}字回答以下问题:{question}`
);
const promptStr = await prompt.format({
role: '前端面试官',
limit: '100',
question: '什么是闭包'
});
console.log(promptStr);
执行结果:
你是一个前端面试官.请用不超过100字回答以下问题:什么是闭包
为什么format方法需要是异步的?尽管当前实现看起来可以是同步的,但LangChain设计为异步是为了未来兼容性。在复杂场景中,模板可能需要从数据库获取动态内容,或者与其他异步服务交互。统一使用异步接口,可以保证API的一致性和可扩展性。
将提示模板与模型结合:
const model = new ChatDeepSeek({
model: 'deepseek-reasoner',
temperature: 0.1
});
const res = await model.invoke(promptStr);
console.log(res.content);
执行结果:
闭包是函数与其词法作用域的组合,使函数能够访问并记住其词法作用域中的变量,
即使函数在其作用域外执行。它允许创建私有变量和模块化代码,是JavaScript的重要特性。
模板化的好处: 可重用、易维护、可读性高。
五、Chain:让 AI 工作流“流动”起来
LangChain的核心思想是将各个组件连接成工作流。pipe方法正是实现这一理念的关键:
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.content);
执行结果:
闭包是函数能够记住并访问它的词法作用域,即使该函数在其作用域外执行,使得内部函数可以访问外部函数的变量。
这里我们看到了LangChain的链式编程风格:prompt.pipe(model)将提示模板和模型连接成一个工作流。当调用invoke时,输入首先通过提示模板生成最终提示词,然后自动传递给模型获取响应。这种设计使代码更加声明式,清晰表达了数据流动的方向。
在底层,pipe操作创建了一个RunnableSequence实例,它是一个可执行的工作流。这种模式类似Unix管道,每个组件接收输入,处理后输出给下一个组件,形成清晰的数据流。
六、复杂工作流:RunnableSequence
现实中的AI应用往往需要多步骤处理。让我们看一个更复杂的例子,创建一个两阶段的解释-总结工作流:
import { ChatDeepSeek } from '@langchain/deepseek';
import { PromptTemplate } from '@langchain/core/prompts';
import { RunnableSequence } from '@langchain/core/runnables';
import 'dotenv/config';
const model = new ChatDeepSeek({
model: 'deepseek-reasoner',
temperature: 0.7
});
const explainPrompt = PromptTemplate.fromTemplate(`
你是一个前端专家,请介绍以下概念:{topic}
要求:覆盖定义、原理、使用方式,不超过300字。
`);
const summaryPrompt = PromptTemplate.fromTemplate(`
请将以下前端概念总结为三个核心要点(每点不超过20个字):{explanation}
`);
const explainChain = explainPrompt.pipe(model);
const summaryChain = summaryPrompt.pipe(model);
// 使用 RunnableSequence 组织工作流
const fullChain = RunnableSequence.from([
(input) => explainChain.invoke({ topic: input.topic })
.then(res => res.content),
(explanation) => summaryChain.invoke({ explanation })
.then(res => `知识点:${explanation}\n\n总结:${res.content}`)
]);
const response = await fullChain.invoke({
topic: '闭包'
});
console.log(response);
执行结果:
知识点:闭包是JavaScript中的一种特殊机制,指函数能够记住并访问其词法作用域,即使该函数在其作用域外执行。原理上,当函数被创建时,会形成一个闭包,捕获其所在环境的变量。这种机制使得内部函数可以访问外部函数的变量,即使外部函数已执行完毕。
闭包常用于:1. 创建私有变量;2. 模块化代码;3. 事件处理;4. 回调函数。虽然功能强大,但不当使用可能导致内存泄漏。
总结:1. 记住词法作用域
2. 实现数据封装和私有变量
3. 可能导致内存泄漏
这个例子展示了LangChain构建复杂工作流的能力:
- 首先,我们创建了两个独立的chain:
explainChain用于详细解释概念,summaryChain用于总结解释 - 然后,使用
RunnableSequence.from将这些chain按顺序组合 - 每个步骤可以是函数、chain或其他可运行组件
- 前一个步骤的输出会自动成为后一个步骤的输入
这种设计使复杂AI应用的构建变得模块化和可维护。每个组件负责单一职责,整体工作流则清晰表达业务逻辑。
七、结构化输出: 让AI输出可预测
大模型的自由文本输出往往难以直接用于程序逻辑。LangChain提供了强大的输出解析器,帮助我们将模型输出转换为结构化数据。这里有两种主要方式:
方法一:StructuredOutputParser + Zod
javascript
import { StructuredOutputParser } from '@langchain/core/output_parsers';
import { z } from 'zod';
// 1. 定义Zod schema
const FrontendConceptSchema = z.object({
name: z.string().describe('概念名称'),
core: z.string().describe('核心要点'),
useCase: z.array(z.string()).describe('常见使用场景'),
difficulty: z.enum(['简单', '中等', '复杂']).describe('学习难度')
});
// 2. 创建结构化输出解析器
const parser = StructuredOutputParser.fromZodSchema(FrontendConceptSchema);
// 3. 获取格式指令(这是关键!)
const formatInstructions = parser.getFormatInstructions();
// 4. 创建提示词模板
const prompt = PromptTemplate.fromTemplate(`
你是一个只会输出 JSON 的 API,不允许输出任何解释性文字。
⚠ 你必须【只返回】符合以下 Schema 的 JSON:
- 不允许增加字段
- 不允许减少字段
- 字段名必须完全一致
- 返回结果必须可以被 JSON.parse 成功解析
{format_instructions}
前端概念:{topic}
`);
const chain = prompt.pipe(model).pipe(parser);
const response = await chain.invoke({
topic: 'Promise',
format_instructions: formatInstructions
});
console.log(response);
执行结果:
{
name: 'Promise',
core: 'Promise是JavaScript中处理异步操作的对象,代表一个异步操作的最终完成或失败及其结果值。它有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。',
useCase: [
'处理AJAX请求结果',
'文件读写等I/O操作',
'定时任务的链式调用',
'并发控制多个异步任务'
],
difficulty: '中等'
}
StructuredOutputParser与Zod结合提供了严格的类型验证,确保输出符合预期的结构。但这种方式需要额外的格式指令,增加了提示词的复杂度。
方法二:JsonOutputParser
对于简单的JSON结构,LangChain提供了更轻量级的JsonOutputParser:
import { JsonOutputParser } from '@langchain/core/output_parsers';
// 1. 创建通用JSON解析器
const jsonParser = new JsonOutputParser();
// 2. 直接在提示词中描述JSON结构
const prompt = PromptTemplate.fromTemplate(
`你是一个只会输出JSON的API,不允许输出任何解释性文字。请返回以下格式的JSON:
{
"name": "概念名称",
"core": "核心要点",
"useCase": ["使用场景1", "使用场景2"],
"difficulty": "简单|中等|复杂"
}
具体要求:
1. name: 概念名称(字符串)
2. core: 核心要点描述(字符串)
3. useCase: 常见使用场景数组(字符串数组)
4. difficulty: 学习难度,只能是"简单"、"中等"或"复杂"
前端概念:{topic}
记住:只返回JSON,不要有任何其他文字!`
);
// 3. 构建链
const chain = prompt.pipe(model).pipe(jsonParser);
// 4. 执行
const response = await chain.invoke({ topic: 'Promise' });
console.log(response);
执行结果:
{
name: 'Promise',
core: 'Promise是JavaScript中用于处理异步操作的对象,代表一个异步操作的最终完成(或失败)及其结果值。它有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。',
useCase: [
'处理API请求结果',
'文件读写操作',
'定时器和事件处理',
'异步任务链式调用'
],
difficulty: '中等'
}
两种解析器的对比:
StructuredOutputParser:适合需要严格类型验证的场景,自动提供格式指令,但在复杂模式下提示词会变得冗长JsonOutputParser:更轻量级,灵活性高,但需要在提示词中明确描述JSON结构,依赖模型的理解能力
两种方法的区别:
| 特点 | StructuredOutputParser | JsonOutputParser |
|---|---|---|
| Schema 定义 | 需要 Zod schema(严格) | 不需要 schema(灵活) |
| 错误处理 | 自动验证,类型不匹配会报错 | 不验证,返回原始 JSON |
| 适合场景 | 生产环境,需要强类型保证 | 快速原型,结构可能变化 |
| 灵活性 | 低(结构固定) | 高(结构可动态调整) |
共同点:
- 都强制 AI 输出 JSON 格式
- 都可以集成到 chain 中
- 都提高了与后续代码的集成度
八、结语:LangChain的工程化思维
LangChain不仅仅是一个工具库,更是一种工程化思维的体现。它教会我们:
- 将复杂AI任务分解为可组合的小单元
- 通过标准化接口实现组件间的无缝连接
- 重视类型安全和结构化输出
- 以声明式而非命令式方式构建应用
LangChain 1.0的成熟标志着AI应用开发进入了一个新阶段。无论你是构建简单的聊天机器人,还是复杂的RAG系统,LangChain都能提供强大的支持和灵活的扩展性。
未来,随着AI技术的演进,LangChain这样的框架将成为连接人类智慧与机器智能的桥梁。掌握它,就是掌握未来应用开发的关键技能之一。
温馨提示:本文所有代码示例均基于 LangChain 1.0+ 版本,确保你的依赖版本正确。如果在实践中遇到问题,不妨查看官方文档——那里有最权威的解答。希望这篇博客能为你打开LangChain世界的大门,祝你在 AI 开发的路上越走越远,越走越稳!