feat: add multi-tool calls experiment

- Add multiToolCalls to ExperimentId type
- Update experiments schema and configuration
- Add MULTI_TOOL_CALLS constant to shared experiments
- Update tool-use prompt to conditionally support multi-tool calls
- Add i18n translations for EN and zh-CN
- Update test fixtures to include multiToolCalls property

Implements #9288
This commit is contained in:
Roo Code 2025-11-16 01:54:16 +00:00
parent 744f4bd4c8
commit a64af0ec6d
8 changed files with 29 additions and 3 deletions

View file

@ -12,6 +12,7 @@ export const experimentIds = [
"preventFocusDisruption",
"imageGeneration",
"runSlashCommand",
"multiToolCalls",
] as const
export const experimentIdsSchema = z.enum(experimentIds)
@ -28,6 +29,7 @@ export const experimentsSchema = z.object({
preventFocusDisruption: z.boolean().optional(),
imageGeneration: z.boolean().optional(),
runSlashCommand: z.boolean().optional(),
multiToolCalls: z.boolean().optional(),
})
export type Experiments = z.infer<typeof experimentsSchema>

View file

@ -1,6 +1,9 @@
import { ToolProtocol, TOOL_PROTOCOL, isNativeProtocol } from "@roo-code/types"
export function getSharedToolUseSection(protocol: ToolProtocol = TOOL_PROTOCOL.XML): string {
export function getSharedToolUseSection(
protocol: ToolProtocol = TOOL_PROTOCOL.XML,
experiments?: Record<string, boolean>,
): string {
if (isNativeProtocol(protocol)) {
return `====
@ -9,11 +12,17 @@ TOOL USE
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.`
}
const multiToolCallsEnabled = experiments?.multiToolCalls === true
const toolUsageGuidance = multiToolCallsEnabled
? `You have access to a set of tools that are executed upon the user's approval. You can use multiple tools per message when appropriate, especially for independent, low-risk operations like reading files or searching code. Every assistant message must include at least one tool call. When using multiple tools, batch independent operations (like multiple file reads or searches) to reduce round trips and improve efficiency.`
: `You have access to a set of tools that are executed upon the user's approval. You must use exactly one tool per message, and every assistant message must include a tool call. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.`
return `====
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You must use exactly one tool per message, and every assistant message must include a tool call. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
${toolUsageGuidance}
# Tool Use Formatting

View file

@ -120,7 +120,7 @@ async function generatePrompt(
${markdownFormattingSection()}
${getSharedToolUseSection(effectiveProtocol)}${toolsCatalog}
${getSharedToolUseSection(effectiveProtocol, experiments)}${toolsCatalog}
${getToolUseGuidelinesSection(codeIndexManager, effectiveProtocol)}

View file

@ -31,6 +31,7 @@ describe("experiments", () => {
preventFocusDisruption: false,
imageGeneration: false,
runSlashCommand: false,
multiToolCalls: false,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
})
@ -42,6 +43,7 @@ describe("experiments", () => {
preventFocusDisruption: false,
imageGeneration: false,
runSlashCommand: false,
multiToolCalls: false,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(true)
})
@ -53,6 +55,7 @@ describe("experiments", () => {
preventFocusDisruption: false,
imageGeneration: false,
runSlashCommand: false,
multiToolCalls: false,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
})

View file

@ -6,6 +6,7 @@ export const EXPERIMENT_IDS = {
PREVENT_FOCUS_DISRUPTION: "preventFocusDisruption",
IMAGE_GENERATION: "imageGeneration",
RUN_SLASH_COMMAND: "runSlashCommand",
MULTI_TOOL_CALLS: "multiToolCalls",
} as const satisfies Record<string, ExperimentId>
type _AssertExperimentIds = AssertEqual<Equals<ExperimentId, Values<typeof EXPERIMENT_IDS>>>
@ -22,6 +23,7 @@ export const experimentConfigsMap: Record<ExperimentKey, ExperimentConfig> = {
PREVENT_FOCUS_DISRUPTION: { enabled: false },
IMAGE_GENERATION: { enabled: false },
RUN_SLASH_COMMAND: { enabled: false },
MULTI_TOOL_CALLS: { enabled: false },
}
export const experimentDefault = Object.fromEntries(

View file

@ -237,6 +237,7 @@ describe("mergeExtensionState", () => {
newTaskRequireTodos: false,
imageGeneration: false,
runSlashCommand: false,
multiToolCalls: false,
} as Record<ExperimentId, boolean>,
checkpointTimeout: DEFAULT_CHECKPOINT_TIMEOUT_SECONDS + 5,
}
@ -258,6 +259,7 @@ describe("mergeExtensionState", () => {
newTaskRequireTodos: false,
imageGeneration: false,
runSlashCommand: false,
multiToolCalls: false,
})
})
})

View file

@ -776,6 +776,10 @@
"RUN_SLASH_COMMAND": {
"name": "Enable model-initiated slash commands",
"description": "When enabled, Roo can run your slash commands to execute workflows."
},
"MULTI_TOOL_CALLS": {
"name": "Enable multi-tool calls",
"description": "When enabled, Roo can issue multiple tool calls in a single message, especially for independent operations like file reads and searches. This reduces round trips and improves efficiency while maintaining safety for stateful operations."
}
},
"promptCaching": {

View file

@ -772,6 +772,10 @@
"RUN_SLASH_COMMAND": {
"name": "启用模型发起的斜杠命令",
"description": "启用后 Roo 可运行斜杠命令执行工作流程。"
},
"MULTI_TOOL_CALLS": {
"name": "启用多工具调用",
"description": "启用后Roo 可以在单个消息中发出多个工具调用,特别是对于文件读取和搜索等独立操作。这减少了往返次数并提高了效率,同时保持了状态操作的安全性。"
}
},
"promptCaching": {