9.7 KiB
9.7 KiB
聊天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](第一个连接的服务) - 如果第一个服务没有该模型,就会发送错误的请求
示例场景:
- 用户配置了两个服务: DashScope 和 Volcengine
- DashScope先连接,成为
services[0] - 用户在聊天界面选择 Volcengine 的模型
doubao-seed-1-6-flash-250828 - 代码将这个模型发送给 DashScope 服务
- DashScope 不认识这个模型,返回404或其他错误
问题2: 缺少调试日志
位置:
/web/src/services/chatService.tscallModel()方法/web/src/services/modelServiceManager.tssendChatRequest()和makeChatRequest()方法
原因:
- 没有日志输出当前使用的服务和模型
- 难以追踪请求的完整路径
- 无法快速定位URL构建问题
解决方案
修复1: 智能服务匹配
文件: /web/src/services/chatService.ts
改动:
// 原代码
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() 方法
// 添加的日志
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() 方法
// 请求前日志
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图标便于快速识别
验证步骤
-
配置多个服务:
- 添加 DashScope 服务(阿里云通义千问)
- 添加 Volcengine 服务(字节跳动豆包)
- 确保两个服务都已连接
-
测试模型匹配:
- 在聊天界面选择 Volcengine 的模型(如
doubao-seed-1-6-flash-250828) - 发送消息
- 打开浏览器控制台
- 应该看到日志:
🔍 [callModel] 使用服务: 火山引擎 模型: doubao-seed-1-6-flash-250828
- 在聊天界面选择 Volcengine 的模型(如
-
测试URL构建:
- 检查控制台日志中的URL
- 应该是:
https://ark.cn-beijing.volces.com/api/v3/chat/completions - 不应该是:
/completions或其他错误格式
-
测试错误处理:
- 暂时输入错误的API Key
- 应该看到详细的错误日志
- 包括状态码、URL、错误响应
预期效果
修复后,用户应该能够:
- ✅ 在聊天界面选择任意已配置服务的模型
- ✅ 系统自动找到正确的服务发送请求
- ✅ 看到清晰的调试日志(便于问题追踪)
- ✅ 收到正确的AI回复
- ✅ 不再看到404错误
相关文件
修改的文件
-
/web/src/services/chatService.ts- 修改
callModel()方法 - 新增服务匹配逻辑
- 修改
-
/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
技术细节
服务匹配算法
// 查找包含指定模型的服务
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
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. 缓存服务-模型映射
目的: 避免每次都遍历查找
// 在 modelServiceManager 中维护映射
private modelToServiceMap: Map<string, string> = 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. 用户选择优先级
目的: 当多个服务有相同模型时,使用用户最后使用的服务
private lastUsedService: Map<string, string> = 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. 模型别名支持
目的: 支持同一模型的不同名称
private modelAliases: Map<string, string[]> = 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. 更智能的错误提示
目的: 帮助用户快速定位配置问题
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: 初始版本,修复服务匹配和调试日志问题
- 待定: 实现缓存和优先级优化