Экономика агентов · Воспроизводимый тест

GitHub Copilot или прямой API: считаем реальную стоимость одной задачи

Уровень: продвинутый Время чтения: 12 минут Результат: сравнение стоимости, токенов, времени и доли завершённых задач

Самый дешёвый запрос к модели не обязательно даёт самую дешёвую закрытую задачу. Готовый coding-агент берёт фиксированную плату или расходует включённую квоту, зато уже умеет читать репозиторий, изменять файлы, запускать команды и удерживать рабочее состояние. При прямом доступе к модели оплачиваются запросы, но поиск файлов, цикл исправлений, ограничения инструментов и журналирование приходится строить самостоятельно. Ниже — протокол, который позволяет сравнить эти варианты на собственных задачах и получить измерения вместо рекламных предположений.

Что именно сравниваем

Сторона A — доступная вашей команде конфигурация GitHub Copilot, используемая как готовая среда для работы с репозиторием. Сторона B — прямые обращения к выбранной модели через API плюс минимальная управляющая программа, которая передаёт файлы, применяет изменения и запускает проверки.

Сравнивать только цену подписки с ценой одного модельного ответа нельзя. У двух вариантов разная структура расходов:

Компонент Готовая среда Прямой API
Доступ к модели Подписка, квота или иная фактическая схема вашего плана Входные, выходные и другие тарифицируемые единицы
Работа с файлами и командами Встроена в используемую среду либо ограничена её возможностями Стоимость разработки и поддержки собственного контура
Время человека Подготовка задачи, подтверждения, исправления То же плюс обслуживание управляющего кода
Неудачные запуски Расходуют время и доступную квоту Расходуют время и оплачиваемые вызовы

Итоговая метрика — не стоимость запуска, а полная стоимость одной принятой задачи. Дополнительно нужны расход токенов, календарное время, активное время оператора и доля успешных задач.

Правильная единица стоимости

Для каждого подхода считайте два показателя:

стоимость_попытки =
  переменные_расходы_модели
  + доля_фиксированных_расходов
  + стоимость_инфраструктуры
  + активное_время_человека × внутренняя_ставка

стоимость_принятой_задачи =
  сумма_расходов_всех_попыток / число_принятых_задач

Первая формула отвечает на вопрос, сколько стоил конкретный запуск. Вторая учитывает провалы. Если дешёвая схема требует повторить половину задач вручную, цена успешного результата будет заметно выше цены одного вызова.

Фиксированную месячную плату распределяйте по фактически завершённым задачам соответствующего периода:

доля_подписки_на_задачу =
  месячная_стоимость_места / число_принятых_coding-задач_за_месяц

Показывайте результат в двух вариантах: без стоимости труда и с ней. Первый полезен для анализа счёта поставщика, второй — для решения бизнеса. Не объявляйте включённый в подписку запуск бесплатным: его предельная цена может быть нулевой, но средняя цена места остаётся ненулевой.

Конкретный тестовый кейс

Используйте небольшой репозиторий, который можно легально копировать и сбрасывать. Задача должна требовать не только генерации текста, но и работы с несколькими файлами. Например:

В функции разбора CSV пустое поле amount ошибочно превращается в ноль. Сохрани пустое значение как null, не меняй обработку корректных чисел, добавь регрессионные тесты и обнови краткое описание поведения. Не изменяй публичные имена функций. Задача принята, если исходные и новые тесты проходят, а diff не содержит несвязанных изменений.

Это конкретное условие, а не результат испытания. Подготовьте собственный дефект с однозначным ожидаемым поведением и закройте эталонное исправление от обеих систем. Оно понадобится проверяющему, но не должно попадать в доступный модели контекст.

Один кейс показывает механику, но не позволяет выбрать платформу. Для рабочего решения соберите eval-набор минимум из нескольких типов задач: локальный баг, изменение в нескольких модулях, рефакторинг без смены поведения, тесты для существующего кода и исправление ошибки сборки. Используйте реальные обезличенные задачи или специально подготовленные эквиваленты.

Подготовка честного эксперимента

  1. Зафиксируйте один commit исходного состояния и создавайте каждый запуск из его чистого клона.
  2. Используйте одинаковый текст задачи, одинаковые тесты, лимит времени и доступ к документации.
  3. Запретите обеим сторонам видеть эталонный патч и результаты предыдущих запусков.
  4. Заранее определите число повторов и не останавливайте неудобные запуски выборочно.
  5. Сохраните версии среды, расширений, управляющего кода и выбранной модели, если она раскрывается.
  6. Чередуйте порядок A/B, чтобы усталость оператора и кэш не помогали одной стороне.

Подготовить изолированные рабочие копии можно обычными командами Git:

git clone ./fixture-repo run-copilot-01
git clone ./fixture-repo run-api-01

git -C run-copilot-01 checkout TEST_BASE_COMMIT
git -C run-api-01 checkout TEST_BASE_COMMIT

git -C run-copilot-01 status --short
git -C run-api-01 status --short

Последние две команды перед стартом должны вернуть пустой вывод. Если проект генерирует файлы при установке зависимостей, либо исключите их заранее, либо выполняйте одинаковую установку до контрольной точки.

Запуск в готовой среде

Создайте новую сессию, откройте только тестовую копию и передайте исходное условие без дополнительных подсказок. Запустите таймер до отправки задания. Отдельно считайте активное время: чтение вопросов, подтверждение команд, ручное копирование данных и исправление результата.

Не подсказывайте путь к нужной функции только потому, что уже нашли его в предыдущем прогоне. Разрешённые вмешательства определите заранее, например:

  • 0 — агент работал без помощи;
  • 1 — оператор подтвердил безопасную команду;
  • 2 — оператор указал файл или объяснил ошибку;
  • 3 — оператор вручную изменил код.

Уровни 2 и 3 не обязательно превращают попытку в провал, но их время и правила должны одинаково применяться к API-варианту. Сохраните стенограмму, diff, список команд и доступные сведения о потреблении. Если среда не раскрывает токены, записывайте unknown, а не ноль.

Запуск через прямой API

Для прямого API используйте фиксированную версию собственного agent harness. Он должен регистрировать каждое обращение, переданный объём, ответ, задержку и вызов инструмента. Если в одном варианте модель может искать по репозиторию и запускать тесты, второй вариант должен получать функционально сопоставимые возможности.

Минимальная конфигурация эксперимента может выглядеть так:

{
  "run_id": "api-case-01",
  "base_commit": "TEST_BASE_COMMIT",
  "task_file": "task.txt",
  "model": "YOUR_CONFIGURED_MODEL",
  "temperature": 0,
  "max_steps": 20,
  "wall_time_limit_seconds": 1200,
  "allowed_tools": [
    "read_file",
    "search_text",
    "apply_patch",
    "run_tests",
    "git_diff"
  ],
  "network": "off",
  "log_usage": true
}

Названия модели, лимиты и список инструментов здесь являются заполнителями. Зафиксируйте свои реальные значения до первого запуска. Секрет передавайте через переменную среды, не записывая его в конфигурацию или журнал:

export MODEL_API_KEY='значение-из-защищённого-хранилища'
./run-agent --config experiment.json
unset MODEL_API_KEY

Стоимость берите из фактических данных ответа или биллинга поставщика. Если поставщик отдельно учитывает кэшированный ввод, рассуждение, изображения или инструменты, сохраняйте эти категории раздельно. Не восстанавливайте счёт только по приблизительной длине строки.

Что записывать для каждого запуска

Создайте CSV до начала теста. Одна строка соответствует одной попытке, включая аварийно завершённую:

run_id,approach,case_id,status,input_tokens,output_tokens,other_units,provider_cost,fixed_cost_share,infra_cost,wall_seconds,human_seconds,intervention_level,test_exit_code,unrelated_diff,notes
copilot-01,copilot,csv-null,pending,unknown,unknown,unknown,,,,,,,,
api-01,api,csv-null,pending,0,0,0,,,,,,,,

Допустимые итоговые статусы задайте заранее:

  • accepted — все обязательные проверки пройдены, результат принят;
  • wrong_result — патч создан, но поведение неверно;
  • timeout — превышен единый лимит времени;
  • tool_failure — управляющий контур не смог выполнить необходимое действие;
  • aborted — запуск остановлен по заранее описанному правилу;
  • invalid_run — эксперимент испорчен внешней причиной и повторяется для обеих сторон по одному правилу.

Не заменяйте неизвестные данные нулями. Для Copilot может быть невозможно увидеть точный расход токенов или стоимость отдельной сессии. Такое ограничение само является результатом сравнения прозрачности, но не даёт права оценивать скрытые значения как точные.

Как посчитать итог

После серии запусков для каждого подхода вычислите:

success_rate = accepted / valid_runs

average_tokens_per_attempt =
  sum(input_tokens + output_tokens) / runs_with_token_data

average_wall_time =
  sum(wall_seconds) / valid_runs

average_human_time =
  sum(human_seconds) / valid_runs

cost_per_accepted_task =
  sum(provider_cost + fixed_cost_share + infra_cost
      + human_seconds / 3600 × hourly_rate)
  / accepted

Если токены скрыты хотя бы у одной стороны, не стройте ложное числовое сравнение. Покажите для неё «не наблюдаются», а рядом сравните доступные прокси: число агентных шагов, продолжительность сессии и число прочитанных файлов. Эти показатели не эквивалентны токенам и должны оставаться отдельными колонками.

Сведите результаты в таблицу, заполняя только измеренные значения:

Показатель Copilot Прямой API
Валидных запусков Заполнить Заполнить
Принятых задач Заполнить Заполнить
Доля успеха Рассчитать Рассчитать
Токенов на попытку Значение или «не наблюдаются» Рассчитать по журналу
Среднее календарное время Рассчитать Рассчитать
Среднее активное время человека Рассчитать Рассчитать
Стоимость принятой задачи без труда Рассчитать Рассчитать
Стоимость принятой задачи с трудом Рассчитать Рассчитать

Для фиксированной подписки дополнительно постройте сценарии нагрузки. При малом количестве задач доля подписки на каждую задачу высока; при большом — снижается, пока команда не упирается в квоты, ожидание или производительность оператора.

Проверка результата

Само сообщение агента «готово» ничего не доказывает. Проверку выполняйте отдельным скриптом после завершения сессии:

cd RUN_DIRECTORY

git status --short
git diff --check
git diff --stat
git diff --name-only

npm test
npm run lint
npm run typecheck

Замените команды на реальные для проекта и запускайте один и тот же набор в обоих вариантах. Добавьте скрытые регрессионные тесты, проверяющие минимум три случая: пустое значение, корректное число и некорректный ввод. Проверяющий не должен оценивать стиль патча, пока не увидит результаты автоматических проверок.

Для защиты от субъективности можно скрыть происхождение патчей и провести слепую проверку. Критерии принятия:

  1. обязательные тесты завершаются с кодом 0;
  2. новый тест падает на исходном commit и проходит после патча;
  3. публичный интерфейс не изменён без требования;
  4. в diff нет секретов, временных файлов и несвязанных правок;
  5. поведение соответствует условию, а не только конкретному примеру теста.

Сохраняйте вывод команд как артефакт запуска. Это обеспечивает наблюдаемость: число в итоговой таблице можно связать с конкретным журналом, diff и результатом тестов.

Когда какой подход выгоднее

Готовая среда чаще оказывается экономически сильнее, когда задач немного или их объём нестабилен, собственная агентная инфраструктура ещё не создана, разработчики работают интерактивно, а время до полезного результата важнее детального контроля каждого модельного шага. Проверять это нужно по меньшему времени оператора и более высокой доле принятых задач, а не по ощущению удобства.

Прямой API чаще становится интереснее, когда поток однотипных задач достаточно велик, контекст можно жёстко ограничить, уже существует надёжный управляющий контур, нужна маршрутизация между моделями, детальная телеметрия или автоматический пакетный запуск без постоянного участия разработчика.

Рассчитайте точку безубыточности для своего периода:

fixed_environment_cost / accepted_tasks
  + variable_environment_cost_per_task
  + environment_human_cost_per_task

сравнить с

api_model_cost_per_accepted_task
  + api_infrastructure_cost_per_task
  + api_human_cost_per_task
  + harness_development_cost / expected_lifetime_tasks

Последнее слагаемое часто пропускают. Если управляющий код потребовал разработки, тестирования и дальнейшего сопровождения, эти часы следует распределить по ожидаемому числу задач за срок его полезной жизни. Иначе API выглядит дешевле только потому, что стоимость платформенной работы спрятана.

Что чаще всего ломает сравнение

Сравниваются разные задачи. Даже два похожих бага могут требовать разного поиска по коду. Используйте одинаковые копии и несколько повторов.

Оператор обучается между запусками. Во втором варианте он уже знает нужный файл и причину ошибки. Чередуйте порядок, изолируйте сессии и фиксируйте все подсказки.

В API считают только последний запрос. В стоимость входят планирование, повторная передача файлов, исправление тестов, сжатие истории и неудачные ответы.

Подписку считают бесплатной. Включённый запуск не создаёт отдельной строки в счёте, но место и дополнительные квоты всё равно оплачиваются.

Время ожидания смешано со временем человека. Двадцать минут автономной работы и двадцать минут ручной отладки имеют разную экономику. Записывайте оба значения.

Среда знает больше, чем API-контур. Если одна сторона получила индекс репозитория, поиск и тестовый терминал, а другой передали два случайных файла, измеряется качество подготовки контекста, а не только выбранный способ доступа к модели.

Неуспешные задачи исчезают из отчёта. Любой запуск после отправки задания входит в знаменатель, если только заранее определённая внешняя причина не сделала его невалидным.

Секрет попал в журнал. Перед публикацией артефактов удалите ключи, персональные пути, закрытый код и содержимое переменных среды. Лучше не записывать их изначально.

Ограничения эксперимента

Полностью изолировать оболочку от модели обычно невозможно. Готовая среда может менять маршрутизацию, системные инструкции, способы отбора файлов и доступные модели, а часть этих параметров не наблюдается пользователем. Поэтому результат относится к проверенной конфигурации и дате, а не ко всем версиям Copilot или API.

Точная сопоставимость токенов также не гарантируется: стороны могут по-разному учитывать служебный контекст, кэш и внутренние шаги. Сравнивайте только показатели, полученные совместимым способом, и явно отмечайте пробелы.

Наконец, экономический итог зависит от типа репозитория, цены труда, загрузки команды, действующего плана и тарифов модели. Не переносите чужую точку безубыточности в свою организацию. Ценность протокола — в одинаковых исходных данных, строгом критерии принятия и полной стоимости всех попыток.

Практическое правило: выбирайте готовую среду, если она заметно сокращает человеческое участие и повышает число принятых задач; выбирайте прямой API, если измеренная экономия на масштабе перекрывает разработку и сопровождение управляющего контура. Решение должно следовать из стоимости принятой задачи, а не из цены миллиона токенов или месячного места по отдельности.

← Все руководства · Лабораторный словарь →