Эвалы навыков¶
Два бенча проверяют агента на настоящей модели. Они работают на
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 проверяет, что каждый инструмент и навык из кейсов
существует и что запросы собирает код плагина.