package toolcall import "strings" // BuildToolCallInstructions generates the unified tool-calling instruction block // used by all adapters (OpenAI, Claude, Gemini). It uses attention-optimized // structure: rules → negative examples → positive examples → anchor. // // The toolNames slice should contain the actual tool names available in the // current request; the function picks real names for examples. func BuildToolCallInstructions(toolNames []string) string { // Pick real tool names for examples; fall back to generic names. ex1 := "read_file" ex2 := "write_to_file" ex3 := "ask_followup_question" used := map[string]bool{} for _, n := range toolNames { switch { // Read/query-type tools case !used["ex1"] && matchAny(n, "read_file", "list_files", "search_files", "Read", "Glob"): ex1 = n used["ex1"] = true // Write/execute-type tools case !used["ex2"] && matchAny(n, "write_to_file", "apply_diff", "execute_command", "exec_command", "Write", "Edit", "MultiEdit", "Bash"): ex2 = n used["ex2"] = true // Interactive/meta tools case !used["ex3"] && matchAny(n, "ask_followup_question", "attempt_completion", "update_todo_list", "Task"): ex3 = n used["ex3"] = true } } ex1Params := exampleReadParams(ex1) ex2Params := exampleWriteOrExecParams(ex2) ex3Params := exampleInteractiveParams(ex3) 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. TOOL_NAME_HERE PARAMETER_VALUE RULES: 1) When calling tools, you MUST use the XML format. 2) No text is allowed AFTER the XML block. 3) should be XML tags, not JSON. Use nested XML elements for structured data (e.g., value). 4) For long text, scripts, novels, or code content, YOU MUST wrap the value in to preserve formatting and avoid character escaping errors. 5) Multiple tools must be inside the same root. 6) Do NOT wrap XML in markdown fences (` + "```" + `). 7) Do NOT invent parameters. Use only the provided schema. 8) CRITICAL: Do NOT output internal monologues (e.g. "I will list files now..."). Just output your answer or the XML. ❌ WRONG — Do NOT do these: Wrong 1 — mixed text after XML: ... I hope this helps. Wrong 2 — function-call syntax: Grep({"pattern": "token"}) Wrong 3 — missing wrapper: ` + ex1 + `{} Wrong 4 — Markdown code fences: ` + "```xml" + ` ... ` + "```" + ` Wrong 5 — native tool tokens: <|Tool|>call_some_tool{"param":1}<|Tool|> Wrong 6 — role markers in response: <|Assistant|> Here is the result... Remember: The ONLY valid way to use tools is the XML block at the end of your response. ✅ CORRECT EXAMPLES: Example A — Single tool: ` + ex1 + ` ` + ex1Params + ` Example B — Two tools in parallel: ` + ex1 + ` ` + ex1Params + ` ` + ex2 + ` ` + ex2Params + ` Example C — Tool with complex structured XML parameters: ` + ex3 + ` ` + ex3Params + ` Example D — Tool with long script using CDATA (RELIABLE FOR CODE/SCRIPTS): ` + ex2 + ` script.sh Remember: Output ONLY the ... XML block when calling tools.` } func matchAny(name string, candidates ...string) bool { for _, c := range candidates { if name == c { return true } } return false } func exampleReadParams(name string) string { switch strings.TrimSpace(name) { case "Read": return `README.md` case "Glob": return `**/*.go.` default: return `src/main.go` } } func exampleWriteOrExecParams(name string) string { switch strings.TrimSpace(name) { case "Bash", "execute_command": return `pwd` case "exec_command": return `pwd` case "Edit": return `README.mdfoobar` case "MultiEdit": return `README.mdfoobar` default: return `output.txtHello world` } } func exampleInteractiveParams(name string) string { switch strings.TrimSpace(name) { case "Task": return `Investigate flaky testsRun targeted tests and summarize failures` default: return `Which approach do you prefer?Option AOption B` } }