Skip to content

Runnable接口 ​

RunnableSequence

.pipe() 是逐个拼接,RunnableSequence.from([...]) 则是显式声明流程结构,将多个步骤写成数组更清晰。

课堂练习:快速上手示例

js
import { ChatOllama } from "@langchain/ollama";
import { PromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
import { RunnableLambda, RunnableSequence } from "@langchain/core/runnables";

// 1. 创建 Prompt 模板
const pt = PromptTemplate.fromTemplate("请使用中文解释以下概念:{topic}");

// 2. 创建本地模型
const model = new ChatOllama({ model: "llama3", temperature: 0.7 });

// 3. 创建字符串解析器
const parser = new StringOutputParser();

// 4. 插件
const fn = (text) => {
  return text.replace(/闭包/g, "*&*闭包*&*");
};
const highlight = RunnableLambda.from(fn);

const chain = RunnableSequence.from([pt, model, parser, highlight]);

const res = await chain.invoke({
  topic: "闭包",
});

console.log(res);

RunnablePassthrough

将输入原样传给输出,中间不做任何处理。

js
import { RunnablePassthrough } from "@langchain/core/runnables";

const passthrough = new RunnablePassthrough();

// 相当于输入什么,输出就是什么
const result = await passthrough.invoke("Hello, LangChain!");
console.log(result); // 输出:Hello, LangChain!

🤔 这有什么意义?

实例化 RunnablePassthrough 的时候,接收一个配置对象,该对象中可以配置 func 副作用函数,用于对输入做一些副作用处理,例如记录日志、写入数据库、做埋点等。

js
import { RunnablePassthrough } from "@langchain/core/runnables";

const logger = new RunnablePassthrough({
  // 一个副作用函数
  func: async (input) => {
    console.log("收到输入:", input);
    // 对输入做一些副作用处理,例如记录日志、写入数据库、做埋点等
  },
}); 

const res = await logger.invoke("LangChain");
console.log(res);

有时希望为传入的对象补充字段(如时间戳、用户信息、API 结果等),这时可以使用 .assign()。 它的作用是“给上下文添加字段”,就像在中间插入一步 Object.assign:

js
import { RunnablePassthrough } from "@langchain/core/runnables";

const injector = RunnablePassthrough.assign({
  timestamp: async () => new Date().toISOString(),
  meta: async () => ({
    region: "us-east",
    requestId: "req-456",
  }),
});

const result = await injector.invoke({ query: "Vue 是什么?" });

console.log(result);
/*
  {
    query: 'Vue 是什么?',
    timestamp: '2025-08-11T08:07:35.358Z',
    meta: { region: 'us-east', requestId: 'req-456' }
  }
*/

可以用它来:

  • 插入时间戳、用户 ID、请求 ID
  • 注入工具结果:摘要、标签、翻译、情绪分析
  • 为 Prompt 添加额外字段

课堂练习:

  1. 打印用户输入日志
  2. 注入 userId
  3. 拼接 prompt
  4. 调用本地模型
  5. 解析返回结果
js
// 课堂练习:

// 1. 打印用户输入日志
// 2. 注入 userId
// 3. 拼接 prompt
// 4. 调用本地模型
// 5. 解析返回结果

import { StringOutputParser } from "@langchain/core/output_parsers";
import { PromptTemplate } from "@langchain/core/prompts";
import { RunnablePassthrough, RunnableSequence } from "@langchain/core/runnables";
import { ChatOllama } from "@langchain/ollama";

// 1. 记录日志
const logFunc = new RunnablePassthrough({
    func: async (input) => {
        // 做一些记录:上报、数据操作、埋点
        console.log("用户提的问题为:", input.query);
    },
});

// 2. 注入用户id
const injector = RunnablePassthrough.assign({
    userId: async () => "user-12345",
});

// 3. 提示词模版
const pt = PromptTemplate.fromTemplate(
    "请使用中文帮用户{userId}解答问题:{query},回答问题时以情爱的{userId}开头",
);

console.log(await injector.invoke(pt));

// 模型
const model = new ChatOllama({
    model: "qwen3.5:4b",
    temperature: 0.7,
});

// 解析器
const parser = new StringOutputParser();

// 构建完整的链
const chain = RunnableSequence.from([logFunc, injector, pt, model, parser]);

const result = await chain.stream({
    query: "闭包是什么意思",
});

for await (const chunk of result) {
    process.stdout.write(chunk);
}