mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-06-03 15:43:52 -07:00
feat(core): enforce granular, single-focus Topic Model with detailed summaries
- Mandate granular, single-focus topics (e.g., 'Researching', 'Implementing [Idea]') to prevent broad, combined topics. - Introduce required 'current_topic_summary' and 'previous_topic_summary' (on transition) parameters to the create_new_topic tool. - Require 5-10 sentence detailed summaries for both parameters to improve context injection and strategic alignment. - Mandate creating a new topic if work deviates from the current topic's stated goal. - Relax silent-mode restrictions to allow Strategic Narration at the start of chapters for better transparency. - Update tool declarations, system prompts, and unit tests to reflect these changes.
This commit is contained in:
@@ -11,6 +11,8 @@ import {
|
||||
ENTER_PLAN_MODE_TOOL_NAME,
|
||||
EXIT_PLAN_MODE_TOOL_NAME,
|
||||
CREATE_NEW_TOPIC_TOOL_NAME,
|
||||
TOPIC_PARAM_PREVIOUS_SUMMARY,
|
||||
TOPIC_PARAM_CURRENT_SUMMARY,
|
||||
GLOB_TOOL_NAME,
|
||||
GREP_TOOL_NAME,
|
||||
MEMORY_TOOL_NAME,
|
||||
@@ -351,7 +353,7 @@ export function renderOperationalGuidelines(
|
||||
- **Role:** A senior software engineer and collaborative peer programmer.
|
||||
- **High-Signal Output:** Focus exclusively on **intent** and **technical rationale**. Avoid conversational filler, apologies, and ${
|
||||
options.topicUpdateNarration
|
||||
? 'per-tool explanations.'
|
||||
? 'unnecessary per-tool explanations.'
|
||||
: 'mechanical tool-use narration (e.g., "I will now call...").'
|
||||
}
|
||||
- **Concise & Direct:** Adopt a professional, direct, and concise tone suitable for a CLI environment.
|
||||
@@ -583,18 +585,20 @@ function mandateTopicUpdateModel(): string {
|
||||
|
||||
- **1. Chapter Initialization:**
|
||||
- **The Trigger:** You MUST call \`${CREATE_NEW_TOPIC_TOOL_NAME}\` ONLY when beginning a new task or when the broad logical nature of your work changes (e.g., transitioning from Research to Strategy, or from Strategy to Implementation).
|
||||
- **The Format:** Provide a concise, high-level title for the chapter (e.g., \`create_new_topic(title="Researching Agent Skills")\`).
|
||||
- **Start of Task:** Your very first tool execution in a new session must be \`${CREATE_NEW_TOPIC_TOOL_NAME}\`.
|
||||
- **Granularity:** You MUST NOT combine topics (e.g., do NOT use "Researching and Implementing"). Use granular, single-focus titles like "Researching [Task]", "Strategy for [Task]", or "Implementing [Specific Idea]".
|
||||
- **Deviations:** If your work deviates from the current topic's stated goal, you MUST call \`${CREATE_NEW_TOPIC_TOOL_NAME}\` again with a new title (e.g., "Implementing [New Idea]") to reflect the shift.
|
||||
- **The Format:** Provide a concise, high-level title for the chapter. You MUST also provide a detailed explanation (5-10 sentences) of what the new topic intends to achieve in the \`${TOPIC_PARAM_CURRENT_SUMMARY}\` parameter. When shifting topics, you MUST additionally provide a detailed summary (5-10 sentences) of the work completed in the previous topic in the \`${TOPIC_PARAM_PREVIOUS_SUMMARY}\` parameter.
|
||||
- **Example:** \`create_new_topic(title="Implementing Auth", ${TOPIC_PARAM_CURRENT_SUMMARY}="Implement the OAuth2 flow and link it to the existing user session. This involves creating new routes for the callback and state management for the token. We will also need to update the middleware to handle authenticated sessions correctly. This will ensure that all subsequent API calls are properly authorized and user-specific. The implementation will follow the project's security standards and include unit tests for each new component.", ${TOPIC_PARAM_PREVIOUS_SUMMARY}="Completed research on OAuth2 and mapped endpoints. We identified the necessary client libraries and configured the initial developer credentials for the authentication provider. A prototype script was written to verify the handshake process with the API. The existing user database schema was reviewed and found to be compatible with the new OAuth IDs. We also finalized the design of the new login UI and obtained user feedback on the mockups.")\`
|
||||
- **Start of Task:** Your very first tool execution in a new session must be \`${CREATE_NEW_TOPIC_TOOL_NAME}\`. For the first topic, leave \`${TOPIC_PARAM_PREVIOUS_SUMMARY}\` empty but you MUST still provide \`${TOPIC_PARAM_CURRENT_SUMMARY}\`.
|
||||
|
||||
- **2. Zero-Noise Execution:**
|
||||
- **2. Strategic Narration:**
|
||||
- **Intent:** At the start of each chapter, or before a significant sequence of tools, you SHOULD provide a concise, one-sentence statement of your intent or strategy.
|
||||
- **No Text Headers:** You are FORBIDDEN from printing "Topic: <Phase>" or any similar text-based headers in your response. The tool handles all UI narration.
|
||||
- **Silent Mode:** No conversational filler, no "I will now...", and no summaries between tools.
|
||||
- Only the tool execution is permitted to define the state. Everything in between must be silent.
|
||||
- **Minimal Noise:** Avoid conversational filler and apologies. While executing tools within a chapter, maintain a high signal-to-noise ratio; brief explanations are encouraged if they provide essential context or clarify your strategy.
|
||||
|
||||
- **3. Internal Reasoning:**
|
||||
- You MUST reason about your plan, track tool calls, and strategize internally before executing tools.
|
||||
- This reasoning process must remain internal. You are strictly FORBIDDEN from including your reasoning, "thoughts," or explanations in your text response.
|
||||
- Between tool calls, your text output MUST remain completely empty (Zero-Noise).
|
||||
- While your deep reasoning should remain internal, you are encouraged to share your high-level strategy and key findings in your text responses to maintain alignment and improve task performance.
|
||||
|
||||
- **4. Completion:**
|
||||
- Only when the entire task is finalized do you provide a **Final Summary**.
|
||||
|
||||
@@ -126,3 +126,5 @@ export const PLAN_MODE_PARAM_REASON = 'reason';
|
||||
// -- create_new_topic --
|
||||
export const CREATE_NEW_TOPIC_TOOL_NAME = 'create_new_topic';
|
||||
export const TOPIC_PARAM_TITLE = 'title';
|
||||
export const TOPIC_PARAM_PREVIOUS_SUMMARY = 'previous_topic_summary';
|
||||
export const TOPIC_PARAM_CURRENT_SUMMARY = 'current_topic_summary';
|
||||
|
||||
@@ -92,6 +92,8 @@ import {
|
||||
SKILL_PARAM_NAME,
|
||||
CREATE_NEW_TOPIC_TOOL_NAME as CREATE_NEW_TOPIC_TOOL_NAME_BASE,
|
||||
TOPIC_PARAM_TITLE,
|
||||
TOPIC_PARAM_PREVIOUS_SUMMARY,
|
||||
TOPIC_PARAM_CURRENT_SUMMARY,
|
||||
} from './base-declarations.js';
|
||||
|
||||
export {
|
||||
@@ -167,6 +169,8 @@ export {
|
||||
EXIT_PLAN_PARAM_PLAN_PATH,
|
||||
SKILL_PARAM_NAME,
|
||||
TOPIC_PARAM_TITLE,
|
||||
TOPIC_PARAM_PREVIOUS_SUMMARY,
|
||||
TOPIC_PARAM_CURRENT_SUMMARY,
|
||||
};
|
||||
|
||||
// Re-export sets for compatibility
|
||||
|
||||
@@ -25,6 +25,8 @@ import {
|
||||
SKILL_PARAM_NAME,
|
||||
CREATE_NEW_TOPIC_TOOL_NAME,
|
||||
TOPIC_PARAM_TITLE,
|
||||
TOPIC_PARAM_PREVIOUS_SUMMARY,
|
||||
TOPIC_PARAM_CURRENT_SUMMARY,
|
||||
} from './base-declarations.js';
|
||||
|
||||
/**
|
||||
@@ -181,7 +183,7 @@ export function getCreateNewTopicDeclaration(): FunctionDeclaration {
|
||||
return {
|
||||
name: CREATE_NEW_TOPIC_TOOL_NAME,
|
||||
description:
|
||||
'Organizes work into a new "Chapter" or "Topic". Call this when transitioning between major phases (e.g., from Research to Implementation).',
|
||||
'Organizes work into a granular, single-focus "Chapter" or "Topic". You MUST NOT combine distinct phases (e.g., do NOT use "Researching and Implementing"). Use specific, singular titles like "Researching", "Planning", or "Implementing [Specific Idea]". If your work deviates from the current topic\'s stated goal, you MUST create a new topic to reflect the shift (e.g., "Implementing [New Idea]"). When shifting topics, you MUST provide a detailed summary (5-10 sentences) of the work completed in the previous topic and a clear statement of what the new topic aims to achieve.',
|
||||
parametersJsonSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@@ -189,8 +191,18 @@ export function getCreateNewTopicDeclaration(): FunctionDeclaration {
|
||||
type: 'string',
|
||||
description: 'The title of the new topic or chapter.',
|
||||
},
|
||||
[TOPIC_PARAM_PREVIOUS_SUMMARY]: {
|
||||
type: 'string',
|
||||
description:
|
||||
'(OPTIONAL) A detailed summary (5-10 sentences) of the work completed in the previous topic. This is required when transitioning between topics to maintain continuity.',
|
||||
},
|
||||
[TOPIC_PARAM_CURRENT_SUMMARY]: {
|
||||
type: 'string',
|
||||
description:
|
||||
'A detailed explanation (5-10 sentences) of what the new topic intends to achieve. This is mandatory for all new topics to provide clear strategic intent.',
|
||||
},
|
||||
},
|
||||
required: [TOPIC_PARAM_TITLE],
|
||||
required: [TOPIC_PARAM_TITLE, TOPIC_PARAM_CURRENT_SUMMARY],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -77,6 +77,8 @@ import {
|
||||
EXIT_PLAN_PARAM_PLAN_PATH,
|
||||
SKILL_PARAM_NAME,
|
||||
TOPIC_PARAM_TITLE,
|
||||
TOPIC_PARAM_PREVIOUS_SUMMARY,
|
||||
TOPIC_PARAM_CURRENT_SUMMARY,
|
||||
} from './definitions/coreTools.js';
|
||||
|
||||
export {
|
||||
@@ -152,6 +154,8 @@ export {
|
||||
EXIT_PLAN_PARAM_PLAN_PATH,
|
||||
SKILL_PARAM_NAME,
|
||||
TOPIC_PARAM_TITLE,
|
||||
TOPIC_PARAM_PREVIOUS_SUMMARY,
|
||||
TOPIC_PARAM_CURRENT_SUMMARY,
|
||||
};
|
||||
|
||||
export const LS_TOOL_NAME_LEGACY = 'list_directory'; // Just to be safe if anything used the old exported name directly
|
||||
|
||||
@@ -11,6 +11,8 @@ import type { PolicyEngine } from '../policy/policy-engine.js';
|
||||
import {
|
||||
CREATE_NEW_TOPIC_TOOL_NAME,
|
||||
TOPIC_PARAM_TITLE,
|
||||
TOPIC_PARAM_PREVIOUS_SUMMARY,
|
||||
TOPIC_PARAM_CURRENT_SUMMARY,
|
||||
} from './definitions/base-declarations.js';
|
||||
import type { Config } from '../config/config.js';
|
||||
|
||||
@@ -79,16 +81,37 @@ describe('CreateNewTopicTool', () => {
|
||||
expect(tool.displayName).toBe('Create New Topic');
|
||||
});
|
||||
|
||||
it('should update TopicState on execute', async () => {
|
||||
const invocation = tool.build({ [TOPIC_PARAM_TITLE]: 'New Chapter' });
|
||||
it('should update TopicState and include current goal on execute', async () => {
|
||||
const invocation = tool.build({
|
||||
[TOPIC_PARAM_TITLE]: 'New Chapter',
|
||||
[TOPIC_PARAM_CURRENT_SUMMARY]: 'The goal is to implement X',
|
||||
});
|
||||
const result = await invocation.execute(new AbortController().signal);
|
||||
|
||||
expect(result.llmContent).toBe('Current topic: "New Chapter"');
|
||||
expect(result.llmContent).toContain('Current topic: "New Chapter"');
|
||||
expect(result.llmContent).toContain(
|
||||
'Topic goal: The goal is to implement X',
|
||||
);
|
||||
expect(mockConfig.topicState.getTopic()).toBe('New Chapter');
|
||||
});
|
||||
|
||||
it('should include previous summary if provided', async () => {
|
||||
const invocation = tool.build({
|
||||
[TOPIC_PARAM_TITLE]: 'New Chapter',
|
||||
[TOPIC_PARAM_CURRENT_SUMMARY]: 'The goal is to implement X',
|
||||
[TOPIC_PARAM_PREVIOUS_SUMMARY]: 'Finished Y',
|
||||
});
|
||||
const result = await invocation.execute(new AbortController().signal);
|
||||
|
||||
expect(result.llmContent).toContain('Previous topic summary: Finished Y');
|
||||
expect(result.llmContent).toContain('Current topic: "New Chapter"');
|
||||
});
|
||||
|
||||
it('should return error if title is invalid after sanitization', async () => {
|
||||
const invocation = tool.build({ [TOPIC_PARAM_TITLE]: ' \n ' });
|
||||
const invocation = tool.build({
|
||||
[TOPIC_PARAM_TITLE]: ' \n ',
|
||||
[TOPIC_PARAM_CURRENT_SUMMARY]: 'Goal',
|
||||
});
|
||||
const result = await invocation.execute(new AbortController().signal);
|
||||
|
||||
expect(result.error).toBeDefined();
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
import {
|
||||
CREATE_NEW_TOPIC_TOOL_NAME,
|
||||
TOPIC_PARAM_TITLE,
|
||||
TOPIC_PARAM_PREVIOUS_SUMMARY,
|
||||
TOPIC_PARAM_CURRENT_SUMMARY,
|
||||
} from './definitions/coreTools.js';
|
||||
import {
|
||||
BaseDeclarativeTool,
|
||||
@@ -60,6 +62,8 @@ export class TopicState {
|
||||
|
||||
interface CreateNewTopicParams {
|
||||
[TOPIC_PARAM_TITLE]: string;
|
||||
[TOPIC_PARAM_PREVIOUS_SUMMARY]?: string;
|
||||
[TOPIC_PARAM_CURRENT_SUMMARY]: string;
|
||||
}
|
||||
|
||||
class CreateNewTopicInvocation extends BaseToolInvocation<
|
||||
@@ -76,11 +80,14 @@ class CreateNewTopicInvocation extends BaseToolInvocation<
|
||||
}
|
||||
|
||||
getDescription(): string {
|
||||
return `Create new topic: "${this.params[TOPIC_PARAM_TITLE]}"`;
|
||||
const title = this.params[TOPIC_PARAM_TITLE];
|
||||
return `Create new topic: "${title}"`;
|
||||
}
|
||||
|
||||
async execute(): Promise<ToolResult> {
|
||||
const title = this.params[TOPIC_PARAM_TITLE];
|
||||
const previousSummary = this.params[TOPIC_PARAM_PREVIOUS_SUMMARY];
|
||||
const currentSummary = this.params[TOPIC_PARAM_CURRENT_SUMMARY];
|
||||
|
||||
const success = this.config.topicState.setTopic(title);
|
||||
|
||||
@@ -98,9 +105,20 @@ class CreateNewTopicInvocation extends BaseToolInvocation<
|
||||
const setTopic = this.config.topicState.getTopic()!;
|
||||
debugLogger.log(`[TopicTool] Changing topic to: "${setTopic}"`);
|
||||
|
||||
let llmContent = `Current topic: "${setTopic}"\nTopic goal: ${currentSummary}`;
|
||||
let returnDisplay = `Current topic: **${setTopic}**\n\n**Topic Goal:**\n${currentSummary}`;
|
||||
|
||||
if (previousSummary) {
|
||||
llmContent =
|
||||
`Previous topic summary: ${previousSummary}\n\n` + llmContent;
|
||||
returnDisplay =
|
||||
`**Previous Topic Summary:**\n${previousSummary}\n\n---\n\n` +
|
||||
returnDisplay;
|
||||
}
|
||||
|
||||
return {
|
||||
llmContent: `Current topic: "${setTopic}"`,
|
||||
returnDisplay: `Current topic: **${setTopic}**`,
|
||||
llmContent,
|
||||
returnDisplay,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user