mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-11 17:26:57 +02:00
Cache : corrections de relecture — la ligne MCP et les descriptions d'outils partagent un seul format
mcpPromptLine relisait le préfixe « [MCP: <serveur>] » des descriptions en le réécrivant à la main : changer le format dans mcpTools aurait fait disparaître la ligne en silence. Un nom de serveur contenant « ] » y était aussi coupé. - mcpTagDescription pose le préfixe, mcpToolServer le relit : un seul endroit. - mcpToolServer retient la coupure dont la forme assainie est celle du nom d'outil — « a] b » reste « a] b ». - Les tests qui appellent EnabledTools isolent $LOKI_HOME : la configuration MCP de la machine de test (et ses connexions) ne fausse plus le cas « sans outil MCP » ni le budget du préambule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7dd319a058
commit
eaf5e313d9
3 files changed
+48
-15
No files matched your search
@@ -49,6 +49,9 @@ import (
|
||||
const promptCharBudget = 11000 // ~2750 tokens, tout allumé
|
||||
|
||||
func TestSystemPromptStaysLean(t *testing.T) {
|
||||
// $LOKI_HOME isolé : les outils MCP configurés sur la machine de test (et
|
||||
// leur ligne au préambule) ne comptent pas dans le budget de Loki.
|
||||
testHome(t)
|
||||
caps := Caps{Agent: true, Internet: true, Mem: MemAlways}
|
||||
tools := EnabledTools(caps)
|
||||
sp := baseSystemPrompt(caps, tools)
|
||||
|
||||
+36
-14
@@ -307,7 +307,7 @@ func mcpTools() []Tool {
|
||||
}
|
||||
// Préfixe le serveur d'origine dans la description : aide le modèle à
|
||||
// choisir entre outils similaires de serveurs différents.
|
||||
desc = "[MCP: " + name + "] " + desc
|
||||
desc = mcpTagDescription(name, desc)
|
||||
out = append(out, Tool{
|
||||
Type: "function",
|
||||
Function: ToolFunction{
|
||||
@@ -449,6 +449,39 @@ func flattenMCPContent(res *mcpsdk.CallToolResult) string {
|
||||
return text
|
||||
}
|
||||
|
||||
// mcpTagDescription préfixe la description d'un outil MCP par le nom d'origine
|
||||
// de son serveur, que le nom d'outil (assaini) ne garde pas. mcpToolServer le
|
||||
// relit pour la ligne du préambule : ce format n'existe qu'ici, pour que les
|
||||
// deux ne puissent pas diverger en silence.
|
||||
func mcpTagDescription(server, desc string) string {
|
||||
return "[MCP: " + server + "] " + desc
|
||||
}
|
||||
|
||||
// mcpToolServer retrouve le serveur d'origine d'un outil MCP annoncé, d'après
|
||||
// le préfixe posé par mcpTagDescription. Le nom brut d'un serveur peut lui-même
|
||||
// contenir « ] » : on retient la première coupure dont la forme assainie est
|
||||
// bien celle qui figure dans le nom de l'outil.
|
||||
func mcpToolServer(t Tool) (string, bool) {
|
||||
name := t.Function.Name
|
||||
if !strings.HasPrefix(name, "mcp__") {
|
||||
return "", false
|
||||
}
|
||||
rest, ok := strings.CutPrefix(t.Function.Description, "[MCP: ")
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
for i := 0; ; {
|
||||
j := strings.Index(rest[i:], "] ")
|
||||
if j < 0 {
|
||||
return "", false
|
||||
}
|
||||
if server := rest[:i+j]; strings.HasPrefix(name, "mcp__"+mcpSanitize(server)+"__") {
|
||||
return server, true
|
||||
}
|
||||
i += j + 1
|
||||
}
|
||||
}
|
||||
|
||||
// mcpPromptLine renvoie une ligne système listant les serveurs MCP annoncés et
|
||||
// leur nombre d'outils, pour situer le modèle. Elle se DÉDUIT des outils
|
||||
// réellement envoyés ce tour-ci (EnabledTools), pas de l'état du pool.
|
||||
@@ -463,21 +496,10 @@ func flattenMCPContent(res *mcpsdk.CallToolResult) string {
|
||||
func mcpPromptLine(tools []Tool) string {
|
||||
counts := map[string]int{}
|
||||
for _, t := range tools {
|
||||
if !strings.HasPrefix(t.Function.Name, "mcp__") {
|
||||
continue
|
||||
}
|
||||
// mcpTools préfixe chaque description par « [MCP: <serveur>] » : le nom
|
||||
// d'origine du serveur, que le nom d'outil (assaini) ne garde pas.
|
||||
rest, ok := strings.CutPrefix(t.Function.Description, "[MCP: ")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
server, _, ok := strings.Cut(rest, "] ")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if server, ok := mcpToolServer(t); ok {
|
||||
counts[server]++
|
||||
}
|
||||
}
|
||||
if len(counts) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
func mcpTestTool(server, tool string) Tool {
|
||||
return Tool{Type: "function", Function: ToolFunction{
|
||||
Name: mcpExposedName(server, tool),
|
||||
Description: "[MCP: " + server + "] décrit " + tool,
|
||||
Description: mcpTagDescription(server, "décrit "+tool),
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,10 @@ func TestMCPPromptLineDeriveDesOutils(t *testing.T) {
|
||||
""},
|
||||
{"description vide après le préfixe", []Tool{{Type: "function", Function: ToolFunction{Name: "mcp__x__y", Description: "[MCP: x] "}}},
|
||||
"x (1)"},
|
||||
{"nom de serveur contenant « ] »", []Tool{mcpTestTool("a] b", "t"), mcpTestTool("a] b", "u")},
|
||||
"a] b (2)"},
|
||||
{"préfixe d'un autre serveur que celui du nom d'outil", []Tool{{Type: "function", Function: ToolFunction{Name: "mcp__x__y", Description: "[MCP: z] y"}}},
|
||||
""},
|
||||
}
|
||||
for _, c := range cases {
|
||||
got := mcpPromptLine(c.tools)
|
||||
@@ -51,6 +55,10 @@ func TestMCPPromptLineDeriveDesOutils(t *testing.T) {
|
||||
// planner (aucun outil MCP) n'a pas de ligne MCP, le fil principal l'a dès le
|
||||
// premier tour.
|
||||
func TestBaseSystemPromptLigneMCPSuitLesOutils(t *testing.T) {
|
||||
// $LOKI_HOME isolé : EnabledTools lit la configuration MCP, et celle de la
|
||||
// machine de test (serveurs réels, connexions comprises) fausserait le cas
|
||||
// « sans outil MCP ».
|
||||
testHome(t)
|
||||
caps := Caps{Agent: true, Mem: MemOff}
|
||||
with := append(EnabledTools(caps), mcpTestTool("github", "issues"))
|
||||
if sp := baseSystemPrompt(caps, with); !strings.Contains(sp, "MCP servers connected") || !strings.Contains(sp, "github (1)") {
|
||||
|
||||
Reference in new issue
Block a user