LangChain 1.0 实战指南:从零构建你的第一个 AI 工作流

175 阅读10分钟

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不仅能“胡说”还能“有据可查”。

这段代码简洁明了:

  1. 我们创建了一个DeepSeek模型实例,指定使用deepseek-reasoner模型,temperature参数控制输出的随机性
  2. 通过invoke方法调用模型,这是LangChain提供的统一接口,无论底层使用哪种LLM,调用方式都保持一致
  3. 最终获取模型返回的内容

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构建复杂工作流的能力:

  1. 首先,我们创建了两个独立的chain:explainChain用于详细解释概念,summaryChain用于总结解释
  2. 然后,使用RunnableSequence.from将这些chain按顺序组合
  3. 每个步骤可以是函数、chain或其他可运行组件
  4. 前一个步骤的输出会自动成为后一个步骤的输入

这种设计使复杂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结构,依赖模型的理解能力

两种方法的区别:

特点StructuredOutputParserJsonOutputParser
Schema 定义需要 Zod schema(严格)不需要 schema(灵活)
错误处理自动验证,类型不匹配会报错不验证,返回原始 JSON
适合场景生产环境,需要强类型保证快速原型,结构可能变化
灵活性低(结构固定)高(结构可动态调整)

共同点:

  • 都强制 AI 输出 JSON 格式
  • 都可以集成到 chain 中
  • 都提高了与后续代码的集成度

八、结语:LangChain的工程化思维

LangChain不仅仅是一个工具库,更是一种工程化思维的体现。它教会我们:

  • 将复杂AI任务分解为可组合的小单元
  • 通过标准化接口实现组件间的无缝连接
  • 重视类型安全和结构化输出
  • 以声明式而非命令式方式构建应用

LangChain 1.0的成熟标志着AI应用开发进入了一个新阶段。无论你是构建简单的聊天机器人,还是复杂的RAG系统,LangChain都能提供强大的支持和灵活的扩展性。

未来,随着AI技术的演进,LangChain这样的框架将成为连接人类智慧与机器智能的桥梁。掌握它,就是掌握未来应用开发的关键技能之一。

温馨提示:本文所有代码示例均基于 LangChain 1.0+ 版本,确保你的依赖版本正确。如果在实践中遇到问题,不妨查看官方文档——那里有最权威的解答。希望这篇博客能为你打开LangChain世界的大门,祝你在 AI 开发的路上越走越远,越走越稳!