GitHub Copilot Chat 扩展通过以下机制来应用 copilot-instructions.md
文件:
1. 配置设置控制
关键设置: github.copilot.chat.codeGeneration.useInstructionFiles
- 默认值:
true
- 作用: 控制是否自动加载 copilot-instructions.md 文件中的指令
- 位置: package.nls.json 第106行
2. 服务架构
主要服务: CustomInstructionsService
这个服务负责:
- 从设置和文件中获取自定义指令
- 处理不同类型的指令(文件引用和文本指令)
- 管理指令的生命周期
3. 指令文件类型
系统支持两种类型的指令文件:
A. 标准指令文件 (.instructions.md
)
- 扩展名:
.instructions.md
- 位置: 可配置,通过
chat.instructionsFilesLocations
设置控制 - 特性: 支持
applyTo
前端元数据,可以根据 glob 模式自动应用
B. Copilot 专用指令文件 copilot-instructions.md
- 位置: copilot-instructions.md
- 控制设置:
useInstructionFiles
- 应用范围: 代码生成请求
4. 应用逻辑流程
步骤1:提示构建时
当构建 AI 提示时,CustomInstructions
组件会:
- 检查是否启用代码生成指令
js
if (includeCodeGenerationInstructions !== false) {
customInstructions.push(...await this.customInstructionsService.fetchInstructionsFromSetting(ConfigKey.CodeGenerationInstructions));
}
-
获取配置的指令
- 从工作区文件夹级别配置
- 从工作区级别配置
- 从全局配置
步骤2:指令收集
CustomInstructionsService.fetchInstructionsFromSetting()
方法:
- 检查配置设置
js
const inspect = this.configurationService.inspectConfig(configKey);
-
收集指令
- 处理文件引用类型的指令 (
{ file: "filename" }
) - 处理文本类型的指令 (
{ text: "instruction text" }
)
- 处理文件引用类型的指令 (
-
文件读取 当遇到文件引用时,通过
_collectInstructionsFromFile()
方法:
js
const promises = this.workspaceService.getWorkspaceFolders().map(async folderUri => {
const fileUri = Uri.joinPath(folderUri, customInstructionsFile);
const instruction = await this.readInstructionsFromFile(fileUri);
if (instruction) {
result.push(instruction);
}
});
步骤3:文件加载
对于 copilot-instructions.md
文件:
- 自动检测: 当
useInstructionFiles
设置为true
时 - 文件路径构建:
copilot-instructions.md
- 内容读取: 使用
readInstructionsFromFile()
方法 - 内容解析: 将文件内容作为指令文本处理
5. 指令应用时机
指令在以下情况下被应用:
- 代码生成请求(默认启用)
- 测试生成请求(需要显式启用)
- 代码反馈请求(需要显式启用)
- 提交消息生成(需要显式启用)
- PR 描述生成(需要显式启用)
6. 配置方式
通过设置文件配置:
json
{
"github.copilot.chat.codeGeneration.instructions": [
{
"file": ".github/copilot-instructions.md"
}
]
}
通过 useInstructionFiles 设置:
json
{
"github.copilot.chat.codeGeneration.useInstructionFiles": true
}
7. 新功能:按需加载
从 0.29 版本开始,系统支持按需加载指令文件:
- AI 模型可以在需要时主动请求加载相关的指令文件
- 支持 glob 模式匹配来确定适用的指令
- 提供指令文件列表和描述给 AI 模型选择
相关代码分析:
js
export class CustomInstructionsService implements ICustomInstructionsService {
// 从文件加载指令
public async fetchInstructionsFromFile(fileUri: Uri): Promise<ICustomInstructions | undefined> {
return await this.readInstructionsFromFile(fileUri);
}
// 从设置加载指令
public async fetchInstructionsFromSetting(configKey: Config<CodeGenerationInstruction[]>): Promise<ICustomInstructions[]> {
const result: ICustomInstructions[] = [];
const instructions: IInstruction[] = [];
const seenFiles: Set<string> = new Set();
const inspect = this.configurationService.inspectConfig(configKey);
if (inspect) {
await this.collectInstructionsFromSettings(
[inspect.workspaceFolderValue, inspect.workspaceValue, inspect.globalValue],
seenFiles, instructions, result
);
}
const reference = Uri.from({
scheme: this.envService.uriScheme,
authority: 'settings',
path: `/${configKey.fullyQualifiedId}`
});
if (instructions.length > 0) {
result.push({
kind: CustomInstructionsKind.Setting,
content: instructions,
reference,
});
}
return result;
}
// 读取指令文件内容
private async readInstructionsFromFile(fileUri: Uri, languageId?: string): Promise<ICustomInstructions | undefined> {
try {
const fileContents = await this.fileSystemService.readFile(fileUri);
const content = new TextDecoder().decode(fileContents);
const instruction = content.trim();
if (!instruction) {
this.logService.debug(`Instructions file is empty: ${fileUri.toString()}`);
return;
}
return {
kind: CustomInstructionsKind.File,
content: [{ instruction, languageId }],
reference: fileUri
};
} catch (e) {
this.logService.debug(`Instructions file not found: ${fileUri.toString()}`);
return undefined;
}
}
}
CustomInstructions
渲染组件:
js
export class CustomInstructions extends PromptElement<CustomInstructionsProps> {
override async render(state: void, sizing: PromptSizing) {
const chunks = [];
// 处理聊天变量中的指令(按需加载)
if (includeCodeGenerationInstructions !== false && this.props.chatVariables) {
for (const variable of this.props.chatVariables) {
if (isPromptInstruction(variable)) {
if (isString(variable.value)) {
chunks.unshift(<TextChunk>{variable.value}</TextChunk>);
} else if (isUri(variable.value)) {
// 按需加载指令文件
const instructions = await this.customInstructionsService.fetchInstructionsFromFile(variable.value);
if (instructions) {
chunks.push(
<Tag name='attachment' attrs={{
filePath: this.promptPathRepresentationService.getFilePath(variable.value)
}}>
<references value={[new CustomInstructionPromptReference(instructions, instructions.content.map(instruction => instruction.instruction))]} />
{instructions.content.map(instruction => <TextChunk>{instruction.instruction}</TextChunk>)}
</Tag>
);
}
}
}
}
}
// 从设置加载指令
const customInstructions: ICustomInstructions[] = [];
if (includeCodeGenerationInstructions !== false) {
customInstructions.push(...await this.customInstructionsService.fetchInstructionsFromSetting(ConfigKey.CodeGenerationInstructions));
}
if (includeTestGenerationInstructions) {
customInstructions.push(...await this.customInstructionsService.fetchInstructionsFromSetting(ConfigKey.TestGenerationInstructions));
}
return (<>
{introduction}{systemMessageConflictWarning}<br />
<Tag name='instructions'>
{chunks}
</Tag>
</>);
}
// 创建指令元素,支持语言过滤
private createInstructionElement(instructions: ICustomInstructions) {
const lines = [];
for (const entry of instructions.content) {
if (entry.languageId) {
if (entry.languageId === this.props.languageId) {
lines.push(`For ${entry.languageId} code: ${entry.instruction}`);
}
} else {
lines.push(entry.instruction);
}
}
if (lines.length === 0) {
return undefined;
}
return (<>
<references value={[new CustomInstructionPromptReference(instructions, lines)]} />
<>
{lines.map(line => <TextChunk>{line}</TextChunk>)}
</>
</>);
}
}
实际工作流程:
js
// 指令文件格式示例
/*
---
applyTo: "**/*.ts"
description: "TypeScript coding style rules"
---
Use camelCase for variable names.
Always use strict type annotations.
Prefer const over let when possible.
*/
// 按需加载的触发机制
const availableInstructions = await this.customInstructionsService.fetchInstructionsFromSetting(ConfigKey.CodeGenerationInstructions);
// 语言模型工具可以访问指令列表
for (const instruction of availableInstructions) {
if (matchesCurrentContext(instruction.reference, currentFiles)) {
chunks.push(this.createInstructionElement(instruction));
}
}
- 🎯 智能选择:根据当前编程语言和文件类型选择合适的指令
- ⚡ 性能优化:只加载相关的指令,避免上下文膨胀
- 🔄 动态适应:根据任务需求自动调整指令集合
- 📁 条件匹配:通过 glob 模式精确匹配适用场景
8. 指令文件生成
扩展提供了 Chat: Generate Instructions 命令来:
- 分析代码库结构和模式
- 自动生成
copilot-instructions.md
文件 - 放置在
.github
文件夹中
总结
copilot-instructions.md
文件的应用是一个 多层次、可配置的系统:
- 自动化: 通过
useInstructionFiles
设置自动加载 - 可配置: 通过详细的指令配置控制具体行为
- 智能化: 支持按需加载和模式匹配
- 集成化: 与提示构建系统深度集成
这个系统让开发者可以为项目定制 AI 行为,而无需在每次交互中手动指定规则。