Разбор договоров через LLM без ложной уверенности в выводах
Разбор договоров через LLM требует точных полей, цитат и ссылок на пункты, чтобы юрист быстро проверял выводы и видел источник каждого ответа.

Почему юристу мало уверенного ответа
LLM часто пишет так, будто уже все проверила. В этом и проблема. Для юриста уверенный тон ничего не значит, если модель не показала, из какого пункта взяла срок, сумму, порядок расторжения или штраф.
При разборе договора ошибка редко выглядит грубой. Обычно модель пропускает оговорку в середине документа, путает общее правило с исключением или берет сумму из приложения, хотя в основном тексте уже есть новая редакция. Ответ звучит гладко, но смысл договора меняется.
Представьте договор поставки. Модель пишет: "оплата в течение 30 дней". Юрист открывает текст и видит, что 30 дней действуют только для первой партии, а дальше в пункте 4.3 стоит 10 рабочих дней после подписания УПД. Если система не дала цитату и номер пункта, человек тратит время на ручной поиск и заново проверяет весь ответ.
Без источника такой вывод трудно использовать по трем причинам:
- нельзя быстро проверить, где именно это написано;
- нельзя понять, не пропущено ли исключение рядом;
- нельзя объяснить коллеге, почему система решила именно так.
Это не мелочь. Ошибка в сроке оплаты, лимите ответственности или размере неустойки сразу меняет оценку риска. Иногда одна фраза вроде "если иное не согласовано в спецификации" переворачивает вывод по всему документу.
Поэтому хороший ответ по договору выглядит прозаичнее, чем многие ждут. Он не просто сообщает значение поля, а показывает связку: значение, цитата, номер пункта и, если нужно, степень уверенности. В командах, где есть внутренний контроль и аудит, это уже не пожелание, а рабочее требование.
Если модель не умеет привязать вывод к конкретному месту в тексте, она помогает только наполовину. Нужен не красивый итог, а проверяемый ответ, который можно открыть и подтвердить за минуту.
Какие поля извлекать из договора
Если просить модель просто "разобрать договор", она почти всегда вернет слишком размытый ответ. Лучше заранее задать набор полей, которые бизнес и юристы используют каждый день. Тогда результат проще проверить, сравнить между договорами и загрузить в учетную систему.
Сначала соберите базовые реквизиты: полные наименования сторон, их роли, номер договора, дату подписания и вид документа. Роли лучше хранить отдельно: "поставщик", "покупатель", "заказчик", "исполнитель". Так меньше шанс перепутать стороны в длинном тексте с приложениями и допсоглашениями.
Предмет договора тоже лучше вынести в отдельное поле, а не прятать в общем описании. Юристу мало фразы "договор поставки". Нужно видеть, что именно поставляют, в каком объеме и по каким спецификациям или услугам. Короткое поле "тип договора" и отдельное поле "предмет" обычно работают лучше, чем одно длинное резюме.
Удобно разбить поля на несколько групп:
- реквизиты: номер, дата, стороны, роли сторон;
- предмет: тип договора, описание товара или услуги, ссылки на приложения и спецификации;
- деньги: сумма, валюта, НДС, аванс, отсрочка, график и условия оплаты;
- сроки: дата начала, дата окончания, автопролонгация, условия досрочного расторжения;
- риски: неустойка, лимит ответственности, исключения ответственности, подсудность и порядок споров.
Денежные условия лучше дробить. Одного поля "стоимость" мало. В реальном договоре сумма может зависеть от этапа, партии, акта или курса валюты. Поэтому отдельно храните общую сумму, валюту, наличие НДС, момент возникновения обязанности оплатить и срок перечисления денег.
Со сроками та же история. Нужны не только даты действия договора, но и правила продления и расторжения. Если в тексте есть автопролонгация, модель должна вынести срок уведомления и способ уведомления. По риску это часто не менее важно, чем цена.
Блок с ответственностью и спорами многие команды недооценивают. Зря. Неустойка, предел убытков, штраф за просрочку, претензионный порядок и арбитражный суд часто определяют, насколько опасен договор.
Если вы строите извлечение полей из договора через API, не начинайте с двадцати редких полей. Сначала зафиксируйте 10-15 полей, без которых документ нельзя быстро оценить. Потом добавляйте отраслевые: SLA, конфиденциальность, обработку персональных данных, требования к актам и приемке.
Как подготовить текст перед запросом
Плохой вход почти всегда дает плохой ответ. Если OCR потерял нумерацию, склеил две страницы или превратил таблицу в набор случайных строк, модель все равно ответит уверенно. Проблема не в "уме" модели, а в том, что ей дали испорченный текст.
Сохраните структуру договора после OCR. Номера разделов, пунктов и подпунктов нельзя выбрасывать даже ради "чистого" текста. Для юриста фраза без адреса в документе почти бесполезна. Если модель нашла срок оплаты, она должна опираться не просто на абзац, а на пункт 4.2 на странице 7.
Длинный договор лучше делить на понятные блоки, а не на куски фиксированной длины. Хороший блок обычно совпадает с разделом, подпунктом или небольшой группой соседних пунктов. Так модель видит локальный смысл и реже смешивает ответственность сторон, сроки и условия оплаты из разных частей документа.
Мусор из сканов лучше убрать до запроса. Колонтитулы, номера страниц внизу, повторяющиеся шапки, следы печатей, артефакты OCR и дубли страниц часто ломают извлечение. Если оставить все как есть, модель может принять текст из шапки за часть условия договора или сослаться на дубль вместо исходного пункта.
Отдельно проверьте таблицы. В договорах поставки и приложениях именно там часто лежат цены, сроки, номенклатура, штрафы и реквизиты. Если после OCR таблица распалась на вертикальный список слов, модель начнет угадывать связи между ячейками. Такой фрагмент лучше либо починить, либо сразу пометить как сомнительный и отправить на ручную проверку.
Что хранить рядом с каждым фрагментом
Без метаданных даже хороший текст быстро теряет ценность. Рядом с каждым блоком полезно хранить:
- номер страницы;
- номер раздела, пункта и подпункта;
- тип фрагмента: основной текст, таблица, приложение;
- порядковый номер блока в документе;
- признак качества OCR, если он у вас есть.
На практике все просто. Вы берете пункт 5.3, добавляете к нему страницу, номер раздела и соседний контекст, а потом отправляете в модель уже такой блок. Если она вернет "срок поставки - 10 рабочих дней", путь к источнику у вас уже есть. Юрист открывает страницу, видит пункт, сверяет формулировку и не тратит время на поиск по всему договору.
Если вы готовите потоковую обработку, лучше один раз зафиксировать одинаковые правила очистки и нарезки. Тогда ответы будут ровнее, а ошибки станет проще находить и исправлять.
Как задать схему ответа
Когда вы просите модель "проанализируй договор и скажи главное", она часто смешивает факт, догадку и пересказ. Для юридической работы это плохой формат. Здесь ответ должен быть не "умным", а проверяемым.
Лучше сразу задать жесткую схему: ответ только в JSON, без текста до и после. Свободный пересказ почти всегда мешает, потому что потом трудно понять, где точная формулировка договора, а где вывод модели.
Простой шаблон выглядит так:
{
"payment_term": {
"status": "found",
"value": "10 банковских дней",
"comment": "Срок указан прямо",
"source": {
"quote": "Покупатель оплачивает товар в течение 10 банковских дней",
"clause": "4.2",
"page": 3
}
},
"penalty": {
"status": "unclear",
"value": null,
"comment": "Есть упоминание ответственности, но размер неустойки не указан явно",
"source": {
"quote": "Стороны несут ответственность в соответствии с законодательством РФ",
"clause": "7.1",
"page": 5
}
}
}
Такой формат решает сразу несколько задач. Поле value хранит только значение. Поле comment - короткое замечание модели, если оно вообще нужно. Источник лежит отдельно, и юрист сразу видит, на какой пункт и страницу опирается вывод.
Статус лучше сделать фиксированным:
found- поле найдено явно;not_found- поле в тексте не найдено;unclear- рядом есть нужный фрагмент, но вывод нельзя считать точным.
Это простое различие заметно снижает ложную уверенность. Если модель не нашла срок оплаты или не поняла, кто несет расходы на доставку, она не должна додумывать ответ.
Цитату просите короткую. Обычно 10-25 слов хватает. Длинные объяснения засоряют ответ и мешают быстрой проверке. Юристу удобнее увидеть фразу из договора, чем абзац рассуждений модели.
Полезно добавить еще одно правило: если нет номера пункта или страницы, статус не может быть found. Для договоров это строгая, но здравая граница. Она отсеивает ответы, которые звучат уверенно, но не опираются на текст.
Если вы работаете через единый OpenAI-совместимый шлюз вроде RU LLM на rullm.com, такую схему удобно закрепить на уровне приложения один раз и использовать с разными моделями. Тогда команда сравнивает не красивые формулировки, а качество извлечения и точность ссылок.
Как привязать вывод к источнику
Поле без источника почти ничего не стоит. Нужен не "ответ модели", а точное место в договоре, где этот ответ подтверждается. При работе с договорами полезно перевернуть порядок: сначала найти цитату, потом заполнять поле.
Модель не должна пересказывать фразу своими словами, если вы хотите проверить ее вывод. Сохраняйте точный фрагмент как есть, даже если текст тяжеловесный, с канцеляритом или длинной ссылкой на приложение. Любая правка усложняет проверку.
Что хранить рядом с каждым полем
Для каждого извлеченного значения держите короткий набор опорных данных:
- само значение поля;
- точную цитату из договора без правки;
- ссылку на пункт, подпункт и страницу;
- 1-2 соседние строки до и после цитаты;
- статус проверки: "подтверждено" или "не подтверждено".
Такой формат сразу показывает, откуда взялся вывод. Если модель пишет "срок оплаты - 10 дней", юрист видит, это пункт 4.3 на странице 7 или домысел из похожей фразы в другом месте.
Соседние строки нужны чаще, чем кажется. В договоре поставки формулировка "оплата производится в течение 10 банковских дней" сама по себе еще не дает полного ответа. Следующая строка может уточнять: "с даты подписания УПД" или "с даты поставки на склад покупателя". Без этого поле будет неточным, хотя цитата внешне выглядит убедительно.
Правило простое: цитата должна доказывать значение, а не просто стоять рядом по смыслу. Если в тексте есть только "поставщик вправе потребовать неустойку", это не подтверждает размер неустойки. Размер можно принять только тогда, когда рядом есть фраза вроде "0,1% от суммы просроченного платежа за каждый день" и ссылка на конкретный подпункт.
Когда ответ надо отклонять
Иногда модель находит похожее место и делает слишком смелый вывод. Это обычная ошибка, особенно если в договоре много перекрестных ссылок, приложений и исключений.
Отклоняйте ответ, если:
- цитата не содержит само значение поля;
- ссылка ведет на общий раздел, а не на точный пункт;
- соседний текст меняет смысл фразы;
- в договоре есть вторая цитата, которая спорит с первой;
- модель объединила два разных условия в одно.
Если вам нужен нормальный след проверки, полезно хранить не только JSON-ответ, но и исходный фрагмент, метку проверки и аудит-трейл запроса. В шлюзах вроде RU LLM это проще встроить в процесс, особенно если документы должны оставаться в российском контуре.
Как собрать процесс по шагам
Сразу просить у модели готовый вывод по договору - плохая идея. Надежнее разбить работу на короткие шаги, где каждый результат можно проверить отдельно. Тогда виден не только ответ, но и фрагмент текста, на котором он держится.
Практичная схема выглядит так:
- Сначала получите чистый текст договора и разбейте его на блоки. Обычно хватает деления по разделам, пунктам и подпунктам, чтобы у каждого куска был свой идентификатор:
sec_4.2,app_1_p3. - Затем дайте модели одно поле за раз и попросите не "ответить", а найти кандидатов. Например: кто стороны, какой срок оплаты, есть ли неустойка, где описан порядок приемки.
- После этого прогоните кандидатов через простые проверки. Даты можно сверить по шаблону, суммы - по числу и валюте, ИНН - регуляркой, ссылки на пункты - по существующим идентификаторам блоков.
- Если поле не прошло проверку или модель нашла два похожих варианта, отправьте второй запрос только по спорному месту. Не надо заново гонять весь договор.
- В конце соберите карточку результата: значение поля, статус, цитата, идентификатор блока и короткий комментарий, почему поле считают надежным или спорным.
Такой процесс заметно режет ложную уверенность. Модель может предположить, что срок оплаты "30 дней", потому что часто видела такую формулировку. Но если в карточке нет точной цитаты из пункта 5.1, этому ответу нельзя доверять.
На договоре поставки это выглядит просто. Модель нашла для поля "срок оплаты" два кандидата: "в течение 10 банковских дней" в пункте 4.3 и "не позднее 30 календарных дней" в приложении. Правила не выбирают победителя автоматически, а ставят статус "спорно" и отправляют только эти два блока на второй проход. Во втором запросе модель уже не блуждает по всему документу, а сравнивает два конкретных фрагмента.
Итог лучше хранить не в виде одного абзаца, а в виде таблицы или JSON-объекта со статусами found, ambiguous, missing. Для юриста это удобнее, чем гладкий текст. Для команды разработки тоже: такой формат легко показать в интерфейсе, залогировать и проверить на тестовом наборе договоров.
Если документы нельзя выводить за пределы РФ, такой конвейер можно запускать через шлюз вроде RU LLM: код и SDK обычно менять не нужно, а логи, биллинг и обработка остаются внутри российского контура. Но сам принцип не зависит от конкретного провайдера. Работает именно дисциплина шагов: поиск, проверка, разбор спорных мест и итог с цитатами.
Пример на договоре поставки
На вход подают договор поставки на 18 страниц и приложение со спецификацией. Модель не пишет общий вывод сразу. Сначала она ищет поле "срок оплаты", потом сохраняет цитату и адрес фрагмента, откуда взяла ответ.
В основном тексте она находит формулировку: "оплата производится в течение 30 календарных дней с даты поставки товара". В карточке рядом стоит источник: пункт 4.2, страница 7. Видно не просто готовый ответ, а точное место в документе, где он появился.
Что происходит дальше
Потом модель проверяет, нет ли условий, которые меняют общее правило. Во втором блоке она находит оговорку о предоплате. Такое бывает часто: базовое условие лежит в разделе о расчетах, а исключение спрятано в приложении, в особых условиях или в части про первую поставку.
Если система настроена правильно, она не склеивает эти фрагменты в один "уверенный" вывод. Она показывает конфликт. В карточке остается базовое правило про 30 дней с даты поставки, но рядом появляется отдельная запись про предоплату. Статус меняется на "неясно".
Это честнее и полезнее. Документ говорит о двух режимах оплаты, и без проверки нельзя понять, какой из них действует в этом кейсе. Может быть, предоплата относится только к первой партии. Может быть, она действует для всей поставки. Иногда приоритет у приложения, иногда у основного текста. Модель не должна гадать.
Как юрист это проверяет
Вместо чтения всего файла юрист открывает два спорных места. Сначала смотрит пункт 4.2 на странице 7. Потом проверяет фрагмент, где упомянута предоплата. На это уходят минуты, а не полный проход по документу.
Для поточного разбора такой формат заметно лучше обычного ответа без ссылок. Система делает извлечение полей из договора, прикладывает ссылки на пункты договора и сразу помечает спорные места. Не нужно верить модели на слово. Видно источник каждого вывода, а проверять приходится только то, что действительно требует внимания.
Где модель ошибается чаще всего
Самая частая проблема проста: модель читает не тот документ. В папке лежат проект, протокол разногласий и подписанная версия, а ответ строится по черновику. В итоге время уходит не на анализ, а на поиск того, какой файл система вообще взяла за основу.
Вторая типичная ошибка связана с суммами. В основном тексте стоит цена договора, а в приложении - лимит по отдельной поставке, скидка или пример расчета. Модель легко хватает число из таблицы в приложении, потому что оно оформлено заметнее. Для договора поставки это обычный сбой: вместо общей суммы она возвращает сумму одной партии.
Плохой OCR ломает смысл еще сильнее. Если распознавание склеило две соседние строки, модель может соединить разные пункты в один и придумать связь, которой нет. Так появляются ложные выводы о сроках, штрафах и порядке расторжения. Один потерянный перенос строки иногда меняет весь ответ.
Отрицания и исключения модель тоже пропускает регулярно. Фраза вроде "поставщик не несет ответственность, кроме случаев умысла" легко превращается в обратный смысл, если модель читает быстро и цепляется только за слово "ответственность". Особенно часто это происходит в длинных предложениях с несколькими "за исключением", "если иное не" и ссылками на другие пункты.
Есть и более неприятный сбой: модель придумывает номер пункта. Она верно уловила смысл, но вместо точной ссылки пишет "п. 4.2" просто потому, что ответ без номера выглядит для нее незавершенным. Если в схеме ответа нет обязательной цитаты и координат источника, такой вымысел проходит незаметно.
Поэтому разбор договоров через LLM нельзя строить на одном поле "ответ". Для каждого извлеченного значения нужны как минимум текстовый фрагмент, номер пункта, страница или диапазон строк, а также признак уверенности. Если ссылка не найдена, система должна вернуть пустое поле, а не догадку.
На практике хорошо работает жесткое правило: нет цитаты - нет вывода. Оно быстро срезает красивую, но опасную самоуверенность и сразу показывает, где документ нужно проверить вручную.
Короткая проверка перед передачей юристу
Перед тем как юрист откроет результат, система должна снять самые частые ошибки. Это занимает минуты, но сильно снижает ложную уверенность. Лучше показать меньше полей, но с ясным источником и честной пометкой там, где модель не уверена.
Подход простой: каждое извлеченное поле проходит короткий контроль, а спорные места не прячутся в общем ответе. Если в договоре нет явного значения, система не должна додумывать формулировку. Она должна поставить статус "неясно" и оставить фрагмент текста, на котором остановилась.
Проверка обычно включает пять вещей:
- у каждого поля есть либо значение, либо статус "неясно" с причиной;
- каждая цитата совпадает с номером пункта и страницей;
- суммы, даты и сроки проходят простую валидацию;
- конфликтующие условия показываются отдельно, без попытки склеить их в один вывод;
- карточка поля и исходный текст стоят рядом.
На практике это выглядит очень приземленно. Например, в карточке "срок оплаты" стоит "30 календарных дней", рядом видны цитата, номер пункта и страница. Ниже система отмечает конфликт: в приложении указан срок "20 банковских дней". Сразу понятно, что нужно проверить приоритет документов, а не верить первому найденному числу.
Если ваша команда уже сохраняет аудит-трейл по запросам, такой контроль собрать проще. Но даже без сложной инфраструктуры этот минимум нужен всегда: значение, источник, валидация и честная отметка о конфликте.
Что сделать перед запуском в компании
Перед пилотом не спорьте о качестве на общих словах. Возьмите 30-50 реальных договоров тех типов, с которыми команда работает каждый день, и соберите по ним эталонные ответы руками юриста. В эталоне нужны не только значения полей, но и точные ссылки на пункты, из которых эти значения взяты.
Для такой системы это важнее, чем общий процент "правильных ответов". Модель может верно назвать срок оплаты, но сослаться не на тот пункт. Для юриста такой ответ уже слабый, потому что его нельзя быстро проверить.
Метрики лучше считать раздельно. Тогда сразу видно, где ломается процесс: на извлечении данных или на привязке к источнику.
- полнота полей: сколько нужных полей модель вообще заполнила;
- точность полей: сколько значений совпало с эталоном;
- точность ссылок: сколько ссылок ведут в правильный пункт или подпункт;
- доля ответов с пометкой "не найдено": она показывает, умеет ли система честно останавливаться.
Полезно сразу завести простой журнал проверки. Например, юрист открывает карточку договора и видит: "лимит ответственности - пункт 8.3", "срок поставки - пункт 2.1", "автопролонгация не найдена". Если источник не показан, такой результат лучше не пускать в работу.
Отдельно настройте аудит-трейлы и маскирование персональных данных. В договорах часто есть ФИО, телефоны, адреса, паспортные данные и банковские реквизиты. Если команда отправляет документы в LLM без маскирования и без нормального журнала запросов, потом трудно понять, кто что проверял, какой текст ушел в модель и на каком основании появился вывод.
Если в компании нужен OpenAI-совместимый контур внутри РФ, имеет смысл смотреть на шлюз, который не ломает текущую интеграцию. У RU LLM для этого есть единый эндпоинт, хранение логов и бэкапов в РФ, маскирование PII и аудит-трейлы на уровне запросов. Для сценариев с договорами это удобно: служба ИБ и юристы хотят видеть не только ответ модели, но и весь след проверки.
Перед запуском я бы не ставил цель "автоматизировать все договоры". Лучше выбрать один тип, например договор поставки, 10-15 полей и жесткое правило: система либо показывает значение и пункт, либо честно пишет, что не нашла подтверждение. Такой старт обычно дает больше пользы, чем красивый, но непроверяемый пилот.
Часто задаваемые вопросы
Почему нельзя брать ответ модели без цитаты?
Потому что уверенный тон ничего не доказывает. Юристу нужен не пересказ, а точное место в договоре: цитата, пункт и страница. Тогда он быстро сверяет вывод и сразу видит, не спряталось ли рядом исключение.
Какие поля лучше извлекать в первую очередь?
Начните с 10–15 полей, без которых договор нельзя быстро оценить. Обычно это стороны, роли сторон, номер и дата договора, предмет, сумма, валюта, НДС, срок оплаты, срок действия, расторжение, неустойка, лимит ответственности и порядок споров.
Как правильно делить договор перед запросом в модель?
Режьте текст по разделам, пунктам и подпунктам, а не по фиксированному числу символов. Так модель видит цельный смысл фрагмента и реже смешивает оплату, сроки и ответственность из разных мест.
Что делать, если OCR испортил договор?
Сначала почистите текст. Уберите дубли страниц, шапки, колонтитулы и мусор после скана, но сохраните нумерацию разделов и пунктов. Если OCR развалил таблицу, не давайте модели угадывать связи между ячейками — лучше пометьте такой кусок как сомнительный и отправьте на ручную проверку.
В каком формате просить ответ у модели?
Просите строгий JSON без лишнего текста. Для каждого поля удобно держать value, status, короткую quote, номер clause и page. Такой формат проще валидировать, логировать и показывать юристу без ручной чистки.
Какие статусы лучше использовать для полей?
Хватает трех статусов: found, not_found и unclear. Если модель не нашла пункт или страницу, не ставьте found, даже если формулировка похожа на правильную. Это сильно режет лишнюю самоуверенность.
Когда ответ модели нужно сразу отклонять?
Отклоняйте вывод, когда цитата не содержит само значение, ссылка ведет только на общий раздел, соседний текст меняет смысл фразы или другой пункт спорит с первым. Еще один частый повод для отказа — модель склеила два разных условия в одно.
Как поступать, если основной текст и приложение говорят разное?
Не выбирайте победителя автоматически. Сохраните оба фрагмента, поставьте статус вроде unclear или ambiguous и отдайте юристу точные цитаты из основного текста и приложения. Так человек проверит приоритет документов за пару минут, а не перечитает весь файл.
Как собрать рабочий процесс без ложной уверенности?
Надежнее идти короткими шагами. Сначала чистите и режьте текст, потом ищите кандидаты по одному полю, затем проверяйте даты, суммы, ИНН и ссылки на пункты, а спорные места отправляйте на второй проход. В итоговой карточке храните значение, статус, цитату и адрес фрагмента.
Как проверить систему перед запуском в компании?
Возьмите 30–50 реальных договоров и соберите эталон руками юриста. Считайте отдельно полноту полей, точность значений и точность ссылок на пункты. Если документы должны оставаться внутри РФ, сразу проверьте маскирование персональных данных, хранение логов и аудит-трейл по каждому запросу.