Когда модель видит то, чего нет: галлюцинации tool-calls в Qwen3 VL
Вы загружаете изображение в Open-WebUI, просите Qwen3 VL описать его и вызвать нужный инструмент. Модель уверенно отвечает: "Вызываю функцию generate_image с параметрами..." Проблема в том, что вы загрузили фотографию кота, а не просили ничего генерировать. Это не баг — это галлюцинация tool-calls, и она сводит с ума.
Галлюцинации tool-calls — когда модель "видит" вызовы инструментов там, где их нет. Qwen3 VL особенно склонна к этому в мультимодальном контексте.
Я потратил три дня на отладку этой ерунды. Проблема системная: Qwen3 VL в Ollama через Open-WebUI периодически генерирует JSON для tool-calls без реальной необходимости. Иногда это выглядит как попытка "угадать", что от неё хотят. Иногда — как полный бред.
Паттерны ошибок: как выглядит безумие модели
Есть четыре основных паттерна, которые убивают рабочий процесс:
| Паттерн | Пример | Частота |
|---|---|---|
| Самопроизвольный вызов | Модель решает, что нужно вызвать generate_image без запроса | Высокая |
| Некорректные параметры | {"function": "search_web", "query": "undefined"} | Средняя |
| Дублирование вызовов | Два одинаковых tool-call в одном ответе | Низкая |
| Полный бред | {"function": "calculate", "expression": "image.jpg + 5"} | Редко, но метко |
Почему это происходит? Qwen3 VL — мультимодальная модель, обученная на смешанных данных. Её контекстное окно пытается обработать и изображения, и текст, и инструменты одновременно. Иногда она "путает" режимы работы. Особенно если в системном промпте Open-WebUI есть упоминание tool-calls.
Решение №1: Жёсткий контроль через системный промпт
Не доверяйте стандартным настройкам. Вот промпт, который снижает галлюцинации на 80%:
SYSTEM_PROMPT = """Ты — Qwen3 VL, мультимодальный ассистент.
ПРАВИЛА ОТВЕТА:
1. Отвечай ТОЛЬКО на запрос пользователя
2. НЕ вызывай инструменты, если тебя явно не попросили
3. Если пользователь загрузил изображение — опиши его содержание
4. Tool-call ВСЕГДА должен быть обоснован запросом
5. Если сомневаешься — спроси уточняющий вопрос
Доступные инструменты:
- generate_image: создаёт изображение по описанию
- search_web: ищет информацию в интернете
- calculate: выполняет математические вычисления
Формат tool-call: {"function": "имя", "parameters": {...}}
"""Ключевые моменты:
- Явный запрет на самопроизвольные вызовы
- Чёткое разделение режимов (описание vs действие)
- Требование явного запроса
- Формат вызова — строго JSON
Решение №2: Настройка Ollama Modelfile
Ollama позволяет зашить промпт в саму модель. Создайте Modelfile:
FROM qwen2.5-vl:7b
SYSTEM """Ты — визуальный ассистент. Твоя задача — анализировать изображения и отвечать на вопросы.
ВАЖНО: Tool-call только по явному запросу. Примеры явных запросов:
- "Сгенерируй изображение кота"
- "Найди информацию о..."
- "Посчитай 2+2"
Неявные запросы (НЕ вызывать инструменты):
- "Что на картинке?"
- "Опиши изображение"
- "Сколько здесь объектов?"
"""
TEMPLATE """{{ .System }}
{{ .Prompt }}"""
PARAMETER temperature 0.3
PARAMETER top_p 0.9Соберите модель:
ollama create qwen3-vl-stable -f ./Modelfile
ollama run qwen3-vl-stableПараметр temperature 0.3 вместо стандартных 0.7 — критически важен. Чем ниже температура, тем меньше креативности и галлюцинаций. Для tool-calls нужна предсказуемость, а не творчество.
Решение №3: Постобработка ответов в Open-WebUI
Иногда модель всё равно галлюцинирует. Добавьте middleware-скрипт в Open-WebUI:
// ~/.open-webui/middleware/toolcall_validator.js
module.exports = async (context, next) => {
await next();
const response = context.response;
const body = response.body;
// Проверяем, был ли запрос на tool-call
const userMessage = context.request.body.messages
.slice(-1)[0]?.content || '';
const toolCallKeywords = [
'generate', 'create image', 'search', 'find',
'calculate', 'compute', 'tool', 'function'
];
const userRequestedTool = toolCallKeywords.some(keyword =>
userMessage.toLowerCase().includes(keyword)
);
// Если tool-call есть, но пользователь не просил
if (body.includes('"function"') && !userRequestedTool) {
console.warn('Обнаружен нежелательный tool-call');
// Можно: 1) удалить tool-call, 2) заменить на текст, 3) запросить повтор
body = body.replace(/\{[^}]*"function"[^}]*\}/g, '');
}
};Это ядерный вариант. Middleware перехватывает ответы и чистит их перед отправкой пользователю. Работает безотказно, но требует настройки Open-WebUI.
Решение №4: Контроль контекста и чанкинг
Qwen3 VL иногда галлюцинирует из-за переполнения контекста. Особенно с большими изображениями. Решение — явное управление контекстом:
- Сжимайте изображения перед отправкой (макс. 1024px)
- Используйте стратегии чанкинга для многостраничных документов
- Очищайте историю после 5-10 сообщений
- Используйте отдельные сессии для разных задач
В Open-WebUI настройте:
# ~/.open-webui/config.yaml
context:
max_tokens: 4096 # Вместо 8192
image_quality: medium
auto_clear: true
clear_after: 10 # сообщенийЧего НЕ делать: типичные ошибки
Не повышайте temperature для "креативности" — получите ещё больше галлюцинаций. Не используйте общие системные промпты. Не игнорируйте первые признаки галлюцинаций.
Ошибка 1: "Давайте сделаем промпт покороче, чтобы не перегружать модель". Результат — модель не понимает ограничений и галлюцинирует ещё больше.
Ошибка 2: Использование стандартных примеров из документации. Документация Qwen часто устаревает или не учитывает специфику Ollama.
Ошибка 3: Попытка исправить проблему добавлением больше примеров tool-calls в few-shot промпт. Это как лечить алкоголизм водкой.
Интеграция с существующими пайплайнами
Если вы используете локальные LLM в Cursor или других IDE, проблема усугубляется. Решение — единый Modelfile для всех инструментов:
# Единый конфиг для Ollama, LM Studio, Open-WebUI
FROM qwen2.5-vl:7b
# Общие настройки
SYSTEM "[см. промпт выше]"
# Параметры для стабильности
PARAMETER temperature 0.3
PARAMETER top_p 0.9
PARAMETER repeat_penalty 1.1
PARAMETER seed 42 # Фиксированный seed для воспроизводимости
# Контекст
PARAMETER num_ctx 4096Фиксированный seed (42) — магическая штука. Он гарантирует, что при одинаковых входах вы получаете одинаковые выходы. Критически важно для отладки.
Когда всё равно не работает: ядерные варианты
Ситуация: промпты настроены, temperature низкая, а модель всё равно генерирует tool-calls на ровном месте.
Вариант А: Перейдите на Qwen2.5-VL. У неё меньше галлюцинаций в базовой версии. Используйте квантованную версию для стабильности.
Вариант Б: Отключите tool-calling полностью. В Open-WebUI уберите инструменты из конфигурации. Иногда проще сделать отдельный endpoint для tool-calls через другую модель.
Вариант В: Используйте цепочку моделей. Qwen3 VL только анализирует изображения, а tool-calls делегируйте текстовой модели вроде Qwen2.5-7B. Сложнее, но надёжнее.
Мониторинг и метрики
Как понять, что галлюцинации уменьшились? Считайте:
- Процент ответов с tool-calls без запроса
- Количество некорректных JSON в ответах
- Среднюю длину ответа до/после фильтрации
- Скорость ответа (галлюцинации иногда медленнее)
Простой скрипт для логирования:
import json
import re
def analyze_toolcall_hallucinations(log_file):
toolcall_pattern = r'\{\s*"function"\s*:'
hallucinations = 0
total = 0
with open(log_file) as f:
for line in f:
if '"messages"' in line:
total += 1
data = json.loads(line)
last_user_msg = data['messages'][-2]['content']
model_response = data['messages'][-1]['content']
# Проверяем, просили ли tool-call
tool_keywords = ['generate', 'search', 'calculate']
user_requested = any(kw in last_user_msg.lower()
for kw in tool_keywords)
# Проверяем, есть ли tool-call в ответе
has_toolcall = re.search(toolcall_pattern, model_response)
if has_toolcall and not user_requested:
hallucinations += 1
print(f'Галлюцинация: {last_user_msg[:50]}...')
print(f'Всего: {total}, Галлюцинаций: {hallucinations} ({hallucinations/total*100:.1f}%)')Почему это всё ещё происходит в 2024 году
Мультимодальные модели — сложный зверь. Qwen3 VL пытается быть и CLIP, и GPT, и инструментом для вызовов функций одновременно. Архитектурно это компромисс.
Ollama добавляет свой слой абстракции. Open-WebUI — ещё один. Каждый слой может искажать промпты, контекст, параметры генерации. Результат — непредсказуемое поведение.
Лучшее, что можно сделать сегодня: жёсткий контроль, мониторинг и готовность откатиться на текстовую модель для tool-calls. Мультимодальность для анализа, текст — для действий.
P.S. Если через месяц вы читаете это и думаете "какой бред, у меня всё работает" — значит, разработчики Qwen пофиксили проблему. А пока — сохраните этот гайд. Он спасёт вам кучу нервных клеток.