Семинар 7 · Вайб-кодинг · практическое задание

Извлечение дат рождения и смерти из «Петербургского некрополя»

Алгоритм извлечения данных из справочника 1912 года, серия промтов для этапа «Даты», по которой код написала модель Claude Sonnet, проверка кода на всём томе и план доработки.

Источник: «Петербургский некрополь», т. 1 (А–Г) Статей: 13 620 Код этапа: Claude Sonnet, 5 промтов Python, только стандартная библиотека

00Источник

Саитов В. И. Петербургский некрополь. Т. 1 (А–Г). СПб.: Тип. М. М. Стасюлевича, 1912. Словарь лиц, погребённых на петербургских кладбищах в XVIII – начале XX в., составлен по поручению вел. кн. Николая Михайловича. Использован OCR-текст с Internet Archive: archive.org/details/peterburgskijnekropolt1ag60 — 61 623 строки, около 3 МБ.

Типичная статья:

Абаза, Аггей Васильевичъ, р. 16 декабря 1782 { 21 марта 1852 (Александро-Невская лавра…)

Почему этот источник. Он близок к актовым книгам, которые разбирались на семинаре. Это массовый серийный источник с устойчивой формулой записи: фамилия, имя, чин, р. дата † дата, возраст, родственники, (кладбище). Формула позволяет описать правила деления. При этом текст «грязный»: дореформенная орфография, OCR-ошибки, разорванные страницы. На таком тексте видно, зачем нужны механизмы контроля. Из дат рождения и смерти получаются возраст смерти, сезонность и динамика по десятилетиям, то есть основа для исследовательского вопроса.

01Алгоритм по схеме семинара

Схема со слайда «Автоматизированная разметка текста» применена к источнику:

  1. Выделить структурные элементы.Статья — основная единица. Внутри статьи: фамилия; имя и отчество; титул или чин; дата рождения («р.»); дата смерти («†»); указанный возраст («на 45 г.»); родственники («Жена:», «Съ …»); место погребения в скобках. Служебные элементы: номера страниц, колонтитулы, стихотворные эпитафии, отсылки «— см.».
  2. Сформулировать принципы деления.Статья начинается с новой строки словами «Фамилия,», фамилии идут по алфавиту. Дата после «р.» — рождение, после «†» — смерть. Первая пара дат до родственников и до скобки с кладбищем относится к самому человеку. Скобка в конце статьи — место погребения.
  3. Перевести принципы на понятный компьютеру язык.Регулярные выражения. Варианты написания месяцев и знака † собраны частотным подсчётом OCR-искажений по корпусу.
  4. Разбить задачу на этапы.Этапы 0–6 в таблице ниже.
  5. Описать каждый этап отдельно.Для каждого этапа указаны правило, вход и выход (таблица).
  6. Спроектировать механизмы контроля.У каждого этапа своя проверка (последний столбец). Для выбранного этапа контроль реализован полностью: самопроверка на примерах из промтов, пометки подозрительных статей, файл на ручную проверку, сверка с эталоном.
ЭтапЧто делаетПравилоКонтроль
0. ПодготовкаСкачивает источник, отсекает предисловие, удаляет мусорные строки, склеивает переносыНачало словаря — «Абаза, Аггей»; мусор — строки из цифр и обрывковПроверка содержимого скачанного файла; число и примеры удалённых строк
1. СтатьиДелит текст на статьи«Фамилия,» с начала абзаца на букву своего раздела; режет склеенные статьи после «).»Число статей по буквам; нарушения алфавитного порядка
2. ИмяФамилия, имя, отчество, полТекст до второй запятой; пол — по окончанию отчества или фамилииДоля статей без отчества, словарь имён
3. ДатыДаты рождения и смертиЗнаки «р.» / «†» с учётом OCR-вариантов; только «своя» часть статьиСамопроверка на примерах из промтов, пометки, эталон
4. СтатусЧин, титул, сословие, профессияСловарь: «колл. сов.», «купецъ», «графиня», «младенецъ»…Частотный список нераспознанных слов
5. КладбищеМесто погребения и его нормализацияПоследняя скобка; словарь вариантов («Волково лютер.» = «Волково лютеранское»)Все варианты сводятся к списку кладбищ из предисловия (около 40)
6. ТаблицаСборка базы: человек — даты — статус — кладбищеСвязь по id статьиВыборочная ручная сверка 50 строк

02Серия промтов для этапа 3 «Даты»

Код этапа написала модель Claude Sonnet: пять промтов подавались по очереди в одном диалоге. Промты написаны обычным языком, как задание коллеге-программисту:

  • задача объяснена словами и примерами из источника, без готовых регулярных выражений и технических указаний;
  • искажения OCR модель находит сама: промт 2 просит сначала посчитать по данным, как на самом деле записаны крест и месяцы, и только потом менять программу;
  • механизм контроля в каждом промте: примеры «текст статьи → правильный ответ». Модель превращает их в самопроверку, которая запускается перед каждой обработкой и останавливает программу, если пример не проходит (18 примеров). Дальше идут пометки подозрительных статей и сверка с ручной разметкой.
Промт 1 · задача и первая версия

Задача: что за источник, как устроена статья, что значат «р.» и крест, куда сохранить результат и в каком формате. «Начни с простой версии».

Контроль: 3 примера в самопроверке (полная дата, только смерть, отсылка без дат); доля статей с датой смерти.

Полный текст промта
Я историк, работаю со справочником «Петербургский некрополь» (1912). Это словарь людей,
похороненных на кладбищах Петербурга. Текст получен распознаванием скана (OCR),
поэтому в нём много опечаток, а орфография дореформенная (ъ, ѣ, і).

Статьи уже разделены: файл output/entries.csv, столбцы id, surname, is_reference, text.
Типичная статья:
  Абаза, Аггей Васильевичъ, р. 16 декабря 1782 { 21 марта 1852 (Александро-Невская лавра).
Здесь «р.» — дата рождения, а дата после креста † — дата смерти (в OCR крест часто
превратился в «{», «+», «|», «1» или «Т»). В скобках в конце — кладбище.

Нужна программа на Python (без сторонних библиотек), которая для каждой статьи находит
дату рождения и дату смерти самого человека и сохраняет их в output/dates_sonnet.csv
(столбцы id, birth, death). Формат даты: 1852-03-21; если известен только год — 1852;
если год и месяц — 1852-03; если даты нет — пусто.

Назови файл code/dates_sonnet.py. Начни с простой версии.

Сделай так, чтобы программа при каждом запуске сначала проверяла себя на примерах
и останавливалась, если какой-то пример не прошёл:
- «Баженовъ, Иванъ Прокофьевичъ, р. 8 ноября 1791 + 8 января 1872 (Смоленское кладбище).»
  → рождение 1791-11-08, смерть 1872-01-08
- «Боголюбовъ, Павелъ Николаевичъ + 28 декабря 1904 (Новодѣвичій монастырь).»
  → рождения нет, смерть 1904-12-28
- «Аксенова, Елизавета Ивановна—см. Анцъ.» → это отсылка, дат нет

Запусти программу и скажи, у какой доли статей нашлась дата смерти.
Промт 2 · искажения OCR

Задача: сначала посчитать по данным, как OCR записал крест и названия месяцев, показать частые варианты, потом научить программу их понимать. Подсказки словами: «1336» = 1836, крест бывает цифрой 1, месяц с заглавной, бывают только годы.

Контроль: +7 примеров («поня», «апрфля», «1 5 марта», «р. 1852 Т 1892», «1336», «Февраля»).

Полный текст промта
Дата смерти находится далеко не у всех статей: OCR исказил текст. Прежде чем исправлять
программу, посмотри на сами данные. Посчитай, какими символами на самом деле записан
крест перед датой смерти и как выглядят названия месяцев (например, «іюля» часто
превратилось в «юля», «поля» или «1юля»). Покажи мне самые частые варианты.

Потом научи программу понимать эти варианты. Несколько вещей, которые я заметила сама:
- в годах OCR путает цифры: «1336» на самом деле 1836, а «т852» — 1852;
- крест иногда выглядит как цифра 1: «р. 15 ноября 1852 1 5 марта 1856» значит,
  что человек умер 5 марта 1856;
- месяц может быть с заглавной буквы: «2 Февраля 1894»;
- иногда указаны только годы: «р. 1852 Т 1892».

Добавь эти примеры к проверкам:
- «Годенъ, Викторія + 28 поня 1905 (Католическое кладбище).» → смерть 1905-06-28
- «Брезинская, Юстина + 16 апрфля 1889 (Смоленское кладбище).» → смерть 1889-04-16
- «Гребне, Софія-Доротея, р. 12 марта 1816 1 26 января 1877 (Волково кладбище).»
  → рождение 1816-03-12, смерть 1877-01-26
- «Гюне, баронъ Эдуардъ, р. 15 ноября 1852 1 5 марта 1856 (Волково кладбище).»
  → рождение 1852-11-15, смерть 1856-03-05
- «Гельмгольцъ, Ѳедоръ, капитанъ, р. 1852 Т 1892 (Гатчинское кладбище).» → 1852 и 1892
- «Гамельманъ, Амалія, р. 9 февраля 1800 + 23 января 1336 (Волково кладбище).» → смерть 1836-01-23
- «Волкова, Марія Филипповна + 2 Февраля 1894 (Новодѣвичій монастырь).» → смерть 1894-02-02

Запусти и покажи, насколько выросла доля статей с датой смерти.
Промт 3 · чьи это даты

Задача: брать только даты самого человека, до родственников и скобки с кладбищем; не всякая скобка — кладбище; слова между «р.» и датой; «рожд.» = урождённая; дату без креста помечать.

Контроль: +5 примеров (дата жены не берётся, «(Бивоча)», «рожд. Вердье», «р. въ Митавѣ»).

Полный текст промта
Теперь важно не перепутать людей. После основного человека в статье часто упомянуты
родственники со своими датами: «Жена: Вѣра Петровна + 23 октября 1909» или
«Съ М. И. Аксеновой». Нам нужны только даты самого человека — те, что идут до упоминания
родственников и до скобки с названием кладбища. Но не всякая скобка — кладбище:
в «рожд. Дюлу (Бивоча)» в скобках просто другое написание фамилии.

Ещё три тонкости:
- между «р.» или крестом и датой бывают слова: «р. въ Ригѣ 9 апрѣля 1829», «+ убитъ 12 октября 1877»;
- «рожд. Вердье» значит «урождённая Вердье» — это девичья фамилия, а не дата рождения;
- если в статье пропущен крест, а дата стоит после даты рождения, это скорее всего тоже дата смерти,
  но такие случаи стоит пометить, чтобы потом проверить.

Примеры для проверок:
- «Акорчевъ, Александръ, колл. сов. { 26 марта 1885, на 61 г. Жена: Вѣра Петровна 1 23 октября 1909 (Смоленское кладбище).» → смерть 1885-03-26
- «Банье, Андрей-Фридрихъ, р. въ Мекленбургъ-Шверинѣ 29 сентября 1769 + 27 апрѣля 1840, 70 л. (Волково кладбище).» → рождение 1769-09-29, смерть 1840-04-27
- «Брюнъ-де-Сентъ-Ипполитъ, Елизавета, рожд. Дюлу (Бивоча) 1 5 февраля 1843, 34 л. (Смоленское кладбище).» → смерть 1843-02-05
- «Годенъ, Викторія, рожд. Вердье + 28 поня 1905, 71 г. (Католическое кладбище).» → рождения нет
- «Вальдманъ, Елена, рожд. Шинна, р. въ Митавѣ 30 сентября 1799 т 1 мая 1875 (Гатчинское кладбище).» → рождение 1799-09-30, смерть 1875-05-01

Запусти и покажи результат.
Промт 4 · контроль

Задача: помечать подозрительные статьи (год вне 1700–1912, рождение позже смерти, больше 110 лет, возраст в тексте не совпадает, год есть — смерти нет, смерть без креста), сохранить их в файл на проверку и печатать сводку.

Контроль: +1 пример (пометки у статьи с годом «1992»); сводка на экране.

Полный текст промта
Мне нужно понимать, где программе можно доверять, а где нужно проверить глазами.
Пусть она помечает подозрительные статьи:
- год рождения или смерти вне 1700–1912 (книга издана в 1912 году);
- рождение позже смерти;
- человек прожил больше 110 лет;
- в тексте указан возраст («на 45 г.», «63 л.»), и он не совпадает с разницей годов больше чем на год;
- в статье есть год, а дату смерти найти не удалось;
- дата смерти взята без креста.

Помеченные статьи вместе с текстом сохрани в output/dates_sonnet_review.csv,
а пометки добавь столбцом flags в dates_sonnet.csv. В конце работы программа должна
печатать короткую сводку: сколько статей обработано, у скольких найдены даты рождения
и смерти, сколько статей с каждой пометкой.

Пример для проверки: у статьи «Аркъ-фанъ, Карлъ, р. 28 августа 1839 + 11 августа 1992 (Волково кладбище).»
должны быть пометки «год вне диапазона» и «больше 110 лет».

Запусти и покажи сводку.
Промт 5 · проверка по ручной разметке

Задача: сравнить с 60 вручную проверенными статьями, объяснить каждое расхождение простыми словами, исправить ошибки кода, а ошибки самого источника не подгонять.

Контроль: точность и полнота до и после исправлений.

Полный текст промта
Я вручную проверила 60 случайных статей (data/gold_dates.csv). Запусти сравнение:
python3 code/evaluate.py output/dates_sonnet.csv
Покажи результат и для каждого расхождения объясни простыми словами, в чём причина.
Если ошибся код — исправь его так, чтобы прежние проверки по-прежнему проходили, и запусти
сравнение ещё раз. Если ошибка в самом тексте источника (OCR) — не подгоняй код, просто скажи об этом.
Файлы с разметкой и evaluate.py не меняй.

03Тестирование на источнике

Код Sonnet запущен на всём томе: 13 620 статей, из них 12 330 без отсылок «см.». Итог на отложенной выборке — 40 случайных статей, размеченных вручную и не показанных модели:

94,8 %статей с найденной датой смерти (без отсылок)
97 / 100 %точность / полнота, дата смерти (отложенная выборка)
94 / 100 %точность / полнота, дата рождения (отложенная выборка)
516статей помечено на ручную проверку (4,2 %)

Как шла работа по промтам

ШагДата смерти найдена (из 13 620)Примеров в самопроверкеЧто произошло
1 · первая версия11 3193 / 3Модель сразу сделала месяцы терпимыми к опечаткам: даже «простая» версия нашла 83 % всех статей
2 · искажения OCR11 46710 / 10Модель сама посчитала варианты креста («+» 1955, «1» 1012, «|» 826, «Т» 490…) и месяцев («поля» 386, «поня» 321, «апрфля» 133…); починила годы 13xx и «т852»
3 · чьи это даты11 57715 / 15Даты родственников отрезаны; склеенная со ссылкой «см.» статья читается дальше; дата без креста помечается
4 · контроль11 57716 / 16Шесть видов пометок, включая сверку с возрастом из текста; 616 статей на проверку
5 · сверка с эталоном11 69418 / 18Эталон: смерть 96 / 93 % → 100 / 100 %. Модель нашла пять ошибок кода: «р. 2 + 3 апрѣля» (нечитаемое рождение), «5 ня», «азгуста», «19 1юля»; на проверку осталось 516 статей

Оценка по ручной разметке

ВыборкаРождение: точн. / полн.Смерть: точн. / полн.Замечание
Эталон, 60 статей, до шага 590 / 96 %96 / 93 %До самопроверки модели
Эталон, 60 статей, после шага 5100 / 100 %100 / 100 %Модель исправляла код по этим статьям, оценка завышена
Отложенная выборка, 40 статей94 / 100 %97 / 100 %Модели не показывалась: честная оценка. До шага 5 было 94 / 100 % и 92 / 92 %: исправления по эталону помогли и на новых статьях

Примеры работы

Текст статьи (OCR)РождениеСмертьЧто сложного
Гребне, Соф!я-Доротея, рожд. //етерсонв, р. 12 марта 1816 1 26 января 18771816-03-121877-01-26«рожд.» без даты; † распознан как «1»
Банье, Андрей-Фридрихъ, р. въ Мекленбургъ-ШверинЪ 29 сентября 1769 + 27 апрЪля 18401769-09-291840-04-27Место между знаком и датой
Алябьевъ, Оедоръ младенецъ, р. 5 ня 1858 + 16 мая 18591858-06-051859-05-16«ня» = іюня
Годенъ, Виктор!я, рожд. Вердье + 28 поня 1905, 71 г.—1905-06-28«рожд.» = урождённая; «поня» = іюня
Бажановъ, Григорий Алексѣевичъ, д. ст. сов. 1820. Вдова: Устина … т 16 мая 1846—1820Знак † потерян; дата вдовы отрезана по слову «Вдова»
Блюмъ, Людвигъ-Фридрихъ, р. 7 октября 1804 | 19 сентября (1 октября) 18641804-10-071864-09-19Двойная дата по старому и новому стилю
Аркъ-фанъ, Карлъ Карловичъ, р. 28 августа 1839 … + 11 августа 19921839-08-281992-08-11на проверку ошибка OCR в источнике (1892)

Пометки контроля

ПометкаСтатейЧто обычно стоит за ней
смерть без креста228Знак † съеден OCR; дата взята по положению (сразу после рождения) — её стоит проверить
есть год, но нет смерти196Сильно испорченный OCR («24 : Бля 1888», «19053»), дата только рождения
возраст не совпадает44Возраст «на 45 г.» расходится с разницей годов: ошибка цифр в OCR или возраст с месяцами
рождение позже смерти32Ошибка цифр в OCR: «р. 3 февраля 1849 1 17 марта 1832»
больше 110 лет23«1992», «1534» — ошибки OCR в годах
год вне диапазона15Год вне 1700–1912: «0099», «1002»

Все статьи с пометками записаны в output/dates_sonnet_review.csv вместе с текстом статьи.

Пример применения: месяц смерти

11 154 статьи с известным месяцем смерти и годом в пределах 1700–1912. Больше всего смертей в марте (1 178), меньше всего в августе (705). Это основа для вопроса о сезонности смертности петербургской элиты XIX в. Медианный возраст смерти — 56 лет (5 661 статья с обеими датами и без пометок).

04Оценка и доработка

Соответствие задаче. Этап выполняет свою задачу. На отложенной выборке программа нашла все 38 дат смерти и все 17 дат рождения. Единственная ошибка возникла до этапа «Даты»:

Доработка на следующем этапе:

05Файлы и воспроизведение

Нужен Python 3.10 или новее, сторонние библиотеки не нужны. Команды выполняются из папки practice (на Windows — python вместо python3):

python3 code/download.py      # скачать OCR-текст с archive.org в data/
python3 code/segment.py       # этапы 0–1 → output/entries.csv
python3 code/dates_sonnet.py  # этап 3 (код Sonnet): самопроверка + даты + пометки
python3 code/evaluate.py      # сверка с эталоном (60 статей)
python3 code/evaluate.py output/dates_sonnet.csv data/holdout_dates.csv   # отложенная выборка

Или одной командой: python3 necropol_dates.py. Это всё решение в одном файле: скачивание, разбиение, код Sonnet, контроль и проверка по встроенной ручной разметке. Результаты побайтно совпадают с поэтапным запуском.

Источник (data/necropol_t1.txt, 3 МБ) и промежуточный файл статей (output/entries.csv) в архив не входят: их создают download.py и segment.py. Если скачивание не работает, файл можно взять вручную: https://archive.org/download/peterburgskijnekropolt1ag60/peterburgskijnekropolt1ag60_djvu.txt, сохранить как data/necropol_t1.txt. Файлы этапов 0–1 записаны в UTF-8 с BOM и сразу открываются в Excel; файлы этапа 3 — в обычном UTF-8, в Excel их открывают через «Данные → Из текста/CSV».

reports/result.docxЭтот отчёт в формате Word
prompts/sonnet_prompts.mdСерия из 5 промтов (раздел 02)
code/dates_sonnet.pyЭтап 3: код, написанный Claude Sonnet по промтам, без правок
code/download.py, code/segment.pyЭтапы 0–1: скачивание, очистка, разбиение на статьи
code/evaluate.pyСверка с ручной разметкой
necropol_dates.pyВсё решение одним файлом
data/gold_dates.csv, holdout_dates.csvРучная разметка: 60 + 40 статей
output/dates_sonnet.csvРезультат: даты и пометки для 13 620 статей
output/dates_sonnet_review.csv516 статей на ручную проверку

06Декларация использования ИИ

Модели: Claude Sonnet (Anthropic) — написала код этапа 3 по серии промтов; Claude Opus 5.5 (Anthropic), в режиме агента Claude Code — подготовка.

Цели: написание кода; подготовка ручной разметки для проверки; оформление отчёта.

Что вошло в работу: код этапа 3 (Sonnet, без правок); код этапов 0–1 и проверки (Opus); тексты промтов; разметка эталона (100 статей); отчёт.

Собственная доработка после общения с моделью:

  • выбраны источник («Петербургский некрополь», т. 1) и этап для автоматизации — даты рождения и смерти;
  • две модели сравнены на одной задаче: серия промтов прогнана на Claude Haiku и на Claude Sonnet, по итогам на отложенной выборке выбран код Sonnet (дата смерти: точность 97 % против 95 %, охват 94,8 % против 89,8 %);
  • промты переписаны простым языком, без технических инструкций, — так их может прочитать и проверить историк;
  • принято решение сохранить код модели без правок, а замеченные недостатки (например, кодировку CSV для Excel) вынести в план доработки;
  • для честной оценки потребована отдельная выборка из 40 статей, которая не показывалась модели;
  • выбрана форма сдачи: отчёт в HTML и Word, архив только с материалами для проверки.

Ручная разметка эталона (100 статей) выполнена с помощью Claude Opus; независимая сверка её человеком со сканом пока не проводилась и стоит в плане доработки.