feat: refactor bulky tools to improve token efficiency

This commit is contained in:
Aishanee Shah
2026-02-17 05:11:04 +00:00
parent b38e0984b9
commit aff5da63cb
11 changed files with 80 additions and 21 deletions

View File

@@ -2440,7 +2440,7 @@ export class Config {
);
if (this.getUseWriteTodos()) {
maybeRegister(WriteTodosTool, () =>
registry.registerTool(new WriteTodosTool(this.messageBus)),
registry.registerTool(new WriteTodosTool(this, this.messageBus)),
);
}
if (this.isPlanEnabled()) {

View File

@@ -63,6 +63,16 @@ export class PromptProvider {
const activeSnippets = isModernModel ? snippets : legacySnippets;
const contextFilenames = getAllGeminiMdFilenames();
const registry =
typeof config.getToolRegistry === 'function'
? config.getToolRegistry()
: undefined;
const allTools =
typeof registry?.getAllTools === 'function' ? registry.getAllTools() : [];
const toolInstructions = allTools
.map((t) => t.instructions)
.filter((i): i is string => !!i);
// --- Context Gathering ---
let planModeToolsList = PLAN_MODE_TOOLS.filter((t) =>
enabledToolNames.has(t),
@@ -201,6 +211,8 @@ export class PromptProvider {
: this.withSection('finalReminder', () => ({
readFileToolName: READ_FILE_TOOL_NAME,
})),
toolInstructions:
toolInstructions.length > 0 ? toolInstructions : undefined,
} as snippets.SystemPromptOptions;
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion

View File

@@ -35,6 +35,7 @@ export interface SystemPromptOptions {
interactiveYoloMode?: boolean;
gitRepo?: GitRepoOptions;
finalReminder?: FinalReminderOptions;
toolInstructions?: string[];
}
export interface PreambleOptions {
@@ -113,6 +114,8 @@ ${
: renderPrimaryWorkflows(options.primaryWorkflows)
}
${renderToolInstructions(options.toolInstructions)}
${renderOperationalGuidelines(options.operationalGuidelines)}
${renderInteractiveYoloMode(options.interactiveYoloMode)}
@@ -244,6 +247,15 @@ ${newApplicationSteps(options)}
`.trim();
}
export function renderToolInstructions(instructions?: string[]): string {
if (!instructions || instructions.length === 0) return '';
return `
# Detailed Tool Reference
${instructions.join('\n\n')}
`.trim();
}
export function renderOperationalGuidelines(
options?: OperationalGuidelinesOptions,
): string {

View File

@@ -35,6 +35,7 @@ export interface SystemPromptOptions {
sandbox?: SandboxMode;
interactiveYoloMode?: boolean;
gitRepo?: GitRepoOptions;
toolInstructions?: string[];
}
export interface PreambleOptions {
@@ -110,6 +111,8 @@ ${
: renderPrimaryWorkflows(options.primaryWorkflows)
}
${renderToolInstructions(options.toolInstructions)}
${renderOperationalGuidelines(options.operationalGuidelines)}
${renderInteractiveYoloMode(options.interactiveYoloMode)}
@@ -268,6 +271,15 @@ ${newApplicationSteps(options)}
`.trim();
}
export function renderToolInstructions(instructions?: string[]): string {
if (!instructions || instructions.length === 0) return '';
return `
# Detailed Tool Reference
${instructions.join('\n\n')}
`.trim();
}
export function renderOperationalGuidelines(
options?: OperationalGuidelinesOptions,
): string {

View File

@@ -506,8 +506,8 @@ exports[`coreTools snapshots for specific models > Model: gemini-2.5-pro > snaps
{
"description": "Replaces text within a file. By default, replaces a single occurrence, but can replace multiple occurrences when \`expected_replacements\` is specified. This tool requires providing significant context around the change to ensure precise targeting. Always use the read_file tool to examine the file's current content before attempting a text replacement.
The user has the ability to modify the \`new_string\` content. If modified, this will be stated in the response.
The user has the ability to modify the \`new_string\` content. If modified, this will be stated in the response.",
"instructions": "
Expectation for required parameters:
1. \`old_string\` MUST be the exact literal text to replace (including all whitespace, indentation, newlines, and surrounding code etc.).
2. \`new_string\` MUST be the exact literal text to replace \`old_string\` with (also including all whitespace, indentation, newlines, and surrounding code etc.). Ensure the resulting code is correct and idiomatic and that \`old_string\` and \`new_string\` are different.
@@ -684,8 +684,8 @@ exports[`coreTools snapshots for specific models > Model: gemini-2.5-pro > snaps
exports[`coreTools snapshots for specific models > Model: gemini-2.5-pro > snapshot for tool: write_todos 1`] = `
{
"description": "This tool can help you list out the current subtasks that are required to be completed for a given user request. The list of subtasks helps you keep track of the current task, organize complex queries and help ensure that you don't miss any steps. With this list, the user can also see the current progress you are making in executing a given task.
"description": "This tool can help you list out the current subtasks that are required to be completed for a given user request. The list of subtasks helps you keep track of the current task, organize complex queries and help ensure that you don't miss any steps. With this list, the user can also see the current progress you are making in executing a given task.",
"instructions": "
Depending on the task complexity, you should first divide a given task into subtasks and then use this tool to list out the subtasks that are required to be completed for a given user request.
Each of the subtasks should be clear and distinct.
@@ -1295,8 +1295,8 @@ exports[`coreTools snapshots for specific models > Model: gemini-3-pro-preview >
{
"description": "Replaces text within a file. By default, replaces a single occurrence, but can replace multiple occurrences when \`expected_replacements\` is specified. This tool requires providing significant context around the change to ensure precise targeting. Always use the read_file tool to examine the file's current content before attempting a text replacement.
The user has the ability to modify the \`new_string\` content. If modified, this will be stated in the response.
The user has the ability to modify the \`new_string\` content. If modified, this will be stated in the response.",
"instructions": "
Expectation for required parameters:
1. \`old_string\` MUST be the exact literal text to replace (including all whitespace, indentation, newlines, and surrounding code etc.).
2. \`new_string\` MUST be the exact literal text to replace \`old_string\` with (also including all whitespace, indentation, newlines, and surrounding code etc.). Ensure the resulting code is correct and idiomatic and that \`old_string\` and \`new_string\` are different.
@@ -1473,8 +1473,8 @@ exports[`coreTools snapshots for specific models > Model: gemini-3-pro-preview >
exports[`coreTools snapshots for specific models > Model: gemini-3-pro-preview > snapshot for tool: write_todos 1`] = `
{
"description": "This tool can help you list out the current subtasks that are required to be completed for a given user request. The list of subtasks helps you keep track of the current task, organize complex queries and help ensure that you don't miss any steps. With this list, the user can also see the current progress you are making in executing a given task.
"description": "This tool can help you list out the current subtasks that are required to be completed for a given user request. The list of subtasks helps you keep track of the current task, organize complex queries and help ensure that you don't miss any steps. With this list, the user can also see the current progress you are making in executing a given task.",
"instructions": "
Depending on the task complexity, you should first divide a given task into subtasks and then use this tool to list out the subtasks that are required to be completed for a given user request.
Each of the subtasks should be clear and distinct.

View File

@@ -269,8 +269,8 @@ export const EDIT_DEFINITION: ToolDefinition = {
name: EDIT_TOOL_NAME,
description: `Replaces text within a file. By default, replaces a single occurrence, but can replace multiple occurrences when \`expected_replacements\` is specified. This tool requires providing significant context around the change to ensure precise targeting. Always use the ${READ_FILE_TOOL_NAME} tool to examine the file's current content before attempting a text replacement.
The user has the ability to modify the \`new_string\` content. If modified, this will be stated in the response.
The user has the ability to modify the \`new_string\` content. If modified, this will be stated in the response.`,
instructions: `
Expectation for required parameters:
1. \`old_string\` MUST be the exact literal text to replace (including all whitespace, indentation, newlines, and surrounding code etc.).
2. \`new_string\` MUST be the exact literal text to replace \`old_string\` with (also including all whitespace, indentation, newlines, and surrounding code etc.). Ensure the resulting code is correct and idiomatic and that \`old_string\` and \`new_string\` are different.
@@ -646,8 +646,8 @@ NEVER save workspace-specific context, local paths, or commands (e.g. "The entry
export const WRITE_TODOS_DEFINITION: ToolDefinition = {
base: {
name: WRITE_TODOS_TOOL_NAME,
description: `This tool can help you list out the current subtasks that are required to be completed for a given user request. The list of subtasks helps you keep track of the current task, organize complex queries and help ensure that you don't miss any steps. With this list, the user can also see the current progress you are making in executing a given task.
description: `This tool can help you list out the current subtasks that are required to be completed for a given user request. The list of subtasks helps you keep track of the current task, organize complex queries and help ensure that you don't miss any steps. With this list, the user can also see the current progress you are making in executing a given task.`,
instructions: `
Depending on the task complexity, you should first divide a given task into subtasks and then use this tool to list out the subtasks that are required to be completed for a given user request.
Each of the subtasks should be clear and distinct.

View File

@@ -17,7 +17,7 @@ import type { ToolDefinition } from './types.js';
export function resolveToolDeclaration(
definition: ToolDefinition,
modelId?: string,
): FunctionDeclaration {
): FunctionDeclaration & { instructions?: string } {
if (!modelId || !definition.overrides) {
return definition.base;
}

View File

@@ -11,10 +11,12 @@ import { type FunctionDeclaration } from '@google/genai';
*/
export interface ToolDefinition {
/** The base declaration for the tool. */
base: FunctionDeclaration;
base: FunctionDeclaration & { instructions?: string };
/**
* Optional overrides for specific model families or versions.
*/
overrides?: (modelId: string) => Partial<FunctionDeclaration> | undefined;
overrides?: (
modelId: string,
) => (Partial<FunctionDeclaration> & { instructions?: string }) | undefined;
}

View File

@@ -910,15 +910,23 @@ export class EditTool
private readonly config: Config,
messageBus: MessageBus,
) {
const modelId =
typeof config.getActiveModel === 'function'
? config.getActiveModel()
: undefined;
const resolved = resolveToolDeclaration(EDIT_DEFINITION, modelId);
super(
EditTool.Name,
EDIT_DISPLAY_NAME,
EDIT_DEFINITION.base.description!,
resolved.description!,
Kind.Edit,
EDIT_DEFINITION.base.parametersJsonSchema,
resolved.parametersJsonSchema,
messageBus,
true, // isOutputMarkdown
false, // canUpdateOutput
undefined, // extensionName
undefined, // extensionId
resolved.instructions,
);
}

View File

@@ -361,6 +361,7 @@ export abstract class DeclarativeTool<
readonly canUpdateOutput: boolean = false,
readonly extensionName?: string,
readonly extensionId?: string,
readonly instructions?: string,
) {}
getSchema(_modelId?: string): FunctionDeclaration {

View File

@@ -12,6 +12,7 @@ import {
type Todo,
type ToolResult,
} from './tools.js';
import type { Config } from '../config/config.js';
import type { MessageBus } from '../confirmation-bus/message-bus.js';
import { WRITE_TODOS_TOOL_NAME } from './tool-names.js';
import { WRITE_TODOS_DEFINITION } from './definitions/coreTools.js';
@@ -81,16 +82,27 @@ export class WriteTodosTool extends BaseDeclarativeTool<
> {
static readonly Name = WRITE_TODOS_TOOL_NAME;
constructor(messageBus: MessageBus) {
constructor(
private readonly config: Config,
messageBus: MessageBus,
) {
const modelId =
typeof config.getActiveModel === 'function'
? config.getActiveModel()
: undefined;
const resolved = resolveToolDeclaration(WRITE_TODOS_DEFINITION, modelId);
super(
WriteTodosTool.Name,
'WriteTodos',
WRITE_TODOS_DEFINITION.base.description!,
resolved.description!,
Kind.Other,
WRITE_TODOS_DEFINITION.base.parametersJsonSchema,
resolved.parametersJsonSchema,
messageBus,
true, // isOutputMarkdown
false, // canUpdateOutput
undefined, // extensionName
undefined, // extensionId
resolved.instructions,
);
}