Роботи / Агенти і ботиробота на відкритий бриф

ShopGuard: сторож чесності для чат-ботів магазинів на відкритих моделях

Що було

Магазини ставлять LLM-ботів за кнопку «чат», і ті обіцяють те, чого магазин не обіцяв: ціну не з каталогу, 12 платежів замість трьох, «в наявності, відправимо сьогодні» для товару, що скінчився тиждень тому. Покупець вірить, магазин або платить за чужу обіцянку, або втрачає покупця.

Що зробили

Поставили між ботом і покупцем сторожа. Мала модель витягує обіцянки з відповіді, звичайний код звіряє ціни, наявність, розстрочку і доставку з даними магазину, велика модель перевіряє характеристики через веб-пошук і переписує відповідь лише з підтверджених фактів. Переписане проходить ту саму перевірку ще раз, і якщо знову не проходить, покупцю йде ввічлива передача менеджеру.

Що отримав клієнт

Живе демо, відкритий код, відео 3:20 і письмовий відгук на шлюз із сімома відтворюваними проблемами, одну з яких команда виправила наступного дня. На 49 запитах покупців вигадані ціни, розстрочки і наявність дійшли до покупця 0 разів замість 3 у звичайного бота і 8 у нав'язливого. Чесні відповіді менеджеру не віддавались.

ЗамовникMozilla, відкритий бриф Developers Wilding на шлюзі Otari
Моя рольІдея, код сторожа, оцінка на 49 запитах, демо на Vercel, відео, письмовий відгук на Otari, звіт про баг
СтатусДемо і відео готові, здача до 11.11.2026, підсумків ще немає
Результат0 з 49 вигаданих обіцянок дійшло до покупця, до сторожа було 8 з 49 у нав'язливого бота · Відкрити
Відхилено: чернетка бота. Ціна 15 999 замість 18 999, 12 платежів замість 2-3, доставка сьогодні, якої немає в політиці, і Wi-Fi, що не підтверджується. До покупця не дійшла
Відхилено: чернетка бота. Ціна 15 999 замість 18 999, 12 платежів замість 2-3, доставка сьогодні, якої немає в політиці, і Wi-Fi, що не підтверджується. До покупця не дійшла
Прийнято: відповідь після переписування. Ціна з каталогу, розстрочка за політикою, доставка 1-3 дні, а про Wi-Fi чесне «перевірю з менеджером»
Прийнято: відповідь після переписування. Ціна з каталогу, розстрочка за політикою, доставка 1-3 дні, а про Wi-Fi чесне «перевірю з менеджером»

Завдання

Mozilla відкрила бриф для розробників на шлюз Otari: взяти свій проєкт, перевести його на відкриті моделі через шлюз, використати його функції по-справжньому і написати відгук, що працює, а що ні. Оцінюють не красу слайдів, а реальне використання і корисність зворотного зв’язку.

Наш проєкт був готовий заздалегідь, але на заглушці замість моделі: сторож чесності для ботів магазинів. Ідея виросла з нашого ж бенчмарку, опублікованого на Kaggle, який міряв, як часто асистенти магазинів вигадують ціни, розстрочки і наявність. Бенчмарк лише міряв. ShopGuard мав зупиняти це на ходу.

Навіщо і яка ідея

Поділ праці між кодом і моделями. Ціни, наявність, правила розстрочки і доставки магазин і так знає, тому їх перевіряє звичайний код: точно і безплатно. Моделі роблять те, що потребує мови: знаходять обіцянки у вільному тексті, українською, російською, англійською або впереміш, і судять про докази з вебу.

Два екстрактори замість одного. Nemotron Nano перелічує обіцянки, а поруч регулярний вираз незалежно ловить кожну суму грошей і кожну кількість платежів. Якщо модель пропустила ціну, перевірку все одно отримає регулярка. Відмова в самій відповіді («12 платежів не надаємо») обіцянкою не вважається.

Переписування не довіряє самому собі. Модель, яку просять виправити відповідь, може додати нову помилку, тому переписане проходить ті самі перевірки ще раз, без нових звернень до вебу. Поле draft в API приймає відповідь від бота, який уже працює, тож сторож стає перед наявним ботом без його заміни.

Хід роботи

Коли Що відбувалося
День 1, ранок Реєстрація листом, ваучер на баланс шлюзу. Перший коміт з Otari: новий base URL, ключ і назви моделей. Клієнт уже говорив форматом OpenAI, тому перехід був малим
День 1, ранок Перша жива оцінка на Nano і Super, 49 запитів: до сторожа 8 з 49 з вигадками, після 1 з 49, чесних заблоковано 0 з 12, витрати 0,007 $. Обхід JSON-режиму, який Bedrock не підтримує
День 1, день Продавець демо переведений на Gemma 3 27B, бо в Nano ламана українська. Сторож перестав приймати відмову за обіцянку. Персона «нав’язливий продавець» як стрес-тест: 10 з 49 до сторожа, 0 після, але 13 передач менеджеру, 6 з них на чистих чернетках
День 1, день Веб-пошук Otari увімкнений, суддя шукає сам через вбудований інструмент. Пошук за кодом моделі знаходить профілі компаній і фінансові новини, у стрес-прогоні не підтвердив жодної характеристики. Записано у відгук
День 1, вечір Фолбек Nano на Super у коді, стелі витрат у панелі, 403 понад ліміт перевірений вживу: демо перемикається на записані прогони. Публічний репозиторій, демо на Vercel, відгук із сімома проблемами, відео v1
День 1, ніч Знайдений відтворюваний баг веб-пошуку, 400 у 3 з 4 однакових викликів. Issue в репозиторії Otari. Ревʼю тексту і відео, версія v2 із живим записом екрана
День 2 Команда Otari підтвердила баг і закрила його виправленням. Дописано в README і відгук. Лист-здача підготовлений, відправка за розкладом

Чим робили

Python і FastAPI, усі виклики моделей через один шлюз api.otari.ai з одним ключем робочого простору. Три відкриті моделі у трьох ролях: Gemma 3 27B пише відповідь демо-продавця, Nemotron 3 Nano витягує обіцянки, Nemotron 3 Super судить докази з вебу і переписує. Вартість кожного виклику береться з usage.cost_usd шлюзу і показується під кожним результатом, разом із платою за інструменти; свою таблицю цін і лічильник витрат ми видалили.

Super має чотирьох провайдерів за одним ідентифікатором, тож шлюз може перемкнутись, коли провайдер падає. У наших прогонах кожен виклик пішов на Bedrock і перемикання не траплялось, заголовки Otari-Provider і Otari-Fallback пишемо в лог. Зверху свій стрибок з Nano на Super, коли мала модель мовчить 20 с або віддає битий JSON: шлюз це помилкою не вважає. Якщо впали обидві, відповідь не йде неперевіреною, покупець отримує передачу менеджеру.

Витрати обмежені на шлюзі: 15 $ на всю роботу, окремі ключі з денною стелею 0,50 $ для публічного демо і 2 $ для суддів. У коді залишились ліміт на IP, щонайбільше 2 веб-перевірки на відповідь, кеш вердиктів і записані прогони, що відтворюються миттєво, коли живий бюджет вичерпано.

Оцінка eval/run_eval.py ганяє 49 запитів покупців, 40 з них українською, російською і впереміш з нашого бенчмарку, 9 нових англійською, і скорить чернетку бота й відправлену відповідь незалежним скорером за шаблонами, а не власним екстрактором сторожа.

Результат

Живе демо на Vercel: питання магазину будь-якою мовою, під результатом чернетка бота і те, що пішло покупцю, список порушень із цитатами і вартість у центах. Демо-магазин «KitchenHub» вигаданий, ціни умовні, моделі товарів справжні, щоб веб-перевірці було що шукати.

Живі прогони на 49 запитах. Звичайний продавець: вигадані ціна, розстрочка чи наявність у 3 з 49 чернеток, у відповіді покупцю 0 з 49, дії сторожа 43 пропущено, 4 переписано, 2 передано менеджеру, весь прогін 0,03 $. Нав’язливий продавець: 8 з 49 до сторожа, 0 після, 27 пропущено, 13 переписано, 9 передано, 0,44 $. Чесних відповідей про ціну і наявність менеджеру не віддано жодної, чистих чернеток передано 3, усі у стрес-прогоні. Більшість витрат другого прогону йде на веб-перевірку: нав’язливий бот сипле характеристиками, і кожна непідтверджена коштує один виклик Super із пошуком.

Відгук на шлюз: сім проблем із кроками відтворення, від неправильної адреси у швидкому старті до ключа, що помер без пояснення. Баг із веб-пошуком команда підтвердила того ж дня і закрила виправленням наступного.

Чого ми поки не знаємо: як бриф оцінить роботу. Здача за розкладом, підсумків ще немає.

Помилки і що винесли

Перша платформа для цього проєкту не відкрила білінг для України.День роботи на заглушці пішов у стіл. Тепер країну перевіряємо на всіх ланках до старту, не лише в правилах, а й в оплаті. Код не пропав: саме він ліг в основу цієї роботи.
Відмова рахувалась обіцянкою.«10 платежів не надаємо» сторож брав як обіцянку 10 платежів і переписував чесну відповідь. Заперечення в самій відповіді тепер обіцянкою не є, і скорер це теж враховує.
Веб-пошук за кодом моделі знаходить не ті сторінки.На «Bosch WAN24263» прийшли корпоративні сторінки і фінансові новини, на код холодильника відділення банку. Непідтверджені характеристики тепер прибираються з відповіді, а в відгуку просимо керований пошуковий запит.
Малий ліміт пошуків ламав виклик.З max_uses 1 або 2 запит на Bedrock падав у 3 з 4 випадків. Тримаємо 3 і повтор із більшим лімітом, баг віддали команді з кроками відтворення, вона виправила.
Ключ помер між двома GET-запитами.Працював о 08:25, 401 о 08:35, без жодної дії в панелі. Причину не знайшли. Ключі тримаємо в Зв'язці, перевипуск кладемо туди напряму, а не через чат.
Каталог шлюзу обіцяв JSON-режим, провайдер ні.Nano на Bedrock відповідає 400 на response_format. Повторюємо один раз без нього і читаємо JSON із тексту.

Інструменти

PythonFastAPIOtariGemma 3 27BNemotron 3 NanoNemotron 3 SuperVercelpytestElevenLabsClaude Code