LANGCHAIN_MIGRATION_PLAN.md 7.1 KB

LangChain 统一改造计划

目标

将项目从"直接 API 调用"模式,迁移到"LangChain 统一调用"模式,实现:

  1. LLM、TTS、Image、Video 统一接口
  2. 自动模型切换
  3. LangGraph 流程编排

当前状态

✅ 已完成

  • Text 模型(qwen3.6-plus、qwen3.5-flash、MiniMax-M2.7、doubao-seed-2.0-code)已配置在 models.json
  • 通过 ChatOpenAI + baseURL 方式调用(OpenAI-compatible)
  • 模型验证器 models-validator.ts 可批量验证模型

❌ 待改造

  • TTS:AliyunTTSProvider 直接 axios 调用
  • Image:MiniMax 直接 API
  • Video:MiniMax 直接 API
  • LangGraph:直接 axios 调用 LLM,未使用 LangChain Tool

阶段一:统一 LLM 调用(已完成 ✅)

目标

所有 Text 模型通过 LangChain ChatOpenAI 统一调用

配置结构

{
  "vendors": {
    "bailian": {
      "baseUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1",
      "apiKey": "sk-xxx",
      "apiType": "openai-chat",
      "models": [...]
    },
    "minimax": { ... },
    "volcengine": { ... }
  }
}

涉及文件

  • src/config/models.json - 厂商/模型配置 ✅
  • src/config/index.ts - 配置读取逻辑 ✅
  • src/services/llm/index.ts - LLM 服务(getLLM/callLLM) ✅
  • src/config/models-validator.ts - 模型验证器 ✅

阶段二:TTS 包装成 LangChain Tool

目标

TTS Provider 实现 Tool 接口,可在 LangGraph 节点中调用

实现方式

1. 定义 TTS Tool 接口

import { Tool } from '@langchain/core/tools';

interface TTSResult {
  audioUrl: string;
  duration: number;
}

// TTS Tool 包装
const ttsTool = new Tool({
  name: "text_to_speech",
  description: "将文本转换为语音,输入文本返回音频URL",
  returnDirect: true,
  args: z.object({
    text: z.string().describe("要转换的文本"),
    voice: z.string().optional().describe("音色ID"),
  }),
  async invoke(input: { text: string; voice?: string }) {
    const provider = new AliyunTTSProvider();
    return await provider.synthesize(input.text, input.voice || 'Cherry');
  },
});

2. 更新 TTS Provider

  • 路径:src/modules/tts/aliyun.provider.ts
  • 修改为支持 Tool 调用格式

3. MiniMax TTS 支持

  • 当前 MiniMax TTS 未集成,需新增 Provider
  • 路径:src/modules/tts/minimax.provider.ts

涉及文件

  • src/modules/tts/aliyun.provider.ts - 修改
  • src/modules/tts/minimax.provider.ts - 新增
  • src/tools/tts.tool.ts - Tool 封装
  • src/config/models.json - 新增 tts 工具定义

阶段三:Image 生成包装成 LangChain Tool

目标

Image 模型(MiniMax-image-01)包装成 Tool

实现方式

const imageTool = new Tool({
  name: "image_generation",
  description: "根据文本描述生成图片",
  args: z.object({
    prompt: z.string().describe("图像描述"),
    model: z.string().optional(),
  }),
  async invoke(input: { prompt: string }) {
    const provider = new MiniMaxImageProvider();
    return await provider.generate(input.prompt);
  },
});

涉及文件

  • src/modules/image/minimax.provider.ts - 新增
  • src/tools/image.tool.ts - Tool 封装

阶段四:Video 生成包装成 LangChain Tool

目标

Video 模型(MiniMax-Hailuo-02)包装成 Tool

实现方式

const videoTool = new Tool({
  name: "video_generation",
  description: "根据文本描述生成视频",
  args: z.object({
    prompt: z.string().describe("视频描述"),
    model: z.string().optional(),
  }),
  async invoke(input: { prompt: string }) {
    const provider = new MiniMaxVideoProvider();
    return await provider.generate(input.prompt);
  },
});

涉及文件

  • src/modules/video/minimax.provider.ts - 新增
  • src/tools/video.tool.ts - Tool 封装

阶段五:LangGraph 流程编排改造

目标

将书籍生成流程从"手动 axios 调用"改为"LangGraph 节点调用 Tool"

当前流程(book-langgraph.ts)

// 当前:直接 axios 调用
async function callLLM(prompt: string) {
  const response = await axios.post(apiUrl, {...});
  return response.data.output.text;
}

改造后

import { ChatOpenAI } from '@langchain/openai';
import { ttsTool, imageTool } from '../../tools';

// LLM 作为 LangChain 实例
const llm = new ChatOpenAI({
  model: modelId,
  apiKey: apiKey,
  configuration: { baseURL: baseUrl },
  temperature: 0.7,
});

// LangGraph 节点使用 Tool
async function llmNode(state: BookState) {
  const result = await llm.invoke(state.task);
  return { text: result.content };
}

async function ttsNode(state: BookState) {
  const audio = await ttsTool.invoke(state.text);
  return { audioUrl: audio };
}

涉及文件

  • src/modules/book-generator/langgraph/book-langgraph.ts - 重构
  • src/tools/index.ts - 统一导出所有 Tool

阶段六:自动模型切换增强

目标

Tool 调用失败时自动切换到备用模型

实现方式

shouldSwitchModel 基础上,增加 Tool 级别的重试和切换逻辑

async function invokeWithFallback(
  tool: Tool,
  input: any,
  modelType: 'text' | 'tts' | 'image' | 'video'
) {
  const models = config.models.getModelsByType(modelType);

  for (const model of models) {
    try {
      return await tool.invoke(input, { config: { model: model.id } });
    } catch (error) {
      if (!shouldSwitchModel(error)) throw error;
      console.log(`[Tool] ${model.id} 失败,切换到 ${model.id}`);
    }
  }
  throw new Error(`所有 ${modelType} 模型均不可用`);
}

执行顺序

阶段一:统一 LLM 调用     →  已完成 ✅
阶段二:TTS Tool          →  待执行
阶段三:Image Tool        →  待执行
阶段四:Video Tool        →  待执行
阶段五:LangGraph 改造    →  待执行(依赖阶段二三四)
阶段六:自动切换增强      →  待执行

风险与注意事项

  1. TypeScript LangChain Tool 支持有限

    • LangChain JS 生态不如 Python 完善
    • Tool 接口可能需要 polyfill
  2. TTS/Image/Video 不是 OpenAI-compatible

    • 无法直接用 ChatOpenAI 调用
    • 必须包装成 Tool 间接调用
  3. API 兼容性

    • MiniMax API: https://api.minimax.chat/v1/chat/completions ✅ 已验证
    • 阿里云 TTS: https://dashscope.aliyuncs.com/api/v1/services/audio/speech/synthesis - 需验证
  4. 改动范围

    • 涉及多个 Provider 文件修改
    • LangGraph 流程需要重新测试
    • 建议分阶段提交

后续优化方向

  1. 多模型并行:一个任务同时调用多个模型,取最快结果
  2. 成本优化:根据任务类型选择最便宜的模型
  3. 流式输出:Text/TTS 都支持流式
  4. 结果缓存:相同 prompt 缓存结果

参考资料