Instrumentación de OpenTelemetry
Este producto no es compatible con el
sitio Datadog seleccionado. (
).
Descripción general
Al utilizar las convenciones semánticas estandarizadas de OpenTelemetry para operaciones de inteligencia artificial generativa, puedes instrumentar tus aplicaciones LLM con cualquier biblioteca o marco compatible con OpenTelemetry y visualizar las trazas en LLM Observability.
LLM Observability admite la ingestión de trazas de OpenTelemetry que siguen las convenciones semánticas de OpenTelemetry 1.37+ para inteligencia artificial generativa. Esto te permite enviar trazas LLM directamente desde aplicaciones instrumentadas con OpenTelemetry a Datadog sin requerir el SDK de Datadog LLM Observability o un Agente de Datadog.
Requisitos previos
Para enviar evaluaciones externas directamente a la API para spans de OpenTelemetry, debes incluir el source:otel etiqueta en la evaluación. Al referenciar spans, proporciona span_id y trace_id como cadenas decimales. OpenTelemetry utiliza IDs hexadecimales de forma nativa, así que conviértelos a decimal antes de enviar evaluaciones. Por ejemplo, utiliza el int(hex_span_id, 16) de Python para convertir un ID de span hexadecimal a su equivalente decimal.
Para información sobre el uso de Seguimiento de Prompts con spans de OpenTelemetry, consulta Seguimiento de Prompts - Instrumentación de OpenTelemetry.
También puedes usar spans de OpenTelemetry dentro de Experimentos de LLM Observability. Al establecer DD_TRACE_OTEL_ENABLED=1, OTel spans creados dentro de una tarea de experimento aparecen automáticamente como hijos del span del experimento.
Configuración
Para enviar trazas de OpenTelemetry a LLM Observability, configura tu exportador de OpenTelemetry con los siguientes ajustes:
Configuración
Establece las siguientes variables de entorno en tu aplicación:
OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=
OTEL_EXPORTER_OTLP_TRACES_HEADERS=dd-api-key=<YOUR_API_KEY>,dd-otlp-source=llmobs
Reemplaza <YOUR_API_KEY> con tu clave API de Datadog.
Si tu marco de trabajo anteriormente soportaba una versión de especificación de OpenTelemetry anterior a la 1.37, también necesitas establecer:
OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental
Esta variable de entorno habilita trazas de OpenTelemetry compatibles con la versión 1.37+ para marcos que ahora soportan las convenciones semánticas de la versión 1.37+, pero que anteriormente soportaban versiones más antiguas (como strands-agents).
Nota:
- Si estás utilizando una biblioteca de OpenTelemetry diferente al SDK de OpenTelemetry por defecto, es posible que necesites configurar el endpoint, el protocolo y los encabezados de manera diferente dependiendo de la API de la biblioteca. Consulta la documentación de tu biblioteca para el método de configuración apropiado.
- Al utilizar instrumentación de OpenTelemetry, algunos datos enviados a LLM Observability también pueden ser escritos en las trazas APM correspondientes. Si estás protegiendo datos sensibles, considera también configurar un Conjunto de Datos Restringido en APM para que coincida con tus controles de acceso de LLM Observability. Consulta Control de Acceso a Datos para más información.
Usando strands-agents
Si estás utilizando la strands-agents biblioteca, necesitas establecer una variable de entorno adicional para habilitar trazas que sean compatibles con OpenTelemetry v1.37+:
OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental
Esta variable de entorno asegura que strands-agents emita trazas siguiendo las convenciones semánticas de OpenTelemetry v1.37+ para IA generativa, que son requeridas por LLM Observability.
Instrumentación
Para generar trazas compatibles con LLM Observability, haz una de las siguientes opciones:
Después de que tu aplicación comience a enviar datos, las trazas aparecerán automáticamente en la página Trazas de Observabilidad LLM. Para buscar tus trazas en la interfaz de usuario, utiliza el atributo ml_app, que se establece automáticamente en el valor del atributo service de tu span raíz de OpenTelemetry.
- OpenLLMetry versión 0.47+ es compatible. Vea el ejemplo de OpenLLMetry.
- OpenInference no es compatible.
- Puede haber un retraso de 3 a 5 minutos entre el envío de trazas y su aparición en la página de Trazas de Observabilidad LLM. Si tienes APM habilitado, las trazas aparecen inmediatamente en la página de Trazas APM.
Frameworks y bibliotecas probados
Estos frameworks y bibliotecas han sido probados con la Observabilidad LLM de Datadog. Cualquier framework que emita spans compatibles con la convención semántica GenAI de OpenTelemetry 1.37+ es compatible.
Ejemplos
Usando Agentes Strands
El siguiente ejemplo demuestra una aplicación completa utilizando Strands Agents con la integración de OpenTelemetry. Este mismo enfoque funciona con cualquier marco que soporte las convenciones semánticas de OpenTelemetry versión 1.37+ para inteligencia artificial generativa.
from strands import Agent
from strands_tools import calculator, current_time
from strands.telemetry.config import StrandsTelemetry
import os
# Configure AWS credentials for Bedrock access
os.environ["AWS_PROFILE"] = "<YOUR_AWS_PROFILE>"
os.environ["AWS_DEFAULT_REGION"] = "<YOUR_AWS_REGION>"
# Enable latest GenAI semantic conventions (1.37)
os.environ["OTEL_SEMCONV_STABILITY_OPT_IN"] = "gen_ai_latest_experimental"
# Configure OTLP endpoint to send traces to Datadog LLM Observability
os.environ["OTEL_EXPORTER_OTLP_TRACES_PROTOCOL"] = "http/protobuf"
os.environ["OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"] = ""
os.environ["OTEL_EXPORTER_OTLP_TRACES_HEADERS"] = f"dd-api-key={os.getenv('DD_API_KEY')},dd-otlp-source=llmobs"
# Initialize telemetry with OTLP exporter
telemetry = StrandsTelemetry()
telemetry.setup_otlp_exporter()
# Create agent with tools
agent = Agent(tools=[calculator, current_time])
# Run the agent
if __name__ == "__main__":
result = agent("I was born in 1993, what is my age?")
print(f"Agent: {result}")
Instrumentación personalizada de OpenTelemetry
El siguiente ejemplo demuestra cómo instrumentar tu aplicación LLM utilizando código personalizado de OpenTelemetry. Este enfoque te da control completo sobre las trazas y tramos emitidos por tu aplicación.
import os
import json
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
from openai import OpenAI
# Configure OpenTelemetry to send traces to Datadog
os.environ["OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"] = ""
os.environ["OTEL_EXPORTER_OTLP_TRACES_HEADERS"] = "dd-api-key=<YOUR_DATADOG_API_KEY>,dd-otlp-source=llmobs"
os.environ["OTEL_SEMCONV_STABILITY_OPT_IN"] = "gen_ai_latest_experimental"
# Initialize OpenTelemetry SDK
resource = Resource(attributes={SERVICE_NAME: "simple-llm-example"})
provider = TracerProvider(resource=resource)
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
trace.set_tracer_provider(provider)
tracer = trace.get_tracer(__name__)
# Make LLM call with OpenTelemetry tracing
with tracer.start_as_current_span(
"chat gpt-4o",
kind=trace.SpanKind.CLIENT,
) as span:
model = "gpt-4o"
max_tokens = 1024
temperature = 0.7
messages = [{"role": "user", "content": "Explain OpenTelemetry in one sentence."}]
# Set request attributes
span.set_attribute("gen_ai.provider.name", "openai")
span.set_attribute("gen_ai.request.model", model)
span.set_attribute("gen_ai.operation.name", "chat")
span.set_attribute("gen_ai.request.max_tokens", max_tokens)
span.set_attribute("gen_ai.request.temperature", temperature)
# Add input messages as event
input_messages_parts = []
for msg in messages:
input_messages_parts.append({
"role": msg["role"],
"parts": [{"type": "text", "content": msg["content"]}]
})
span.add_event(
"gen_ai.client.inference.operation.details",
{
"gen_ai.input.messages": json.dumps(input_messages_parts)
}
)
# Make actual LLM call
client = OpenAI(api_key="<YOUR_OPENAI_API_KEY>")
response = client.chat.completions.create(
model=model,
max_tokens=max_tokens,
temperature=temperature,
messages=messages
)
# Set response attributes from actual data
span.set_attribute("gen_ai.response.id", response.id)
span.set_attribute("gen_ai.response.model", response.model)
span.set_attribute("gen_ai.response.finish_reasons", [response.choices[0].finish_reason])
span.set_attribute("gen_ai.usage.input_tokens", response.usage.prompt_tokens)
span.set_attribute("gen_ai.usage.output_tokens", response.usage.completion_tokens)
# Add output messages as event
output_text = response.choices[0].message.content
span.add_event(
"gen_ai.client.inference.operation.details",
{
"gen_ai.output.messages": json.dumps([{
"role": "assistant",
"parts": [{"type": "text", "content": output_text}],
"finish_reason": response.choices[0].finish_reason
}])
}
)
print(f"Response: {output_text}")
# Flush spans before exit
provider.force_flush()
Después de ejecutar este ejemplo, busca ml_app:simple-llm-example en la interfaz de usuario de Observabilidad LLM para encontrar la traza generada.
Usando OpenLLMetry
El siguiente ejemplo demuestra el uso de OpenLLMetry para instrumentar automáticamente las llamadas a OpenAI con OpenTelemetry.
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.openai import OpenAIInstrumentor
import openai
from opentelemetry.sdk.resources import Resource
resource = Resource.create({
"service.name": "simple-openllmetry-test",
})
provider = TracerProvider(resource=resource)
trace.set_tracer_provider(provider)
exporter = OTLPSpanExporter(
endpoint="",
headers={
"dd-api-key": "<YOUR_DATADOG_API_KEY>",
"dd-ml-app": "simple-openllmetry-test",
"dd-otlp-source": "llmobs",
},
)
provider.add_span_processor(BatchSpanProcessor(exporter))
OpenAIInstrumentor().instrument()
# Make OpenAI call (automatically traced)
client = openai.OpenAI(api_key="<YOUR_OPENAI_API_KEY>")
client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "What is 15 multiplied by 7?"}]
)
provider.force_flush(timeout_millis=5000)
Después de ejecutar este ejemplo, busca ml_app:simple-openllmetry-test en la interfaz de usuario de Observabilidad LLM para encontrar la traza generada.
Referencia de mapeo de atributos
Esta sección proporciona el mapeo entre las convenciones semánticas de OpenTelemetry GenAI (v1.37+) así como OpenLLMetry al esquema de span de Observabilidad LLM de Datadog.
Mapeos de atributos de OpenTelemetry 1.37+
Atributos base de span
| Campo OTLP | Campo de Observabilidad LLM | Notas |
|---|
resource.attributes.service.name | ml_app, tags.service | |
name | name | Anulado por gen_ai.tool.name si está presente |
parent_span_id | parent_id | |
start_time_unix_nano | start_ns | |
end_time_unix_nano | duration | Calculado: fin - inicio |
status.code | status | error si > 0, de lo contrario ok |
status.message | meta.error.message | |
attributes.error.type | meta.error.type | |
Resolución del tipo de Span
gen_ai.operation.name | Observabilidad de LLM span.kind |
|---|
generate_content, chat, text_completion, completion | llm |
embeddings, embedding | embedding |
execute_tool | tool |
invoke_agent, create_agent | agent |
rerank, unknown, (predeterminado) | workflow |
| Atributo de OTEL | Campo de observabilidad de LLM | Notas |
|---|
gen_ai.operation.name | meta.span.kind | Ver tabla de resolución arriba |
gen_ai.provider.name | meta.model_provider | Recurre a gen_ai.system, luego a custom |
gen_ai.response.model | meta.model_name | |
gen_ai.request.model | meta.model_name | Recurso si response.model está ausente |
Métricas de uso de tokens
| Atributo de OTEL | Campo de observabilidad de LLM |
|---|
gen_ai.usage.input_tokens | metrics.input_tokens |
gen_ai.usage.output_tokens | metrics.output_tokens |
gen_ai.usage.prompt_tokens | metrics.prompt_tokens |
gen_ai.usage.completion_tokens | metrics.completion_tokens |
gen_ai.usage.total_tokens | metrics.total_tokens |
Parámetros de solicitud
Todos los gen_ai.request.* parámetros se mapean a meta.metadata.* con el prefijo eliminado.
| Atributo de OTEL | Campo de observabilidad de LLM |
|---|
gen_ai.request.seed | metadata.seed |
gen_ai.request.frequency_penalty | metadata.frequency_penalty |
gen_ai.request.max_tokens | metadata.max_tokens |
gen_ai.request.stop_sequences | metadata.stop_sequences |
gen_ai.request.temperature | metadata.temperature |
gen_ai.request.top_k | metadata.top_k |
gen_ai.request.top_p | metadata.top_p |
gen_ai.request.choice.count | metadata.choice.count |
| Atributo de OTel | Campo de LLM Observability | Notas |
|---|
gen_ai.tool.name | name | Sobrescribe el nombre del span |
gen_ai.tool.call.id | metadata.tool_id | |
gen_ai.tool.description | metadata.tool_description | |
gen_ai.tool.type | metadata.tool_type | |
gen_ai.tool.definitions | meta.tool_definitions | Array JSON analizado |
gen_ai.tool.call.arguments | input.value | |
gen_ai.tool.call.result | output.value | |
Sesión y conversación
| Atributo de OTel | Campo de LLM Observability | Notas |
|---|
gen_ai.conversation.id | session_id | También agregado a metadata.conversation_id y etiquetas |
Atributos de respuesta
| Atributo de OTel | Campo de LLM Observability |
|---|
gen_ai.response.model | meta.model_name |
gen_ai.response.finish_reasons | metadata.finish_reasons |
Los mensajes de entrada y salida se extraen de las siguientes fuentes, en orden de prioridad:
- Atributos directos:
gen_ai.input.messages, gen_ai.output.messages, gen_ai.system_instructions - Eventos de tramo (
meta["events"]) con nombre gen_ai.client.inference.operation.details
| Fuente de OTel | Campo de LLM Observability | Notas |
|---|
gen_ai.input.messages | meta.input.messages (llm) / meta.input.value (otros) | |
gen_ai.output.messages | meta.output.messages (llm) / meta.output.value (otros) | |
gen_ai.system_instructions | Precedido por entrada | Agregado como mensajes de rol del sistema |
Tramos de incrustación
| Fuente OTEL | Campo de Observabilidad LLM |
|---|
gen_ai.input.messages | meta.input.documents |
| N/A | meta.output.value = [N embedding(s) returned] |
Las etiquetas se colocan directamente en el span:
- Atributos no-
gen_ai.* se convierten en etiquetas key:value - Claves desconocidas
gen_ai.* se añaden con el prefijo eliminado
Filtrado: - , _dd.*, llm.*, ddtags, events, y claves gen_ai.* ya mapeadas específicamente
Cualquiera gen_ai.* los atributos que no están explícitamente mapeados a los campos de span de LLM Observability se colocan en las etiquetas del span de LLM, con un límite de 256 caracteres por valor. Los valores que exceden este límite son truncados. Todos los demás no-gen_ai atributos son eliminados.
Mapeo de atributos de OpenLLMetry
Esta sección documenta los mapeos de atributos específicos de OpenLLMetry que difieren o extienden las convenciones semánticas estándar de OpenTelemetry GenAI.
Resolución del tipo de span
llm.request.type se utiliza como respaldo cuando gen_ai.operation.name está ausente.
llm.request.type | LLM Observability span.kind |
|---|
chat | llm |
completion | llm |
embedding | embedding |
rerank | workflow |
unknown, (predeterminado) | workflow |
| Atributo de OpenLLMetry | Campo de Observabilidad de LLM | Notas |
|---|
gen_ai.system | meta.model_provider | Reemplazo cuando gen_ai.provider.name está ausente |
Métricas de uso de tokens
| Atributo de OpenLLMetry | Campo de Observabilidad de LLM | Notas |
|---|
llm.usage.total_tokens | metrics.total_tokens | Reemplazo cuando gen_ai.usage.total_tokens está ausente |
OpenLLMetry utiliza atributos indexados en lugar de arreglos JSON. Estas son las fuentes de menor prioridad y solo se utilizan cuando no existen fuentes estándar de OTel.
| Atributo de OpenLLMetry | Descripción |
|---|
gen_ai.prompt.<index>.role | Rol del mensaje (usuario, sistema, asistente, herramienta) |
gen_ai.prompt.<index>.content | Contenido del mensaje |
gen_ai.prompt.<index>.tool_call_id | ID de llamada de herramienta para mensajes de respuesta de herramienta |
Atributos de finalización (salida)
| Atributo de OpenLLMetry | Descripción |
|---|
gen_ai.completion.<index>.role | Rol del mensaje |
gen_ai.completion.<index>.content | Contenido del mensaje |
gen_ai.completion.<index>.finish_reason | Razón de finalización de la tarea |
Mapeo
Los mensajes se convierten al formato compatible con OTel y se procesan normalmente:
| Fuente de OpenLLMetry | Campo de LLMObs |
|---|
gen_ai.prompt.* | meta.input.messages (llm) / meta.input.value (otros) |
gen_ai.completion.* | meta.output.messages (llm) / meta.output.value (otros) |
Las llamadas a herramientas están anidadas dentro de los atributos de finalización.
| Atributo de OpenLLMetry | Se mapea a |
|---|
gen_ai.completion.<index>.tool_calls.<idx>.name | tool_calls[].name |
gen_ai.completion.<index>.tool_calls.<idx>.id | tool_calls[].tool_id |
gen_ai.completion.<index>.tool_calls.<idx>.arguments | tool_calls[].arguments |
Cuando role = "tool" y tool_call_id están presentes, el mensaje se convierte en un resultado de herramienta:
| Atributo de OpenLLMetry | Se mapea a |
|---|
gen_ai.prompt.<index>.tool_call_id | tool_results[].tool_id |
gen_ai.prompt.<index>.content | tool_results[].result |
Tramos de embedding
Para los tramos de embedding, los documentos se extraen de los atributos de contenido del prompt.
| Fuente de OpenLLMetry | Campo de Observabilidad de LLM |
|---|
gen_ai.prompt.<index>.content | meta.input.documents[].text |
Los siguientes atributos específicos de OpenLLMetry se filtran de las etiquetas:
gen_ai.prompt.*gen_ai.completion.*llm.*
Convenciones semánticas soportadas
LLM Observability soporta tramos que siguen las convenciones semánticas de OpenTelemetry 1.37+ para IA generativa, incluyendo:
- Operaciones de LLM con
gen_ai.provider.name, "gen_ai.operation.name", gen_ai.request.model y otros atributos de gen_ai - Entradas/salidas de operación en atributos directos de tramo o mediante eventos de tramo
- Métricas de uso de tokens (
gen_ai.usage.input_tokens, gen_ai.usage.output_tokens) - Parámetros y metadatos del modelo
Para la lista completa de atributos soportados y sus especificaciones, consulte la documentación de convenciones semánticas de OpenTelemetry para IA generativa.
Deshabilitando la conversión de LLM Observability
Si solo desea que sus tramos de IA generativa permanezcan en APM y no aparezcan en LLM Observability, puede deshabilitar la conversión automática configurando el atributo dd_llmobs_enabled a false. Configurar este atributo en cualquier tramo de una traza impide que toda la traza se convierta en LLM Observability.
Usando variables de entorno
Agregue el atributo dd_llmobs_enabled=false a su variable de entorno OTEL_RESOURCE_ATTRIBUTES:
OTEL_RESOURCE_ATTRIBUTES=dd_llmobs_enabled=false
Usando código
También puede establecer el atributo programáticamente en cualquier tramo de su traza:
from opentelemetry import trace
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("my-span") as span:
# Disable LLM Observability conversion for this entire trace
span.set_attribute("dd_llmobs_enabled", False)