Чому агент потребує іншої моделі спостережуваності
Звичайний API-запит зазвичай має зрозумілі початок, обробник і відповідь. AI-агент додає цикл. Він обирає модель, вирішує, чи викликати інструмент, чекає на віддалену систему, читає результат і може знову звернутися до моделі. Один запит користувача може містити кілька викликів моделі, пошукових запитів, виконань інструментів, повторів і перевірок політик. Запис у журналі «запит завершився помилкою» не показує, яка гілка витратила час або бюджет.
Спостережуваність робить цей шлях видимим через пов’язані трейси, метрики й журнали. OpenTelemetry описує трейс як шлях запиту, а span як операцію всередині нього. Використовуйте один кореневий span для запуску агента, який бачить користувач, і дочірні spans для інференсу моделі, пошуку, інструментів, guardrails та серіалізації. Назви моделей та інструментів мають мати низьку кардинальність. Ідентифікатори конкретного запиту зберігайте в контексті трейсу або структурованих журналах, а не в мітках метрик. Так оператор може з’ясувати, що сталося в одному запуску, як часто це відбувається та які робочі процеси зачеплено.
У статті використано схему, незалежну від постачальника. Точні поля використання та правила оплати визначає документація постачальника. Семантичні угоди OpenTelemetry для GenAI spans і для метрик GenAI дають спільний словник для інтеграцій, що розвиваються.
Схема трейсу для циклу агента
Створюйте кореневий span у момент прийняття запиту застосунком, а не під час першого звернення до моделі. Дайте йому операцію на кшталт invoke_agent і атрибути сервісу, розгортання, середовища, версії workflow та несекретного класу орендаря. Ідентифікатор розмови записуйте лише за наявності та дозволеного терміну зберігання. Не додавайте повідомлення користувача, повний промпт або параметри інструмента до мітки метрики.
Кожен дочірній span має відповідати на одне операційне запитання. Корисний мінімум:
| Span | Що записувати |
|---|---|
| agent.run | назву й версію workflow, результат, кількість спроб, тривалість |
| gen_ai.inference | постачальник, запитану й фактичну модель, операцію, stream, причину завершення, токени |
| gen_ai.retrieval | клас індексу або джерела, режим запиту, число результатів, cache hit |
| gen_ai.tool | назву й тип інструмента, рішення авторизації, timeout, результат |
| guardrail.check | версію політики, рішення, код причини, тривалість |
Для невдалої операції задайте статус span і значення error.type. Для повтору запишіть подію з номером спроби, затримкою backoff та кодом причини. Повтор не є другим кореневим запуском. Це ще одна спроба тієї самої логічної операції з окремим client span, якщо запит відправляється мережею. Так дашборд не подвоює користувацькі запити, але показує спроби постачальника.
Нижче наведено форму експортованого запису, а не формат конкретного постачальника. У ній є лічильники й коди, але немає вмісту.
{
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"span_id": "00f067aa0ba902b7",
"parent_span_id": "b7ad6b7169203331",
"name": "chat model",
"kind": "CLIENT",
"status": "OK",
"attributes": {
"gen_ai.operation.name": "chat",
"gen_ai.provider.name": "provider.example",
"gen_ai.request.model": "model.example",
"gen_ai.response.model": "model.example-2026-01",
"gen_ai.usage.input_tokens": 820,
"gen_ai.usage.output_tokens": 146,
"gen_ai.response.finish_reasons": ["stop"],
"app.agent.attempt": 1
}
}
Назва span має описувати клас операції, а не ID чи текст користувача. Зберігайте версію семантичної угоди в метаданих інструментації. Якщо постачальник окремо повідомляє тарифіковані та оброблені токени, зберігайте обидва в приватному обліку, а для вартості використовуйте тарифіковані. Не змішуйте клієнтську й серверну інструментацію одного запиту без позначення рівня, інакше токени порахуються двічі.
Correlation ID і поширення контексту
Trace ID зв’язує сервіси. Span ID позначає одну операцію. Власний request ID зручний для підтримки, але не замінює контекст трейсу. Передавайте заголовок W3C traceparent через API-шлюз, сервіс агента, сервіс пошуку та адаптери інструментів. Посібник OpenTelemetry з поширення контексту описує вилучення віддаленого контексту та створення дочірнього span. Той самий контекст можна додавати до структурованих журналів, щоб перейти від запису до трейсу.
Коли підтримці потрібен короткий ідентифікатор, створюйте окремий випадковий request ID. Відповідність із trace ID зберігайте в журналах, а не у метриці з високою кардинальністю. Для черги додавайте контекст до метаданих повідомлення і створюйте consumer span під час обробки. Для незалежних паралельних задач без одного батька використовуйте span links. На публічній межі перевіряйте вхідні tracing headers і не передавайте внутрішній baggage постачальникам чи стороннім інструментам. Baggage може містити облікові дані або персональну інформацію.
Spans інструментів показують справжню поведінку агента
Коли це важливо, розділяйте рішення моделі та виконання застосунку. Span моделі показує, що модель попросила інструмент. Span інструмента показує, що застосунок реально виконав. Додайте стабільне ім’я tool.name, тип function, extension або datastore, рішення політики та зовнішню операцію. Метод запиту чи клас пошуку додавайте лише без розкриття секретів. Не зберігайте access token, параметри SQL, текст документів або повний URL із query.
Для кожного виклику інструмента записуйте час, налаштований timeout, клас результату, кількість повторів і обмежений розмір результату. Timeout, бізнес-відмова, заборона політики та upstream 5xx мають різні коди причин. Якщо інструмент викликає інший сервіс, передавайте контекст. Якщо запускає локальну команду, зберігайте лише сімейство команди та клас виходу, а не введення користувача.
Показуйте також очікування, яке не є викликом інструмента. Додайте spans для затримки в черзі, сну через ліміт швидкості, відкритого circuit breaker і потокової відповіді. Інакше повільний span моделі може приховувати очікування слота конкурентності. У multi-agent workflow називайте кожен делегований workflow і пов’язуйте його з батьківським трейcом. Не створюйте новий трейс для кожної внутрішньої думки чи зміни стану.
Облік токенів і вартості
Беріть usage з відповіді постачальника, коли це можливо. Вхідні, вихідні, кешовані та reasoning-токени можуть мати різні ставки, як і зображення та одиниці інструментів. Посібник OpenAI про токени пояснює, що токенізація залежить від моделі й мови, а usage у відповіді є основою завершеного запиту. Локальний tokenizer корисний для попередньої оцінки бюджету, але не замінює usage постачальника під час звірки.
Для кожної спроби моделі зберігайте provider, requested model, response model, категорію токена, кількість, валюту, версію прайсу й центр витрат. Розрахунок після відповіді:
cost = input_billable_tokens * input_price
+ cached_input_tokens * cached_input_price
+ output_billable_tokens * output_price
+ provider_units * unit_price
Ціни мають бути конфігурацією з датою чинності, а не константою експортора. Зберігайте сирий лічильник і розраховану суму, щоб виправлення таблиці цін можна було перевірити. Додавайте всі спроби, адже невдалий виклик також може спожити токени. Оціночну або відкладену суму позначайте як попередню та звіряйте зі звітом постачальника.
Показуйте виміри, на які може вплинути власник: workflow, родина моделі, середовище, клас орендаря й результат. Не використовуйте ID користувача, промпт чи довільні аргументи інструмента як виміри метрик. Дашборд вартості має показувати загальну суму, суму на завершений запуск, токени на запуск, частку повторів і частку дорогих моделей. Збільшення вихідних токенів за нормальної затримки може означати необмежену відповідь, цикл або зміну промпту.
Розподіли затримки: p50, p95 і p99
Середнє приховує хвіст, який бачить користувач під час очікування в черзі або повільного інструмента. Записуйте тривалість кореневого запуску та важливих spans як histogram у секундах. Вимірюйте time to first token, інтервал між частинами stream, якщо він впливає на відображення, і повний час. Коли постачальник надає поля, розділяйте сервер, чергу та мережу.
p50 описує типовий випадок, p95 повільних користувачів, а p99 рідкісний важкий хвіст. Це квантілі розподілу, а не три середні. Виберіть buckets навколо фактичного SLO від часток секунди до хвилин і не змішуйте одиниці. Функція Prometheus histogram_quantile оцінює квантиль із buckets. Для класичного histogram спершу агрегуйте за le.
histogram_quantile(
0.95,
sum by (le, workflow) (
rate(agent_run_duration_seconds_bucket[10m])
)
)
Не додавайте trace ID до міток метрик. Розбивайте дані за невеликим контрольованим набором: workflow, родина моделі, регіон і результат. Зразок трейсу пояснить рух p95. Не додавайте тривалості паралельних дочірніх spans, а визначайте критичний шлях у трейсi.
Помилки, повтори й обмеження швидкості
Визначте класифікацію помилок до створення alerts. Розділяйте валідацію клієнта, заборону політики, автентифікацію, rate limit, timeout, upstream error, некоректний вивід моделі, збій інструмента й скасування. Мапуйте коди постачальника у ці стабільні класи, зберігаючи обмежений оригінальний код. Очікуване скасування користувачем позначайте окремо від збою сервера.
Кожен retry повинен мати причину, номер спроби, backoff і фінальний результат. Використовуйте обмежений експоненційний backoff із jitter лише там, де це дозволяє контракт. Не повторюйте валідацію, авторизацію, рішення політики чи детерміновану помилку схеми. Задайте deadline для всього запуску й кожної спроби. Кореневий span повідомляє retry_count, attempt_count і результат, а кожна спроба зберігає власний статус. Велика частка повторів може збільшити вартість за нормального success rate.
Вибір резервної моделі записуйте як подію або span. Дашборд має розрізняти fallback через rate limit, затримку, політику безпеки або перевірку можливостей. Окремо контролюйте неправильні аргументи інструмента і цикли виправлення схеми. Якщо виправлення дозволене, обмежте ітерації та видавайте loop_limit після досягнення межі.
Приватність, редагування і sampling
Промпти та дані інструментів можуть містити персональну, конфіденційну або чутливу для безпеки інформацію. Безпечне значення за замовчуванням — метадані, лічильники токенів, відбитки й коди причин без вмісту. Якщо для відладки потрібні приклади, використовуйте окреме контрольоване сховище зі згодою, коротким retention, шифруванням, журналом доступу та редагуванням полів. Редагуйте дані до експорту.
Застосовуйте allowlist атрибутів. Видаляйте заголовки авторизації, cookie, API keys, контактні дані, номери рахунків, URL із query і текст документів. Хешування не робить дані автоматично анонімними. Тримайте відбиток промпту окремо й документуйте, хто може пов’язати його з текстом. Перевіряйте редагування фікстурами з реалістичними секретами та багатомовними персональними даними.
Sampling зменшує обсяг, але не має приховувати інциденти. Використовуйте parent-based sampling для узгодженості трейсу. У Collector застосовуйте tail sampling, щоб зберігати помилки, timeouts, дорогі й повільні traces, а звичайні успішні запити семплювати. Специфікація sampling OpenTelemetry розрізняє запис та експорт, тому локальний sampler може не створювати дорогі атрибути. Метрики залишайте без sampling, а traces використовуйте для exemplars і розслідування.
OpenTelemetry, Grafana і Sentry разом
Інструментуйте агента API OpenTelemetry та семантичними угодами, надсилайте OTLP до OpenTelemetry Collector, а в Collector застосовуйте batching, memory limits, редагування, sampling і маршрутизацію. Експортуйте traces, metrics і logs до відповідних backends з однаковими атрибутами сервісу, версії, середовища, регіону та розгортання. Перед production sampling перевірте один повний трейс у staging.
Grafana дає спільне операційне представлення. Створіть панелі для обсягу, success ratio, p50/p95/p99 кореневої затримки, часу першого токена, токенів, вартості, повторів, тривалості інструментів і стану постачальника. Додайте посилання з панелей на пошук трейсу та runbook. Документація Grafana про alert rules описує запити, умови, періоди оцінки й сповіщення. Sentry додає групування проблем, контекст помилки та перегляд трейсу. Його Trace API повертає spans і помилки однієї трасси. До будь-якого error backend надсилайте тільки відредаговані дані та явно налаштовуйте sampling.
SLO й alerts, придатні для операторів
SLO має описувати обіцянку користувачу, а не здоров’я Collector. Визначте доступність як частку завершених запусків без класифікованої помилки сервера, постачальника чи інструмента. Затримку можна визначити як частку кореневих запусків нижче заданого порога. Якщо вартість є обмеженням продукту, відстежуйте budget SLI окремо.
Обирайте цілі за виміряною базовою лінією та вимогами продукту. Розділяйте workflow з різними очікуваннями. Показуйте error budget на довгому вікні й короткий діагностичний вигляд для розгортань. Документація Grafana SLO описує SLI, витрати бюджету та fast-burn і slow-burn alerts. Надсилайте page лише тоді, коли оператор може діяти. Повільний тренд краще перетворити на ticket.
Корисні alerts: стійке зростання помилок кореневого запуску, швидке споживання error budget, p95 вище контракту, сплеск rate limit, зростання retry ratio, відсутні usage records, неочікуваний темп вартості та застрягла черга. Додайте workflow, регіон, розгортання, поточне значення, поріг, посилання на трейс, власника й runbook. Pending period не дасть одній точці спричинити page. Групуйте сповіщення за сервісом і severity. Якщо негайної дії немає, потрібен дашборд, а не alert.
Від симптому до причини
Почніть зі SLO або повідомлення користувача й виберіть репрезентативний трейс. Перевірте дочірні spans кореня і поширення контексту через шлюз, чергу та межу інструмента. Якщо spans відсутні, спершу перевірте exporter, sampling та injection контексту.
Для повільних запусків порівняйте затримку черги, time to first token, тривалість виводу, retrieval і критичні шляхи інструментів. Високий p99 за звичайного p50 зазвичай вказує на хвостову залежність, обмеження конкурентності або retry. Зміщення p50 часто означає зміну розгортання, моделі, промпту чи регіону. При стрибку вартості групуйте за фактичною моделлю, категорією токена, версією workflow та спробами й звіряйте usage зі звітом постачальника.
Для помилки починайте з першого невдалого span, а не з обгортки-винятку. Перевіряйте status, error.type, код постачальника, бюджет timeout та події повтору. Розрізняйте відмову інструмента, збій постачальника, некоректну відповідь моделі й помилку парсера. Переконайтеся, що редагування залишило безпечний діагностичний код. Очікуване блокування політикою рахуйте результатом продукту і створюйте alert лише при неочікуваній зміні частки.
Підтримуйте синтетичні запити з детермінованими несекретними фікстурами. Після зміни інструментації, моделі, промпту або маршрутизації перевіряйте, що traces, metrics, записи токенів і помилки мають один correlation ID. Архітектурний контекст є в матеріалах production AI agent architecture, AI agent evaluations, prompt injection and MCP security і hybrid RAG with pgvector. Ці теми визначають потрібні spans та прийнятний SLO, а спостережуваність залишається нейтральним шаром доказів.