Aller au contenu
English

Gérer l'état de la conversation

  • Leçon 14
  • 50 min
  • Module 5 : Conversations et diffusion en continu

Ce que vous saurez faire

Permettez à un agent de poser des questions de suivi à l’assistant sur un billet de soutien, comme « raccourcis la réponse ». Vous séparez les conversations des agents et décidez quelle quantité d’historique chaque requête peut contenir.

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

Ce que vous devez savoir

  • Pourquoi un modèle ne garde rien entre les requêtes et pourquoi la fenêtre de contexte grandit avec l’historique. Voir Contexte et mémoire dans le cours sur les modèles de langage.
  • Les messages et les rôles (system (système), user (utilisateur), assistant (assistant)), vus dans Les éléments d’une requête.
  • Les deux barrières (schéma, puis règles d’affaires), vues dans Valider le sens.

Ce qu’il vous faut sur votre ordinateur

  • Le projet à la fin du module 4. python -m pytest affiche 59 passed, 1 deselected (59 tests réussis, 1 désélectionné). L’environnement virtuel est actif.
  • L’instantané end-of-m05 du dépôt des exercices. Vous en copiez deux fichiers.
  • Si votre projet ne fonctionne plus, copiez plutôt l’instantané end-of-m04, en suivant les étapes de Réinitialiser le projet.

Les modèles et les API changent vite. Les réponses, les nombres de jetons et les durées de ce module sont un instantané. La méthode, elle, reste.

TermeCamilleUn membre de l’équipe du service d’assistance de Larkfield. Camille utilise les brouillons de l’assistant pour répondre aux clients. et TermeGraceLa responsable du service d’assistance de Larkfield. Elle écrit la politique de support et décide ce qu’est une bonne réponse à un client. partagent un bureau au service de soutien de 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. Pour économiser du papier, elles partagent aussi un bloc-notes. Camille reçoit un appel au sujet d’un hamac et prend des notes. Grace reçoit ensuite un appel au sujet d’une bouilloire et écrit ses notes en dessous. Le client de Camille rappelle : « Pouvez-vous raccourcir ça? » Camille regarde le bloc-notes. La dernière note parle d’une bouilloire.

Personne ne gérerait un service ainsi. Chaque appel a sa propre page. Pourtant, cette erreur est facile à faire dans le code, où elle est beaucoup plus difficile à voir.

Dans le cours sur les modèles de langage, Contexte et mémoire montrait qu’un modèle ne garde rien entre deux requêtes. Pour poursuivre une conversation, l’application garde les messages précédents et les renvoie avec chaque nouvelle requête. Cette leçon porte sur le code qui les garde.

Camille l’a essayé avec le billet T-80005. Sophie écrit en français que son hamac (commande LK-266415) n’est pas arrivé. L’assistant a rédigé une réponse. Camille a ensuite posé deux questions de suivi. Chaque requête contenait toute la conversation jusqu’à ce moment :

Billet T-80005 et deux questions de suivi, chat-small (gpt-6-luna sur Azure), enregistrements réels. Chaque requête renvoie le message système, le billet et tous les échanges précédents.
Description textuelle du graphique

Trois barres. La requête 1 contient 2 messages, le message système et le billet : 1652 jetons d’entrée. La requête 2 ajoute la première réponse et « Raccourcis la réponse : deux phrases. » : 1749 jetons. La requête 3 ajoute la réponse raccourcie et « Écris maintenant la même réponse en anglais pour mon collègue » : 1851 jetons.

Deux choses ressortent. D’abord, le message système (les instructions et la politique de Grace) représente la majeure partie de chaque requête. Ensuite, chaque échange ajoute environ 100 jetons, payés de nouveau à chaque requête suivante. Le fournisseur a déclaré la plupart de ces jetons comme TermeJetons mis en cacheDes jetons d’entrée que le fournisseur a gardés d’une requête précédente qui commençait par le même texte, par exemple les mêmes instructions et la même politique. Beaucoup de fournisseurs les facturent moins cher.En savoir plus Les éléments d'une requêteCréer des applications fiables avec les API de LLM (1 746 sur 1 851 à la requête 3). Le début de chaque requête était identique au précédent. Beaucoup de fournisseurs facturent moins cher les jetons d’entrée mis en cache : consultez la page de prix de votre fournisseur pour voir ce que cela change sur la facture.

L’application est donc responsable de la conversation. Cela soulève deux questions : à qui appartient cette conversation, et quelle quantité faut-il envoyer?

Le problème : une seule liste pour tout le service de soutien

Section intitulée « Le problème : une seule liste pour tout le service de soutien »

La première version de la fonction de suivi d’TermeAmiraLa développeuse de Larkfield. Elle écrit le code des outils du service d’assistance dans les cours. gardait l’historique dans une seule liste Python. Elle fonctionnait dans ses tests, car Amira était la seule utilisatrice. Voici l’idée, réduite à un script que vous pouvez exécuter avec le simulateur :

shared_history.py
import json
from assistant.context import MAX_TOKENS, build_request
from assistant.data import load_tickets
from assistant.providers import MockProvider
tickets = load_tickets()
provider = MockProvider()
SYSTEM = build_request(tickets["T-80005"], "chat-small", "v2")["messages"][0]["content"]
history = [] # one list for the whole help desk: the bug
def ask(agent, text):
history.append({"role": "user", "content": text})
request = {"model": "chat-small", "messages": [{"role": "system", "content": SYSTEM}] + history,
"max_completion_tokens": MAX_TOKENS}
completion = provider.complete(request)
history.append({"role": "assistant", "content": completion.text})
answer = json.loads(completion.text)
print(f"{agent}: {len(request['messages'])} messages sent, {completion.input_tokens} tokens in")
print(f" {answer['language']} | {answer['team']} | {answer['reply']}")
def opening(ticket_id):
"""The first user message for a ticket: the ticket text between <ticket> tags."""
return build_request(tickets[ticket_id], "chat-small", "v2")["messages"][1]["content"]
ask("camille", opening("T-80005")) # the French ticket: a hammock that has not arrived
ask("grace", opening("T-64704")) # an English ticket: a kettle with a burning smell
ask("camille", "Make the reply shorter: two sentences.")

La sortie. Chaque réponse est une réponse réelle de 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 (gpt-6-luna sur Azure), rejouée par le simulateur :

camille: 2 messages sent, 1652 tokens in
fr | delivery | Je suis désolé que votre commande ne soit pas encore arrivée. Notre équipe de livraison pourra vérifier où elle se trouve.
grace: 4 messages sent, 1808 tokens in
en | warranty | I’m sorry this happened, and thank you for stopping use of the kettle. A member of our team will contact you about the next steps.
camille: 6 messages sent, 1909 tokens in
en | warranty | Please stop using the kettle. A member of our team will contact you about next steps.

Camille a demandé de raccourcir la réponse à Sophie au sujet du hamac. Elle a reçu une courte réponse en anglais sur une bouilloire, destinée à l’équipe des garanties. Le modèle a fait ce que les messages demandaient. Le brouillon le plus récent concernait la bouilloire. Il a donc raccourci celui-là.

La mauvaise réponse est la partie visible. La partie cachée est pire. La requête de Grace contenait 4 messages. Elle incluait le billet de Sophie, son nom et son numéro de commande dans une conversation sur un autre client. Avec 45 billets par jour et plusieurs agents, chaque requête contiendrait des morceaux de billets d’autres clients. Aucune erreur n’apparaît, et chaque réponse est un objet JSON valide. Les règles d’affaires vérifient une réponse par rapport au billet fourni par le code. Rien ici n’indique au code que la conversation est la mauvaise. Seul un agent attentif le remarquerait.

La solution suit la règle du bloc-notes : une page par appel. Dans le code, l’état de la conversation est la liste des messages qui appartiennent à une conversation. Vous le stockez sous une clé qui indique son propriétaire. Pour Larkfield, ce propriétaire est une paire : l’agent et le billet. Les suivis de Camille sur T-80005 ne se mélangent jamais au travail de Grace sur T-64704. Les requêtes de deux agents travaillant sur le même billet ne se mélangent pas non plus.

Deux panneaux. À gauche, une seule liste, le bogue : le billet T-80005 de Camille et sa réponse française, puis le billet T-64704 de Grace et sa réponse anglaise, puis la demande de Camille « Raccourcis la réponse : deux phrases. » Camille reçoit une réponse anglaise sur la garantie : « Veuillez cesser d'utiliser la bouilloire. » À droite, des listes séparées, la solution : la liste de camille et T-80005 contient le billet, la réponse et le suivi. Camille reçoit la réponse française sur la livraison « Désolé pour le retard. ... ». Une liste distincte pour grace et T-64704 contient le billet sur la bouilloire et sa réponse.
Les trois mêmes étapes avec une liste partagée (le bogue) et une liste par agent et par billet (la solution). Toutes les réponses sont des réponses réelles de chat-small.
Description textuelle du diagramme

Panneau de gauche, une liste pour tout le service de soutien. Dans l’ordre : 1, le billet T-80005 de Camille (hamac, français). 2, sa réponse : français, livraison. 3, le billet T-64704 de Grace (bouilloire, anglais). 4, sa réponse : anglais, garantie. 5, Camille demande « Raccourcis la réponse : deux phrases. » Camille reçoit une réponse anglaise sur la garantie : « Veuillez cesser d’utiliser la bouilloire. »

Panneau de droite, une liste par agent et par billet. La liste pour (camille, T-80005) contient le billet, la réponse et le suivi. Camille reçoit la réponse française sur la livraison « Désolé pour le retard. … » Une liste distincte pour (grace, T-64704) contient le billet sur la bouilloire et sa réponse.

Le ConversationStore du projet fait cela avec un dictionnaire. Sa clé est (agent_id, ticket_id), et chaque valeur est une Conversation. Dans une véritable application de soutien, ce stockage serait une table de base de données avec la même clé. L’historique survivrait ainsi à un redémarrage. L’idée ne change pas.

L’historique grandit à chaque échange. Sans limite, une longue conversation devient lente et coûteuse, puis finit par dépasser la TermeFenêtre de contexteLe nombre maximal de jetons qu’un modèle peut lire et écrire en un appel. Les instructions, la conversation et la réponse doivent toutes y tenir.En savoir plus Contexte et mémoireLes LLM : comment ils fonctionnent et comment en choisir un. L’application fixe donc un budget de jetons : le nombre maximal de jetons d’entrée qu’elle autorise pour une requête. Vous choisissez ce budget selon le coût et la vitesse, bien en dessous de la fenêtre.

Quand la conversation dépasse le budget, l’application doit choisir quoi envoyer. Il existe trois méthodes courantes :

Méthode Ce qu’elle envoie Utile pour Risque
Couper les échanges les plus récents qui entrent dans le budget; les plus anciens sont exclus de courts suivis sur le dernier brouillon l’échange le plus ancien peut être celui qui compte
Sélectionner des échanges choisis : toujours le billet, puis les plus récents une conversation sur un seul document vous devez savoir quels échanges comptent
Résumer un court résumé des anciens échanges, produit par un appel au modèle, puis les échanges les plus récents de longues conversations un appel supplémentaire; le résumé est une sortie du modèle et peut omettre ou inventer des faits

Le projet utilise la règle la plus simple : couper. Conversation.messages() envoie toujours le message système et l’échange le plus récent. Ensuite, la méthode remonte les anciens échanges et les ajoute tant que le total respecte le budget. Elle estime le nombre de jetons à environ 4 caractères par jeton. Le nombre exact n’est connu qu’après la réponse du fournisseur.

Voici cette règle appliquée à la conversation réelle de Camille, avant la troisième requête, avec un budget estimé de 1 900 jetons :

Six cases de haut en bas. Le message système, les instructions et la politique, 1 724 jetons estimés, toujours envoyé. Échange 1, utilisateur, le billet, 45, coupé. Échange 2, assistant, la première réponse, 86, coupé. Échange 3, utilisateur, Raccourcis la réponse, 9, envoyé. Échange 4, assistant, la réponse raccourcie, 77, envoyé. Échange 5, utilisateur, Maintenant en anglais, 19, le plus récent, toujours envoyé. Une note : budget de 1 900. Remontez depuis l'échange le plus récent : 19, 77 et 9 entrent avec les 1 724 du message système. Ajouter 86 donnerait 1 915 : arrêt. Envoyés : 1 829. Coupés : échanges 1 et 2.
La règle de coupe de assistant/history.py sur la conversation de Camille, avec un budget estimé de 1 900 jetons. Les cases en pointillé sont coupées. Les nombres de jetons sont les estimations du projet (caractères ÷ 4).
Description textuelle du diagramme

Le message système, les instructions et la politique : 1 724 jetons estimés, toujours envoyé. Puis les échanges, du plus ancien au plus récent. Échange 1, utilisateur, le billet : 45 jetons, coupé. Échange 2, assistant, la première réponse : 86, coupé. Échange 3, utilisateur, « Raccourcis la réponse » : 9, envoyé. Échange 4, assistant, la réponse raccourcie : 77, envoyé. Échange 5, utilisateur, « Maintenant en anglais » : 19, l’échange le plus récent, toujours envoyé.

Le parcours remonte depuis l’échange le plus récent. 1 724 + 19 + 77 + 9 = 1 829 entre dans 1 900. Ajouter l’échange 2 donnerait 1 915, ce qui dépasse le budget. Le parcours s’arrête donc. Envoyés : 1 829 jetons estimés. Coupés : échanges 1 et 2.

Regardez ce que la coupe a retiré : le billet. Le modèle recevrait « Raccourcis la réponse », une courte réponse française et « Maintenant en anglais », sans le message de Sophie. Pour un service de soutien, l’échange le plus ancien est souvent le plus important. Le projet garde cette règle simple pour vous montrer cet échec. Dans l’exercice du module, à la fin du module, vous le détectez et arrêtez plutôt le traitement avec un message clair.

L’estimation est aussi approximative. Elle annonçait 1 864 jetons pour la deuxième requête de Camille, et le fournisseur en a compté 1 749. Gardez une marge et utilisez les nombres de usage (utilisation) du fournisseur quand vous avez besoin de comptes exacts.

  1. Créez le fichier assistant/history.py :

    assistant/history.py
    """Conversation state: the model remembers nothing, so the application keeps and resends the history.
    An agent can ask follow-up questions about a ticket ("make the reply shorter"). Each (agent, ticket)
    pair has its own conversation, and only the newest turns that fit in the token budget are sent.
    """
    from dataclasses import dataclass, field
    def estimate_tokens(text: str) -> int:
    """A rough count: about 4 characters per token for English. Use the provider's usage for exact numbers."""
    return max(1, len(text) // 4)
    @dataclass
    class Conversation:
    system: str
    budget_tokens: int = 3000
    turns: list[dict] = field(default_factory=list)
    def add(self, role: str, content: str) -> None:
    self.turns.append({"role": role, "content": content})
    def messages(self) -> list[dict]:
    """The system message, then the newest turns that fit in the budget (the oldest are cut first)."""
    used = estimate_tokens(self.system)
    kept = []
    for turn in reversed(self.turns):
    cost = estimate_tokens(turn["content"])
    if kept and used + cost > self.budget_tokens: # the newest turn is always kept
    break
    kept.insert(0, turn)
    used += cost
    return [{"role": "system", "content": self.system}] + kept
    def dropped(self) -> int:
    return len(self.turns) - (len(self.messages()) - 1)
    class ConversationStore:
    """One conversation per agent and ticket. Never one shared list for everybody."""
    def __init__(self, system: str, budget_tokens: int = 3000):
    self.system = system
    self.budget_tokens = budget_tokens
    self._conversations: dict[tuple[str, str], Conversation] = {}
    def get(self, agent_id: str, ticket_id: str) -> Conversation:
    key = (agent_id, ticket_id)
    if key not in self._conversations:
    self._conversations[key] = Conversation(self.system, self.budget_tokens)
    return self._conversations[key]
  2. Copiez les conversations enregistrées de l’instantané end-of-m05 dans votre projet. Le simulateur en a besoin. Dans le dossier du projet, sur macOS ou Linux :

    Fenêtre de terminal
    cp ../nextia-learning-labs/C08/snapshots/end-of-m05/recordings/conversation.jsonl recordings/

    Sur Windows, dans PowerShell :

    Fenêtre de terminal
    Copy-Item ..\nextia-learning-labs\C08\snapshots\end-of-m05\recordings\conversation.jsonl recordings\

    Modifiez le chemin si votre copie du dépôt des exercices se trouve dans un autre dossier.

  3. Créez shared_history.py à partir de la section sur le problème. Exécutez-le avec python shared_history.py. Camille reçoit la réponse sur la bouilloire.

  4. Créez separate_history.py. C’est le même script, mais avec le stockage des conversations plutôt que la liste :

    separate_history.py
    import json
    from assistant.context import MAX_TOKENS, build_request
    from assistant.data import load_tickets
    from assistant.history import ConversationStore
    from assistant.providers import MockProvider
    tickets = load_tickets()
    provider = MockProvider()
    store = ConversationStore(build_request(tickets["T-80005"], "chat-small", "v2")["messages"][0]["content"])
    def ask(agent, ticket_id, text):
    conversation = store.get(agent, ticket_id) # one conversation per agent and ticket
    conversation.add("user", text)
    request = {"model": "chat-small", "messages": conversation.messages(), "max_completion_tokens": MAX_TOKENS}
    completion = provider.complete(request)
    conversation.add("assistant", completion.text)
    answer = json.loads(completion.text)
    print(f"{agent} on {ticket_id}: {len(request['messages'])} messages sent, {completion.input_tokens} tokens in")
    print(f" {answer['language']} | {answer['team']} | {answer['reply']}")
    def opening(ticket_id):
    """The first user message for a ticket: the ticket text between <ticket> tags."""
    return build_request(tickets[ticket_id], "chat-small", "v2")["messages"][1]["content"]
    ask("camille", "T-80005", opening("T-80005"))
    ask("grace", "T-64704", opening("T-64704"))
    ask("camille", "T-80005", "Make the reply shorter: two sentences.")
    ask("camille", "T-80005", "Now write the same reply in English for my colleague, so that I can check it.")
  5. Exécutez-le avec python separate_history.py.

  6. Copiez tests/test_history.py de l’instantané end-of-m05 dans votre dossier tests, comme à l’étape 2. Exécutez ensuite toute la suite avec python -m pytest.

La sortie de l’étape 5 (réponses réelles de chat-small) :

camille on T-80005: 2 messages sent, 1652 tokens in
fr | delivery | Je suis désolé que votre commande ne soit pas encore arrivée. Notre équipe de livraison pourra vérifier où elle se trouve.
grace on T-64704: 2 messages sent, 1672 tokens in
en | warranty | I'm sorry this happened, and I'm glad you've stopped using the kettle. Please continue not to use it. A member of our team will contact you about the next steps and your safety concern.
camille on T-80005: 4 messages sent, 1749 tokens in
fr | delivery | Désolé pour le retard. Notre équipe de livraison vérifiera où se trouve votre commande.
camille on T-80005: 6 messages sent, 1851 tokens in
fr | delivery | Désolé pour le retard. Notre équipe de livraison vérifiera où se trouve votre commande.

La requête de Grace contient maintenant 2 messages, plutôt que 4, et rien du billet de Sophie. Camille reçoit une réponse française de deux phrases sur son hamac. L’étape 6 affiche 62 passed, 1 deselected (62 tests réussis, 1 désélectionné). Les trois nouveaux tests vérifient la règle de coupe, la séparation des conversations et la conversation enregistrée.

Regardez toutefois la dernière ligne. Camille a demandé une réponse en anglais et a reçu la même réponse française. Le cas d’échec ci-dessous explique pourquoi.

Gardez une conversation seulement quand les gens posent vraiment des questions de suivi. La tâche principale de l’assistant, l’analyse d’un billet, n’a pas besoin d’historique : chaque billet correspond à une requête. La fonction de suivi est un ajout, et chaque suivi coûte une requête complète. Dans la conversation de Camille, chaque suivi envoyait environ 1 750 à 1 850 jetons d’entrée pour modifier deux phrases.

Choisissez la clé avec soin. (agent, ticket) sépare à la fois les personnes et les billets. Une clé contenant seulement le billet laisserait se mélanger les requêtes de deux agents. Une clé contenant seulement l’agent ramènerait le bogue du bloc-notes dès qu’un agent ouvre un deuxième billet.

Décidez aussi quand une conversation se termine. Un billet fermé n’a plus besoin de son historique. Supprimez-le pour économiser de l’espace de stockage et parce qu’il contient les propos d’un client.

Utilisez la conversation de Camille avant la troisième requête, comme dans le schéma. Prédisez combien d’échanges messages() envoie et combien elle coupe, avec un budget de 3 000, puis de 1 800. Vérifiez ensuite votre réponse avec un court script. Construisez la conversation comme le fait tests/test_history.py, et rejouez les deux requêtes enregistrées avec le simulateur. Pour chaque budget, définissez conversation.budget_tokens et affichez len(conversation.messages()) - 1 et conversation.dropped().

Afficher la réponse
budget 3000: 5 turns sent, 0 cut, 1960 tokens (estimated)
budget 1800: 1 turns sent, 4 cut, 1743 tokens (estimated)

Avec 3 000, tout entre : 1 724 pour le message système, plus 236 pour les cinq échanges. Avec 1 800, le message système et l’échange le plus récent totalisent déjà 1 743. L’échange précédent, la réponse de 77 jetons, porterait le total à 1 820. Seul l’échange le plus récent est donc envoyé : « Écris maintenant la même réponse en anglais ». Le modèle ne saurait même pas de quelle réponse Camille parle. Un budget trop petit rend la conversation inutile, pas seulement moins chère.

Erreur courante

« Le modèle suivra le dernier message de l'agent »

Symptôme : Camille demande : « Écris maintenant la même réponse en anglais pour mon collègue, pour que je puisse la vérifier. » La réponse enregistrée (chat-small) est le même JSON français qu’avant : "language":"fr" (langue : français), et la réponse « Désolé pour le retard. Notre équipe de livraison vérifiera où se trouve votre commande. » TermeTomásL’ingénieur en IA de Larkfield. Il propose souvent un raccourci rapide, et la leçon montre pourquoi il échoue. dit que le modèle a « ignoré » Camille.

Cause : le suivi a été ajouté à une conversation dont le message système est la requête (prompt) d’analyse de billet. Ce message définit le contrat : un objet JSON et « un brouillon de réponse au client dans la langue du billet ». Le message système a priorité sur la requête d’un utilisateur, par conception. Pour la même raison, un client ne peut pas changer les règles en écrivant « ignore tes instructions » dans un billet. Ici, le modèle a respecté le contrat. C’était correct pour une réponse au client, mais incorrect pour la tâche de Camille.

Solution : concevez une requête adaptée à chaque tâche de suivi. Une traduction pour un collègue est une tâche différente. Elle a besoin de son propre message système, comme « traduis ce brouillon en anglais pour un agent; ce n’est pas pour le client ». Elle a aussi besoin de son propre contrat de sortie et de ses propres vérifications. La conversation d’analyse du billet garde ses règles.

Ce que cela signifie pour un véritable service d’IA : le message système fait partie de l’état de la conversation. Quand la tâche change, modifiez les instructions dans le code. Ne comptez pas sur un message de conversation pour les remplacer.

Vérifiez votre compréhension

Dans le bogue de la liste partagée, chaque réponse était du JSON valide et aucune erreur n'apparaissait. Nommez deux préjudices qu'aucune barrière ne détecterait.

Afficher la réponse

D’abord, Camille a reçu une réponse pour le mauvais billet : une réponse en anglais sur une bouilloire, pour le billet en français de Sophie sur un hamac. Les barrières vérifient une réponse selon le billet que le code leur fournit. Rien ne leur indique que les conversations sont mélangées. Ensuite, la requête de Grace a transmis au modèle le billet de Sophie, son nom et son numéro de commande. Ces renseignements se trouvaient dans la conversation d’une autre cliente. C’est un problème de confidentialité, même quand la réponse semble correcte. Les deux préjudices viennent de la clé de la conversation, pas du modèle.

Vérifiez votre compréhension

Pourquoi le projet utilise-t-il la paire (agent, billet) comme clé d'une conversation, plutôt que le billet seul?

Afficher la réponse

Avec le billet seul comme clé, deux agents qui travaillent sur le même billet partageraient une liste. La demande de suivi d’un agent (« raccourcis la réponse ») modifierait alors le brouillon que l’autre agent lit. Chacun recevrait des réponses à des questions qu’il n’a jamais posées. Avec l’agent seul comme clé, les conversations d’un agent sur différents billets se mélangeraient. Ce serait encore le bogue du bloc-notes. La paire sépare les conversations selon les deux éléments.

Vérifiez votre compréhension

Faites une prédiction : Conversation coupe d'abord les échanges les plus anciens. Dans une longue conversation de suivi sur T-80005, quel échange est coupé en premier? Pourquoi est-ce un problème ici?

Afficher la réponse

L’échange 1, le billet lui-même, est coupé en premier, car c’est le plus ancien. Le modèle reçoit alors les questions de suivi et les brouillons, mais pas les mots du client. Il peut quand même répondre avec assurance, sans les faits dont il a besoin. C’est pourquoi l’exercice du module détecte l’absence du billet et s’arrête avec un message clair. C’est aussi pourquoi « sélectionner » (toujours garder le billet) ou « résumer » peut être préférable à « couper ».

Récapitulatif

  • Le modèle ne garde rien entre les requêtes. L’application garde la conversation et la renvoie : les trois requêtes de Camille comptaient 1,652, 1,749, 1,851 jetons (tokens) d’entrée.
  • Gardez chaque conversation séparée. Une liste partagée a donné à Camille une réponse sur une bouilloire pour son billet sur un hamac (un enregistrement réel). Elle a aussi envoyé le billet de Sophie dans la requête de Grace. Le projet utilise (agent, billet) comme clé de chaque conversation.
  • Un budget de jetons limite l’entrée d’une requête. Vous pouvez couper (les plus anciens d’abord), sélectionner (toujours garder l’essentiel) ou résumer (un appel supplémentaire et une sortie du modèle à vérifier). Le projet coupe, donc le billet disparaît en premier : l’exercice du module gère ce cas.
  • Le nombre de jetons calculé dans le code est une estimation (environ 4 caractères par jeton). Gardez une marge et utilisez usage (utilisation) du fournisseur pour obtenir les nombres exacts.
  • Le message système fait partie de l’état de la conversation. Une nouvelle tâche, comme une traduction pour un collègue, nécessite ses propres instructions.

Ensuite, affichez la réponse à mesure qu’elle arrive. Découvrez pourquoi le texte à l’écran ne constitue pas des données tant que la diffusion n’est pas terminée.

Leçon suivante : Diffuser les réponses en continu