Instrumentation OpenTelemetry :
Ce produit n'est pas pris en charge par le
site Datadog que vous avez sélectionné. (
).
Aperçu
En utilisant les conventions sémantiques standardisées d’OpenTelemetry pour les opérations d’IA générative, vous pouvez instrumenter vos applications LLM avec n’importe quelle bibliothèque ou framework compatible OpenTelemetry et visualiser les traces dans LLM Observability.
LLM Observability prend en charge l’ingestion des traces OpenTelemetry qui suivent les conventions sémantiques OpenTelemetry 1.37+ pour l’IA générative. Cela vous permet d’envoyer des traces LLM directement depuis des applications instrumentées avec OpenTelemetry à Datadog sans nécessiter le SDK Datadog LLM Observability ou un Datadog Agent.
Prérequis
Pour envoyer des évaluations externes directement à l’API pour les spans OpenTelemetry, vous devez inclure le source:otel tag dans l’évaluation. Lors de la référence aux spans, fournissez span_id et trace_id sous forme de chaînes décimales. OpenTelemetry utilise des identifiants hexadécimaux nativement, donc convertissez-les en décimal avant de soumettre les évaluations. Par exemple, utilisez la fonction de Python int(hex_span_id, 16) pour convertir un identifiant de span hexadécimal en son équivalent décimal.
Pour des informations sur l’utilisation du suivi des invites avec les spans OpenTelemetry, consultez Suivi des invites - Instrumentation OpenTelemetry.
Vous pouvez également utiliser des spans OpenTelemetry à l’intérieur de Expériences LLM Observability. En définissant DD_TRACE_OTEL_ENABLED=1, les spans OTel créés à l’intérieur d’une tâche d’expérience apparaissent automatiquement comme des enfants du span d’expérience.
Configuration
Pour envoyer des traces OpenTelemetry à LLM Observability, configurez votre exportateur OpenTelemetry avec les paramètres suivants :
Configuration
Définissez les variables d’environnement suivantes dans votre application :
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
Remplacez <YOUR_API_KEY> par votre clé API Datadog.
Si votre framework prenait auparavant en charge une version de spécification OpenTelemetry antérieure à 1.37, vous devez également définir :
OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental
Cette variable d’environnement active les traces OpenTelemetry conformes à la version 1.37+ pour les frameworks qui prennent désormais en charge les conventions sémantiques de la version 1.37+, mais qui prenaient auparavant en charge des versions plus anciennes (comme strands-agents).
Remarque :
- Si vous utilisez une bibliothèque OpenTelemetry autre que le SDK OpenTelemetry par défaut, vous devrez peut-être configurer l’endpoint, le protocole et les en-têtes différemment selon l’API de la bibliothèque. Consultez la documentation de votre bibliothèque pour la méthode de configuration appropriée.
- Lors de l’utilisation de l’instrumentation OpenTelemetry, certaines données envoyées à LLM Observability peuvent également être écrites dans les traces APM correspondantes. Si vous protégez des données sensibles, envisagez également de configurer un ensemble de données restreint sur APM pour correspondre à vos contrôles d’accès LLM Observability. Consultez Contrôle d’accès aux données pour plus d’informations.
Utilisation de strands-agents
Si vous utilisez la strands-agents bibliothèque, vous devez définir une variable d’environnement supplémentaire pour activer les traces conformes à OpenTelemetry v1.37+ :
OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental
Cette variable d’environnement garantit que strands-agents émet des traces suivant les conventions sémantiques OpenTelemetry v1.37+ pour l’IA générative, qui sont requises par LLM Observability.
Instrumentation
Pour générer des traces compatibles avec LLM Observability, choisissez l’une des méthodes suivantes :
- Utilisez une bibliothèque OpenTelemetry ou un package d’instrumentation qui émet des spans suivant les conventions sémantiques OpenTelemetry 1.37+ pour l’IA générative.
- Créez une instrumentation OpenTelemetry personnalisée qui produit des spans avec les attributs
gen_ai.* requis, tels que définis dans les conventions sémantiques.
Après le démarrage de votre application, les traces apparaissent automatiquement dans la page Traces d’observabilité LLM. Pour rechercher vos traces dans l’interface utilisateur, utilisez l’attribut ml_app, qui est automatiquement défini sur la valeur de l’attribut service de votre span racine OpenTelemetry.
- OpenLLMetry version 0.47+ est pris en charge. Consultez l'exemple OpenLLMetry.
- OpenInference n'est pas pris en charge.
- Il peut y avoir un délai de 3 à 5 minutes entre l'envoi des traces et leur apparition sur la page des Traces d'observabilité LLM. Si vous avez APM activé, les traces apparaissent immédiatement sur la page des Traces APM.
Frameworks et bibliothèques testés
Ces frameworks et bibliothèques ont été testés avec Datadog LLM Observability. Tout framework qui émet des spans conformes à la convention sémantique OpenTelemetry 1.37+ GenAI est pris en charge.
Exemples
Utilisation des agents Strands
L’exemple suivant démontre une application complète utilisant Strands Agents avec l’intégration OpenTelemetry. Cette même approche fonctionne avec tout framework qui prend en charge les conventions sémantiques OpenTelemetry version 1.37+ pour l’IA générative.
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}")
Instrumentation OpenTelemetry personnalisée
L’exemple suivant démontre comment instrumenter votre application LLM en utilisant du code OpenTelemetry personnalisé. Cette approche vous donne un contrôle total sur les traces et les spans émis par votre application.
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()
Après avoir exécuté cet exemple, recherchez ml_app:simple-llm-example dans l’interface utilisateur d’observabilité LLM pour trouver la trace générée.
Utilisation d’OpenLLMetry
L’exemple suivant démontre l’utilisation de OpenLLMetry pour instrumenter automatiquement les appels OpenAI avec 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)
Après avoir exécuté cet exemple, recherchez ml_app:simple-openllmetry-test dans l’interface utilisateur d’observabilité LLM pour trouver la trace générée.
Référence de mappage d’attributs
Cette section fournit le mappage entre les conventions sémantiques OpenTelemetry GenAI (v1.37+) ainsi qu’OpenLLMetry au schéma de span d’observabilité LLM de Datadog.
Mappages d’attributs OpenTelemetry 1.37+
Attributs de base des spans
| Champ OTLP | Champ d’observabilité LLM | Remarques |
|---|
resource.attributes.service.name | ml_app, tags.service | |
name | name | Remplacé par gen_ai.tool.name si présent |
parent_span_id | parent_id | |
start_time_unix_nano | start_ns | |
end_time_unix_nano | duration | Calculé : fin - début |
status.code | status | error si > 0, sinon ok |
status.message | meta.error.message | |
attributes.error.type | meta.error.type | |
Résolution du type de span
gen_ai.operation.name | Observabilité LLM span.kind |
|---|
generate_content, chat, text_completion, completion | llm |
embeddings, embedding | embedding |
execute_tool | tool |
invoke_agent, create_agent | agent |
rerank, unknown, (par défaut) | workflow |
| Attribut OTel | Champ d’observabilité LLM | Notes |
|---|
gen_ai.operation.name | meta.span.kind | Voir le tableau de résolution ci-dessus |
gen_ai.provider.name | meta.model_provider | Revient à gen_ai.system, puis custom |
gen_ai.response.model | meta.model_name | |
gen_ai.request.model | meta.model_name | Solution de repli lorsque response.model est absent |
Métriques d’utilisation des jetons
| Attribut OTel | Champ d’observabilité 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 |
Paramètres de requête
Tous gen_ai.request.* les paramètres correspondent à meta.metadata.* avec le préfixe supprimé.
| Attribut OTel | Champ d’observabilité 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 |
| Attribut OTel | Champ d’observabilité LLM | Notes |
|---|
gen_ai.tool.name | name | Nom du span remplacé |
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 | Tableau JSON analysé |
gen_ai.tool.call.arguments | input.value | |
gen_ai.tool.call.result | output.value | |
Session et conversation
| Attribut OTel | Champ d’observabilité LLM | Notes |
|---|
gen_ai.conversation.id | session_id | Également ajouté à metadata.conversation_id et étiquettes |
Attributs de réponse
| Attribut OTel | Champ d’observabilité LLM |
|---|
gen_ai.response.model | meta.model_name |
gen_ai.response.finish_reasons | metadata.finish_reasons |
Les messages d’entrée et de sortie sont extraits des sources suivantes, par ordre de priorité :
- Attributs directs :
gen_ai.input.messages, gen_ai.output.messages, gen_ai.system_instructions - Span events (
meta["events"]) with name gen_ai.client.inference.operation.details
| OTel Source | Champ d’observabilité LLM | Remarques |
|---|
gen_ai.input.messages | meta.input.messages (llm) / meta.input.value (autres) | |
gen_ai.output.messages | meta.output.messages (llm) / meta.output.value (autres) | |
gen_ai.system_instructions | Préfixé à l’entrée | Ajouté en tant que messages de rôle système |
Embedding spans
| OTel Source | Champ d’observabilité LLM |
|---|
gen_ai.input.messages | meta.input.documents |
| N/A | meta.output.value = [N embedding(s) returned] |
Les étiquettes sont placées directement sur le span :
- Les attributs non-
gen_ai.* sont convertis en balises key:value - Les clés inconnues
gen_ai.* sont ajoutées avec le préfixe supprimé - Filtrés :
_dd.*, llm.*, ddtags, events, et les clés gen_ai.* déjà spécifiquement mappées
Tout gen_ai.* Les attributs qui ne sont pas explicitement mappés aux champs de portée d'observabilité LLM sont placés dans les étiquettes de la portée LLM, avec une limite de 256 caractères par valeur. Les valeurs dépassant cette limite sont tronquées. Tous les autres non-gen_ai attributs sont supprimés.
Mappages d’attributs OpenLLMetry
Cette section documente les mappages d’attributs spécifiques à OpenLLMetry qui diffèrent ou étendent les conventions sémantiques standard d’OpenTelemetry GenAI.
Résolution du type de portée
llm.request.type est utilisé comme solution de repli lorsque gen_ai.operation.name est absent.
llm.request.type | Observabilité LLM span.kind |
|---|
chat | llm |
completion | llm |
embedding | embedding |
rerank | workflow |
unknown, (par défaut) | workflow |
| OpenLLMetry Attribute | LLM Observability Field | Notes |
|---|
gen_ai.system | meta.model_provider | Solution de repli lorsque gen_ai.provider.name est absent |
Métriques d’utilisation des jetons
| OpenLLMetry Attribute | LLM Observability Field | Notes |
|---|
llm.usage.total_tokens | metrics.total_tokens | Solution de repli lorsque gen_ai.usage.total_tokens est absent |
OpenLLMetry utilise des attributs indexés au lieu de tableaux JSON. Ce sont les sources de la plus basse priorité et ne sont utilisées que lorsque aucune source standard OTel n’existe.
| Attribut OpenLLMetry | Description |
|---|
gen_ai.prompt.<index>.role | Rôle du message (utilisateur, système, assistant, outil) |
gen_ai.prompt.<index>.content | Contenu du message |
gen_ai.prompt.<index>.tool_call_id | ID d’appel d’outil pour les messages de réponse d’outil |
Attributs de complétion (sortie)
| Attribut OpenLLMetry | Description |
|---|
gen_ai.completion.<index>.role | Rôle du message |
gen_ai.completion.<index>.content | Contenu du message |
gen_ai.completion.<index>.finish_reason | Raison de la fin de la complétion |
Mapping
Les messages sont convertis au format compatible OTel et traités normalement :
| OpenLLMetry Source | LLMObs Field |
|---|
gen_ai.prompt.* | meta.input.messages (llm) / meta.input.value (autres) |
gen_ai.completion.* | meta.output.messages (llm) / meta.output.value (autres) |
Les appels d’outils sont imbriqués dans les attributs de complétion.
| Attribut OpenLLMetry | Correspond à |
|---|
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 |
Lorsque role = "tool" et tool_call_id sont présents, le message est converti en un résultat d’outil :
| Attribut OpenLLMetry | Correspond à |
|---|
gen_ai.prompt.<index>.tool_call_id | tool_results[].tool_id |
gen_ai.prompt.<index>.content | tool_results[].result |
Embedding spans
Pour les embedding spans, les documents sont extraits des attributs de contenu de l’invite.
| OpenLLMetry Source | LLM Observability Field |
|---|
gen_ai.prompt.<index>.content | meta.input.documents[].text |
Les attributs spécifiques à OpenLLMetry suivants sont filtrés des étiquettes :
gen_ai.prompt.*gen_ai.completion.*llm.*
Conventions sémantiques prises en charge
LLM Observability prend en charge les spans qui suivent les conventions sémantiques OpenTelemetry 1.37+ pour l’IA générative, y compris :
- Opérations LLM avec
gen_ai.provider.name, "gen_ai.operation.name", gen_ai.request.model et d’autres attributs gen_ai - Entrées/sorties d’opération sur les attributs de span directs ou via des événements de span
- Métriques d’utilisation des jetons (
gen_ai.usage.input_tokens, gen_ai.usage.output_tokens) - Paramètres et métadonnées du modèle
Pour la liste complète des attributs pris en charge et de leurs spécifications, voir la documentation des conventions sémantiques OpenTelemetry pour l’IA générative.
Disabling LLM Observability conversion
Si vous souhaitez que vos spans d’IA générative restent uniquement dans APM et n’apparaissent pas dans LLM Observability, vous pouvez désactiver la conversion automatique en définissant l’attribut dd_llmobs_enabled sur false. Définir cet attribut sur n’importe quel span dans une trace empêche la conversion de l’ensemble de la trace en LLM Observability.
Utilisation des variables d’environnement
Ajoutez l’attribut dd_llmobs_enabled=false à votre variable d’environnement OTEL_RESOURCE_ATTRIBUTES :
OTEL_RESOURCE_ATTRIBUTES=dd_llmobs_enabled=false
Utilisation du code
Vous pouvez également définir l’attribut de manière programmatique sur n’importe quel span dans votre trace :
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)