Когда RAG — зло. История 258 диалогов
Год назад я закладывал архитектуру мультимодального агента для поддержки 1С:УНФ. Классика: юзер шлет скриншот ошибки, агент должен разобраться и выполнить действие. Первое, что приходит в голову — нарезать изображения, прогнать через OCR, сделать эмбеддинги, найти похожие кейсы. Multimodal RAG, блеск. Через 258 диалогов я его выкинул.
Почему? Потому что RAG отлично работает, когда у тебя есть текст и структурированные данные. Но интерфейс 1С — это не структура. Это пиксели. Таблицы, кнопки, модалки — всё на своих местах, но OCR превращает это в кашу. Эмбеддинги не улавливают взаимное расположение элементов. Точность упала до 67%. Я психанул и переписал всё заново.
В итоге родился агент, который просто шлет скриншот в мультимодальную LLM — и получает ответ за 1.2 секунды. Никакого RAG. Никаких векторных баз. Только зрение модели.
Почему Multimodal RAG ломается на интерфейсах
Теория говорит: «Извлеки текст из картинки, найди похожие инструкции, верни ответ». На практике:
- OCR в модалках 1С глотает русский текст с венгерской раскладкой
- Эмбеддинги не видят, что красная кнопка — это «Удалить», а не «Отмена»
- Контекст диалога (пользователь уже нажал «Сохранить») теряется при поиске по кускам
- Латентность: OCR + эмбеддинг + поиск = 4-6 секунд. В чате это вечность
Параллельно я тестировал прямой визуальный подход — GPT-4o, Gemini 2.5 Pro и свежий Claude 4 Opus (релиз апреля 2026). Разница в скорости и точности оказалась колоссальной. 258 диалогов — это не просто число, это 258 падений RAG, после которых я перестал верить в «мультимодальное извлечение».
Архитектура: агент, который видит
Вместо цепочки извлечения — простой конвейер. Вход: скриншот + история чата. Выход: JSON c действием (call tool или ответ пользователю). Модель — GigaChat Pro Vision (последняя версия от Сбера, май 2026) или OpenRouter с GPT-4o. Выбор пал на GigaChat из-за лицензии и скорости внутри РФ, но код универсален.
Важно: модель получает сырой скриншот (не обрезанный, без предобработки). Только изменение размера до 1024px по длинной стороне — это экономит токены без потери смысла.
1 Настройка Telegram-бота
Для старта вам понадобится сам бот. Можно написать с нуля на python-telegram-bot или воспользоваться готовым решением. Если хотите прокачать навыки именно создания ботов — есть отличный курс Skillbox по созданию Telegram-ботов и продвижению в мессенджерах. В проекте используется webhook на FastAPI.
from fastapi import FastAPI, Request
from telegram import Update, Bot
app = FastAPI()
bot = Bot(token="YOUR_TOKEN")
@app.post("/webhook")
async def webhook(request: Request):
update = Update.de_json(await request.json(), bot)
await dispatcher.process_update(update)
return {"ok": True}
2 Захват и передача скриншота
Агент работает как ассистент — пользователь делает скриншот экрана (Win+Shift+S, Cmd+Shift+4) и отправляет боту. Никаких автоматических скриншотеров — только то, что видит человек. Это ключевое: мы сохраняем контекст взгляда пользователя. Не нужно вырезать окно 1С — пусть в кадре будет даже панель задач. LLM сама разберется, что важно.
async def handle_photo(update: Update, context: ContextTypes.DEFAULT_TYPE):
photo = update.message.photo[-1] # самая большая версия
file = await photo.get_file()
image_bytes = await file.download_as_bytearray()
# ресайз до 1024px
from PIL import Image
img = Image.open(BytesIO(image_bytes))
img.thumbnail((1024, 1024))
buffer = BytesIO()
img.save(buffer, format="JPEG", quality=85)
buffer.seek(0)
# отправляем в LLM
await process_with_llm(buffer, context)
3 Мультимодальный промпт и инструменты
Здесь магия. Модель получает системное сообщение, историю диалога (последние 10 сообщений) и скриншот. Она должна вернуть структурированный ответ: либо ответ пользователю, либо вызов инструмента. Я использую архитектуру planner/executor, описанную в статье про современного AI-агента.
Системный промпт:
Ты — ассистент поддержки 1С:УНФ. Ты видишь скриншот интерфейса.
Опиши, что на нём происходит. Если нужно выполнить действие — верни JSON с tool и arguments.
Если нужно просто ответить — верни JSON с response (текст).
Не выдумывай данные. Опирайся только на то, что видишь.
История (последние 10):
{history}
Скриншот: [изображение]
Агент имеет набор инструментов: check_order_status, update_document, get_client_info. Вызов идет через REST API 1С через OpenID Connect.
В итоге вместо RAG-пайплайна мы имеем один вызов LLM, который сразу видит проблему. Результаты после 258 диалогов: точность ответов — 94% (против 67% с RAG), среднее время ответа — 2.1 сек (против 5.8 сек). И да, мы убрали хранение эмбеддингов вообще.
Нюансы, которые сломают ваш бот
Выкатил в прод — и через день агент начал «галлюцинировать» номера заказов. Оказалось, он читал цифры из скриншота, но не проверял их через API. Решение — добавить валидатор: перед выполнением инструмента модель обязана подтвердить, что данные взяты из скриншота, а не из головы.
- Проблема: LLM дорисовывает недостающие поля (номер документа, дату). Решение: требовать JSON с полем
source— может бытьscreenshotилиhistory. - Проблема: Пользователь шлет скриншот с частично закрытым окном. Решение: не делать обрезку — модель сама поймет, что информации недостаточно, и попросит новый скрин.
- Проблема: Дороговизна токенов: скриншот ~1000 токенов. 258 диалогов × 3 витка = 774000 токенов. Решение: кэшировать повторяющиеся скриншоты (по хешу) и использовать более дешевую модель (GigaChat Lite) для простых вопросов.
Кстати, о кэшировании — подробно про организацию stateful memory я писал в статье «AI-агент, который не спит». Мы используем SQLite для хранения последних 50 диалогов — этого хватает.
Мониторинг и улучшение
Каждый ответ агента логируется с указанием, использовал ли он скриншот или нет. Метрика — точность первого ответа. Если пользователь отправляет уточняющий скриншот, значит первый был плохим. После 258 диалогов мы собрали 31 случай переспроса и докрутили промпт.
Инструменты вызываются через ReAct-цикл (как в производственном гайде по ReAct), но без RAG-составляющей. Модель сама решает, когда ей достаточно одного взгляда на скриншот, а когда нужно дернуть API.
Roadmap: куда движемся дальше
Сейчас экспериментируем с видео-потоком — пользователь стримит экран, а агент анализирует действия в реальном времени. Модели Gemini 2.5 Flash уже поддерживают видео до 30 секунд. Но это совсем другая история — и, скорее всего, я напишу о ней отдельно.
А пока — если вы всё ещё строите мультимодального агента через RAG, остановитесь. Попробуйте дать модели картинку целиком. Результат удивит. Как удивил меня 258 диалогов назад.