mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-11 17:26:57 +02:00
Mode Code : les consignes du dépôt (AGENTS.md, CLAUDE.md) font partie du contexte
Repris de context/instructions.ts d'OpenFox. Un dépôt qui documente ses commandes de build et de test, ses conventions et ses pièges le fait dans AGENTS.md ou CLAUDE.md. Le modèle les redécouvrait à coups de read et de bash — quand il les redécouvrait. En mode Code, le premier de ces fichiers trouvé (dépôt cloné détecté, puis racine de la discussion) part avec le contexte du projet, tronqué à 4 000 caractères. Comme le reste du contexte projet, il est déplacé dans le premier message utilisateur : le préfixe système, et le cache de prompt, restent intacts. Les tours de correction et de relance du builder reçoivent désormais le même contexte projet que le tour de build : sans lui, le début du premier message utilisateur changeait et tout le prompt était recalculé. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
dfafca6fb2
commit
d5632e3595
5 files changed
+102
-3
No files matched your search
@@ -769,6 +769,9 @@ func (c *Conversation) generate(ctx context.Context, caps Caps, temperature floa
|
||||
final := msgs
|
||||
if caps.Agent {
|
||||
final = append(projectSystemMessages(), msgs...)
|
||||
if m, ok := codeInstructionsMessage(caps); ok {
|
||||
final = append([]Message{m}, final...)
|
||||
}
|
||||
if sp := readSysPrompt(); sp != "" {
|
||||
final = append([]Message{{Role: "system", Content: sp}}, final...)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package loki
|
||||
|
||||
// code_instructions.go — consignes du dépôt (AGENTS.md, CLAUDE.md) en mode Code.
|
||||
// Repris de context/instructions.ts d'OpenFox.
|
||||
//
|
||||
// Un dépôt qui documente ses commandes de build et de test, ses conventions,
|
||||
// ses pièges, le fait dans ces fichiers. Sans eux, le modèle les redécouvrait
|
||||
// à coups de read et de bash — quand il les redécouvrait. Le contenu part avec
|
||||
// le contexte du projet : déplacé dans le premier message utilisateur par
|
||||
// normalizeSystemMessages, il ne touche pas au préfixe système (cache).
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const codeInstructionsPrefix = "Repository instructions"
|
||||
|
||||
// codeInstructionsMax : au-delà, on tronque (≈ 1k tokens) — ces fichiers
|
||||
// peuvent être longs, et le contexte est compté.
|
||||
const codeInstructionsMax = 4000
|
||||
|
||||
// codeInstructionFiles : par ordre de préférence ; le premier trouvé gagne.
|
||||
var codeInstructionFiles = []string{"AGENTS.md", "CLAUDE.md", ".github/copilot-instructions.md"}
|
||||
|
||||
// codeInstructionsMessage renvoie les consignes du dépôt de la discussion
|
||||
// (racine, ou dépôt cloné détecté par gitRepoDir), ok=false s'il n'y en a pas.
|
||||
func codeInstructionsMessage(caps Caps) (Message, bool) {
|
||||
if !caps.Code {
|
||||
return Message{}, false
|
||||
}
|
||||
dirs := []string{agentCwd()}
|
||||
if d, err := gitRepoDir(""); err == nil && d != dirs[0] {
|
||||
dirs = append([]string{d}, dirs...)
|
||||
}
|
||||
for _, dir := range dirs {
|
||||
for _, name := range codeInstructionFiles {
|
||||
b, err := os.ReadFile(filepath.Join(dir, name))
|
||||
if err != nil || len(strings.TrimSpace(string(b))) == 0 {
|
||||
continue
|
||||
}
|
||||
s := strings.TrimSpace(string(b))
|
||||
if r := []rune(s); len(r) > codeInstructionsMax {
|
||||
s = string(r[:codeInstructionsMax]) + "\n…[tronqué — lis le fichier pour la suite]"
|
||||
}
|
||||
rel := name
|
||||
if r, err := filepath.Rel(agentCwd(), filepath.Join(dir, name)); err == nil {
|
||||
rel = filepath.ToSlash(r)
|
||||
}
|
||||
return Message{Role: "system", Content: codeInstructionsPrefix + " (" + rel + ") — follow them:\n\n" + s}, true
|
||||
}
|
||||
}
|
||||
return Message{}, false
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package loki
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// AGENTS.md du dépôt cloné part avec le contexte en mode Code, déplacé dans le
|
||||
// premier message utilisateur (préfixe système intact).
|
||||
func TestConsignesDuDepot(t *testing.T) {
|
||||
withWorkspace(t)
|
||||
if _, ok := codeInstructionsMessage(Caps{Code: true}); ok {
|
||||
t.Fatal("consignes trouvées dans un dossier vide")
|
||||
}
|
||||
repo := filepath.Join(agentCwd(), "projet")
|
||||
_ = os.MkdirAll(filepath.Join(repo, ".git"), 0o755)
|
||||
if err := os.WriteFile(filepath.Join(repo, "AGENTS.md"), []byte("Build : make test\n"+strings.Repeat("x", 5000)), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, ok := codeInstructionsMessage(Caps{}); ok {
|
||||
t.Fatal("consignes injectées hors mode Code")
|
||||
}
|
||||
m, ok := codeInstructionsMessage(Caps{Code: true})
|
||||
s := msgText(m)
|
||||
if !ok || !strings.Contains(s, "make test") || !strings.Contains(s, "projet/AGENTS.md") || !strings.Contains(s, "tronqué") {
|
||||
t.Fatalf("message = %.200q", s)
|
||||
}
|
||||
out := normalizeSystemMessages([]Message{{Role: "system", Content: "base"}, m, {Role: "user", Content: "fais-le"}})
|
||||
if msgText(out[0]) != "base" || !strings.Contains(msgText(out[1]), "make test") {
|
||||
t.Fatalf("consignes restées dans le système : %+v", out)
|
||||
}
|
||||
}
|
||||
@@ -152,9 +152,15 @@ func (c *Conversation) runBuilderTurn(ctx context.Context, caps Caps, temperatur
|
||||
msgs := append([]Message(nil), c.Messages...)
|
||||
c.mu.Unlock()
|
||||
|
||||
final := msgs
|
||||
// Même contexte que le tour de build : projet (description, index mémoire,
|
||||
// trackers) et consignes du dépôt. Sans eux, la reprise changeait le début
|
||||
// du premier message utilisateur — tout le cache de prompt à refaire.
|
||||
final := append(projectSystemMessages(), msgs...)
|
||||
if m, ok := codeInstructionsMessage(caps); ok {
|
||||
final = append([]Message{m}, final...)
|
||||
}
|
||||
if sp := readSysPrompt(); sp != "" {
|
||||
final = append([]Message{{Role: "system", Content: sp}}, msgs...)
|
||||
final = append([]Message{{Role: "system", Content: sp}}, final...)
|
||||
}
|
||||
var content strings.Builder
|
||||
extra, _ := runChat(ctx, InjectSkills(final, caps), temperature, caps, func(ev StreamEvent) bool {
|
||||
|
||||
@@ -359,7 +359,8 @@ func isProjectSystem(m Message) bool {
|
||||
return strings.HasPrefix(s, projectContextPrefix) ||
|
||||
strings.HasPrefix(s, memIndexPrefix) ||
|
||||
strings.HasPrefix(s, trackerIndexPrefix) ||
|
||||
strings.HasPrefix(s, memReminderPrefix)
|
||||
strings.HasPrefix(s, memReminderPrefix) ||
|
||||
strings.HasPrefix(s, codeInstructionsPrefix)
|
||||
}
|
||||
|
||||
// prependToFirstUser place ctx en tête du premier message user de msgs (modifié
|
||||
|
||||
Reference in new issue
Block a user