微软开源 GitHub Copilot VS code plugin 源码分析 (二) copilot-instructions.md 文件的应用逻辑

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

4. 应用逻辑流程

步骤1:提示构建时

当构建 AI 提示时,CustomInstructions 组件会:

  1. 检查是否启用代码生成指令
js 复制代码
if (includeCodeGenerationInstructions !== false) {
    customInstructions.push(...await this.customInstructionsService.fetchInstructionsFromSetting(ConfigKey.CodeGenerationInstructions));
}
  1. 获取配置的指令

    • 从工作区文件夹级别配置
    • 从工作区级别配置
    • 从全局配置

步骤2:指令收集

CustomInstructionsService.fetchInstructionsFromSetting() 方法:

  1. 检查配置设置
js 复制代码
const inspect = this.configurationService.inspectConfig(configKey);
  1. 收集指令

    • 处理文件引用类型的指令 ({ file: "filename" })
    • 处理文本类型的指令 ({ text: "instruction text" })
  2. 文件读取 当遇到文件引用时,通过 _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 文件:

  1. 自动检测:useInstructionFiles 设置为 true
  2. 文件路径构建: copilot-instructions.md
  3. 内容读取: 使用 readInstructionsFromFile() 方法
  4. 内容解析: 将文件内容作为指令文本处理

5. 指令应用时机

指令在以下情况下被应用:

  1. 代码生成请求(默认启用)
  2. 测试生成请求(需要显式启用)
  3. 代码反馈请求(需要显式启用)
  4. 提交消息生成(需要显式启用)
  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 文件的应用是一个 多层次、可配置的系统

  1. 自动化: 通过 useInstructionFiles 设置自动加载
  2. 可配置: 通过详细的指令配置控制具体行为
  3. 智能化: 支持按需加载和模式匹配
  4. 集成化: 与提示构建系统深度集成

这个系统让开发者可以为项目定制 AI 行为,而无需在每次交互中手动指定规则。

相关推荐
yangshuo12811 小时前
AI编程工具对决:Kilo vs Augment 开发Flutter俄罗斯方块游戏实战对比
flutter·游戏·ai编程
大志说编程2 小时前
LangChain框架入门17: 手把手教你创建LLM工具
python·langchain·ai编程
bug菌3 小时前
还在为Java开发效率低下而苦恼?Trae能否成为你的编程救星?
aigc·ai编程·trae
风云信步3 小时前
GitHub CEO '不改变就改行': 拥抱AI,Copilot instruction用法详解
aigc·openai·ai编程
葡萄城技术团队3 小时前
BI+AI:让数据会思考,让决策更聪明
aigc
jserTang4 小时前
让 AI coding 不再就近解决:如何在 monorepo 中建设 AI context
ai编程·vibecoding
游戏AI研究所4 小时前
ComfyUI 里的 Prompt 插值器(prompt interpolation / text encoder 插值方式)的含义和作用!
人工智能·游戏·机器学习·stable diffusion·prompt·aigc
bug菌4 小时前
如何快速借助字节Trae解决Java 性能问题?看此篇最为关键!
aigc·ai编程·trae
Mintopia4 小时前
Web AIGC 前端修炼手册:那些不该错过的底层秘籍
前端·javascript·aigc