
Сесії, трейси та спани в LLM-спостережності: один із них не структурний рівень
Сторінка термінів Datadog, перший результат Google за запитом LLM observability sessions traces spans, визначає два з цих трьох слів. Не три. Те, якого бракує, відповідає gen_ai.conversation.id, і причина в тому, що специфікація OpenTelemetry ніколи не робила його структурним рівнем. Якщо вам потрібне обґрунтування самої спостережності, почніть звідси. Ця стаття починається там, де та закінчується: модель даних.
Ключові висновки
- Спани вкладаються в трейси; трейси групуються в сесії. Вкладеність іде зсередини назовні: спан, потім трейс, потім сесія.
- Спан: одна операція з виміряною тривалістю. Трейс: один запит від початку до кінця. Сесія: одна багатохідна розмова.
- Конвенції OpenTelemetry GenAI визначають спани та атрибут
gen_ai.conversation.id. Рівень сесії вони не визначають. - ID трейса та спана поширюються автоматично через контекст. ID сесії ні. Ви задаєте його власноруч на кожному ході.
Сесії проти трейсів проти спанів: короткий огляд
У LLM-спостережності спан це одна операція з виміряною тривалістю (виклик моделі, етап вибірки), трейс це дерево спанів, яке породжує один запит, а сесія групує багато трейсів з однієї розмови. Вкладеність іде всередину: спани всередині трейсів, трейси всередині сесій. Третє групування якраз і є тим, що не таке, яким здається.
| Рівень | Що він охоплює | Скільки живе | Хто задає ID | На що відповідає | Типова кількість на розмову |
|---|---|---|---|---|---|
| Сесія | Багато трейсів з однієї розмови користувача | Від хвилин до днів; завершується за тайм-аутом бездіяльності або явним закриттям (визначає вендор) | Ви, вручну, на кожному ході | Чи була ця розмова успішною загалом? | 1 |
| Трейс | Один запит від початку до кінця, або один хід | Від мілісекунд до секунд | Автоматично (SDK / OTel) | Що сталося на цьому ході? | Зазвичай 5–20 |
| Спан | Одна операція: вибірка, виклик моделі, виклик інструменту | Від субмілісекунд до секунд | Автоматично (SDK / OTel) | Який крок був повільним, помилковим чи дорогим? | Приблизно 3–30 на трейс |
Ці цифри кількості й тривалості це типові діапазони, яких варто очікувати в RAG-чатботі чи агентному циклі, а не виміри з контрольованого тесту. Ваші числа відрізнятимуться. Що не відрізнятиметься: рядок «Сесія» той самий, що не є структурним рівнем у специфікації, і розділ «Сесії: рівень, який, ймовірно, вигадав ваш інструмент» це доводить.
Що таке спан і що таке вид спана?
Спан це одна операція з виміряною тривалістю, яка має назву, початкову мітку часу, кінцеву мітку часу, код стану та набір атрибутів «ключ-значення». У трасуванні LLM саме в атрибутах живуть корисні дані: gen_ai.usage.input_tokens, gen_ai.usage.output_tokens та gen_ai.request.model кажуть, скільки коштувала операція і яка модель її виконала.
Спан це одна операція, а не один виклик функції
Кожен спан несе вказівник на ID батьківського спана (порожній у кореневого спана), що будує дерево. Набір атрибутів відкритий: ви додаєте будь-який контекст, який потрібен. Конвенції спанів OpenTelemetry GenAI (статус: Development) вимагають gen_ai.operation.name і gen_ai.provider.name на кожному GenAI-спані та рекомендують наведені вище атрибути споживання токенів.
Одне практичне правило зі сторінки термінів Datadog: спани LLM, Workflow та Agent можуть бути кореневим спаном; спани Tool, Task, Embedding і Retrieval не можуть. Це правило Datadog, а не універсальне, але Datadog єдиний із вендорів формулює його явно, і воно рятує вас від трейса, який починається з виклику інструменту без батьківського елемента.
Види спанів: та сама ідея, п'ять словників
Кожен інструмент потребує способу сказати «цей спан це виклик моделі» на противагу «цей спан це вибірка». Просто вони не згодні щодо слова:
| Інструмент | Його слово для «виду операції» | Значення |
|---|---|---|
| OpenTelemetry GenAI | атрибут gen_ai.operation.name | 15 загальновідомих значень (chat, embeddings, execute_tool, invoke_agent, retrieval і ще 10); одне з них МАЄ використовуватися, якщо воно застосовне, власні значення дозволені, коли жодне не підходить |
| Datadog | Span kind | LLM, Workflow, Agent, Tool, Task, Embedding, Retrieval |
| OpenInference / Phoenix | Span kind | CHAIN, LLM, TOOL, RETRIEVER, RERANKER, EMBEDDING, AGENT, GUARDRAIL, EVALUATOR, PROMPT |
| Langfuse | Observation type | generation, span, event |
| LangSmith | Run type | LLM, chain, tool, retriever |
Специфікація OpenInference перелічує десять видів. Datadog перелічує сім. OTel обирає третій шлях: його реєстр атрибутів GenAI публікує 15 загальновідомих значень для gen_ai.operation.name (chat, create_agent, create_memory, create_memory_store, delete_memory, delete_memory_store, embeddings, execute_tool, generate_content, invoke_agent, invoke_workflow, plan, retrieval, search_memory, text_completion) і стверджує: якщо одне з них застосовне, МАЄ використовуватися саме воно; власне значення МОЖНА використовувати лише тоді, коли жодне не підходить. Тобто це напіввідкрита енумерація, а не її відсутність. Три списки, три різні довжини, і жодного узгодження між ними. Якщо ви обираєте інструмент, ця розбіжність у словниках важить більше за список функцій, бо саме на ній будуватимуться ваші дашборди та фільтри сповіщень.
Що таке трейс і чому форма дерева має значення?
Трейс це дерево спанів, породжене одним запитом. Один кореневий спан стоїть на вершині; усі інші спани звисають під ним через ребра «ID батьківського спана». Форма дерева це вся суть: плоский лог каже, що щось було повільним, але дерево каже, який саме крок був повільним і який крок видав неправильний результат.
chat_request (root) 2,340ms
├── retrieval 410ms
│ └── rerank 85ms
├── chat gpt-4o 1,720ms
└── tool_call: search_calendar 190msПрочитайте це дерево, і діагноз миттєвий: 74% затримки припало на виклик моделі, а не на вибірку. Плоский лог із п'яти міток часу дасть вам ту саму суму, але не дає жодної атрибуції.
Агентний цикл робить це дерево глибшим і ширшим, ніж звичайний RAG-запит. Кожен виклик інструменту породжує власне піддерево; п'ятикроковий хід агента легко породжує 30+ спанів під одним коренем. Це нормально, і саме тому існує наведене нижче питання про гранулярність спанів.
Різниця між трасуванням і логуванням тут теж важлива: логування записує події, трасування записує причинність. Якщо ви досі вирішуєте, що логувати, а що трасувати, наша стаття найкращі практики логування LLM проводить цю межу.
Сесії: рівень, який, ймовірно, вигадав ваш інструмент
Ні. Сесія не є структурним рівнем у конвенціях OpenTelemetry GenAI. Специфікація визначає спани та атрибут gen_ai.conversation.id (умовно обов'язковий, «за наявності», статус: Development), описаний як унікальний ідентифікатор розмови чи гілки для кореляції повідомлень. Вендори потім будують власний об'єкт сесії поверх цього атрибута. Ніхто інший на цій сторінці видачі не називає статус специфікації прямо, тож ось він.
Наслідок: речення, заради якого існує вся ця стаття.
Сесія — це ключ групування, а не батьківський спан. Вона не поширюється так, як ID трейса; ви задаєте її самостійно на кожному ході.
Пропустіть один хід, і цей хід випаде з сесії. Автоматичного поширення контексту для неї не існує.
Коли сесія починається і закінчується?
Визначає вендор. Деякі інструменти відкривають сесію на першому трейсі з новим ID розмови та закривають її за тайм-аутом бездіяльності (Langfuse типово має налаштовуване вікно). Інші вимагають явного виклику закриття. Специфікація нічого не каже про життєвий цикл, бо специфікація не моделює сесію як об'єкт.
Що переноситься між ходами, а що ні?
Контекстне вікно моделі не є сесією. Сесія є ключем групування поверх незалежних трейсів. Кожен хід отримує власний трейс, власний кореневий спан, власну кількість токенів. Переноситься атрибут ID розмови, який ви поставили на кожному кореневому спані. Не переноситься: затримка, споживання токенів, структура спанів. Це все на рівні окремого трейса.
Що вимірює метрика рівня сесії?
Те, чого не може один трейс: частка вирішення (чи розв'язала розмова проблему користувача?), ходи до відповіді (скільки трейсів минуло, перш ніж користувач отримав потрібне?) та покинуті розмови (сесії без сигналу закриття). Запуск оцінювань на живих трейсах на рівні сесії ось як ви ловите багатохідні збої, які виглядають нормально хід за ходом.
Код, без прив'язки до вендора
Цей фрагмент використовує лише стабільні примітиви OTel. Жодного SDK вендора. Він створює кореневий спан для одного ходу, дочірній спан для вибірки, дочірній для виклику моделі та встановлює gen_ai.conversation.id, щоб три ходи потрапили в одну сесію:
from opentelemetry import trace
tracer = trace.get_tracer("my-llm-app")
SESSION_ID = "conv-8f3a2c" # same value on every turn
def handle_turn(user_message: str):
with tracer.start_as_current_span("chat_request") as root:
# You set this. It does not propagate automatically.
root.set_attribute("gen_ai.conversation.id", SESSION_ID)
with tracer.start_as_current_span("retrieval") as ret:
ret.set_attribute("gen_ai.operation.name", "retrieval")
docs = retrieve(user_message)
with tracer.start_as_current_span("chat gpt-4o") as llm:
llm.set_attribute("gen_ai.operation.name", "chat")
llm.set_attribute("gen_ai.provider.name", "openai")
llm.set_attribute("gen_ai.request.model", "gpt-4o")
response = call_model(user_message, docs)
llm.set_attribute("gen_ai.usage.input_tokens", 1_204)
llm.set_attribute("gen_ai.usage.output_tokens", 312)
return responseВикличте handle_turn тричі з тим самим SESSION_ID, і всі три трейси згрупуються в одну сесію в будь-якому бекенді, який читає цей атрибут. Змініть ID, і ви почали нову сесію. Ось і весь механізм.
Ми прочитали документацію п'ятьох вендорів пліч-о-пліч. Вони не згодні.
30 липня 2026 року ми прочитали чинну документацію моделей даних Langfuse, LangSmith, OpenInference / Phoenix і Datadog пліч-о-пліч, плюс специфікацію спанів OpenTelemetry GenAI. Четверо з п'ятьох називають той самий об'єкт по-різному. Лише один трактує сесію як повноцінний об'єкт, а не атрибут. Сторінка термінів Datadog, перший результат Google за цим запитом, узагалі не визначає сесію.
| Концепт | Семантичні конвенції OTel GenAI | Langfuse | LangSmith | OpenInference / Phoenix | Datadog |
|---|---|---|---|---|---|
| Уся розмова | атрибут gen_ai.conversation.id | Session (необов'язкове групування трейсів) | Thread (через метадані session_id / thread_id) | атрибут спана session.id | Не визначено на сторінці термінів |
| Один запит | Trace | Trace | Trace («колекція ранів») | Trace | Trace |
| Одна операція | Span | Observation (span / generation / event) | Run («спан, що представляє одну одиницю роботи») | Span із видом спана | Span із видом спана |
Одна примітка щодо джерела в першому рядку: session.id в OpenInference немає в специфікації трейсів, на яку посилаємося вище і яка охоплює десять видів спанів. Він визначений у сусідньому файлі семантичних конвенцій OpenInference як унікальний ідентифікатор сесії. Два файли, одна специфікація.
Ми не вигадували міжвендорне порівняння; FutureAGI теж публікує таблицю OTel проти вендорів. Наші два доповнення це рядок сесії (FutureAGI його пропускає) і пастка «те саме слово, інше значення»: «observation» у Langfuse і «run» у LangSmith це той самий об'єкт, що й спан, тоді як види спанів у Datadog та OpenInference це різні словники для тієї самої ідеї.
Langfuse називає це observation, LangSmith називає це run, Datadog називає це span. Той самий об'єкт, три дашборди, які ламаються під час міграції.
Це наше прочитання вартості міграції, а не заява вендора. Але саме тому збережені фільтри, конфігурації оцінювань і правила сповіщень, прив'язані до «observation» чи «run», перестають працювати того дня, коли ви змінюєте інструмент. Ви не перейменовуєте поле. Ви перейменовуєте рівень. Якщо ви зважуєте між цими двома конкретними інструментами, наше порівняння Langfuse та LangSmith глибше розбирає цю розбіжність.
Читачі також можуть уже мати у своєму стеку Opik, PostHog, Sentry чи Weights & Biases; Google пов'язує всі чотири з llm tracing, і кожен відображає ці концепти трохи інакше. Обираєте правильний? Наш огляд платформ спостережності охоплює весь ринок.
Одна примітка про свіжість: конвенції GenAI переїхали у власний репозиторій з основного репозиторію semantic-conventions. Старий шлях opentelemetry.io/docs/specs/semconv/gen-ai/ тепер містить лише вказівник.
Який ID куди йде?
ID трейса ідентифікує один запит і поширюється через контекст автоматично. ID спана ідентифікує одну операцію всередині цього трейса, теж автоматично. ID кореляції (або ID запиту) приходить з вашого веб-рівня ще до початку трасування, і саме його найчастіше плутають з ID трейса. ID сесії тут зайвий: його задаєте ви, вручну, на кожному ході.
| ID | Хто задає | Область дії | Плутають із |
|---|---|---|---|
| ID трейса | Автоматично | Один запит; поширюється через контекст | ID кореляції з вашого веб-рівня |
| ID спана | Автоматично | Одна операція | , |
| ID батьківського спана | Автоматично | Будує дерево; порожній у кореневого спана | , |
| ID сесії / розмови | Ви, вручну, на кожному ході | Багато трейсів | Вважають, що поширюється. Ні. |
| ID користувача | Ви, вручну | Багато сесій | ID сесії |
| ID запиту / кореляції | Ваш веб-рівень, до початку трасування | Один HTTP-запит | ID трейса (це найчастіше) |
Практичне правило: прикріплюйте gen_ai.conversation.id як атрибут спана до кореневого спана кожного ходу і ставте ID користувача поруч. Пропустіть один хід, і ваші метрики рівня сесії тихо втратять цей хід.
Одне застереження щодо кардинальності: ID користувачів та ID сесій це значення з високою кардинальністю. Це важливо для рахунку за індексацію вашого бекенду, а це вже проблема наступного розділу.
Наскільки гранулярним має бути спан?
Два режими відмови, обидва поширені:
Надмірне подрібнення. Спан на кожен виклик функції дає вам трейс на 400 спанів, який ніхто не зможе прочитати, і рахунок за кожен спан, під яким ніхто не підписувався. Керовані бекенди (Datadog, Langfuse Cloud) тарифікують за обсягом спанів. Балакучий агентний цикл, який інструментує кожну конкатенацію рядків, спалить безкоштовний тариф за одне післяобіддя.
Недостатнє подрібнення. Один спан на «весь ланцюжок» каже, що було повільно, але не каже де. Ви закінчуєте тим, що повертаєте print-вирази, які трасування мало замінити.
Емпіричне правило (і це саме емпіричне правило, а не вимір): покривайте спанами межі, де відбувається рішення або зовнішній виклик.
- Етап вибірки: спан.
- Виклик переранжування: спан.
- Кожен виклик моделі: спан.
- Кожен виклик інструменту: спан.
- Кожна перевірка гардрейла: спан.
- Чисті внутрішньопроцесні перетворення (форматування рядків, парсинг JSON, складання промпту): атрибути на батьківському спані, не власні спани.
Про кардинальність, семплінг і зберігання:
- Атрибути з високою кардинальністю (ID користувачів, повні промпти) роздувають вартість зберігання. Семплуйте або обрізайте їх.
- Більшість бекендів дають семплувати на рівні трейса. Зберігайте 100% помилкових трейсів; семплуйте щасливий шлях.
- Вікна зберігання різняться: 7 днів на безкоштовних тарифах, 30–90 днів на платних. Вирішіть до того, як дані вам знадобляться.
Щодо фактичної моделі вартості за обсяг спанів і ціни за спан, дивіться наш гід із моніторингу витрат на LLM. Ми не відтворюватимемо її тут.
Як Techsy підходить до цього
У роботі з клієнтськими агентами ми стандартизуємо три правила:
- Один трейс на хід. Ніколи не об'єднуйте два ходи користувача в один трейс, навіть якщо агент циклічно працює всередині.
- ID сесії на кожному кореневому спані, заданий у коді застосунку, ніколи з припущенням про автоматичне поширення.
- Види спанів тримаємо в малому фіксованому наборі (вибірка, інференс, інструмент, гардрейл), щоб дашборди пережили зміну вендора.
Саме третє правило команди пропускають, і саме воно рятує міграцію. Якщо ваш словник спанів прив'язаний до енумерації одного вендора, кожне сповіщення та збережений вигляд ламається того дня, коли ви перемикаєтесь.
Якщо ви будуєте агентну систему й хочете почути другу думку про архітектуру трасування, отримайте безкоштовну консультацію.
Про автора
Мерт Батур — співзасновник Techsy.io, де команда постачає AI-агентів, системи автоматизації та голосові/SDR-конвеєри для B2B-клієнтів. Він пише про стек інструментів LLM, який команда Techsy реально використовує в продакшені. Зв'яжіться через LinkedIn.
Часті запитання
Що таке спан у розподіленому трасуванні?
Спан це одна одиниця роботи з виміряною тривалістю: він має назву, час початку, час завершення, стан і набір атрибутів. Спани з'єднуються один з одним через посилання на ID батьківського спана, утворюючи дерево. У застосунках LLM спан типово охоплює один виклик моделі, одну вибірку або один виклик інструменту.
Що таке спан у Datadog?
У LLM Observability від Datadog спан це та сама операція з виміряною тривалістю, але Datadog додає таксономію видів спанів: LLM, Workflow, Agent, Tool, Task, Embedding і Retrieval. Лише види LLM, Workflow та Agent можуть бути кореневим спаном. Таксономія специфічна для Datadog; вона не є частиною стандарту OpenTelemetry.
Які чотири стовпи спостережності?
Чотири стовпи це логи, метрики, трейси та (залежно від того, чия це рамка) профілі або події. Саме трейси є стовпом, у якому живе ця стаття. Випадок LLM додає нюанс: споживання токенів та ідентичність моделі це атрибути на спанах трейса, а не окремі потоки метрик, що згортає те, що було б двома стовпами, в один запит.
Які чотири золоті сигнали спостережності?
Затримка, трафік, помилки та насичення. Для систем LLM затримка означає час до першого токена та загальний час генерації; трафік означає запитів за секунду на модель; помилки означають невдалі спани (код стану ERROR); насичення означає вичерпання бюджету токенів або глибину черги. Сигнали ті самі; одиниці різні.
Чи є сесія частиною специфікації OpenTelemetry?
Не як структурний рівень. Конвенції спанів OTel GenAI визначають gen_ai.conversation.id як умовно обов'язковий атрибут («за наявності») для кореляції повідомлень у розмові чи гілці. Він лежить на спанах. Вендори на кшталт Langfuse та LangSmith будують власні об'єкти сесії чи гілки поверх нього.
У чому різниця між ID трейса, ID спана та ID кореляції?
ID трейса ідентифікує один запит і поширюється автоматично через усі сервіси нижче за потоком. ID спана ідентифікує одну операцію всередині цього трейса. ID кореляції (або ID запиту) генерується вашим веб-рівнем до початку трасування, і саме це значення найчастіше приймають за ID трейса. Вони перетинаються за областю дії, але походять по-різному.
Скільки спанів має бути в одному трейсі?
Фіксованої відповіді немає, але типові діапазони: 3–30 для RAG-запиту та 10–50+ для агентного циклу з кількома викликами інструментів. Емпіричне правило: покривайте спанами зовнішні виклики та точки рішень, а не внутрішньопроцесні перетворення. Якщо ваш трейс перевищує 100 спанів, ви, ймовірно, інструментуєте зайве.
Чи «observations» у Langfuse це те саме, що спани?
Так. Observation у Langfuse це той самий об'єкт, що й спан OTel: одна операція з виміряною тривалістю та атрибутами. Langfuse ділить observations на три типи (generation, span, event), тоді як OTel використовує gen_ai.operation.name. Якщо ви оцінюєте інструменти, які читають ваші трейси, наш огляд інструментів оцінювання LLM розповідає, які з них приймають обидва словники.
Як згрупувати багатохідну розмову чатбота в одну сесію?
Задайте той самий ідентифікатор розмови на кореневому спані кожного ходу. Мовою OTel це gen_ai.conversation.id. У Langfuse ви передаєте session_id під час створення трейсів. У LangSmith ви задаєте метадані session_id або thread_id. Пропустіть один хід, і цей хід випаде з групування.
Чи потрібні мені сесії, якщо я обробляю лише однохідні запити?
Ймовірно, ні. Сесії існують, щоб корелювати кілька трейсів в одну розмову. Якщо кожен запит незалежний (API класифікації, одноразовий підсумовувач), достатньо метрик рівня трейса. Додавайте сесії, коли вам потрібні метрики між ходами: частка вирішення, ходи до відповіді або вартість на рівні розмови. Наш гід з оцінювання LLM розповідає, коли оцінювання рівня сесії виправдовують себе.
Коротка версія
Спани вкладаються в трейси; трейси групуються в сесії. Вкладеність реальна, але специфікація структурує лише два з трьох рівнів. gen_ai.conversation.id: атрибут, який ви задаєте самостійно, а не батьківський спан, що поширюється. А вендор, якого ви обираєте сьогодні, називає ці об'єкти інакше, ніж вендор, на якого ви перейдете через 18 місяців, тож тримайте свій словник спанів малим і переносним.
Якщо ви обираєте платформу, почніть з нашого порівняння платформ спостережності. Якщо ви будуєте оцінювання поверх своїх трейсів, гід з оцінювання LLM продовжує з цього місця.