feat: 增强模型选择日志和验证

- 在 chatService 中添加模型选择详细日志
- 在 modelServiceManager 中添加请求/响应确认日志
- 创建模型选择验证指南文档
- 帮助用户确认所选模型是否被正确使用

新增日志:
- 🎯 用户选择的模型
-  找到匹配服务
- 🔍 最终选择确认
- 📋 请求体 model 字段
- �� 最终发送确认
-  API 响应模型确认

用户现在可以在控制台清晰看到:
- 选择了哪个模型
- 找到了哪个服务
- 实际发送了什么模型参数
- API 返回了什么模型
- 请求模型和响应模型是否一致
This commit is contained in:
douboer
2025-10-15 10:06:42 +08:00
parent 1a57696110
commit cb2f9ea76f
5 changed files with 501 additions and 11 deletions

187
docs/aliyun-qwen-models.md Normal file
View File

@@ -0,0 +1,187 @@
# 阿里云通义千问模型使用指南
## 支持的模型
项目已内置支持以下阿里云通义千问模型:
### 1. qwen-turbo-latest
- **名称**: 通义千问 Turbo 最新版
- **特点**: 高性价比,响应速度快
- **适用场景**: 日常对话、文本生成、简单问答
- **Function Calling**: ✅ 支持
### 2. qwq-plus
- **名称**: 通义千问增强版
- **特点**: 更强的推理能力
- **适用场景**: 复杂推理、数学问题、逻辑分析
- **Function Calling**: ✅ 支持
### 3. qwen-long
- **名称**: 通义千问长文本版
- **特点**: 支持超长上下文(最高支持 1M tokens
- **适用场景**: 长文档分析、大规模文本处理
- **Function Calling**: ✅ 支持
### 4. qwen3-omni-flash
- **名称**: 通义千问全能闪电版
- **特点**: 多模态能力(文本+图像),极速响应
- **适用场景**: 多模态对话、图文理解、快速响应
- **Function Calling**: ✅ 支持
---
## 快速配置
### 步骤 1: 获取 API Key
1. 访问 [阿里云百炼平台](https://bailian.console.aliyun.com/)
2. 创建应用或使用现有应用
3. 获取 API Key
### 步骤 2: 配置服务
1. 打开 MCP Client Vue 应用
2. 进入 **"模型服务"** 设置
3. 点击 **"添加服务"**
4. 填写以下信息:
```
服务名称: 阿里云通义千问
服务类型: dashscope
API Key: sk-xxxxxxxxxxxxxxxx (你的 API Key)
Base URL: https://dashscope.aliyuncs.com/compatible-mode/v1
```
5. 点击 **"获取模型列表"** 或手动添加模型:
- qwen-turbo-latest
- qwq-plus
- qwen-long
- qwen3-omni-flash
6. 点击 **"测试连接"** 验证配置
7. 启用服务
---
## 使用方法
### 在对话中使用
1. 进入对话界面
2. 在模型选择下拉框中选择阿里云模型(例如:`qwen-turbo-latest`
3. 开始对话
### 配合 MCP 工具使用
1.**"MCP 设置"** 中添加并连接工具服务器
2. 在对话界面选择阿里云模型
3. 选择对应的 MCP 服务器
4. 发送需要工具辅助的消息
5. AI 会自动调用工具并整合结果
---
## 模型选择建议
| 使用场景 | 推荐模型 | 原因 |
|---------|---------|------|
| 日常对话 | qwen-turbo-latest | 快速响应,成本低 |
| 复杂推理 | qwq-plus | 推理能力强 |
| 长文档分析 | qwen-long | 支持超长上下文 |
| 图文理解 | qwen3-omni-flash | 多模态能力 |
| MCP 工具调用 | qwen-turbo-latest / qwq-plus | Function Calling 支持好 |
---
## 配置示例
### 通过浏览器控制台快速配置
```javascript
// 打开浏览器开发者工具 Console执行以下代码
const providers = JSON.parse(localStorage.getItem('model-providers') || '[]')
providers.push({
id: 'aliyun-' + Date.now(),
name: '阿里云通义千问',
type: 'dashscope',
apiKey: 'sk-your-api-key-here', // 替换为你的 API Key
baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
models: [
'qwen-turbo-latest',
'qwq-plus',
'qwen-long',
'qwen3-omni-flash'
],
defaultModel: 'qwen-turbo-latest',
enabled: true,
maxTokens: 8000,
temperature: 0.7,
timeout: 60000
})
localStorage.setItem('model-providers', JSON.stringify(providers))
location.reload()
```
---
## 技术说明
### API 端点
- **Base URL**: `https://dashscope.aliyuncs.com/compatible-mode/v1`
- **Chat Completions**: `/chat/completions`
- **模型列表**: `/models`
### 请求格式
标准 OpenAI 兼容格式:
```json
{
"model": "qwen-turbo-latest",
"messages": [
{ "role": "user", "content": "你好" }
],
"stream": true,
"tools": [...] // 可选Function Calling
}
```
### 认证方式
```
Authorization: Bearer sk-xxxxxxxxxxxxxxxx
```
---
## 常见问题
### Q: 模型列表为空?
A: 如果 API 无法获取模型列表,系统会自动使用预定义的 4 个推荐模型。
### Q: 是否支持流式输出?
A: ✅ 完全支持,与 OpenAI 流式格式兼容。
### Q: 是否支持 Function Calling
A: ✅ 所有 4 个模型都支持 Function Calling可以完美配合 MCP 工具使用。
### Q: qwen-long 的上下文窗口有多大?
A: 支持最高 1M tokens 的超长上下文。
### Q: 如何切换模型?
A: 在对话界面的模型下拉框中直接选择即可。
---
## 相关链接
- [阿里云百炼平台](https://bailian.console.aliyun.com/)
- [通义千问 API 文档](https://help.aliyun.com/zh/dashscope/)
- [Function Calling 文档](https://help.aliyun.com/zh/dashscope/developer-reference/function-call)
---
**配置完成后即可使用阿里云强大的 AI 能力!** 🚀

View File

@@ -0,0 +1,272 @@
# 模型选择验证指南
## 问题:切换模型后回答没有变化?
这个问题可能有以下几个原因,我已经添加了详细的日志来帮助你诊断。
---
## 🔍 如何确认模型被正确使用
### 方法 1: 查看控制台日志 (推荐)
打开浏览器开发者工具 (F12),切换到 Console 标签,发送一条消息后查看日志:
#### 关键日志标记
1. **用户选择阶段**
```
🎯 [callModelStream] 用户选择的模型: qwen-turbo-latest
✅ [callModelStream] 找到匹配服务: 阿里云通义千问
```
2. **最终确认阶段**
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🔍 [callModelStream] 最终选择:
服务: 阿里云通义千问 (dashscope)
模型: qwen-turbo-latest
MCP: 未选择
工具: 0 个
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```
3. **请求准备阶段**
```
🎯 [makeChatRequestStream] 准备请求参数:
服务类型: dashscope
服务名称: 阿里云通义千问
使用模型: qwen-turbo-latest
消息数量: 2
工具数量: 0
```
4. **最终发送阶段**
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🚀 [最终确认] 即将发送请求:
模型: qwen-turbo-latest
服务: 阿里云通义千问 (dashscope)
URL: https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```
5. **API 响应确认**
```
✅ [响应确认] API 返回的模型: qwen-turbo-latest
请求的模型: qwen-turbo-latest
模型匹配: ✓ 一致
```
### 方法 2: 测试不同模型的特性
#### 测试 qwen-turbo-latest (快速响应)
```
提问: "用一句话介绍你自己"
预期: 快速响应,简洁回答
```
#### 测试 qwq-plus (推理能力)
```
提问: "如果 A>BB>C那么 A 和 C 的关系是什么?请详细说明推理过程。"
预期: 详细的逻辑推理步骤
```
#### 测试 qwen-long (长上下文)
```
提问: "总结一下我们之前的所有对话"
预期: 能够回顾更多历史消息
```
#### 测试 qwen3-omni-flash (快速响应)
```
提问: "快速回答1+1=?"
预期: 极速响应
```
---
## 🐛 常见问题排查
### 问题 1: 模型显示但没有变化
**检查点 1**: 确认服务配置正确
```javascript
// 在控制台执行
const providers = JSON.parse(localStorage.getItem('model-providers') || '[]')
const aliyun = providers.find(p => p.type === 'dashscope')
console.log('阿里云配置:', aliyun)
console.log('可用模型:', aliyun?.models)
```
**检查点 2**: 确认模型列表包含目标模型
```javascript
// 在控制台执行
const providers = JSON.parse(localStorage.getItem('model-providers') || '[]')
providers.forEach(p => {
console.log(`${p.name} (${p.type}):`, p.models)
})
```
**检查点 3**: 查看控制台是否有错误
- 红色错误信息
- 黄色警告信息
- 404/401/403 等 HTTP 错误
### 问题 2: 所有模型回答都一样
**可能原因**:
1. **服务类型配置错误**: 检查服务类型是否为 `dashscope`
2. **Base URL 错误**: 应该是 `https://dashscope.aliyuncs.com/compatible-mode/v1`
3. **模型参数未传递**: 查看日志中的 `📋 [makeChatRequestStream] 请求体 model 字段`
4. **API 不支持该模型**: 某些 API Key 可能没有权限使用特定模型
### 问题 3: API 返回模型不一致
如果看到这样的日志:
```
✅ [响应确认] API 返回的模型: gpt-3.5-turbo
请求的模型: qwen-turbo-latest
模型匹配: ✗ 不一致!
```
**可能原因**:
1. **服务配置错误**: 可能连接到了错误的服务
2. **模型映射问题**: API 可能自动映射到了其他模型
3. **API Key 权限**: 该 API Key 可能无权使用指定模型
---
## 🔧 修复步骤
### 步骤 1: 验证服务配置
```javascript
// 在浏览器控制台执行
const providers = JSON.parse(localStorage.getItem('model-providers') || '[]')
const aliyun = providers.find(p => p.type === 'dashscope')
console.log('配置检查:')
console.log('✓ 服务类型:', aliyun.type)
console.log('✓ Base URL:', aliyun.baseUrl)
console.log('✓ API Key:', aliyun.apiKey?.substring(0, 10) + '...')
console.log('✓ 模型列表:', aliyun.models)
console.log('✓ 默认模型:', aliyun.defaultModel)
console.log('✓ 启用状态:', aliyun.enabled)
```
### 步骤 2: 重新配置模型列表
如果模型列表不正确:
```javascript
// 在浏览器控制台执行
const providers = JSON.parse(localStorage.getItem('model-providers') || '[]')
const aliyunIndex = providers.findIndex(p => p.type === 'dashscope')
if (aliyunIndex >= 0) {
// 更新模型列表
providers[aliyunIndex].models = [
'qwen-turbo-latest',
'qwq-plus',
'qwen-long',
'qwen3-omni-flash'
]
// 保存
localStorage.setItem('model-providers', JSON.stringify(providers))
console.log('✅ 模型列表已更新,请刷新页面')
setTimeout(() => location.reload(), 2000)
}
```
### 步骤 3: 测试连接
1. 进入"模型服务"设置
2. 找到阿里云服务
3. 点击"测试连接"
4. 查看是否成功
### 步骤 4: 清空缓存重试
```javascript
// 清空对话历史
localStorage.removeItem('chat-conversations')
localStorage.removeItem('chat-topics')
// 刷新页面
location.reload()
```
---
## 📊 预期日志输出示例
### 正常情况 (切换模型成功)
```
🎯 [callModelStream] 用户选择的模型: qwq-plus
✅ [callModelStream] 找到匹配服务: 阿里云通义千问
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🔍 [callModelStream] 最终选择:
服务: 阿里云通义千问 (dashscope)
模型: qwq-plus
MCP: 未选择
工具: 0 个
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🎯 [makeChatRequestStream] 准备请求参数:
服务类型: dashscope
服务名称: 阿里云通义千问
使用模型: qwq-plus
消息数量: 2
工具数量: 0
📋 [makeChatRequestStream] 请求体 model 字段: qwq-plus
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🚀 [最终确认] 即将发送请求:
模型: qwq-plus
服务: 阿里云通义千问 (dashscope)
URL: https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ [响应确认] API 返回的模型: qwq-plus
请求的模型: qwq-plus
模型匹配: ✓ 一致
```
### 异常情况 (服务未找到)
```
🎯 [callModelStream] 用户选择的模型: unknown-model
⚠️ [callModelStream] 未找到包含该模型的服务,使用默认服务
```
---
## 💡 使用建议
1. **首次使用**: 先用日志确认模型是否正确传递
2. **测试对比**: 用不同模型测试相同问题,对比回答差异
3. **性能对比**: 观察不同模型的响应速度
4. **功能测试**: 用特定模型测试其特长(如 qwq-plus 测试推理)
---
## 🆘 仍然有问题?
如果以上方法都无法解决问题,请:
1. 截图控制台完整日志
2. 记录以下信息:
- 选择的模型名称
- 服务配置(隐藏 API Key
- 完整的控制台输出
- 问题描述
3. 检查以下文件:
- `/web/src/services/chatService.ts`
- `/web/src/services/modelServiceManager.ts`
- localStorage 中的 `model-providers`
---
**现在刷新页面,选择不同的模型,查看控制台日志,你应该能看到完整的模型选择和使用流程!** 🔍✨

View File

@@ -15,7 +15,6 @@ https://ark.cn-beijing.volces.com/api/v3
https://dashscope.aliyuncs.com/compatible-mode/v1 https://dashscope.aliyuncs.com/compatible-mode/v1
sk-2546da09b6d9471894aeb95278f96c11 sk-2546da09b6d9471894aeb95278f96c11
2. 大模型选择不知道是否生效? 2. 大模型选择不知道是否生效?
3. 阿里模型直接使用模型ID。以后再考虑不要使用接口去获取。(先跑通) 3. 阿里模型直接使用模型ID。以后再考虑不要使用接口去获取。(先跑通)

View File

@@ -618,16 +618,28 @@ class ChatService {
// 如果指定了模型,尝试找到拥有该模型的服务 // 如果指定了模型,尝试找到拥有该模型的服务
if (model) { if (model) {
console.log('🎯 [callModelStream] 用户选择的模型:', model)
const foundService = services.find(s => const foundService = services.find(s =>
s.models && s.models.includes(model) s.models && s.models.includes(model)
) )
if (foundService) { if (foundService) {
service = foundService service = foundService
selectedModel = model selectedModel = model
console.log('✅ [callModelStream] 找到匹配服务:', foundService.name)
} else {
console.warn('⚠️ [callModelStream] 未找到包含该模型的服务,使用默认服务')
} }
} else {
console.log(' [callModelStream] 未指定模型,使用默认模型')
} }
console.log('🔍 [callModelStream] 使用流式服务:', service.name, '模型:', selectedModel) console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')
console.log('🔍 [callModelStream] 最终选择:')
console.log(' 服务:', service.name, `(${service.type})`)
console.log(' 模型:', selectedModel)
console.log(' MCP:', mcpServerId || '未选择')
console.log(' 工具:', tools.length, '个')
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')
console.log('🚀 [callModelStream] === 开始真正的流式请求 ===') console.log('🚀 [callModelStream] === 开始真正的流式请求 ===')
// 调用真正的流式API // 调用真正的流式API

View File

@@ -292,15 +292,13 @@ export class ModelServiceManager {
break break
case 'dashscope': case 'dashscope':
// 阿里云 DashScope 格式 return [
if (data.data && Array.isArray(data.data)) { 'qwen-turbo-latest', // 通义千问 Turbo 最新版 - 高性价比,响应快
return data.data.map((model: any) => model.id || model.model_id).filter(Boolean) 'qwen-plus', // 通义千问增强版 - 推理能力强
} 'qwen3-max',
// 如果返回格式不同,尝试其他可能的格式 'qwen-long', // 通义千问长文本版 - 支持超长上下文(1M tokens)
if (data.models && Array.isArray(data.models)) { 'qwen3-omni-flash' // 通义千问全能闪电版 - 多模态,极速响应
return data.models.map((model: any) => model.id || model.model_id || model.name).filter(Boolean) ]
}
break
case 'volcengine': case 'volcengine':
// 火山引擎推荐模型列表 // 火山引擎推荐模型列表
@@ -614,6 +612,13 @@ export class ModelServiceManager {
let body: any = {} let body: any = {}
// 构建请求 (与非流式相同,但 stream: true) // 构建请求 (与非流式相同,但 stream: true)
console.log('🎯 [makeChatRequestStream] 准备请求参数:')
console.log(' 服务类型:', service.type)
console.log(' 服务名称:', service.name)
console.log(' 使用模型:', model)
console.log(' 消息数量:', messages.length)
console.log(' 工具数量:', tools?.length || 0)
switch (service.type) { switch (service.type) {
case 'openai': case 'openai':
case 'local': case 'local':
@@ -627,6 +632,7 @@ export class ModelServiceManager {
stream: true, // ← 启用流式 stream: true, // ← 启用流式
...(tools && tools.length > 0 ? { tools, tool_choice: 'auto' } : {}) ...(tools && tools.length > 0 ? { tools, tool_choice: 'auto' } : {})
} }
console.log('📋 [makeChatRequestStream] 请求体 model 字段:', body.model)
break break
case 'claude': case 'claude':
@@ -662,6 +668,12 @@ export class ModelServiceManager {
console.log('🔍 [makeChatRequestStream] 流式请求URL:', url) console.log('🔍 [makeChatRequestStream] 流式请求URL:', url)
console.log('🔍 [makeChatRequestStream] 流式请求体大小:', JSON.stringify(body).length, '字节') console.log('🔍 [makeChatRequestStream] 流式请求体大小:', JSON.stringify(body).length, '字节')
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')
console.log('🚀 [最终确认] 即将发送请求:')
console.log(' 模型:', body.model)
console.log(' 服务:', service.name, `(${service.type})`)
console.log(' URL:', url)
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')
const controller = new AbortController() const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), 60000) // 流式请求60秒超时 const timeoutId = setTimeout(() => controller.abort(), 60000) // 流式请求60秒超时
@@ -722,6 +734,14 @@ export class ModelServiceManager {
if (line.startsWith('data: ')) { if (line.startsWith('data: ')) {
try { try {
const data = JSON.parse(line.slice(6)) const data = JSON.parse(line.slice(6))
// 记录第一个响应中的模型信息
if (chunkCount === 1 && data.model) {
console.log('✅ [响应确认] API 返回的模型:', data.model)
console.log(' 请求的模型:', body.model)
console.log(' 模型匹配:', data.model === body.model ? '✓ 一致' : '✗ 不一致!')
}
const delta = data.choices?.[0]?.delta const delta = data.choices?.[0]?.delta
// 处理普通内容 // 处理普通内容