From 4602614be09cb75098973297578fce230ae85435 Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Mon, 28 Sep 2026 00:13:23 +0200 Subject: [PATCH] =?UTF-8?q?Chat=20:=20mode=20agent=20coup=C3=A9=20=3D=20mo?= =?UTF-8?q?d=C3=A8le=20brut,=20vraiment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Repris d'AJEAN 0.13.12 / 0.13.13. Couper le mode agent retirait bien les outils, la mémoire et le préambule Loki, mais laissait passer deux choses : - Le prompt système du PRESET et le contexte du projet (description, index mémoire, trackers). Ils décrivent des outils et une mémoire que ce mode n'a pas : le modèle, persuadé de pouvoir agir, écrivait des en clair dans sa réponse. Agent off, generate n'injecte plus rien — le modèle reçoit les messages nus, comme un llama-server direct. - Un tool_call que le moteur parvient à parser alors qu'aucun outil n'a été annoncé (agent off, ou relance sans outils après un 500). Il était exécuté, répondait « outil inconnu », et la boucle repartait. Il est désormais ignoré : le modèle répond en texte. Co-Authored-By: Claude Opus 5.5 --- internal/loki/chat_conversation.go | 14 +++++++++++--- internal/loki/llm_client.go | 7 ++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/internal/loki/chat_conversation.go b/internal/loki/chat_conversation.go index ca8bc5e..180df49 100644 --- a/internal/loki/chat_conversation.go +++ b/internal/loki/chat_conversation.go @@ -574,9 +574,17 @@ func (c *Conversation) generate(ctx context.Context, caps Caps, temperature floa // Contexte du projet actif (description, index mémoire, index des trackers), // même traitement : injecté dans la vue envoyée, jamais persisté — voir // projectSystemMessages. - final := append(projectSystemMessages(), msgs...) - if sp := readSysPrompt(); sp != "" { - final = append([]Message{{Role: "system", Content: sp}}, final...) + // + // Mode agent OFF = modèle BRUT (AJEAN 0.13.13) : ni l'un ni l'autre. Sans + // agent, on veut parler au modèle nu, comme à un llama-server direct. Le + // prompt du preset et l'index mémoire décrivent des outils que ce mode n'a + // pas : le modèle se mettait à écrire des en clair. + final := msgs + if caps.Agent { + final = append(projectSystemMessages(), msgs...) + if sp := readSysPrompt(); sp != "" { + final = append([]Message{{Role: "system", Content: sp}}, final...) + } } // newBase : vue modèle publiée par une compaction survenue PENDANT le tour. diff --git a/internal/loki/llm_client.go b/internal/loki/llm_client.go index 40e6796..06b8bcc 100644 --- a/internal/loki/llm_client.go +++ b/internal/loki/llm_client.go @@ -1094,7 +1094,12 @@ func runChat(ctx context.Context, messages []Message, temperature float64, caps if ch.FinishReason != "" { finishReason = ch.FinishReason } - if len(ch.Delta.ToolCalls) > 0 { + // Un tool_call n'est retenu que si des outils ont VRAIMENT été annoncés ce + // tour-ci (même condition que le payload). Sinon — mode agent coupé, ou + // relance sans outils après un 500 — le moteur peut quand même parser un + // appel que le modèle a émis de lui-même ; l'exécuter donnait « outil + // inconnu », réinjecté puis mis en boucle. On l'ignore (AJEAN 0.13.12). + if len(tools) > 0 && !disableTools && len(ch.Delta.ToolCalls) > 0 { // Un appel d'outil clôt le texte : on vide MAINTENANT le reliquat // retenu par la garde « » (voir plus bas). Sinon il n'était // émis qu'en fin de flux, donc APRÈS l'événement d'outil, et l'UI