Воспроизводимость
docs.stripe.com · аудит 2026-07-27 · AgentFit v3.4.0 · рубрика v4 · Claude Opus 5
Оценка, которую нельзя повторить, — это не измерение. Поэтому вот проверка, которую мы обязаны показать: один и тот же сайт мы прогнали через аудит шесть раз за один день — три раза попросив языковую модель применить рубрику к документации и три раза запустив AgentFit. Публикуем то, что записали, включая то, что неудобно нам самим.
Спросить модель · 3 прогона
78 · 80 · 65 15 баллов разброса
Три независимых агента, один и тот же публичный промпт, одна модель, один день. Ни один не знал ни об остальных, ни о том, зачем его спрашивают.
Запустить AgentFit · 3 прогона
57 · 57 · 57
0 баллов разброса
eabbb2f6abcdea473b600dc4ee8e25282fd002344f1e8425519caa32ff858b01
одна sha256 на три файла
Три живых аудита подряд, три JSON-файла, одна контрольная сумма. Это не кэш: каждый прогон заново ходил на сайт.
Читайте колонки сверху вниз, а не поперёк. Слева рубрика на 26 критериев, у AgentFit — 28, и веса у них разные: машиночитаемая спека стоит 25 баллов слева и 17 здесь. 57 — не «правильный ответ», а 78 — не «неправильный». Мы утверждаем меньше — и только это подтверждается данными: одна из двух процедур выдаёт одно и то же число каждый раз, вторая — нет.
Где прогоны разошлись
Все три прогона модели работали добросовестно: ходили по живым адресам, разбирали критерии по одному и докладывали то, что нашли. По нашему собственному подсчёту в транскриптах — которые мы не публикуем — каждый прогон сделал от 36 до 47 обращений к инструментам, и ни один ничего не выдумал. И то и другое считайте нашим прочтением прогонов, а не тем, что можно проверить по выложенным ниже файлам. И всё равно они разъехались на 15 баллов, из которых одиннадцать пришлись на одну категорию.
| Категория | Макс. | A | B | C | Разброс |
|---|---|---|---|---|---|
| A — Обнаружение | 18 | 13 | 13 | 12 | 1 |
| B — Артефакты страницы | 22 | 12 | 13 | 12 | 1 |
| C — Машиночитаемая спека | 25 | 20 | 21 | 10 | 11 |
| D — Содержание | 20 | 18 | 19 | 18 | 1 |
| E — Рендеринг и гигиена | 15 | 15 | 14 | 13 | 2 |
| Итого | 100 | 78 | 80 | 65 | 15 |
Здесь смешаны расхождения двух сортов. Мелкие — A5, B3, B5, B6, D1, D3, D6, по баллу туда-сюда, в обе стороны — это то, чего мы и ждали бы от трёх внимательных читателей одной страницы: один нашёл вкладки SDK прямо в DOM, а двое решили, что они существуют только внутри JS-выдачи. Обычный ли это шум измерения — по трём прогонам сказать нельзя, тут мы гадаем. В опубликованных сводках видно только расхождение по D1, остальное — из нашего собственного сравнения трёх транскриптов. Крупное расхождение устроено иначе — ради него страница и написана.
Остальные критерии категории C во всех трёх прогонах дали ровно 10 баллов. Весь одиннадцатибалльный разрыв — это два критерия.
C1a: одни и те же факты, три разные оценки
Критерий C1a — он из 26-критериальной рубрики модели, у AgentFit обнаружение и валидность слиты в один C1 — спрашивает, есть ли у API машиночитаемая спека, до которой агент может дойти. Все три прогона установили про docs.stripe.com ровно одно и то же:
- конвенциональные адреса — /openapi.json и родня — отдают 404;
- валидная и полная OpenAPI-спека опубликована в GitHub-репозитории Stripe.
Никто не ошибся. Никто не галлюцинировал. Оценки: 0 из 8, 3 из 8 и 4 из 8.
| Критерий | A | B | C |
|---|---|---|---|
C1a · spec discoverable | 3/8 | 4/8 | 0/8 |
C1b · spec valid | 7/7 | 7/7 | 0/7 |
E5 · terms of use | 3/3 | 2/3 | 1/3 |
Разошлись не в сайте, а в вопросе, которого рубрика не задала: считается ли спека опубликованной, если она лежит на чужом домене и до неё нужен лишний переход? Это настоящая развилка без очевидного ответа, и каждый прогон подходил к ней в одиночку, посреди длинной задачи, без правила, в которое можно заглянуть. Три прогона — три политики.
Дальше расхождение идёт каскадом. C1b оценивает качество найденной спеки. Прогону, который решил, что GitHub не считается, валидировать было нечего — 0 из 7. Два других её скачали и поставили 7 из 7, но насчитали разное число путей — 414 и 136: в репозитории Stripe лежит не один файл спецификации, и они взяли разные (spec3.json и spec3.yaml). Два прогона сошлись в оценке по противоположным причинам.
E5 — «указаны ли условия использования» — вышел 3/3, 2/3 и 1/3. Один прогон нашёл в футере ссылку на stripe.com/legal/ssa. Второй эту ссылку не зачёл и зачёл вместо неё директиву Content-Signal в robots.txt. Третий поискал /terms, /legal и /privacy на домене документации, получил три 404 и поставил по факту. Три защитимых прочтения одной строчки рубрики.
Самопроверка тоже разъехалась
В промпте есть калибровочный якорь — таблица ранее измеренных сайтов, в которой Stripe стоит на 56, замер от 13 мая 2026 года. Он там именно для того, чтобы ловить дрейф. Из-за этой же таблицы сайтом стал docs.stripe.com: Stripe уже стоял в чужом калибровочном якоре до того, как мы что-либо запускали, — сайт выбран не нами и не под результат.
Якорем воспользовались все три прогона. Все три заметили своё отклонение — +22, +24, +9. Все три после этого перепроверили критерии, порассуждали о том, что могло измениться с мая, и пришли к выводу, что якорь устарел, а собственное число вернее. Трижды, независимо, в одну сторону — и с тремя разными числами.
Вот это место кажется нам самым поучительным. Это не небрежность — так выглядит добросовестная работа, когда в процедуре остался свободный параметр. Самопроверка, вердикт которой меняется от прогона к прогону, — не самопроверка.
Якорь стоит в одном балле от 57 у AgentFit. Не делайте из этого выводов: это ручной замер, по другой рубрике и от другого месяца, и он не доказывает, что 57 — истинная оценка.
Почему код отвечает одинаково
Ничего хитрого. Каждый критерий — это запрос плюс правило, и оба записаны до того, как аудит начался.
| Критерий | Результат | Что запрошено | Что записано |
|---|---|---|---|
| A1 · индекс llms.txt | присутствует 3/3 | GET /llms.txt → 200 | Stripe Documentation |
| C1 · поиск спеки | отсутствует 0/8 | фиксированный список адресов, ответа нет | — |
| D2 · реалистичность примеров | присутствует 5/5 | выбранная страница эндпоинта → 200 | placeholder_ratio = 0.00, 0/3 blocks |
| E6 · доступность | частично 1/4 | главная → 200 | a11y: 3 violations (e.g. button-name) |
D2 — это как раз про машинное обучение. Внутри аудита работают два небольших классификатора: один оценивает, насколько реалистичен пример кода, второй — полноту страницы эндпоинта. Это ONNX-графы с замороженными весами, вкомпилированные в бинарник. Один и тот же вход даёт тот же тензор и тот же float — всегда. Детерминизм не требует отказа от моделей; он требует отказа от моделей, чей ответ нельзя закрепить.
C1 — ровно та развилка, которую три прогона прошли по-разному, и AgentFit ставит здесь 0 из 8. Наше правило — не «GitHub не считается», а фиксированный список мест, куда мы смотрим: семнадцать конвенциональных путей на аудируемом хосте, ссылка api-catalog по RFC 9727 и любая ссылка на спеку, найденная на запрошенной нами главной. На docs.stripe.com ни один из этих путей спеку не отдал, а на этой странице такой ссылки не было — значит, ничего не найдено. Правило, которое прошло бы на переход дальше, в репозиторий вендора, не было бы менее верным. Наше просто закреплено до начала аудита, одинаково применяется к каждому сайту корпуса, а его шкала опубликована в рубрике. В этом вся разница — не в лучшем ответе, а в закреплённом. Как считается C1.
Сверьте с живым прогоном
Репозиторий закрыт, готовых сборок мы не выкладываем, — поэтому «соберите и запустите» вы здесь не увидите: сделать это нельзя. Проверить можно другое, и лучше сказать точно что именно. Файлы выше записала консольная сборка на нашей машине 27 июля 2026 года. Публичный сервис — другая сборка, работающая в другом месте, и по тому же сайту он выдаст вам свой отчёт. Если процедура такая, как мы утверждаем, эти два отчёта совпадут по всем баллам. Ниже — как это сравнить и чего такое совпадение стоит.
Ничего не запуская (квота не тратится)
В публичном датасете у каждого хоста лежит идентификатор последнего валидного аудита. Возьмите его для Stripe, вычитайте этот прогон из API, сведите оба документа к полям с оценками и сравните:
$ RUN=$(curl -s https://agentfit.dev/dataset.csv \
| awk -F, '$1=="docs.stripe.com" && $3=="true" {print $7; exit}')
$ curl -s https://agentfit.dev/static/reproducibility/agentfit-run1.json \
| jq -S '{total_score, max_score, categories,
criteria: [.criteria[] | {id, status, score, max}]}' > ours.json
$ curl -s https://agentfit.dev/api/public/audit/$RUN \
| jq -S '.report | {total_score, max_score, categories,
criteria: [.criteria[] | {id, status, score, max}]}' > theirs.json
$ diff ours.json theirs.json
27 июля 2026 года эта команда не вывела ничего: 200 строк, ни одного расхождения. Две разные сборки, две разные машины, одни и те же 28 вердиктов.
Запустить самому (один раз в сутки на сайт)
Вставьте https://docs.stripe.com в форму на главной. Когда прогон закончится, в адресе будет /r/<идентификатор>. Подставьте его вместо $RUN — команды те же. Стоит понимать, что это добавляет, а что нет: аудит всё равно уходит с наших серверов, так что ваши здесь запрос и момент времени, но не точка выхода.
Один аудит одного сайта с одной сети в 24 часа. Вторая попытка вернёт 429 и никакого идентификатора прогона — специально, чтобы сервисом нельзя было долбить чужую документацию. Если упёрлись в квоту, предыдущий способ вообще ничего не требует отправлять. У MCP-сервера квота та же: попросить агента переаудитить тот же сайт в тот же день — та же стена. А перед этой стеной стоит вторая, и она уже вообще не про вас: два аудита одного хоста за десять минут, чьи угодно, съедают общий запас, и следующий получит 429 с кодом rate_limited, а не site_quota. Эта рассасывается сама за десять минут.
Без curl и jq
Откройте agentfit-run1.json в одной вкладке, а страницу отчёта /r/<идентификатор> — в другой. Всё решают семь чисел: шесть категорий и итог. Они напечатаны прямо здесь, так что сверить можно и не открывая файл.
| Категория | Балл | Макс. |
|---|---|---|
| A — Обнаруживаемость | 11 | 14 |
| B — Элементы страницы | 11 | 21 |
| C — API-контракт | 4 | 17 |
| D — Контент | 16 | 23 |
| E — Рендеринг и гигиена | 15 | 21 |
| F — Агентские возможности | 0 | 4 |
| Итого | 57 | 100 |
Три вещи разойдутся — и должны
- auditor — метка сборки, которая записала отчёт. У нас там agentfit/v3.4.0, потому что такой была локальная сборка в тот день; у свежего отчёта будет то, что сейчас развёрнуто. Это имя бинарника, а не измерения, — поэтому сравнение выше его и выбрасывает.
- evidence_snippet — те самые слова, которые сайт отдал машине, пришедшей за ними. Наши три прогона уходили с адреса, которому Stripe отвечал по-немецки; прогон сервиса получил английский. 27 июля этим расхождение и исчерпывалось: у B1, B3 и E1 разный текст при одинаковом URL, одинаковом коде ответа и одинаковом балле. Фрагмент фиксирует вход, а не вердикт.
- Порядок ключей. Наши файлы — это то, что записал инструмент; API отдаёт тот же объект после круга через Postgres, а тот хранит ключи от коротких к длинным. Побайтово сравнивать эти документы бессмысленно, и к баллам это отношения не имеет: jq -S приводит оба к одному порядку — только поэтому diff и что-то значит.
Совпадение означает ровно одно: та же процедура на том же сайте выдала тот же вердикт из другой сборки и с другой машины. Оно не означает, что 57 — правильная оценка; этого страница нигде не утверждает. И оно завязано на сам сайт: если docs.stripe.com изменился с 27 июля 2026 года, числа поедут — и это прибор работает, а не ломается. Прежде чем делать выводы из расхождения, посмотрите дату audited_on в строке датасета.
Эти файлы заморожены, и к каждому релизу мы их не перевыпускаем. Причин две: контрольная сумма на этой странице снята ровно с этих байтов, а все числа в тексте выше вычитаны из них — самообновляющийся экспонат не может ни того, ни другого. Сигнальная нить не внутри замороженного файла, а на живой стороне: колонка 9 той строки датасета, которую вы вычитали выше, — это rubric_version, и пока эта страница висит, там должна стоять 4. В самом экспонате этой колонки нет: он старше её. Значит сверять придётся с числом, напечатанным в этом предложении, — гарантия слабее, и именно поэтому число выписано словами. Если однажды в колонке окажется другое, значит линейку сменили уже после того, как страница была написана: сравнение по полям недействительно, а перед вами исторический документ. Пусть страница скажет это вслух, чем тихо перезапустит себя до совпадения.
Два критерия из 28 — D2 и C3 — решает не правило, а небольшой классификатор. Оба вкомпилированы в бинарник и версионируются вместе с ним: модельного сервиса нет, и ничьё переобучение не сдвинет уже опубликованный балл.
Чего мы не утверждаем
- Мы не утверждаем, что 57 — правильно. Мы не публикуем истинную оценку docs.stripe.com, потому что её ни у кого нет. Мы публикуем процедуру и её результат. И воспроизводимость — условие необходимое, а не достаточное: команда echo 57 тоже прекрасно воспроизводима, только она ничего не знает о сайте. Чувствительна ли к сайту наша процедура — судите по /browse: там та же самая процедура разносит реальные сайты по всему диапазону.
- Мы не сравниваем два числа. Рубрики разные: другое количество критериев, другие веса. Сравнение осмысленно внутри каждой колонки, а не между ними.
- Это один сайт, один день, одна модель, три прогона. Это демонстрация, а не исследование, и регулярно её перезапускать мы не обещаем.
- Модель — Claude Opus 5, та же самая, что помогала делать эту страницу и заметную часть сервиса. Мы показываем разброс, который воспроизвели в инструменте, которым сами пользуемся каждый день, — держите это в уме. Мы не говорим, что языковые модели для этого плохи. Мы говорим, что не смогли добиться от модели одного и того же числа дважды.
- Детерминизм — свойство процедуры, а не обещание про интернет. Наши три прогона уходили с одной машины в один день, и сайт отдавал на этот адрес немецкие тексты — это видно в evidence у B1 и E1, и один из прогонов модели зафиксировал то же самое. Сменится точка выхода — сменится вход. Гарантия ровно одна: одинаковый вход — одинаковый выход.
В чём модель здесь действительно хороша
Всё это не делает прогоны модели бесполезными — оно делает их другим инструментом. Диагноз в каждом из трёх прогонов хорош сам по себе: почему отсутствие спеки бьёт по агенту, что стоит опубликовать вместо этого, какие страницы Stripe переживут чтение без браузера. Это объяснение — и объясняет модель прекрасно.
Языковая модель отлично объясняет, что чинить. Как измерительный прибор она годится плохо. Мы — про второе. AgentFit и есть прибор; отнесите его число модели и спросите, что с ним делать. Вот правильное разделение труда, и мы сами работаем именно так.
Данные
Все шесть прогонов лежат прямо здесь. Три отчёта AgentFit — это ровно те файлы, которые записал инструмент, байт в байт: контрольная сумма снята с них. Три файла прогонов модели — наши:
Сырые транскрипты мы не публикуем. По колонке модели вы получаете структурированные сводки, которые мы составили постфактум по этим транскриптам: итоговый балл, суммы по категориям и покритериальные заметки, процитированные выше. Баллы и суммы — собственный результат каждого прогона; формулировки заметок — наши.
- три отчёта AgentFit и контрольная сумма —
run1.json·run2.json·run3.json·sha256.txt - три прогона модели — сводки, которые мы составили по транскриптам —
runA.json·runB.json·runC.json - промпт, по которому шла колонка модели, на том коммите, который мы запускали —
ai-ready-audit
Ничего не отсеивали — ни прогон, ни сайт. Три аудита модели и три аудита инструмента, на том единственном сайте, который уже был назван в калибровочной таблице промпта; других мы не прогоняли и ничего не выбрасывали. Если бы четвёртый прогон дал 57, мы бы опубликовали и его — страница получилась бы короче.
Бесплатно, без регистрации, около 30 секунд. 28 критериев, HTTP-доказательство под каждым, ссылка на отчёт — и то же самое число завтра, если ничего не изменилось.