Skip to main content
Для агента, который вы написали сами, или фреймворка, для которого у Failproof AI нет адаптера. Инструментировать нечего — вы сами генерируете события. Это тот же API, который используют четыре адаптера фреймворков. Они — таблицы трансляции над ним.

Установка

Никаких дополнений и зависимостей.

Инструментирование

Читайте сверху вниз — и станет ясно, что это означает: И что каждый из них фактически генерирует: Всё внутри может опустить session_id и agent_id. Области привязывают идентификацию на переменные контекста и каждый вызов события читает её обратно, поэтому вам никогда не нужно передавать id через функции. Все три работают с async with наравне с with. Вложенные агенты строят дерево. parent_id и глубина вычисляются из стека:

Как закрывается область

agent() обрабатывает исключения за вас: Ошибка генерируется перед agent_end, потому что панель управления закрывает span при agent_end и всё после этого не атрибутируется ничему. Отмена — не ошибка, поэтому отменённые запуски не загромождают поверхность ошибок. Исключение всегда переиспускается: область никогда его не подавляет.

Методы события

Пятнадцать методов в шести семействах. Большинство идут парами — вы генерируете открывающее событие, затем закрывающее, и SDK измеряет промежуток между ними.
Предпочитайте области — agent() и tool_call() — везде, где они подходят. Они гарантируют закрывающее событие даже когда тело выбрасывает исключение. Обращайтесь к этим методам напрямую, когда ваш управляющий поток не вложен, например вызов модели внутри вспомогательной функции.
Два семейства human указывают в противоположных направлениях.Ни один фреймворк не сигнализирует вторую пару, поэтому её всегда нужно генерировать вам.
Передавайте request_id когда вызовы модели выполняются одновременно. Без него запросы и ответы сопряжаются в порядке поступления за агента — и одновременные вызовы неправильно сопрягаются, присоединяя каждый ответ к неправильному запросу.

Пример

Цикл вызовов инструментов против OpenAI API без фреймворка агента:
Это генерирует те же шесть типов событий, которые даст адаптер. Полная рабочая версия с определениями инструментов поставляется в репозитории SDK в docs/manual/examples/.

Потоки и async

Переменные контекста автоматически распространяются в asyncio задачи. Они не распространяются в новые потоки, потому что поток начинается с пустым контекстом.
Без propagate() события рабочего выбросят TypeError с названием исправления вместо приземления без session. Это намеренно: событие без session пропускается при обработке и ему ответили 200, что — это скрытый отказ, который существует уровень идентификации чтобы предотвратить.

Инструментирование фреймворка без адаптера

Каждый фреймворк агента даёт вам те же три точки. Отобразите их — и у вас есть полная трассировка. Четыре поставляемых адаптера делают ровно это.
1

Ограничьте запуск

2

Ограничьте каждый инструмент

В чём бы фреймворк ни называл обёртку инструмента или middleware.
3

Сопряжьте каждый вызов модели

Есть граница узла, шага или middleware, достойная внимания? Оберните её в пару hook — hook_triggered / hook_completed — а не вложенный agent(). agent_id — низко-кардинальный фасет, и одна запись на узел его захламляет. Span-ы hook отображаются так же и дают вам задержку за узел.
Ручное и автоматическое взаимодействуют. Адаптер, работающий внутри ручной области, присоединяется к этой session и становится родителем этого агента, поэтому вы получаете одно дерево вместо двух — это полезно когда вы инструментируете один фреймворк сами наряду с поддерживаемым.
Две причины, и три точки выше — ответ на обе:
  • autogen-core не обслуживается с сентября 2025.
  • AG2 не раскрывает точку регистрации масштаба процесса, эквивалентную хукам других фреймворков, поэтому инструментирование означает обёртывание каждого агента на каждом месте конструирования.
Ручное отображение точек записывает те же события с той же точностью, что и поставляемый адаптер.

Глубже

Как запись фактически работает. Ничего из этого не требуется для начала.
Каждая запись имеет одну форму: span открывается, работа вложена внутри, и каждому открывающему событию соответствует закрывающее.Пара — это единица. Каждое закрывающее событие несёт длительность, которую SDK измеряет от открывающего.Ниже один реальный запуск на фреймворк — захвачен из примеров, которые поставляются с SDK, имя модели нормализовано. Обратите внимание, сколько возвращается из одного вызова.
14 events
Узлы становятся парами hook, поэтому вы получаете задержку за узел без того, чтобы они загромождали список агентов.
Нет события завершения session. Session — это не то, что вы закрываете — это группа событий, разделяющих session_id.Статус выводится из формы трассировки:Поэтому session заканчивается когда каждая пара закрыта. Адаптеры генерируют agent_end для вас, и при завершении они закрывают всё ещё открытое и отмечают его неполным — упавший запуск урегулируется как done с видимым пробелом вместо вечного зависания.
Вот почему session может охватывать два вызова. interrupt() LangGraph пауза запуска, root span намеренно остаётся открыт, и возобновляющий вызов его закрывает. Оба вызова — одна session.
session_id и agent_id опциональны для каждого метода события. Опущены — они разрешаются из вмещающей области:
Их явная передача всё ещё работает и имеет приоритет. Без привязанного и без переданного вызов выбросит TypeError с названием исправления вместо того чтобы генерировать событие без session, которое ingest пропустит при ответе 200.Области привязывают идентификацию на переменные контекста. Они распространяются в asyncio задачи автоматически но не в новые потоки — оберните рабочего в failproofai_sdk.propagate().

Кто создаёт какой id

Как адаптеры разрешают session_id

Первое совпадение выигрывает:
  1. Явная опция session_id
  2. Per-call метаданные
  3. Вмещающая область session()
  4. Метаданные фреймворка
  5. Собственный run id фреймворка
Она никогда не синтезируется пока одна из них существует — синтезированный id расколол бы один запуск на несколько session.

Держите agent_id низко-кардинальным

Это первичный фасет на каждой поверхности панели управления и LowCardinality(String) колонка. Per-run значение деградирует колонку и заполняет выпадающий фильтр одной записью за запуск.Адаптеры защищают эту колонку для вас:Реальный id хранится на fw_agent_id / fw_run_id, где остаётся queryable без того чтобы быть фасетом.
Эта защита только трогает ярлыки, которые фреймворк выбрал. agent_id, который вы сами передали — в event.* или в failproofai_sdk.agent(...) — записывается ровно как дано. Молчаливое переписывание явного аргумента было бы хуже чем кардинальность, которую оно предотвращает, поэтому называйте ваши span-ы сами соответственно.
Какой фреймворк что записывает, измеренный от запусков выше:Тире означает фреймворк не имеет такой концепции. human_pause и human_interrupt описывают человека, действующего на агента, что ни один фреймворк не сигнализирует — генерируйте их сами.
Событие никогда не приходит одно. Одно открывает span, одно закрывает его, и закрывающее событие несёт длительность, которую SDK измеряет от открывающего.
Открывающее событие без закрывающего — это span, который никогда не завершается. Session отображается как всё ещё работающий, навсегда, и его активная длительность продолжает расти. Это режим отказа, за которым нужно следить когда вы инструментируете вручную.

Правила корреляции

  • Переиспользуйте тот же tool_call_id, hook_id, pause_id или input_id для соответствующего события завершения.
  • SDK вычисляет duration_ms для tool_result, hook_completed, agent_resume и human_input. Передача его этим методам выбросит ValueError.
  • duration_ms принят на model_response, потому что только вызывающий знает реальную задержку провайдера. Это должно быть целое число — float выбросит ValueError на месте вызова, потому что сервер читает колонку как 32-битное целое число без знака и сохранит NULL для чего-либо ещё.
  • Ключи корреляции ограничены видом и session, поэтому вызов инструмента и hook могут безопасно разделить id, и две одновременные session могут переиспользовать те же id без столкновения. Они не ограничены агентом: пара открытая под одним агентом и закрытая под другим всё ещё коррелирует, это обычный случай в multi-agent фреймворках.
  • request_id сопрягает model_request с model_response. Без него события модели сопрягаются в порядке за агента, поэтому одновременные вызовы неправильно сопрягаются.
  • Пара разделённая через процессы всё ещё коррелирует downstream, но SDK не может вычислить её in-process длительность.
  • Ожидающая карта держит максимум 10,000 стартов и вытеснит самую старую запись когда полна.
Установка failproofai-sdk устанавливает всё, все четыре адаптера включены. Extras вытягивают фреймворк, а не адаптер.
import failproofai_sdk контрактно ноль-зависимости, принудительно тестом который устанавливает построенное колесо с --no-deps и другое которое доказывает что ни один фреймворк не достигает sys.modules.
Нет атрибута failproofai_sdk.crewai. Адаптеры намеренно не раскрыты на пакете верхнего уровня: трогание одного импортировало бы фреймворк как побочный эффект доступа атрибута, ломая ноль-зависимость обещание. Используйте instrument().
Auto-detection читает sys.modules, не список установленных пакетов, поэтому фреймворк, который вы установили но никогда не импортировали, не инструментируется и никогда не импортируется от вашего имени. Чтобы увидеть что подключено:
instrument("crewai") на машине без CrewAI не выбросит. Это логирует предупреждение и возвращает (), поэтому один отсутствующий фреймворк никогда не возьмёт процесс который также инструментирует других.Предупреждение несёт базовую ImportError, и это сообщение называет точную команду установки — поэтому исправление в ваших логах, не спрятано.
Установите FAILPROOFAI_SDK_STRICT=1 чтобы это выбросило вместо этого. Этот флаг читается один раз и кэшируется, поэтому экспортируйте его перед тем как ваш процесс начнётся вместо того чтобы устанавливать mid-run.
instrument() должен прийти после вашего импорта фреймворка. Auto-detection читает sys.modules, поэтому чистый вызов выше импорта находит ничего, устанавливает ничего, и возвращает ().
Получите это неправильно и процесс работает с импортированным SDK, видимо установленным адаптером, и не одно событие не генерируется. Это логирует предупреждение говоря ровно это — поэтому проверьте ваши логи первым когда запуск ничего не записывает.
Spool — это то что делает это безопасным: ваш агент никогда не блокируется на сети, и Cloud outage означает растущую директорию вместо потерянных событий.Каждый flush пишет один batch файл, .tmp сначала, затем fsync, затем атомарное переименование:
Daemon только подбирает .jsonl, поэтому никогда не может прочитать наполовину написанный файл. Stem несёт timestamp, process id и sequence number, поэтому два процесса flushing в той же миллисекунде не могут столкнуться. Очередь ограничена 10,000 событиями; сверх этого она отбрасывает самое старое и логирует.
collector.redact не применяется к вашим SDK событиям. Он их никогда не видит.
Daemon доставляет ваши батчи. Он их не открывает и не переписывает.Редакция работает где daemon пишет его собственные события — не где батчи доставляются. Поэтому prompt или инструмент argument держащий API key всё ещё держит его по прибытии.Это намеренно. Это ваши собственные вызовы инструментирования, и переписывание их в пути бы означало события которые вы получаете не события которые вы генерировали.
Вы контролируете payloads на источнике в двух местах:
  • Выключите захват контента на адаптере. Имя опции отличается и один адаптер не имеет — это не один универсальный переключатель:
    • LangChain / LangGraph, Pydantic AI — capture_content=False
    • LlamaIndex — capture_messages=False
    • CrewAI — no content switch at all; session_id единственная опция, которую он читает, поэтому prompts и completions всегда записываются.
    instrument() отбрасывает опции адаптер не читает, поэтому передача неправильного имени выбросит ничего и изменит ничего.
  • Не передавайте secret в input= в первую очередь.
collector.redact не является заменой для обоих.
Пустая spool директория — здоровое состояние. Не используйте её для проверки доставки.
Daemon удаляет каждый батч в миллисекундах доставки, поэтому ls гонится по collector и показывает fraction того что вы генерировали — неразличимый от SDK который ничего не записал.Чтобы подтвердить события действительно приземлились, проверьте панель управления. Чтобы看着 spool заполняться, сначала остановите daemon.
Каждый обратный вызов работает внутри обёртки чья единственная работа переиспустить, поэтому ваш вызов сидит в ровно одном try и всё что SDK делает происходит вне его.Default правильный в production и неправильный при debugging, потому что может только когда-либо доказать that it did not crash. Установите FAILPROOFAI_SDK_STRICT=1 чтобы сделать проглоченную ошибку громкой.

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

Открывающее событие не имеет закрывающего: model_request без model_response или tool_use без tool_result. Используйте области, которые гарантируют пару даже когда тело выбросит. Если вы вызываете методы события напрямую, используйте try и finally.
Это измеряется от соответствующего открывающего события, поэтому отклоняется на tool_result, hook_completed, agent_resume и human_input. Принято на model_response, потому что только вы знаете реальную задержку провайдера, и это должно быть целое число.
Поток никогда не наследовал контекст. Оберните callable в failproofai_sdk.propagate(). См. Потоки и async.
Дополнительные поля слияны последние, поэтому одно названное как реальное поле такое как model или outcome переписало бы его и изменило сохранённую колонку. Пространство имён ваши; адаптеры используют префикс fw_.
agent_id — низко-кардинальный фасет и вы положили в него run id. Используйте role или имя узла и положите реальный id в поле payload.

Дальше

Как это работает

Пары, id, жизненный цикл session и доставка.

Читай трассировку

Следите за причинностью через session которую вы только что захватили.

Адаптеры фреймворков

LangGraph, CrewAI, LlamaIndex и Pydantic AI.