refactor: enforce strict XML-only output for tool calls and remove mixed-content instructions

This commit is contained in:
CJACK
2026-04-05 17:25:52 +08:00
parent 22efd8178b
commit 5d59775051
3 changed files with 4 additions and 41 deletions

View File

@@ -86,12 +86,6 @@ func TestBuildOpenAIFinalPrompt_VercelPreparePathKeepsFinalAnswerInstruction(t *
if !strings.Contains(finalPrompt, "Do NOT wrap the XML in markdown code fences") {
t.Fatalf("vercel prepare finalPrompt missing no-fence xml instruction: %q", finalPrompt)
}
if !strings.Contains(finalPrompt, "If a tool call fails, is rejected, or returns no usable result") {
t.Fatalf("vercel prepare finalPrompt missing failure recovery instruction: %q", finalPrompt)
}
if !strings.Contains(finalPrompt, "Never claim that a tool was run") {
t.Fatalf("vercel prepare finalPrompt missing no-fabrication instruction: %q", finalPrompt)
}
if strings.Contains(finalPrompt, "```json") {
t.Fatalf("vercel prepare finalPrompt should not require fenced tool calls: %q", finalPrompt)
}

View File

@@ -36,7 +36,7 @@ func BuildToolCallInstructions(toolNames []string) string {
return `TOOL CALL FORMAT — FOLLOW EXACTLY:
When calling tools, the final block of your response MUST be raw XML. Do not use markdown fences. Do not put any text after the XML block.
When calling tools, emit ONLY raw XML at the very end of your response. No text before, no text after, no markdown fences.
<tool_calls>
<tool_call>
@@ -46,17 +46,14 @@ When calling tools, the final block of your response MUST be raw XML. Do not use
</tool_calls>
RULES:
1) When calling tools, the final block of your response MUST be the XML above. Any explanatory text must appear before that block, never after it.
1) Output ONLY the XML above when calling tools. Do NOT mix tool XML with regular text.
2) <parameters> MUST contain a strict JSON object. All JSON keys and strings use double quotes.
3) Multiple tools → multiple <tool_call> blocks inside ONE <tool_calls> root.
4) Do NOT wrap the XML in markdown code fences (no triple backticks).
5) After receiving a tool result, use it directly. Only call another tool if the result is insufficient.
6) If you want to say something AND call a tool, output text first, then the XML block on its own. Do not narrate pseudo-tool calls in plain text.
6) If you want to say something AND call a tool, output text first, then the XML block on its own.
7) Parameters MUST use the exact field names from the selected tool schema.
8) CRITICAL: Do NOT invent or add any extra fields (such as "_raw", "_xml"). Use ONLY the fields strictly defined in the schema. Extra fields will cause execution failure.
9) Never claim that a tool was run, or that you saw search/browser/file results, unless you actually received a tool result for that tool call.
10) If a tool call fails, is rejected, or returns no usable result: do NOT fabricate the missing result. Either retry once with corrected parameters when the fix is obvious, or ask the user / answer with the limitation.
11) Never output internal tool-planning traces such as "let me search", "calling tool", "<tool_calls>", "<tool_call>", or fake tool transcripts unless you are emitting a real tool call block at the end.
❌ WRONG — Do NOT do these:
Wrong 1 — mixed text and XML:
@@ -67,11 +64,6 @@ Wrong 3 — missing <tool_calls> wrapper:
<tool_call><tool_name>` + ex1 + `</tool_name><parameters>{}</parameters></tool_call>
Wrong 4 — extra/invented fields:
<parameters>{"_raw": "...", "command": "ls"}</parameters>
Wrong 5 — fake tool execution/result narration:
Let me search the web first.
Search results show the answer is 42.
Wrong 6 — tool failed, but model pretends it succeeded:
The tool returned three skills, so install review-implementing.
✅ CORRECT EXAMPLES:
@@ -104,7 +96,7 @@ Example C — Tool with complex nested JSON parameters:
</tool_call>
</tool_calls>
Remember: when calling tools, end with ONE real <tool_calls>...</tool_calls> XML block and do not invent tool results.`
Remember: Output ONLY the <tool_calls>...</tool_calls> XML block when calling tools.`
}
func matchAny(name string, candidates ...string) bool {

View File

@@ -24,26 +24,3 @@ func TestBuildToolCallInstructions_ExecuteCommandUsesCommandExample(t *testing.T
t.Fatalf("expected command parameter example for execute_command, got: %s", out)
}
}
func TestBuildToolCallInstructions_IncludesFailureRecoveryAndNoFabricationRules(t *testing.T) {
out := BuildToolCallInstructions([]string{"WebSearch"})
if !strings.Contains(out, "Never claim that a tool was run") {
t.Fatalf("expected no-fabrication guard, got: %s", out)
}
if !strings.Contains(out, "If a tool call fails, is rejected, or returns no usable result") {
t.Fatalf("expected failure recovery rule, got: %s", out)
}
if !strings.Contains(out, "Never output internal tool-planning traces") {
t.Fatalf("expected no internal trace rule, got: %s", out)
}
}
func TestBuildToolCallInstructions_AllowsTextBeforeXMLButNotAfter(t *testing.T) {
out := BuildToolCallInstructions([]string{"read_file"})
if !strings.Contains(out, "Any explanatory text must appear before that block, never after it.") {
t.Fatalf("expected clarified xml placement rule, got: %s", out)
}
if strings.Contains(out, "No text before, no text after") {
t.Fatalf("expected contradictory no-text-before wording removed, got: %s", out)
}
}