Исправление галлюцинаций tool-calls в Qwen3 VL для Ollama и Open-WebUI | AiManual
AiManual Logo Ai / Manual.
13 Янв 2026 Гайд

Qwen3 VL галлюцинирует tool-calls в Ollama: как заставить модель видеть реальность

Паттерны ошибок, промпты и решения для мультимодальных галлюцинаций в Qwen3 VL. Практический гайд по отладке tool-calling в Ollama.

Когда модель видит то, чего нет: галлюцинации 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 иногда галлюцинирует из-за переполнения контекста. Особенно с большими изображениями. Решение — явное управление контекстом:

  1. Сжимайте изображения перед отправкой (макс. 1024px)
  2. Используйте стратегии чанкинга для многостраничных документов
  3. Очищайте историю после 5-10 сообщений
  4. Используйте отдельные сессии для разных задач

В 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 пофиксили проблему. А пока — сохраните этот гайд. Он спасёт вам кучу нервных клеток.