## Major Features ### Streaming Response System - Implement LlmDriver trait with `stream()` method returning async Stream - Add SSE parsing for Anthropic and OpenAI API streaming - Integrate Tauri event system for frontend streaming (`stream:chunk` events) - Add StreamChunk types: Delta, ToolStart, ToolEnd, Complete, Error ### MCP Protocol Implementation - Add MCP JSON-RPC 2.0 types (mcp_types.rs) - Implement stdio-based MCP transport (mcp_transport.rs) - Support tool discovery, execution, and resource operations ### Browser Hand Implementation - Complete browser automation with Playwright-style actions - Support Navigate, Click, Type, Scrape, Screenshot, Wait actions - Add educational Hands: Whiteboard, Slideshow, Speech, Quiz ### Security Enhancements - Implement command whitelist/blacklist for shell_exec tool - Add SSRF protection with private IP blocking - Create security.toml configuration file ## Test Improvements - Fix test import paths (security-utils, setup) - Fix vi.mock hoisting issues with vi.hoisted() - Update test expectations for validateUrl and sanitizeFilename - Add getUnsupportedLocalGatewayStatus mock ## Documentation Updates - Update architecture documentation - Improve configuration reference - Add quick-start guide updates Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
165 lines
4.7 KiB
Markdown
165 lines
4.7 KiB
Markdown
# 修复 LLM API 404 错误
|
||
|
||
## 问题描述
|
||
|
||
用户在对话时收到错误:
|
||
```
|
||
Chat failed: LLM error: API error 404 Not Found: {"error":{"message":"The requested resource was not found","type":"resource_not_found_error"}}
|
||
```
|
||
|
||
## 根因分析
|
||
|
||
### 1. 模型别名未被解析(主要原因)
|
||
|
||
**数据流问题:**
|
||
- 前端 `chatStore.ts:194` 默认模型是 `'glm-5'`
|
||
- 配置文件 `config.toml:131` 定义了别名映射 `"glm-5" = "zhipu/glm-4-plus"`
|
||
- **但是后端 `kernel.rs` 没有解析这个别名**,直接把 `'glm-5'` 发送给API
|
||
- 智谱API不认识 `'glm-5'`,返回404
|
||
|
||
### 2. 模型ID格式问题
|
||
|
||
配置文件使用带provider前缀的格式 `"zhipu/glm-4-plus"`,但:
|
||
- 智谱API只认识 `"glm-4-plus"`(不带前缀)
|
||
- `openai.rs:140` 直接传递模型ID,不做任何处理
|
||
|
||
### 3. 多处配置不一致
|
||
|
||
| 位置 | 默认Provider | 默认Model |
|
||
|------|-------------|-----------|
|
||
| `config.rs:84-88` | `"qwen"` | `"qwen-plus"` |
|
||
| `config.toml:115-116` | `"zhipu"` | `"glm-4-plus"` |
|
||
| `chatStore.ts:194` | - | `'glm-5'` (别名) |
|
||
| `loop_runner.rs:38` | - | `"claude-sonnet-4-20250514"` (硬编码) |
|
||
|
||
### 4. Kimi Base URL错误
|
||
|
||
`config.rs:92` 使用 `https://api.kimi.com/coding/v1`,正确应该是 `https://api.moonshot.cn/v1`
|
||
|
||
## 修复方案
|
||
|
||
### 修复1: 在Kernel中添加模型别名解析
|
||
|
||
**文件**: `crates/zclaw-kernel/src/kernel.rs`
|
||
|
||
在 `send_message` 和 `send_message_stream` 方法中,解析模型别名:
|
||
|
||
```rust
|
||
// 在 send_message 方法中,约第119行
|
||
// 添加模型别名解析函数
|
||
fn resolve_model_alias(model: &str, aliases: &HashMap<String, String>) -> String {
|
||
aliases.get(model).map(|s| s.as_str()).unwrap_or(model).to_string()
|
||
}
|
||
|
||
// 然后在确定模型后,检查是否是别名
|
||
let model = resolve_model_alias(model, &self.config.model_aliases);
|
||
```
|
||
|
||
### 修复2: 在KernelConfig中添加模型别名配置
|
||
|
||
**文件**: `crates/zclaw-kernel/src/config.rs`
|
||
|
||
1. 添加模型别名字段:
|
||
```rust
|
||
/// Model aliases (e.g., "glm-5" -> "zhipu/glm-4-plus")
|
||
#[serde(default)]
|
||
pub model_aliases: HashMap<String, String>,
|
||
```
|
||
|
||
2. 修正Kimi Base URL:
|
||
```rust
|
||
fn default_kimi_base_url() -> String {
|
||
"https://api.moonshot.cn/v1".to_string() // 修正
|
||
}
|
||
```
|
||
|
||
### 修复3: 在OpenAIDriver中规范化模型ID
|
||
|
||
**文件**: `crates/zclaw-runtime/src/driver/openai.rs`
|
||
|
||
在 `build_api_request` 方法中,去除provider前缀:
|
||
|
||
```rust
|
||
fn normalize_model_id(model: &str) -> String {
|
||
// 如果模型ID包含 "/",取最后一部分
|
||
// 例如 "zhipu/glm-4-plus" -> "glm-4-plus"
|
||
if model.contains('/') {
|
||
model.split('/').last().unwrap_or(model).to_string()
|
||
} else {
|
||
model.to_string()
|
||
}
|
||
}
|
||
```
|
||
|
||
### 修复4: 统一默认配置
|
||
|
||
**文件**: `crates/zclaw-kernel/src/config.rs`
|
||
|
||
将默认provider和model改为与config.toml一致:
|
||
```rust
|
||
fn default_provider() -> String {
|
||
"zhipu".to_string()
|
||
}
|
||
|
||
fn default_model() -> String {
|
||
"glm-4-plus".to_string()
|
||
}
|
||
```
|
||
|
||
### 修复5: 移除loop_runner.rs中的硬编码默认值
|
||
|
||
**文件**: `crates/zclaw-runtime/src/loop_runner.rs`
|
||
|
||
第38行的硬编码模型应该移除,使用传入的配置:
|
||
```rust
|
||
// 移除硬编码,改为空字符串或从配置获取
|
||
model: String::new(), // 必须通过 with_model() 设置
|
||
```
|
||
|
||
## 关键文件
|
||
|
||
| 文件 | 修改内容 |
|
||
|------|----------|
|
||
| [config.rs](crates/zclaw-kernel/src/config.rs) | 添加model_aliases字段、修正Kimi URL、统一默认配置 |
|
||
| [kernel.rs](crates/zclaw-kernel/src/kernel.rs) | 添加模型别名解析逻辑 |
|
||
| [openai.rs](crates/zclaw-runtime/src/driver/openai.rs) | 添加模型ID规范化(去除provider前缀) |
|
||
| [loop_runner.rs](crates/zclaw-runtime/src/loop_runner.rs) | 移除硬编码默认模型 |
|
||
|
||
## 问题场景确认
|
||
|
||
用户使用**智谱 GLM** 模型时遇到404错误:
|
||
- 前端默认 `currentModel: 'glm-5'`
|
||
- 配置别名 `"glm-5" = "zhipu/glm-4-plus"`
|
||
- 后端未解析别名,直接发送 `'glm-5'` 给API
|
||
- 智谱API返回404
|
||
|
||
## 验证步骤
|
||
|
||
1. **单元测试**
|
||
```bash
|
||
cargo test -p zclaw-kernel --lib config::tests
|
||
cargo test -p zclaw-runtime --lib driver::openai::tests
|
||
```
|
||
|
||
2. **集成测试**
|
||
```bash
|
||
cargo test -p zclaw-kernel
|
||
```
|
||
|
||
3. **手动验证(智谱GLM)**
|
||
- 启动桌面应用:`pnpm start:dev`
|
||
- 在"模型与API"设置中配置智谱API Key
|
||
- 选择 `glm-5` 或 `glm-4-plus` 模型
|
||
- 发送消息,验证不再出现404错误
|
||
- 检查日志确认:
|
||
- 模型别名 `'glm-5'` 被解析为 `'zhipu/glm-4-plus'`
|
||
- provider前缀被去除,发送给API的是 `'glm-4-plus'`
|
||
|
||
## 实现顺序
|
||
|
||
1. **config.rs** - 添加model_aliases字段和修正Kimi URL
|
||
2. **openai.rs** - 添加模型ID规范化函数
|
||
3. **kernel.rs** - 添加别名解析逻辑
|
||
4. **loop_runner.rs** - 移除硬编码
|
||
5. **测试验证**
|