Ver Fonte

Mémoire : MAX_TOKENS relevé à 4000, plus de « […] » seul

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Johnounet há 2 dias atrás
pai
commit
54b4959036
6 ficheiros alterados com 39 adições e 6 exclusões
  1. 4 2
      .env.exemple
  2. 1 1
      README.md
  3. 14 2
      bot/claude_client.py
  4. 3 1
      bot/config.py
  5. 10 0
      bot/discord_bot.py
  6. 7 0
      changelog.txt

+ 4 - 2
.env.exemple

@@ -11,8 +11,10 @@ CHANNEL_ID=
 # Modèle utilisé : claude-sonnet-5-5 (recommandé), claude-haiku-5-5 (bien moins cher)
 # ou claude-opus-5-5 (plus capable, plus cher). Redémarre le bot après un changement.
 CLAUDE_MODEL=claude-sonnet-5-5
-# Longueur maximale d'une réponse (en tokens, ~0,75 mot par token)
-MAX_TOKENS=3000
+# Longueur maximale d'une réponse (en tokens, ~0,75 mot par token), réflexion et notes de mémoire
+# comprises. Ne coûte que si elle est atteinte. Avec MEMORY=on, au moins 4000 (le bot prévient au
+# démarrage si c'est trop peu pour MEMORY_SERVER_LIMIT).
+MAX_TOKENS=4000
 # Réflexion avant de répondre : off (rapide) ou on (plus réfléchi, plus lent et plus cher).
 # Sur claude-opus-5-5, la réflexion ne peut pas être coupée : règle plutôt EFFORT=low.
 THINKING=on

+ 1 - 1
README.md

@@ -104,7 +104,7 @@ Tous les réglages se trouvent dans le `.env`, et chacun est expliqué dans `.en
 | `CLAUDE_MODEL` | Modèle utilisé : `claude-sonnet-5-5` (recommandé), `claude-haiku-5-5` (bien moins cher) ou `claude-opus-5-5` (plus capable, plus cher) | `claude-sonnet-5-5` |
 | `THINKING` | Réflexion avant de répondre : `on` (plus réfléchi, plus lent et plus cher) ou `off` | `on` |
 | `EFFORT` | Niveau d'effort : `low`, `medium`, `high`, `xhigh` ou `max` ; vide = valeur du modèle | `medium` |
-| `MAX_TOKENS` | Longueur maximale d'une réponse (réflexion comprise), en tokens | `3000` |
+| `MAX_TOKENS` | Longueur maximale d'une réponse (réflexion et notes de mémoire comprises), en tokens ; ne coûte que si elle est atteinte. Avec la mémoire, au moins 4000 : le bot prévient au démarrage si c'est trop peu | `4000` |
 | `MAX_HISTORY_LENGTH` | Messages gardés en mémoire dans le salon dédié (jusqu'à la moitié de plus avant d'y revenir d'un coup, ce qui garde la mémoire en cache) | `10` |
 | `CONTEXT_MESSAGE_LIMIT` | Messages lus autour d'une mention | `20` |
 | `WEB_SEARCH` | Recherche web et lecture de liens (`on` ou `off`) ; prévois alors un `API_TIMEOUT` d'au moins 90 secondes | `off` |

+ 14 - 2
bot/claude_client.py

@@ -150,6 +150,8 @@ CUSTOM_STATUS_PROMPT = (
 
 # Erreur due à la configuration (clé, crédit, modèle, réglages) : seul l'administrateur peut la corriger.
 CONFIG_ERROR = "Je suis mal configuré, préviens l'administrateur du bot."
+TRUNCATED_ERROR = ("Ma réponse a été coupée avant que j'aie pu l'écrire. Si cela se répète, "
+                   "préviens l'administrateur du bot.")
 
 # Nombre maximal de relances quand l'API met la réponse en pause pendant les recherches.
 MAX_CONTINUATIONS = 3
@@ -343,8 +345,18 @@ class ClaudeClient:
 
         stop_reason = responses[-1].stop_reason
         if stop_reason == "max_tokens":
-            logger.warning("Réponse coupée par MAX_TOKENS (%s) : augmente MAX_TOKENS dans le .env "
-                           "si cela arrive souvent.", self.settings.max_tokens)
+            # Un appel d'outil coupé n'est pas exécuté : sans ce message, la note perdue passerait inaperçue.
+            cut = [block.name for block in responses[-1].content if block.type in ("tool_use", "server_tool_use")]
+            if cut:
+                logger.warning("Appel à l'outil « %s » coupé par MAX_TOKENS (%s), donc pas exécuté : augmente "
+                               "MAX_TOKENS dans le .env.", ", ".join(cut), self.settings.max_tokens)
+            else:
+                logger.warning("Réponse coupée par MAX_TOKENS (%s) : augmente MAX_TOKENS dans le .env "
+                               "si cela arrive souvent.", self.settings.max_tokens)
+            # Coupée avant son premier mot (réflexion ou appel d'outil trop long) : « […] » seul
+            # n'aurait aucun sens sur Discord ni dans l'historique.
+            if not text:
+                raise ClaudeError(TRUNCATED_ERROR)
             text += " […]"
         elif stop_reason == "pause_turn":
             logger.warning("Recherche web toujours en cours après %s relances : réponse partielle.", MAX_CONTINUATIONS)

+ 3 - 1
bot/config.py

@@ -218,7 +218,9 @@ def load_settings() -> Settings:
         anthropic_api_key=_get_str("ANTHROPIC_API_KEY", required=True),
         channel_id=_get_int("CHANNEL_ID", required=True),
         model=model,
-        max_tokens=_get_int("MAX_TOKENS", default=1500, minimum=1),
+        # Une limite haute ne coûte que si elle est atteinte ; trop basse, elle coupe la réécriture
+        # d'un fichier de mémoire plein (voir la vérification au démarrage, dans discord_bot.py).
+        max_tokens=_get_int("MAX_TOKENS", default=4000, minimum=1),
         thinking=thinking,
         effort=effort,
         api_timeout=_get_float("API_TIMEOUT", default=60.0, minimum=1),

+ 10 - 0
bot/discord_bot.py

@@ -189,6 +189,16 @@ class ChatBot(commands.Bot):
                 prices = (" S'il existe bien, renseigne PRICE_INPUT_PER_MTOK et PRICE_OUTPUT_PER_MTOK "
                           "pour estimer les coûts.")
             logger.warning("Modèle %s inconnu du bot : vérifie CLAUDE_MODEL dans le .env.%s", s.model, prices)
+        if self.memory:
+            # Un fichier plein se réécrit d'un seul appel d'outil, qui doit tenir dans MAX_TOKENS avec
+            # la réflexion : environ 2,5 caractères par token en français (estimation prudente),
+            # plus une marge pour la réflexion et la réponse.
+            needed = max(self.memory.member_limit, self.memory.server_limit) * 2 // 5 + 2000
+            if s.max_tokens < needed:
+                logger.warning("MAX_TOKENS (%s) est trop petit pour la mémoire des membres : la réécriture "
+                               "d'un fichier plein serait coupée et la note perdue. Mets au moins %s dans "
+                               "le .env, ou baisse MEMORY_SERVER_LIMIT et MEMORY_MEMBER_LIMIT.",
+                               s.max_tokens, needed)
         if self.presence:
             self.presence.start()
 

+ 7 - 0
changelog.txt

@@ -154,6 +154,13 @@ Corrections
 - Mémoire : un administrateur ne peut plus écrire la fiche d'un membre
   qui n'a pas activé la mémoire (« Modifier » grisé, avec une
   explication) ; il peut toujours l'effacer.
+- Mémoire : MAX_TOKENS vaut 4000 par défaut (1500 avant), assez pour
+  que Rika réécrive le fichier général quand il est plein ; avant, la
+  réécriture était coupée et la note perdue, à chaque nouvel essai.
+  Le bot prévient au démarrage si MAX_TOKENS est trop petit pour
+  MEMORY_SERVER_LIMIT, et le log signale un appel d'outil coupé.
+- Une réponse coupée avant son premier mot n'envoie plus « […] » seul
+  sur Discord (ni dans l'historique), mais un message d'explication.
 
 Divers
 - Le token Discord et les clés Anthropic et Steam n'apparaissent plus