solvi - открытая Python-библиотека под лицензией Apache-2.0, которая делит принятие бизнес-решения между языковой моделью и обычным кодом. Модель предлагает факты: цитату из документа, выбор из закрытого списка. Код проверяет эти факты и выносит вердикт. Каждый шаг попадает в след, сцеплённый хешами.
Практическая ценность в проверяемости. Когда решение уходит аудитору, регулятору или в разбор спорного случая, нужен ответ на вопрос «почему». Модель, отвечающая напрямую, такого ответа не даёт: ни правила, ни проверяемого доказательства, ни записи, которую можно воспроизвести через полгода.
В версии 0.5.0 контрактом solvi стали аннотации типов Python. Они описывают факты, вопросы и допустимые виды ответов, включая «в тексте не сказано», точный фрагмент текста и цитату-довод. Ниже разбор схемы по шагам, пример отказа от галлюцинации на авансовом отчёте и три ограничения, которые стоит взвесить до пилота. Описание библиотеки и её архитектуры автор опубликовал на Habr: «Модель предлагает, код решает: проверяемые бизнес-решения на Python с маленькими моделями».
Что такое solvi и какую проблему она решает
Почему прямые ответы LLM не подходят для бизнес-решений
Если спросить языковую модель «одобрить возврат?», она вернёт ответ и вероятность. Правило, которое гарантированно выполнится, доказательство, которое можно проверить, и запись, которую можно воспроизвести, она не вернёт. Это не техническая мелочь: вероятность не обязывает модель соблюсти инструкцию, поэтому правило, прописанное в промпте, остаётся пожеланием.
Автор библиотеки приводит показательный случай из сравнений. Модели прямо сказали: угроза судом означает высокий приоритет и очередь legal. На обращении со словом «юрист» она ответила «очеред…». Правило было, соблюдение не случилось. Разбор этой ситуации и самой библиотеки автор опубликовал на Habr: статья о solvi.
Похожая история с «решающими моделями», которые напрямую отвечают на типизированные вопросы: закрытая Jev и её открытый аналог Laya. Они быстрые, но нельзя проверить, почему они ответили именно так. Они могут перебить правило и выдумать число или цитату.
Ключевая идея: LLM извлекает факты, код принимает решение
solvi меняет роли. Модель превращает неструктурированный документ в набор фактов, которые можно проверить: цитата, число, дата, вариант из списка. Решение считает детерминированный код, и это обычные правила вида «заявке старше 30 дней отказ» или «счёт, который не сходится с заказом, не оплачивается».
Мотив автора простой. Большая часть решений внутри компании мелкие и частые: одобрить авансовый отчёт, направить обращение в нужную очередь, оплатить счёт, заморозить аккаунт. У них три общих свойства. Часть правил не обсуждается. Кто-то обязательно спросит «почему». Большая часть ответа - арифметика.
До solvi автор проверял, дают ли нейросетям реальное преимущество гиперкомплексные числа: кватернионы, октонионы и то, что выше. Результат оказался скромным: «настоящая» алгебра работала не лучше подделки, кроме случаев, где задачу покрывает точный закон, например композиция поворотов. Отсюда и вывод, который лёг в основу библиотеки: там, где есть точное правило, решать должен код.
Как устроен контракт в solvi 0.5.0: аннотации типов Python
В версии 0.5.0 контрактом стали типы Python. Аннотации описывают факты, вопросы и виды ответов, включая «в тексте не сказано», точный фрагмент текста и цитату-довод. Типы работают здесь и как схема данных, и как правила валидации одновременно.
Типы ответов: «в тексте не сказано», точный фрагмент, цитата-довод
Три вида ответов закрывают разные ситуации. «В тексте не сказано» даёт модели законный способ сообщить об отсутствии информации, вместо того чтобы достроить недостающее. Точный фрагмент текста требует указать конкретную подстроку из документа. Цитата-довод обосновывает извлечённый факт: по ней видно, из какого места документа взято число или дата.
Именно из-за третьего пункта ловится галлюцинация. Если модель назвала сумму, а цитаты, подтверждающей её, в документе нет, проверка не проходит.
Как аннотации становятся контрактом
Аннотации превращаются в вопросы к модели и в проверки для её ответов. Поле с закрытым набором значений заставляет выбрать один из вариантов, поле типа float требует число, поле с цитатой требует подстроку, которую код найдёт в исходном тексте.
Схематичный пример, который показывает принцип, а не точный API библиотеки:
class ExpenseReport:
amount: float # сумма из документа
spent_on: str # дата расхода
quote: str # фрагмент, подтверждающий сумму
verdict: Literal["одобрить", "отклонить", "не сказано"]
Модель заполняет amount, spent_on и quote. Код проверяет, что quote реально встречается в тексте, что amount совпадает с числом внутри цитаты и что spent_on укладывается в допустимый период. Значение verdict здесь результат вычисления по правилам, а не генерация модели. Такой контракт читается человеком, проверяется машиной и не теряет смысла при смене версии извлекателя.
След, сцеплённый хешами: воспроизводимость и защита от подмены
Каждый шаг работы попадает в след: запрос к модели, полученный ответ, результат проверки, применённое правило. Записи сцеплены хешами, то есть каждая содержит отпечаток предыдущей. Логика знакома по блокчейну, только без майнинга и распределённого консенсуса: тот же приём контроля целостности, применённый к журналу решений.
Практический эффект двойной. Решение можно воспроизвести через полгода и увидеть, какие факты и правила к нему привели. Правка задним числом проявляется сразу: подмена данных или смена версии модели ломает цепочку хешей, и это заметно без сверки с оригиналом.
Для аудита такой журнал полезнее, чем лог чата с моделью. Логи чата показывают текст, но не дают ответа на вопрос, почему выбран этот вариант, а не соседний. Как выглядят артефакты для разбора отдельных ответов модели, разбирается в материале про lm-eval-ledger и SQLite-хранилище прогонов.
Пример: авансовый отчёт и отклонение галлюцинации
Авансовый отчёт удобен как пример: короткий документ, есть сумма, дата и лимит, а цена ошибки понятна любому бухгалтеру. В описанной схеме галлюцинация извлекателя отклоняется проверкой привязки цитаты к смещениям, а жёсткая проверка не перебивается уверенностью модели.
Пошаговый разбор: от извлечения до решения
- Модель получает текст документа и вопрос, возвращает набор полей: сумму, дату, категорию расхода и цитату, из которой взята сумма.
- Код ищет цитату в документе по указанным смещениям. Строка должна совпасть с исходным текстом.
- Если цитаты по этим смещениям нет или она не совпадает, ответ отклоняется, и это фиксируется как ошибка извлечения, а не как решение по заявке.
- Дальше работают бизнес-правила: лимит по категории, срок подачи, допустимость расхода.
- Готовое решение уходит в след, сцеплённый хешами, вместе с фактами и сработавшими правилами.
Ключевой момент на третьем шаге. Модель могла уверенно назвать сумму, и вероятность при этом высокая. Проверка всё равно отклонит ответ, потому что подтверждения в тексте нет. Автор описывает этот сценарий в статье о библиотеке на Habr: «Модель предлагает, код решает».
Почему уверенность модели не может перебить проверку
Решение в solvi считает код, и он опирается только на проверенные факты. Уверенность модели попадает в журнал как метаданные, но в вердикте не участвует. Если факт не подтверждён, статус остаётся «не подтверждено», независимо от того, насколько модель была убеждена.
Это переворачивает привычную логику ретраев. Обычно низкую уверенность отправляют на переспрос к модели, а высокую принимают. Здесь порог задаёт не модель, а проверка: либо цитата найдена, либо нет.
Ограничения и подводные камни solvi
Три ограничения названы прямо, и их стоит учитывать до пилота. Автор библиотеки перечисляет их вместе с разбором архитектуры в статье на Habr: «Модель предлагает, код решает: проверяемые бизнес-решения на Python с маленькими моделями».
Необходимость размеченных данных для новых полей
Каждому новому полю нужны размеченные примеры. Если добавляете извлечение кода проекта или номера договора, готовьте набор документов, где это поле размечено верно. Без таких примеров качество извлечения предсказуемо проседает, и это не лечится одной правкой промпта. Разметка становится регулярной работой при каждом расширении контракта.
Потеря точности при int8 на CPU
Квантование в int8 позволяет запускать извлекатель на CPU, но точность при этом падает. Для задач, где важна каждая цифра в сумме, экономия на железе оборачивается ошибками извлечения, которые потом ловит проверка цитат. Часть документов будет уходить в отказ, и это ещё не худший сценарий: хуже, когда неверное число попадает в корректную по смещениям цитату.
Некалиброванная уверенность между доменами
Сырая уверенность решателя не калибрована между доменами. Значение 0,9 на счетах и 0,9 на заявлениях сотрудников означают разное, поэтому сравнивать их напрямую нельзя. Если хотите использовать порог уверенности как фильтр, его нужно подбирать на своих данных. Как это выглядит на практике с тысячами вызовов и подбором порогов отбрасывания, разобрано в материале про 16 000 вызовов Jev на открытых и внутренних данных.
Общий вывод из ограничений: за проверяемость платят разметкой и вниманием к железу. Проверка цитат страхует от подмены, но не от того, что модель уверенно извлечёт не тот фрагмент.
Сравнение с альтернативами: Jev, Laya и прямые ответы LLM
Jev и Laya решают задачу иначе: они напрямую отвечают на типизированные вопросы и выдают класс или значение без генерации текста. Jev закрытая, Laya открытая. Обе быстрые, но проверить, почему выбран именно этот ответ, нельзя, и правило они могут перебить. Как устроены примитивы Choice, Noulli и Score в System One модели, разобрано в материале про Jev от TypeSafe AI, а про оркестрацию таких решений вместе с графом состояний - в статье про Jev и LangGraph.
Прямые ответы LLM через промпт проигрывают по тем же причинам, плюс добавляется зависимость от формулировки запроса: правило живёт внутри текста инструкции, и его соблюдение держится на вероятности, а не на исполнении кода.
solvi занимает третью позицию. Скорость модели сохраняется, но вердикт считает код, поэтому выигрыш проявляется там, где правила перечислимы, а факты можно привязать к фрагментам документа. Там, где решение строится на свободном суждении, разделение труда не помогает: проверять будет нечего.
Кому и когда стоит использовать solvi
Подходит для частых мелких решений с формальными правилами: одобрение авансовых отчётов, маршрутизация обращений, оплата счетов, заморозка аккаунтов. Если регулятор или внутренний аудит требует обосновать решение, хеш-след закрывает заметную часть этой потребности, а проверка цитат убирает класс ошибок с выдуманными числами.
Не подходит, когда решение строится на суждении без чётких правил: оценка нестандартного договора, разбор конфликтной ситуации с клиентом, выбор приоритета между противоречащими целями. Отложить внедрение стоит и в случае, если нет ресурсов на разметку данных под каждое новое поле.
Как начать работу с solvi: установка и первый пример
- Убедиться, что под задачу есть перечислимые правила. Если правила нельзя записать в коде, схема потеряет смысл.
- Собрать 20-50 документов и разметить факты, которые нужно извлекать.
- Описать контракт классом с аннотациями типов: факты, вопрос, вид ответа, цитата.
- Подключить извлекатель. Подойдёт локальная LLM; при запуске на CPU учесть потерю точности при int8.
- Прогнать набор и посмотреть, на каких документах проверка цитат отклоняет ответ. Это самый полезный сигнал на старте.
- Сверить актуальные команды установки и рабочие примеры в репозитории проекта: библиотека распространяется как открытый Python-пакет под Apache-2.0, и версия 0.5.0 соответствует именно этому состоянию контракта.
Первый прогон даст понятную метрику: долю документов, где факты подтверждаются цитатами. Она важнее, чем средняя уверенность модели, потому что показывает, сколько заявок дойдёт до правил, а сколько уйдёт на ручной разбор.
Итог: стоит ли внедрять solvi в продакшн
solvi предлагает рабочую схему для случаев, где нужны аудит и соблюдение правил: модель извлекает факты, код считает решение, хеш-след делает результат воспроизводимым. Версия 0.5.0 уже даёт контракт на типах Python, но требует разметки под новые поля и внимания к точности извлечения на CPU.
Разумный следующий шаг: взять один процесс с формальными правилами, собрать небольшой размеченный набор и проверить, сколько ответов отклоняет проверка цитат. Если доля отказов окажется приемлемой, схему можно переносить на соседние процессы, начиная с самых частых.