[LangChain] 22. 回退机制

什么是回退机制?

可以理解为一种“保险机制”,就像:

  • A 计划不行 → 执行 B 计划
  • A 服务器挂了 → 走 B 节点
  • A 模型限流 → 让本地模型顶上

主模型出错时,自动调用备用模型继续执行,用户无感知,体验不中断

如何实现回退

早期的时候,LangChain.js 没有提供相应的 API,所以需要通过 try/catch 来进行模拟:

async function safeInvoke(prompt) {
  try {
    const res = await openaiLLM.invoke(prompt);
    console.log("OpenAI模型回复:", res.content);
  } catch (err) {
    console.warn("OpenAI模型出错,切换到备用模型:", err.message);
    const fallbackRes = await llama3LLM.invoke(prompt);
    console.log("Llama3模型回复:", fallbackRes.content);
  }
}

不过这种模型不够优雅,而且有一定的局限性。

现在 LangChain.js 已经提供了对应的方法 withFallbacks,这是 Runnable 类型上的一个方法:给“主”Runnable 挂上备选 Runnable 列表。当主 Runnable 执行失败(抛错)时,会按顺序尝试后备,直到某个成功或全部失败为止。

  1. 在现有 Runnable 上调用

    const chainWithFallback = primaryRunnable.withFallbacks([fallback1, fallback2]);
    
  2. 直接实例化 RunnableWithFallbacks(工具类)

    import { RunnableWithFallbacks } from "@langchain/core/runnables";
    
    const r = new RunnableWithFallbacks({
      runnable: primaryRunnable, // 主链
      fallbacks: [fallback1, fallback2], // 备用链
    });
    

课堂练习

使用 withFallbacks 实现回退

import { ChatOpenAI } from "@langchain/openai";
import { ChatOllama } from "@langchain/ollama";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
import { RunnableLambda, RunnableSequence } from "@langchain/core/runnables";
import dotenv from "dotenv";
dotenv.config();
// 1. 创建 3 个链条

// 主模型
const primaryLLM = new ChatOpenAI({
  model: "gpt-4o-mini",
  temperature: 0,
  // apiKey: process.env.API_KEY,
  streaming: true,
});

// 备用模型
const secondaryLLM = new ChatOpenAI({
  model: "gpt-3.5-turbo",
  temperature: 0,
  // apiKey: process.env.API_KEY,
  streaming: true,
});

// 本地模型
const localLLM = new ChatOllama({
  model: "llama3",
  temperature: 0,
  streaming: true,
});

// 提示词
const pt = ChatPromptTemplate.fromTemplate(
  "请用中文、最多三句话回答:{question}"
);

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

// 一个中间件
// source 就是来源于哪个链:主链、备用链、本地链
function tagOutput(source) {
  return new RunnableLambda({
    func: async (text) => `[from=${source}] ${text}`,
  });
}

// 组装链条
const primaryChain = RunnableSequence.from([
  pt,
  primaryLLM,
  parser,
  // tagOutput("主链"),
]);
const secondaryChain = RunnableSequence.from([
  pt,
  secondaryLLM,
  parser,
  // tagOutput("备用链"),
]);
const localChain = RunnableSequence.from([
  pt,
  localLLM,
  parser,
  // tagOutput("本地链"),
]);

// 根据上面的三条链,组装成一条可回退的链条
const canFallbackChain = primaryChain.withFallbacks([
  secondaryChain,
  localChain,
]);

const stream = await canFallbackChain.streamEvents(
  {
    question: "RAG的核心是什么?",
  },
  {
    version: "v2",
  }
);

for await (const chunk of stream) {
  if (chunk.event === "on_chat_model_stream") {
    process.stdout.write(chunk.data.chunk.content);
  }
}

质量驱动的“软回退”

默认回退只在抛异常时触发。如果你想“结果不达标时也回退”,可以在主链末尾加一个“校验器”,不达标就主动抛错,让回退机制接管。

import { RunnableLambda } from "@langchain/core/runnables";

// 一个简单的输出质量校验器:含“抱歉/无法”就认为不达标
const rejectIfLowQuality = new RunnableLambda({
  func: async (text: string) => {
    const bad = /抱歉|无法|我不能/.test(text);
    if (bad) {
      throw new Error("LowQualityOutput"); // 主动抛错,触发回退
    }
    return text;
  },
});

// 把校验器挂在主链末尾
const primaryChecked = primaryChain.pipe(rejectIfLowQuality);

// 回退链保持不变
const resilientQuality = primaryChecked.withFallbacks([secondaryChain, localChain]);

const answer = await resilientQuality.invoke({
  question: "给我 3 条提升 RAG 召回质量的做法。",
});

-EOF-

posted @ 2025-11-25 14:43  Zhentiw  阅读(5)  评论(0)    收藏  举报