# Qwen3-VL 工具调用兼容性修复 ## 问题描述 选择 `Qwen3-VL-8B-Instruct` 模型发送消息后,模型回复被误判为"仅返回思考过程"而被删除,用户看不到任何回复内容。 ## 根本原因 Qwen3-VL 不支持 function calling/tools。当请求携带 `tools` 参数时,SiliconFlow 平台会将模型输出放到 `reasoning_content` 字段而非 `content` 字段: ```json // 带 tools 时 {"delta":{"content":null,"reasoning_content":"Hello"}} // 不带 tools 时 {"delta":{"content":"Hello"}} ``` 同时前端 `validateAssistantContent` 只检查 `content` 是否为空,未考虑 `reasoning` 类型的 part,导致只有思考过程的消息被误删。 ## 解决方案 ### 1. parseMode 重构 将 `parseMode` 从 `openai`/`anthropic` 改为 `openai`/`openai-compatible`: - `openai`:使用 `@ai-sdk/openai` 的 `createOpenAI`(原生 OpenAI API) - `openai-compatible`:使用 `@ai-sdk/openai-compatible` 的 `createOpenAICompatible`(兼容 API,如 SiliconFlow、DeepSeek) ### 2. 模型表增加 supportsTools 字段 `llmModels` 表新增 `supportsTools` 字段(`integer("supports_tools").default(1).notNull()`): - `1`(true):模型支持工具调用,请求时正常传 tools 参数 - `0`(false):模型不支持工具调用,请求时不传 tools 参数 ### 3. 前端工具按钮禁用 不支持工具的模型,工具开关按钮自动禁用,tooltip 显示"当前模型不支持工具调用"。 ### 4. 前端 validateAssistantContent 修复 增加 `reasoning` 类型检查,避免只有思考过程的消息被误删。 ## 改动文件清单 ### 数据库 | 文件 | 改动 | |------|------| | `packages/drizzle-pkg/lib/schema/llm.ts` | `LlmParseModes` 改为 `["openai", "openai-compatible"]`;`llmModels` 增加 `supportsTools` 字段 | | `packages/drizzle-pkg/migrations/0018_colossal_barracuda.sql` | 迁移文件:`ALTER TABLE llm_models ADD supports_tools` | ### 服务端 - resolveModel 实现 | 文件 | 改动 | |------|------| | `server/api/agent/chat/index.post.ts` | `resolveModel`:`openai` 用 `createOpenAI`,`openai-compatible` 用 `createOpenAICompatible`;`streamText` 中 `supportsTools=false` 时不传 tools | | `server/api/llm/chat/index.post.ts` | 同步 `resolveModel` 和 tools 逻辑 | | `server/service/agent/title.ts` | 同步 `resolveModel` | ### 服务端 - Service 层 | 文件 | 改动 | |------|------| | `server/service/llm/index.ts` | `LlmModelRow` 接口加 `supportsTools`;`listSystemModels`、`listAllEnabledModelsForUser` 查询加 `supportsTools`;`createModel`、`createSystemModel`、`updateModel` 加 `supportsTools` 字段 | ### 服务端 - API 接口 | 文件 | 改动 | |------|------| | `server/api/admin/llm/providers/index.post.ts` | `parseMode` 校验更新为 `["openai", "openai-compatible"]` | | `server/api/admin/llm/models/index.post.ts` | 加 `supportsTools` 参数 | | `server/api/llm/models/[providerId].post.ts` | 加 `supportsTools` 参数 | | `server/api/llm/models/detail/[id].put.ts` | 加 `supportsTools` 参数 | | `server/api/llm/models/index.get.ts` | 返回值包含 `supportsTools`(通过 service 层) | | `server/api/llm/chat/models.get.ts` | 查询加 `supportsTools` 字段 | | `server/api/agent/models/index.get.ts` | 返回值加 `supportsTools` | ### 前端 | 文件 | 改动 | |------|------| | `app/pages/index.vue` | `ModelOption` 加 `supportsTools`;新增 `currentModelSupportsTools` computed;`enableTools` getter 不支持时返回 false;传 `tools-disabled` prop | | `app/components/agent/AgentChatArea.vue` | 加 `toolsDisabled` prop,传给 `AgentInput` | | `app/components/agent/AgentInput.vue` | 加 `toolsDisabled` prop;工具按钮禁用逻辑和样式 | | `app/composables/useAgentChat.ts` | `validateAssistantContent` 增加 `hasReasoning` 检查 | | `app/composables/useLlmChat.ts` | `validateAssistantContent` 增加 `hasReasoning` 检查 | | `app/pages/settings/llm-config/index.vue` | provider 编辑页面 `parseMode` 选项更新;模型编辑表单加 `supportsTools` 下拉选项 | | `app/pages/settings/llm-test/index.vue` | `ModelOption` 加 `supportsTools`;工具开关不支持时禁用 | ## 数据库现状 - 数据库路径:`packages/drizzle-pkg/db.sqlite` - provider 配置: - deepseek(id=2, parseMode=`openai-compatible`) - siliconflow(id=3, parseMode=`openai-compatible`) - 模型配置: - deepseek-v4-flash(id=2, supportsTools=1) - Qwen3-VL-8B-Instruct(id=4, supportsTools=0) ## 关键技术决策 1. **`createOpenAI` 返回类型与 `LanguageModel` 不完全兼容**:需用 `as unknown as LanguageModel` 转换 2. **`enableThinking`/`enableTools` 改为请求体参数优先于 session 配置**:更灵活的控制方式 3. **`openai-compatible` 作为默认 parseMode**:大多数第三方 API 都是 OpenAI 兼容协议 ## 测试验证 - [x] Qwen3-VL-8B-Instruct 发送消息正常返回内容(不再只有思考过程) - [x] Qwen3-VL-8B-Instruct 工具按钮禁用,tooltip 显示"当前模型不支持工具调用" - [x] deepseek-v4-flash 工具按钮正常启用,可开关工具 - [x] deepseek-v4-flash 工具列表正常显示(fetch_html、calculator、uuid 等) ## 后续注意事项 - `server/api/llm/providers/[id].models.get.ts` 中仍有 `anthropic` 分支代码,但不会命中(parseMode 不再有 "anthropic"),走 else 分支 - `app/pages/index.vue` 第 72 行硬编码 `enableThinking: () => false` - 新增模型时默认 `supportsTools=1`,需在管理后台手动改为 0 如果模型不支持工具