Привет, Хабр! Меня зовут Кирилл Воронин, я data scientist в Doubletapp. В статье расскажу вам, как мы автоматизировали рутинные процессы отсмотра и сверки документов для допуска подрядчиков к тендерам.
Тендеры проводит нефтегазовая компания, которой нужно подобрать исполнителей для кейтеринга на удаленных производственных объектах, где отсутствует транспортное сообщение и должны соблюдаться строгие требования к качеству и безопасности продуктов. Поэтому надёжный процесс подбора исполнителей очень важен.
При допуске к тендеру требуется отсмотреть и проанализировать большой объем информации о потенциальных подрядчиках. Ранее все изучение и сравнение документов велось вручную, это отнимало много времени, возникали ошибки. Для повышения эффективности процедуры отбора и сокращения времени на принятие решения была построена система, использующая большую языковую модель Mistral-7B-v0.2, в разработке которой я участвовал.
Читайте в статье:
• С какой документацией мы работали
• Как мы классифицировали и сверяли документы
• Как система показала себя в работе
С какой документацией мы работали
Расскажу подробнее, в чем состояла задача.
Мы получили набор документов, предоставленный участником отбора, а также набор критериев для его оценки. Критерий может быть либо подтверждён, либо не подтвержден имеющимися документами, причём по каждому из критериев было заранее задано, какие документы могут служить подтверждением.
Например, чтобы подтвердить наличие опыта, подрядчик должен предоставить договор об оказании подобных услуг в прошлом, а также акт, свидетельствующий о фактическом выполнении услуг.
Или для подтверждения наличия в штате технолога общепита, необходим диплом соответствующего специалиста вместе с документом о назначении на должность.
Таким образом, имеет место отношение «один ко многим», то есть один критерий может подтверждаться несколькими документами и только в совокупности, а не по отдельности. Из-за этого возникла необходимость не просто определять, подходит ли некоторый документ для подтверждения, но и сравнивать найденные документы между собой, чтобы понимать, относятся ли, например, договор и акт к одному и тому же кейсу оказания услуг.
Как мы классифицировали и сверяли документы
Из-за специфики, упомянутой выше, процесс проверки участника на соответствие критерию был реализован в два этапа.
Классификация
На первом требовалось отсеять документы, нерелевантные для данного критерия. Например, при проверке критерия про опыт, убрать списки транспортных средств, договоры о проведении лабораторных исследований и т.д. Для этого на основе LLM был реализован бинарный классификатор, который паре (критерий, документ) ставит в соответствие метку 0 или 1, где 0 означает, что данный документ не подходит для подтверждения данного критерия, 1 — что подходит.
Для классификации используется следующий промпт:
"""
Ты специалист по подбору подрядчиков для оказания услуг следующего типа: {contract_type}
Тебе будет дано условие, которому должен удовлетворять потенциальный исполнитель и предоставленный им документ.
Может ли документ служить подтверждением того, что исполнитель соответствует условию?
Приведи краткое рассуждение. Только после рассуждения предоставь ответ в формате [ответ].
Например, [1], если документ может подтвердить выполнение условия, или [0] в противном случае.
Пиши ответ ТОЛЬКО В САМОМ КОНЦЕ и только в формате
[0] или [1].
Условие: {criterion}
Документ: {document}
"""
где criterion
— формулировка критерия оценки исполнителя, document
— собственно сам документ, предоставленный исполнителем.
Просьба привести рассуждение прежде чем выдавать ответ обусловлена тем, что decoder-only модели, к которым относится и Mistral, генерируют каждый следующий токен ответа, опираясь как на входные данные, так и на уже сгенерированную часть собственного ответа. Таким образом, имея возможность видеть собственные рассуждения, предшествующие выводу, модель принимает более обоснованное решение.
Дополнительно в качестве входных данных был введён contract_type, который определяет, какие именно услуги или работы предполагается выполнять. В данном случае, в contract_type всегда передавалось «Услуги по обеспечению питанием». Это позволило корректно проверять критерий про наличие у исполнителя опыта работы, поскольку сам критерий формулировался так: «Наличие опыта оказания услуг по предмету отбора». Изначально модель ничего не знала о предмете отбора, поэтому документы, в действительности не относящиеся к данному критерию, помечала как релевантные. Например, если на входе был договор об установке POS-систем и не было этого дополнительного контекста contract_type, то модель делала вывод, что проверяется опыт исполнителя в установке POS-систем, и отвечала 1. Менять исходную формулировку критерия, чтобы в ней уточнялся вид услуг, было нежелательно, поскольку в дальнейшем планировалось использовать систему для подбора исполнителей и на другие виды подрядов с одним и тем же набором критериев, без необходимости редактировать их.
Кроме того, исходные документы предварительно проходили суммаризацию, и в классификатор попадали уже их «краткие содержания». Обусловлено это было размером контекстного окна модели Mistral-Instruct-v0.1, которая использовалась изначально. Позже, после перехода на Mistral более новой версии с бОльшим контекстным окном, от суммаризации отказались, поскольку часто данные, необходимые модели для принятия решения, после неё терялись.
Таким образом, классификация позволила из общего перечня документов выделить те, которые имеют отношение к проверяемому критерию. В результате оценки качества получили следующие метрики:
accuracy |
precision |
recall |
F1 |
0.79 |
0.90 |
0.82 |
0.86 |
Для расчета метрик использовалась разметка, сформированная экспертом заказчика при ручной оценке исполнителей в предыдущих тендерах. Для всех участников отбора каждому критерию был поставлен в соответствие список номеров документов, относящихся к данному критерию.
Сверка
После того, как получен отфильтрованный список документов после классификатора, производится сверка документов между собой. Для этого необходимо все документы разом подать в LLM с запросом на формирование окончательного ответа, достаточно ли данных документов для подтверждения критерия.
Предположим, что нужно проверить наличие в штате компании-участника технолога общественного питания. В соответствии с процедурой оценки, используемой заказчиком, необходимо проверить наличие приказа о назначении сотрудника на должность, а также наличие диплома, подтверждающего квалификацию этого сотрудника как технолога общественного питания. Может возникнуть ситуация, когда и приказ, и диплом имеются, но относятся к разным сотрудникам. При отсутствии других документов этого будет недостаточно для подтверждения критерия. Именно для таких ситуаций было решено дать LLM видеть все документы в совокупности, с расчётом на то, что она обнаружит расхождения в ключевых сведениях, содержащихся в документах (таких как ФИО сотрудника/выпускника).
Вместо того, чтобы подавать в LLM документы целиком (или в суммаризованном виде), было решено выделить из каждого документа сами ключевые характеристики, по которым и требовалось произвести сравнение. Для каждого критерия был заранее определён перечень сведений для извлечения (здесь представлена только его часть):
{
"0" : [
"Наименование документа (договор, акт, техзадание, либо ничего из перечисленного)",
"Контрагенты (заказчик, исполнитель",
"Место оказания услуг",
"Даты оказания услуг"
],
"2" : [
"Наименование документа (ПТС, СТС, договор купли-продажи или лизинга, либо ничего из перечисленного",
"Тип транспортного средства",
"Срок эксплуатации транспортного средства",
"Категория транспортного средства"
],
"5" : [
"Наименование документа (договор, акт, либо ничего из перечисленного)",
"Контрагенты (заказчик, исполнитель договора на проведение медосмотра",
"Если документ - акт, подписан ли он главным врачом лечебного учреждения"
]
}
Далее для извлечения этих метаданных на каждом из документов строился RAG со следующим промптом.
Подробнее о RAG читайте в статье.
prompt_template = """
Извлеки из документа требуемые сведения. Отвечай максимально коротко.
Если нужно сведения не указаны в документе, отвечай None.
В ответе пиши только требуемые сведения и ничего более.
Ответ выводи только в квадратных скобках.
Сведения: {meta}
Документ: {document}
"""
Здесь input meta означает наименование сведения из перечня выше (например, «Категория транспортного средства» или «Место оказания услуг»). В document передается чанк документа, найденный векторным поиском. При поиске запросом было то же meta. Векторный поиск осуществлялся по косинусной близости векторных представлений, полученных с помощью модели e5-base.
В итоге вместо списка документов (их содержаний), потенциально подтверждающих выполнение критерия, получаем список извлеченных данных, который мог иметь, например, следующий вид:
metas = [
[«Диплом», «Иванов Иван Иванович», «Технолог общественного питания»],
[«Приказ», «Иванов Иван Иванович», «Технолог общественного питания»],
],
который затем передавался LLM для принятия итогового решения о соответствии либо несоответствии участника критерию:
prompt_template = """
Ты специалист по отбору подрядчиков для выполнения работ/оказания услуг.
Критерий отбора:
{condition}.
Для подтверждения критерия требуется следующий пабор документов:
{confirming_docs}
Участник отбора предоставил следующие документы:
{metas}
Отвечай [1], если предоставленные документы подтверждают критерий отбора.
В этом случае выведи также номера подтверждающих документов.
В противном случае, отвечай [0].
В этом случае, дай очень краткое объяснения, чего не хватает участнику отбора для подтверждения критерия.
"""
Здесь condition — то же самое, что criterion в промпте для классификатора, а confirming_docs — наименования документов, которые, согласно процедуре проверки, могут подтверждать данный критерий.
Как система показала себя в работе
Аналогично оценке классификатора, при расчете метрик для второй части пайплайна (извлечение метаданных и сверка) были использованы результаты оценки подрядчиков в тендерах прошлых лет.
Здесь важно было оценить, насколько хорошо система отсеивает документы без «пары» (например, договоры об оказании услуг без актов или дипломы сотрудников без приказов о назначении на должность). Поэтому каждому критерию был поставлен в соответствие тот же набор документов, что и в разметке для классификатора, за вычетом таких беспарных документов.
Результат оценки показал высокую эффективность системы по большинству из критериев, на которых был получен F1 ~0.82.
Исключение составили три критерия с F-мерой ~0.5: критерий о наличии опыта оказания услуг, о наличии транспортных средств и о наличии лабораторного контроля за соблюдением санитарных норм.
Анализ выходных данных дал понять, что имеются проблемы в извлечении метаданных из документов, наиболее выраженные для этих трех критериев. Вероятная причина — не лучшим образом сформулированные meta (которые передаются в промпт LLM для извлечения метаданных, а также используются в векторном поиске в качестве запроса), из-за которых часть документа, содержащая требуемые данные, находится при поиске не в top1.
Из этого вытекает вариант дальнейшего улучшения системы — использовать модель с большим контекстным окном, которое бы позволило подать на вход весь текст документа целиком с запросом на выделение нужных данных, без необходимости предварительно выполнять поиск по чанкам документа. Это бы позволило избежать ошибок на этапе поиска.
Заключение
В статье я описал, как для автоматизации бизнес-процессов мы разработали систему, использующую большую языковую модель Mistral-7B-v0.2, как она работает и с какими проблемами мы столкнулись, а также перспективы ее совершенствования. Если у вас есть подобный опыт, поделитесь, с какими трудностями вы сталкивались при автоматизации рабочих процессов и какие перспективы видите в применении больших языковых моделей в этой области. Приглашаю вас обсудить эти вопросы в комментариях.
Комментарии (2)
akakoychenko
02.12.2024 19:43Вот пытался для совершенно другой сферы писать промпты типа "по описаниям оцени вероятность от 0.0 до 1.0 того, что сущность А и сущность Б одно и тоже", "Напиши 1, если описания А и Б представляют одну и ту же сущность", и так далее, чтобы потом чуть оптимизированным декартовым произведением пометчить две базы.
Ну не работает оно адекватно. И, самое гадкое, что ну не работает оно стабильно. Чуть-чуть можно поменять что-то в тексте описания, и, скажем, скачек с 0.3 до 0.8 получить. Что внушает сильный страх перед выкатыванием такого в прод, и сильно проигрывает классическим классификаторам типа скоринга на правилах, линейной регрессии, и даже нелинейных классификаторов на фичах (ибо там можно фиче импортанс посчитать).
Вообще удивлен, что, особенно в такой сфере опасной, как тендеры (где потом могут быть подозрения в коррупции, суды, дела уголовные, и так далее, в отличии от, например, техподдержки условного авито, где можно просто послать к черту пользователя, и он ничего не сделает корпорации), вот так в лоб пытаетсь решать. Как будто бы, методы с куда большим контролем должны использоваться (например, приведение тендерной заявки к стандартизированному виду силами ЛЛМ, и последующие IF-ELSE правила поверх этого стандартизированного шаблона)
ENick
Случайный таксист как-то рассказал, что в их таксопарке тендеры выигрываются ещё до их объявления. Механизм очень простой - правильно сформулированные критерии отбора участников. Вот если бы попросить Mistral-7B-v0.2, чтобы она сформулировала эти критерии, разработала форму представления тендерной документации, а потом семантическим поиском сразу определила победителя тендера. Мечты, мечты!!!