Prompt → Chain → Done:一场 LangChain 驱动的 Vibe Coding 之旅

37 阅读7分钟

前言

2022 年 ChatGPT 横空出世,Transformer 架构开启了 AIGC 时代。而 LangChain 作为 AI 应用开发框架,实际上比 ChatGPT 更早推出了 1.0+ 版本。

LangChain = Language + Chain,它通过统一的接口连接不同的 LLM(大语言模型),并将复杂的 AI 任务拆分成可执行的链式工作流,就像 n8n、Coze 这样的节点式工作流工具。

不用框架(直接调用 API)

// 需要手动处理所有细节

import fetch from 'node-fetch';
async function chatWithoutFramework(question) {
  const response = await fetch('https://api.deepseek.com/v1/chat/completions', {
    method'POST',

    headers: {
      'Content-Type''application/json',
      'Authorization'`Bearer ${process.env.DEEPSEEK_API_KEY}`
    },
    bodyJSON.stringify({
      model'deepseek-reasoner',
      messages: [
        { role'user'content: question }
      ],
      temperature0.7
    })
  });
  const data = await response.json();
  return data.choices[0].message.content;
}

// 使用

const answer = await chatWithoutFramework('你好');

直接调用大模型 API 的方式需要手动处理 HTTP 请求、响应解析、错误处理等底层细节,不仅繁琐而且容易出错。

同时,不同平台(如 OpenAI、DeepSeek、Claude)的接口格式各不相同,导致代码难以复用,每次切换模型都需要重写大量逻辑,增加了开发和维护成本。

用 LangChain

import { ChatDeepseek } from '@langchain/deepseek';
const model = new ChatDeepseek({
  model'deepseek-reasoner',
  apiKey: process.env.DEEPSEEK_API_KEY
});
// 使用 - 超级简单
const response = await model.invoke('你好');
console.log(response.content);

相比之下,使用 LangChain 可以极大简化这一过程。它封装了各类模型的调用细节,提供统一的编程接口,开发者只需几行代码即可完成模型调用。无论是更换模型还是切换服务商,都无需修改核心逻辑,显著提升了开发效率与代码的可维护性。


初始化langchain

现在我会将带你从零开始,使用 LangChain 快速接入 DeepSeek 模型,体验高效、整洁的大模型调用方式

我们首先初始化项目:

npm init -y 

接着安装 LangChain 核心包:

npm i langchain

然后安装 DeepSeek 的专用集成模块:

npm i @langchain/deepseek

最后,为了方便管理 API 密钥等敏感信息,我们安装 dotenv 来加载环境变量:

npm i dotenv

我们要在.env下加入我们deepseek的APIKEY:

image.png

DEEPSEEK_API_KEY=your-api-key

我们可以先创建一个 main.js 文件,快速体验 LangChain 开发的简洁与高效。

import 'dotenv/config';
import { ChatDeepSeek } from '@langchain/deepseek';
const model = new ChatDeepSeek({
     model:'deepseek-reasoner',
     temperature:0,
     // langchain 帮我们适配了市面上大多数的 llm
     // baseURL ? 不用 适配器模式  Provider
     // apiKey
    
})
// invoke 调用
const res = await model.invoke('用一句话解释什么是RAG?');
console.log(res.content);

image.png


PromptTemplate —— 提示词模板化

问题场景

直接拼接提示词存在明显局限:

const question = '什么是闭包?';
const prompt = `你是一个前端面试官,请用不超过50字回答:${question}`;
// 问题:硬编码、缺乏灵活性、难以复用

这种方式将逻辑与提示内容耦合在一起,一旦需求变化(比如角色、字数限制或问题类型),就需要修改代码,维护成本高。

解决方案

使用 PromptTemplate 实现提示词的模板化管理:

import { PromptTemplate } from '@langchain/core/prompts';

// 定义模板,使用 {变量名} 作为占位符
const prompt = PromptTemplate.fromTemplate(`
你是一个{role}。
请用不超过 {limit} 字回答以下问题:
{question}
`);

// 动态传入变量进行填充
const promptStr = await prompt.format({
  role: '前端面试官',
  limit: '50',
  question: '什么是闭包?'
});

const res = await model.invoke(promptStr)
console.log(res.content);
// 生成结果:
// "你是一个前端面试官。请用不超过 50 字回答以下问题:什么是闭包?"

image.png

调用 .format() 方法,传入一个对象,键名对应模板中的占位符,LangChain 会自动替换。

核心优势

可复用:定义一次模板,适用于多种输入组合

可维护:提示词逻辑集中管理,便于迭代和调试

动态灵活:根据运行时上下文生成定制化提示

结构清晰:支持变量校验,减少遗漏或拼写错误,提升健壮性


RunnableSequence(或 .pipe())—— 简洁的链式调用

问题场景

典型的 AI 应用往往包含多个步骤:

  1. 格式化提示词
  2. 调用大模型
  3. 处理返回结果

如果手动串联这些步骤,代码会显得分散且难以复用:

const formattedPrompt = await prompt.format({ topic: '闭包' });
const response = await model.invoke(formattedPrompt);
const answer = response.content;
// 问题:逻辑割裂,不易封装,难以测试或复用

更糟糕的是,若在 RunnableSequence.from() 中直接递归调用自身(例如 fullChain.invoke(...)),会导致无限嵌套,甚至内存溢出:

// 错误示例:自引用导致栈溢出
const fullChain = RunnableSequence.from([
  (input) => fullChain.invoke({ topic: input.topic })
]);

正确的做法是组合独立的子链,而非自我调用。

解决方案:使用 .pipe() 构建工作流

LangChain 提供了 .pipe() 方法,可将多个可运行单元(如 PromptTemplateLLM、自定义函数等)像管道一样连接起来,形成自动流转的处理链。

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}
`);

// 使用 .pipe() 将提示模板与模型连接成一条链
const chain = prompt.pipe(model);

// 一行调用,自动完成格式化 + 模型推理
const response = await chain.invoke({ topic: '闭包?' });
console.log(response.content); // 注意:DeepSeek 返回的是 .content,不是 .text

image.png

核心优势

✅ 自动流转:各步骤按顺序执行,无需手动传递中间结果

✅ 代码简洁:多步逻辑压缩为一行调用

✅ 高度可组合:每个节点都是独立的 Runnable,可自由拼接、测试或替换


RunnableSequence.from —— 构建复杂多步骤链

问题场景

在真实业务中,AI 任务往往不是“问一句答一句”那么简单,而是包含多个处理阶段。例如:

用户问题 → 详细解释 → 提炼要点 → 格式化输出

如果用传统方式手动串联,代码会变得冗长且难以维护:

// 步骤1:生成详细解释
const explanation = await explainChain.invoke({ topic: '闭包' });

// 步骤2:基于解释生成摘要
const summary = await summaryChain.invoke({ explanation: explanation.content });

// 步骤3:手动拼接最终结果
const result = `知识点: ${explanation.content} 总结: ${summary.content}`;

// 问题:步骤之间强耦合,逻辑分散,难以复用或测试

这种写法不仅可读性差,而且一旦流程变更(比如增加校验、重试或日志),就需要到处修改。


解决方案:使用 RunnableSequence.from() 编排多步流水线

LangChain 的 RunnableSequence.from() 允许我们将多个处理单元(如提示模板、模型调用、自定义函数)组合成一条声明式的执行链,自动传递中间结果。

import { RunnableSequence } from '@langchain/core/runnables';
import { PromptTemplate } from '@langchain/core/prompts';
import { ChatDeepSeek } from '@langchain/deepseek';

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

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

// 2. 摘要提炼模板
const summaryPrompt = PromptTemplate.fromTemplate(`
  请将以下概念解释总结为三个核心要点(每点不超过20字):
  {explanation}
`);

// 创建子链
const explainChain = explainPrompt.pipe(model);
const summaryChain = summaryPrompt.pipe(model);

// 构建完整处理链
const fullChain = RunnableSequence.from([
  // 步骤1:获取详细解释
  async (input) => {
    const res = await explainChain.invoke({ topic: input.topic });
    return res.content; // 提取文本内容作为下一步输入
  },
  // 步骤2:基于解释生成摘要,并格式化最终输出
  async (explanation) => {
    const res = await summaryChain.invoke({ explanation });
    return `知识点: ${explanation}\n总结: ${res.content}`;
  }
]);

// 一行调用,自动执行全流程
const response = await fullChain.invoke({ topic: '闭包' });
console.log(response);

image.png deepseek给我生成的内容如下:

知识点:**闭包(Closure)** 指一个函数能够记住并访问其词法作用域,即使该函数在其词法作用 域之外执行。

**定义与原理**:在JavaScript中,函数在创建时会绑定其所在的词法环境(作用域链)。当一个函数内部定义了另一个函数,且内层函数引用了外层函数的变量时,就形成了闭包。这使得外层函数的作用域在内层函数执行期间依然被保留,不会被垃圾回收。

**使用方式**:
1.  **数据封装/私有变量**:通过闭包创建只有特定函数能访问的“私有”数据。
    ```javascript
    function createCounter() {
        let count = 0; // 私有变量
        return function() {
            return ++count;
        };
    }
    ```
2.  **函数工厂与柯里化**:基于不同参数动态生成特定功能的函数。
3.  **模块模式**:在模块化编码中隔离作用域,避免全局污染。

闭包是JavaScript实现作用域控制、数据封装的基石,但也需注意不当使用可能导致内存无法释放。 总结: 1. 函数能记住并访问其词法作用域。
2. 内层函数引用外层变量形成闭包。
3. 用于封装私有变量,需注意内存释放。

优势

流程清晰:每一步职责单一,易于理解与调试

自动衔接:前一步的输出自动作为后一步的输入

高度可组合:子链可独立测试、复用或替换

易于扩展:后续可轻松插入日志、验证、缓存等中间件

通过 RunnableSequence.from(),我们可以将复杂的 AI 工作流转化为声明式、模块化、可维护的代码结构,真正实现“像搭积木一样构建智能应用”。


总结

通过本文的实战演示,我们从最基础的模型调用出发,逐步深入到提示词模板化(PromptTemplate)、链式工作流(.pipe())以及多步骤复杂流程编排(RunnableSequence.from),完整体验了 LangChain 如何将原本琐碎、易错的大模型交互过程,转化为模块化、可组合、可维护的开发范式。

LangChain 的核心价值,并不在于“封装了一个 API”,而在于它提供了一套面向 AI 应用的工程化思维:把提示词当作配置、把模型调用当作节点、把业务逻辑拆解为流水线。这种设计不仅大幅降低了开发门槛,更重要的是让 AI 应用具备了传统软件应有的可测试性、可复用性和可扩展性

无论是构建一个简单的问答机器人,还是设计支持 RAG、工具调用、多轮对话的智能客服系统(如我在 从 TRAE 脚手架 到 Coze 智能体 :打造支持 RAG 的编程教育客服系统中所实践的),LangChain 都能为你提供坚实的底层抽象和灵活的组合能力。

未来,随着更多 LLM 厂商和工具生态的接入,LangChain 这类框架将成为连接“模型能力”与“真实业务”的关键桥梁。而作为开发者,掌握这套“链式思维”,或许就是迈入下一代应用开发的第一步。