Skip to content

Commit 565d748

Browse files
authored
Add customInstructionsInSystemMessage setting (#257)
* Add customInstructionsInSystemMessage setting Fix microsoft/vscode#255859 * Add test for CustomInstructionsInSystemMessage * Add OmitBaseAgentInstructions setting * Don't include the 'when generating code' message
1 parent 2101218 commit 565d748

File tree

7 files changed

+201
-11
lines changed

7 files changed

+201
-11
lines changed

package.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2813,6 +2813,14 @@
28132813
],
28142814
"description": "%github.copilot.config.agent.currentEditorContext.enabled%"
28152815
},
2816+
"github.copilot.chat.customInstructionsInSystemMessage": {
2817+
"type": "boolean",
2818+
"default": false,
2819+
"tags": [
2820+
"experimental"
2821+
],
2822+
"description": "%github.copilot.config.customInstructionsInSystemMessage%"
2823+
},
28162824
"github.copilot.chat.agent.terminal.allowList": {
28172825
"type": "object",
28182826
"default": {},

package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@
280280
"copilot.tools.getDocInfo.name": "Doc Info",
281281
"copilot.tools.createDirectory.name": "Create Directory",
282282
"github.copilot.config.agent.currentEditorContext.enabled": "When enabled, Copilot will include the name of the current active editor in the context for agent mode.",
283+
"github.copilot.config.customInstructionsInSystemMessage": "When enabled, custom instructions and mode instructions will be appended to the system message instead of a user message.",
283284
"github.copilot.config.agent.terminal.allowList": "A list of commands or regular expressions that allow the run in terminal tool commands to run without explicit approval. These will be matched against the start of a command. A regular expression can be provided by wrapping the string in `/` characters.\n\nExamples:\n- `\"mkdir\"` Will allow all command lines starting with `mkdir`\n- `\"npm run build\"` Will allow all command lines starting with `npm run build`\n- `\"/^git (status|show\\b.*)$/\"` will allow `git status` and all command lines starting with `git show`\n- `\"/.*/\"` will allow all command lines\n\nThis will be overridden by anything that matches an entry in `#github.copilot.config.agent.terminal.denyList#`.",
284285
"github.copilot.config.agent.terminal.allowList.key": "The start of a command to match against. A regular expression can be provided by wrapping the string in `/` characters.",
285286
"github.copilot.config.agent.terminal.allowList.value.true": "Allow the pattern.",

src/extension/prompts/node/agent/agentPrompt.tsx

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -90,21 +90,18 @@ export class AgentPrompt extends PromptElement<AgentPromptProps> {
9090
codesearchMode={this.props.codesearchMode}
9191
/>;
9292

93-
const baseInstructions = <>
93+
const omitBaseAgentInstructions = this.configurationService.getConfig(ConfigKey.Internal.OmitBaseAgentInstructions);
94+
const baseAgentInstructions = <>
9495
<SystemMessage>
9596
You are an expert AI programming assistant, working with a user in the VS Code editor.<br />
9697
<CopilotIdentityRules />
9798
<SafetyRules />
9899
</SystemMessage>
99100
{instructions}
100-
<UserMessage>
101-
<CustomInstructions languageId={undefined} chatVariables={this.props.promptContext.chatVariables} />
102-
{this.props.promptContext.modeInstructions && <Tag name='customInstructions'>
103-
Below are some additional instructions from the user.<br />
104-
<br />
105-
{this.props.promptContext.modeInstructions}
106-
</Tag>}
107-
</UserMessage>
101+
</>;
102+
const baseInstructions = <>
103+
{!omitBaseAgentInstructions && baseAgentInstructions}
104+
{this.getAgentCustomInstructions()}
108105
<UserMessage>
109106
{await this.getOrCreateGlobalAgentContext(this.props.endpoint)}
110107
</UserMessage>
@@ -137,6 +134,28 @@ export class AgentPrompt extends PromptElement<AgentPromptProps> {
137134
}
138135
}
139136

137+
private getAgentCustomInstructions() {
138+
const putCustomInstructionsInSystemMessage = this.configurationService.getConfig(ConfigKey.CustomInstructionsInSystemMessage);
139+
const customInstructionsBody = <>
140+
<CustomInstructions
141+
languageId={undefined}
142+
chatVariables={this.props.promptContext.chatVariables}
143+
includeSystemMessageConflictWarning={!putCustomInstructionsInSystemMessage}
144+
customIntroduction={putCustomInstructionsInSystemMessage ? '' : undefined} // If in system message, skip the "follow these user-provided coding instructions" intro
145+
/>
146+
{
147+
this.props.promptContext.modeInstructions && <Tag name='customInstructions'>
148+
Below are some additional instructions from the user.<br />
149+
<br />
150+
{this.props.promptContext.modeInstructions}
151+
</Tag>
152+
}
153+
</>;
154+
return putCustomInstructionsInSystemMessage ?
155+
<SystemMessage>{customInstructionsBody}</SystemMessage> :
156+
<UserMessage>{customInstructionsBody}</UserMessage>;
157+
}
158+
140159
private async getOrCreateGlobalAgentContext(endpoint: IChatEndpoint): Promise<PromptPieceChild[]> {
141160
const globalContext = await this.getOrCreateGlobalAgentContextContent(endpoint);
142161
return globalContext ?

src/extension/prompts/node/agent/test/__snapshots__/agentPrompt.spec.tsx.snap

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,141 @@ copilot_cache_control: {"type":"ephemeral"}
595595
"
596596
`;
597597
598+
exports[`AgentPrompt > custom instructions in system message 1`] = `
599+
"### System
600+
~~~md
601+
You are an expert AI programming assistant, working with a user in the VS Code editor.
602+
When asked for your name, you must respond with "GitHub Copilot".
603+
Follow the user's requirements carefully & to the letter.
604+
Follow Microsoft content policies.
605+
Avoid content that violates copyrights.
606+
If you are asked to generate content that is harmful, hateful, racist, sexist, lewd, or violent, only respond with "Sorry, I can't assist with that."
607+
Keep your answers short and impersonal.
608+
<instructions>
609+
You are a highly sophisticated automated coding agent with expert-level knowledge across many different programming languages and frameworks.
610+
The user will ask a question, or ask you to perform a task, and it may require lots of research to answer correctly. There is a selection of tools that let you perform actions or retrieve helpful context to answer the user's question.
611+
You will be given some context and attachments along with the user prompt. You can use them if they are relevant to the task, and ignore them if not.
612+
If you can infer the project type (languages, frameworks, and libraries) from the user's query or the context that you have, make sure to keep them in mind when making changes.
613+
If the user wants you to implement a feature and they have not specified the files to edit, first break down the user's request into smaller concepts and think about the kinds of files you need to grasp each concept.
614+
If you aren't sure which tool is relevant, you can call multiple tools. You can call tools repeatedly to take actions or gather as much context as needed until you have completed the task fully. Don't give up unless you are sure the request cannot be fulfilled with the tools you have. It's YOUR RESPONSIBILITY to make sure that you have done all you can to collect necessary context.
615+
When reading files, prefer reading large meaningful chunks rather than consecutive small sections to minimize tool calls and gain better context.
616+
Don't make assumptions about the situation- gather context first, then perform the task or answer the question.
617+
Think creatively and explore the workspace in order to make a complete fix.
618+
Don't repeat yourself after a tool call, pick up where you left off.
619+
You don't need to read a file if it's already provided in context.
620+
</instructions>
621+
<toolUseInstructions>
622+
If the user is requesting a code sample, you can answer it directly without using any tools.
623+
When using a tool, follow the JSON schema very carefully and make sure to include ALL required properties.
624+
No need to ask permission before using a tool.
625+
NEVER say the name of a tool to a user. For example, instead of saying that you'll use the run_in_terminal tool, say "I'll run the command in a terminal".
626+
If you think running multiple tools can answer the user's question, prefer calling them in parallel whenever possible
627+
When invoking a tool that takes a file path, always use the absolute file path. If the file has a scheme like untitled: or vscode-userdata:, then use a URI with the scheme.
628+
You don't currently have any tools available for editing files. If the user asks you to edit a file, you can ask the user to enable editing tools or print a codeblock with the suggested changes.
629+
You don't currently have any tools available for running terminal commands. If the user asks you to run a terminal command, you can ask the user to enable terminal tools or print a codeblock with the suggested command.
630+
Tools can be disabled by the user. You may see tools used previously in the conversation that are not currently available. Be careful to only use the tools that are currently available to you.
631+
</toolUseInstructions>
632+
<outputFormatting>
633+
Use proper Markdown formatting in your answers. When referring to a filename or symbol in the user's workspace, wrap it in backticks.
634+
<example>
635+
The class \`Person\` is in \`src/models/person.ts\`.
636+
</example>
637+
638+
</outputFormatting>
639+
640+
<instructions>
641+
This is a test custom instruction file
642+
</instructions>
643+
<customInstructions>
644+
Below are some additional instructions from the user.
645+
646+
custom mode instructions
647+
</customInstructions>
648+
copilot_cache_control: {"type":"ephemeral"}
649+
~~~
650+
651+
652+
### User
653+
~~~md
654+
<environment_info>
655+
The user's current OS is: Linux
656+
The user's default shell is: "zsh". When you generate terminal commands, please generate them correctly for this shell.
657+
</environment_info>
658+
<workspace_info>
659+
I am working in a workspace with the following folders:
660+
- /workspace
661+
I am working in a workspace that has the following structure:
662+
\`\`\`
663+
664+
\`\`\`
665+
This is the state of the context at this point in the conversation. The view of the workspace structure may be truncated. You can use tools to collect more context if needed.
666+
</workspace_info>
667+
copilot_cache_control: {"type":"ephemeral"}
668+
~~~
669+
670+
671+
### User
672+
~~~md
673+
<context>
674+
(Date removed from snapshot)
675+
</context>
676+
<reminderInstructions>
677+
678+
</reminderInstructions>
679+
<userRequest>
680+
hello
681+
</userRequest>
682+
copilot_cache_control: {"type":"ephemeral"}
683+
~~~
684+
"
685+
`;
686+
687+
exports[`AgentPrompt > omit base agent instructions 1`] = `
688+
"### System
689+
~~~md
690+
691+
<instructions>
692+
This is a test custom instruction file
693+
</instructions>
694+
copilot_cache_control: {"type":"ephemeral"}
695+
~~~
696+
697+
698+
### User
699+
~~~md
700+
<environment_info>
701+
The user's current OS is: Linux
702+
The user's default shell is: "zsh". When you generate terminal commands, please generate them correctly for this shell.
703+
</environment_info>
704+
<workspace_info>
705+
I am working in a workspace with the following folders:
706+
- /workspace
707+
I am working in a workspace that has the following structure:
708+
\`\`\`
709+
710+
\`\`\`
711+
This is the state of the context at this point in the conversation. The view of the workspace structure may be truncated. You can use tools to collect more context if needed.
712+
</workspace_info>
713+
copilot_cache_control: {"type":"ephemeral"}
714+
~~~
715+
716+
717+
### User
718+
~~~md
719+
<context>
720+
(Date removed from snapshot)
721+
</context>
722+
<reminderInstructions>
723+
724+
</reminderInstructions>
725+
<userRequest>
726+
hello
727+
</userRequest>
728+
copilot_cache_control: {"type":"ephemeral"}
729+
~~~
730+
"
731+
`;
732+
598733
exports[`AgentPrompt > one attachment 1`] = `
599734
"### System
600735
~~~md

src/extension/prompts/node/agent/test/agentPrompt.spec.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ suite('AgentPrompt', () => {
111111
return result;
112112
}
113113

114-
115114
test('simple case', async () => {
116115
expect(await agentPromptToString(accessor, {
117116
chatVariables: new ChatVariablesCollection(),
@@ -233,4 +232,23 @@ suite('AgentPrompt', () => {
233232
enableCacheBreakpoints: true,
234233
})).toMatchSnapshot();
235234
});
235+
236+
test('custom instructions in system message', async () => {
237+
accessor.get(IConfigurationService).setConfig(ConfigKey.CustomInstructionsInSystemMessage, true);
238+
expect(await agentPromptToString(accessor, {
239+
chatVariables: new ChatVariablesCollection(),
240+
history: [],
241+
query: 'hello',
242+
modeInstructions: 'custom mode instructions',
243+
}, undefined)).toMatchSnapshot();
244+
});
245+
246+
test('omit base agent instructions', async () => {
247+
accessor.get(IConfigurationService).setConfig(ConfigKey.Internal.OmitBaseAgentInstructions, true);
248+
expect(await agentPromptToString(accessor, {
249+
chatVariables: new ChatVariablesCollection(),
250+
history: [],
251+
query: 'hello',
252+
}, undefined)).toMatchSnapshot();
253+
});
236254
});

src/extension/prompts/node/panel/customInstructions.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ export interface CustomInstructionsProps extends BasePromptElementProps {
3737
*/
3838
readonly includePullRequestDescriptionGenerationInstructions?: boolean;
3939
readonly customIntroduction?: string;
40+
41+
/**
42+
* @default true
43+
*/
44+
readonly includeSystemMessageConflictWarning?: boolean;
4045
}
4146

4247
export class CustomInstructions extends PromptElement<CustomInstructionsProps> {
@@ -50,6 +55,7 @@ export class CustomInstructions extends PromptElement<CustomInstructionsProps> {
5055
override async render(state: void, sizing: PromptSizing) {
5156

5257
const { includeCodeGenerationInstructions, includeTestGenerationInstructions, includeCodeFeedbackInstructions, includeCommitMessageGenerationInstructions, includePullRequestDescriptionGenerationInstructions, customIntroduction } = this.props;
58+
const includeSystemMessageConflictWarning = this.props.includeSystemMessageConflictWarning ?? true;
5359

5460
const chunks = [];
5561

@@ -97,9 +103,10 @@ export class CustomInstructions extends PromptElement<CustomInstructionsProps> {
97103
return undefined;
98104
}
99105
const introduction = customIntroduction ?? 'When generating code, please follow these user provided coding instructions.';
106+
const systemMessageConflictWarning = includeSystemMessageConflictWarning && ' You can ignore an instruction if it contradicts a system message.';
100107

101108
return (<>
102-
{introduction} You can ignore an instruction if it contradicts a system message.<br />
109+
{introduction}{systemMessageConflictWarning}<br />
103110
<Tag name='instructions'>
104111
{
105112
...chunks

src/platform/configuration/common/configurationService.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,7 @@ export namespace ConfigKey {
711711
export const AskAgent = defineExpSetting<boolean>('chat.advanced.enableAskAgent', { defaultValue: false, teamDefaultValue: true, internalDefaultValue: true }, INTERNAL_RESTRICTED);
712712
export const VerifyTextDocumentChanges = defineExpSetting<boolean>('chat.advanced.inlineEdits.verifyTextDocumentChanges', true, INTERNAL_RESTRICTED);
713713
export const EnableApplyPatchForNotebooks = defineExpSetting<boolean>('chat.advanced.enableApplyPatchForNotebooks', false, INTERNAL_RESTRICTED);
714+
export const OmitBaseAgentInstructions = defineSetting<boolean>('chat.advanced.omitBaseAgentInstructions', false, INTERNAL);
714715
}
715716

716717
export const AgentThinkingTool = defineSetting<boolean>('chat.agent.thinkingTool', false);
@@ -773,6 +774,7 @@ export namespace ConfigKey {
773774
export const EditsCodeNewNotebookAgentEnabled = defineExpSetting<boolean>('chat.edits.newNotebook.enabled', true);
774775
export const AutoFixDiagnostics = defineSetting<boolean>('chat.agent.autoFix', true);
775776
export const NotebookFollowCellExecution = defineSetting<boolean>('chat.notebook.followCellExecution.enabled', false);
777+
export const CustomInstructionsInSystemMessage = defineSetting<boolean>('chat.customInstructionsInSystemMessage', false);
776778
}
777779

778780
export function getAllConfigKeys(): string[] {

0 commit comments

Comments
 (0)