Как я автоматизировал юридическую рутину. Claude.. Claude. excel.. Claude. excel. legaltech.. Claude. excel. legaltech. python.. Claude. excel. legaltech. python. word.. Claude. excel. legaltech. python. word. искусственный интеллект.. Claude. excel. legaltech. python. word. искусственный интеллект. право.. Claude. excel. legaltech. python. word. искусственный интеллект. право. Программирование.. Claude. excel. legaltech. python. word. искусственный интеллект. право. Программирование. Управление проектами.. Claude. excel. legaltech. python. word. искусственный интеллект. право. Программирование. Управление проектами. юриспруденция.
Как я автоматизировал юридическую рутину - 1

Хабр, привет! Меня зовут Ману, и я бывший фронтенд-разработчик, который уже семь лет работает корпоративным юристом. Да, звучит странно. Но именно эта комбинация помогает в моей повседневной работе, с которой сталкивается каждый практикующий юрист: бесконечное заполнение одних и тех же форм, где отличаются только наименования юрлиц и их реквизиты.

В этой статье я расскажу, как из Excel-таблиц пришел к Python-скрипту, который генерирует десятки документов за пару секунд. Эта история о том, как технический бэкграунд помогает использовать ИИ не просто для генерации текстов, а создавать инструменты, которые реально экономят часы рутинной работы. Прежде всего для минимизации фактора человеческой ошибки и автоматизации рутины.

Как Minecraft привёл меня в юриспруденцию

Начну с предыстории. Под конец 9-го класса я увлекся Minecraft, но не самой игрой, а возможностью создать свой проект под нее. Это были 2012-2015 годы, когда индустрия игровых серверов переживала настоящий бум. Сидя на форумах и изучая, как собрать лаунчер и сайт для своего проекта, я решил начать с дизайна. Так меня и втянуло в веб-разработку.

Около шести лет я проработал в небольшой студии верстальщиком и фронтендером, хотя второе можно было назвать с большой натяжкой. Потом началась взрослая жизнь, и надо было думать о чем-то более серьезном. Меня пригласили в юридическую фирму стажером просто потому, что я разбирался в компьютерах и знал, что такое Word. Да, тогда с наймом было чуточку проще :)

С 2019 года началась моя полноценная юридическая практика. Я полностью ушел из разработки и переключился на юриспруденцию. Работаю в основном с корпоративным и миграционным правом. И вот тут началось самое интересное.

Я быстро понял, что работа юриста делится на два типа. Первая – это более творческий подход, где включается и работает мозг на полную. Сюда относится все, что связано с аналитикой: составление стратегии защиты в суде, написание юридических заключений с анализом нестандартных ситуаций, разработка сложных договорных конструкций. А вторая – формализированная бюрократическая рутина, когда ты просто заполняешь типовые договора, заявления, ходатайства для государственных органов. И вот вторая часть съедала львиную долю времени. 

Эксель как первая попытка не сойти с ума

Еще в начале карьеры я устал копировать одни и те же данные из документа в документ. Наименование юридического лица тут, его адрес там, ОГРН в третьем месте – и так по кругу. Тогда мне пришла идея сверстать типовые бланки в Excel и объединить их в один файл по разным вкладкам.

Схема была простая:

  • На первой вкладке (мастер-вкладка) я заполнял всю информацию о клиенте;

  • Со второй и по предпоследнюю (верстка) шли готовые формы документов;

  • А последняя вкладка (техническая) работала как мозг всей системы.

Как я автоматизировал юридическую рутину - 2

Техническая вкладка забирала данные с первой, унифицировала их, форматировала и так далее и выдавала конечный результат. Также тут были прописаны условия вроде «если поле пустое, выведи прочерк». 

После заполнения я просто переходил на нужную вкладку и печатал документ напрямую из Excel. Конечно, были сложности с версткой, потому что печать из Excel – не тоже самое, что и из Word. Приходилось возиться с отступами и размерами ячеек, чтобы все красиво укладывалось на стандартный лист А4.

Однако, этот подход можно было использовать до поры до времени. Но проблема в том, что формы живут своей жизнью. Ведомства обновляют приказы, вводят новые бланки, меняют старые. И каждый раз нужно было переверстывать Excel-файл. Учитывая сложность верстки, руки доходили до этого не сразу – проще было вручную забить данные в новый документ. Со временем форм становилось все больше, и система начала трещать по швам. Нужно было придумать что-то кардинально новое.

Как я автоматизировал юридическую рутину - 3

Мечты о красивом решении и почему я их забросил

Возвращаться обратно к Excel не хотелось. Требовалось унифицированное решение, которое можно было бы масштабировать и передать коллегам без особых проблем. Плюс использовать не только в моей практике, но и других областях. В голове сразу возникла идея: берем регламентированные формы в PDF, сохраняем их и накладываем текст поверх через SVG. Реализовать это можно было бы через обычное SPA-приложение на React.

Но когда я начал прикидывать объем работы, энтузиазм быстро остыл. Нужно было:

  1. Упорно и долго писать код на JavaScript;

  2. Разбираться в тонкостях React, и других библиотеках для работы с SVG или canvas;

  3. Сделать превью документа;

  4. Придумать интерфейс для разметки форм;

  5. И много еще чего, включая дебаг и небыстрое начало к самой сути – заполнения документов.

Проблема усложнялась тем, что в некоторых государственных формах текст пишется строго побуквенно по клеткам. Представьте себе: поле для названия организации – это 34 отдельные ячейки, в каждую нужно вписать по одной букве. Программно это означало необходимость распознавать эти клетки и корректно заполнять каждую. Для меня это не было невыполнимой задачей, но я хотел масштабировать решение на всю команду, чтобы другие юристы могли пользоваться без необходимости разбираться в технических деталях.

Эта идея очень долго висела в моей голове мертвым грузом. Я на протяжении нескольких лет раздумывал как подступиться к этой проблеме и с чего начать. Были разные мысли о том, как группировать данные по категориям (например, база юридических лиц, база физических лиц) и при создании нового проекта просто выбирать стороны, параметры и генерировать нужные документы. Но до реализации так и не доходило.

Как ContentControl стал спасением

Время шло, формы менялись и добавлялись новые. И тут в наше бюро пришел клиент – запрос объемный и с большой типовой документацией. Откладывать больше уже было нельзя. 

Все новое – хорошо забытое старое. Думал я, и решил пойти проверенным путем: скормить вордовский документ ИИ и посмотреть, что получится. Результат меня не удивил – он выдал непонятное что вместо верстки, и его нельзя в этом винить. Он не видит то, что видит человек. В его мире алгоритмов – все сделано в соответствии с правилами.

Тогда в наших юридических документах, где требовался аналитический подход, я вовсю использовал Элементы управления контентом (ContentControl). Но его применение было по минимуму: выбрать дату из календарика, вместо ручного прописывания; выбор формы юридического лица (АО или ООО); список с арбитражными судами и их адресами; выбор поверенных лиц и так далее. Простые вещи, упрощающие работу, но не более того.

И тут меня осенило: а что, если использовать ContentControl на полную катушку? Идея была такая:

  • в папке проекта у нас лежит мастер-файл (вордовского формата), внутри него уже приличная и красивая таблица, где слева указаны название полей (наименование, ОГРН, ИНН и т.д.), а справа само поле ContentControl для ввода данных;

  • рядом с мастер-файлом находятся подпапки с формами для разных инстанций;

    • сами формы внутри также размечены полями ContentControl, но их идентификаторы полностью совпадают с идентификаторами из мастер-файла.

project/
│
├── master.docx                    # Мастер-файл с данными
├── script.py                      # Скрипт обработки
│
├── forms_tax/
│   ├── *.docx                     # Формы с ContentControl
│   ├── *_map.json                 # Конфиги (если нужны)
│   └── PDF/                       # ← создается скриптом
│
├── forms_migration/
│   ├── *.docx
│   └── PDF/
│
└── forms_court/
    ├── *.docx
    └── PDF/
Как я автоматизировал юридическую рутину - 4

А дальше в дело вступает скрипт на Python, который и является мозгом всей этой затеи. Запускаем через консоль скрипт, и он проходится по всем подпапкам, смотрит вордовские файлы и вставляет данные из мастер-файла в соответствующие поля с одинаковыми идентификаторами. Если в процессе выполнения не обнаружены ошибки, то в каждой подпапке создается папка PDF, где в них будут располагаться сгенерированные PDF-документы.

Так выглядит на текущий момент мастер-файл и размеченные поля типовой формы. Как видно из скриншота, в форме имеется как обычное написание текста, так и по клеткам.

Так выглядит на текущий момент мастер-файл и размеченные поля типовой формы. Как видно из скриншота, в форме имеется как обычное написание текста, так и по клеткам.

Сначала я хотел положить все формы в один вордовский файл и обновлять поля через VBA, но быстро от этой затеи отказался из-за разных типов полей в формах (об этом – далее).

Реализовать задуманное через Python, как я видел, можно посредством вызова функции Word или LibreOffice. Однако, проблемы возникли и тут. При запуске, Word сильно капризничал и каждый раз хотел права на просмотр папок и редактирование файлов, тогда как LibreOffice – нет. Пытался также сделать вызов Word в «тихом» режиме без запуска GUI, но мой скрипт сломался. И на данный момент я решил полностью остановиться на LibreOffice. Буду благодарен, если знающие специалисты подскажут как в будущих итерациях отказаться от этой зависимости и использовать только open-source библиотеки для Python.

Технические грабли, по которым я прошелся

При первых тестах и пробах уже вырисовывался контур этой архитектуры. Она была понятной и простой, не требовала значительного времени и усердной верстки. Буквально за 4-6 часов я размечал и тестировал 12 шаблонов, тогда как на верстку одного документа у меня уходило по несколько суток.

Итак, технически, формат docx – это обычный zip-архив с XML внутри. Если вы когда-нибудь смотрели на древо веб-страницы в DevTools, то структура вордовского документа покажется знакомой. Те же вложенные теги, те же атрибуты, только namespace другой. Библиотека python-docx предназначенная для создания, чтения, редактирования вордовских файлов и позволяет работать с ними напрямую, не открывая Word вообще.

Скрипт при запуске читает мастер-файл, собирает все ContentControl-поля и их значения в словарь (по аналогии с объектами в JavaScript), где ключ – это идентификатор поля, а значение – то, что вы туда вписали. Дальше он обходит все подпапки, находит размеченные вордовские файлы и в каждом из них заменяет поля с совпадающими идентификаторами на нужный текст. После этого запускается LibreOffice (без GUI, просто прыгающая иконка) и генерируются PDF. Пока не дошел до способа, чтобы приложение вообще не запускалось, а работало только через консольные команды. 

Не сказать, что все обошлось без ошибок и проблем. Word хранит текст внутри XML весьма своеобразно. Простая фраза [company_name] может быть разбита на несколько отдельных XML-элементов, каждый со своим форматированием. В структуре XML-файла вордовского документа текст внутри абзаца (<w:p>) разбивается на так называемые «прогоны» (runs) – элементы <w:r>. 

Это примерно, как если бы текст в теге <p> был разбит на десяток <span> без видимой причины. Поэтому простая замена строки не работала. Чтобы корректно обновить данные, было решено собирать текст всего абзаца воедино, выполнять замену и перезаписывать содержимое, предварительно очищая структуру от лишних «прогонов».

Когда разобрался с обычным текстом, пришел черед нестандартных ситуаций. Например, некоторые государственные формы содержат таблицы, где каждая буква пишется в отдельную клетку. Поле «наименование организации» это 34 клетки подряд, каждая отдельная ячейка в XML. Размечать такое через ContentControl – это сизифов труд. Нужно было искать другое решение.

Для таких форм я ввёл дополнительный файл конфигурации имя_файла_map.json. Простой JSON, где для каждого поля указывается в какие таблицы и ячейки писать символы. Скрипт читает значение из мастер-файла и последовательно вписывает каждый символ в соответствующую ячейку. Чтобы не высчитывать индексы ячеек вручную, была разработана утилита, которая генерирует размеченную версию документа. Суть заключалась в том, что в каждой пустой ячейке красным мелким шрифтом пишется её индекс. Открываешь размеченный документ в Word, смотришь нужные координаты и прописываешь их в map-файл.

Так выглядит размеченный документ после работы скрипта.

Так выглядит размеченный документ после работы скрипта.

В итоге получилось три типа полей, с которым скпт умеет работать:

  1. ContentControl для стандартных форм и простой разметки;

  2. Побуквенное заполнение для нестандартных форм;

  3. Гибридный вариант, сочетающий оба подхода.

Отдельно пришлось повозиться с датами. В государственных формах дата рождения – восемь клеток (две под число, две под месяц, четыре под год). Для некоторых ячеек под год выделено не четыре клетки, а две. Поэтому в map-файле для дат есть отдельная секция, где явно указывается какие ячейки под что. Скрипт берёт дату из мастер-файла в формате ДД.ММ.ГГГГ, разбивает её и раскладывает по нужным индексам.

Была и ещё одна нетривиальная проблема. Это когда одно ContentControl-поле находится внутри другого, такое случается если копировать и вставлять поля в Word. Визуально всё выглядит нормально, но при открытии сгенерированного файла Word выдавал предупреждение о повреждённом содержимом. XML был синтаксически валидным, но семантически нет, потому что вложенные поля – это неподдерживаемая конструкция. Пришлось добавить предварительную обработку, чтобы перед заполнением скрипт разворачивал все вложенные поля, заменяя их на простой текст.

Про форматирование стоит сказать отдельно. При заполнении некоторых форм я придерживаюсь единообразия: заглавные буквы, жирный шрифт и конкретный размер. Если в мастер-файле мы пишем как обычно (первая буква заглавная, а остальные строчными), то он так и переносится в другие формы. Захардкодить это в скрипте было бы неправильно, потому что в разных документах требования разные. Поэтому в map-файле появилась секция _format где для каждого поля можно указать свои параметры (upper переводит текст в верхний регистр, bold делает жирным, size задаёт размер шрифта, align выравнивание). Если форматирование явно не указано в map-файле, то применяются дефолтные значения. Работает одинаково и для побуквенных ячеек и для ContentControl-полей.

{
  "_comment": "Маппинг для Ходатайства РР ВКС",
  "_format": {
    "inn":              { "size": 8 },
    "okato":            { "size": 8 },
    "passissue":        { "upper": true, "bold": true,  "size": 9, "align": "center"  },
    "polis_issued":     { "upper": true, "size": 9 }," }
  },
  "_dates": {
    "ogrn_date": {
      "day":   [[13, 15], [13, 16]],
      "month": [[13, 17], [13, 18]],
      "year":  [[0, 0], [0, 0], [13, 19], [13, 20]]
    }
  },
  "inn":             [[11, 1]],
  "ogrn":            [[13, 1]],
  "okato":           [[11, 12, true]],
  "company_phone":   [[20, 1]]
}
Как я автоматизировал юридическую рутину - 7

Ещё одна проблема, которую я не сразу предвидел: количество ячеек в форме не всегда совпадает с длиной текста. Поле ОКАТО это одиннадцать символов, а ячеек под него десять – скорее всего при разработке формы просто не посчитали. Первое время это решалось вручную и было неудобно. В итоге в формат записи map-файла добавился третий параметр (булев). Если стоит true и символов больше, чем ячеек, скрипт идёт с конца строки и добавляет последний символ в крайнюю ячейку. Выглядит это так: [11, 1, true] – таблица 11, начиная с ячейки 1, с переносом остатка в последнюю клетку. Скрипт берёт строку, нарезает её кусками нужной длины и раскладывает по ячейкам.

Причём это не мое решение проблемы – это требование инспектора уполномоченного органа. Некоторые коллеги пошли другим путём и просто добавили лишнюю ячейку в таблицу, чтобы влезли все символы. Казалось бы логично, но их документы признали недействительными – форма не совпадала с утверждённым регламентом. Бюрократия есть бюрократия.

Реализацию скрипта я полностью отдал искусственному интеллекту (решил проверить на деле Claude Code). В процессе дебага и разработки нужно было лишь корректировать его и направлять в нужно русло. Конечно, задумка не доведена до идеала, но с ним моя команда уже сэкономила уйму времени и снизила вероятность человеческой ошибки при заполнении документов. 

Что получилось и куда дальше

Хочется более унифицированного и масштабируемого решения, а не дублирования шаблонов и мастер-файла в каждый новый проект. В будущих планах отказаться от мастер-файла. Например, перейти на работу с библиотекой python-docx-template, которая умеет подставлять данные прямо в шаблоны на основе переменных. Или, что ещё лучше, сделать простое SPA-приложение, где можно создавать проекты, вбивать реквизиты сторон, выбирать нужные документы из каталога и одним кликом генерировать их. По сути, довести текущую идею до полноценного конструктора документов.

Но когда я начал об этом думать, быстро понял, что автоматизация заполнения форм – это только вершина айсберга. Да, мы научились делать массово нужные нам документы, но что происходит с ними дальше? Они либо уходят в архив на локальном диске, либо распечатываются и подшиваются в папки. А если через месяц нужно найти конкретный договор или проверить, не истёк ли срок действия доверенности? Без нормальной системы учёта начинается тот самый ад с Excel-табличками, который мы все так любим.

Поэтому следующим логичным шагом вижу не просто улучшение генератора, а создание полноценной системы учёта договоров и документооборота. Представьте: цифровой реестр договоров с удобными тегами и категориями, чтобы за пару кликов находить всё, что связано с конкретным клиентом или судебным процессом. Хранилище скан-копий с шифрованием, контроль версий документов (как в Git), автоматические напоминания о дедлайнах, шаблонизация и даже OCR с NLP-анализом текста, адаптированным под российское законодательство.

Пока что мои коллеги ведут документооборот по старинке: нумерация в Excel, а сканы просто лежат в папках с названиями вроде «договоры_2025». Если нужно что-то найти – начинается перебор. Хотелось бы, чтобы отечественная юриспруденция не отставала от технологий и предлагала удобные инструменты как для небольших команд, так и для крупных фирм. Короче, планов много. Начну, наверное, с прототипа на python-docx-template, а дальше видно будет.

Вывод

Оглядываясь назад, понимаю, что каждый мой опыт в итоге сыграл свою роль. Получилось не запланированно, но в какой-то момент знания из совершенно разных сфер начали работать вместе. И сейчас мне уже кажется странным, что так мало людей используют этот синтез.

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

И вот тут я упираюсь в главную проблему. На данный момент для небольших юридических команд и юристов-предпринимателей практически нет готовых инструментов, которые закрывали бы повседневные потребности. Либо это монструозный 1С:Документооборот, рассчитанный на большой отдел, либо самописные Excel-таблицы, которые рано или поздно начинают сыпаться. Золотой середины, к сожалению, почти нет.

Если у вас есть опыт автоматизации в юридической или другой профессиональной сфере, буду рад обсудить в комментариях.

Промт для создания идентичного скрипта
---
Напиши Python скрипт `fill_documents.py` для автоматического заполнения Word-документов данными из мастер-файла с последующей конвертацией в PDF через LibreOffice headless.

**Архитектура:**

Есть папка проекта где лежит `master.docx` — Word-документ с ContentControl-полями (тег каждого поля это его идентификатор, например `company_name`, `inn`, `surname`). Рядом лежат подпапки с шаблонами документов. Скрипт читает данные из мастера и заполняет все найденные шаблоны, после чего конвертирует каждый в PDF и кладёт в подпапку `PDF/`.

**Типы полей которые должны поддерживаться:**

Первый тип — ContentControl (sdt). Скрипт находит их по тегу и заменяет значение. Перед заполнением необходимо разворачивать вложенные sdt — Word иногда создаёт sdt внутри sdt при копировании полей, и это делает итоговый файл невалидным.

Второй тип — текстовые плейсхолдеры вида `[field_name]` прямо в тексте параграфов. Проблема в том что Word внутри XML может разбить строку `[company_name]` на несколько отдельных `<w:r>` элементов с разным форматированием. Поэтому нужно склеивать весь текст параграфа из всех ранов, делать замену и записывать результат в первый `<w:t>`, зануляя остальные.

Третий тип — побуквенные таблицы. В некоторых госформах каждый символ пишется в отдельную ячейку таблицы. Для таких документов рядом с шаблоном кладётся файл `имя_документа_map.json` — если он найден, скрипт переключается в режим побуквенного заполнения.

**Формат map.json:**

Секция `_format` — форматирование для каждого поля: `upper` (bool), `bold` (bool), `size` (int, пункты), `align` (center/left/right). Если поле не указано в `_format` — применяются дефолтные значения `bold: true, size: 10, align: center, upper: false`. Форматирование применяется ко всем типам полей включая ContentControl.

Секция `_computed` — вычисляемые поля по шаблону: `"company_full_name": "{org_form} "{company_name}""`. Скрипт вычисляет их из других полей данных перед заполнением.

Секция `_dates` — для полей где дата разбита по отдельным ячейкам. Формат: отдельно перечисляются ячейки под день, месяц и год. Каждый элемент это конкретная ячейка `[таблица, ячейка]` — один элемент равно один символ. Дата в мастере хранится в формате `ДД.ММ.ГГГГ`.

Секция `_text` — текст целиком в одну конкретную ячейку, без побуквенного разбиения.

Обычные поля — побуквенное заполнение. Формат записи: целое число `49` означает всю таблицу с нулевой ячейки. Массив `[49, 1]` означает таблицу 49 начиная с ячейки 1. Массив `[49, 1, true]` означает таблицу 49 начиная с ячейки 1, и если символов больше чем ячеек — последний символ добавляется в крайнюю ячейку.

**Конвертация в PDF:**

Через LibreOffice headless: `soffice --headless --convert-to pdf`. Скрипт ищет LibreOffice по стандартным путям для macOS, Linux и Windows. LibreOffice создаёт PDF в директории источника — скрипт затем перемещает его в папку `PDF/`.

**Вспомогательные утилиты:**

`mark_tables.py` — принимает путь к docx, создаёт копию где в каждой пустой ячейке таблицы красным шрифтом размером 5pt написан индекс в формате `таблица:ячейка`. Используется для составления map.json вручную.

**Зависимости:** только `python-docx` и `lxml`. LibreOffice должен быть установлен отдельно.

**Обработка ошибок:** файл map.json читать с кодировкой `utf-8-sig` чтобы корректно обрабатывать BOM который добавляют Windows-редакторы. При ошибке конкретного файла скрипт логирует её и продолжает обработку остальных.

---
Как я автоматизировал юридическую рутину - 8

Автор: mnkh

Источник

Rambler's Top100