Почему ИИ врёт про ваши таблицы (и как это исправить)
Вы загружаете Excel-файл с финансовыми отчётами в ChatGPT. Спрашиваете: "Какая была выручка в Q3?" Получаете красивый ответ с цифрами. А потом обнаруживаете, что этих цифр в таблице никогда не было. LLM просто придумала их, потому что где-то в контексте промпта мелькнуло слово "выручка".
Галлюцинации в Excel-анализе — не баг, а фундаментальная проблема. LLM обучены на текстах, а таблицы — это структурированные данные со своей логикой. Без специальной обработки нейросеть будет "додумывать" связи там, где их нет.
Мы три месяца ломали голову над этой проблемой. Тестировали десятки подходов. Упали в производительности с 10 запросов в секунду до одного в минуту. Зато добились 99.8% точности ответов по реальным бизнес-таблицам.
Архитектура, которая не врёт
Universal Excel Insight Engine — это не просто RAG поверх таблиц. Это система с тройной проверкой каждого ответа. Представьте себе трёх экспертов: один ищет данные, второй проверяет их корректность, третий формулирует ответ. Именно так работает наш пайплайн.
1 Схема-осознанный парсинг: как читать таблицы правильно
Первая ошибка большинства RAG-систем для Excel — они превращают таблицы в плоский текст. Теряют структуру, связи между ячейками, формулы, объединённые области. Наш парсер сохраняет всё:
- Иерархию заголовков — понимает, какие строки являются подзаголовками
- Семантические типы колонок — отличает даты от чисел, валюты от процентов
- Формулы и вычисляемые поля — извлекает логику, а не только результаты
- Связи между листами — если в Sheet2 есть ссылка на Sheet1, мы это знаем
from excel_insight_engine.parser import SmartExcelParser
# Не делайте так (плоский текст):
# df.to_string() — теряет всю структуру
# Делайте так:
parser = SmartExcelParser(file_path="financials.xlsx")
schema = parser.extract_schema() # Получаем мета-информацию о таблице
data_chunks = parser.chunk_with_context(
max_chunk_size=1000,
preserve_relationships=True
)
print(f"Таблица содержит {schema['sheet_count']} листов")
print(f"Обнаружено {schema['calculated_fields']} вычисляемых полей")
print(f"Семантические типы колонок: {schema['column_types']}")
2 Двойная индексация: векторы + схема
Обычный векторный поиск для таблиц работает плохо. Почему? Потому что "выручка Q3" и "revenue third quarter" могут иметь разные эмбеддинги, но означать одно и то же. Мы добавили схематический индекс:
| Тип индекса | Что индексирует | Когда использовать |
|---|---|---|
| Векторный | Семантическое значение данных | "Найди аномалии в продажах" |
| Схематический | Структуру таблицы, названия колонок | "Какая колонка содержит выручку?" |
| Гибридный | Оба подхода с реранкингом | Любые сложные запросы |
from excel_insight_engine.indexing import HybridIndexer
indexer = HybridIndexer()
# Индексируем с пониманием схемы
index_result = indexer.index_excel(
chunks=data_chunks,
schema=schema,
embedding_model="text-embedding-3-small",
create_schema_index=True # Этот флаг критически важен
)
# Поиск работает в два этапа:
# 1. Сначала ищем по схематическому индексу
# 2. Затем усиливаем векторным поиском
results = indexer.hybrid_search(
query="выручка за третий квартал по регионам",
schema_hint=True # LLM подсказывает, какие колонки искать
)
Эта техника похожа на то, что мы использовали в Ragex для анализа кода, но адаптирована для табличных данных.
3 Data Quality Guardrails: стоп-сигналы для галлюцинаций
Самая важная часть системы. Мы называем это "ограждениями" — правилами, которые не дадут LLM соврать даже при большом желании:
from excel_insight_engine.guardrails import DataQualityGuard
guard = DataQualityGuard(
rules=[
"no_extrapolation", # Запрещаем экстраполяцию данных
"require_citation", # Каждое утверждение должно иметь ссылку на ячейку
"validate_calculations", # Проверяем математику
"check_date_ranges", # Не позволяем выходить за границы дат в таблице
"prevent_assumptions" # Блокируем фразы "вероятно", "скорее всего"
]
)
# Перед отправкой ответа пользователю
verified_response = guard.validate(
llm_response=raw_answer,
source_chunks=retrieved_data,
schema=schema,
query=user_query
)
if not verified_response["passed"]:
# Вместо галлюцинации возвращаем:
return {
"answer": "Не могу дать точный ответ. В таблице нет данных за указанный период.",
"confidence": 0.1,
"citations": [],
"validation_errors": verified_response["errors"]
}
Эти guardrails снизили галлюцинации с 40% до 0.2%. Цена — иногда система отказывается отвечать, когда не уверена. Но лучше молчать, чем врать в финансовых отчётах.
Пайплайн от запроса до ответа
Вот как всё работает вместе:
- Анализ запроса — LLM определяет, какие части таблицы нужны (как в анализе BPMN, но для Excel)
- Гибридный поиск — схема + семантика
- Верификация источников — проверяем, что найденные данные релевантны
- Генерация с цитированием — LLM создаёт ответ, указывая номера ячеек
- Проверка guardrails — триггеры срабатывают при малейшем подозрении
- Форматирование ответа — таблицы, графики, если нужно
Скорость vs точность: наш пайплайн работает в 3-5 раз медленнее, чем простой RAG. Но в бизнес-аналитике ошибка стоит дороже, чем лишняя секунда ожидания. Мы оптимизировали не latency, а accuracy.
Ошибки, которые мы совершили (чтобы вы их не повторили)
Ошибка 1: Слишком большие чанки
Разбивали таблицы по 10 000 токенов. LLM тонула в данных, начинала "угадывать". Работающий размер — 500-1000 токенов с перекрытием в 10%.
Ошибка 2: Игнорирование формул
Сначала парсили только значения. Пользователь спрашивал: "Почему итог не сходится?" А мы не знали, потому что не видели формул =SUM(A1:A10). Теперь извлекаем и значения, и формулы.
Ошибка 3: Слишком доверчивые промпты
Промпт "Ответь на вопрос на основе таблицы" — это приглашение к галлюнациям. Теперь промпт начинается с: "Ты можешь использовать ТОЛЬКО данные из приведённых фрагментов. Если данных нет — скажи 'не знаю'."
Производительность в продакшене
Мы развернули систему для аналитического отдела крупного ритейлера. 50 пользователей, ~200 запросов в день. Метрики за месяц:
- Точность ответов: 99.8% (проверяли вручную 1000 случайных ответов)
- Среднее время ответа: 4.2 секунды
- Отказы от ответа: 7.3% (система предпочла не отвечать, чем ошибиться)
- Самые частые запросы: сравнение периодов, поиск аномалий, агрегация по категориям
Самое интересное — пользователи начали доверять системе больше, чем коллегам-аналитикам. Потому что система не придумывает, а если не знает — честно признаётся.
Что дальше? Мультимодальность и реальное время
Сейчас мы работаем над двумя улучшениями:
1. Мультимодальный анализ — многие Excel-файны содержат графики, диаграммы, даже фотографии товаров. Как в мультимодальном RAG, но для таблиц. Пользователь спрашивает: "Почему на графике пик в марте?" Система анализирует и данные, и визуализацию.
2. Real-time верификация — вместо пост-фактум проверки guardrails, встроить верификацию в процесс генерации. Как техники из RAG 2024, но для структурированных данных.
FAQ: ответы на вопросы, которые вы ещё не задали
Как система работает с большими файлами (100+ МБ)?
Постепенная загрузка + selective indexing. Мы не индексируем весь файл сразу, а сначала анализируем структуру, затем индексируем наиболее вероятно полезные листы. Остальные — по запросу.
Поддерживаются ли Google Sheets?
Да, через API. Архитектура та же, меняется только парсер. Важно: Sheets часто содержат real-time данные, поэтому кеширование нужно настраивать аккуратнее.
Можно ли использовать локальные модели вместо GPT-4?
Мы тестировали с Llama 3.1 70B и Claude 3.5 Sonnet. Результаты хуже на 5-15% по точности, но guardrails компенсируют разницу. Главное — достаточно большой контекст (минимум 32K токенов).
Как обрабатываются персональные данные?
Все парсеры работают on-premise. Эмбеддинги можно считать локально (через SentenceTransformers). В облако уходит только запрос и ответ (если вы используете cloud LLM).
И последнее: самая частая ошибка при внедрении такой системы — попытка сделать её "умнее", снизив strictness guardrails. Не делайте этого. Лучше пусть система десять раз откажется ответить, чем один раз соврёт на важном совещании. Доверие к ИИ хрупко — одна серьёзная галлюцинация может разрушить месяцы работы.