Aller au contenu

Capability Observability

Arclith fournit une observabilité optionnelle aux frontières sans introduire de SDK fournisseur dans le domaine ou les use cases.

Adapters

Adapter Usage
langsmith traces agents, contexte distribué et API avancées LangSmith
opentelemetry runtime traces, métriques, logs OTLP et propagation W3C

Sans adapter activé, arclith.tracer() retourne un tracer no-op. Le code applicatif reste donc identique avec ou sans backend.

Pour la configuration complète des providers, signaux, profils, instrumentations, règles de confidentialité et du Collector local, lire le guide OpenTelemetry de bout en bout.

Installer et activer LangSmith

L'installation, l'activation et l'émission sont trois décisions indépendantes:

Bash
1
2
3
4
5
6
7
uv add "arclith[langsmith]"

arclith-cli add-adapter \
  --capability observability \
  --adapter langsmith \
  --profile development \
  --yes

La commande:

  • ajoute l'extra arclith[langsmith] idempotemment;
  • génère config/adapters/outbound/langsmith.yaml;
  • ajoute langsmith à observability.enabled;
  • génère uniquement les valeurs non secrètes dans .env.example;
  • ajoute .env à .gitignore;
  • ne demande et n'écrit jamais la clé API.

Copier les valeurs utiles de .env.example vers le secret store du runtime, puis définir:

Bash
export LANGSMITH_API_KEY="<secret>"

Si LangSmith n'est pas souhaité sur un environnement, ne pas le placer dans adapters.observability.enabled. Aucun module LangSmith, client, buffer ou appel réseau n'est alors créé.

Configuration complète

YAML
1
2
3
4
# config/adapters/adapters.yaml
observability:
  enabled:
    - langsmith
YAML
# config/adapters/outbound/langsmith.yaml
project: "my-service-dev"
endpoint: "https://api.smith.langchain.com"
api_key_env: LANGSMITH_API_KEY
workspace_id_env: LANGSMITH_WORKSPACE_ID

tracing:
  enabled: true
  mode: otel
  sampling_rate: 1.0

instrumentation:
  langgraph: true
  pydantic_ai: true
  fastapi: false
  fastmcp: true
  command_bus: true

capture:
  inputs: false
  outputs: false
  metadata: true
  model_content: false
  binary_content: false
  model_request_parameters: false

propagation:
  enabled: true
  langsmith_headers: true
  traceparent: true
  baggage_allowlist: []

tags:
  - arclith
metadata:
  deployment.environment: development

lifecycle:
  flush_timeout_seconds: 5.0

diagnostics:
  enabled: true
  log_level: info

failure_mode: log-and-continue

Les prompts, réponses, arguments/résultats de tools et contenus binaires sont masqués par défaut. Activer leur capture uniquement après analyse des données et de leur durée de rétention.

Pour une redaction métier plus fine, le projet consommateur injecte une fonction neutre au composition root. Arclith la transmet au client sans connaître les champs du domaine:

Python
from typing import Any

from arclith import Arclith


def redact_trace(payload: dict[str, Any]) -> dict[str, Any]:
    sanitized = dict(payload)
    sanitized.pop("email", None)
    return sanitized


arclith = Arclith("config", trace_anonymizer=redact_trace)

Cette fonction complète les interrupteurs capture.*; elle ne doit jamais réintroduire un champ masqué ni journaliser le payload reçu.

Profils CLI

development active un sampling à 1.0 et les diagnostics. production utilise 0.1, désactive les diagnostics et conserve tous les contenus sensibles masqués. Les deux profils gardent model_content: false; une capture explicite reste nécessaire.

Bash
1
2
3
4
5
6
arclith-cli add-adapter \
  --capability observability \
  --adapter langsmith \
  --profile production \
  --param sampling_rate=0.05 \
  --yes

Précédence

La résolution est déterministe:

  1. contexte d'invocation;
  2. variables LANGSMITH_*;
  3. YAML;
  4. valeurs sûres par défaut.

Overrides supportés: LANGSMITH_TRACING, LANGSMITH_API_KEY, LANGSMITH_WORKSPACE_ID, LANGSMITH_PROJECT, LANGSMITH_ENDPOINT, LANGSMITH_TRACING_SAMPLING_RATE, LANGSMITH_HIDE_INPUTS, LANGSMITH_HIDE_OUTPUTS, LANGSMITH_HIDE_METADATA et LANGSMITH_TRACING_MODE.

Arclith ne transforme jamais le YAML en mutations de os.environ.

API neutre

Python
1
2
3
4
5
6
7
8
9
tracer = arclith.tracer()

with tracer.span(
    "resolve-user-intent",
    kind="chain",
    metadata={"feature": "todo-agent"},
) as span:
    result = resolve_intent()
    span.set_outputs({"status": "resolved"})

Désactiver une invocation sensible sans modifier la configuration globale:

Python
with tracer.context(enabled=False):
    await process_sensitive_request()

Le contexte accepte aussi project, tags, metadata et parent. Seuls langsmith-trace, traceparent et le baggage allowlisté peuvent être propagés.

API LangSmith avancée

Arclith n'encapsule pas datasets, feedbacks, évaluations ou prompts:

Python
client = arclith.langsmith_client()

Le client est préconfiguré avec endpoint, workspace, politique de capture, mode et sampling. Il permet d'utiliser directement les nouvelles fonctions du SDK LangSmith.

Pour un script court:

Python
1
2
3
4
5
try:
    run_job()
finally:
    arclith.flush_observability()
    arclith.close_observability()

FastAPI, FastMCP et les runners Arclith ferment automatiquement le runtime à l'arrêt.

Instrumentation automatique

  • Arclith.langgraph() configure le client, projet, tags et métadonnées avant compilation.
  • Arclith.pydantic_ai_llm() injecte l'instrumentation uniquement dans les agents Pydantic AI construits par Arclith; aucun Agent.instrument_all() global n'est utilisé.
  • Arclith.instrument_mcp() trace les tools sans capturer leurs arguments/résultats bruts.
  • Arclith.rabbitmq_command_bus() injecte le contexte à la publication et l'extrait autour du handler.
  • instrumentation.fastapi: true ajoute un span HTTP LangSmith lorsque FastAPI n'est pas déjà instrumenté par OpenTelemetry.

Les metadata de transport sont bornées: méthode/route HTTP, nom du tool, type de commande, destination et statut. Les payloads métier ne sont jamais ajoutés automatiquement.

Runtime LangGraph durable

Un graphe compilé par Arclith.langgraph() conserve une référence en mémoire vers son ObservabilityRuntimePort. Lorsque arclith-agent-runtime charge ce graphe, il réutilise ce même runtime pour la propagation entrante et son cycle de vie. Cette référence n'est ni sérialisée ni placée dans le catalogue PostgreSQL.

Pour /threads/{thread_id}/runs/wait et /threads/{thread_id}/runs/stream, le propagateur actif extrait uniquement:

  • langsmith-trace lorsque le backend LangSmith l'autorise;
  • traceparent et tracestate lorsque la propagation W3C est active;
  • les membres de baggage présents dans la baggage_allowlist du backend.

Authorization, Cookie, clés API, JWT et tout header arbitraire sont éliminés avant l'exécution. Le contexte filtré reste éphémère: il n'est ajouté ni à RunRecord, ni à input, ni à la configuration LangGraph persistée.

Le runtime ouvre une span serveur langgraph.runtime.run pendant toute la durée du graphe, y compris les itérations SSE, erreurs et annulations. Ses seules métadonnées automatiques sont langgraph.thread_id, langgraph.run_id, langgraph.assistant_id et le statut technique langgraph.run.status; aucun payload métier n'est capturé. Le graphe et ses instrumentations créent leurs spans sous ce parent distribué.

Au démarrage, le serveur durable appelle start() et compose l'instrumentation FastAPI configurée. À l'arrêt, il exécute force_flush() puis shutdown(). /ready continue de vérifier uniquement le catalogue et la coordination: une panne d'export LangSmith ou OTLP ne devient pas une dépendance de readiness. Sans adapter actif, le runtime utilise le propagateur et le tracer no-op.

LangSmith et OpenTelemetry ensemble

Les deux adapters partagent un seul TracerProvider et plusieurs processors/exporters. Lorsque les deux sont activés, tracing.mode doit être otel; la configuration est refusée autrement pour éviter deux arbres concurrents.

YAML
1
2
3
4
observability:
  enabled:
    - opentelemetry
    - langsmith

L'instrumentation FastAPI et Pydantic AI n'est alors créée qu'une fois et les spans sont fan-out vers OTLP et LangSmith. Une panne d'export reste fail-open et ne doit pas interrompre le métier.

Hors ligne

Pour exécuter LangGraph sans LangSmith et sans clé, retirer langsmith de observability.enabled. L'extra langgraph ne déclare plus directement LangSmith; une éventuelle dépendance transitive du runtime LangGraph n'est jamais initialisée par Arclith.

YAML
observability:
  enabled: []

Le graphe et arclith.tracer() continuent de fonctionner localement.

Test live optionnel

Le test d'intégration est désactivé par défaut. Il émet un run identifiable, force le flush puis le recherche via le client:

Bash
1
2
3
4
ARCLITH_LANGSMITH_INTEGRATION=1 \
LANGSMITH_API_KEY="<secret>" \
LANGSMITH_PROJECT="arclith-integration" \
uv run --extra all pytest -q tests/integration/test_langsmith.py

Dépannage

  • clé API absente: injecter LANGSMITH_API_KEY ou retirer l'adapter de observability.enabled.
  • trace absente: vérifier successivement observability.enabled, tracing.enabled, le sampling, LANGSMITH_TRACING et le projet résolu dans arclith.observability_diagnostics().
  • ancienne valeur encore utilisée: les variables sont résolues au démarrage lazy du runtime et le client est ensuite conservé. Redémarrer le worker après un changement de secret ou de LANGSMITH_*; Arclith ne relit pas l'environnement pour chaque span.
  • spans dupliqués: avec OpenTelemetry actif, imposer tracing.mode: otel et ne conserver qu'une instrumentation FastAPI.
  • Studio local hors ligne: retirer LangSmith de l'activation et appeler directement l'API locale LangGraph.

Suite

Lire Observabilité production, logger, agent et Validation IA locale.