Задача обычно приходит в одной и той же формулировке: вот два файла, сверьте, там должно сойтись. Дальше кто-то кладет их в сравниватель файлов или тянет ВПР по наименованию, получает стену красного и половину столбца в «#Н/Д» — и на этом сверка заканчивается. Разбирать такой результат дольше, чем сверить глазами.
Программа при этом отработала честно. Ее просили найти минимальный набор вставок и удалений, превращающих одну последовательность строк в другую, — она нашла. Неверно поставлена сама задача. Накладная, заказ, акт и реестр платежей устроены как множества позиций: порядок строк в них не значит ничего, равенство определяется смыслом, и одна позиция слева законно превращается в две справа.
Ниже — механика сопоставления пары документов: нормализация, отбор кандидатов, взвешенная уверенность, назначение, связи многие ко многим, порог и очередь. Отдельным блоком договоры, где единица сравнения другая.
Что делает построчное сравнение и почему на документах оно врет
Классический алгоритм сравнения ищет наибольшую общую подпоследовательность строк. У него два встроенных допущения: строки равны или не равны побайтово, и порядок строк несет смысл. На исходном коде оба верны, на документах оба ложны.
| Что происходит в документах | Как это выглядит в построчном сравнении | Что должно делать сопоставление |
|---|---|---|
| Слева сортировка по коду, справа по алфавиту | Документ целиком удален и добавлен заново | Сравнивать множества, порядок игнорировать |
| «Кабель ВВГнг(А)-LS 3х2,5» и «Кабель силовой ВВГ-нг(A)LS 3*2.5 ГОСТ» | Две несвязанные строки | Нормализация и нечеткое сравнение |
| Позицию отгрузили с двух складов: одна строка стала двумя | Одно удаление и две вставки | Связь один ко многим с проверкой суммы количеств |
| Две строки с одной номенклатурой схлопнулись в одну | Два удаления и одна вставка | Связь многие к одному |
| Поставщик пишет упаковки, у вас в базе штуки | Расхождение по количеству в кратность раз | Приведение к базовой единице |
| НДС округлен построчно с одной стороны и от итога с другой | Копейки почти в каждой строке | Допуск, заданный в деньгах |
| «Итого», «в том числе НДС», заголовки групп, повтор шапки на второй странице | Мусорные совпадения вперемешку с расхождениями | Классификация строк до сопоставления |
Практический вывод: построчное сравнение полезно на последнем шаге, внутри сопоставленной пары. Сначала надо понять, что с чем сравнивать.
Нормализация: привести обе стороны к одному виду
Самый дешевый шаг с самым большим эффектом. Делается отдельно для каждого типа поля и по одним правилам для обеих сторон.
- Текст. Регистр, схлопывание пробелов, дефис против длинного тире. Отдельно — латиница внутри кириллических слов: буквы a, c, e, o, p, x, y, A, B, E, K, M, H, O, P, C, T, X выглядят одинаково и сравниваются как разные символы. В артикулах встречается постоянно.
- Обозначения размеров. «3х2,5», «3x2.5», «3*2,5» и «3 х 2.5» — одна и та же величина. Разделитель и десятичную запятую приводим к одному виду до всякого сравнения.
- Сокращения. Свой словарь: шт, уп, кор, компл, ГОСТ, ТУ, оцинк, нерж. Пополняется он из очереди спорных.
- Числа и даты. Разряды через пробел, минус в скобках, валюта отдельным полем. Даты — к одному представлению.
Правило, которое спасает на разборе спорных: нормализация не должна быть разрушительной. Храните и оригинал строки, и нормализованную форму. Сравнение идет по нормализованной, отчет показывает оригинал. Иначе оператор смотрит на строку, которой нет ни в одном из двух исходных файлов.
Тем же шагом выбрасываются служебные строки. Признаки простые: пустое количество при заполненной сумме, слова «итого», «всего», «в том числе», совпадение суммы строки с суммой нескольких предыдущих, повтор шапки таблицы. Пропустите этот шаг — итоговая строка найдет себе пару, и отчет получит расхождение на всю сумму документа.
Кандидаты и ключи: от артикула до вектора
Сравнивать все со всем можно, пока документы маленькие. Пара накладных по 200 строк дает 200 × 200 = 40 000 пар, любой ноутбук проглотит это за секунду. Реестр на 5000 строк против такого же дает 25 миллионов пар, и если на каждую считается векторная близость, ждать придется долго. Поэтому перед скорингом идет отбор кандидатов по грубому признаку: совпадение артикула, общие триграммы наименования, одинаковый порядок величины суммы, одна дата. Строка детально сравнивается с двумя-тремя десятками кандидатов вместо всего документа.
Сами ключи выстраиваются от точного к слабому: штрихкод, артикул поставщика, накопленные соответствия из прошлых загрузок, точное совпадение нормализованного наименования, нечеткое сравнение, векторная близость. Эта лестница разобрана в статье про поток первичных документов в 1С. Для сверки пары документов важнее два момента, о которых там речи не шло.
Первое: составной ключ сильнее любого одиночного. Артикул совпал, но количество разошлось вдвое — перед вами, скорее всего, две разные строки одной позиции. Наименование совпало на две трети, зато количество и цена сошлись до копейки — это почти наверняка одна позиция. Комбинация слабых сигналов работает лучше, чем один сильный.
Второе: нечеткое сравнение по токенам обыгрывает посимвольное. Расстояние между строками хорошо ловит опечатки и плохо ловит перестановку слов и лишнее слово в середине. Разбивка наименования на токены с весом по редкости дает то, что нужно: слова «кабель» и «шт» встречаются тысячи раз и различающей силы не несут. Токены «ВВГнг» и «3x2.5» редкие, вся сила в них. Там, где формулировки расходятся принципиально, подключается векторный поиск с переранжированием кандидатов.
Оценка уверенности: как складывать слабые сигналы
Результат сравнения пары строк — число от нуля до единицы. Считается оно как взвешенная сумма отдельных признаков, и веса подбираются на своей размеченной выборке. Форма записи выглядит так:
уверенность = 0,45 × схожесть наименования + 0,25 × совпадение количества + 0,20 × совпадение цены + 0,10 × совпадение единицы измерения
Числа в этой строке — стартовая точка для подбора. Константой их считать нельзя. Подбор простой: размечаете руками 200–300 пар строк из своих документов, прогоняете скоринг, смотрите, на каких весах верные пары уходят вверх, а мусор остается внизу. День работы, зато дальше понятно, почему система решила так.
Два требования к этой части. Каждый признак должен уметь возвращать состояние «данных нет»: пустой артикул с одной стороны обнуляет вес артикула, расхождением он не является. И вся арифметика обязана быть объяснимой — рядом со связкой лежит разбор, какой признак сколько дал. Оператор, который видит «совпало 0,82», работать не может. Оператор, который видит «наименование 0,7, количество и цена точно, единица разная», решает вопрос за пять секунд.
Назначение и связи многие ко многим
Дальше идет шаг, который чаще всего делают неправильно. Для каждой строки слева выбирают лучшего кандидата справа — и получают конфликт: три разные строки слева выбрали одну и ту же строку справа. Локально каждый выбор верный, вместе они противоречивы.
Задача решается глобально. Все пары с уверенностью выше нижней границы складываются в один список и сортируются по убыванию. Список проходится сверху вниз: пара фиксируется, если обе ее строки еще свободны, и пропускается, если хотя бы одна занята. Жадный проход работает мгновенно и дает результат, близкий к оптимальному. Строгий оптимум считается венгерским алгоритмом, и нужен он там, где в документе много почти одинаковых позиций: крепеж, метизы, кабель разного сечения.
После этого прохода остаются нераспределенные строки с обеих сторон. Вот они и есть материал для связей многие ко многим:
- Один ко многим. Берем свободную строку слева и подмножество свободных строк справа с тем же артикулом. Сумма их количеств совпала с количеством исходной строки — это разбивка: отгрузили с двух складов, разбили по сериям, довезли партиями.
- Многие к одному. Зеркальный случай: в заказе позиция шла двумя строками, в накладной ее свернули в одну.
- Многие ко многим. На длинных периодах: три отгрузки закрыты двумя платежами. Проверяется совпадением сумм подмножеств.
Перебор подмножеств взрывается комбинаторно, ограничивать его надо жестко: только строки с одним артикулом или наименованием выше порога схожести, группы не больше трех-четырех строк, обязательная проверка сумм с допуском. Иначе система найдет «совпадение» там, где его нет, и это будет самый вредный тип ошибки — правдоподобный.
Итог работы — граф связей с типом у каждой: один к одному, один ко многим, многие к одному, строка без пары слева, строка без пары справа. Плоский список такую структуру не выражает, и отчет надо строить сразу под граф.
Округления и единицы: где расхождение законно
Сравнение денег на точное равенство гарантирует поток ложных расхождений. Считаем, откуда они берутся. НДС округляется до копейки построчно, каждая строка дает погрешность до половины копейки. На документе в 40 строк максимальное законное расхождение итога составляет 40 × 0,5 = 20 копеек. Одна сторона считает налог от итога, вторая построчно — разойдутся именно эти копейки.
Отсюда правило: допуск задается в деньгах и растет с числом строк. Процентный допуск — ловушка: 0,1% на строке в миллион рублей это тысяча рублей, и такое расхождение проедет мимо контроля.
С единицами измерения похожая история, только цена ошибки выше. Поставщик пишет «уп» и подразумевает коробку из двенадцати, в вашей карточке упаковка шесть. В обоих документах стоит «10 уп», в штуках это 120 против 60, а сумма сходится до копейки — расхождение целиком спрятано в единице измерения. Количества приводятся к базовой единице по коэффициенту из справочника, сравниваются уже базовые. Если коэффициент для пары неизвестен, система не имеет права вычислить его из отношения количеств и принять за факт. Такая строка уходит человеку. Один раз угаданная кратность разъезжается по остаткам и всплывает на инвентаризации через квартал.
Порог и очередь на ручное решение
Порогов должно быть два. Выше верхнего связка принимается автоматически, под нижним пара не рассматривается вовсе. Между ними серая зона — очередь, где решает человек.
Цена ошибки в двух направлениях разная, и это определяет, куда двигать пороги. Пропущенная связка шумная и безопасная: строка попадает в отчет как расхождение, человек ее видит и закрывает. Ложная связка тихая и опасная: две разные позиции объявлены одной, расхождение исчезает из отчета, и о нем никто не узнает. Верхний порог поэтому ставится высоко и опускается только по результатам замера.
Нагрузку на очередь считайте заранее. Формула простая: строк в паре документов × доля серой зоны × время на одно решение. Двести строк, каждая десятая в серой зоне, тридцать секунд на решение — двадцать решений и десять минут работы оператора на пару документов. Если у вас получается два часа, автоматизация в таком виде не окупится, и увидеть это надо на бумаге до старта работ.
Каждое решение оператора обязано возвращаться в систему: подтвержденная связка пополняет таблицу соответствий, отклоненная — список запретов. Через месяц работы серая зона на тех же контрагентах сжимается сама. Экономика этого цикла на потоке разобрана в статье про автоматическую сверку документов с контрагентами.
Тип сверки, метод, типичная ошибка
Слово «сверка» покрывает несколько задач, метод у каждой свой. Найдите свой случай и смотрите правую колонку: там то, на чем спотыкаются.
| Тип сверки | Метод сопоставления | Типичная ошибка |
|---|---|---|
| Накладная против заказа | Позиции по артикулу и наименованию, сверка количеств и цен, связи один ко многим | Частичная отгрузка засчитана как недостача, довоз идет вторым документом |
| Акт сверки взаиморасчетов | Документы по номеру, дате и сумме, затем сальдо нарастающим итогом | Разный период и разный момент признания: отгрузка у одного, получение у другого |
| Реестр платежей против выписки | Сумма, дата и разбор номеров счетов из назначения платежа | Один платеж закрывает несколько счетов, связь многие ко многим не построена |
| Данные ЭДО против учетной базы | Идентификатор документа и бизнес-ключ из ИНН, номера, даты, суммы | Дубли контрагентов, документ падает на вторую карточку |
| Новый прайс против старого | Артикул с проверкой наименования на неизменность | Артикул переиспользован под другой товар, рост цены выглядит как скидка |
| Остаток против инвентаризации | Код номенклатуры вместе с характеристикой, серией и складом | Пересорт: характеристика проигнорирована, итог сходится, товар лежит не тот |
| Две версии договора | Структурное сопоставление пунктов, текстовое сравнение внутри пары | Сверили суммы и приложения, пропустили сроки и ответственность |
Про строку с ЭДО есть отдельный разбор, где именно расходятся суммы с учетной системой: сверка ЭДО и данных 1С. Классы расхождений и формат отчета описаны на странице сверки документов: пилот от 350 000 ₽, четыре недели до первого отчета, старт на выгрузках без доступа в базу.
Версии договоров: другая единица сравнения
Договор — иерархия пунктов, и сравнивать его надо пунктами. Строка тут не единица смысла: одна вставленная запятая переносит половину абзаца на следующую строку и красит в красный весь остаток документа.
Из обоих файлов вытаскивается структура: нумерация вида 1., 1.1., 1.1.1., заголовки разделов, приложения. Затем пункты сопоставляются между версиями тем же аппаратом, что и позиции накладной. По номеру пункта сопоставлять нельзя: вставили один пункт в начало, и вся нумерация ниже съехала на единицу. Работает комбинация заголовка и текста с нечетким сравнением и глобальным назначением.
Только после того, как пары найдены, внутри каждой запускается текстовое сравнение. Тут классический diff идеален: он покажет ровно те слова, которые изменились. Порядок операций решает все — сопоставление сверху, посимвольное сравнение внизу.
Отдельная категория находок — пункты без пары. Пункт, которого нет в старой версии, важнее любой правки внутри существующего текста. Именно так в договор въезжают автопролонгация и односторонний отказ.
Почему у модели нет последнего слова
Соблазн отдать сравнение версий целиком языковой модели понятен: она читает оба файла и пишет человеческим языком, что изменилось. На коротком договоре это даже выглядит убедительно. Проблема вылезает на длинном, и вылезает молча.
Модель хорошо объясняет найденное: переводит правку с юридического на человеческий, расставляет приоритеты, не устает к сороковой странице. Чего она не дает — гарантии полноты. Молчание модели о пункте ничего не доказывает, и проверить это по ее ответу невозможно. Механика такого самообмана разобрана в статье про галлюцинации LLM в бизнесе.
Разделение обязанностей поэтому жесткое. Факт «что изменилось» устанавливает детерминированный слой: структурное сопоставление плюс текстовое сравнение внутри пары. Он воспроизводим и проверяется глазами по двум файлам. Модель работает поверх, объясняет и ранжирует, и каждое ее утверждение обязано вести к точной цитате из обеих версий. Какие пункты договора читать первыми — сроки, ответственность, приемку, автопролонгацию, право менять цену в одностороннем порядке — и как держать этот разбор на своем железе, разобрано отдельно: ИИ-юрист и поиск рисков в документах.
Как подготовить два документа к сверке
Сверки ломаются на входе чаще, чем на алгоритме: вместо данных подают печатную форму. Восемь пунктов, которые займут день и определят качество результата.
- Выгружайте данные таблицей. Одна строка на позицию, CSV, xlsx или xml. PDF-печатка означает, что перед сопоставлением встанет извлечение — отдельная задача со своей ценой.
- Уберите из таблицы все, кроме позиций. Объединенные ячейки, подытоги, повторы шапки, пустые разделители. Настраивается один раз в выгрузке.
- Дайте ключевые колонки. Код позиции с каждой стороны, артикул контрагента, наименование, количество, единица, цена, сумма. Без артикулов сверка работает, но серая зона будет шире.
- Договоритесь о периоде и моменте признания. По какой дате режем: отгрузки, поступления, проведения. Заметная часть строк в акте сверки расходится по одной этой причине: стороны режут период по разным датам.
- Выгрузите справочник единиц и кратностей упаковок отдельным файлом, вместе с коэффициентами пересчета к базовой единице.
- Разметьте руками 200–300 пар строк. Это и материал для подбора весов, и приемочная выборка.
- Зафиксируйте допуск в деньгах. Копейки на строку, копейки на документ. Проценты не используйте.
- Определите, какое расхождение требует действия. Иначе отчет на тысячу строк утонет в копеечных отклонениях и его никто не откроет.
Когда сопоставление не нужно
Четыре ситуации, в которых честный ответ — «задача другая».
- С обеих сторон есть общий надежный ключ. Номер заказа и код позиции присутствуют в обоих файлах и заполнены везде. Это соединение таблиц по ключу, запрос на десять строк кода. Нечеткая логика тут лишняя.
- Пара документов на два десятка строк раз в месяц. Описать правила дороже, чем сверить глазами. Автоматизация окупается на регулярном потоке.
- Две версии одного файла с сохраненным порядком строк. Выгрузка из одной системы за два дня, тот же экспорт, та же сортировка. Обычное построчное сравнение справится и будет точнее.
- Справочники в хаосе с обеих сторон. Дубли контрагентов, позиции «Товар без названия 3», артикулы, переиспользованные под разные товары. Сопоставлять пока не с чем: сначала порядок в данных, потом автоматика.
Что дальше
Начинать стоит с выгрузок за один закрытый период с обеих сторон. По этой паре файлов видно главное: есть ли сквозной ключ, насколько расходятся наименования и какой ширины получится серая зона. Эти три вещи определяют и достижимое качество, и цену работ. Прикинуть бюджет под свой объем можно на калькуляторе.