Перейти к основному содержимому

Наблюдаемость AI-агентов

Базовый логгер не зависит от домена. Extension pack для AI-агентов добавляет набор пространств имён атрибутов и типизированных событий для приложений вокруг LLM-вызовов, agent-конвейеров, retrieval-augmented generation и диспетчеризации инструментов через Model Context Protocol (MCP).

Расширение опциональное: приложения без AI-конвейеров игнорируют его целиком. Когда pack загружен, перечисленные пространства имён становятся зарезервированными (биндинг отвергает пользовательские события на тех же префиксах); когда не загружен — это обычные пользовательские пространства имён.

Соответствие OpenTelemetry GenAI

Pack обязуется быть совместимым с семантическими соглашениями OpenTelemetry GenAI:

  • Для концепций, покрытых OTel (gen_ai.*, mcp.*), pack использует имена атрибутов OTel как есть, без параллельных ключей под ту же идею.
  • Для концепций, которые OTel ещё не стабилизировал (rag.*, agent.*, prompt.*), pack определяет собственные пространства имён. Когда появится OTel-эквивалент, pack мигрирует на него в следующей мажорной версии logger-spec, с deprecation-периодом для старых ключей.
  • Source-of-truth маппинг запланирован в _meta/conventions/genai_otel_mapping.yaml (v1.1) и будет обновляться к каждому релизу OTel GenAI; v1.0-биндинги поставляют маппинг инлайн.

Бэкенды, уже понимающие OTel GenAI (Datadog, Grafana Cloud, Honeycomb, Langfuse, OpenLLMetry, Helicone), читают записи нативно без per-vendor адаптации.

Пять пространств имён

gen_ai.* — используется как есть из OTel

Стандартные атрибуты LLM-вызова. Самые частые ключи:

АтрибутЗначение
gen_ai.systemopenai, anthropic, gemini, vllm, openrouter, bedrock, ...
gen_ai.operation.namechat, text_completion, embeddings, tool_call
gen_ai.request.modelid запрошенной модели
gen_ai.response.modelid фактической модели в ответе (может отличаться — например, версионирование OpenAI)
gen_ai.request.temperature, .top_p, .max_tokens, .frequency_penalty, .presence_penaltyпараметры сэмплирования
gen_ai.usage.input_tokens, .output_tokens, .cache.read_input_tokensучёт токенов
gen_ai.response.finish_reasons[]stop, length, tool_calls, content_filter, function_call
gen_ai.response.idid ответа от провайдера
gen_ai.tool.name, gen_ai.tool.call.idдиспетчеризация инструмента
gen_ai.prompt.<N>.role, .contentсообщения диалога (подпадают под усечение)

mcp.* — используется как есть из OTel

Наблюдаемость Model Context Protocol по соглашениям OTel MCP:

АтрибутЗначение
mcp.server.name, mcp.server.versionидентификация MCP-сервера
mcp.method.nametools/call, tools/list, resources/list, resources/read, prompts/get, logging/setLevel
mcp.request.idid JSON-RPC запроса
mcp.tool.nameимя инструмента, когда method.name = "tools/call"
mcp.response.is_errorbool
mcp.transportstdio, http+sse, streamable_http

rag.* — специфично для dagstack

У retrieval-augmented generation пока нет стабилизированного пространства имён в OTel, поэтому pack определяет собственное:

АтрибутЗначение
rag.query.original, rag.query.rewrittenпользовательский запрос и его rewrite (режим simple)
rag.retrieval.top_k, .min_score, .results_countпараметры поиска и итог
rag.retrieval.collections[]имена коллекций, по которым шёл поиск
rag.chunk.id, .score, .repo, .path, .line_rangeper-chunk атрибуты в событии chunk_retrieved
rag.reranker.model, .top_n_before_rerankстадия rerank, если используется

agent.* — специфично для dagstack

Состояние agent-loop'а для multi-step конвейеров:

АтрибутЗначение
agent.pipelinesimple, agent, two_agent
agent.roleanalyst, answerer, describer, planner, executor
agent.iteration, agent.iteration.maxсостояние цикла
agent.decisioncontinue, need_more, finish
agent.decision.rationaleсвободное обоснование (v1.1 введёт структурированную форму)

prompt.* — специфично для dagstack

Метрики сборки prompt'а, дополняющие gen_ai.*:

АтрибутЗначение
prompt.template.id, .template.versionпоиск шаблона prompt'а
prompt.section.system.tokens, .user.tokens, .history.tokens, .tools.tokensразбивка по секциям
prompt.total_tokens, .token_budgettotal против budget
prompt.truncatedbool — была ли history усечена под budget
prompt.content.markdownполный собранный prompt (подпадает под усечение)

Типизированные события

Pack публикует схемы событий под event.domain = "ai_agent". Минимальный набор (полная схема будет жить в _meta/events/ai_agent.yaml в v1.1; v1.0-биндинги поставляют per-event списки атрибутов инлайн):

event.nameКогда отправляетсяОбязательные атрибуты
ai_agent.context.assembledprompt готов к отправкеprompt.total_tokens, prompt.token_budget
ai_agent.llm.requestперед LLM-вызовомgen_ai.system, gen_ai.request.model, gen_ai.operation.name
ai_agent.llm.responseпосле ответа LLMgen_ai.response.model, gen_ai.usage.input_tokens, gen_ai.usage.output_tokens, gen_ai.response.finish_reasons
ai_agent.llm.retryпопытка повтораgen_ai.request.model, retry.attempt, retry.reason, retry.backoff_ms
ai_agent.tool.calledдиспетчеризация инструментаgen_ai.tool.name, gen_ai.tool.call.id
ai_agent.tool.returnedответ инструментаgen_ai.tool.name, operation.duration_ms, operation.status
ai_agent.retrieval.requested, .completedграницы RAG-поискаrag.retrieval.top_k, rag.retrieval.min_score
ai_agent.iteration.started, .completedграницы итерации agent-loop'аagent.iteration, agent.pipeline, agent.role
ai_agent.decisionсмена состояния конвейераagent.decision, agent.decision.rationale
ai_agent.session.started, .completedжизненный цикл сессииsession.id; на completed: суммы usage и max iteration

Ключи корреляции

Через W3C Baggage идут три ключа корреляции, чтобы каждая запись в одном пользовательском взаимодействии группировалась:

  • session.id — долгоживущая пользовательская сессия.
  • conversation.id — один обмен «вопрос-ответ» в сессии.
  • agent.run.id — одно исполнение agent-loop'а внутри conversation.

Интуитивно вложенность — session.id ⊃ conversation.id ⊃ agent.run.id, но спека не предписывает иерархию — это рекомендация, плоский набор ключей тоже допустим.

Усечение body и приватность

Prompt'ы и ответы LLM легко переваливают за 100 КБ. Pack по умолчанию задаёт:

  • max_body_bytes: 4096 — поля по шаблонам *.content, *.markdown, *.code, *.table_json, *.html усекаются, при этом *_truncated: true и *_original_bytes: <N> устанавливаются.
  • capture_bodies: false — production-дефолт. Совпавшие поля заменяются на "" или null; выживают только метаданные (gen_ai.usage.*, *_original_bytes, *_hash).
  • hash_bodies: true*.content.hash = sha256(content)[:8] позволяет дедупликацию и корреляцию между запусками без хранения plaintext. Опциональная env-переменная body_hash_salt закрывает окружения, где утечка хеша сама по себе риск.

Включай capture_bodies: true только в явном debug-режиме (например, через DAGSTACK_DEBUG_AI=true или per-request через scoped-логгер — см. Локальные переопределения).

Рекомендованная структура span'ов

Pack рекомендует иерархию OTel-span'ов, естественно сочетающуюся с событиями:

span: ai_agent.session (root)
└─ span: ai_agent.run (один пользовательский запрос)
└─ span: ai_agent.iteration.1
├─ span: ai_agent.context.assembly
├─ span: gen_ai.chat (LLM-вызов — OTel GenAI)
│ events: ai_agent.llm.request → ai_agent.llm.response
├─ span: mcp.tools/call (диспетчеризация инструмента — OTel MCP)
│ events: ai_agent.tool.called → ai_agent.tool.returned
└─ span: mcp.tools/call (read_file)
└─ span: ai_agent.iteration.2 …

Корреляция трассировок и логов работает автоматически — каждая запись несёт trace_id / span_id по Пробросу контекста.

См. также