从零理解 LangChain:如何用 DeepSeek 大模型构建一个“前端面试官”问答工具?

40 阅读9分钟

我会从 核心概念拆解 → 代码逐行解析 → 运行逻辑梳理 三个层面,带你彻底弄懂它。全程使用通俗语言,不跳过任何基础细节——哪怕你今天第一次听说 LangChain,也能看明白。

我们将从最简单的“一句话解释闭包”开始,再升级到更实用的“详细讲解 + 自动总结”工作流,让你真正理解 LangChain 如何让 AI 应用开发变得像搭积木一样简单


一、先搞懂核心概念(代码的 “骨架”)

在看具体代码前,必须先建立几个关键认知。否则,代码会显得杂乱无章,像一堆零件堆在一起。

1. 什么是「Chain(链 / 工作流)」?

你的代码注释里其实已经点出了本质:

AI 业务复杂,需要分步骤、按顺序处理,每一步都是一个 “可执行、可配置” 的独立节点,把这些节点按顺序串联起来,就形成了 Chain(工作流 / 链条)

▶ 简单场景(单步 Chain)

你想让 AI 回答“什么是闭包?”,但要求它:

  • 扮演前端专家
  • 只用一句话回答

这就不能直接扔问题给大模型,而要:
构造提示词 → 调用模型 → 输出结果
这就是一条最简单的 线性 Chain(Sequential Chain)

▶ 复杂场景(多步 Chain)

更真实的场景可能是:

先让 AI 详细讲解“闭包”(含定义、原理、用法,≤300 字),
再让它把讲解内容提炼为 3 个核心要点(每点 ≤20 字)。

这就需要 两个 AI 任务串联执行
讲解 → 总结 → 拼接输出
这正是 RunnableSequence 的用武之地。

Chain 的价值:把复杂任务拆解为可复用、可组合的小单元,就像搭积木一样灵活。


2. 关键工具 / 类的作用(代码的 “零件”)

工具 / 类核心作用
dotenv/config加载 .env 文件中的环境变量(如 API 密钥),避免硬编码泄露
ChatDeepSeek对接「深度求索(DeepSeek)」大模型的客户端,负责和大模型服务器通信
PromptTemplate提示词模板,用于规范化向大模型提问的格式,避免手动拼接字符串的混乱
RunnableSequence链式执行的核心类,负责将多个 “可运行节点” 按顺序串联,实现自动化工作流
pipe() 方法简化版的 “节点连接工具”,本质是创建 RunnableSequence,把前一个节点的输出作为后一个节点的输入
invoke() 方法启动工作流的 “开关”,传入初始参数,触发整个链条按顺序执行并返回结果

这些“零件”组合起来,就能搭建出一个自动化的 AI 应用流水线。


二、基础版:单步 Chain —— 一句话解释“闭包”

我们先从最简单的例子入手,建立直觉。

1. 加载环境变量(必备配置)

ts
编辑
import 'dotenv/config';
  • 作用:自动加载项目根目录下的 .env 文件。
  • 为什么重要
    DeepSeek 的 API 密钥(如 DEEPSEEK_API_KEY=xxx)属于敏感信息,绝不能写死在代码里
  • 没有这行会怎样
    后续调用大模型时因缺少密钥而认证失败,直接报错。

2. 导入需要的工具类

ts
编辑
import { ChatDeepSeek } from '@langchain/deepseek'; 
import { PromptTemplate } from '@langchain/core/prompts';
  • @langchain/deepseek:LangChain 官方为 DeepSeek 提供的适配模块。
  • @langchain/core/prompts:处理提示词模板的核心能力。

💡 这些不是通用 JavaScript 功能,而是 LangChain 框架提供的“乐高积木”。


3. 创建提示词模板(规范提问格式)

ts
编辑
const prompt = PromptTemplate.fromTemplate(`
  你是一个前端专家,用一句话解释: {topic}
`);
  • {topic} 是一个“变量插槽”,后续可动态替换为“闭包”“事件循环”等。

  • 好处:避免每次手写 "你是一个前端专家,请解释...",让提示词结构统一、易于维护。

  • ✅ 示例:当传入 topic: '闭包',模板自动渲染为:

    “你是一个前端专家,用一句话解释: 闭包”


4. 初始化大模型

ts
编辑
const model = new ChatDeepSeek({
    model: 'deepseek-reasoner', // 推理型模型,适合技术问答
    temperature: 0.7,           // 平衡严谨性与灵活性
});
  • 相当于“创建了一个能和 DeepSeek 对话的机器人”。
  • temperature: 0.7:既不会死板重复,也不会胡说八道,非常适合技术解释。

5. 串联节点,形成工作流(核心:创建 Chain)

ts
编辑
const chain = prompt.pipe(model);

这是整段代码的灵魂所在。你可能会疑惑:“是不是先创建一条空链子,再把 prompt 和 model 接上去?”

答案是否定的!

▶ pipe() 干了什么?

  • 一步到位创建完整链式工作流(Chain)。

  • 同时完成两件事:

    1. 定义执行顺序:先执行 prompt,再执行 model
    2. 自动传递数据prompt 的输出 → 自动作为 model 的输入

🔍 技术上,prompt.pipe(model) 等价于:

ts
编辑
const chain = RunnableSequence.from([prompt, model]);

所以 chain instanceof RunnableSequence 会返回 true

▶ 为什么叫 “pipe(管道)”?

想象水流从水管一端流入,经过过滤器,再流出——pipe() 就是那根“水管”,确保数据按顺序、无损地从前一个节点流向后一个节点。


6. 启动工作流,传入参数并获取结果

ts
编辑
const response = await chain.invoke({ topic: '闭包' });
console.log(response.text);
  • await:因为调用大模型是异步操作(需要网络请求),必须等待响应。

  • chain.invoke(...):启动整个工作流的“开关”。

  • 执行过程:

    1. prompt 渲染出完整提示词 → “你是一个前端专家,用一句话解释: 闭包”
    2. pipe() 自动将提示词传给 model
    3. model 调用 DeepSeek → 返回回答
    4. response.text 是纯文本结果

✅ 示例输出:
“闭包是指一个函数能够访问并保留其定义时所在作用域的变量,即使该函数在其定义作用域外被调用的一种前端编程特性。”


三、进阶版:多步骤工作流 —— 讲解 + 总结

现在升级到真实场景:先详细讲解,再自动总结

1. 新增依赖:引入 RunnableSequence

ts
编辑
import { RunnableSequence } from '@langchain/core/runnables';
  • 用于串联多个异步任务,构建复杂工作流。

2. 定义两个提示词模板

(1)讲解模板:要求全面但简洁

ts
编辑
const explainPrompt = PromptTemplate.fromTemplate(`
    你是一个前端专家,请详细介绍以下概念: {topic}
    要求:覆盖定义、原理、使用方式,不超过300字。
`);

(2)总结模板:强制提炼要点

ts
编辑
const summaryPrompt = PromptTemplate.fromTemplate(`
  请将以下前端概念解释总结为3个核心要点(每点不超过20字):
  {explaination}  
`);

注意:第二个模板的占位符是 {explaination}(拼写需一致),将由第一步的输出填充。


3. 创建两个单任务链

ts
编辑
const explainChain = explainPrompt.pipe(model);   // 输入 topic → 输出详细讲解
const summaryChain = summaryPrompt.pipe(model);  // 输入讲解 → 输出3个要点

每个 Chain 都是一个独立、可复用的 AI 任务单元。


4. 用 RunnableSequence 串联两步

ts
编辑
const fullChain = RunnableSequence.from([
    // 第一步:执行讲解链
    (input) => explainChain.invoke({ topic: input.topic }).then(res => res.content),
    
    // 第二步:用讲解内容执行总结链,并拼接最终结果
    (explaination) => summaryChain.invoke({ explaination }).then(res => 
        `知识点: ${explaination}\n总结: ${res.content}`
    )
]);

▶ 关键机制解析:

  • 第一步输入input 是 fullChain.invoke({ topic: '闭包' }) 传入的对象。
  • 数据流转:第一步返回的 res.content(即详细讲解文本)自动作为第二步的参数 explaination
  • 异步处理:每个 .invoke() 返回 Promise,用 .then() 提取文本内容。
  • 结果拼接:最终返回包含“讲解 + 总结”的完整字符串。

🔑 核心思想RunnableSequence 让多步骤 AI 任务像工厂流水线一样自动运行。


5. 执行完整工作流(必须包裹在 async 函数中)

ts
编辑
async function run() {
    const response = await fullChain.invoke({ topic: '闭包' });
    console.log(response);
}
run();
  • 必须使用 async/await,因为涉及多次异步 AI 调用。
  • fullChain.invoke(...) 触发整个链条:讲解 → 总结 → 拼接 → 返回。

四、整体运行逻辑梳理(从启动到输出)

基础版流程(单步 Chain):

text
编辑
加载 .env → 导入工具 → 创建 model/prompt → pipe() 串联 → invoke() 执行 → 输出

进阶版流程(多步 Chain):

text
编辑
1. 初始化:model + 两个模板 + 两个单链
2. 调用 fullChain.invoke({ topic: '闭包' })
   ↓
3. 第一步:explainChain 生成详细讲解(≤300字)
   ↓
4. 第二步:summaryChain 用讲解内容生成3个要点(每点≤20字)
   ↓
5. 拼接结果:`知识点: ...\n总结: ...`6. 控制台打印完整输出

整个过程全自动、无手动干预——这就是 LangChain 工作流的威力


五、运行前提与完整代码

运行前提:

  1. Node.js ≥ v18(支持 ES Module)

  2. package.json 中添加 "type": "module"

  3. 安装依赖:

    bash
    编辑
    npm install dotenv @langchain/deepseek @langchain/core
    
  4. 创建 .env 文件:

    text
    编辑
    DEEPSEEK_API_KEY=你的_api_key(从 deepseek.com 获取)
    

完整可运行代码:

ts
编辑
import { ChatDeepSeek } from '@langchain/deepseek';
import { PromptTemplate } from '@langchain/core/prompts';
import 'dotenv/config';
import { RunnableSequence } from '@langchain/core/runnables';

// 1. 初始化模型
const model = new ChatDeepSeek({
    model: 'deepseek-reasoner',
    temperature: 0.7,
});

// 2. 定义提示词模板
const explainPrompt = PromptTemplate.fromTemplate(`
    你是一个前端专家,请详细介绍以下概念: {topic}
    要求:覆盖定义、原理、使用方式,不超过300字。
`);

const summaryPrompt = PromptTemplate.fromTemplate(`
  请将以下前端概念解释总结为3个核心要点(每点不超过20字):
  {explaination}  
`);

// 3. 创建单任务链
const explainChain = explainPrompt.pipe(model);
const summaryChain = summaryPrompt.pipe(model);

// 4. 串联成完整工作流
const fullChain = RunnableSequence.from([
    (input) => explainChain.invoke({ topic: input.topic }).then(res => res.content),
    (explaination) => summaryChain.invoke({ explaination }).then(res => 
        `知识点: ${explaination}\n总结: ${res.content}`
    )
]);

// 5. 执行
async function run() {
    const response = await fullChain.invoke({ topic: '闭包' });
    console.log(response);
}
run();

六、运行结果示例(参考)

plaintext
编辑
知识点: 闭包是指函数能够访问其定义时所在的词法作用域,即使该函数在其词法作用域之外执行。原理:JavaScript 函数在创建时会形成词法环境,闭包会保留对外部词法环境的引用,使其变量不被垃圾回收。使用方式:可用于创建私有变量(如计数器)、延迟执行(如定时器回调)、模块化开发(隐藏内部逻辑),示例:function outer() { let a = 1; return function inner() { console.log(a++); }; } const fn = outer(); fn(); // 1。
总结: 1. 函数可访问自身定义时的词法作用域
2. 保留外部变量引用,避免被垃圾回收
3. 用于创建私有变量与模块化开发

七、核心概念总结表

概念作用类比
PromptTemplate结构化提示词,支持参数填充Word 模板 + 填空
ChatDeepSeekDeepSeek 模型的封装客户端数据库连接池
pipe()连接两个可运行单元Linux 管道 `cmd1cmd2`
RunnableSequence多步骤顺序执行的工作流工厂流水线
invoke()启动整个 Chain 的“开关”按下启动按钮

八、LangChain 的真正价值

  • 简单场景(一句话解释)→ 用 pipe() 快速连接

  • 复杂场景(讲解 + 总结 + 校验 + 格式化)→ 用 RunnableSequence 精细编排

  • 核心优势

    • 屏蔽底层细节(不用写 fetch
    • 提供高层抽象(模板、链、工具)
    • 支持灵活组合(节点可插拔、可复用、可测试)

🌟 建议:先理解手写 API 调用的原理,再用 LangChain 提效——知其然,更知其所以然。