# 聊天404错误修复报告 ## 问题描述 用户在发送聊天消息时遇到404错误: ``` Failed to load resource: the server responded with a status of 404 () (completions, line 0) ``` ## 根本原因分析 ### 问题1: 服务选择逻辑错误 **位置**: `/web/src/services/chatService.ts` 第492-530行 `callModel()` 方法 **原因**: - 用户在聊天界面选择了特定的AI模型(例如: `doubao-seed-1-6-flash-250828`) - `callModel()` 方法接收到 `model` 参数,但**没有根据模型名称查找对应的服务** - 代码直接使用 `services[0]`(第一个连接的服务) - 如果第一个服务没有该模型,就会发送错误的请求 **示例场景**: 1. 用户配置了两个服务: DashScope 和 Volcengine 2. DashScope先连接,成为 `services[0]` 3. 用户在聊天界面选择 Volcengine 的模型 `doubao-seed-1-6-flash-250828` 4. 代码将这个模型发送给 DashScope 服务 5. DashScope 不认识这个模型,返回404或其他错误 ### 问题2: 缺少调试日志 **位置**: - `/web/src/services/chatService.ts` `callModel()` 方法 - `/web/src/services/modelServiceManager.ts` `sendChatRequest()` 和 `makeChatRequest()` 方法 **原因**: - 没有日志输出当前使用的服务和模型 - 难以追踪请求的完整路径 - 无法快速定位URL构建问题 ## 解决方案 ### 修复1: 智能服务匹配 **文件**: `/web/src/services/chatService.ts` **改动**: ```typescript // 原代码 const service = services[0] // 使用第一个可用服务 const selectedModel = model || service.models?.[0] || 'default' // 修复后 let service = services[0] // 默认使用第一个可用服务 let selectedModel = model || service.models?.[0] || 'default' // 如果指定了模型,尝试找到拥有该模型的服务 if (model) { const foundService = services.find(s => s.models && s.models.includes(model) ) if (foundService) { service = foundService selectedModel = model } else { console.warn(`⚠️ 未找到包含模型 "${model}" 的服务,使用默认服务`) } } console.log('🔍 [callModel] 使用服务:', service.name, '模型:', selectedModel) ``` **效果**: - ✅ 根据模型名称自动匹配正确的服务 - ✅ 避免将模型发送给错误的服务 - ✅ 提供降级方案(找不到服务时使用默认) - ✅ 记录调试日志 ### 修复2: 增强调试日志 **文件**: `/web/src/services/modelServiceManager.ts` #### 2.1 `sendChatRequest()` 方法 ```typescript // 添加的日志 console.log('🔍 [sendChatRequest] serviceId:', serviceId, 'service:', service) // 添加URL验证 if (!service.url || !service.url.startsWith('http')) { console.error('❌ [sendChatRequest] 无效的服务URL:', service.url) return { success: false, error: `服务URL无效: ${service.url}` } } // 添加异常日志 console.error('❌ [sendChatRequest] 请求异常:', error) ``` #### 2.2 `makeChatRequest()` 方法 ```typescript // 请求前日志 console.log('🔍 [makeChatRequest] 服务信息:', { type: service.type, name: service.name, url: service.url, model }) console.log('🔍 [makeChatRequest] 最终请求URL:', url) console.log('🔍 [makeChatRequest] 请求体:', body) // 响应日志 console.log('🔍 [makeChatRequest] 响应状态:', response.status, response.statusText) // 错误日志 console.error('❌ [makeChatRequest] 请求失败:', { status: response.status, statusText: response.statusText, url, errorText }) ``` **效果**: - ✅ 完整记录请求流程 - ✅ 输出服务名称、URL、模型 - ✅ 显示最终构建的URL - ✅ 记录响应状态和错误详情 - ✅ 使用emoji图标便于快速识别 ## 验证步骤 1. **配置多个服务**: - 添加 DashScope 服务(阿里云通义千问) - 添加 Volcengine 服务(字节跳动豆包) - 确保两个服务都已连接 2. **测试模型匹配**: - 在聊天界面选择 Volcengine 的模型(如 `doubao-seed-1-6-flash-250828`) - 发送消息 - 打开浏览器控制台 - 应该看到日志: `🔍 [callModel] 使用服务: 火山引擎 模型: doubao-seed-1-6-flash-250828` 3. **测试URL构建**: - 检查控制台日志中的URL - 应该是: `https://ark.cn-beijing.volces.com/api/v3/chat/completions` - 不应该是: `/completions` 或其他错误格式 4. **测试错误处理**: - 暂时输入错误的API Key - 应该看到详细的错误日志 - 包括状态码、URL、错误响应 ## 预期效果 修复后,用户应该能够: - ✅ 在聊天界面选择任意已配置服务的模型 - ✅ 系统自动找到正确的服务发送请求 - ✅ 看到清晰的调试日志(便于问题追踪) - ✅ 收到正确的AI回复 - ✅ 不再看到404错误 ## 相关文件 ### 修改的文件 1. `/web/src/services/chatService.ts` - 修改 `callModel()` 方法 - 新增服务匹配逻辑 2. `/web/src/services/modelServiceManager.ts` - 修改 `sendChatRequest()` 方法 - 修改 `makeChatRequest()` 方法 - 新增URL验证 - 新增调试日志 ### 涉及的方法调用链 ``` ChatLayout.vue (用户发送消息) ↓ chatService.sendMessageStream(model: string) ↓ chatService.callModelStream(model: string) ↓ chatService.callModel(model: string) ← 修复点1: 服务匹配 ↓ modelServiceManager.sendChatRequest(serviceId, messages, model) ← 修复点2: 日志 ↓ modelServiceManager.makeChatRequest(service, messages, model) ← 修复点3: 日志 ↓ fetch(url) → AI服务API ``` ## 技术细节 ### 服务匹配算法 ```typescript // 查找包含指定模型的服务 const foundService = services.find(s => s.models && s.models.includes(model) ) ``` **特点**: - 使用 `Array.find()` 查找第一个匹配的服务 - 检查 `s.models` 存在性(避免空指针) - 使用 `includes()` 精确匹配模型名称 - 找不到时提供降级方案 ### URL构建规则 不同服务类型的endpoint: - **OpenAI/Local**: `{baseUrl}/chat/completions` - **DashScope**: `{baseUrl}/chat/completions` - **Volcengine**: `{baseUrl}/chat/completions` - **Claude**: `{baseUrl}/messages` - **Gemini**: `{baseUrl}/models/{model}:generateContent?key={apiKey}` - **Azure**: `{baseUrl}/openai/deployments/{model}/chat/completions?api-version=2023-12-01-preview` ### 预设URL ```typescript const defaultUrls = { openai: 'https://api.openai.com/v1', claude: 'https://api.anthropic.com/v1', gemini: 'https://generativelanguage.googleapis.com/v1', azure: 'https://your-resource.openai.azure.com', dashscope: 'https://dashscope.aliyuncs.com/compatible-mode/v1', volcengine: 'https://ark.cn-beijing.volces.com/api/v3', local: 'http://localhost:1234/v1' } ``` ## 后续优化建议 ### 1. 缓存服务-模型映射 **目的**: 避免每次都遍历查找 ```typescript // 在 modelServiceManager 中维护映射 private modelToServiceMap: Map = new Map() // 更新映射 updateModelMapping() { this.modelToServiceMap.clear() this.services.forEach(service => { service.models?.forEach(model => { this.modelToServiceMap.set(model, service.id) }) }) } // 快速查找 getServiceByModel(model: string): ModelService | undefined { const serviceId = this.modelToServiceMap.get(model) return serviceId ? this.services.get(serviceId) : undefined } ``` ### 2. 用户选择优先级 **目的**: 当多个服务有相同模型时,使用用户最后使用的服务 ```typescript private lastUsedService: Map = new Map() // model -> serviceId callModel(conversation, model) { // 优先使用用户最后一次使用的服务 const lastServiceId = this.lastUsedService.get(model) let service = services.find(s => s.id === lastServiceId) || services.find(s => s.models?.includes(model)) || services[0] // 记录使用历史 this.lastUsedService.set(model, service.id) } ``` ### 3. 模型别名支持 **目的**: 支持同一模型的不同名称 ```typescript private modelAliases: Map = new Map([ ['gpt-4', ['gpt-4-0613', 'gpt-4-32k']], ['doubao', ['doubao-seed-1-6', 'doubao-seed-1-6-flash']] ]) findServiceByModel(model: string): ModelService | undefined { // 尝试直接匹配 let service = services.find(s => s.models?.includes(model)) // 尝试别名匹配 if (!service) { const aliases = this.modelAliases.get(model) || [] service = services.find(s => s.models?.some(m => aliases.includes(m)) ) } return service } ``` ### 4. 更智能的错误提示 **目的**: 帮助用户快速定位配置问题 ```typescript if (!foundService) { const availableModels = services .flatMap(s => s.models || []) .join(', ') throw new Error( `未找到支持模型 "${model}" 的服务。\n` + `当前可用模型: ${availableModels}\n` + `请检查模型服务配置或选择其他模型。` ) } ``` ## 测试场景 ### 场景1: 单服务单模型 - **配置**: 1个DashScope服务 - **操作**: 选择 `qwen-turbo` - **预期**: 正常工作 ### 场景2: 多服务不同模型 - **配置**: DashScope + Volcengine - **操作**: 交替选择两个服务的模型 - **预期**: 自动切换服务,都能正常工作 ### 场景3: 多服务相同模型名 - **配置**: 两个OpenAI兼容服务,都有 `gpt-3.5-turbo` - **操作**: 选择 `gpt-3.5-turbo` - **预期**: 使用第一个找到的服务(可后续优化为用户选择) ### 场景4: 模型不存在 - **配置**: DashScope服务 - **操作**: 选择不存在的模型 `nonexistent-model` - **预期**: 降级使用默认服务,输出警告日志 ### 场景5: 服务URL错误 - **配置**: 服务URL为空或不含http - **操作**: 发送消息 - **预期**: 立即返回错误,不发送请求 ## 更新历史 - **2024-01-XX**: 初始版本,修复服务匹配和调试日志问题 - **待定**: 实现缓存和优先级优化