ПРАКТИКА · УПРАВЛЕНИЕ И БЕЗОПАСНОСТЬ

Проверяем контур управления AI-агентом с помощью AgentGovBench

Уровень: продвинутый Время чтения и практики: 75 минут Результат: 48 сценариев и базовый scorecard

Обычный тест модели может показать правильный ответ и одновременно пропустить критический системный дефект: по пути к инструменту исчезла пользовательская идентичность, дочерний агент получил больше прав, чем родитель, запрос одного клиента попал в пространство другого, а журнал аудита не позволяет восстановить произошедшее. AgentGovBench проверяет именно этот слой — не качество текста модели, а фактическое применение правил перед вызовом инструмента и доказательства, оставшиеся после вызова.

Почему тестов модели недостаточно

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

Рассмотрим внешне успешный запуск:

Пользователь → оркестратор → worker → read_email → краткий ответ
                                      └────────────→ audit

Ответ может быть точным, хотя внутри произошли четыре нарушения:

  1. Worker вызвал инструмент от имени общей сервисной учётной записи, а не пользователя.
  2. Политика пользователя запрещала чтение почты, но применялась только к оркестратору.
  3. Идентификатор клиента был взят из аргументов модели, а не из проверенного контекста авторизации.
  4. В журнал попала запись «инструмент выполнен» без пользователя, решения политики и цепочки делегирования.

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

Единица проверки — не ответ модели, а переход через границу полномочий.

Поэтому AgentGovBench выполняет детерминированные последовательности действий без LLM в критическом пути. Это важное свойство: случайность генерации не должна скрывать ошибку в авторизации. Бенчмарк синтезирует вызовы инструментов и проверяет наблюдаемые решения контура управления.

Что находится внутри 48 сценариев

Набор версии 0.2 разделён на восемь категорий по шесть сценариев. Перед запуском сверяйте структуру своей закреплённой версии репозитория: названия, проверки и формат результатов могут меняться.

Категория Сценариев Что проверяется Типичный риск
identity_propagation 6 Передача UID и читаемого идентификатора пользователя через прямые и делегированные вызовы Действие приписано агенту или service account
per_user_policy_enforcement 6 Пользовательские исключения, недостающие права и отзыв разрешения Общее правило пространства перекрывает персональный запрет
scope_inheritance 6 Сужение scope на каждом шаге Дочерний процесс приобретает полномочие, которого не было у пользователя
delegation_provenance 6 Полная и упорядоченная цепочка делегирования Нельзя установить, какой агент передал задачу исполнителю
audit_completeness 6 Обязательные поля, разрешения, отказы и отсутствие внутренних ошибок runner Журнал показывает активность, но не позволяет восстановить решение
rate_limit_cascade 6 Наследование лимитов запросов дочерними агентами Пользователь умножает свой лимит созданием нескольких workers
fail_mode_discipline 6 Предсказуемые fail-open и fail-closed режимы Недоступность шлюза незаметно отключает защиту
cross_tenant_isolation 6 Изоляция клиентов в политиках, идентичности, аудите и лимитах Пользователь клиента A читает или изменяет состояние клиента B

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

Конкретный случай: пользователь исчезает при передаче задачи worker

Возьмём синтетический стенд с двумя клиентами. У Alice из tenant-a есть право email.read, но нет admin.grant_permission. Оркестратор получает задачу прочитать письмо и создаёт worker. Worker подключён к инструментам через общую сервисную учётную запись.

Ошибочная реализация передаёт в очередь только содержание задачи:

{
  "task": "Прочитать письмо и подготовить краткое резюме",
  "mailbox": "alice@example.test",
  "agent_name": "mail-worker"
}

Проверенный пользователь, клиент, исходные права и идентификатор делегирования потеряны. Worker восстанавливает контекст так:

actor_uid = "service:mail-worker"
tenant = payload.get("tenant", DEFAULT_TENANT)
scopes = SERVICE_ACCOUNT_SCOPES

Теперь тот же worker может попытаться вызвать административный инструмент. Если policy engine проверяет полномочия service account, а не Alice, персональный запрет не сработает. Даже если административный вызов случайно не произойдёт, журнал уже неверен: он связывает действие с техническим процессом, а не с человеком, от имени которого выполнялась задача.

Правильный конверт выполнения отделяет доверенную идентичность от данных задачи:

{
  "execution": {
    "request_id": "req-test-001",
    "tenant_id": "tenant-a",
    "subject": {
      "uid": "user-alice",
      "display": "alice@example.test"
    },
    "delegation": [
      {
        "from": "orchestrator",
        "to": "mail-worker",
        "scopes": ["email.read"]
      }
    ],
    "effective_scopes": ["email.read"],
    "policy_version": "test-policy-v1"
  },
  "input": {
    "mailbox": "alice@example.test"
  }
}

Поле input считается недоверенным. Оно не может изменить subject, tenant_id или effective_scopes. Конверт создаётся на границе аутентификации, подписывается либо передаётся по доверенному внутреннему каналу и заново проверяется непосредственно перед инструментом.

Эффективные права дочернего агента вычисляются пересечением, а не объединением:

effective_child_scopes =
    user_scopes
    ∩ parent_effective_scopes
    ∩ delegated_task_scopes
    ∩ tool_policy_scopes

Если хотя бы один слой не разрешает admin.grant_permission, вызов блокируется. Ни промпт, ни имя worker, ни полномочия технического процесса не могут расширить результат.

Шаг 1. Подготавливаем безопасный стенд

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

Минимальные условия:

  • Python 3.10 или новее;
  • Git и отдельный каталог;
  • два тестовых клиента: tenant-a и tenant-b;
  • три синтетических пользователя с разными правами;
  • безопасные инструменты read_email, read_file, read_public_doc и grant_permission;
  • возможность очистить политики, лимиты и журналы между сценариями;
  • отдельный экспорт аудита для проверок runner.
mkdir -p agentgovbench-lab
cd agentgovbench-lab
umask 077

git clone https://github.com/agentic-control-plane/agentgovbench.git
cd agentgovbench

git status --short
git rev-parse HEAD
git rev-parse HEAD > ../agentgovbench.commit

python3 -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e .

Файл agentgovbench.commit нужен для повторяемости. Запись «мы запускали AgentGovBench» недостаточна: через месяц сценарий, допуск или runner может измениться. В отчёте фиксируйте commit, версию Python, имя runner, версию проверяемой системы и хеш конфигурации политики.

Перед выполнением посмотрите доступные команды и runner именно в закреплённой версии:

agentgovbench --help
agentgovbench run --help

find scenarios -type f \( -name '*.yaml' -o -name '*.yml' \) -print | sort
find runners -type f -name '*.py' -print | sort

Проверьте число файлов сценариев:

find scenarios -type f \( -name '*.yaml' -o -name '*.yml' \) -print \
  | sort \
  | tee ../scenario-files.txt \
  | wc -l

Для профиля из этой статьи ожидается ровно 48. Если число другое, не подгоняйте scorecard вручную: вы используете другую версию или считаете вспомогательные YAML-файлы. Сначала изучите структуру каталога и документацию закреплённого commit.

Шаг 2. Запускаем baseline без контура управления

Runner vanilla нужен не для оценки вашей системы, а для проверки установки, загрузки сценариев и работы scorer. Он не требует рабочих секретов.

mkdir -p results

agentgovbench run \
  --runner vanilla \
  --out results/vanilla-local.json

Не копируйте чужое итоговое число в свой отчёт. Зафиксируйте то, что реально вывела ваша версия CLI, и убедитесь в трёх свойствах:

  1. Runner обнаружил все 48 сценариев.
  2. В результате нет необработанного исключения установки или импорта.
  3. Файл results/vanilla-local.json создан и содержит результаты отдельных сценариев, а не только общий балл.

Сохраните диагностическую информацию рядом:

python --version > results/environment.txt
python -m pip freeze >> results/environment.txt
git rev-parse HEAD >> results/environment.txt
sha256sum results/vanilla-local.json > results/SHA256SUMS

Baseline нельзя сравнивать с рабочим runner, если между прогонами изменились сценарии. Оба результата должны быть получены на одном commit.

Шаг 3. Подключаем свой контур через runner

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

Не встраивайте логику прохождения тестов в runner. Он должен адаптировать интерфейс, а не решать, что считать правильным. Ожидаемые результаты уже находятся в сценариях.

Контракт адаптера

Операция сценария Что должен сделать адаптер Что нельзя подменять
direct_tool_call Провести вызов через тот же enforcement path, что используется агентом Нельзя напрямую вызывать фикстуру в обход политики
delegation Создать реальный внутренний контекст дочернего агента Нельзя дописывать цепочку только перед экспортом аудита
policy_change Изменить политику через штатный механизм и дождаться применимости Нельзя менять только локальное ожидание runner
parallel_fan_out Создать нужное количество логических workers и вызовов Нельзя использовать отдельный лимит для каждого worker
gateway_failure Сделать точку решения реально недоступной для тестового трафика Нельзя заранее вернуть желаемый allow или deny
audit query Прочитать тот же журнал, который доступен расследованию Нельзя строить аудит из памяти самого runner

Начните с ближайшего адаптера из каталога runners и реализации базового класса, существующей в вашем commit:

grep -R "class BaseRunner" -n benchmark runners
grep -R "BaseRunner" -n runners | head -30
grep -R "def direct_tool_call\|def run_action\|def query_audit" -n runners benchmark

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

Доверенная и недоверенная часть запроса

В адаптере полезно явно разделить две структуры:

trusted_execution_context = {
    "actor_uid": authenticated_user.uid,
    "actor_email": authenticated_user.email,
    "tenant_id": authenticated_membership.tenant_id,
    "effective_scopes": computed_scopes,
    "delegation_chain": verified_chain,
    "request_id": scenario_request_id
}

untrusted_tool_input = {
    "tool": scenario.tool,
    "arguments": scenario.input
}

Если ваша система принимает actor_uid или tenant_id из тела запроса, runner должен передавать их только через предусмотренный тестовый механизм имперсонации. Этот механизм обязан быть отключён в production, ограничен отдельным правом и отражён в аудите.

Инструмент-фикстура

Фикстура должна записывать только фактически дошедшие до неё вызовы:

{
  "received_at": "2026-01-01T00:00:00Z",
  "request_id": "req-test-001",
  "tool": "read_file",
  "arguments": {
    "path": "/tenant-a/report.txt"
  }
}

Отказ считается настоящим только тогда, когда запрещённого вызова нет в журнале фикстуры. Сообщение агента «я не выполнил действие» не является доказательством.

Шаг 4. Сначала запускаем одну категорию

Полный прогон затрудняет первичную отладку. Начните с передачи идентичности:

agentgovbench run \
  --runner your_runner_name \
  --category identity_propagation \
  --out results/identity.json

Замените your_runner_name именем, реально зарегистрированным вашей версией runner. Если CLI его не видит, проверьте механизм регистрации, импорт модуля и вывод agentgovbench run --help.

После категории идентичности последовательно проверьте четыре чувствительные границы:

agentgovbench run --runner your_runner_name \
  --category per_user_policy_enforcement \
  --out results/per-user-policy.json

agentgovbench run --runner your_runner_name \
  --category scope_inheritance \
  --out results/scope-inheritance.json

agentgovbench run --runner your_runner_name \
  --category delegation_provenance \
  --out results/delegation.json

agentgovbench run --runner your_runner_name \
  --category cross_tenant_isolation \
  --out results/cross-tenant.json

Не продолжайте, если runner падает на setup или teardown. Такой результат нельзя интерпретировать как провал управления: сначала нужно отделить ошибку адаптера от наблюдаемого поведения системы.

Шаг 5. Запускаем все 48 сценариев

После отладки отдельных категорий очистите тестовое состояние и выполните полный прогон:

agentgovbench run \
  --runner your_runner_name \
  --out results/your-system.json

Сразу сохраните контрольную сумму:

sha256sum results/your-system.json >> results/SHA256SUMS

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

python - <<'PY'
import json
from pathlib import Path

path = Path("results/your-system.json")
data = json.loads(path.read_text(encoding="utf-8"))

print("root_type:", type(data).__name__)
if isinstance(data, dict):
    print("root_keys:", sorted(data.keys()))
elif isinstance(data, list):
    print("items:", len(data))
PY

Найдите коллекцию результатов сценариев и проверьте:

  • в ней 48 уникальных идентификаторов;
  • каждый идентификатор относится к одной из восьми категорий;
  • в каждой категории находится шесть сценариев;
  • у каждого сценария есть итоговый статус и объяснение проверки;
  • declined и runner error не были автоматически превращены в pass.

Если формат результата содержит массив scenarios, пример проверки выглядит так:

python - <<'PY'
import json
from collections import Counter
from pathlib import Path

data = json.loads(
    Path("results/your-system.json").read_text(encoding="utf-8")
)
rows = data["scenarios"]

ids = [row["id"] for row in rows]
categories = Counter(row["category"] for row in rows)

assert len(rows) == 48, f"получено {len(rows)}, ожидалось 48"
assert len(set(ids)) == 48, "обнаружены повторяющиеся scenario id"
assert set(categories.values()) == {6}, categories

print("scenarios:", len(rows))
for category, count in sorted(categories.items()):
    print(f"{category}: {count}")
PY

Если ключи отличаются, адаптируйте только чтение результата. Не меняйте критерии сценариев ради прохождения проверки.

Шаг 6. Собираем базовый scorecard

Базовый scorecard должен показывать не только общий балл. Система с высоким итогом может иметь единственный неприемлемый провал — например, разрешённый доступ к другому клиенту.

Категория Pass Fail Declined Error Решение
Identity propagation из результата из результата из результата из результата разобрать потерю субъекта
Per-user policy enforcement из результата из результата из результата из результата проверить приоритет персональной политики
Scope inheritance из результата из результата из результата из результата проверить пересечение прав
Delegation provenance из результата из результата из результата из результата восстановить цепочку
Audit completeness из результата из результата из результата из результата добавить обязательные поля и отказы
Rate-limit cascade из результата из результата из результата из результата связать бюджет с пользователем и клиентом
Fail-mode discipline из результата из результата из результата из результата зафиксировать режим отказа
Cross-tenant isolation из результата из результата из результата из результата остановить выпуск при утечке
Итого фактический результат из файла готово / условно / не готово

Pass означает, что все утверждения сценария проверены. Fail — система наблюдаемо нарушила ожидание. Declined означает, что runner честно не поддерживает требуемую возможность или топологию. Error — сценарий не дал интерпретируемого результата из-за сбоя setup, выполнения либо teardown.

Declined не следует считать ни успехом, ни обычным провалом. Например, одноклиентское развёртывание может не уметь воспроизвести часть межклиентских сценариев. В scorecard нужно сохранить статус, причину и владельца решения, а не увеличивать знаменатель удобным способом.

Рекомендуемый выпускной барьер

Числовой порог выбирает владелец системы, но для критических границ полезно применять отдельный барьер:

  • ни одного подтверждённого обхода пользовательского запрета;
  • ни одного расширения scope при делегировании;
  • ни одного разрешённого межклиентского доступа;
  • ни одного fail-open поведения там, где политика требует fail-closed;
  • для каждого выполненного либо заблокированного инструмента существует пригодная для расследования запись.

Такой барьер не является правилом самого AgentGovBench. Это практическая политика выпуска, которую нужно согласовать с вашей моделью угроз.

Разбор провалов: идентификация

Признаки потери идентичности:

  • в аудите указан service:worker вместо пользователя;
  • UID есть, но отсутствует зафиксированный читаемый идентификатор;
  • делегированный вызов имеет другую пользовательскую идентичность;
  • запрос без аутентифицированного пользователя выполняется от default account;
  • tenant берётся из тела запроса без проверки членства.

Чаще всего причина находится не в модели, а на асинхронной границе. HTTP-обработчик проверил пользователя, но отправил в очередь только текст задачи. Worker создал новый контекст с техническими правами. Второй распространённый дефект — идентичность сохраняется в изменяемом словаре состояния, который дочерний агент может перезаписать.

Исправление состоит из четырёх частей:

  1. Создавать субъект только из проверенного результата аутентификации.
  2. Передавать его через очередь в отдельном неизменяемом execution envelope.
  3. Проверять целостность конверта перед каждым инструментом.
  4. Сохранять снимок UID и читаемого идентификатора в момент решения, а не восстанавливать имя при последующем просмотре журнала.

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

Разбор провалов: делегирование и наследование прав

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

Опасная реализация выглядит так:

child_scopes = parent_scopes | requested_scopes

Оператор | объединяет множества. Любое право, записанное в запросе на создание дочернего агента, становится действующим. Безопасная реализация использует пересечение:

child_scopes = (
    user_scopes
    & parent_effective_scopes
    & requested_scopes
    & policy_allowed_scopes
)

Отдельно проверяйте происхождение делегирования. Запись только последнего worker недостаточна. Для цепочки

user-alice → orchestrator → specialist → worker → tool

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

Типичные причины провала:

  • проверка политики выполняется только при создании задачи;
  • worker использует долгоживущий токен оркестратора;
  • дочернему агенту передаётся полный набор инструментов родителя;
  • цепочка хранится в тексте промпта, а не в доверенном контексте;
  • при повторной постановке задачи создаётся новая корневая цепочка;
  • background-задача ошибочно помечается как интерактивная.

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

Разбор провалов: изоляция клиентов

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

Проверьте ключи каждого разделяемого хранилища:

policy_cache[(tenant_id, policy_version, subject_uid)]
rate_bucket[(tenant_id, subject_uid, agent_tier)]
audit_partition[tenant_id]
memory_namespace[(tenant_id, subject_uid, conversation_id)]
tool_credentials[(tenant_id, connector_id)]

Кеш вида policy_cache[subject_uid] небезопасен, если идентификаторы пользователей уникальны только внутри клиента. Кеш вида policy_cache[tool_name] ещё опаснее: первое вычисленное решение может применяться ко всем клиентам.

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

authenticated_subject
    → verified_membership
    → tenant_id
    → tenant_policy
    → tenant_credentials
    → tenant_audit_partition

Администратор tenant-b остаётся администратором только внутри tenant-b. Роль admin без клиентской области превращается в неявного глобального администратора.

После провала проведите двустороннюю проверку:

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

Разбор провалов: неполный аудит

Наблюдаемость и аудит пересекаются, но решают разные задачи. Метрика показывает частоту и задержку. Аудит должен позволить доказательно восстановить конкретное решение.

Минимальная запись решения содержит:

{
  "timestamp": "точное время решения",
  "request_id": "сквозной идентификатор",
  "tenant": "tenant-a",
  "actor_uid": "user-alice",
  "actor_display": "alice@example.test",
  "agent_name": "mail-worker",
  "agent_tier": "background",
  "delegation_chain": ["orchestrator", "mail-worker"],
  "tool": "read_email",
  "decision": "allow",
  "reason_code": "scope_present",
  "effective_scopes": ["email.read"],
  "policy_version": "test-policy-v1",
  "arguments_hash": "хеш нормализованных аргументов"
}

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

Нужны две связанные записи:

  1. Decision record создаётся до передачи вызова инструменту и сообщает, почему запрос разрешён или запрещён.
  2. Execution receipt создаётся после попытки выполнения и сообщает, был ли инструмент фактически вызван и чем завершился.
request
  → policy decision: allow
  → fixture received call
  → execution receipt: success

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

Типичные дефекты аудита:

  • логируются только успешные действия, а отказы исчезают;
  • timestamp добавляет аналитический pipeline через несколько минут;
  • email пользователя разрешается по текущему каталогу и исчезает после удаления учётной записи;
  • вызов и запись аудита имеют разные request ID;
  • повторная попытка перезаписывает первую;
  • исключение runner ошибочно считается отсутствием нарушения;
  • в журнале указан выбранный моделью инструмент, но нет доказательства фактического вызова.

Что искать в остальных четырёх категориях

Персональная политика

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

Каскад лимитов

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

Аварийный режим

Fail-closed и fail-open — явные режимы, а не случайное следствие обработки исключения. Если политика требует fail-closed, timeout управляющего шлюза должен остановить инструмент. Если для конкретного низкорискового окружения объявлен fail-open, runner должен проверить именно его, а не изображать поддержку через заранее подготовленный ответ.

Наследование scope

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

Итоговая проверка результата

Работа завершена, когда у вас есть не скриншот общего числа, а воспроизводимый пакет доказательств:

results/
├── environment.txt
├── SHA256SUMS
├── vanilla-local.json
├── your-system.json
├── identity.json
├── per-user-policy.json
├── scope-inheritance.json
├── delegation.json
└── cross-tenant.json

agentgovbench.commit
scenario-files.txt
runner/
└── ваша реализация и конфигурация

Проверьте пакет по следующему списку:

  1. Закреплён commit AgentGovBench.
  2. Обнаружены и выполнены все 48 сценариев.
  3. Каждая из восьми категорий содержит шесть результатов.
  4. Baseline и рабочий runner запускались на одной версии.
  5. Scorecard построен из JSON, а не переписан из памяти.
  6. Fail, declined и error сохранены отдельно.
  7. Для каждого провала есть scenario ID, наблюдаемое поведение и ссылка на артефакт.
  8. Запрещённые вызовы сверены с журналом инструмента-фикстуры.
  9. Записи аудита не содержат тестовых секретов или содержимого частных данных.
  10. Повторный запуск после очистки состояния даёт интерпретируемый результат без скрытого влияния предыдущего прогона.

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

Scenario: identity_propagation.02_...
Status: FAIL
Expected: actor_uid=user-alice
Observed: actor_uid=service:mail-worker
Side effect: фикстура получила допустимый read_email
Root cause: execution envelope потерян при постановке в очередь
Fix owner: platform-runtime
Fix: сериализация и проверка trusted execution context
Retest: вся identity_propagation + delegation_provenance
Evidence: result JSON, audit event, fixture receipt, commit

Не указывайте предполагаемую первопричину как установленный факт. Сначала свяжите scenario result, событие политики, запись аудита и receipt инструмента одним request ID.

Что обычно ломает сам прогон

Runner обходит реальный enforcement path

Тест вызывает служебный API напрямую, а агенты используют другой proxy или hook. Полученный scorecard описывает тестовый путь, а не рабочую архитектуру. Сравните сетевой маршрут и точки проверки в обоих режимах.

Состояние течёт между сценариями

Отозванное право, заполненный rate bucket или запись прошлого клиента остаются после teardown. Добавьте уникальный run ID, явную очистку и проверку пустого начального состояния.

Аудит появляется с задержкой

Runner запрашивает журнал сразу после действия и получает пустой результат. Используйте ограниченное ожидание с повторным чтением и фиксированным timeout. Не ждите бесконечно и не считайте timeout успехом.

Тестовый инструмент выполняет настоящее действие

Название send_email подключено к рабочему коннектору. Подмените исполнителя на fixture на уровне маршрутизации, а не инструкцией в промпте. Модель и сценарий не должны иметь возможность переключить его обратно.

Declined скрывает неудобный провал

Declined допустим, когда топология действительно недоступна и причина документирована. Если система поддерживает два клиента, но runner объявляет межклиентские сценарии неподдерживаемыми, scorecard вводит в заблуждение.

После исправления запускается один удобный сценарий

Изменение идентичности затрагивает аудит, scope, делегирование и лимиты. Сначала повторите категорию, затем весь набор из 48 сценариев.

Ограничения AgentGovBench

Scorecard не является сертификатом безопасности и не заменяет модель угроз. У бенчмарка есть важные границы:

  • Детерминированные вызовы не проверяют, сопротивляется ли модель prompt injection и правильно ли выбирает инструмент.
  • Набор не доказывает безопасность операционной системы, контейнера, сети, секрет-хранилища и самого инструмента.
  • Разрешённое политикой, но семантически вредное действие может остаться незамеченным.
  • Шесть сценариев на категорию не покрывают все варианты кеширования, гонок и восстановления после аварии.
  • Runner может дать ложную уверенность, если его путь отличается от production.
  • Результаты разных commit нельзя напрямую сравнивать без анализа изменений сценариев и scorer.
  • Бенчмарк поддерживается командой, связанной с одним из продуктов управления; сценарии и критерии нужно независимо читать, а не принимать итоговый балл на доверии.
  • Соответствие техническим сценариям не означает автоматического соответствия внутренним, отраслевым или правовым требованиям.

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

Что делать после первого scorecard

  1. Заморозьте исходный JSON и контрольную сумму.
  2. Классифицируйте провалы по границе: auth, очередь, policy engine, tool proxy, audit pipeline или runner.
  3. Сначала исправляйте разрешённые обходы, затем неполные доказательства, после этого чрезмерные запреты.
  4. Добавьте найденные production-варианты в собственный регрессионный набор рядом с AgentGovBench.
  5. Запускайте 48 сценариев при изменении identity middleware, политики, runner, оркестрации, кеша, аудита и подключения инструментов.
  6. Храните историю scorecard вместе с commit системы и конфигурацией политики.

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

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