Читать 6 мин

Как проверять действия AI-агента без хранения промптов

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

Как проверять действия AI-агента без хранения промптов

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

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

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

Запись действия отвечает не на тот же вопрос, что и расшифровка

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

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

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

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

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

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

Полные промпты создают хранилище данных, которое никто не планировал защищать

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

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

Обычный ответ звучит так: «Мы замаскируем секреты». Это работает, только если путь журналирования видит каждый формат секрета до сохранения и понимает каждый протокол. Такой подход пропустит короткоживущие подписанные URL, cookie сеансов, проприетарные форматы токенов, секреты внутри JSON-строк и учётные данные, которые сервис возвращает в ошибке. Маскирование также не вернёт конфиденциальность после того, как исходную запись прочитала широкая группа людей.

OWASP Logging Cheat Sheet проводит полезное различие: журналы должны помогать мониторингу и расследованиям, но системы не должны записывать токены доступа, пароли, строки подключения, ключи шифрования и данные, сбор которых создаёт неоправданный риск для конфиденциальности. Применяйте этот принцип к проверке агентов ещё строже. Контекст агента необычно широк и содержит больше случайных данных, чем обычный журнал запросов.

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

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

Идентичность процесса должна пережить понятное имя процесса

Имя процесса не идентифицирует агента. agent, node, python и shell почти ничего не говорят проверяющему, а вредоносная или небрежно написанная программа может выбрать любое из них.

Записывайте достаточно данных, чтобы отличить программу, инициировавшую сеанс:

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

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

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

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

Для авторизации нужны область действия, время и подтверждающий

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

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

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

Рабочий объект авторизации может выглядеть так:

{
  "authorization_id": "auth_7f3c",
  "kind": "session",
  "decision": "approved",
  "approved_at": "2025-03-08T14:22:31Z",
  "approver": "local-account:maya",
  "process_session": "sess_31a9",
  "process_identity": {
    "executable": "/Applications/Agent.app/Contents/agent",
    "signing_authority": "Example Development Team"
  },
  "scope": {
    "credential_refs": ["deploy-production"],
    "expires_when": "process exits"
  }
}

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

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

Метаданные запроса должны описывать пересечённую границу

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

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

Нормализованный шаблон маршрута означает запись /v1/projects/{project_id}/deployments вместо буквального URL с идентификатором клиента или похожим на секрет непрозрачным значением. Исходный маршрут может остаться в целевом сервисе, который уже владеет этими данными и применяет собственные правила доступа.

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

HTTP Semantics, RFC 9110, различает семантику методов запроса, например безопасные, идемпотентные и небезопасные методы. Используйте это различие как сигнал для проверки, а не как замену авторизации. GET может раскрыть чувствительные данные. PUT может быть идемпотентным и всё же заменить конфигурацию в рабочей среде. POST может создать необратимый внешний эффект. Метод помогает оценить действие, но реальный риск определяют назначение и маршрут.

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

{
  "call_id": "call_c24e",
  "session_id": "sess_31a9",
  "channel": "https",
  "destination": "api.example.internal",
  "method": "POST",
  "route_template": "/v1/projects/{project_id}/deployments",
  "credential_ref": "deploy-production",
  "request_bytes": 842,
  "request_digest": "sha256:6d1d...",
  "started_at": "2025-03-08T14:24:09Z"
}

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

Результаты должны содержать операционные свидетельства, а не дампы ответов

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

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

Для SSH сохраняйте код завершения, длительность, результат проверки идентичности хоста и краткое описание, выбранное адаптером команды. Если команде нужно доказательство успешной работы, пусть она выдаёт ограниченный машиночитаемый результат, например {\"release\":\"r42\",\"status\":\"published\"}. Не принимайте произвольную расшифровку терминала как результат аудита.

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

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

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

Схема проверки должна явно показывать запрещённые поля

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

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

Следующий пример объединяет идентичность, авторизацию, метаданные действия, результат и данные целостности. В нём намеренно нет полей prompt, messages, headers, request_body, response_body и stderr.

{
  "event_type": "external_action",
  "event_id": "evt_91bd",
  "occurred_at": "2025-03-08T14:24:10Z",
  "actor": {
    "local_account": "maya",
    "process_session": "sess_31a9",
    "pid": 4812,
    "parent_pid": 4601,
    "executable_digest": "sha256:2a84...",
    "signing_authority": "Example Development Team"
  },
  "authorization": {
    "authorization_id": "auth_7f3c",
    "mode": "session",
    "decision": "approved"
  },
  "action": {
    "channel": "https",
    "destination": "api.example.internal",
    "operation": "POST /v1/projects/{project_id}/deployments",
    "credential_ref": "deploy-production",
    "request_digest": "sha256:6d1d..."
  },
  "result": {
    "category": "completed",
    "status_code": 201,
    "destination_request_id": "req_18c7",
    "duration_ms": 614
  },
  "integrity": {
    "previous_event_digest": "sha256:8f50...",
    "event_digest": "sha256:bd7e..."
  }
}

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

Проверяющим также нужно понятное представление события. Создавайте его из канонической записи, а не поддерживайте отдельное описание вручную. Запись для человека может выглядеть так: «Одобренный сеанс процесса использовал deploy-production для создания развёртывания на api.example.internal. Сервис вернул 201 за 614 мс». В журнале остаются идентификаторы, необходимые для изучения события, но в представлении по умолчанию нет лишних данных.

Целостность доказывает изменение, но не полноту

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

NIST SP 800-92, Guide to Computer Security Log Management, рекомендует защищать целостность журналов, определять события, которые нужно записывать, и проверять журналы при ясной операционной ответственности. Важна именно совокупность мер. Целостность без определённой границы событий даёт надёжные записи неполной истории. Длинный список событий без защиты целостности даёт историю, которую можно незаметно изменить.

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

Проверка должна быть независимой от обычного чтения журналов. Команда проверки должна работать с зашифрованными сохранёнными записями и сообщать, сохранилась ли цепочка. Sallyport предоставляет такую проверку через sp audit verify: она проверяет зашифрованный журнал аудита с цепочкой хешей без ключа хранилища. Это важно, потому что расследователь должен иметь возможность проверить непрерывность записей, не получая доступа к учётным данным.

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

Доступ и хранение решают, не станет ли журнал ещё одной точкой утечки

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

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

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

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

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

Создайте запись на шлюзе и проверьте неудачные сценарии

Безопаснее всего собирать данные для проверки там, где выполняется действие с учётными данными. Агент должен запрашивать действие через ограниченный интерфейс, шлюз должен аутентифицировать локальный процесс и применить авторизацию, а затем выполнить HTTP- или SSH-операцию. Агент получает результат, а запись проверки фиксирует факты действия.

Sallyport использует такую схему для агентов с поддержкой MCP: локальный адаптер sp mcp направляет HTTP- и SSH-действия через приложение, а его зашифрованное хранилище держит API- и SSH-учётные данные вне контекста агента. Журнал сеансов и журнал активности формируются из одного и того же защищённого от записи зашифрованного журнала аудита, поэтому авторизация сеанса и отдельные вызовы остаются связанными, а агенту не нужно хранить секрет.

Проверьте дизайн на сбоях, которые обычно не показывают в успешных демонстрациях:

  1. Отправьте запрос, который завершится ошибкой после того, как сервис вернёт поддельный заголовок авторизации. Убедитесь, что запись содержит категорию ошибки и статус, но не заголовок и тело ответа.
  2. Запустите два процесса с одинаковым отображаемым именем, но разной идентичностью исполняемых файлов. Убедитесь, что представление проверки разделяет их сеансы.
  3. Одобрите один сеанс, завершите его и запустите новый процесс. Убедитесь, что старое одобрение не применяется к новому запуску.
  4. Вызовите тайм-аут и повторите запрос. Убедитесь, что записи связывают попытки с одним вызовом и сохраняют факт неопределённого результата.
  5. Измените тестовое сохранённое событие и запустите проверку целостности. Убедитесь, что проверяющий сообщает об ошибке и у команды есть письменная процедура реагирования.

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

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

Вопросы и ответы

Что должно входить в журнал аудита действий AI-агента?

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

Почему командам не стоит хранить полные промпты AI-агентов?

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

Как определить, какой AI-агент сделал вызов API?

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

Должны ли записи о подтверждении содержать промпт пользователя?

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

Какие HTTP-метаданные действий агента безопасно записывать?

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

Могут ли сообщения об ошибках API раскрыть секреты в журналах аудита?

Считайте ошибки недоверенными данными. Удаляйте заголовки авторизации, cookie, подписанные URL, закрытые идентификаторы, фрагменты ответов, трассировки стека и отражённое тело запроса до попадания ошибки в запись проверки.

Доказывает ли журнал с цепочкой хешей, что AI-агент не скрыл действия?

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

Как долго хранить журналы действий AI-агента?

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

Достаточно ли подтверждения человека для подотчётности AI-агента?

Нет. Подтверждение человека показывает, что кто-то разрешил область действия или сеанс, но не указывает исполняемый файл, который сделал вызов, точный отправленный запрос или полученный результат. Все эти факты нужно связать в одной записи проверки.

Как AI-агент может использовать учётные данные, не видя их?

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

Sallyport

Sallyport выполняет API-вызовы и SSH-команды за вашего ИИ-агента. Ключи остаются в локальном хранилище на вашем Mac; вы подтверждаете каждый запуск, и каждое действие попадает в запечатанный журнал.

© 2026 Sallyport · Открытый код по лицензии Apache-2.0 · Oleg Sotnikov