将项目从"直接 API 调用"模式,迁移到"LangChain 统一调用"模式,实现:
models.jsonChatOpenAI + baseURL 方式调用(OpenAI-compatible)models-validator.ts 可批量验证模型AliyunTTSProvider 直接 axios 调用所有 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 Provider 实现 Tool 接口,可在 LangGraph 节点中调用
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');
},
});
src/modules/tts/aliyun.provider.tssrc/modules/tts/minimax.provider.tssrc/modules/tts/aliyun.provider.ts - 修改src/modules/tts/minimax.provider.ts - 新增src/tools/tts.tool.ts - Tool 封装src/config/models.json - 新增 tts 工具定义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 模型(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 封装将书籍生成流程从"手动 axios 调用"改为"LangGraph 节点调用 Tool"
// 当前:直接 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 - 统一导出所有 ToolTool 调用失败时自动切换到备用模型
在 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 改造 → 待执行(依赖阶段二三四)
阶段六:自动切换增强 → 待执行
TypeScript LangChain Tool 支持有限
TTS/Image/Video 不是 OpenAI-compatible
API 兼容性
https://api.minimax.chat/v1/chat/completions ✅ 已验证https://dashscope.aliyuncs.com/api/v1/services/audio/speech/synthesis - 需验证改动范围