agate-proxy¶
Ограниченный контекст прокси: встроенный обратный прокси, который инспектирует трафик LLM-агента и решает — по каждому событию — разрешить, запретить, преобразовать, буферизовать или завершить его.
agate-proxy — это плоскость данных. Ядро инспекции не зависит от
протокола: проводной протокол (сначала AG-UI, позже адаптер агент ↔ LLM)
входит через адаптер, который переводит проводные события в доменные. Полный
дизайн см. в Модели угроз.
Ответственность¶
- Терминировать TLS и принимать AG-UI-запрос (
RunAgentInput). - «Нога» запроса (превентивная): валидировать, авторизовать и ограничивать размер запроса до пересылки — отвергать рано, чтобы агент не запускался на плохом вводе.
- «Нога» ответа (потоковая): инкрементально парсить SSE-поток событий и по каждому событию применять вердикт — пересылая, редактируя или завершая.
- Буферизовать фрагменты аргументов вызова инструмента между
TOOL_CALL_STARTиTOOL_CALL_END, чтобы вердикт видел полные аргументы. - Питать каждой парой
(событие, вердикт)приёмник аудита, вне «горячего» пути пересылки.
Конечный автомат инспекции¶
stateDiagram-v2
[*] --> RequestValidation
RequestValidation --> Rejected: невалидно / неавторизовано / слишком большое
RequestValidation --> Streaming: переслано агенту
Streaming --> Streaming: Allow / Transform (по событию)
Streaming --> Buffering: TOOL_CALL_START
Buffering --> Buffering: TOOL_CALL_ARGS
Buffering --> Streaming: TOOL_CALL_END → вердикт по полному вызову
Streaming --> Terminated: Deny / Terminate (RUN_ERROR)
Streaming --> [*]: RUN_FINISHED
Rejected --> [*]
Terminated --> [*]
Шов «событие → вердикт»¶
Инспекция производит, по каждому событию (или по буферизованной логической единице), вердикт:
| Вердикт | Значение |
|---|---|
Allow |
переслать без изменений |
Deny(reason) |
заблокировать; на «ноге» ответа выдать как RUN_ERROR |
Transform(replacement) |
переслать изменённое событие (например, редактированный контент) |
Buffer |
нужно больше кадров перед решением (например, в середине вызова инструмента) |
Terminate(reason) |
завершить run/поток |
Этот единственный шов — место, куда через порты подключаются два контекста:
agate-policy вычисляет вердикт (порт PolicyPort), а
agate-audit записывает (событие, вердикт). Прокси зависит
только от портов; конкретные адаптеры политики и аудита внедряются в корне
композиции server. Первая веха поставляет тривиальный адаптер
политики allow-all за PolicyPort.
Язык домена¶
Session/Run— агрегат(ы) инспекции.InspectedEvent— объекты-значения событий, не зависящие от протокола.Verdict— объект-значение решения, перечисленный выше.
Слои¶
| Слой | Содержимое |
|---|---|
domain |
Чистый: агрегаты инспекции, InspectedEvent, Verdict. Без I/O. |
application |
Сценарии и порты: PolicyPort (источник вердикта), приёмник аудита, клиент вышестоящего агента, рекордер ProxyMetrics. |
infrastructure |
Адаптеры: SSE-кодек AG-UI (инкрементальный, сохраняющий порядок), валидация RunAgentInput, HTTP-клиент к агенту. |
presentation |
HTTP/SSE-обработчики (axum/hyper), терминирование TLS, обвязка запрос/ответ. |
setup |
Корень композиции: ProxyConfig (AGENT_ENDPOINT, BIND_ADDR), сборка. |
Инспекция «ноги» запроса¶
Перед форвардингом прокси инспектирует входящий RunAgentInput (превентивно —
агент никогда не запускается на отклонённом вводе):
- Валидация — тело, не являющееся валидным JSON
RunAgentInput, отклоняется с400; его размер уже ограничен (см. Конфигурацию). - Авторизация инструментов — каждый инструмент, который клиент предлагает,
оценивается тем же
PolicyPort, что и на «ноге» ответа (проецируется наAgentEvent); предложенный инструмент, запрещённый политикой, отклоняет run с403. - Маркеры секретов — пользовательское сообщение с настроенным маркером
редакции трактуется как срабатывание политики и отклоняется с
403. - SSRF-гард — текст пользовательских сообщений сканируется на URL; не-
http(s)схема либо loopback / приватный / link-local хост (включая адрес облачных метаданных) отклоняют run с403. Best-effort и только по литеральному хосту — DNS не резолвится, поэтому DNS-rebinding вне области.
Каждый отказ записывается в приёмник аудита, поэтому отказы на «ноге» запроса лежат в журнале прозрачности рядом с решениями «ноги» ответа.
Наблюдаемость¶
Метрики плоскости данных идут через порт ProxyMetrics, а не через counter!
из слоя presentation. Обработчик запуска и потоковый инспектор фиксируют
agate_runs_total, agate_events_inspected_total{outcome} и
agate_upstream_errors_total через внедрённый порт — поэтому inspect_stream
принимает порт и юнит-тестируется с фейковым рекордером. Реальный адаптер пишет
через фасад metrics (no-op, пока сервер не установит рекордер
Prometheus).
Fail-open или fail-closed¶
Реальная политика может обращаться к внешним сервисам и зависнуть. FailModePolicy
декорирует PolicyPort, ограничивая каждое решение таймаутом
([policy].decision_timeout_ms) и применяя настроенный режим при его превышении:
fail-open пересылает событие, fail-closed останавливает его. По умолчанию —
closed (безопасность важнее доступности). На «ноге» ответа fail-closed-таймаут
завершает поток; на «ноге» запроса — отклоняет run до форвардинга. Декоратор оборачивает любую политику
композицией, поэтому реализации политики ничего не знают о таймаутах — см.
Конфигурацию.