Aller au contenu
English

Mesurer l'utilisation et le coût

  • Leçon 18
  • 45 min
  • Module 6 : Contrôles de fiabilité et de coût

Ce que vous saurez faire

Répondez aux deux questions d’TermeOmarLe gestionnaire de produit de Larkfield. Il pose les questions d’affaires : ce qu’un changement coûte et ce qu’il apporte à la boutique. avec des chiffres : combien coûte l’assistant et combien de temps un agent attend-il? Consignez chaque appel au modèle, lisez les totaux et fixez un budget que le code peut vérifier.

Avant de commencerCe qu’il faut savoir et avoir sous la mainEnviron 45 minutes

Ce que vous devez savoir

Ce qu’il vous faut sur votre ordinateur

  • Le projet après Gérer les limites du fournisseur : retry.py et simulate.py. python -m pytest affiche 75 passed, 1 deselected (75 tests réussis, 1 non sélectionné).
  • Si vous avez sauté cette leçon, copiez l’instantané end-of-m05 en suivant Réinitialiser le projet, puis faites les étapes de cette leçon. Ou copiez end-of-m06 et consultez-le en lisant les étapes de cette leçon.

Une facture de téléphone qui indique seulement « Total : 84 dollars » ne vous aide pas. Vous ne voyez pas quel appel coûtait cher, ni si le mois prochain coûtera autant. Une facture détaillée contient une ligne par appel : la date, le numéro, les minutes et le prix. Elle permet de trouver le long appel à l’étranger et de décider quoi changer.

Le problème : la facture arrive plus tard, en un seul montant

Section intitulée « Le problème : la facture arrive plus tard, en un seul montant »

Omar pose deux questions avant que TermeLarkfieldUne boutique en ligne d’articles pour la maison. Ce n’est pas une vraie entreprise. Tous les cours Nextia utilisent son service d’assistance et ses données comme exemple.En savoir plus L'IA dans les produits de tous les joursBases d'une carrière en IA : rôles, compétences et feuilles de route active l’assistant : « Combien coûtera-t-il par jour? Et combien de temps un agent attend-il une ébauche? » La facture du fournisseur répond à la première question un mois plus tard, par un total pour tout le compte. Elle ne précise pas quel billet, quelle requête ou quel modèle a consommé cet argent.

Certains coûts sont aussi cachés. Termechat-smallLe nom que le code de Larkfield donne à un petit modèle hébergé peu coûteux : gpt-6-luna sur Azure. Le code utilise ce nom, donc le modèle peut changer sans modifier le code.En savoir plus Ce qu'est une API de LLMCréer des applications fiables avec les API de LLM est un TermeModèle de raisonnementUn modèle qui écrit des jetons de raisonnement cachés avant sa réponse. chat-small en est un. Un tel modèle accepte souvent seulement sa température par défaut, et ses jetons cachés comptent dans la limite de sortie.En savoir plus Raisonnement et multimodalitéLes LLM : comment ils fonctionnent et comment en choisir un : il réfléchit avant de répondre. Le fournisseur facture ces TermeJetons de raisonnementDes jetons que certains modèles écrivent avant la réponse pour travailler le problème. Vous ne les voyez pas, mais ils comptent dans la limite de sortie et vous les payez.En savoir plus Raisonnement et multimodalitéLes LLM : comment ils fonctionnent et comment en choisir un comme des jetons de sortie. Sur le TermeJeu de testUn ensemble fixe d’exemples dont on connaît les bonnes réponses. Chaque version d’un prompt ou d’un modèle passe sur le même ensemble, pour comparer les résultats équitablement.En savoir plus Créer un jeu de test représentatifLes LLM : comment ils fonctionnent et comment en choisir un, il utilisait une TermeMédianeLa valeur du milieu quand on trie les valeurs. La moitié est plus basse, l’autre plus haute. Un seul appel très lent la change peu : c’est un bon temps « habituel ».En savoir plus Mesurer la performance en pratiqueLes LLM : comment ils fonctionnent et comment en choisir un de 215 jetons de raisonnement par billet, que vous ne voyez jamais. Une fois, pour T-64849 (requête v2, sans TermeSortie structuréeUne requête qui envoie au fournisseur le schéma de la réponse, pour que le modèle ne puisse écrire qu’une réponse de cette forme. Elle contrôle la forme, pas la vérité : l’application vérifie toujours chaque réponse.En savoir plus Définir un schémaCréer des applications fiables avec les API de LLM), il a consacré les 1 000 jetons de sortie au raisonnement. Il n’a renvoyé aucun texte. Larkfield a payé pour une réponse vide.

Le temps pose le même problème. Une moyenne cache les appels lents, et un agent se souvient des appels lents.

Un journal d’utilisation contient une ligne pour chaque appel au modèle, comme la facture détaillée. Chaque ligne indique de quel appel il s’agit et ce qu’il a utilisé. Elle ne contient rien que le journal ne doit pas conserver. Le projet écrit ces lignes dans logs/usage.jsonl, un objet JSON par ligne. Voici les champs et leur raison d’être :

Champ Raison
request_id L’identifiant de l’appel fourni par le fournisseur. Vous le mentionnez au soutien du fournisseur pour poser une question sur cet appel.
model, prompt Pour comparer le coût de chat-small et chat-strong, ou des requêtes v1 et v2.
ticket_id Pour additionner les appels d’un billet : une boucle d’outils en fait deux.
input_tokens, output_tokens, reasoning_tokens Ce que le fournisseur facture. Les jetons de raisonnement font partie de la sortie.
latency_s Le temps d’attente de l’agent pour cet appel.
cost_usd, prices_checked Le coût calculé à partir d’une table de prix datée : le prix était une hypothèse à cette date.
finish_reason, status, note Ce qui s’est passé. La note passe par redact().

Deux éléments manquent volontairement : le texte du billet et la réponse. Le journal sert à consigner des chiffres. Le texte des billets contient des noms, des adresses et parfois des numéros de carte. Plus de personnes lisent un fichier journal que le système de billets. Le champ ticket_id suffit pour retrouver le billet au besoin.

La méthode vient de Coût par billet, dans le cours sur les modèles de langage. Multipliez chaque type de jeton par son prix, puis additionnez. Les prix viennent d’une table de prix : dollars américains par million de jetons, pour l’entrée et la sortie. Voici les prix Azure des deux TermeDéploiementUn modèle qu’un compte configure chez un fournisseur, avec son propre nom et ses propres limites. Larkfield a deux déploiements sur Azure, le nuage de Microsoft : chat-small et chat-strong.En savoir plus Gérer les limites du fournisseurCréer des applications fiables avec les API de LLM, tels que nous les avons vérifiés pour le cours. Consultez la page des prix actuels avant de les utiliser; les prix changent.

Déploiement Entrée, par million de jetons Sortie, par million de jetons
chat-small (gpt-6-luna) 0,10 $ US 0,50 $ US
chat-strong (gpt-6.1-sol) 2,00 $ US 10,00 $ US

T-80008, le billet de démonstration, a utilisé deux appels à chat-small : 2 043 + 2 181 = 4 224 jetons d’entrée. Il a utilisé 21 + 102 = 123 jetons de sortie. Donc :

input 4,224 × 0.10 / 1,000,000 = 0.0004224
output 123 × 0.50 / 1,000,000 = 0.0000615
total = 0.0004839 ≈ US$0.00048

L’entrée représente ici la majeure partie du coût. La requête, la politique et le résultat de l’outil sont longs, et la réponse est courte. Cela change selon le modèle. Voici le coût du jeu d’évaluation complet, mesuré à partir des enregistrements, pour les quatre exécutions avec sortie structurée :

  • chat-small (gpt-6-luna)
  • chat-strong (gpt-6.1-sol)
Coût par 1 000 billets : coût moyen des 69 billets enregistrés, avec les prix de la table. Sans outils, avec sortie structurée.
Description textuelle du graphique

Un graphique à colonnes du coût par 1 000 billets. Avec chat-small : 0,28 $ US pour la requête v1 et 0,35 $ US pour la requête v2. Avec Termechat-strongLe nom que le code de Larkfield donne à un modèle hébergé plus grand : gpt-6.1-sol sur Azure. Il est souvent plus exact que chat-small et coûte environ 14 à 18 fois plus par appel.En savoir plus Les éléments d'une requêteCréer des applications fiables avec les API de LLM : 3,97 $ US pour la requête v1 et 5,03 $ US pour la requête v2. Les colonnes de chat-strong sont environ 14 fois plus hautes que celles de chat-small.

chat-strong coûte environ 14 fois plus par billet. Sur les mêmes 69 billets avec la requête v2, il a choisi la bonne équipe une fois de plus (68 contre 67 sur 68). Il a aussi obtenu un bon résultat de plus pour needs_human (intervention humaine nécessaire) : 68 contre 67 sur 69. Pour les 45 billets quotidiens de Larkfield, chat-small avec la requête v2 coûte environ 0,016 $ US. chat-strong coûte environ 0,23 $ US. Les deux montants sont faibles. Mais le rapport reste de 14 quand le volume augmente, et le journal permet de le remarquer.

La requête v2 coûte environ 27 % de plus que v1 avec chat-small. Elle envoie 609 jetons d’entrée supplémentaires par billet : la spécification et deux exemples. Vous payez une requête plus longue à chaque appel.

Vous avez vu la médiane et le p95 dans Mesurer la performance en pratique. Ce sont l’attente typique et la durée sous laquelle restent 95 appels sur 100. L’image montre les deux pour l’assistant.

Temps enregistré de chacun des 69 billets sur chat-small, requête v2, sortie structurée (gpt-6-luna sur Azure) : médiane de 2,77 s, p95 de 4,34 s.
Description textuelle du graphique

Un histogramme du temps pour 69 billets, en classes d’une demi-seconde, de 1 à 7 secondes. La plupart des billets prennent entre 2 et 3,5 secondes. Une queue à droite dépasse 5 secondes. Une ligne pointillée indique la médiane à 2,77 secondes, et une seconde indique le Termep95Le 95e centile : le temps sous lequel restent 95 appels sur 100. Il montre la durée des appels lents, que la médiane cache.En savoir plus Mesurer la performance en pratiqueLes LLM : comment ils fonctionnent et comment en choisir un à 4,34 secondes.

Un agent attend généralement moins de 3 secondes, mais une ébauche sur 20 prend plus de 4,3 secondes. Le déploiement chat-strong a la même médiane, 2,77 s, et une queue plus longue : son p95 est de 4,84 secondes. Les nouvelles tentatives allongent la queue, pas la médiane. Un billet qui a nécessité deux tentatives après une erreur 429 attend un peu plus longtemps.

Un budget est une limite que le code vérifie, pas un espoir. Le projet définit un budget par billet dans usage.py : au plus 8 000 jetons et 0,02 $ US. T-80008, avec sa boucle d’outils, a utilisé 4 347 jetons et 0,00048 $ US. Le budget laisse donc de la place pour un billet plus long et une nouvelle tentative, mais pas pour une boucle incontrôlée. L’image présente côte à côte les trois limites d’un billet.

Le budget d'un billet, T-80008 sur chat-small, en trois parties. Jetons : 4 224 en entrée et 123 en sortie, soit 4 347 en 2 appels; budget de 8 000 par billet. Coût : 0,00048 $ US avec les prix de la table; budget de 0,02 $ US par billet. Temps : 0,99 s et 1,69 s, soit 2,68 s; limites de 30 s par appel, 20 s d'attente entre les tentatives, 60 s par boucle d'outils. Ensemble : 45 billets semblables par jour coûtent 0,022 $ US par jour, au plus 0,90 $ US avec le budget.
Le budget de requêtes d'un billet : ce que T-80008 a utilisé et la limite que le code vérifie pour chaque partie.
Description textuelle du diagramme

Le budget d’un billet, T-80008 sur chat-small, comprend trois parties.

  • Jetons : il a utilisé 4 224 jetons d’entrée et 123 de sortie, soit 4 347 en 2 appels. Le budget est de 8 000 par billet.
  • Coût : il a utilisé 0,00048 $ US avec les prix de la table. Le budget est de 0,02 $ US par billet.
  • Temps : il a utilisé 0,99 s et 1,69 s, soit 2,68 s au total. Les limites sont de 30 s par appel, 20 s d’attente entre les tentatives et 60 s pour la boucle d’outils.

Ensemble : 45 billets semblables par jour coûtent 0,022 $ US par jour. Avec le budget, une journée peut coûter au plus 0,90 $ US.

Le budget quotidien découle du budget par billet : 45 billets × 0,02 $ US = 0,90 $ US au maximum. Si le total quotidien dépasse un seuil d’alerte plus bas, quelqu’un devrait vérifier. Quelque chose a changé : une nouvelle requête, un nouveau modèle ou une boucle qui dure trop longtemps. over_budget() vérifie les lignes du journal d’un billet et indique quelle limite a été dépassée.

Choisissez les chiffres à partir du journal, pas d’une supposition. Fixez-les assez haut pour les vrais billets et assez bas pour arrêter un problème. Quand vous changez la requête ou le modèle, consultez de nouveau le journal et ajustez-les.

  1. Créez assistant/usage.py :

    assistant/usage.py
    """One log line per model call: tokens, time, cost, request ID. No ticket text, no keys."""
    import json
    import statistics
    from dataclasses import dataclass
    from datetime import datetime, timezone
    from pathlib import Path
    from .config import redact
    from .providers import Completion
    LOG_FILE = Path(__file__).resolve().parent.parent / "logs" / "usage.jsonl"
    # US dollars per million tokens (input, output). Output includes reasoning tokens.
    # Azure AI Foundry, Global Standard deployments, prices checked on 2026-10-08. Check the current price page.
    PRICES_CHECKED = "2026-10-08"
    PRICES = {
    "chat-small": (0.10, 0.50), # gpt-6-luna 2026-09-22
    "chat-strong": (2.00, 10.00), # gpt-6.1-sol 2026-09-29
    "gemma3:4b": (0.0, 0.0), # local model through Ollama: no price per token (your own computer)
    }
    def cost_usd(model: str, input_tokens: int, output_tokens: int) -> float | None:
    """None when the model is not in the price table: unknown cost is not zero cost."""
    if model not in PRICES:
    return None
    price_in, price_out = PRICES[model]
    return round((input_tokens * price_in + output_tokens * price_out) / 1_000_000, 8)
    @dataclass
    class Budget:
    max_tokens_per_ticket: int = 8000
    max_cost_per_ticket_usd: float = 0.02
    class UsageLog:
    def __init__(self, path: Path | None = None):
    self.path = path or LOG_FILE # read at call time, so that the tests can point it elsewhere
    self.path.parent.mkdir(parents=True, exist_ok=True)
    def record(self, completion: Completion, model: str, ticket_id: str, prompt: str, status: str = "ok",
    note: str = "") -> dict:
    entry = {
    "at": datetime.now(timezone.utc).isoformat(timespec="seconds"),
    "request_id": completion.request_id,
    "model": model,
    "prompt": prompt,
    "ticket_id": ticket_id,
    "input_tokens": completion.input_tokens,
    "output_tokens": completion.output_tokens,
    "reasoning_tokens": completion.reasoning_tokens,
    "latency_s": completion.latency_s,
    "cost_usd": cost_usd(model, completion.input_tokens, completion.output_tokens),
    "prices_checked": PRICES_CHECKED,
    "finish_reason": completion.finish_reason,
    "status": status,
    "note": redact(note),
    }
    with self.path.open("a", encoding="utf-8") as f:
    f.write(json.dumps(entry) + "\n")
    return entry
    def entries(self) -> list[dict]:
    if not self.path.exists():
    return []
    return [json.loads(line) for line in self.path.read_text(encoding="utf-8").splitlines()]
    def summarise(entries: list[dict]) -> dict:
    if not entries:
    return {"calls": 0}
    latencies = sorted(e["latency_s"] for e in entries)
    costs = [e["cost_usd"] for e in entries]
    return {
    "calls": len(entries),
    "input_tokens": sum(e["input_tokens"] for e in entries),
    "output_tokens": sum(e["output_tokens"] for e in entries),
    "latency_median_s": round(statistics.median(latencies), 2),
    "latency_p95_s": round(latencies[min(len(latencies) - 1, int(0.95 * len(latencies)))], 2),
    "cost_usd": None if None in costs else round(sum(costs), 6),
    }
    def over_budget(ticket_entries: list[dict], budget: Budget) -> list[str]:
    tokens = sum(e["input_tokens"] + e["output_tokens"] for e in ticket_entries)
    cost = sum(e["cost_usd"] or 0 for e in ticket_entries)
    reasons = []
    if tokens > budget.max_tokens_per_ticket:
    reasons.append(f"{tokens} tokens > {budget.max_tokens_per_ticket}")
    if cost > budget.max_cost_per_ticket_usd:
    reasons.append(f"US${cost:.4f} > US${budget.max_cost_per_ticket_usd}")
    return reasons
  2. Ouvrez assistant/analyse.py. Ajoutez l’importation from .usage import UsageLog sous celle de .tools. Ajoutez ensuite le paramètre log après retry, et consignez chaque appel dans complete :

    assistant/analyse.py (changed lines)
    retry: RetryPolicy = RetryPolicy(), log: UsageLog | None = None,
    reasoning_effort: str = "") -> Outcome:
    def complete(req: dict) -> Completion:
    completion = call_with_retry(lambda: provider.complete(req), retry)
    if log:
    log.record(completion, model, ticket.ticket_id, prompt)
    return completion
  3. Ouvrez assistant/__main__.py. Ajoutez cette importation sous celle de .store :

    from .usage import UsageLog, cost_usd, summarise
  4. Dans le même fichier, ajoutez la commande usage après l’analyseur eval et ses options, juste avant args = parser.parse_args(argv) :

    commands.add_parser("usage", help="summarise logs/usage.jsonl")
  5. Traitez-la en premier, juste après args = parser.parse_args(argv) :

    if args.command == "usage":
    print(json.dumps(summarise(UsageLog().entries()), indent=2))
    return 0
  6. Fournissez le journal à chaque analyse. Remplacez la ligne options = … :

    log = UsageLog()
    options = dict(model=model, prompt=args.prompt, structured=not args.no_schema, log=log,
    reasoning_effort=settings.reasoning_effort)
  7. Affichez le coût sur la dernière ligne de analyse. Remplacez la ligne print(f"{outcome.status} …") :

    assistant/__main__.py (analyse branch)
    cost = cost_usd(model, tokens_in, tokens_out)
    print(f"{outcome.status} | {model} | prompt {args.prompt} | {len(outcome.completions)} call(s) | "
    f"{tokens_in} tokens in, {tokens_out} out | cost "
    f"{'unknown' if cost is None else f'US${cost:.5f}'}")

    Ajoutez la ligne python -m assistant usage totals from logs/usage.jsonl à la chaîne de documentation au début du fichier.

  8. Ouvrez tests/conftest.py. Un test qui exécute analyse écrirait maintenant dans votre vrai journal. Ajoutez cette fixture (fonction de préparation des tests) après use_the_mock :

    tests/conftest.py (new fixture)
    @pytest.fixture(autouse=True)
    def keep_the_usage_log_clean(tmp_path, monkeypatch):
    """A test run must not add lines to your real logs/usage.jsonl."""
    monkeypatch.setattr("assistant.usage.LOG_FILE", tmp_path / "usage.jsonl")
  9. Copiez tests/test_usage.py depuis l’instantané end-of-m06. Puis exécutez les tests :

    Fenêtre de terminal
    python -m pytest
  10. Analysez le billet de démonstration, puis lisez les totaux :

    Fenêtre de terminal
    python -m assistant analyse T-80008
    python -m assistant usage

L’étape 9 affiche 78 passed, 1 deselected (78 tests réussis, 1 non sélectionné). À l’étape 10, la dernière ligne de analyse se termine maintenant par le coût :

valid | chat-small | prompt v2 | 2 call(s) | 4224 tokens in, 123 out | cost US$0.00048

Le journal contient deux nouvelles lignes, une par appel au modèle. Ouvrez logs/usage.jsonl dans votre éditeur. La première ligne correspond à l’appel qui a demandé la recherche de commande :

{"at": "2026-10-09T03:20:24+00:00", "request_id": "chatcmpl-EWuY0oVX3oudORY8oq8dTswM4pXCu", "model": "chat-small", "prompt": "v2", "ticket_id": "T-80008", "input_tokens": 2043, "output_tokens": 21, "reasoning_tokens": 0, "latency_s": 0.99, "cost_usd": 0.0002148, "prices_checked": "2026-10-08", "finish_reason": "tool_calls", "status": "ok", "note": ""}

L’heure at (à) correspond au moment de l’exécution; le reste vient de l’enregistrement. usage additionne les deux lignes :

{
"calls": 2,
"input_tokens": 4224,
"output_tokens": 123,
"latency_median_s": 1.34,
"latency_p95_s": 1.69,
"cost_usd": 0.000484
}

Avec la simulation, latency_s est le temps enregistré, pas le temps sur votre ordinateur. La relecture est instantanée. Cela garde les mêmes chiffres pour chaque personne qui suit le cours. Avec un fournisseur réel, le journal contient vos temps réels.

Masquage : ce qu’une ligne de journal ne doit pas contenir

Section intitulée « Masquage : ce qu’une ligne de journal ne doit pas contenir »

Les journaux durent plus longtemps que les requêtes, et plus de personnes les lisent. Le champ note passe donc par redact(), la fonction vue dans La frontière de l’application. Essayez-la avec une clé et une adresse courriel inventées. Créez try_log.py dans le dossier du projet :

try_log.py
"""Record one usage line with a note that holds a made-up key and email address. Print what was saved."""
import json
from assistant.context import build_request
from assistant.data import load_ticket
from assistant.providers import MockProvider
from assistant.schema import response_format
from assistant.usage import UsageLog
request = build_request(load_ticket("T-80008"), "chat-small", "v2", response_format())
completion = MockProvider().complete(request)
note = "Proxy said: Authorization: Bearer sk-proj-4f9Qx2LmTz81 rejected; reply to nadia.price@example.com"
entry = UsageLog().record(completion, "chat-small", "T-80008", "v2", note=note)
print(json.dumps(entry, indent=2))

Exécutez python try_log.py. Voici la fin de la sortie :

"finish_reason": "stop",
"status": "ok",
"note": "Proxy said: Authorization: [REDACTED] rejected; reply to [EMAIL REDACTED]"
}

La clé et l’adresse courriel n’atteignent jamais le fichier. La ligne se trouve aussi dans logs/usage.jsonl; supprimez donc le dossier logs et try_log.py ensuite. Le masquage est une protection fondée sur des motifs, et ces motifs ne détectent pas tout. La première protection est la décision prise plus haut : le texte du billet et la réponse ne vont jamais au journal.

Supprimez le dossier logs (rm -r logs sur macOS ou Linux, Remove-Item -Recurse logs dans PowerShell). Exécutez ensuite le jeu d’évaluation sur chat-small et lisez les totaux :

Fenêtre de terminal
python -m assistant eval --prompt v2
python -m assistant usage

Prédisez le coût de 69 billets à partir du graphique ci-dessus. Puis supprimez de nouveau logs et faites la même chose avec --model chat-strong.

Afficher les totaux

chat-small, requête v2 :

{
"calls": 69,
"input_tokens": 131733,
"output_tokens": 22038,
"latency_median_s": 2.77,
"latency_p95_s": 4.34,
"cost_usd": 0.024192
}

chat-strong, requête v2 :

{
"calls": 69,
"input_tokens": 131733,
"output_tokens": 8343,
"latency_median_s": 2.77,
"latency_p95_s": 4.84,
"cost_usd": 0.346896
}

0,024 $ US pour 69 billets équivaut à 0,35 $ US par 1 000 billets, comme dans le graphique. chat-strong coûte 5,03 $ US par 1 000 billets. Les deux exécutions ont envoyé le même nombre de jetons d’entrée. chat-small a produit plus du double de jetons de sortie, surtout du raisonnement caché. Mais son prix de sortie est 20 fois plus bas.

Erreur courante

« Mettez simplement 0 pour les modèles dont nous n'avons pas le prix »

Symptôme : TermeTomásL’ingénieur en IA de Larkfield. Il propose souvent un raccourci rapide, et la leçon montre pourquoi il échoue. essaie un nouveau modèle avec un fournisseur réel. Le nom du modèle n’est pas dans PRICES, et la ligne de analyse indique cost unknown (coût inconnu). Il suggère une valeur par défaut de 0, « pour que les rapports s’additionnent ». Avec ce changement, le rapport quotidien indiquerait 0 $ US pour chaque appel au nouveau modèle. La vérification du budget ne l’arrêterait jamais.

Cause : un prix manquant ne signifie pas un appel gratuit. Zéro est un vrai nombre auquel le budget fait confiance. Le projet renvoie plutôt None, et summarise refuse d’additionner un total incomplet :

print(cost_usd("chat-small", 4224, 123))
print(cost_usd("my-new-model", 4224, 123))
print(summarise([{"input_tokens": 4224, "output_tokens": 123, "latency_s": 2.68,
"cost_usd": cost_usd("my-new-model", 4224, 123)}]))

La sortie :

0.0004839
None
{'calls': 1, 'input_tokens': 4224, 'output_tokens': 123, 'latency_median_s': 2.68, 'latency_p95_s': 2.68, 'cost_usd': None}

Correction : ajoutez le modèle à PRICES avec le prix indiqué sur la page du fournisseur, puis mettez à jour PRICES_CHECKED. Le modèle local figure volontairement dans la table avec 0 : TermeOllamaUn programme gratuit qui télécharge des modèles ouverts et les fait fonctionner sur votre ordinateur. Votre code l’appelle à une adresse locale, comme une API hébergée.En savoir plus Modèle hébergé ou autohébergéLes LLM : comment ils fonctionnent et comment en choisir un sur votre ordinateur n’a pas de prix par jeton. Il n’est pas gratuit non plus : votre ordinateur fait le travail. Sa médiane était de 3,61 s par billet, contre 2,77 s sur chat-small.

Ce que cela signifie pour un vrai service d’IA : une valeur inconnue doit rester visiblement inconnue. Un rapport qui remplit les valeurs manquantes avec des zéros semble complet, mais il est faux.

Vérifiez votre compréhension

chat-small produit 313 jetons de sortie par billet (médiane), et chat-strong, 104. Pourtant, chat-small coûte beaucoup moins cher. Expliquez pourquoi avec la table de prix.

Afficher la réponse

Le prix de sortie de chat-small est de 0,50 $ US par million de jetons. Celui de chat-strong est de 10 $ US, soit 20 fois plus. Son prix d’entrée est de 0,10 $ US contre 2 $ US. Même avec trois fois plus de jetons de sortie, surtout du raisonnement caché, chat-small coûte environ 14 fois moins par billet. Considérez ensemble les jetons et les prix; le nombre de jetons seul ne donne pas le coût.

Vérifiez votre compréhension

Pourquoi le journal d'utilisation garde-t-il les identifiants du billet et de la requête, mais pas le texte ni la réponse?

Afficher la réponse

Les identifiants suffisent pour retrouver le billet dans le système de soutien et interroger le fournisseur sur un appel. Le texte et la réponse contiennent des données personnelles : noms, adresses, parfois numéros de carte. Un fichier journal est copié, envoyé à des outils de surveillance et lu par plus de personnes que le système de billets. Garder seulement les chiffres et les identifiants rend le journal utile et sûr. redact() offre une deuxième protection pour la note en texte libre.

Vérifiez votre compréhension

Prédisez : la boucle d'outils d'un billet fonctionne mal et fait 4 appels d'environ 2 500 jetons d'entrée chacun. over_budget le signale-t-il avec le Budget par défaut? Que dit-il?

Afficher la réponse

Oui. Quatre appels envoient environ 10 000 jetons d’entrée, plus des jetons de sortie. Cela dépasse le budget de 8 000 jetons. over_budget renvoie une raison, comme "10200 tokens > 8000". La vérification du coût réussit : sur chat-small, cela coûte environ 0,001 $ US, bien en dessous de 0,02 $ US. Sur un modèle peu coûteux, le budget de jetons détecte une boucle incontrôlée bien avant le budget de coût.

Récapitulatif

  • Un journal d’utilisation contient une ligne par appel au modèle. Il contient l’identifiant de requête, le modèle, la version de requête, l’identifiant du billet, les jetons, la latence, le coût et la date des prix. Il ne contient ni texte du billet ni réponse, et redact() nettoie la note.
  • Coût = jetons × prix d’une table de prix datée. Un prix inconnu vaut None, jamais 0.
  • Chiffres réels enregistrés : chat-small coûte 0,35 $ US par 1 000 billets avec la requête v2. chat-strong coûte 5,03 $ US, environ 14 fois plus. T-80008 avec sa boucle d’outils : 0,00048 $ US.
  • Regardez la médiane et le p95 : sur chat-small, 2,77 s et 4,34 s.
  • Un budget est une limite que le code vérifie : 8 000 jetons et 0,02 $ US par billet. Pour 45 billets par jour, cela donne au plus 0,90 $ US.

Ensuite, vous testerez chaque scénario d’échec sans réseau et empaquetterez l’assistant pour quelqu’un d’autre.

Leçon suivante : Tester et empaqueter