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!

相关推荐
AlfredZhao21 小时前
LangChain、LangFlow、LangGraph:一文讲清三大 LLM 框架的定位与差异
langchain·langgraph·langflow
孟健1 天前
出海圈都说好用的 Claude Skills,我用 Codex 试了一遍:UI/UX 直接起飞大家好,我是孟健。 最近我研究了一下出海圈很多人都在用、并且口碑
ai编程·claude
奶糖的次元空间1 天前
带你用 Javascript 生成器玩转「会暂停」的函数
node.js
用户47949283569151 天前
拒绝傻瓜式截断 Diff:聊聊我在 AI Commit 插件里做的 7 个技术微创新
ai编程·trae·vibecoding
嗡嗡嗡qwq1 天前
Claude Code体验-ai高级工程师
ai编程·claude code
闲云一鹤1 天前
2026 最新 ComfyUI 教程 - 本地部署 AI 生图模型 - Z-Image-Turbo
前端·人工智能·ai编程
树叶会结冰1 天前
Milvus:可检索记忆的漂流瓶
langchain·milvus·llamaindex
狼爷1 天前
一文看懂 AI 世界里的新黑话Skills、MCP、Projects、Prompts
人工智能·openai·ai编程
西柚小萌新1 天前
【人工智能:Agent】--5.Langchain模型+工具
langchain
该用户已不存在1 天前
Symfony AI v0.2.0 正式发布:功能解读与实战指南
php·ai编程·symfony