Перейти к содержанию

Эвалы навыков

Два бенча проверяют агента на настоящей модели. Они работают на promptfoo с любым OpenAI-совместимым эндпоинтом и требуют Node для npx. Плагину и его тестам они не нужны.

  • Бенч навыков проверяет отдельные решения: грузит ли модель нужные навыки и вызывает ли потом нужный инструмент с правильными аргументами?
  • Бенч агента выполняет целые запросы в QGIS без интерфейса и проверяет, что они оставили в проекте.

Бенч навыков

Каждый кейс — один запрос к модели, собранный собственным кодом плагина из core/agent/request.py (evals/context.py): системный промпт, схемы инструментов после загрузки проверяемого навыка, контекст проекта и просьба пользователя. Проект описан в evals/fixture.json. Правка SKILL.md или описания инструмента измеряется уже следующим прогоном.

Модель, которая сначала читает, за это не наказывается. Если в ответе только чтения, на которые есть ответ в фикстуре (describe_layer, get_field_values для любого поля, describe_processing, describe_style, list_layers, get_project_info, get_canvas_extent, get_qgis_info, list_plugins), evals/provider.py отвечает на них и спрашивает снова, до трёх раз. Ответы устроены как у настоящих инструментов: они взяты из прогона QGIS без интерфейса на проекте, собранном по фикстуре, без идентификаторов слоёв. Кейс оценивается по всем вызовам, так что запрещённый вызов проваливает его на любом шаге.

Наборы:

  • routing (evals/routing.yaml): первый ход, загружен только inspect. Какие навыки грузит модель?
  • по набору на навык (ai_agent/skills/<навык>/evals/cases.yaml): навык загружен, кейс проверяет следующий вызов. В ZIP плагина кейсы не попадают.

Кейс

- description: Graduated classes with a count
  vars:
    request: Colour the districts by population in 5 classes
    reads: districts
    expect:
      call: set_graduated
      args: {layer_name: districts, field: pop2020, classes: 5}
  • request: что пишет пользователь, по-английски или по-русски.
  • reads: вызовы, которые агент уже сделал, через запятую: roads — это describe_layer, roads/highway — значения этого поля, native:buffer — описание алгоритма. Чтение со своим результатом пишется как {steps: [{tool, args, result}]}. YAML-список сюда писать нельзя: promptfoo превращает список в vars в отдельный тест на каждый элемент.
  • expect: call и args, loads и not_loads, not (инструменты, которые вызывать нельзя), after ({run_processing: describe_processing}: запуск только после описания), answer (ответ без вызова инструмента) и any_of. Каждый ключ описан в evals/checks.py.

Сравнение нестрогое: регистр строк не важен, "500" равно 500, словарь совпадает по перечисленным ключам, re: начинает регулярное выражение, * требует только наличия аргумента, а "~33580±1%" — число в пределах 1 %.

Запуск

export EVAL_API_URL=https://api.deepseek.com/v1
export EVAL_MODEL=deepseek-chat
export EVAL_API_KEY=...   # для локального сервера не нужен
python3 evals/run.py
  • --suites routing,style запускает только эти наборы; --only "Labels|Buffer" — только подходящие кейсы.
  • --repeat 3 --fresh прогоняет каждый кейс трижды без кеша и сообщает долю успешных.
  • Несколько моделей через запятую в EVAL_MODEL сравниваются бок о бок.
  • EVAL_LOCALE=ru собирает промпт как для русского интерфейса.
  • EVAL_AUTH=OAuth — для шлюзов, которым нужен OAuth вместо Bearer.

Запуск печатает прошедшие кейсы, токены и задержку по каждому набору, а также каждый провал и что модель сделала вместо ожидаемого. Та же таблица уходит в build/evals/summary.md; npx promptfoo view показывает кейсы по одному.

Бенч агента

python3 evals/run.py --agent
  • evals/agent/fixture.py строит небольшой город из настоящих слоёв в UTM 37N с фиксированными значениями, так что задача может назвать точный результат.
  • evals/agent/tasks.yaml — задачи: стили, буферы, поля, правки, экспорт, макет, соединения, диаграмма, цепочка через несколько навыков.
  • evals/agent/server.py работает внутри QGIS. Для каждой задачи он строит проект, отправляет запрос, нажимает «Применить» для каждого пакета (включая опасные шаги), ждёт проверки после применения и возвращает ответ, выполненные инструменты, токены и снимок проекта.
  • evals/agent/outcome.py сравнивает снимок с expect задачи: ответ, слои, записанные файлы и проект. Площади измеряются на эллипсоиде, так что буфер в «метрах» Web Mercator не проходит.

На macOS с установленным QGIS 4 сервер работает в Python этого QGIS; в остальных случаях или с --agent-server docker — в закреплённом образе QGIS. Журнал пишется в build/evals/agent-server.log, результаты — в build/evals/agent.results.json. tests/real_qgis_agent_bench.py проверяет механику сервера на скриптовой модели в каждом запуске CI.

В CI

Workflow skill-evals никогда не запускается на push. Запустите его из вкладки Actions (с наборами, числом повторов, save_thresholds или agent) или поставьте метку skill-evals на pull request. Он берёт настройки провайдера live-model: LIVE_MODEL_URL, LIVE_MODEL, секрет LIVE_MODEL_KEY и LIVE_MODEL_AUTH. Pull request из форков секретов не получают и пропускаются.

У каждого набора есть порог в evals/thresholds.json: доля успешных в базовом прогоне минус десять пунктов, округлённая вниз до пяти. Прогон падает, если набор опустился ниже порога или больше десятой части его кейсов не дошла до модели: такие кейсы выпадают из доли успешных. Пороги относятся к модели, на которой их измерили; если для модели порогов нет, сводка пишет, что ничего не проверено. Чтобы их задать, запустите workflow с repeat 3 и save_thresholds или локально python3 evals/run.py --repeat 3 --fresh --save-thresholds и закоммитьте файл. EVAL_REQUIRE_THRESHOLDS=1 роняет набор без порога.

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

Когда запускать

  • После правки SKILL.md, описания инструмента или ядра промпта: набор этого навыка, а при смене описания навыка ещё и routing.
  • После изменения поведения инструмента, применения или проверки: бенч агента.
  • Перед выпуском: всё, с --repeat 3 --fresh.
  • С новым навыком: добавьте evals/cases.yaml рядом с его SKILL.md (запросы, ради которых он есть, ловушку, о которой предупреждают его правила, один запрос по-русски) и кейс для routing.

tests/test_skill_evals.py проверяет, что каждый инструмент и навык из кейсов существует и что запросы собирает код плагина.