> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coreweave.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Démarrage rapide : configurer l’observabilité d’un agent personnalisé

> Instrumentez un agent personnalisé avec Agent Lens pour capturer les spans OTel existants.

Le SDK CoreWeave Forge vous permet de tracer des agents conçus avec des SDK populaires ou des harness personnalisés. Ce guide de démarrage rapide explique comment intégrer manuellement CoreWeave Agent Lens à un agent personnalisé fonctionnant sur plusieurs tours de conversation, afin d’émettre et de capturer des spans OpenTelemetry. Pour découvrir les concepts d’Agent Lens appliqués aux agents, consultez [Tracer vos agents](/fr/products/agent-lens/tracing/instrument).

Si vous souhaitez plutôt intégrer Agent Lens à des SDK ou à des harness tels que le Claude Agent SDK ou Codex, consultez [Choisir une intégration d’agent](/fr/products/agent-lens/get-started/integrations). Agent Lens s’intègre automatiquement, par patch, à plusieurs SDK de création d’agents et harness d’agents, ce qui permet une intégration rapide.

<h2 id="what-youll-learn">
  Ce que vous allez apprendre
</h2>

À la fin de ce démarrage rapide, vous disposerez d’un agent fonctionnel sur plusieurs tours de conversation qui émet des spans OTel compatibles avec Agent Lens. Vous comprendrez également comment Agent Lens fait correspondre les conversations, les tours de conversation, les appels LLM et les appels d’outil au code de votre agent, afin d’appliquer le même modèle à vos propres agents personnalisés.

Le code de ce guide met en place un petit agent de recherche en Python ou en TypeScript capable de consulter Wikipédia. Il pose trois questions (trois tours de conversation) et laisse le LLM choisir quand effectuer une recherche sur Wikipédia pour trouver une réponse. Agent Lens enregistre chaque étape (la conversation, chaque question, chaque réponse de l’IA et chaque recherche sur Wikipédia) afin que vous puissiez voir ce qui s’est passé dans l’onglet Conversations d’Agent Lens.

Ce guide vous montre comment :

* Initialiser Agent Lens pour le traçage d’agents avec `tracing.init()`.
* Ouvrir une conversation et un tour de conversation avec `start_conversation` / `startConversation` et `start_turn` / `startTurn`.
* Encapsuler les appels LLM avec `start_llm` / `startLLM` et enregistrer l’utilisation.
* Encapsuler les exécutions d’outils avec `start_tool` / `startTool` et enregistrer les résultats.
* Enregistrer l’utilisation complète des jetons et un modèle tarifable afin que le nombre de jetons et le coût s’affichent.
* Afficher la conversation, les tours de conversation et les appels d’outil qui en résultent dans l’onglet Conversations.

<h2 id="how-the-agent-lens-sdk-works-with-agents">
  Fonctionnement du SDK Agent Lens avec les agents
</h2>

Le SDK Agent Lens intègre un système générique d’ingestion OTel pour les agents : Agent Lens peut donc capturer les informations de n’importe quel span OTel dans le code de votre agent. Toutefois, Agent Lens nécessite une gestion particulière des spans suivants pour effectuer le rendu des traces de votre agent dans l’onglet Conversations de l’interface Agent Lens.

| Concept | Python | TypeScript | Span OTel |
| - | - | - | - |
| Une conversation | `tracing.start_conversation(...)` | `tracing.startConversation(...)` | (aucun span, regroupe les tours de conversation) |
| Un échange de l’utilisateur ou de l’agent | `tracing.start_turn(...)` | `tracing.startTurn(...)` | `invoke_agent` |
| Un appel d’API LLM | `tracing.start_llm(...)` | `tracing.startLLM(...)` | `chat` |
| Une exécution d’outil | `tracing.start_tool(...)` | `tracing.startTool(...)` | `execute_tool` |

En Python, les quatre fonctions s’utilisent comme gestionnaires de contexte (`with tracing.start_*(...) as obj:`). À la sortie, elles terminent le span et vident les attributs, y compris en cas d’exception. En TypeScript, appelez `.end()` sur chaque objet renvoyé. Utilisez `try { ... } finally { obj.end(); }` pour garantir le nettoyage en cas d’exception, et encapsulez chaque run de l’agent dans `tracing.runIsolated()` afin que des runs simultanés ne partagent pas la même conversation.

D’autres [attributs des conventions sémantiques GenAI](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-agent-spans/), tels que `gen_ai.usage.*` et `gen_ai.agent.name`, enrichissent le rendu, mais ils sont facultatifs.

<h2 id="prerequisites">
  Prérequis
</h2>

* Un compte CoreWeave Forge et une [clé API](https://forge.coreweave.com/settings#apikeys).
* Une clé API OpenAI.
* Python 3.9 ou version ultérieure (pour les exemples Python).
* Node.js 18 ou version ultérieure et un exécuteur TypeScript tel que `tsx` (les exemples TypeScript nécessitent la fonction `fetch` intégrée et ne sont pas du JavaScript pur).

<h2 id="install-packages">
  Installer les packages
</h2>

Installez les packages suivants dans votre environnement de développement :

<CodeGroup>
  ```bash Python theme={"system"}
  pip install coreweave openai requests
  ```

  ```bash TypeScript theme={"system"}
  npm install @coreweave/forge-sdk openai
  npm install --save-dev tsx
  ```
</CodeGroup>

Enregistrez les exemples TypeScript dans des fichiers `.mts`, puis exécutez-les avec `npx tsx [FILENAME].mts`.

<h2 id="initialize-agent-lens">
  Initialiser Agent Lens
</h2>

`tracing.init()` s’authentifie à l’aide de votre clé API et configure l’exporteur OTel qui envoie les spans d’agent à Agent Lens. Le nom du projet doit inclure votre équipe. Le SDK lit la clé API dans la variable d’environnement `WANDB_API_KEY`, mais vous pouvez aussi la transmettre avec l’argument `api_key` / `apiKey`.

<CodeGroup>
  ```python lines Python theme={"system"}
  import getpass
  import os

  os.environ["WANDB_API_KEY"] = getpass.getpass("Enter your API key: ")
  os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter your OpenAI API key: ")

  TEAM = input("Enter your team name: ")
  PROJECT = input("Enter your project name: ")

  from coreweave.forge.agentlens import tracing
  tracing.init(f"{TEAM}/{PROJECT}", autopatch_integrations=False)
  ```

  ```typescript lines highlight="" TypeScript theme={"system"}
  // Définissez WANDB_API_KEY et OPENAI_API_KEY dans votre environnement avant d’exécuter ce projet
  import { tracing } from '@coreweave/forge-sdk/agentlens';

  await tracing.init('[YOUR-TEAM]/[YOUR-PROJECT]');
  ```
</CodeGroup>

<h2 id="define-a-tool">
  Définir un outil
</h2>

Le code suivant définit l’outil de recherche Wikipédia de l’agent, ainsi qu’un schéma d’outil OpenAI qui indique quand et comment l’utiliser.

<CodeGroup>
  ```python lines Python theme={"system"}
  import json
  import requests

  def wikipedia_search(query: str) -> str:
      r = requests.get(
          "https://en.wikipedia.org/w/api.php",
          params={
              "action": "query", "generator": "search", "gsrsearch": query, "gsrlimit": 1,
              "prop": "extracts", "exintro": True, "explaintext": True, "format": "json",
          },
          headers={"User-Agent": "agent-lens-demo"},
      ).json()
      return next(iter(r["query"]["pages"].values()))["extract"]

  wikipedia_tool_schema = {
      "type": "function",
      "function": {
          "name": "wikipedia_search",
          "description": "Search Wikipedia for a topic and return its intro paragraph.",
          "parameters": {
              "type": "object",
              "properties": {"query": {"type": "string"}},
              "required": ["query"],
          },
      },
  }
  ```

  ```typescript lines TypeScript theme={"system"}
  async function wikipediaSearch(query: string): Promise<string> {
    const url = new URL('https://en.wikipedia.org/w/api.php');
    url.search = new URLSearchParams({
      action: 'query',
      generator: 'search',
      gsrsearch: query,
      gsrlimit: '1',
      prop: 'extracts',
      exintro: 'true',
      explaintext: 'true',
      format: 'json',
    }).toString();
    const res = await fetch(url, { headers: { 'User-Agent': 'agent-lens-demo' } });
    const data = (await res.json()) as {
      query: { pages: Record<string, { extract: string }> };
    };
    return Object.values(data.query.pages)[0].extract;
  }

  const wikipediaToolSchema = {
    type: 'function' as const,
    function: {
      name: 'wikipedia_search',
      description: 'Search Wikipedia for a topic and return its intro paragraph.',
      parameters: {
        type: 'object',
        properties: { query: { type: 'string' } },
        required: ['query'],
      },
    },
  };
  ```
</CodeGroup>

<h2 id="run-a-traced-multi-turn-agent">
  Exécuter un agent tracé sur plusieurs tours de conversation
</h2>

Une fois l’outil et l’initialisation d’Agent Lens en place, l’étape suivante consiste à les réunir dans une boucle d’agent complète. Cette boucle montre comment les conversations, les tours de conversation, les appels LLM et les appels d’outil s’imbriquent.

L’exemple suivant exécute trois tours de conversation au sein d’une même conversation. À chaque tour, l’agent :

1. Ouvre un span `chat` et laisse le LLM décider s’il doit appeler l’outil.
2. Si le LLM demande un outil, ouvre un span `execute_tool` autour de l’appel et renvoie le résultat au LLM.
3. Ouvre un second span `chat` pour produire la réponse finale.

<Note>
  Le SDK Agent Lens trace automatiquement les appels effectués avec les bibliothèques clientes OpenAI, Anthropic et Google Gen AI. Ce démarrage rapide enregistre manuellement chaque appel LLM avec `start_llm()` afin de montrer comment les spans s’articulent entre eux ; il transmet donc `autopatch_integrations=False` à `init()`. Sans cet argument, chaque appel serait enregistré deux fois : une fois par votre span `start_llm()` et une fois par l’intégration automatique. Transmettez cet argument dès votre premier appel à `init()`, car un appel ultérieur avec `autopatch_integrations=False` ne supprime pas les patchs appliqués par un appel précédent. Dans votre propre code, laissez le patching automatique enregistrer vos appels LLM, ou bien désactivez-le et enregistrez-les vous-même.
</Note>

<CodeGroup>
  ```python lines Python theme={"system"}
  from coreweave.forge.agentlens import tracing
  from openai import OpenAI

  openai_client = OpenAI()
  MODEL = "gpt-4o-mini"

  def run_turn(history, user_message):
      history.append({"role": "user", "content": user_message})

      with tracing.start_turn(user_message=user_message, model=MODEL):
          # Appel LLM 1 : le modèle peut décider d’utiliser un outil.
          with tracing.start_llm(model=MODEL, provider_name="openai") as llm:
              resp = openai_client.chat.completions.create(
                  model=MODEL, messages=history, tools=[wikipedia_tool_schema],
              )
              msg = resp.choices[0].message
              llm.output(msg.content or "")
              # record() définit en un seul appel l’utilisation, le modèle servant à la tarification et l’ID de réponse.
              llm.record(
                  usage=tracing.Usage(
                      input_tokens=resp.usage.prompt_tokens,
                      output_tokens=resp.usage.completion_tokens,
                      cache_read_input_tokens=getattr(
                          resp.usage.prompt_tokens_details, "cached_tokens", 0
                      ),
                  ),
                  response_id=resp.id,
                  response_model=resp.model,
              )
              history.append(msg.model_dump(exclude_none=True))

          # Si aucun outil n’a été demandé, la première réponse du LLM constitue la réponse finale.
          if not msg.tool_calls:
              return msg.content

          # Exécuter chaque appel d’outil demandé.
          for tc in msg.tool_calls:
              with tracing.start_tool(
                  name=tc.function.name,
                  arguments=tc.function.arguments,
                  tool_call_id=tc.id,
              ) as tool:
                  tool.result = wikipedia_search(**json.loads(tc.function.arguments))
                  history.append({
                      "role": "tool",
                      "tool_call_id": tc.id,
                      "content": tool.result,
                  })

          # Appel LLM 2 : synthétiser la réponse finale.
          with tracing.start_llm(model=MODEL, provider_name="openai") as llm:
              resp = openai_client.chat.completions.create(model=MODEL, messages=history)
              msg = resp.choices[0].message
              llm.output(msg.content)
              llm.record(
                  usage=tracing.Usage(
                      input_tokens=resp.usage.prompt_tokens,
                      output_tokens=resp.usage.completion_tokens,
                      cache_read_input_tokens=getattr(
                          resp.usage.prompt_tokens_details, "cached_tokens", 0
                      ),
                  ),
                  response_id=resp.id,
                  response_model=resp.model,
              )
              history.append({"role": "assistant", "content": msg.content})
              return msg.content

  tracing.init(f"{TEAM}/{PROJECT}", autopatch_integrations=False)

  with tracing.start_conversation(agent_name="research-bot") as conversation:
      history = []
      for question in [
          "Who founded Anthropic?",
          "What is Claude (the AI assistant)?",
          "Summarize what we discussed in one sentence.",
      ]:
          print(f"USER: {question}")
          print(f"AGENT: {run_turn(history, question)}\n")

  tracing.shutdown()
  ```

  ```typescript lines TypeScript theme={"system"}
  import { tracing } from '@coreweave/forge-sdk/agentlens';
  import OpenAI from 'openai';

  const openaiClient = new OpenAI();
  const MODEL = 'gpt-4o-mini';

  // history est une liste de messages de chat OpenAI ; typage volontairement souple par souci de concision.
  async function runTurn(history: any[], userMessage: string): Promise<string | null> {
    history.push({ role: 'user', content: userMessage });

    const turn = tracing.startTurn({ userMessage, model: MODEL });
    try {
      // Appel LLM 1 : le modèle peut choisir d'utiliser un outil.
      const llm1 = tracing.startLLM({ model: MODEL, providerName: 'openai' });
      let msg;
      try {
        const resp = await openaiClient.chat.completions.create({
          model: MODEL,
          messages: history,
          tools: [wikipediaToolSchema],
        });
        msg = resp.choices[0].message;
        llm1.output(msg.content ?? '');
        // record() définit l'utilisation, le modèle facturé et l'ID de réponse en un seul appel.
        llm1.record({
          usage: {
            inputTokens: resp.usage?.prompt_tokens,
            outputTokens: resp.usage?.completion_tokens,
            cacheReadInputTokens: resp.usage?.prompt_tokens_details?.cached_tokens,
          },
          responseId: resp.id,
          responseModel: resp.model,
        });
        history.push(msg);
      } finally {
        llm1.end();
      }

      // Si aucun outil n'a été demandé, la première réponse du LLM est la réponse finale.
      if (!msg.tool_calls?.length) {
        return msg.content ?? null;
      }

      // Exécuter chaque appel d'outil demandé.
      for (const tc of msg.tool_calls) {
        if (tc.type !== 'function') continue;
        const tool = tracing.startTool({
          name: tc.function.name,
          args: tc.function.arguments,
          toolCallId: tc.id,
        });
        try {
          const { query } = JSON.parse(tc.function.arguments);
          tool.result = await wikipediaSearch(query);
          history.push({ role: 'tool', tool_call_id: tc.id, content: tool.result });
        } finally {
          tool.end();
        }
      }

      // Appel LLM 2 : synthétiser la réponse finale.
      const llm2 = tracing.startLLM({ model: MODEL, providerName: 'openai' });
      try {
        const resp = await openaiClient.chat.completions.create({
          model: MODEL,
          messages: history,
        });
        const msg2 = resp.choices[0].message;
        llm2.output(msg2.content ?? '');
        llm2.record({
          usage: {
            inputTokens: resp.usage?.prompt_tokens,
            outputTokens: resp.usage?.completion_tokens,
            cacheReadInputTokens: resp.usage?.prompt_tokens_details?.cached_tokens,
          },
          responseId: resp.id,
          responseModel: resp.model,
        });
        history.push({ role: 'assistant', content: msg2.content });
        return msg2.content ?? null;
      } finally {
        llm2.end();
      }
    } finally {
      turn.end();
    }
  }

  await tracing.init('[YOUR-TEAM]/[YOUR-PROJECT]');

  await tracing.runIsolated(async () => {
    const conversation = tracing.startConversation({ agentName: 'research-bot' });
    try {
      const history: any[] = [];
      for (const question of [
        'Who founded Anthropic?',
        'What is Claude (the AI assistant)?',
        'Summarize what we discussed in one sentence.',
      ]) {
        console.log(`USER: ${question}`);
        console.log(`AGENT: ${await runTurn(history, question)}\n`);
      }
    } finally {
      conversation.end();
    }
  });

  await tracing.shutdown();
  ```
</CodeGroup>

<h2 id="record-token-usage-and-cost">
  Enregistrer l’utilisation des jetons et le coût
</h2>

Chaque span `chat` contient l’utilisation des jetons et un ID du modèle. Agent Lens affiche le nombre de jetons à partir de l’utilisation et calcule le coût à partir de l’utilisation et de l’ID du modèle. Une valeur incomplète ou impossible à tarifer affiche donc `0 in / 0 out` jetons ou `Cost -`, même si le reste de la trace semble correct. `record(...)` définit ces champs (ainsi que `output_messages`, `response_id`, `reasoning`, etc.) en un seul appel. Seuls les champs que vous transmettez sont appliqués.

Deux conditions doivent être remplies pour que le coût s’affiche :

* **Une utilisation complète.** `input_tokens` correspond à l’entrée *totale*, jetons mis en cache compris. Agent Lens facture les lectures et les écritures de cache à leurs propres tarifs et les soustrait du total d’entrée : `cache_read_input_tokens` et `cache_creation_input_tokens` doivent donc être indiqués *en plus* d’un total `input_tokens` qui les inclut. Chez les fournisseurs proposant la mise en cache des prompts (par exemple, Anthropic), les jetons mis en cache représentent souvent l’essentiel de l’entrée : si vous les omettez, l’utilisation et le coût affichés seront quasi nuls.
* **Un ID du modèle tarifable.** Transmettez l’ID précis renvoyé par la réponse (`resp.model`) en tant que `response_model`. Le coût est déterminé à partir du modèle. Agent Lens privilégie `response_model` (le modèle exact servi par le fournisseur) et, à défaut, utilise le `model` que vous avez transmis à `start_llm`. Un alias tel que `opus` ou `sonnet` n’est pas tarifable et affiche `Cost -`.

OpenAI inclut les jetons mis en cache dans `prompt_tokens` : l’exemple ci-dessus s’applique donc tel quel. Anthropic indique les jetons mis en cache *séparément* de `input_tokens` ; réintégrez-les donc dans le total sur lequel Agent Lens calcule le prix :

<CodeGroup>
  ```python lines Python theme={"system"}
  with tracing.start_llm(model=MODEL, provider_name="anthropic") as llm:
      resp = anthropic_client.messages.create(
          model=MODEL, max_tokens=1024, messages=history,
      )
      u = resp.usage
      llm.output(resp.content[0].text)
      llm.record(
          usage=tracing.Usage(
              input_tokens=u.input_tokens
              + u.cache_read_input_tokens
              + u.cache_creation_input_tokens,
              output_tokens=u.output_tokens,
              cache_read_input_tokens=u.cache_read_input_tokens,
              cache_creation_input_tokens=u.cache_creation_input_tokens,
          ),
          response_id=resp.id,
          response_model=resp.model,
      )
  ```

  ```typescript lines TypeScript theme={"system"}
  const u = resp.usage;
  llm.record({
    usage: {
      inputTokens:
        u.input_tokens + u.cache_read_input_tokens + u.cache_creation_input_tokens,
      outputTokens: u.output_tokens,
      cacheReadInputTokens: u.cache_read_input_tokens,
      cacheCreationInputTokens: u.cache_creation_input_tokens,
    },
    responseId: resp.id,
    responseModel: resp.model,
  });
  ```
</CodeGroup>

<h2 id="see-your-agent-traces-in-the-conversations-tab">
  Afficher les traces de votre agent dans l’onglet Conversations
</h2>

Ouvrez votre projet dans Agent Lens et sélectionnez **Conversations**. Vous y trouverez :

* Une conversation pour `research-bot` comportant trois tours de conversation.
* Pour chaque tour de conversation (`invoke_agent`), deux spans `chat` et un span `execute_tool` imbriqués.
* Pour chaque `chat`, le nombre de jetons, la latence, le modèle et l’échange de messages complet.

Sélectionnez la conversation pour examiner les entrées, les sorties, les arguments et les résultats des outils dans les onglets **Thread** et **Spans**.

<h2 id="link-to-a-conversation-from-your-app">
  Créer un lien vers une conversation depuis votre application
</h2>

Pour créer un lien profond (deep link) depuis votre propre interface vers une conversation dans Agent Lens, vous avez besoin de l’ID de la conversation. `start_conversation` / `startConversation` l’expose via `conversation_id` / `conversationId`. Vous pouvez ainsi le journaliser ou le stocker avec votre propre ID de requête, puis ouvrir la conversation plus tard depuis l’onglet **Conversations**.

<CodeGroup>
  ```python lines Python theme={"system"}
  with tracing.start_conversation(agent_name="research-bot") as conversation:
      # ... exécuter les tours de conversation ...
      print(f"Agent Lens conversation ID: {conversation.conversation_id}")
  ```

  ```typescript lines TypeScript theme={"system"}
  await tracing.runIsolated(async () => {
    const conversation = tracing.startConversation({ agentName: 'research-bot' });
    try {
      // ... exécuter les tours de conversation ...
      console.log(`Agent Lens conversation ID: ${conversation.conversationId}`);
    } finally {
      conversation.end();
    }
  });
  ```
</CodeGroup>

<h2 id="next-steps">
  Étapes suivantes
</h2>

* Découvrez comment [tracer des agents avec Agent Lens](/fr/products/agent-lens/tracing/instrument), ainsi que les fonctionnalités et options disponibles dans le SDK Agent Lens.
* Consultez [Choisir une intégration d’agent](/fr/products/agent-lens/get-started/integrations) pour découvrir d’autres façons d’intégrer Agent Lens à vos agents.
