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 { return `TOOL CALL FORMAT — FOLLOW EXACTLY: RULES: 1) Use the XML wrapper format only. 2) Put one or more entries under a single root. 3) Put the tool name in the invoke name attribute: . 4) All string values must use , even short ones. This includes code, scripts, file contents, prompts, paths, names, and queries. 5) Every top-level argument must be a ... node. 6) Objects use nested XML elements inside the parameter body. Arrays may repeat children. 7) Numbers, booleans, and null stay plain text. 8) Use only the parameter names in the tool schema. Do not invent fields. 9) Do NOT wrap XML in markdown fences. Do NOT output explanations, role markers, or internal monologue. 10) If you call a tool, the first non-whitespace characters of that tool block must be exactly . 11) Never omit the opening tag, even if you already plan to close with . PARAMETER SHAPES: - string => - object => ... - array => ...... - number/bool/null => plain_text 【WRONG — Do NOT do these】: Wrong 1 — mixed text after XML: ... I hope this helps. Wrong 2 — Markdown code fences: ` + "```xml" + ` ... ` + "```" + ` Wrong 3 — missing opening wrapper: ... Remember: The ONLY valid way to use tools is the ... XML block at the end of your response. ` + buildCorrectToolExamples(toolNames) } type promptToolExample struct { name string params string } func buildCorrectToolExamples(toolNames []string) string { names := uniqueToolNames(toolNames) examples := make([]string, 0, 4) if single, ok := firstBasicExample(names); ok { examples = append(examples, "Example A — Single tool:\n"+renderToolExampleBlock([]promptToolExample{single})) } if parallel := firstNBasicExamples(names, 2); len(parallel) >= 2 { examples = append(examples, "Example B — Two tools in parallel:\n"+renderToolExampleBlock(parallel)) } if nested, ok := firstNestedExample(names); ok { examples = append(examples, "Example C — Tool with nested XML parameters:\n"+renderToolExampleBlock([]promptToolExample{nested})) } if script, ok := firstScriptExample(names); ok { examples = append(examples, "Example D — Tool with long script using CDATA (RELIABLE FOR CODE/SCRIPTS):\n"+renderToolExampleBlock([]promptToolExample{script})) } if len(examples) == 0 { return "" } return "【CORRECT EXAMPLES】:\n\n" + strings.Join(examples, "\n\n") + "\n\n" } func uniqueToolNames(toolNames []string) []string { names := make([]string, 0, len(toolNames)) seen := map[string]bool{} for _, name := range toolNames { name = strings.TrimSpace(name) if name == "" || seen[name] { continue } seen[name] = true names = append(names, name) } return names } func firstBasicExample(names []string) (promptToolExample, bool) { for _, name := range names { if params, ok := exampleBasicParams(name); ok { return promptToolExample{name: name, params: params}, true } } return promptToolExample{}, false } func firstNBasicExamples(names []string, count int) []promptToolExample { out := make([]promptToolExample, 0, count) for _, name := range names { if params, ok := exampleBasicParams(name); ok { out = append(out, promptToolExample{name: name, params: params}) if len(out) == count { return out } } } return out } func firstNestedExample(names []string) (promptToolExample, bool) { for _, name := range names { if params, ok := exampleNestedParams(name); ok { return promptToolExample{name: name, params: params}, true } } return promptToolExample{}, false } func firstScriptExample(names []string) (promptToolExample, bool) { for _, name := range names { if params, ok := exampleScriptParams(name); ok { return promptToolExample{name: name, params: params}, true } } return promptToolExample{}, false } func renderToolExampleBlock(calls []promptToolExample) string { var b strings.Builder b.WriteString("\n") for _, call := range calls { b.WriteString(` \n") b.WriteString(indentPromptParameters(call.params, " ")) b.WriteString("\n \n") } b.WriteString("") return b.String() } func indentPromptParameters(body, indent string) string { if strings.TrimSpace(body) == "" { return indent + `` } lines := strings.Split(body, "\n") for i, line := range lines { if strings.TrimSpace(line) == "" { lines[i] = line continue } lines[i] = indent + line } return strings.Join(lines, "\n") } func wrapParameter(name, inner string) string { return `` + inner + `` } func exampleBasicParams(name string) (string, bool) { switch strings.TrimSpace(name) { case "Read": return wrapParameter("file_path", promptCDATA("README.md")), true case "Glob": return wrapParameter("pattern", promptCDATA("**/*.go")) + "\n" + wrapParameter("path", promptCDATA(".")), true case "read_file": return wrapParameter("path", promptCDATA("src/main.go")), true case "list_files": return wrapParameter("path", promptCDATA(".")), true case "search_files": return wrapParameter("query", promptCDATA("tool call parser")), true case "Bash", "execute_command": return wrapParameter("command", promptCDATA("pwd")), true case "exec_command": return wrapParameter("cmd", promptCDATA("pwd")), true case "Write": return wrapParameter("file_path", promptCDATA("notes.txt")) + "\n" + wrapParameter("content", promptCDATA("Hello world")), true case "write_to_file": return wrapParameter("path", promptCDATA("notes.txt")) + "\n" + wrapParameter("content", promptCDATA("Hello world")), true case "Edit": return wrapParameter("file_path", promptCDATA("README.md")) + "\n" + wrapParameter("old_string", promptCDATA("foo")) + "\n" + wrapParameter("new_string", promptCDATA("bar")), true case "MultiEdit": return wrapParameter("file_path", promptCDATA("README.md")) + "\n" + `` + promptCDATA("foo") + `` + promptCDATA("bar") + ``, true } return "", false } func exampleNestedParams(name string) (string, bool) { switch strings.TrimSpace(name) { case "MultiEdit": return wrapParameter("file_path", promptCDATA("README.md")) + "\n" + `` + promptCDATA("foo") + `` + promptCDATA("bar") + ``, true case "Task": return wrapParameter("description", promptCDATA("Investigate flaky tests")) + "\n" + wrapParameter("prompt", promptCDATA("Run targeted tests and summarize failures")), true case "ask_followup_question": return wrapParameter("question", promptCDATA("Which approach do you prefer?")) + "\n" + `` + promptCDATA("Option A") + `` + promptCDATA("Option B") + ``, true } return "", false } func exampleScriptParams(name string) (string, bool) { scriptCommand := `cat > /tmp/test_escape.sh <<'EOF' #!/bin/bash echo 'single "double"' echo "literal dollar: \$HOME" EOF bash /tmp/test_escape.sh` scriptContent := `#!/bin/bash echo 'single "double"' echo "literal dollar: $HOME"` switch strings.TrimSpace(name) { case "Bash": return wrapParameter("command", promptCDATA(scriptCommand)) + "\n" + wrapParameter("description", promptCDATA("Test shell escaping")), true case "execute_command": return wrapParameter("command", promptCDATA(scriptCommand)), true case "exec_command": return wrapParameter("cmd", promptCDATA(scriptCommand)), true case "Write": return wrapParameter("file_path", promptCDATA("test_escape.sh")) + "\n" + wrapParameter("content", promptCDATA(scriptContent)), true case "write_to_file": return wrapParameter("path", promptCDATA("test_escape.sh")) + "\n" + wrapParameter("content", promptCDATA(scriptContent)), true } return "", false } func promptCDATA(text string) string { if text == "" { return "" } if strings.Contains(text, "]]>") { return "", "]]]]>") + "]]>" } return "" }