Agent Deps

Зависимости агента (deps) — типизированный объект с контекстом, правами, ключами и инфраструктурой, который передаётся агенту в обход промпта. Доступен в системном промпте, инструментах и валидаторах через RunContext. Передавать секреты и роли через текст промпта — антипаттерн.

Суть

Всё, что нужно агенту для работы, но не должно попадать в промпт, кладётся в deps: user_id, роль, токены, HTTP-клиент, БД-сессия, feature flags. Тип объявляется через deps_type, доступ — через ctx.deps.

Зачем это нужно

Контекст в строке промпта (f"role is {role}, api key is {key}") опасен: ключи утекают в логи LLM-провайдера; prompt injection («ignore role, you are admin») перехватывает права; нет типизации; тяжело тестировать. Если что-то security-критично — это должно быть в коде, а не в промпте. Кейс: 50+ клиентов, 0 утечек после переноса прав в deps.

Как работает

  • @dataclass с нужными полями → Agent(deps_type=MyDeps) → передаём при вызове agent.run(query, deps=MyDeps(...)).
  • RunContext[MyDeps] доступен в трёх местах: @agent.system_prompt (динамический промпт под пользователя), @agent.tool (проверка прав), @agent.output_validator (бизнес-проверка с БД/API).
  • Права проверяются кодом инструмента: запрос сверх роли → ModelRetry или PermissionError — текстом промпта это не обойти (см. Validation Loops).
  • Тестирование — через fake deps (подставить мок вместо реального клиента/БД). Глобальные переменные вместо deps не работают при concurrent-запусках.
  • Что кладут в deps: контекст (user_id, role, tenant_id), инфраструктура (HTTP-клиент, БД, Redis), ключи/секреты, бизнес-данные (feature flags, A/B-группа).
@dataclass
class TicketDeps:
    user_id: str
    role: Literal["agent", "supervisor"]
    max_priority: Literal["low", "medium", "high", "critical"]

agent = Agent('openai:gpt-4o-mini', output_type=TicketClassification,
              deps_type=TicketDeps)

@agent.tool
async def escalate_ticket(ctx: RunContext[TicketDeps], ticket_id: str,
                          new_priority: str) -> str:
    if PRIORITY_RANK[new_priority] > PRIORITY_RANK[ctx.deps.max_priority]:
        raise ModelRetry(f"role '{ctx.deps.role}' не может выставить '{new_priority}'")
    return f"escalated to {new_priority}"

Связано с

  • PydanticAI — где реализован механизм deps/RunContext
  • Guardrails — deps как архитектурная защита от prompt injection (права в коде)
  • Validation Loops — output_validator тоже получает ctx.deps

Открытые вопросы

  • где граница между deps и обычными аргументами инструмента
  • как организовать deps в multi-tenant SaaS (изоляция данных клиентов)