diff --git a/internal/adapter/openai/prompt_build_test.go b/internal/adapter/openai/prompt_build_test.go
index 223689b..3b85be3 100644
--- a/internal/adapter/openai/prompt_build_test.go
+++ b/internal/adapter/openai/prompt_build_test.go
@@ -86,6 +86,12 @@ 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)
}
diff --git a/internal/util/tool_prompt.go b/internal/util/tool_prompt.go
index 13ea906..bec9490 100644
--- a/internal/util/tool_prompt.go
+++ b/internal/util/tool_prompt.go
@@ -36,7 +36,7 @@ func BuildToolCallInstructions(toolNames []string) string {
return `TOOL CALL FORMAT — FOLLOW EXACTLY:
-When calling tools, emit ONLY raw XML at the very end of your response. No text before, no text after, no markdown fences.
+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.
@@ -46,14 +46,17 @@ When calling tools, emit ONLY raw XML at the very end of your response. No text
RULES:
-1) Output ONLY the XML above when calling tools. Do NOT mix tool XML with regular text.
+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.
2) MUST contain a strict JSON object. All JSON keys and strings use double quotes.
3) Multiple tools → multiple blocks inside ONE 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.
+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.
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", "", "", 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:
@@ -64,6 +67,11 @@ Wrong 3 — missing wrapper:
` + ex1 + `{}
Wrong 4 — extra/invented fields:
{"_raw": "...", "command": "ls"}
+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:
@@ -96,7 +104,7 @@ Example C — Tool with complex nested JSON parameters:
-Remember: Output ONLY the ... XML block when calling tools.`
+Remember: when calling tools, end with ONE real ... XML block and do not invent tool results.`
}
func matchAny(name string, candidates ...string) bool {
diff --git a/internal/util/tool_prompt_test.go b/internal/util/tool_prompt_test.go
index e10f176..e4d6ae4 100644
--- a/internal/util/tool_prompt_test.go
+++ b/internal/util/tool_prompt_test.go
@@ -24,3 +24,26 @@ 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)
+ }
+}