Практическое руководство
Как отделить ошибки OCR от ошибок RAG на русскоязычных документах
Неверный ответ системы легко списать на ретривер или языковую модель. Но если слово, число или заголовок были искажены при распознавании страницы, все последующие компоненты уже работают с неверными данными.
Почему сквозной показатель скрывает причину
OCR преобразует изображение страницы в текст. Затем текст разбивается на фрагменты, индексируется, извлекается по запросу и передаётся модели для ответа. Ошибка в начале цепочки может выглядеть как сбой любого следующего этапа.
Например, на скане написано «срок — 30 календарных дней», а распознано «срок — 80 календарных дней». Поиск найдёт подходящий фрагмент, а модель аккуратно ответит «80 дней». Сквозная проверка отметит ответ как неверный, хотя ретривер и генератор корректно обработали испорченный вход.
Диагностика должна отвечать не только на вопрос «верен ли финальный ответ», но и на четыре независимых вопроса:
- Правильно ли распознана нужная область страницы?
- Сохранилась ли информация после разбиения?
- Попал ли содержащий ответ фрагмент в результаты поиска?
- Сформировала ли модель ответ из предоставленного контекста?
Минимальный набор для воспроизводимой оценки
Соберите небольшой контрольный набор из документов, которые разрешено обрабатывать. Для каждого вопроса вручную зафиксируйте эталонный ответ и место, где он находится. Не начинайте с тысяч примеров: несколько десятков тщательно размеченных случаев полезнее большого набора с неопределённой разметкой.
Что хранить для каждого примера
{
"question_id": "q-001",
"document_id": "doc-017",
"question": "Какой срок указан в пункте 4.2?",
"reference_answer": "30 календарных дней",
"evidence": [
{
"page": 12,
"region_id": "p12-r07",
"reference_text": "Срок составляет 30 календарных дней."
}
]
}
Это демонстрационная структура, а не результат реального теста. Поле region_id может обозначать абзац, строку, таблицу или координаты прямоугольника. Важно, чтобы оно связывало эталонный текст с исходной страницей.
Зафиксируйте версии
Запишите версию OCR-движка, параметры обработки изображения, алгоритм разбиения, модель представлений, настройки поиска, шаблон запроса и модель генерации. Иначе два запуска нельзя будет корректно сравнить.
run_id: ru-docs-001
ocr:
engine: "<название и версия>"
language: "ru"
deskew: true
chunking:
strategy: "by_heading_then_length"
max_chars: 1800
overlap_chars: 200
retrieval:
top_k: 5
generation:
temperature: 0
require_evidence: true
Значения приведены как пример конфигурации. Они не являются универсальной рекомендацией: размер фрагмента зависит от структуры документов и используемой модели.
Шаг 1. Оцените OCR отдельно
Сравнивайте распознанный текст с ручной расшифровкой тех областей, на которых основаны вопросы. Полное вычитывание каждого документа необязательно, если задача — диагностировать конкретный набор ответов.
Базовые показатели
- CER
- Доля вставленных, удалённых и заменённых символов относительно длины эталонного текста.
- WER
- Аналогичный показатель на уровне слов. Для русского языка он чувствителен к окончаниям, дефисам и ошибкам разделения слов.
- Точность критических полей
- Доля безошибочно распознанных дат, сумм, номеров пунктов, фамилий и единиц измерения.
Одного CER недостаточно. Замена кавычки почти не влияет на ответ, а замена «30» на «80» меняет его полностью. Поэтому пометьте критические фрагменты и проверяйте их точным сравнением после допустимой нормализации.
def normalize(text):
return " ".join(
text.replace("\u00a0", " ")
.replace("ё", "е")
.split()
).lower()
critical_field_ok = (
normalize(ocr_value) == normalize(reference_value)
)
Замена «ё» на «е» здесь — осознанное правило примера. Не применяйте её, если различие значимо для имён, терминов или требований проекта. Числа, знаки валют, десятичные разделители и номера пунктов лучше не исправлять эвристически.
Особые риски русскоязычных сканов
- смешение кириллицы и латиницы: «С» и «C», «Р» и «P»;
- замены «З/3», «О/0», «В/8», «1/І/l»;
- потеря минуса, процента или десятичной запятой;
- слияние колонок и строк таблицы;
- переносы слов с дефисом и разрыв номера пункта;
- ошибочный порядок чтения на многостолбцовой странице.
Шаг 2. Проверьте разбиение без участия поиска
Подайте в модуль разбиения эталонный текст, а не результат OCR. Для каждого вопроса проверьте, существует ли хотя бы один фрагмент, содержащий все необходимые свидетельства.
Полезно считать покрытие свидетельств:
evidence_coverage =
число вопросов, для которых свидетельство сохранено во фрагментах
/ общее число вопросов
Дополнительно отмечайте причину потери:
- ответ и условие оказались в разных фрагментах;
- заголовок отделился от относящегося к нему текста;
- строка таблицы разорвана;
- сноска или подпись отброшена;
- служебная очистка удалила содержательную строку.
Этот шаг отделяет ошибку разбиения от ошибки поиска. Если нужного свидетельства нет ни в одном индексируемом фрагменте, ретривер физически не может его вернуть.
Шаг 3. Оцените поиск на эталонном тексте
Постройте временный индекс из фрагментов эталонной расшифровки. Выполните контрольные запросы и определите, на какой позиции появился фрагмент со свидетельством.
Минимальные метрики
Recall@k— доля вопросов, для которых нужное свидетельство найдено среди первыхkрезультатов;MRR— среднее обратное значение позиции первого релевантного результата;- доля запросов без релевантного результата;
- распределение позиций релевантного фрагмента, а не только среднее значение.
Релевантность следует определять по заранее размеченному свидетельству, а не по совпадению слов с эталонным ответом. Иначе фрагмент с похожей датой или формулировкой может быть ошибочно признан правильным.
for question in dataset:
results = retrieve(
query=question["question"],
index="reference-text-index",
top_k=5
)
hit = first_result_with_evidence(results, question["evidence"])
save_rank(question["question_id"], hit.rank if hit else None)
Функции в этом фрагменте обозначают интерфейс проверки; это не готовая библиотека. Реализация должна сопоставлять результаты с идентификаторами страниц и областей.
Шаг 4. Изолируйте генерацию
Передайте модели вопрос и вручную выбранное эталонное свидетельство. Так вы исключите OCR, разбиение и поиск. Инструкция должна разрешать модели отказаться от ответа, если контекста недостаточно.
Ответь только по приведённому контексту.
Если ответа в контексте нет, верни: «Недостаточно данных».
Укажи идентификатор использованного фрагмента.
Вопрос:
{question}
Контекст:
[{region_id}] {reference_text}
Проверяйте отдельно:
- совпадает ли содержание ответа с эталоном;
- подтверждается ли каждое существенное утверждение контекстом;
- верно ли указано свидетельство;
- не добавлены ли числа, условия или исключения, которых нет во фрагменте;
- воздерживается ли модель от ответа при недостаточном контексте.
Для чисел, дат и коротких полей используйте детерминированное сравнение. Для развёрнутых ответов потребуется заранее описанная рубрика и ручная проверка спорных случаев.
Шаг 5. Проведите четыре контрольных прогона
Теперь меняйте по одному источнику данных, сохраняя остальные настройки неизменными.
| Прогон | Текст | Контекст для модели | Что проверяет |
|---|---|---|---|
| A | Эталонный | Эталонное свидетельство | Генерацию |
| B | Эталонный | Результат поиска | Разбиение и поиск |
| C | OCR | Вручную выбранная область | Влияние распознавания без ретривера |
| D | OCR | Результат поиска | Полную рабочую цепочку |
Интерпретация различий:
- A неверен — проблема находится в генерации, инструкции или критерии ответа;
- A верен, B неверен — проверяйте разбиение и поиск на чистом тексте;
- A верен, C неверен — OCR повредил информацию, необходимую для ответа;
- B верен, D неверен — распознавание ухудшило индексируемый текст или поисковое представление;
- C верен, D неверен — текст ответа сохранился, но поиск не поднял нужный фрагмент;
- все прогоны верны, а рабочая система ошибается — ищите различия конфигурации, фильтров, версий индекса или шаблона запроса.
Как проверить, что оценка действительно разделена
После запуска у каждой ошибки должна быть трасса от исходной страницы до ответа:
question_id
→ page и region_id
→ reference_text
→ ocr_text
→ chunk_ids
→ retrieved_chunk_ids с позициями
→ prompt_context
→ generated_answer
→ verdict по каждому этапу
Возьмите несколько ошибочных случаев и вручную повторите классификацию. Для каждого случая должны выполняться следующие условия:
- Исходное изображение страницы доступно проверяющему.
- Разница между эталонным и распознанным текстом видна без повторного запуска OCR.
- Границы всех индексируемых фрагментов сохранены.
- Результаты поиска содержат позиции, оценки и стабильные идентификаторы.
- Контекст генерации сохранён именно в том виде, в котором его получила модель.
- Вердикт указывает первый этап, на котором необходимая информация была потеряна или искажена.
Главное правило классификации: назначайте причиной самый ранний сломанный этап. Если OCR заменил число, а поиск затем вернул фрагмент с этим числом, это ошибка OCR, а не поиска.
Практический формат отчёта
Не смешивайте показатели в один «процент качества». Сведите результаты по этапам и типам документов.
| Слой | Основной показатель | Диагностический срез |
|---|---|---|
| OCR | CER, WER, точность критических полей | Скан, таблица, колонка, качество страницы |
| Разбиение | Покрытие свидетельств | Заголовки, таблицы, сноски, длинные пункты |
| Поиск | Recall@k, MRR | Тип вопроса, длина фрагмента, фильтры |
| Генерация | Точность ответа и подтверждённость | Число, перечисление, условие, отказ |
| Вся цепочка | Доля верных ответов | Первопричина ошибки |
Полезно добавить матрицу переходов: сколько вопросов были исправны после OCR, потерялись при разбиении, не нашлись ретривером или исказились при генерации. Такая форма показывает, куда направить следующую итерацию.
Типовые ошибки оценки
Оценивать OCR только на обычном тексте
Средний CER может быть низким, хотя таблицы и числа распознаются плохо. Выделяйте типы содержимого и критические поля.
Считать фрагмент релевантным по словам запроса
Лексически похожий фрагмент не обязательно содержит ответ. Релевантность должна быть привязана к размеченной области документа.
Менять несколько компонентов одновременно
Одновременная замена OCR, размера фрагментов и модели представлений не позволяет определить причину изменения результата. Меняйте один слой за прогон.
Исправлять OCR до сохранения сырого результата
Автокоррекция может скрыть источник ошибки или заменить правильный термин. Храните исходный OCR-текст, нормализованную версию и журнал преобразований отдельно.
Проверять модель на контексте из ретривера
Так ошибка поиска смешивается с ошибкой генерации. Для изолированного теста модели нужен вручную подтверждённый контекст.
Не учитывать отсутствие ответа
В наборе должны быть вопросы, ответа на которые в документе нет. Иначе невозможно проверить корректный отказ и склонность модели дополнять контекст догадками.
Безопасная организация эксперимента
- Работайте с копиями документов и отдельным тестовым индексом.
- Не включайте реальные секреты, персональные данные и закрытые документы в примеры, логи или запросы к внешним сервисам.
- Не перезаписывайте исходные изображения и сырой результат OCR.
- Храните идентификаторы вместо полного текста там, где для метрики достаточно ссылки на область.
- Перед очисткой тестового индекса проверяйте его точное имя и среду; не используйте широкие маски удаления.
Для локальной структуры эксперимента достаточно безопасно создать новые каталоги без удаления существующих данных:
mkdir -p evaluation/reference
mkdir -p evaluation/ocr
mkdir -p evaluation/chunks
mkdir -p evaluation/retrieval
mkdir -p evaluation/generation
mkdir -p evaluation/reports
Ограничения метода
Ручная расшифровка и разметка свидетельств требуют времени и могут содержать ошибки. Для важных наборов полезна независимая проверка разметки вторым специалистом.
CER и WER не измеряют сохранность макета. Текст таблицы может быть распознан посимвольно правильно, но связь между ячейками окажется потеряна. Для таблиц, формул и многостолбцовых страниц нужны отдельные структурные критерии.
Результаты относятся к выбранному распределению документов и вопросов. Качественная работа на печатных договорах не доказывает такое же качество на рукописных формах, архивных копиях или сложных таблицах.
Наконец, граница между этапами не всегда абсолютна. Например, разметка блоков может выполняться внутри OCR-системы. В отчёте следует явно указать, к какому слою отнесена обработка макета, и сохранять это определение между запусками.
Итоговый рабочий цикл
- Разметьте вопросы, ответы и области-свидетельства на исходных страницах.
- Сравните OCR с эталонной расшифровкой, уделяя отдельное внимание критическим полям.
- Проверьте сохранность свидетельств после разбиения эталонного текста.
- Оцените поиск на индексе из эталонных фрагментов.
- Проверьте генерацию на вручную выбранном контексте.
- Выполните контрольные прогоны с эталонным и OCR-текстом.
- Назначьте каждой ошибке самый ранний этап, на котором была повреждена необходимая информация.
Такой подход превращает общий показатель качества ответа в диагностическую систему. Команда видит не просто падение точности, а конкретный слой, тип документа и класс данных, которые требуют исправления.
Другие практические материалы доступны в разделе руководств, а определения используемых терминов — в глоссарии.