Skip to content

Commit 7c29ffb

Browse files
committed
fix: enhance tool use instructions for Ollama models
- Add enhanced XML formatting instructions for local models (Ollama/LMStudio) - Include explicit examples of correct and incorrect tool usage - Add mandatory requirements to prevent models from adding explanatory text - Pass apiProvider through system prompt generation chain - Add comprehensive tests for the new functionality This addresses the issue where local Ollama models were not properly following the XML tool format, causing them to hallucinate code or enter infinite loops instead of using the codebase_search tool. Fixes #8430
1 parent f8ed7a7 commit 7c29ffb

File tree

5 files changed

+140
-3
lines changed

5 files changed

+140
-3
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { describe, it, expect } from "vitest"
2+
import { getSharedToolUseSection } from "../tool-use"
3+
4+
describe("getSharedToolUseSection", () => {
5+
describe("base functionality", () => {
6+
it("should return base tool use section when no apiProvider is provided", () => {
7+
const result = getSharedToolUseSection()
8+
9+
expect(result).toContain("TOOL USE")
10+
expect(result).toContain("Tool uses are formatted using XML-style tags")
11+
expect(result).toContain("<actual_tool_name>")
12+
expect(result).not.toContain("CRITICAL: Tool Use Requirements")
13+
expect(result).not.toContain("MANDATORY")
14+
})
15+
16+
it("should return base tool use section for non-local providers", () => {
17+
const providers = ["anthropic", "openai", "openrouter", "bedrock"]
18+
19+
providers.forEach((provider) => {
20+
const result = getSharedToolUseSection(provider)
21+
22+
expect(result).toContain("TOOL USE")
23+
expect(result).toContain("Tool uses are formatted using XML-style tags")
24+
expect(result).not.toContain("CRITICAL: Tool Use Requirements")
25+
expect(result).not.toContain("MANDATORY")
26+
})
27+
})
28+
})
29+
30+
describe("local model enhancements", () => {
31+
it("should include enhanced instructions for ollama provider", () => {
32+
const result = getSharedToolUseSection("ollama")
33+
34+
// Check base content is still there
35+
expect(result).toContain("TOOL USE")
36+
expect(result).toContain("Tool uses are formatted using XML-style tags")
37+
38+
// Check enhanced instructions
39+
expect(result).toContain("CRITICAL: Tool Use Requirements for Your Response")
40+
expect(result).toContain("MANDATORY")
41+
expect(result).toContain("Every response MUST contain EXACTLY ONE tool use")
42+
expect(result).toContain("DO NOT")
43+
expect(result).toContain("Write explanations or text outside of the tool XML tags")
44+
expect(result).toContain("Guess file locations or code content")
45+
expect(result).toContain("ALWAYS")
46+
expect(result).toContain("Start with codebase_search tool when exploring code")
47+
48+
// Check examples
49+
expect(result).toContain("Example of a CORRECT response")
50+
expect(result).toContain("<codebase_search>")
51+
expect(result).toContain("<query>main function entry point</query>")
52+
expect(result).toContain("</codebase_search>")
53+
54+
expect(result).toContain("Example of an INCORRECT response")
55+
expect(result).toContain("I'll search for the main function")
56+
57+
// Check final reminder
58+
expect(result).toContain("Remember: Your ENTIRE response should be the tool XML, nothing else")
59+
})
60+
61+
it("should include enhanced instructions for lmstudio provider", () => {
62+
const result = getSharedToolUseSection("lmstudio")
63+
64+
// Check base content is still there
65+
expect(result).toContain("TOOL USE")
66+
expect(result).toContain("Tool uses are formatted using XML-style tags")
67+
68+
// Check enhanced instructions (same as ollama)
69+
expect(result).toContain("CRITICAL: Tool Use Requirements for Your Response")
70+
expect(result).toContain("MANDATORY")
71+
expect(result).toContain("Every response MUST contain EXACTLY ONE tool use")
72+
expect(result).toContain("DO NOT")
73+
expect(result).toContain("ALWAYS")
74+
expect(result).toContain("Example of a CORRECT response")
75+
expect(result).toContain("Example of an INCORRECT response")
76+
expect(result).toContain("Remember: Your ENTIRE response should be the tool XML, nothing else")
77+
})
78+
})
79+
80+
describe("formatting and structure", () => {
81+
it("should maintain proper formatting with line breaks", () => {
82+
const result = getSharedToolUseSection("ollama")
83+
84+
// Check that there are proper line breaks between sections
85+
expect(result).toMatch(/TOOL USE\n\n/)
86+
expect(result).toMatch(/# Tool Use Formatting\n\n/)
87+
expect(result).toMatch(/# CRITICAL: Tool Use Requirements/)
88+
})
89+
90+
it("should have consistent XML examples", () => {
91+
const result = getSharedToolUseSection("ollama")
92+
93+
// Check XML structure is properly formatted
94+
expect(result).toMatch(/<actual_tool_name>\n<parameter1_name>value1<\/parameter1_name>/)
95+
expect(result).toMatch(/<codebase_search>\n<query>/)
96+
})
97+
})
98+
})

src/core/prompts/sections/tool-use.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
export function getSharedToolUseSection(): string {
2-
return `====
1+
export function getSharedToolUseSection(apiProvider?: string): string {
2+
// Enhanced instructions for local models that may struggle with tool formatting
3+
const isLocalModel = apiProvider === "ollama" || apiProvider === "lmstudio"
4+
5+
const baseSection = `====
36
47
TOOL USE
58
@@ -16,4 +19,33 @@ Tool uses are formatted using XML-style tags. The tool name itself becomes the X
1619
</actual_tool_name>
1720
1821
Always use the actual tool name as the XML tag name for proper parsing and execution.`
22+
23+
if (isLocalModel) {
24+
return (
25+
baseSection +
26+
`
27+
28+
# CRITICAL: Tool Use Requirements for Your Response
29+
30+
**MANDATORY**: Every response MUST contain EXACTLY ONE tool use in the XML format shown above.
31+
**DO NOT**: Write explanations or text outside of the tool XML tags.
32+
**DO NOT**: Guess file locations or code content - use the appropriate search tools first.
33+
**ALWAYS**: Start with codebase_search tool when exploring code for the first time.
34+
35+
Example of a CORRECT response (using codebase_search):
36+
<codebase_search>
37+
<query>main function entry point</query>
38+
</codebase_search>
39+
40+
Example of an INCORRECT response (this will fail):
41+
I'll search for the main function in your codebase.
42+
<codebase_search>
43+
<query>main function</query>
44+
</codebase_search>
45+
46+
Remember: Your ENTIRE response should be the tool XML, nothing else.`
47+
)
48+
}
49+
50+
return baseSection
1951
}

src/core/prompts/system.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ async function generatePrompt(
6262
settings?: SystemPromptSettings,
6363
todoList?: TodoItem[],
6464
modelId?: string,
65+
apiProvider?: string,
6566
): Promise<string> {
6667
if (!context) {
6768
throw new Error("Extension context is required for generating system prompt")
@@ -92,7 +93,7 @@ async function generatePrompt(
9293
9394
${markdownFormattingSection()}
9495
95-
${getSharedToolUseSection()}
96+
${getSharedToolUseSection(apiProvider)}
9697
9798
${getToolDescriptionsForMode(
9899
mode,
@@ -153,6 +154,7 @@ export const SYSTEM_PROMPT = async (
153154
settings?: SystemPromptSettings,
154155
todoList?: TodoItem[],
155156
modelId?: string,
157+
apiProvider?: string,
156158
): Promise<string> => {
157159
if (!context) {
158160
throw new Error("Extension context is required for generating system prompt")
@@ -225,5 +227,6 @@ ${customInstructions}`
225227
settings,
226228
todoList,
227229
modelId,
230+
apiProvider,
228231
)
229232
}

src/core/task/Task.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2445,6 +2445,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
24452445
},
24462446
undefined, // todoList
24472447
this.api.getModel().id,
2448+
this.apiConfiguration.apiProvider,
24482449
)
24492450
})()
24502451
}

src/core/webview/generateSystemPrompt.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ export const generateSystemPrompt = async (provider: ClineProvider, message: Web
8989
.getConfiguration("roo-cline")
9090
.get<boolean>("newTaskRequireTodos", false),
9191
},
92+
undefined, // todoList
93+
undefined, // modelId
94+
apiConfiguration?.apiProvider,
9295
)
9396

9497
return systemPrompt

0 commit comments

Comments
 (0)