# 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` 统一调用

### 配置结构
```json
{
  "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 接口
```typescript
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

### 实现方式
```typescript
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

### 实现方式
```typescript
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）
```typescript
// 当前：直接 axios 调用
async function callLLM(prompt: string) {
  const response = await axios.post(apiUrl, {...});
  return response.data.output.text;
}
```

### 改造后
```typescript
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 级别的重试和切换逻辑

```typescript
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 缓存结果

---

## 参考资料

- [LangChain Python TTS Tool](https://python.langchain.com/docs/integrations/tools/)
- [LangChain JS Tool 文档](https://js.langchain.com/docs/integrations/tools/)
- [LangGraph 状态管理](https://langchainai.github.io/langgraphjs/)
- [阿里云 TTS API 文档](https://help.aliyun.com/zh/model-studio/qwen-tts)
- [MiniMax API 文档](https://www.minimaxi.com/document)
