用 LangChain 玩转大模型:从零搭建你的第一个 AI 应用

引言

一句话概括:LangChain 是一个让你像搭积木一样构建 AI 应用的工程化框架。

在 ChatGPT 横空出世、Transformer 架构席卷全球的 2022 年,你可能以为 LangChain 是为了"蹭热度"才诞生的。但其实------LangChain 比 ChatGPT 还早!它早在 LLM(大语言模型)爆发前就默默布局,如今已推出 1.0+ 稳定版本,成为构建 AI 应用的事实标准之一。

今天,我们就通过一个真实的小项目,手把手带你走进 LangChain 的世界。我们将从环境准备 开始,到逐行解析代码 ,再到深入理解每个 API 的作用,让你不仅会用,还能讲清楚"为什么这么用"。


一、什么是 LangChain?

1.1 背景与定位

LangChain = Lang (语言模型) + Chain(链)

它的核心思想非常简单又强大:

  • 统一接口:无论你用的是 DeepSeek、OpenAI、Anthropic 还是本地 Llama,LangChain 都提供一致的调用方式。
  • 模块化组合:把 Prompt、LLM、Memory、Tools 等组件像乐高一样拼接成工作流(Chain)。
  • 工程化支持:不再是"prompt 调试即上线",而是支持可测试、可维护、可扩展的 AI 应用开发。

你可以把它想象成 AI 版的 n8n 或 Coze ------ 把一个个"任务节点"(Node)连接起来,形成自动化流程。

1.2 核心概念速览

  • Model (LLM/ChatModel):大语言模型的抽象接口。
  • PromptTemplate:结构化提示词模板,支持变量注入。
  • Chain:多个组件(如 Prompt + Model)组成的可执行流水线。
  • Runnable:所有可执行单元的基类(包括 Chain、Model、Function 等)。
  • Agent:能自主决策、调用工具的高级智能体(本项目未涉及,但基于 Chain 构建)。

LangChain 的哲学是:将 LLM 从"黑盒"变为"可编程模块"


二、项目准备:5 分钟搭好脚手架

我们先看看项目的骨架。以下是 package.json 的内容:

bash 复制代码
{
  "name": "demo",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "type": "module",
  "dependencies": {
    "@langchain/core": "^1.1.8",
    "@langchain/deepseek": "^1.0.3",
    "dotenv": "^17.2.3",
    "langchain": "^1.2.3"
  }
}

2.1 关键字段解析

  • "name": "demo":项目名称。
  • "version": "1.0.0":遵循语义化版本。
  • "type": "module"关键配置 !启用 ES Module(ESM),所以我们使用 import 语法而非 CommonJS 的 require。这是现代 Node.js 项目的推荐做法。
  • "dependencies"
    • @langchain/core:LangChain 的核心抽象层,包含 PromptTemplateRunnableSequence 等基础类。
    • @langchain/deepseek:DeepSeek 官方提供的模型适配器(Provider),封装了 API 调用细节。
    • dotenv:用于加载 .env 文件中的环境变量,保护敏感信息(如 API Key)。
    • langchain:主包,整合常用功能,简化导入路径。

💡 为什么分开 coredeepseek

LangChain 采用插件化架构core 提供通用接口,而 @langchain/deepseek 是针对特定模型的实现。这样你可以轻松切换模型,只需更换适配器包。

2.2 环境变量配置

接着看 .env 文件(敏感信息已脱敏,但结构保留):

复制代码
DEEPSEEK_API_KEY=sk-986270f999f548f4b35dee32393eaf42
  • 这个文件绝不应提交到 Git (通常加入 .gitignore)。
  • dotenv 包会在程序启动时自动读取该文件,并将变量注入 process.env
  • LangChain 的模型适配器会自动检测 环境变量中的 DEEPSEEK_API_KEY,无需手动传入。

2.3 安装依赖

在项目根目录运行:

bash 复制代码
pnpm install
# 或 npm install / yarn install

推荐使用 pnpm:速度快、磁盘占用少,且与 LangChain 官方示例一致。

至此,项目环境已准备就绪!


三、从最简单的开始:main.js ------ 直连 LLM

来看 main.js(原样引用,一字不改):

javascript 复制代码
import 'dotenv/config' // 加载环境变量
// console.log(process.env.DEEPSEEK_API_KEY)
import { ChatDeepSeek } from '@langchain/deepseek' // 适配器
const model = new ChatDeepSeek({ 
  model: 'deepseek-reasoner', 
  temperature: 0, 
})

// invoke执行 是一个异步函数,需要用 await 等待结果
const res = await model.invoke('用一句话解释什么是RAG')
console.log(res.content)
// RAG是一种基于检索的生成模型,它通过从外部知识库中检索相关信息来生成文本。

3.1 逐行详解

第 1 行:import 'dotenv/config'
  • 这是一个 side-effect import(副作用导入)。
  • 执行时会自动读取项目根目录下的 .env 文件,并将其中的键值对加载到 process.env 对象中。
  • 因此,后续代码可以直接通过 process.env.DEEPSEEK_API_KEY 访问密钥(虽然这里不需要显式使用)。
第 3 行:import { ChatDeepSeek } from '@langchain/deepseek'
  • 从 DeepSeek 适配器包中导入 ChatDeepSeek 类。
  • 这是一个 Chat 模型专用类 ,继承自 LangChain 的 BaseChatModel 抽象基类。
  • 它封装了与 DeepSeek API 的通信细节(如 HTTP 请求、错误处理、流式响应等)。
第 4--7 行:实例化模型
javascript 复制代码
const model = new ChatDeepSeek({ 
  model: 'deepseek-reasoner', 
  temperature: 0, 
})
  • model: 'deepseek-reasoner' :指定使用的模型名称。DeepSeek 提供多种模型,reasoner 版本擅长逻辑推理和复杂任务。
  • temperature: 0 :控制生成文本的随机性。
    • 0 表示完全确定性(相同输入永远得到相同输出),适合事实性问答。
    • 值越高(最大通常为 1 或 2),输出越有创意但也越不稳定。

设计亮点:通过适配器模式,LangChain 屏蔽了不同 LLM 的 API 差异。换模型?只需改一行 import 和类名!

第 10-11 行:调用模型
javascript 复制代码
const res = await model.invoke('用一句话解释什么是RAG')
console.log(res.content)
  • .invoke(input)
    • 这是所有 LangChain Runnable 对象的标准方法。
    • 对于 Chat 模型,input 可以是:
      • 字符串(本例):自动包装为 HumanMessage
      • BaseMessage[] 数组:用于多轮对话(如 [new HumanMessage("..."), new AIMessage("...")])。
    • 返回一个 Promise<AIMessage>
  • res.content
    • AIMessage 是 LangChain 定义的消息类型,包含:
      • .content:模型生成的文本。
      • .additional_kwargs:额外元数据(如 token 使用量,取决于模型支持)。
    • 打印结果如注释所示,准确解释了 RAG(Retrieval-Augmented Generation)。

小结main.js 展示了 LangChain 最基础的能力------用统一接口调用任意 LLM,背后是"适配器模式"的威力。


四、加入提示词模板:1.js ------ 动态 Prompt

现在让 AI 更聪明一点。看 1.js

javascript 复制代码
// 适配器 provider 省去了适配工作
// 适配大模型也是工作量
import 'dotenv/config'
import { ChatDeepSeek } from '@langchain/deepseek' // 提示词模块 用于格式化提示词模板
import {PromptTemplate} from '@langchain/core/prompts'
// static 方法 用于创建提示词模板 方法 类的 不是实例的
const prompt = PromptTemplate.fromTemplate(`
 你是一个{role} 请用不超过{limit}字回答以下问题: {question} 
`)
const promptStr = await prompt.format({ 
  role: '前端面试官', 
  limit: 50, 
  question: '什么是闭包',
})
// const prompt2 = await prompt.format({
// role: '后端面试官',
// limit: '50',
// question: '什么是MVC'
// })
// console.log(promptStr, prompt2)
const model = new ChatDeepSeek({ 
  model: 'deepseek-reasoner', 
  temperature: 0.7
});
const res = await model.invoke(promptStr)
console.log(res.content); 
// 闭包是函数及其所在词法环境的组合,它使函数能够记住并访问创建时的作用域,即使函数在其他地方执行。

4.1 逐行详解

第 5 行:import {PromptTemplate} from '@langchain/core/prompts'
  • 从核心包导入 PromptTemplate
  • 这是 LangChain 实现 Prompt 工程化 的基石。
第 7--9 行:创建模板
javascript 复制代码
const prompt = PromptTemplate.fromTemplate(`
 你是一个{role} 请用不超过{limit}字回答以下问题: {question} 
`)
  • PromptTemplate.fromTemplate(templateString)
    • 这是一个 静态工厂方法(static method),直接通过类调用,无需实例化。
    • 模板字符串中的 {role}{limit}{question}占位符
    • LangChain 会自动解析这些占位符,并在 .format() 时替换。

💡 为什么用反引号(`````)?

方便书写多行字符串,提升可读性。实际内容会被 trim 和标准化。

第 10--14 行:格式化 Prompt
javascript 复制代码
const promptStr = await prompt.format({ 
  role: '前端面试官', 
  limit: 50, 
  question: '什么是闭包',
})
  • .format(variables)
    • 接收一个对象,键必须与模板中的占位符匹配。

    • 返回一个格式化后的字符串(Promise<string>,所以用 await)。

    • 本例生成:

      复制代码
      你是一个前端面试官 请用不超过50字回答以下问题: 什么是闭包 

📝 注释中的 prompt2 示例展示了模板的复用性:同一模板,不同角色、问题。

第 23--26 行:调用模型
javascript 复制代码
const model = new ChatDeepSeek({ 
  model: 'deepseek-reasoner', 
  temperature: 0.7
});
const res = await model.invoke(promptStr)
  • temperature: 0.7
    • 引入适度随机性,让回答更自然、不机械。
    • 对比 main.js0,这里更适合"面试官"这种需要人性化表达的场景。
第 27 行:输出结果
javascript 复制代码
console.log(res.content); 
// 闭包是函数及其所在词法环境的组合,它使函数能够记住并访问创建时的作用域,即使函数在其他地方执行。
  • 模型成功扮演"前端面试官",并在 50 字内精准回答。

小结1.js 引入了 动态提示词,让同一个模板能应对不同角色、限制和问题,极大提升复用性与可控性。


五、构建工作流:2.js ------ 用 Chain 连接 Prompt 和 LLM

AI 应用往往不止一步。2.js 展示了如何用 Chain(链) 组合多个步骤:

javascript 复制代码
// chain 链 多个节点 链接起来 形成一个流程
// AI业务是复杂的,需要多个节点来完成 分步骤处理
// 每个节点都有自己的功能,每个点都可执行
// 节点之间通过 输入输出 来通信
// 链 就是多个节点 链接起来 形成一个流程,即工作流
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} 
`)
// prompt 模板节点 -> model 节点
// model 代表的是llm节点 
// 结束节点 invoke 
// pipe 管道 用于链接多个节点,形成工作流
// runnable 可运行序列 是一个链 Sequencial workflow Agent
// SequencialChain
const chain = prompt.pipe(model)
// console.log(chain)
// console.log(chain, instanceof RunnableSequence)
const response = await chain.invoke({ topic: '什么是闭包'})
console.log(response.content) 
// 闭包是指函数能够记住并访问其词法作用域中的变量,即使该函数在其原始作用域之外执行。

5.1 逐行详解

第 12--15 行:定义模型与模板
  • 与之前类似,但模板更简洁:用一句话解释{topic}
第 24 行:构建 Chain
javascript 复制代码
const chain = prompt.pipe(model)
  • .pipe(other)
    • 这是 Runnable 接口的核心方法。
    • 它将当前对象(prompt)的输出作为下一个对象(model)的输入,返回一个新的 RunnableSequence
    • 数据流:{topic} → prompt.format() → string → model.invoke() → AIMessage
  • RunnableSequence
    • 代表一个顺序执行的工作流
    • 它本身也是一个 Runnable,因此可以继续 .pipe() 或直接 .invoke()

💡 注释提到 "SequencialChain" ------ 这是 LangChain 旧版术语,新版统一为 RunnableSequence

第 29 行:调用 Chain
javascript 复制代码
const response = await chain.invoke({ topic: '什么是闭包'})
  • 注意:现在传入的是 { topic: '...' },而不是格式化后的字符串!
  • chain 内部会自动将这个对象传递给 prompt.format()
第 30 行:输出
javascript 复制代码
console.log(response.content)
  • 返回 AIMessage,取 .content 即可。

小结2.js 展示了 LangChain 的核心抽象------Chain 。它把"构造 prompt"和"调用 LLM"封装成一个原子操作,代码更简洁、逻辑更清晰,且具备组合性(可嵌套、可复用)。


六、复杂工作流:3.js ------ 多步 Chain 与总结提炼

最后,挑战高阶玩法:多阶段 Chain 。看 3.js

javascript 复制代码
import 'dotenv/config' // 加载环境变量
import { ChatDeepSeek } from '@langchain/deepseek' // 适配器
import { PromptTemplate } from '@langchain/core/prompts' // 提示词模板
// AI 应用的编程方式
// LLM 黑盒打开 以前key是prompt 现在是input
// langchain AI应用的工程化
import { RunnableSequence } from '@langchain/core/runnables' // 可运行序列 链
const model = new ChatDeepSeek({ 
  model: 'deepseek-reasoner', 
  temperature: 0.7
})
const explainPrompt = PromptTemplate.fromTemplate(`
 你是一个前端专家,请详细介绍一下概念:{topic}
 要求:覆盖定义、原理、使用方式、别超过300字。
`)
const summaryPrompt = PromptTemplate.fromTemplate(`
 请将以下前端概念解释总结为3个核心要点(每点不超过30个字):
 {explanation}
`)
const explainChain = explainPrompt.pipe(model)
// console.log(explainChain)
const summaryChain = summaryPrompt.pipe(model)
// console.log(summaryChain)
const fullChain = RunnableSequence.from([
  // input 作为一个对象,包含topic
  (input) => explainChain.invoke({ topic: input.topic }).then(res => res.text),
  (explanation) => summaryChain.invoke({ explanation }).then(res => `知识点:${explanation} 总结:${res.text}`)
])
// console.log(fullChain)
const response = await fullChain.invoke({ topic: '什么是RAG'})
console.log(response) 
// RAG是一种基于检索的生成模型,它通过从外部知识库中检索相关信息来生成文本。

6.1 逐行详解

第 7 行:导入 RunnableSequence
  • 显式导入 RunnableSequence,用于手动构建复杂链。
第 10--19 行:定义两个 Prompt 模板
  • explainPrompt:要求详细解释(定义、原理、使用方式),限 300 字。
  • summaryPrompt:要求将解释总结为 3 个要点,每点 ≤30 字。
第 20--23 行:构建两个子 Chain
javascript 复制代码
const explainChain = explainPrompt.pipe(model)
const summaryChain = summaryPrompt.pipe(model)
  • 每个 Chain 负责一个独立任务,高内聚、低耦合
第 26--31 行:构建主 Chain
javascript 复制代码
const fullChain = RunnableSequence.from([
  // input 作为一个对象,包含topic
  (input) => explainChain.invoke({ topic: input.topic }).then(res => res.text),
  (explanation) => summaryChain.invoke({ explanation }).then(res => `知识点:${explanation} 总结:${res.text}`)
])
  • RunnableSequence.from(steps)
    • 接收一个函数数组,每个函数代表一个处理步骤。
    • 步骤 1:
      • 输入:{ topic: 'RAG' }
      • 调用 explainChain,获取详细解释。
      • .then(res => res.text):提取 AIMessage 的文本内容(res.textres.content 的别名)。
    • 步骤 2:
      • 输入:上一步的输出(即解释文本)。
      • 调用 summaryChain,生成摘要。
      • 拼接最终字符串:知识点:... 总结:...
  • 为什么用 .then()
    • 因为 invoke() 返回 Promise,而 RunnableSequence 要求每个步骤同步返回 Promise(不能用 await,因为函数不是 async)。

⚠️ 注意:这种写法略显冗长。LangChain 也支持更简洁的 LCEL(LangChain Expression Language)写法,但本例展示了底层机制。

第 34--35 行:执行与输出
javascript 复制代码
const response = await fullChain.invoke({ topic: '什么是RAG'})
console.log(response)
  • 最终输出是一个包含"详细解释 + 要点总结"的字符串。

小结3.js 展示了 LangChain 处理复杂 AI 逻辑的能力------分阶段思考、逐步精炼,模拟人类专家的工作流。这种模式非常适合教学、报告生成、知识提炼等场景。


七、总结:LangChain 的工程化价值

通过这 4 个文件,我们见证了 LangChain 如何一步步将"调用大模型"这件事:

文件 能力 核心 API 工程价值
main.js 基础调用 ChatDeepSeek, .invoke() 统一接口,屏蔽模型差异
1.js 动态 Prompt PromptTemplate.fromTemplate(), .format() Prompt 可配置、可复用
2.js 单步 Chain .pipe(), RunnableSequence 流程封装,逻辑清晰
3.js 多步 Chain RunnableSequence.from([...]) 复杂任务分解,模块化

为什么需要 LangChain?

  • 避免重复造轮子:每个 LLM 的 API 都不同,LangChain 提供统一抽象。
  • 提升开发效率:用声明式方式构建 AI 应用,而非过程式拼接字符串。
  • 增强可维护性:Chain 可测试、可组合、可监控。
  • 面向未来:轻松集成 Memory、Tools、Agents 等高级功能。

完整项目链接:lesson_zp/ai/langchain/demo: AI + 全栈学习仓库


下一步建议

  1. 添加对话记忆 :使用 ConversationChainRunnableWithMessageHistory
  2. 接入 RAG :用 RetrievalQAChain 连接向量数据库,实现知识库问答。
  3. 构建 Agent:让 AI 自主选择工具(如搜索、计算、API 调用)。
  4. 部署优化:使用缓存、流式响应、token 限制等生产级技巧。

Happy Coding!🚀

相关推荐
m0_692457102 小时前
图像噪点消除
人工智能·算法
Brduino脑机接口技术答疑2 小时前
TDCA 算法在 SSVEP-BCI 中的时间戳技术要求与工程实现
人工智能·深度学习·机器学习·脑机接口·ssvep
KAI智习2 小时前
大模型榜单周报(2025/12/27)
人工智能·大模型
duanju6662 小时前
2025年AI写作工具实战测评:寻找真正适配网文创作的工具
人工智能·ai写作·ai漫剧工具·ai创作工具
musk12122 小时前
english-12-word-25-12-25 , 单词 legacy Linear scenes 深度学习知识卡片
人工智能·深度学习
qq_2518364572 小时前
RAG(Retrieval-Augmented Generation,检索增强生成)系统开发
ai编程
丝斯20112 小时前
AI学习笔记整理(36)——自然语言处理
人工智能·笔记·学习
ar01232 小时前
AR+AI:人工智能驱动增强现实的无限可能
人工智能·ar
mini_0552 小时前
elementPlus版本升级,el-select默认值显示问题
前端·javascript·vue.js