
В 2023 году мы рассказывали, как в Поиске по архивам появилось распознавание рукописных документов и почему сама по себе расшифровка архивного текста — нетривиальная задача. Старые почерки, сложная вёрстка, нестандартные формулировки и огромное разнообразие источников делают архивы трудным доменом даже для сильных OCR‑моделей.
По мере развития сервиса стало ясно, что одной только расшифровки недостаточно: чтобы действительно помогать пользователям находить своих родственников, нужно не просто видеть слова на скане, а понимать, кто именно упомянут в записи, в какой роли и как связан с другими людьми.
Теперь в Поиске по архивам работает новая модель распознавания документов. Она не только распознаёт текст архивного файла, но и структурирует информацию из него. Например, понимает роли и связи между разными людьми: «родившийся», «отец» и «мать» для рождения или «жених», «невеста», «свидетель» для брака.
Меня зовут Даша Виноградова, я руковожу универсальными применениями компьютерного зрения в Яндексе. Вместе с Аней Сидоровой, главным разработчиком распознавания архивов, мы расскажем, как мы сделали шаг от распознавания текста к извлечению структуры и смысла из архивных документов: как мы перестраивали OCR‑пайплайн, почему нам не подошли универсальные VLM‑модели и как пытались разобраться, кто есть кто: отец, мать, жених или свидетель.
С какими данными мы работаем
Основные типы документов, которые мы используем, — это метрические книги, ревизские сказки и исповедные ведомости. О них мы уже рассказывали в нашем предыдущем материале, но я напомню, что это за источники:
Метрические книги — документы для актовых записей о рождении, браке или смерти в период с начала XVIII века по 1918 год.
Ревизские сказки — результаты проведения подушных переписей населения Российской империи начала XVIII — второй половины XIX века.
Исповедные ведомости — ежегодный отчёт по каждому приходу православной церкви в Российской империи в XVIII — начале XX века.
На момент публикации статьи мы работаем с архивами из 24 регионов, а всего оцифровали и распознали уже примерно 23 млн сканов.
Архивные тексты сложны для человеческого восприятия — не то что для автоматической разметки. Особенности записи, старая грамматика, разные почерки — всё это добавляет сложностей. Поэтому нам помогают эксперты по работе с архивными документами: они расшифровывают тексты и размечают сканы для создания обучающей выборки.
В прошлый раз мы писали о первой версии разметки: мы смотрели только на строки рукописного текста. Сейчас же мы сосредоточились на том, чтобы сделать шаг в анализе и понимании структуры рукописного текста, чтобы вычленять его суть. Эта задача сложна тем, что должна находить информацию, расшифровывать её и выделять контекст. Для этого нужно было пересмотреть весь пайплайн: от формата разметки до архитектуры модели.
Распознавание структур
Что есть на старте
До сих пор мы предлагали пользователям работать только с распознанным текстом скана, состоящим из отдельных отрывков. Искать ФИО интересующих людей приходилось среди всего, что упомянуто на скане, — порядковых номеров, пометок, имён священников и свидетелей события. Другими словами, результат поиска был довольно шумным.
Причём проблема не только в лишней информации: даже внутри смысловых блоков оригинального текста информация не расположена в нужном для поиска виде. Например, вот достаточно типичная запись о рождении из метрической книги:

Имя родившейся (Параскева) внесено в отдельную колонку, а её отчество и фамилия (Александрова, Воронина) в принципе не существуют в распознанном тексте как отдельные слова. Конечно, прочитав имя отца, легко понять, что полное имя родившейся — Параскева Александрова Воронина. Однако с точки зрения поиска слова «Александр», «Егоров», «Воронин», «Дмитров» — абсолютно равнозначные. Так что в указанном отрывке можно с одинаковым успехом искать Параскеву Александрову, Параскеву Егорову, Параскеву Воронину и Параскеву Дмитрову.
Аналогичная ситуация происходит не только с родившимися детьми: например, полное ФИО матери — Воронина Акилина Тимофеева — тоже не упомянуто в тексте впрямую.
Ещё один специфичный, но нередкий случай: прадед пользователя был тёзкой священника, ведущего записи в документах всего прихода. А это сильно усложняет поиск, ведь в таком случае на нашем портале могла найтись сотня, тысяча, а то и десяток тысяч страниц, где встречалось искомое ФИО, но не участника события, а в качестве служебной записи о церковнослужителе.

Конечно, можно попробовать некими эвристиками вытащить из распознанного текста имена и фамилии участников событий, но есть нюанс. В условиях разнообразия метрических книг, при наличии половинчатых и расположенных под углом сканов, иногда находящих друг на друга записей обойти погрешности распознавания невозможно. Поэтому метод эвристик не способен обеспечить здесь хорошее качество.
В то же время на дворе 2026 год. Современное решение очевидно: нам нужно задействовать мощь моделей, которые смогут не просто распознать текст, но и осознанно извлечь из него информацию.
Решение
По сути, перед нами стоит KIE‑задача (Key Information Extraction): нужно по изображению документа и заданной инструкции извлечь из него ключевую информацию, а такой формат хорошо подходит для большой мультимодальной модели. Но в нашем случае есть важное ограничение: распознавание русского архивного рукописного текста — слишком специфичный домен, чтобы внешние универсальные модели могли показать хорошее качество.
Впрочем, для нас это не стало проблемой. В нашем распоряжении есть собственная VLM‑модель Alice AI, которая хорошо понимает русский язык и умеет работать с изображениями. Благодаря этим базовым навыкам модель можно довольно быстро доучить под нашу задачу, не собирая для этого гигантскую обучающую выборку.
Гораздо серьёзнее оказался другой вопрос — размер контекста. Типичный архивный скан — это изображение со стороной больше 2500 пикселей, на котором размещено сразу несколько записей, а в них суммарно может упоминаться до 30–35 человек. Это довольно большое количество информации и на входе, и на выходе.

Отсюда вытекает сразу несколько рисков:
С большой вероятностью модель не сможет справиться с таким разрешением входной картинки.
Даже если она справится, чтобы проучить весь этот огромный контекст, потребуется слишком большое количество обучающих данных.
Обработка каждого скана будет долгой и достаточно дорогой с точки зрения вычислений, ведь генерация каждого следующего токена будет происходить с учётом всех прошлых токенов. Последнее, конечно, полезно, когда выдаётся информация о людях, относящихся к одному событию, но бессмысленно при обработке несвязанных между собой записей со страницы.
Кроме того, на каждом скане хватает участков, которые нерелевантны для нашей задачи: поля по краям, шапка книги, колонки с порядковыми номерами и именами священников.
Поэтому мы решили съесть нашего архивного слона по частям: сначала находить на странице отдельные записи, а уже потом извлекать информацию о людях из каждой выделенной области. Заодно такой подход дал нам хороший граундинг — то есть точные координаты самих записей.

Наш итоговый пайплайн работы со сканом выглядит так:
Модель распознавания сначала расшифровывает текст на скане. Это важный отдельный этап: архивный рукописный текст — очень сложный домен, и мультимодальная модель заметно лучше справляется с задачей, если получает на вход результат работы специализированного OCR.
Дальше детектор записей находит на странице шапку таблицы с названиями колонок и сами записи трёх типов: о рождении, бракосочетании и смерти.
После этого для каждой найденной записи мы вырезаем соответствующий фрагмент исходного скана и добавляем к нему текст, который OCR‑модель получила на первом этапе. Этот текст служит для VLM внешней подсказкой и помогает точнее понять содержимое записи.
Затем каждая запись подаётся в дообученную под нашу задачу VLM вместе с промптом, который задаёт нужный формат извлечения информации.
Таким образом, мы извлекаем лучшее из всех наших моделей: получаем распознанный текст от специализированной OCR‑модели, а затем понимаем и структурируем полученную информацию при помощи VLM‑модели.
Желаемый формат
Следуя логике нашего пайплайна, в качестве целевой сущности мы сначала выбрали запись о событии. У каждого из трёх типов событий есть свой стандартный набор ролей: в записи о рождении это ребёнок, его родители и пара восприемников, то есть крёстных; в записи о бракосочетании — жених, невеста и поручители; в записи о смерти роль, как правило, одна — умерший.
Для каждой такой роли в тексте обычно можно выделить набор типичных сведений и разложить их по стандартным полям.

Для уже знакомой нам записи минимальный набор полей будет такой:
{ "имя ребенка": "Параскева", "имя отца": "Александр", "отчество отца": "Егоров", "фамилия отца": "Воронин", "гео отца": "город Дмитров", "инфо об отце": "мещанин из цыган", "имя матери": "Акилина", "отчество матери": "Тимофеева", ... }
Таким образом, для одной записи о рождении у нас получалось около 20 полей, которые модель должна была заполнить. На стандартных примерах первая версия модели справлялась с этим довольно успешно. Вот только жизнь пишет сценарии покруче любого кино, и довольно скоро нашей проблемой стали случаи, которые наша схема описывала с трудом:
у незаконнорожденного ребёнка в графе родителей были указаны мать и муж матери;
у крёстной матери очень часто дополнительно упоминался её муж или отец — а это довольно важная информация о семейных связях;
запись была сделана не в честь рождения, а в честь перехода человека из католической в православную веру;
у усыновлённых детей указывались только приёмные родители;
родилась двойня.
А теперь представьте комбо: родилась двойня, в которой каждого из детей усыновили разные семьи, и у них, конечно, были разные крёстные, причём крёстной одного из мальчиков стала его приёмная мать.
К слову, данные о бракосочетаниях и смертях добавили непредсказуемости:
Практика показала, что у числа поручителей (свидетелей) на свадьбе нет верхней границы. Текущий обнаруженный нами рекорд — 13 поручителей!
И у жениха, и у невесты этот брак мог быть не первым. Причём если упоминания первой жены жениха нам пока не попадались, то бывший муж невесты в таких записях встречается довольно часто.
В одной записи о смерти могут быть упомянуты муж или отец, а также встречаются упоминания сразу нескольких умерших: например, «крестьянина Ивана Саврасова дети: сын Михаил, дочь Аксинья».
Помучившись с описанием разметки для подобных случаев, мы пришли к выводу, что нужно пересмотреть подход. Так смысловой единицей для нашей модели стала не запись с множеством фиксированных полей, а человек, для которого можно заполнить:
ФИО,
пол,
возраст,
роль в событии (статус),
географическое положение,
дополнительную информацию (профессию, социальный статус и тому подобное).
Соответственно, одна запись — это список из некоторого количества людей. И на уже знакомом нам примере можно посмотреть финальный вид разметки.

[ { "status" = "родившийся"; "name" = "Параскева"; "second_name" = "Александрова"; "surname" = "Воронина"; "sex" = "ж"; }; { "status" = "отец"; "name" = "Александр"; "second_name" = "Егоров"; "surname" = "Воронин"; "geo" = "город Дмитров"; "info" = "мещанин из цыган"; "sex" = "м"; }; ... ]
Это сильно упростило задачу. Вместо трёх похожих сценариев, у каждого из которых был свой набор полей, мы получили один универсальный: нужно выписать всех людей из записи и для каждого человека заполнить 8 полей.
Такое изменение помогло и на этапе разметки. Задача у разметчиков и без того была непростой: нужно было прочитать рукописный текст и аккуратно распределить информацию о каждом человеке по нужным полям в таблице. С новой схемой делать это стало заметно проще и понятнее.

Но и распределение по полям способно доставить хлопоты. Попробуйте разобраться в этой записи о крещении:
Восприемниками были: 5-й бригады корпуса морской Артилеріи Канонир Елисей Аникъевъ, и Арестантской роты прапорщика Ивана Дуванова денщика Саввы Антонова сына Карчавина, жена Марія Исаева дочь
Правильный ответ
{ "status" = "крестный отец"; "name" = "Елисей"; "second_name" = "Аникеев"; "sex" = "м"; "info" = "5-й бригады корпуса морской артиллерии канонир"; }; { "status" = "муж крестной матери"; "name" = "Савва"; "second_name" = "Антонов"; "surname" = "Карчавин" "sex" = "м"; "info" = "денщик прапорщика арестантской роты Ивана Дуванова"; }; { "status" = "крестная мать"; "name" = "Мария"; "second_name" = "Исаева"; "surname" = "Карчавина" "sex" = "ж"; }
В итоге для обучения текущей модели мы собрали разметку для 3000 записей. Ещё 3000 записей были размечены для тестовой выборки.
Результат и метрики
В качестве внутренних метрик мы считаем множество параметров: насколько хорошо заполняется каждый тип полей, насколько точно находятся люди в разных ролях, сколько получается точных и неточных совпадений и так далее. Но в качестве главной итоговой метрики нам хотелось выбрать такую, которая напрямую связана с основной задачей сервиса — помогать пользователям находить людей в архивных записях.
В итоге мы сформулировали её так: это доля людей, которых можно найти по ФИО в нашем сервисе. Для этого мы сравниваем записи о людях из ground‑truth‑разметки со сведениями, которые предсказала модель. При этом учитываем не только точные, но и неточные совпадения — по тем же правилам, которые уже используются в поиске Поиска по архивам.
Получившиеся числа радуют:
для рождений — 92,7%
для свадеб — 89,7%
для смертей — 87,2%
Итого по всем категориям — 90,5%
Имена собственные — традиционно один из самых сложных типов данных в распознавании архивных текстов, поэтому такой результат нас, конечно, очень радует. При этом мы видим, что «потерянные» люди — это обычно более редкие участники события, например мужья крёстных матерей или поручители в записях, где их особенно много. Значит, именно на таких случаях нам и нужно продолжать работу.
Бонус: статистика по именам
Новый подход открывает пространство для различных исследований. Например, во время работы над новой моделью мы собрали интересную статистику: какие имена у детей были популярны в Москве 200 лет назад. Для сравнения — статистика по именам за прошлый год:
1825 |
2025 |
|
Мужские |
Иван, Василий, Петр |
Михаил, Александр, Лев |
Женские |
Мария, Анна, Евдокия |
Анна, София/Софья, Мария |
Переход к блоковой модели
Зачем нужны блоки
Современные большие мультимодальные модели довольно мощные: они умеют понимать сложные инструкции и решать задачи, где нужно сразу несколько разных навыков. Но в узких и специфичных срезах они нередко заметно уступают моделям, которые обучены под конкретную задачу. Поэтому на практике полезно передавать в VLM не только исходные данные, но и дополнительную информацию от внешней специализированной модели.
Пока то же самое верно и для OCR: специализированные модели распознавания текста работают заметно быстрее больших VLM и в своих доменных задачах часто показывают более высокое качество. Поэтому мы используем распознанный текст как дополнительный источник информации. Собственно, по этой причине нам важно улучшать и сам этап расшифровки.
В задаче OCR сейчас существует два основных подхода.
Модель разбивает документ на отдельные фрагменты по порядку чтения, например абзацы, таблицы и другие блоки, а затем распознаёт каждый из них отдельно. Так задачу распознавания всего документа удаётся разложить на обработку более компактных частей и тем самым уменьшить проблемы, связанные со слишком длинным контекстом. По такому принципу, например, устроены PaddleOCR и MinerU: в них детекция и распознавание — это отдельные этапы, за которые отвечают разные модели.
End‑to‑end‑распознавание, которое сразу превращает изображение документа в текст без промежуточных этапов. Так, например, работают DeepSeekOCR и Qianfan‑OCR.
Оба подхода объединяет то, что современные OCR‑модели стараются использовать как можно больше контекста. Вместо отдельных символов или строк они анализируют целые фрагменты документа, учитывая расположение текста на странице, её структуру и визуальные зависимости между элементами.
На протяжении нескольких лет наша модель распознавания обучалась на рукописных и печатных текстах, обрабатывала сканы из новых архивов и шаг за шагом становилась лучше. Пайплайн при этом оставался неизменным: сначала детектор находил отдельные строки, затем модель распознавала текст построчно, отдельная модель собирала строки в блоки, а между этими этапами работало довольно много дополнительного процессинга.
Со временем мы упёрлись в новый класс проблем. Ошибки в итоговом тексте всё чаще возникали не потому, что модель плохо распознаёт строки, а из‑за ограничений архитектуры нашего подхода.
Например, одна из частых и довольно нелепых для человеческого глаза ошибок — лишние пробелы внутри слов. Это связано с особенностью метрических книг: в них, как правило, нет знаков переноса.

Распознанный текст блока:
Дъревни Калмиковой у крестья_нина Ивана Михайлова Лаза_рева и жены его Неонилы Стефа_новой Сынъ тимофъй
Если смотреть на этот результат с позиции, что модель распознавания обрабатывала отдельные строки, — распознавание идеальное. Но итоговый текст всё равно выглядит плохо: несмотря на отсутствие переноса на скане, легко понять, что нужно склеить слова «крестьянина», «Лазарева» и «Стефановой». Однако подобная склейка происходит на этапе постпроцессинга и сборки финального текста при одном условии — наличии знака переноса.
Другая широкая область ошибок — распознавание сложных случаев. Специалист, разбирая сложные записи, обычно смотрит не только на проблемное слово, но и на соседний текст, чтобы понять особенности начертания букв по уже известным словам или встретить то же самое название или фамилию, написанные более разборчиво. В итоге он может догадаться, какое слово написано.
Для модели работают те же правила: мы видели, как переход от распознавания отдельных символов к распознаванию целой строки сильно улучшил качество модели. Следующим логичным этапом такого масштабирования нам казался переход от строк к блокам:
Например, при прочтении отдельной строки…

…сложно распознать последнее слово без ошибки. Но если увидеть целый отрывок, то становится значительно проще:

Похожий пример: слово «Владикавказскаго» в первой строке отрывка наша строковая модель распознала верно, а вот в последней строке после склеивания получилось только «Влабикавказского».

Кроме того, мы работаем и с довольно специфичными документами XVII века — они написаны скорописью. Этому виду письма свойственно большое количество сокращений, выносные буквы, лигатуры (соединение двух или более букв или их элементов в одну графическую форму), использование титла (надстрочного знака для сокращений). Чтобы правильно распознать такой текст, модели нужно видеть не одну строку, а сразу весь нужный контекст: и основную строку, и ту, где находятся выносные буквы. Для таких документов значение контекста особенно велико.

Всё это подтолкнуло нас к переходу от строкового пайплайна к блоковому. Теперь детектор выделяет не отдельные строки, а целые текстовые блоки, а модель распознавания умеет работать именно с ними.
Переход и результаты
Дополнительным плюсом пайплайна стало то, что мы сокращали его на целый шаг. Мы пропускали группировку строк и их частей в отдельные блоки, уменьшая количество используемых моделей и сокращая процессинг в обработке скана.
Существующий детектор блоков пришлось переучивать, причём гораздо строже, чем раньше. Если в старом пайплайне его ошибка обычно приводила к тому, что какие‑то строки не склеивались в итоговый блок, то теперь цена ошибки стала выше — можно полностью потерять текст нужного фрагмента.
Архитектура модели распознавания при этом осталась трансформерной, но сам блок оказался гораздо менее предсказуемой единицей, чем строка. Он может состоять из одного короткого слова и из десятка длинных строк, поэтому размер входа по пикселям меняется очень сильно — от маленького квадрата 20 × 20 до большого блока 2000 × 2000. Из‑за этого нам пришлось отдельно решать две важные подзадачи: подобрать энкодер, который хорошо работает с таким разбросом размеров, и оптимизировать словарь токенов вместе с его наполнением. Это важно потому, что генерировать текст для целого блока символ за символом довольно затратно с точки зрения вычислений.
После перехода на блочную архитектуру мы замерили результаты, и они нас порадовали. Для оценки у нас есть две рукописные тестовые корзины: первая собрана в тех же пропорциях типов документов, в каких они представлены в нашей базе, а вторая специально смещена в сторону более сложных случаев. Качество на них мы считаем следующим образом: metric = hits / (hits + deletions + substitutions), где hits — это слова, которые совпали у модели и в референсе, deletions — слова, которые модель пропустила, а substitutions — лишние или неправильно распознанные слова.
На обеих тестовых корзинах переход на новый пайплайн дал рост качества: на потоковой выборке мы получили recall 93,2%, а на сложной — 88,1%.
Распознавание структуры документа и выделение людей вместе с их родственными связями и взаимоотношениями — это большой шаг вперёд в нашей работе с архивными данными. Такой подход открывает много новых возможностей: можно строить полноценную базу данных людей, автоматически собирать генеалогические деревья и получать более надёжную историческую статистику — например, по числу рождений, браков и смертей в разные годы, по популярности имён или по средней продолжительности жизни.
Сейчас мы уже переобошли наши базы с использованием обоих новых подходов и надеемся, что поиск по архивам стал точнее и удобнее. Очень надеемся, что это поможет вам узнать что‑то новое о своих родственниках. Будем рады вашим отзывам и репортам.
Но самое главное, что поиск по архивам теперь не просто поиск словосочетания в тексте. Теперь это поиск человека среди людей.
Комментарии (4)

dei
28.05.2026 05:23open source модели?
По сравнению с архивом Финляндии - у вас хуже/лучше получается?
https://huggingface.co/Kansallisarkisto/cyrillic-htr-model

andy_p
28.05.2026 05:23Да, пользуюсь вашим архивом при составлении генеалогического древа. Нашел много документов о своих предках.
YuMo
Спасибо за работу, пожалуй добавлю ссылку на вас. https://ya.ru/archive