Skip to content

Commit 9b99890

Browse files
feat(prompts): strengthen native tool-use guidance (#10311)
1 parent 40812dc commit 9b99890

File tree

6 files changed

+153
-32
lines changed

6 files changed

+153
-32
lines changed

src/core/prompts/sections/__tests__/tool-use-guidelines.spec.ts

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { getToolUseGuidelinesSection } from "../tool-use-guidelines"
22
import { TOOL_PROTOCOL } from "@roo-code/types"
3+
import { EXPERIMENT_IDS } from "../../../../shared/experiments"
34

45
describe("getToolUseGuidelinesSection", () => {
56
describe("XML protocol", () => {
@@ -35,30 +36,53 @@ describe("getToolUseGuidelinesSection", () => {
3536
})
3637

3738
describe("native protocol", () => {
38-
it("should include proper numbered guidelines", () => {
39-
const guidelines = getToolUseGuidelinesSection(TOOL_PROTOCOL.NATIVE)
39+
describe("with MULTIPLE_NATIVE_TOOL_CALLS disabled (default)", () => {
40+
it("should include proper numbered guidelines", () => {
41+
const guidelines = getToolUseGuidelinesSection(TOOL_PROTOCOL.NATIVE)
4042

41-
// Check that all numbered items are present with correct numbering
42-
expect(guidelines).toContain("1. Assess what information")
43-
expect(guidelines).toContain("2. Choose the most appropriate tool")
44-
expect(guidelines).toContain("3. If multiple actions are needed")
45-
expect(guidelines).toContain("4. After each tool use")
46-
})
43+
// Check that all numbered items are present with correct numbering
44+
expect(guidelines).toContain("1. Assess what information")
45+
expect(guidelines).toContain("2. Choose the most appropriate tool")
46+
expect(guidelines).toContain("3. If multiple actions are needed")
47+
expect(guidelines).toContain("4. After each tool use")
48+
})
49+
50+
it("should include single-tool-per-message guidance when experiment disabled", () => {
51+
const guidelines = getToolUseGuidelinesSection(TOOL_PROTOCOL.NATIVE, {})
52+
53+
expect(guidelines).toContain("use one tool at a time per message")
54+
expect(guidelines).not.toContain("you may use multiple tools in a single message")
55+
expect(guidelines).not.toContain("Formulate your tool use using the XML format")
56+
expect(guidelines).not.toContain("ALWAYS wait for user confirmation")
57+
})
4758

48-
it("should include native protocol-specific guidelines", () => {
49-
const guidelines = getToolUseGuidelinesSection(TOOL_PROTOCOL.NATIVE)
59+
it("should include simplified iterative process guidelines", () => {
60+
const guidelines = getToolUseGuidelinesSection(TOOL_PROTOCOL.NATIVE)
5061

51-
expect(guidelines).toContain("you may use multiple tools in a single message")
52-
expect(guidelines).not.toContain("Formulate your tool use using the XML format")
53-
expect(guidelines).not.toContain("ALWAYS wait for user confirmation")
62+
expect(guidelines).toContain("carefully considering the user's response after tool executions")
63+
// Native protocol doesn't have the step-by-step list
64+
expect(guidelines).not.toContain("It is crucial to proceed step-by-step")
65+
})
5466
})
5567

56-
it("should include simplified iterative process guidelines", () => {
57-
const guidelines = getToolUseGuidelinesSection(TOOL_PROTOCOL.NATIVE)
68+
describe("with MULTIPLE_NATIVE_TOOL_CALLS enabled", () => {
69+
it("should include multiple-tools-per-message guidance when experiment enabled", () => {
70+
const guidelines = getToolUseGuidelinesSection(TOOL_PROTOCOL.NATIVE, {
71+
[EXPERIMENT_IDS.MULTIPLE_NATIVE_TOOL_CALLS]: true,
72+
})
73+
74+
expect(guidelines).toContain("you may use multiple tools in a single message")
75+
expect(guidelines).not.toContain("use one tool at a time per message")
76+
})
77+
78+
it("should include simplified iterative process guidelines", () => {
79+
const guidelines = getToolUseGuidelinesSection(TOOL_PROTOCOL.NATIVE, {
80+
[EXPERIMENT_IDS.MULTIPLE_NATIVE_TOOL_CALLS]: true,
81+
})
5882

59-
expect(guidelines).toContain("carefully considering the user's response after tool executions")
60-
// Native protocol doesn't have the step-by-step list
61-
expect(guidelines).not.toContain("It is crucial to proceed step-by-step")
83+
expect(guidelines).toContain("carefully considering the user's response after tool executions")
84+
expect(guidelines).not.toContain("It is crucial to proceed step-by-step")
85+
})
6286
})
6387
})
6488

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { getSharedToolUseSection } from "../tool-use"
2+
import { TOOL_PROTOCOL } from "@roo-code/types"
3+
4+
describe("getSharedToolUseSection", () => {
5+
describe("XML protocol", () => {
6+
it("should include one tool per message requirement", () => {
7+
const section = getSharedToolUseSection(TOOL_PROTOCOL.XML)
8+
9+
expect(section).toContain("You must use exactly one tool per message")
10+
expect(section).toContain("every assistant message must include a tool call")
11+
})
12+
13+
it("should include XML formatting instructions", () => {
14+
const section = getSharedToolUseSection(TOOL_PROTOCOL.XML)
15+
16+
expect(section).toContain("XML-style tags")
17+
expect(section).toContain("Always use the actual tool name as the XML tag name")
18+
})
19+
})
20+
21+
describe("native protocol", () => {
22+
it("should include one tool per message requirement when experiment is disabled", () => {
23+
// No experiment flags passed (default: disabled)
24+
const section = getSharedToolUseSection(TOOL_PROTOCOL.NATIVE)
25+
26+
expect(section).toContain("You must use exactly one tool call per assistant response")
27+
expect(section).toContain("Do not call zero tools or more than one tool")
28+
})
29+
30+
it("should include one tool per message requirement when experiment is explicitly disabled", () => {
31+
const section = getSharedToolUseSection(TOOL_PROTOCOL.NATIVE, { multipleNativeToolCalls: false })
32+
33+
expect(section).toContain("You must use exactly one tool call per assistant response")
34+
expect(section).toContain("Do not call zero tools or more than one tool")
35+
})
36+
37+
it("should NOT include one tool per message requirement when experiment is enabled", () => {
38+
const section = getSharedToolUseSection(TOOL_PROTOCOL.NATIVE, { multipleNativeToolCalls: true })
39+
40+
expect(section).not.toContain("You must use exactly one tool per message")
41+
expect(section).not.toContain("every assistant message must include a tool call")
42+
expect(section).toContain("You must call at least one tool per assistant response")
43+
expect(section).toContain("Prefer calling as many tools as are reasonably needed")
44+
})
45+
46+
it("should include native tool-calling instructions", () => {
47+
const section = getSharedToolUseSection(TOOL_PROTOCOL.NATIVE)
48+
49+
expect(section).toContain("provider-native tool-calling mechanism")
50+
expect(section).toContain("Do not include XML markup or examples")
51+
})
52+
53+
it("should NOT include XML formatting instructions", () => {
54+
const section = getSharedToolUseSection(TOOL_PROTOCOL.NATIVE)
55+
56+
expect(section).not.toContain("XML-style tags")
57+
expect(section).not.toContain("Always use the actual tool name as the XML tag name")
58+
})
59+
})
60+
61+
describe("default protocol", () => {
62+
it("should default to XML protocol when no protocol is specified", () => {
63+
const section = getSharedToolUseSection()
64+
65+
expect(section).toContain("XML-style tags")
66+
expect(section).toContain("You must use exactly one tool per message")
67+
})
68+
})
69+
})

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

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { ToolProtocol, TOOL_PROTOCOL } from "@roo-code/types"
22
import { isNativeProtocol } from "@roo-code/types"
33

4-
export function getToolUseGuidelinesSection(protocol: ToolProtocol = TOOL_PROTOCOL.XML): string {
4+
import { experiments, EXPERIMENT_IDS } from "../../../shared/experiments"
5+
6+
export function getToolUseGuidelinesSection(
7+
protocol: ToolProtocol = TOOL_PROTOCOL.XML,
8+
experimentFlags?: Record<string, boolean>,
9+
): string {
510
// Build guidelines array with automatic numbering
611
let itemNumber = 1
712
const guidelinesList: string[] = []
@@ -17,9 +22,21 @@ export function getToolUseGuidelinesSection(protocol: ToolProtocol = TOOL_PROTOC
1722

1823
// Remaining guidelines - different for native vs XML protocol
1924
if (isNativeProtocol(protocol)) {
20-
guidelinesList.push(
21-
`${itemNumber++}. If multiple actions are needed, you may use multiple tools in a single message when appropriate, or use tools iteratively across messages. Each tool use should be informed by the results of previous tool uses. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.`,
25+
// Check if multiple native tool calls is enabled via experiment
26+
const isMultipleNativeToolCallsEnabled = experiments.isEnabled(
27+
experimentFlags ?? {},
28+
EXPERIMENT_IDS.MULTIPLE_NATIVE_TOOL_CALLS,
2229
)
30+
31+
if (isMultipleNativeToolCallsEnabled) {
32+
guidelinesList.push(
33+
`${itemNumber++}. If multiple actions are needed, you may use multiple tools in a single message when appropriate, or use tools iteratively across messages. Each tool use should be informed by the results of previous tool uses. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.`,
34+
)
35+
} else {
36+
guidelinesList.push(
37+
`${itemNumber++}. If multiple actions are needed, use one tool at a time per message to accomplish the task iteratively, with each tool use being informed by the result of the previous tool use. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.`,
38+
)
39+
}
2340
} else {
2441
guidelinesList.push(
2542
`${itemNumber++}. If multiple actions are needed, use one tool at a time per message to accomplish the task iteratively, with each tool use being informed by the result of the previous tool use. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.`,

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

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,27 @@
11
import { ToolProtocol, TOOL_PROTOCOL, isNativeProtocol } from "@roo-code/types"
22

3-
export function getSharedToolUseSection(protocol: ToolProtocol = TOOL_PROTOCOL.XML): string {
3+
import { experiments, EXPERIMENT_IDS } from "../../../shared/experiments"
4+
5+
export function getSharedToolUseSection(
6+
protocol: ToolProtocol = TOOL_PROTOCOL.XML,
7+
experimentFlags?: Record<string, boolean>,
8+
): string {
49
if (isNativeProtocol(protocol)) {
10+
// Check if multiple native tool calls is enabled via experiment
11+
const isMultipleNativeToolCallsEnabled = experiments.isEnabled(
12+
experimentFlags ?? {},
13+
EXPERIMENT_IDS.MULTIPLE_NATIVE_TOOL_CALLS,
14+
)
15+
16+
const toolUseGuidance = isMultipleNativeToolCallsEnabled
17+
? " You must call at least one tool per assistant response. Prefer calling as many tools as are reasonably needed in a single response to reduce back-and-forth and complete tasks faster."
18+
: " You must use exactly one tool call per assistant response. Do not call zero tools or more than one tool in the same response."
19+
520
return `====
621
722
TOOL USE
823
9-
You have access to a set of tools that are executed upon the user's approval. Use the provider-native tool-calling mechanism. Do not include XML markup or examples.`
24+
You have access to a set of tools that are executed upon the user's approval. Use the provider-native tool-calling mechanism. Do not include XML markup or examples.${toolUseGuidance}`
1025
}
1126

1227
return `====

src/core/prompts/system.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,9 @@ async function generatePrompt(
138138
139139
${markdownFormattingSection()}
140140
141-
${getSharedToolUseSection(effectiveProtocol)}${toolsCatalog}
141+
${getSharedToolUseSection(effectiveProtocol, experiments)}${toolsCatalog}
142142
143-
${getToolUseGuidelinesSection(effectiveProtocol)}
143+
${getToolUseGuidelinesSection(effectiveProtocol, experiments)}
144144
145145
${mcpServersSection}
146146

src/core/prompts/tools/native-tools/write_to_file.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,16 @@ const WRITE_TO_FILE_DESCRIPTION = `Request to write content to a file. This tool
44
55
**Important:** You should prefer using other editing tools over write_to_file when making changes to existing files, since write_to_file is slower and cannot handle large files. Use write_to_file primarily for new file creation.
66
7-
When using this tool, use it directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code.
7+
When using this tool, use it directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. Failure to do so will result in incomplete or broken code.
88
99
When creating a new project, organize all new files within a dedicated project directory unless the user specifies otherwise. Structure the project logically, adhering to best practices for the specific type of project being created.
1010
11-
Parameters:
12-
- path: (required) The path of the file to write to (relative to the current workspace directory)
13-
- content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include line numbers in the content.
14-
1511
Example: Writing a configuration file
1612
{ "path": "frontend-config.json", "content": "{\\n \\"apiEndpoint\\": \\"https://api.example.com\\",\\n \\"theme\\": {\\n \\"primaryColor\\": \\"#007bff\\"\\n }\\n}" }`
1713

18-
const PATH_PARAMETER_DESCRIPTION = `Path to the file to write, relative to the workspace`
14+
const PATH_PARAMETER_DESCRIPTION = `The path of the file to write to (relative to the current workspace directory)`
1915

20-
const CONTENT_PARAMETER_DESCRIPTION = `Full contents that the file should contain with no omissions or line numbers`
16+
const CONTENT_PARAMETER_DESCRIPTION = `The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include line numbers in the content.`
2117

2218
export default {
2319
type: "function",

0 commit comments

Comments
 (0)