Aller au contenu principal

Traçage manuel et personnalisé

Le traçage automatique instrumente plus de 30 frameworks en un seul appel. Utilisez le traçage manuel lorsque vous devez instrumenter du code que la journalisation automatique ne couvre pas — logique d'agent personnalisée, frameworks propriétaires ou tout chemin d'exécution que vous souhaitez observer précisément. Les mêmes APIs fonctionnent que votre agent s'exécute sur Databricks ou sur une infrastructure externe.

Quelle méthode ?

Méthode

Cas d'utilisation

Parent-enfant automatique

Gestion des exceptions

@mlflow.trace décorateur

Traçage d’une fonction Python entière

Oui

Automatic

mlflow.start_span() gestionnaire de contexte

Traçage d'un bloc de code dans une fonction

Oui

Automatic

Wrapper Node.js mlflow.trace()

Suivi des fonctions TypeScript ou JavaScript

Oui

Automatic

API MlflowClient de bas niveau

ID de trace personnalisés, intégration avec un système d’observabilité externe

Non — manuel

Manuel

Méthode

Cas d'utilisation

Parent-enfant automatique

Gestion des exceptions

@mlflow.trace décorateur

Traçage d’une fonction Python entière

Oui

Automatic

mlflow.start_span() gestionnaire de contexte

Traçage d'un bloc de code dans une fonction

Oui

Automatic

Wrapper Node.js mlflow.trace()

Suivi des fonctions TypeScript ou JavaScript

Oui

Automatic

API MlflowClient de bas niveau

ID de trace personnalisés, intégration avec un système d’observabilité externe

Non — manuel

Manuel

Prérequis

Python
%pip install --upgrade "mlflow[databricks]>=3.1.0"
dbutils.library.restartPython()

Le décorateur @mlflow.trace

Le décorateur @mlflow.trace crée une étendue pour toute fonction Python. Il capture automatiquement le nom de la fonction, les entrées, les sorties et le temps d’exécution, et gère les relations parent-enfant ainsi que l’enregistrement des exceptions sans code supplémentaire.

Python
import mlflow


@mlflow.trace(span_type="func", attributes={"key": "value"})
def add_1(x):
return x + 1


@mlflow.trace(span_type="func", attributes={"key1": "value1"})
def minus_1(x):
return x - 1


@mlflow.trace(name="Trace Test")
def trace_test(x):
step1 = add_1(x)
return minus_1(step1)


trace_test(4)

Décorateur de traçage

remarque

Lorsqu'une trace contient plusieurs portées portant le même nom, MLflow ajoute un suffixe à incrémentation automatique — _1, _2, etc.

Personnaliser les étendues

Le décorateur accepte trois arguments optionnels :

  • name — remplace le nom de portée default (le nom de la fonction)
  • span_type — définit le type d'étendue ; utilisez un type d'étendue intégré ou une chaîne personnalisée
  • attributes — ajoute des métadonnées de type clé-valeur à l'étendue

Pour mettre à jour les attributs de manière dynamique depuis l’intérieur de la fonction, appelez mlflow.get_current_active_span():

Python
from mlflow.entities import SpanType

@mlflow.trace(span_type=SpanType.LLM)
def invoke(prompt: str):
model_id = "gpt-4o-mini"
span = mlflow.get_current_active_span()
span.set_attributes({"model": model_id})
return client.invoke(messages=[{"role": "user", "content": prompt}], model=model_id)

Utiliser avec d’autres décorateurs

Placez @mlflow.trace comme décorateur le plus externe . S’il n’est pas le premier, il risque de manquer les modifications apportées par les décorateurs internes et de produire des traces incomplètes.

Python
# Correct: @mlflow.trace is outermost
@mlflow.trace(name="my_function")
@other_decorator
def my_function(x, y):
return x + y

Ajouter des tags de trace et des aperçus d'interface utilisateur

Utilisez mlflow.update_current_trace() dans une fonction tracée pour personnaliser les colonnes d’aperçu Request / Response dans l’interface utilisateur des traces. Le même appel peut joindre des tags ; pour le flux de travail complet sur les tags et les métadonnées, consultez Enrichissement des traces : tags, contexte et retours d’expérience.

Python
@mlflow.trace(name="Summarization Pipeline")
def summarize_document(document_content: str, user_instructions: str):
mlflow.update_current_trace(tags={"environment": "production"})

request_p = f"Doc: {document_content[:30]}... Instr: {user_instructions[:30]}..."
mlflow.update_current_trace(request_preview=request_p)

summary = generate_summary(document_content, user_instructions)

mlflow.update_current_trace(response_preview=f"Summary: {summary[:50]}...")
return summary

Gestion des exceptions

Lorsqu’une exception est levée à l’intérieur d’une fonction tracée, la portée est automatiquement marquée comme ayant échoué et les détails de l’exception sont enregistrés dans l’onglet Events de la tab.

Multi-threading

MLflow tracing is thread-safe and isolates traces per thread by default. To create one trace that spans multiple threads, copy the execution context from the main thread into each worker:

Python
import contextvars
from concurrent.futures import ThreadPoolExecutor, as_completed
import mlflow
import openai

client = openai.OpenAI()
mlflow.openai.autolog()


@mlflow.trace
def worker(question: str) -> str:
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": question},
]
response = client.chat.completions.create(
model="gpt-4o-mini", messages=messages, temperature=0.1, max_tokens=100
)
return response.choices[0].message.content


@mlflow.trace
def main(questions: list[str]) -> list[str]:
results = []
with ThreadPoolExecutor(max_workers=2) as executor:
futures = []
for question in questions:
ctx = contextvars.copy_context() # copy context in main thread
futures.append(executor.submit(ctx.run, worker, question)) # run in copy
for future in as_completed(futures):
results.append(future.result())
return results


main(["What is the capital of France?", "What is the capital of Germany?"])

Traçage multithread

astuce

asyncio les tâches héritent automatiquement du contexte — aucune copie manuelle n’est requise pour le code async/await.

Sorties de streaming

Le décorateur prend en charge les fonctions de génération et de génération asynchrone (MLflow 2.20.2+). Par default, MLflow collecte toutes les valeurs produites sous forme de liste dans la sortie de la portée. Passez un output_reducer pour agréger les blocs de Stream en une seule valeur ; le réducteur reçoit la liste complète une fois l'itération terminée :

Python
@mlflow.trace(output_reducer=lambda chunks: "".join(chunks))
def stream_text():
for word in ["Hello", " ", "World", "!"]:
yield word
# Span output: "Hello World!"

Les segments bruts restent visibles dans l’onglet Events de l’étendue à des fins de debugging, que vous utilisiez ou non un réducteur. Pour les Stream de SDK de fournisseur dont les segments ne sont pas des chaînes simples, tels que les objets ChatCompletionChunk d’OpenAI, écrivez un réducteur qui cumule les deltas en un seul objet de réponse.

astuce

Pour OpenAI en production, privilégiez le traçage automatique pour OpenAI, qui gère le streaming automatiquement.

Types de fonctions pris en charge :

Type de fonction

Pris en charge

Synchroniser

Toutes les versions

Asynchrone

MLflow 2.16.0+

Générateur (synchrone ou asynchrone)

MLflow 2.20.2+

Type de fonction

Pris en charge

Synchroniser

Toutes les versions

Asynchrone

MLflow 2.16.0+

Générateur (synchrone ou asynchrone)

MLflow 2.20.2+

Le gestionnaire de contexte mlflow.start_span()

Utilisez mlflow.start_span() pour tracer n’importe quel bloc de code au sein d’une fonction. À l'instar du décorateur, il gère automatiquement les relations parents-enfants et l'enregistrement des exceptions. Contrairement au décorateur, vous définissez le nom, les entrées et les sorties de la portée par le biais de l'objet LiveSpan qu'elle renvoie.

Python
import mlflow

with mlflow.start_span(name="my_span") as span:
x, y = 1, 2
span.set_inputs({"x": x, "y": y})
z = x + y
span.set_outputs(z)

Événements de portée

Les objets SpanEvent enregistrent des occurrences spécifiques au cours de la durée de vie d’un intervalle (avec le timestamp actuel, un timestamp spécifique en nanosecondes ou à partir d’une exception) :

Python
from mlflow.entities import SpanEvent, SpanType
import time

with mlflow.start_span(name="pipeline_step", span_type=SpanType.CHAIN) as span:
span.add_event(SpanEvent(
name="validation_completed",
attributes={"records_validated": 1000, "errors_found": 3},
))
span.add_event(SpanEvent(
name="data_checkpoint",
timestamp=int(time.time() * 1e9),
attributes={"checkpoint_id": "ckpt_123"},
))
try:
raise ValueError("Invalid input format")
except Exception as e:
# SpanEvent.from_exception captures exception.message, exception.type, exception.stacktrace
mlflow.get_current_active_span().add_event(SpanEvent.from_exception(e))

Statut de l'étendue

SpanStatus indique si un intervalle a réussi ou échoué. Le gestionnaire de contexte remplace le statut à la sortie (OK en cas de sortie propre, ERROR en cas d’exception). Veillez donc à le définir avant la fermeture du bloc with si vous avez besoin d’un statut personnalisé :

Python
from mlflow.entities import SpanStatus, SpanStatusCode, SpanType

with mlflow.start_span(name="my_span", span_type=SpanType.CHAIN) as span:
span.set_status(SpanStatus(SpanStatusCode.OK))
# String shortcuts also work: span.set_status("OK") or span.set_status("ERROR")

État d’une query à partir d’une étendue terminée :

Python
trace = mlflow.get_trace(mlflow.get_last_active_trace_id())
for span in trace.data.spans:
print(span.status.status_code)

Portées RETRIEVER

Utilisez SpanType.RETRIEVER lorsque votre étendue récupère des documents à partir d'un magasin de données. Les étendues RETRIEVER doivent générer une liste de Document objets afin que l'interface utilisateur les affiche correctement :

Python
from mlflow.entities import Document, SpanType


@mlflow.trace(span_type=SpanType.RETRIEVER)
def retrieve_documents(query: str):
span = mlflow.get_current_active_span()
documents = [
Document(
page_content="The content of the document...",
metadata={"doc_uri": "path/to/document.md", "relevance_score": 0.95},
id="doc_123",
),
Document(
page_content="Another relevant section...",
metadata={"doc_uri": "path/to/other.md", "relevance_score": 0.87},
),
]
span.set_outputs(documents)
return [doc.to_dict() for doc in documents]


retrieve_documents(query="What is ML?")

Node.js / TypeScript

Le package npm mlflow-tracing apporte le traçage MLflow aux agents TypeScript et JavaScript. L'API reflète l'approche de Python : une API de wrapping de fonction (équivalente au décorateur), une API de traçage de bloc (équivalente à start_span) et un décorateur de méthode de classe pour TypeScript 5.0+.

Configurer

TypeScript
import * as mlflow from 'mlflow-tracing';

mlflow.init({
trackingUri: 'databricks',
experimentId: '<your-experiment-id>',
});

Recherchez l’identifiant d’expérimentation dans votre Workspace Databricks sous AI/ML > Experimentation > GenAI apps & agents en cliquant sur l’icône Icône Info.. Configurez les informations d’identification avec des variables d’environnement :

Bash
export DATABRICKS_TOKEN=<personal-access-token>
export DATABRICKS_HOST=https://<workspace>.cloud.databricks.com

Tracer une fonction

Enveloppez n'importe quelle fonction avec mlflow.trace() pour créer une version tracée. MLflow capture automatiquement les entrées, les sorties, les exceptions et la latence. Les appels tracés imbriqués produisent une trace à plusieurs portées reflétant la hiérarchie des appels.

TypeScript
const getWeather = async (city: string) => `The weather in ${city} is sunny`;
const tracedGetWeather = mlflow.trace(getWeather, { name: 'get-weather' });
const result = await tracedGetWeather('San Francisco');

Décorateur de méthode de classe (TypeScript 5.0+)

TypeScript
class MyAgent {
@mlflow.trace({ spanType: mlflow.SpanType.LLM })
generateText(prompt: string) {
return "It's sunny in Seattle!";
}
}

Tracer un bloc de code

Utilisez mlflow.withSpan() pour tracer un bloc de code — l'équivalent TypeScript de mlflow.start_span():

TypeScript
const result = await mlflow.withSpan(async (span: mlflow.Span) => "It's sunny in Seattle!", {
name: 'generateText',
spanType: mlflow.SpanType.TOOL,
inputs: { prompt: question },
});

Traçage automatique pour OpenAI

Enveloppez le client OpenAI avec tracedOpenAI pour tracer automatiquement tous les appels :

TypeScript
import { OpenAI } from 'openai';
import { tracedOpenAI } from 'mlflow-openai';

const client = tracedOpenAI(new OpenAI());
const response = await client.chat.completions.create({
model: 'gpt-4o-mini',
messages: [{ role: 'user', content: "What's the weather in Seattle?" }],
});

Pour obtenir un exemple complet et fonctionnel, consultez l’ exemple full-stack en TypeScript sur GitHub.

Combiner le traçage automatique et manuel

Le traçage automatique et le traçage manuel se conjuguent. Activez autolog() pour chaque framework utilisé par votre agent et MLflow capture ces appels en une seule trace ; ajoutez @mlflow.trace pour les regrouper sous une seule portée parente ou pour instrumenter vos propres fonctions (prétraitement/post-traitement, logique métier, routage) que le journal automatique ne détecte pas.

Tracez plusieurs frameworks en une seule trace

Activez le log automatique pour chaque framework et MLflow associe leurs appels en une seule trace cohérente. Utilisez cette option lorsque votre agent associe des appels LLM directs à une couche d'orchestration :

Python
import mlflow

mlflow.openai.autolog()
mlflow.langchain.autolog()

# All OpenAI and LangChain calls in the same execution appear in one trace

Pour regrouper les appels de plusieurs frameworks sous une seule étendue parente, enveloppez le flux de travail avec @mlflow.trace:

Python
import mlflow
import openai
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate

mlflow.openai.autolog()
mlflow.langchain.autolog()

client = openai.OpenAI()

@mlflow.trace
def multi_provider_workflow(query: str):
# Direct OpenAI call — auto-traced as a child span
topics = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "Extract key topics from the query."},
{"role": "user", "content": query},
],
).choices[0].message.content

# LangChain chain — also auto-traced as a child span
chain = ChatPromptTemplate.from_template(
"Topics: {topics}\nRespond to: {query}"
) | ChatOpenAI(model="gpt-4o-mini")
return chain.invoke({"topics": topics, "query": query})

multi_provider_workflow("Explain quantum computing")

Ajouter des étendues manuelles en parallèle du log automatique

Ajoutez @mlflow.trace à vos propres fonctions pour capturer la logique que le log automatique ne couvre pas. MLflow Merge ces portées avec celles capturées automatiquement en une seule trace :

Python
import mlflow
import openai

mlflow.openai.autolog()
client = openai.OpenAI()

@mlflow.trace
def run(question):
messages = build_messages(question)
response = client.chat.completions.create( # auto-traced by autolog
model="gpt-4o-mini", max_tokens=100, messages=messages,
)
return parse_response(response)

@mlflow.trace
def build_messages(question):
return [
{"role": "system", "content": "You are a helpful chatbot."},
{"role": "user", "content": question},
]

@mlflow.trace
def parse_response(response):
return response.choices[0].message.content

run("What is MLflow?")

Cela produit une trace : une portée parente run avec des enfants build_messages et parse_response, ainsi que la portée OpenAI capturée automatiquement.

Combinaison de traçage automatique et manuel

Déployer en dehors de Databricks

Le traçage d'un agent déployé en dehors de Databricks utilise la même instrumentation. Définissez les variables d'environnement suivantes avant de lancer le processus de l'agent, puis instrumentez votre code à l'aide de l'une des méthodes ci-dessus :

Bash
export DATABRICKS_HOST="https://your-workspace.cloud.databricks.com"
export DATABRICKS_TOKEN="your-databricks-token"
export MLFLOW_TRACKING_URI=databricks
export MLFLOW_EXPERIMENT_NAME="/Shared/production-genai-agent"

Pour les déploiements de production, préférez le package léger mlflow-tracing (pip install mlflow-tracing) au package complet mlflow[databricks]. Pour la configuration du stockage Docker, Kubernetes et UC, consultez Agents de trace déployés en dehors de Databricks.

Avancé : API client bas niveau

L’API MlflowClient vous offre un contrôle direct sur chaque aspect du cycle de vie de la trace. La plupart des agents n’en ont pas besoin ; utilisez plutôt le décorateur ou le gestionnaire de contexte. Optez pour l’API client lorsque vous avez besoin de schémas d’ID de trace personnalisés ou d’une intégration avec un système d’observabilité existant.

important

Les APIs clientes ne sont pas interopérables avec le décorateur ou mlflow.start_span(). Utilisez un seul style de manière cohérente au sein d'une trace donnée.

Cycle de vie

Chaque appel à start_trace ou à start_span doit correspondre à un appel à end_trace ou à end_span. Les portées non fermées produisent des traces incomplètes.

Le cycle de vie des traces et des spans : start_trace, start_span, end_span, end_trace.

Identifiant

Description

Utilisation

request_id

Identifiant unique de trace

Link tous les spans de la trace

span_id

Identifiant de span unique

Identifie la portée à terminer

parent_id

De l’étendue parente span_id

Crée la hiérarchie parent-enfant

Identifiant

Description

Utilisation

request_id

Identifiant unique de trace

Link tous les spans de la trace

span_id

Identifiant de span unique

Identifie la portée à terminer

parent_id

De l’étendue parente span_id

Crée la hiérarchie parent-enfant

Utilisation de base

Python
from mlflow import MlflowClient

client = MlflowClient()

root_span = client.start_trace(
name="my_agent_flow",
inputs={&quot;user_id&quot;: &quot;123&quot;, &quot;action&quot;: &quot;generate_report&quot;},
attributes={&quot;environment&quot;: &quot;production&quot;, &quot;version&quot;: &quot;1.0.0&quot;},
)
request_id = root_span.request_id

data_span = client.start_span(
name="fetch_user_data",
request_id=request_id,
parent_id=root_span.span_id,
inputs={&quot;user_id&quot;: &quot;123&quot;},
attributes={&quot;database&quot;: &quot;users_db&quot;},
)

client.end_span(
request_id=data_span.request_id,
span_id=data_span.span_id,
outputs={&quot;record_count&quot;: 42},
status="OK",
)

client.end_trace(
request_id=request_id,
outputs={&quot;report_url&quot;: &quot;https://example.com/report/123&quot;},
status="OK",
)

Gestion des erreurs

Toujours fermer les étendues même en l'absence d'exceptions. Un gestionnaire de contexte réutilisable rend cette opération sûre et concise :

Python
from contextlib import contextmanager

@contextmanager
def traced_span(client, name, request_id, parent_id=None, **kwargs):
span = client.start_span(name=name, request_id=request_id, parent_id=parent_id, **kwargs)
try:
yield span
except Exception as e:
client.end_span(request_id=span.request_id, span_id=span.span_id,
status="ERROR", attributes={&quot;error&quot;: str(e)})
raise
else:
client.end_span(request_id=span.request_id, span_id=span.span_id, status="OK")

# Usage
with traced_span(client, "my_operation", request_id, parent_id) as span:
result = perform_operation()

Principaux écueils

  1. Oubli de clôturer les portées — utilisez toujours try/finally ou le modèle de gestionnaire de contexte ci-dessus.
  2. IDs parents incorrects — vérifiez que vous transmettez le bon span_id en tant que parent_id.
  3. IDs de trace codés en dur — générez toujours des ID uniques.
  4. Thread safety — les APIs client ne sont pas thread-safe par default ; gérez la concurrence de manière explicite.
  5. Utilisation de mlflow.log_metric() — ceci écrit dans une exécution MLflow, et non dans le span actuel. Utilisez span.set_attribute() ou span.set_attributes() à la place.

Custom OpenTelemetry instrumentation

remarque

Une instrumentation OTel personnalisée envoyant des traces à Databricks utilise l’ aperçu du traçage OTel . Assurez-vous que cet aperçu est activé dans votre Workspace avant de poursuivre.

Si votre agent utilise directement le SDK OTel plutôt qu’une intégration pré-construite, définissez les attributs de portée décrits dans cette section afin que MLflow restitue correctement les types de portée, les entrées, les sorties et les nombres de tokens. Les intégrations pré-construites définissent ces attributs automatiquement.

remarque

Les correspondances d’attributs OTel pour MLflow managé sur Databricks diffèrent de celles d’OSS MLflow. Pour la correspondance des attributs OSS, consultez la documentation MLflow.

Conditions requises

Cette section nécessite une expérimentation prise en charge par Unity Catalog avec un emplacement de trace OTel et la préversion du traçage OTel activée dans votre workspace. Voir Exigences.

Définir le type d’étendue

Définissez gen_ai.operation.name pour identifier le type d'opération. MLflow lit cet attribut et affiche le type d'intervalle MLflow correspondant dans l'interface utilisateur de suivi. La valeur respecte la convention sémantique GenAI OpenTelemetry.

Valeur de gen_ai.operation.name OTel

Type d'étendue MLflow

chat

CHAT_MODEL

text_completion

LLM

generate_content

LLM

response

LLM

embeddings

EMBEDDING

execute_tool

TOOL

create_agent

AGENT

invoke_agent

AGENT

Valeur de gen_ai.operation.name OTel

Type d'étendue MLflow

chat

CHAT_MODEL

text_completion

LLM

generate_content

LLM

response

LLM

embeddings

EMBEDDING

execute_tool

TOOL

create_agent

AGENT

invoke_agent

AGENT

Python
span.set_attribute("gen_ai.operation.name", "chat")

Définir les entrées et les sorties

Définissez gen_ai.input.messages et gen_ai.output.messages sur chaque portée qui doit afficher les entrées et les sorties. Les définir sur la racine de la portée permet également de remplir les aperçus de requêtes et de réponses au niveau de la trace.

Attribut OTel

Attribut MLflow

gen_ai.input.messages

mlflow.spanInputs

gen_ai.output.messages

mlflow.spanOutputs

Attribut OTel

Attribut MLflow

gen_ai.input.messages

mlflow.spanInputs

gen_ai.output.messages

mlflow.spanOutputs

Les valeurs peuvent être des chaînes simples ou des chaînes sérialisées au format JSON. Les tableaux JSON d'objets de message avec les champs role et content permettent un rendu plus riche dans l'interface utilisateur de MLflow (bulles étiquetées « User » et « Assistant ») :

Python
import json

# Plain string — displays as-is in the UI
span.set_attribute("gen_ai.input.messages", "What is the weather today?")

# JSON message array — renders with role labels in the UI
span.set_attribute("gen_ai.input.messages", json.dumps([
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What is the weather today?"}
]))
span.set_attribute("gen_ai.output.messages", json.dumps([
{"role": "assistant", "content": "It is sunny and 72°F in San Francisco."}
]))

Définir l'utilisation des jetons

Définissez gen_ai.usage.input_tokens et gen_ai.usage.output_tokens sur le root span pour afficher les comptes de jetons dans le résumé des traces de l'interface utilisateur. MLflow lit ces valeurs à partir du root span, car il agrège les comptes au niveau de la trace.

Attribut OTel gen_ai.usage.*

Champ de jeton MLflow

gen_ai.usage.input_tokens

Nombre de jetons d'entrée

gen_ai.usage.output_tokens

Nombre de jetons de sortie

(non défini — calculé automatiquement)

Nombre total de jetons

Attribut OTel gen_ai.usage.*

Champ de jeton MLflow

gen_ai.usage.input_tokens

Nombre de jetons d'entrée

gen_ai.usage.output_tokens

Nombre de jetons de sortie

(non défini — calculé automatiquement)

Nombre total de jetons

Python
root.set_attribute("gen_ai.usage.input_tokens", 150)
root.set_attribute("gen_ai.usage.output_tokens", 42)

Définir la session et l'utilisateur

Définissez session.id et user.id pour associer les traces à une session ou à un utilisateur spécifique. MLflow les lit à partir de la portée racine et les affiche sous forme de métadonnées au niveau de la trace. Le paramétrage de session.id active le tab de session dans l’interface utilisateur de MLflow.

Attribut OTel

Champ de métadonnées MLflow

session.id

Identifiant de session ou de conversation

user.id

Identifiant d'utilisateur final d'agent

Attribut OTel

Champ de métadonnées MLflow

session.id

Identifiant de session ou de conversation

user.id

Identifiant d'utilisateur final d'agent

Python
span.set_attribute("session.id", "conversation-123")
span.set_attribute("user.id", "user-456")

Exemple complet : un agent Python avec une étendue enfant LLM

L’exemple suivant rassemble les quatre catégories d’attributs dans un agent simple doté d’un span enfant LLM. Cet exemple suppose que vous avez déjà configuré l’exportateur OTLP pour envoyer des traces à Databricks.

Python
import json
from opentelemetry import trace

tracer = trace.get_tracer("my-agent")

def run_agent(query: str) -> str:
with tracer.start_as_current_span("agent-run") as root:
# Child LLM span — set gen_ai attributes for this individual call
with tracer.start_as_current_span("chat") as llm:
llm.set_attribute("gen_ai.operation.name", "chat")
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": query}
]
response = call_llm(messages)
llm.set_attribute("gen_ai.input.messages", json.dumps(messages))
llm.set_attribute("gen_ai.output.messages", json.dumps([
{"role": "assistant", "content": response}
]))
llm.set_attribute("gen_ai.usage.input_tokens", 150)
llm.set_attribute("gen_ai.usage.output_tokens", 42)

# Root span — MLflow reads inputs, outputs, token usage, and session ID
# from the root span to populate the trace summary in the UI.
root.set_attribute("gen_ai.operation.name", "chat")
root.set_attribute("session.id", "conversation-123")
root.set_attribute("user.id", "user-456")
root.set_attribute("gen_ai.input.messages", json.dumps([
{"role": "user", "content": query}
]))
root.set_attribute("gen_ai.output.messages", json.dumps([
{"role": "assistant", "content": response}
]))
root.set_attribute("gen_ai.usage.input_tokens", 150)
root.set_attribute("gen_ai.usage.output_tokens", 42)
return response

Vérifier dans l'interface utilisateur MLflow

Après avoir appelé run_agent(), ouvrez l'onglet Traces de votre expérimentation MLflow. Une trace correctement instrumentée indique :

  • Types d’étendue : chaque étendue affiche son libellé de type (par exemple, chat) au lieu de UNKNOWN.
  • Requête et réponse : La portée racine affiche les messages d’entrée et de sortie.
  • Utilisation des jetons : le récapitulatif des traces affiche les nombres de jetons d'entrée, de sortie et le total.
  • Session et utilisateur : la trace apparaît dans la tab de session sous l'identifiant de session spécifié, et l'ID utilisateur apparaît dans les métadonnées de la trace.

Trace GenAI OTel dans MLflow

Rechercher des traces par attributs de span OTel

Une fois les traces ingérées dans Unity Catalog — que ce soit à partir de Langfuse ou d’un agent instrumenté OTel personnalisé —, utilisez le préfixe span.attributes.* dans mlflow.search_traces() pour filtrer par les valeurs d’attribut OTel que vous avez définies. Le nom de l’attribut après le préfixe est le même que celui transmis à span.set_attribute().

Python
import mlflow

# experiment_id is visible in the MLflow UI URL and experiment details panel
mlflow.set_experiment(experiment_id="<experiment-id>")

# Find traces from a specific session (set using session.id)
traces = mlflow.search_traces(
filter_string="span.attributes.session.id = 'conversation-123'"
)

# Find traces from a specific user (set using user.id)
traces = mlflow.search_traces(
filter_string="span.attributes.user.id = 'user-456'"
)

# Find traces from a specific model (set using gen_ai.request.model)
traces = mlflow.search_traces(
filter_string="span.attributes.gen_ai.request.model LIKE '%gpt%'"
)

# Find traces by operation type (set using gen_ai.operation.name)
traces = mlflow.search_traces(
filter_string="span.attributes.gen_ai.operation.name = 'chat'"
)

# Find high-token traces (set using gen_ai.usage.input_tokens)
traces = mlflow.search_traces(
filter_string="span.attributes.gen_ai.usage.input_tokens > 1000"
)

Pour connaître la syntaxe complète de filter_string, y compris les opérateurs et comparateurs pris en charge, consultez Accès programmatique aux traces.

Limitations

Les attributs de portée OTel personnalisés ne sont pas affichés en tant qu'étiquettes de trace MLflow. Les attributs définis avec span.set_attribute() en dehors des correspondances OTel vers MLflow reconnues n'apparaissent pas dans :

  • La colonne Tags ou la vue unifiée des traces dans l’interface utilisateur MLflow.
  • La table Unity Catalog _traces_unified.
  • Le champ tags renvoyé par mlflow.search_traces().

Ces attributs sont conservés sur l’étendue sous-jacente. Elles restent visibles dans l’onglet Attributes tab de l’interface utilisateur des traces et peuvent être interrogées via le champ <prefix>_otel_spans.attributes de la table des étendues OTel.

Pour joindre des tags interrogeables qui apparaissent dans la vue unifiée des traces, utilisez les API de tag MLflow. Voir Enrichir les traces : tags, contexte et commentaires.

Référence du modèle de données de trace

Les attributs d’étendue, les types d’étendue et les concepts de cycle de vie ci-dessous s’appliquent à toute étendue que vous créez, que ce soit par le biais d’un décorateur, d’un gestionnaire de contexte ou du client de bas niveau.

Attributs de portée

Les attributs sont des paires clé-valeur qui fournissent des insight sur la configuration et le contexte d'exécution d'une opération.

Vous pouvez ajouter des attributs spécifiques à la plate-forme pour enrichir l’observabilité. Par exemple, vous pouvez ajouter les objets Unity Catalog touchés par la portée, l’ endpoint de serveur de modèle ou la ressource de compute.

Par exemple, définissez des attributs sur une étendue qui encapsule un appel LLM :

Python
span.set_attributes({
"ai.model.name": "claude-3-5-sonnet-20241022",
"ai.model.version": "2024-10-22",
"ai.model.provider": "anthropic",
"ai.model.temperature": 0.7,
"ai.model.max_tokens": 1000,
})

Types d’étendue

MLflow fournit des valeurs prédéfinies SpanType pour les opérations courantes. Pour des cas spécialisés, passez une valeur de chaîne personnalisée comme type de portée.

Type

Description

CHAT_MODEL

Query vers un modèle de chat (interaction LLM spécialisée)

CHAIN

Chaîne d'opérations

AGENT

Opération d'agent autonome

TOOL

Exécution d’outils (généralement par des agents), telles que des search queries

EMBEDDING

Opération d'intégration de texte

RETRIEVER

Opération de récupération de contexte telle que des queries de base de données vectorielle

PARSER

Opération de parsing transformant le texte en format structuré

RERANKER

Opération de réévaluation ordonnant les contextes par pertinence

MEMORY

Opération mémoire persistant le contexte dans le stockage à long terme

UNKNOWN

default type used when no other type is specified

Type

Description

CHAT_MODEL

Query vers un modèle de chat (interaction LLM spécialisée)

CHAIN

Chaîne d'opérations

AGENT

Opération d'agent autonome

TOOL

Exécution d’outils (généralement par des agents), telles que des search queries

EMBEDDING

Opération d'intégration de texte

RETRIEVER

Opération de récupération de contexte telle que des queries de base de données vectorielle

PARSER

Opération de parsing transformant le texte en format structuré

RERANKER

Opération de réévaluation ordonnant les contextes par pertinence

MEMORY

Opération mémoire persistant le contexte dans le stockage à long terme

UNKNOWN

default type used when no other type is specified

Vous attribuez un type d’étendue lorsque vous créez l’étendue. Voir Personnaliser les étendues pour savoir comment définir span_type sur le décorateur, ou le gestionnaire de contexte pour un bloc d’étendue.

Traces et étendues actives ou terminées

Une trace active est une trace en cours d’écriture par MLflow, par exemple pendant l’exécution d’une fonction décorée avec @mlflow.trace. Une fois que la fonction décorée s’est terminée, la trace est terminée , mais vous pouvez toujours l’annoter avec de nouvelles données.

Les portées suivent le même cycle de vie. Une portée active, représentée par LiveSpan, est produite par une fonction décorée ou un gestionnaire de contexte de portée. Une fois que la fonction se termine ou que le gestionnaire de contexte se ferme, la portée est terminée et devient un élément Span immuable.

Pour travailler avec des traces et des étendues actives ou récentes, utilisez ces méthodes :

Ressources supplémentaires

Étape suivante : Enrichir les traces : tags, contexte et retours d'information