LangChain Chain & Pipe 知识点详解

🎯 本文将带你深入理解 LangChain 中的 Chain 和 Pipe 机制,通过实际代码示例掌握串行、并行、流式输出等核心技能。

📚 核心概念

什么是 Runnable?

Runnable 是 LangChain 中的核心接口,所有可执行的组件都实现了这个接口:

  • Prompt Template - 提示词模板
  • LLM / ChatModel - 语言模型
  • Output Parser - 输出解析器
  • Chain - 链(本身也是 Runnable)

每个 Runnable 都支持以下方法:

方法 说明 使用场景
invoke() 同步执行 普通调用
stream() 流式执行 实时输出
batch() 批量执行 处理多个输入

什么是 Pipe?

pipe() 是连接多个 Runnable 的方法,将前一个组件的输出作为后一个组件的输入:

javascript 复制代码
// 基本用法
const chain = prompt.pipe(llm).pipe(outputParser);

// 等价于
// input → prompt → llm → outputParser → output

Pipe 可接收的参数类型

pipe() 方法可以接收任何 Runnable 类型的对象,包括:

类型 说明 常见示例
Prompt Templates 提示词模板 ChatPromptTemplate, PromptTemplate
LLMs / ChatModels 语言模型 ChatDeepSeek, ChatOpenAI, ChatAnthropic
Output Parsers 输出解析器 StringOutputParser, JsonOutputParser, StructuredOutputParser
Runnable 组合器 组合多个 Runnable RunnableSequence, RunnableParallel, RunnablePassthrough, RunnableLambda
函数 (Function) 自动包装为 RunnableLambda 普通函数、箭头函数、async 函数
其他 Runnable 任何实现 Runnable 接口的对象 Tools, Retrievers, 自定义 Runnable

示例:不同类型参数的使用

javascript 复制代码
import { RunnableLambda } from "@langchain/core/runnables";

// 1. 最常见:Prompt → LLM → OutputParser
const basicChain = prompt.pipe(llm).pipe(new StringOutputParser());

// 2. 使用函数(自动包装为 RunnableLambda)
const chainWithFunction = prompt
  .pipe(llm)
  .pipe(new StringOutputParser())
  .pipe((text) => text.toUpperCase()); // 普通函数

// 3. 使用 async 函数
const chainWithAsync = prompt
  .pipe(llm)
  .pipe(new StringOutputParser())
  .pipe(async (text) => {
    // 可以在这里做异步操作
    return `处理后: ${text}`;
  });

// 4. 使用 RunnableLambda 显式包装
const customRunnable = new RunnableLambda({
  func: (input) => `[${input}]`,
});
const chainWithLambda = prompt
  .pipe(llm)
  .pipe(new StringOutputParser())
  .pipe(customRunnable);

// 5. 嵌套使用其他 Chain
const innerChain = prompt2.pipe(llm).pipe(new StringOutputParser());
const outerChain = prompt1
  .pipe(llm)
  .pipe(new StringOutputParser())
  .pipe(innerChain);

// 6. 使用 RunnableParallel 进行分支
const branchChain = prompt
  .pipe(llm)
  .pipe(new StringOutputParser())
  .pipe(
    RunnableParallel.from({
      upper: (text) => text.toUpperCase(),
      lower: (text) => text.toLowerCase(),
      length: (text) => text.length,
    })
  );

类型匹配规则

⚠️ 重要pipe() 连接时,前一个 Runnable 的输出类型必须与后一个 Runnable 的输入类型兼容

javascript 复制代码
// ✅ 正确:LLM 输出 AIMessage → StringOutputParser 接收 AIMessage
prompt.pipe(llm).pipe(new StringOutputParser());

// ✅ 正确:StringOutputParser 输出 string → 函数接收 string
prompt
  .pipe(llm)
  .pipe(new StringOutputParser())
  .pipe((text) => text.trim());

// ❌ 错误:LLM 输出 AIMessage → 函数期望 string
// prompt.pipe(llm).pipe((msg) => msg.trim()); // msg 是 AIMessage 对象,不是 string

🔧 示例详解

示例 1:Pipe 的基本使用

最基础的 Chain 组合:Prompt → LLM → OutputParser

javascript 复制代码
import { ChatDeepSeek } from "@langchain/deepseek";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
import "dotenv/config";

// 初始化 LLM
const llm = new ChatDeepSeek({
  model: "deepseek-chat",
  temperature: 0.7,
});

// 创建 Prompt
const translatePrompt = ChatPromptTemplate.fromMessages([
  [
    "system",
    "你是一个专业的翻译助手,请将 {source_lang} 翻译成 {target_lang}。",
  ],
  ["human", "{text}"],
]);

// 使用 pipe() 组合成 Chain
const translateChain = translatePrompt.pipe(llm).pipe(new StringOutputParser());

// 调用 Chain
const result = await translateChain.invoke({
  source_lang: "English",
  target_lang: "中文",
  text: "Hello, LangChain!",
});

console.log(result); // 你好,LangChain!

要点解析:

  • ChatPromptTemplate.fromMessages() 创建聊天提示词模板
  • pipe() 将组件串联起来
  • StringOutputParser 将 AI 返回的消息对象转换为纯字符串
  • invoke() 传入模板变量,执行整个链

示例 2:Stream 流式输出

流式输出可以实时显示 AI 生成的内容,提升用户体验:

javascript 复制代码
const storyPrompt = ChatPromptTemplate.fromMessages([
  ["system", "你是一个创意写作助手"],
  ["human", "请写一个关于 {topic} 的小故事"],
]);

const storyChain = storyPrompt.pipe(llm).pipe(new StringOutputParser());

// 使用 stream() 获取流式输出
const stream = await storyChain.stream({
  topic: "程序员与 AI",
});

// 实时打印每个 chunk
for await (const chunk of stream) {
  process.stdout.write(chunk);
}

流式输出的优势:

  • ✅ 用户无需等待完整响应
  • ✅ 实时看到生成过程
  • ✅ 适合生成长文本
  • ✅ 改善交互体验

示例 3:串行任务 (RunnableSequence)

当任务有依赖关系时,使用串行执行:

javascript 复制代码
import { RunnableSequence } from "@langchain/core/runnables";

// 步骤 1: 生成标题
const titleChain = titlePrompt.pipe(llm).pipe(new StringOutputParser());

// 步骤 2: 根据标题生成内容
const contentChain = contentPrompt.pipe(llm).pipe(new StringOutputParser());

// 串行组合
const sequentialChain = RunnableSequence.from([
  // 第一步:生成标题
  {
    title: titleChain,
    topic: (input) => input.topic,
  },
  // 第二步:生成内容(依赖标题)
  {
    title: (input) => input.title,
    content: RunnableSequence.from([
      (input) => ({ title: input.title }),
      contentChain,
    ]),
  },
]);

const result = await sequentialChain.invoke({ topic: "AI 学习" });
// result = { title: "...", content: "..." }

串行任务特点:

  • 📌 任务按顺序执行
  • 📌 后续任务可以使用前置任务的结果
  • 📌 适合有依赖关系的多步骤流程

示例 4:并行任务 (RunnableParallel)

当多个任务互不依赖时,使用并行执行提升效率:

javascript 复制代码
import { RunnableParallel } from "@langchain/core/runnables";

// 定义三个独立的分析任务
const sentimentChain = sentimentPrompt.pipe(llm).pipe(new StringOutputParser());
const keywordsChain = keywordsPrompt.pipe(llm).pipe(new StringOutputParser());
const languageChain = languagePrompt.pipe(llm).pipe(new StringOutputParser());

// 并行组合
const parallelChain = RunnableParallel.from({
  sentiment: sentimentChain,
  keywords: keywordsChain,
  language: languageChain,
});

const result = await parallelChain.invoke({ text: "待分析的文本" });
// result = { sentiment: "积极", keywords: "...", language: "中文" }

并行任务优势:

  • ⚡ 显著减少总执行时间
  • ⚡ 充分利用 API 并发能力
  • ⚡ 适合多维度分析场景

示例 5:RunnablePassthrough 数据透传

在复杂链中保留原始输入:

javascript 复制代码
import { RunnablePassthrough } from "@langchain/core/runnables";

const translateWithOriginal = RunnableParallel.from({
  original: new RunnablePassthrough(), // 原样传递
  translated: translateChain,
});

const result = await translateWithOriginal.invoke({ text: "Hello" });
// result = { original: { text: "Hello" }, translated: "你好" }

示例 6:串行 + 并行混合

实际项目中常需要组合使用:

scss 复制代码
输入 → 预处理(串行) → 多维分析(并行) → 汇总报告(串行) → 输出
javascript 复制代码
const complexChain = RunnableSequence.from([
  // 步骤 1: 预处理
  {
    original: (input) => input.text,
    processed: preprocessChain,
  },
  // 步骤 2: 并行分析
  {
    original: (input) => input.original,
    analysis: RunnableParallel.from({
      sentiment: sentimentChain,
      keywords: keywordsChain,
    }),
  },
  // 步骤 3: 汇总
  summaryChain,
]);

📊 核心对象速查表

对象 作用 常用方法
ChatPromptTemplate 创建聊天提示词模板 fromMessages()
StringOutputParser 将输出转为字符串 直接 pipe()
JsonOutputParser 将输出解析为 JSON 直接 pipe()
RunnableSequence 串行执行 from([...])
RunnableParallel 并行执行 from({...})
RunnablePassthrough 数据透传 new RunnablePassthrough()

🎨 流程图解

基础 Pipe 流程

css 复制代码
┌─────────┐    ┌─────────┐    ┌──────────────┐    ┌─────────┐
│  Input  │ → │ Prompt  │ → │     LLM      │ → │ Parser  │ → Output
└─────────┘    └─────────┘    └──────────────┘    └─────────┘

串行任务流程

vbnet 复制代码
┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐
│  Input  │ → │ Step 1  │ → │ Step 2  │ → │ Step 3  │ → Output
└─────────┘    └─────────┘    └─────────┘    └─────────┘

并行任务流程

arduino 复制代码
                 ┌─────────┐
              ┌→ │ Task A  │ ─┐
┌─────────┐   │  └─────────┘  │   ┌──────────────┐
│  Input  │ ──┼→ ┌─────────┐  ├→ │ Merge Result │ → Output
└─────────┘   │  │ Task B  │  │   └──────────────┘
              │  └─────────┘  │
              └→ ┌─────────┐ ─┘
                 │ Task C  │
                 └─────────┘

💡 最佳实践

1. 合理选择执行方式

javascript 复制代码
// ❌ 错误:无依赖任务串行执行,浪费时间
const result1 = await chain1.invoke(input);
const result2 = await chain2.invoke(input);
const result3 = await chain3.invoke(input);

// ✅ 正确:无依赖任务并行执行
const results = await RunnableParallel.from({
  r1: chain1,
  r2: chain2,
  r3: chain3,
}).invoke(input);

2. 使用流式输出提升体验

javascript 复制代码
// ❌ 用户需要等待完整响应
const result = await chain.invoke(input);

// ✅ 实时显示生成内容
const stream = await chain.stream(input);
for await (const chunk of stream) {
  process.stdout.write(chunk);
}

3. 使用 OutputParser 简化处理

javascript 复制代码
// ❌ 手动提取内容
const response = await llm.invoke(prompt);
const text = response.content;

// ✅ 使用 Parser 自动处理
const chain = prompt.pipe(llm).pipe(new StringOutputParser());
const text = await chain.invoke(input);

🔗 相关资源


📝 练习建议

  1. 基础练习:修改翻译 Chain,支持多种语言互译
  2. 进阶练习:创建一个并行分析 Chain,同时分析文本的情感、主题、风格
  3. 综合练习:实现一个"文章改写助手",包含:分析原文 → 并行生成多个改写版本 → 评选最佳版本

🎯 总结

知识点 核心要点
pipe() 连接 Runnable 组件,构建处理管道
stream() 流式输出,提升用户体验
RunnableSequence 串行执行,处理有依赖的任务
RunnableParallel 并行执行,提升处理效率
RunnablePassthrough 数据透传,保留原始输入

掌握这些核心概念后,你就可以灵活组合构建复杂的 AI 应用流程了!


📖 本文配套代码langchain-learning/src/chain.js

💻 推荐工具 :使用 Trae_solo 获得更好的 AI 开发体验

Happy Coding!

相关推荐
一粒麦仔1 小时前
A2A协议:打破AI智能体孤岛,构建智能协作新时代
ai编程
j***29481 小时前
如何在Windows系统上安装和配置Node.js及Node版本管理器(nvm)
windows·node.js
赋范大模型技术社区2 小时前
LangChain1.0 搭建法务合同审核 Agent(附源码)
langchain·ocr·agent·rag·文档审核·langchain1.0
进击的野人2 小时前
Node.js文件系统(fs模块)深度解析与实践应用
后端·正则表达式·node.js
猫头虎2 小时前
本地部署 Stable Diffusion3.5超详细教程
stable diffusion·开源·prompt·github·aigc·midjourney·ai编程
该用户已不存在2 小时前
一句话让一个AI为我花了(划掉)生成一个APP,Google Antigravity 实操
后端·ai编程·gemini
z***3352 小时前
使用Node.js搭配express框架快速构建后端业务接口模块Demo
node.js·express
极客密码3 小时前
【开源】一个丝滑的 Claude Code 环境变量快速切换工具
ai编程·claude·cursor
Peter_Monster4 小时前
LangChain到底是什么?
人工智能·langchain·大模型