笔者长期在 cursor / windsurf 之间徘徊,多次切换 GitHub Copilot 发现都没有达到很好的效果。
这里主要有几个重要的能力差距:1. Copilot 代码智能、且快速的自动应用。 2. Agent 自动编程模式。3. UI/UX交互体验细节
前面两个功能的实现,从原理上并不复杂,但提示工程细节优化非常需要耐心 。Copilot 团队在文档中也介绍了这里的工作量。
今天尝试了最新的 vscode insiders 预览版,直接基于 3.5 sonnet 体验 Agent 模式,跑出一个现实真实的任务,让其整个过程自主思考、行动、编码的流程非常丝滑,包括自动 Apply 代码的交互细节(光标滚动的 UI/UX 动画 比 sursor 还要棒),整体几乎与 cursor 趋同。
虽然目前有 cursor sonnet 3.5/3.7 作为主力,但重度依赖海外的模型很容易在关键时刻受制于人(最近笔者的openai pro 被封了~~), 加上公司最近禁用 cursor, 于是在想,能否让 vscode Copilot 走自己的指定的模型,比如 qwen/ deepseek 等系列。 为了避免“重复工作”, 首先用中英文,分别在网络,以及X 平台检索了下,得到的结论是:整个 Copilot 是封闭的,不提供任何用户模型可配,目前也无任何人进行这一适配工作,以实现用自己的模型替代。
在简单研究(逆向)了下 vscode Copilot 后,我发现 vscode insiders 可以替换成 qwen 的模型,打造自己专属的 IDE了。
效果 我们观察右下角,出现了一个 qwen-max。我们成功切换到 了 qwen 模型。并能调用工具启动 Agent 模式,同时自动 apply 应用和修改代码 (也是配的 qwen-max)。
原理 简单介绍 Copilot Agent 的原理,但本文不会过多细节介绍,官方已经做了基本介绍Introducing GitHub Copilot agent mode (preview)。 实际上,目前的行业的实现的方案都大同小异,方向上归于 ACI (Agent-Computer Interfacehttps://arxiv.org/abs/2405.15793),差别在于细节。也可结合笔者之前的之前的一些技术分享等。
普通模式: 普通模式下的 Copilot 其实是简单的代码上下文 + chat,用户提出问题或需求,Copilot一次性生成回答或代码建议,不能主动执行操作或探索代码库。
普通模式下, Copilot 会先让一个小模型 gpt4o-mini 做任务分类,
是一个有帮助的AI编程助手,为一位软件工程师用户服务,代表Visual Studio Code编辑器行动。你的任务是从下面的Markdown类别表中选择一个与用户问题匹配的类别。仔细审查用户的问题、任何之前的消息以及提供的上下文,如代码片段。只需回复类别名称。你选择的类别将帮助Visual Studio Code为用户提供更高质量的回应,选择错误将降低用户使用Visual Studio Code的体验,所以你必须明智选择。如果你无法选择仅一个类别,或者如果没有类别似乎能为用户提供更好的结果,你必须始终回复\"unknown\"。 Copilot 内置了集中任务分类,然后再返回,让更大的模型去完成,比如涉及到“用户想了解或更新当前工作区中的代码或文件“ ,返回 “workspace_project_questions“ ,这种类型则会自动调用 codebase 模块进行向量化全局检索。
Edit 模式: 在普通模式的基础上,添加自动编辑的能力,会调用 edit_file ,把代码片段智能插入到代码中,完成编辑。
提示词示例
你是一个具有专家级知识的高度复杂的自动化编码代理,精通多种不同的编程语言和框架。用户会提出问题或要求你执行任务,可能需要大量研究才能正确回答。有一系列工具可以让你执行操作或获取有用的上下文来回答用户的问题。如果你能从用户的查询或你拥有的上下文中推断出项目类型(语言、框架和库),请在进行更改时牢记这些信息。如果用户希望你实现一个功能,但没有指定要编辑的文件,首先将用户的请求分解为更小的概念,并思考你需要理解每个概念所需的文件类型。如果你不确定哪个工具相关,可以调用多个工具。你可以重复调用工具来执行操作或收集尽可能多的上下文,直到你完全完成任务。除非你确定无法用你拥有的工具完成请求,否则不要放弃。确保你已尽一切努力收集必要的上下文是你的责任。除非你知道确切的字符串或文件名模式,否则优先使用semantic_search工具搜索上下文。不要对情况做假设 - 首先收集上下文,然后执行任务或回答问题。创造性思考并探索工作空间,以做出完整的修复。工具调用后不要重复自己,从你离开的地方继续。除非用户要求,否则永远不要打印出带有文件更改的代码块。请使用edit_file工具。除非用户要求,否则永远不要打印出带有要运行的终端命令的代码块。请使用run_in_terminal工具。如果上下文中已提供文件,则不需要读取该文件。 Agent 模式: 具有主动性和自主决策能力,可以使用多种工具探索和理解代码库,能够执行实际操作(读取文件、运行命令、编辑代码等),消耗更多 token。一会我们重点看这里。
Agent模式下,Copilot可以使用多种工具:
semantic_search:自然语言搜索相关代码或文档 list_code_usages:列出函数、类、方法等的所有使用情况 run_in_terminal:在终端中运行shell命令 get_terminal_output:获取终端命令的输出 get_errors:获取代码文件中的编译或lint错误 get_changed_files:获取当前git仓库中的文件更改 这是一种标准的 ACI操作(Agent-Computer Interface),和笔者自己实现过的一些 Agent 工具类似, 其本质就是一个 Loop 循环自迭代调用工具,直到完成任务,里面的策略细节由提示工程驱动。但 Copilot 提示词中特别强调,优先使用 semantic_search 工具。
Prefer using the semantic_search tool to search for context unless you know the exact string or filename pattern you're searching for 这是一种挺好的策略,大多情况下,向量化检索,能初步从广泛的代码空间筛选出相关性高的,随后再调用其他工具深入研究。
智能 apply 应用 这个功能可谓是 cursor 独领风骚一段时间的关键之一。最初,大多数智能 ide 由大模型生成代码,但却需要人手工插入到指定行,实际这个过程是很费心智的事情,特别是涉及一些长代码片段。智能应用则是 AI 替代了人,把代码片段插入到合适的文件。原理也是基于 LLM 做输出,目前看 github Copilot 底层实现较为粗糙,使用了一个特别的模型,重写整个文件,应用的时间较久。 提示词如下(翻译版):
你是一个专门应用于现有文档代码更改的AI编程助手。\n遵循微软内容政策。\n避免侵犯版权的内容。\n如果被要求生成有害、仇恨、种族主义、性别歧视、淫秽、暴力或与软件工程完全无关的内容,仅回复“抱歉,我无法协助。”\n保持回答简短且不带个人色彩。\n用户有一个代表代码更改建议的代码块和一个在代码编辑器中打开的Python文件。\n重写现有文档以完全整合提供的代码块中的更改。\n对于响应,始终遵循以下说明:\n1.分析代码块和现有文档,决定是替换现有代码还是插入新代码。\n2.如有必要,将代码块拆分为多个部分并在适当位置插入每个部分。\n3.保留修改后部分的空白和新行。\n4.最终结果必须在语法上有效、格式正确且缩进无误,不应包含任何...现有代码......more... 这个 appy 的模型,理论上是一个参数较小,但特别微调,专用于将代码片段应用的模型。cursor 负责人在采访中,也是提到使用了专门优化的 diff 对修改模型,并且结合了大模型基础上,再做了大量效率优化。
apply 是 Agent 自动化的关键 ,因为它保证了Agent 能正确的把代码片段写入文件。
Copilot & vscode github Copilot 在设计上本身不属于 vscode 内核的一部分,但其主体的大模型 Chat 聊天 UI/UX 、服务等基础逻辑在vscode contrib/chat 模块下,属于开源的基础设施。但开源版本核心不包括提示词工程,Agent 策略,模型请求 等 Copilot 逻辑。 我们上面提到的这些提示词等,都是在 github Copilot chat 这个插件中,插件是以压缩和非开源的形式发布。
如上提到的 3 种 Copilot 策略,都是属于微软官方私有“业务层”,配置在插件中,以插件的形式发布和加载, 并需要登录 github 账户才能使用。
能理解微软的这种方案和架构,在未来的智能 IDE中,Chat UI 交互(聊天界面、发消息等)等已经是编辑器不可缺失的功能,没有必要让大家重复造轮子。但背后的模型,以及模型的提示工程策略则属于私有策略,这部分可以成为“增值产品”,这也是 github Copilot 付费收入的来源。
未来应该会有更多的第三方 Copilot 开源插件实现,并重新, 替代微软自己的模型策略。
替换成个人模型 简单了解上面的原理后,接下来,我们尝试能否快速在 LLM 接口层用 qwen 替代内置模型。 先配置 https 代理,用于拦截 vscode 的请求。
拦截模型列表 Copilot 能选择的模型由这个接口返回:https://api.individual.githubcopilot.com/models
我们在代理层拦截并mock 这个接口,在第一行添加 qwen-max ,启用工具调用,可把下的的 json 直接复制到 mock 的接口。(文本使用的是 whistle )
{ "data":[ { "capabilities":{ "family":"qwen-max", "limits":{ "max_context_window_tokens":128000, "max_output_tokens":4096, "max_prompt_tokens":128000 }, "object":"model_capabilities", "supports":{ "parallel_tool_calls":true, "streaming":true, "tool_calls":true }, "tokenizer":"o200k_base", "type":"chat" }, "id":"qwen-max-latest", "model_picker_enabled":true, "name":"Qwen Max", "object":"model", "preview":false, "vendor":"Alibaba Cloud", "version":"qwen-max-latest" }, ...more... { "capabilities":{ "family":"text-embedding-3-small", "object":"model_capabilities", "supports":{ "dimensions":true }, "tokenizer":"cl100k_base", "type":"embeddings" }, "id":"text-embedding-3-small-inference", "model_picker_enabled":false, "name":"Embedding V3 small (Inference)", "object":"model", "preview":false, "vendor":"Azure OpenAI", "version":"text-embedding-3-small" }, { "capabilities":{ "family":"o3-mini", "limits":{ "max_context_window_tokens":200000, "max_output_tokens":100000, "max_prompt_tokens":64000 }, "object":"model_capabilities", "supports":{ "streaming":true, "structured_outputs":true, "tool_calls":true }, "tokenizer":"o200k_base", "type":"chat" }, "id":"o3-mini", "model_picker_enabled":true, "name":"o3-mini (Preview)", "object":"model", "preview":true, "vendor":"Azure OpenAI", "version":"o3-mini-2025-01-31" }, { "capabilities":{ "family":"claude-3.5-sonnet", "limits":{ "max_context_window_tokens":90000, "max_output_tokens":8192, "max_prompt_tokens":90000, "vision":{ "max_prompt_image_size":3145728, "max_prompt_images":1, "supported_media_types":[ "image/jpeg", "image/png", "image/gif", "image/webp" ] } }, "object":"model_capabilities", "supports":{ "parallel_tool_calls":true, "streaming":true, "tool_calls":true }, "tokenizer":"o200k_base", "type":"chat" }, "id":"claude-3.5-sonnet", "model_picker_enabled":true, "name":"Claude 3.5 Sonnet (Preview)", "object":"model", "policy":{ "state":"enabled", "terms":"Enable access to the latest Claude 3.5 Sonnet model from Anthropic. [Learn more about how GitHub Copilot serves Claude 3.5 Sonnet](https://docs.github.com/copilot/using-github-copilot/using-claude-sonnet-in-github-copilot)." }, "preview":true, "vendor":"Anthropic", "version":"claude-3.5-sonnet" } ], "object":"list" } ingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: medium;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: normal;orphans: 2;text-align: start;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;white-space: normal;background-color: rgb(255, 255, 255);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;">
如上,重启 vscode 我们就能看到新增加的模型了。
模型请求 上面全部列表的模型 ,Copilot 都使用https://api.individual.githubcopilot.com/chat/completions这个接口请求。
接口的参数,竟然是标准的 openai 类的格式,意味着我们之间使用 qwen 的oepnai 兼容接口即可。
https://api.individual.githubcopilot.com/chat/completionshttps://dashscope.aliyuncs.com/compatible-mode/v1/chat/completionsreqHeaders://{test-reqHeaders.json}reqReplace://{test-reqReplace.json} 我们把http://api.individual.githubcopilot.com指向http://dashscope.aliyuncs.com,并重写 api_key 为你自己阿里云的 key。
此时,不需要重启,直接测试
能正常发起聊天,已经是 qwen-max 返回的内容了。
智能 apply 前面提到过,Agent 正确的修改必须依赖 apply,所以我们还需要找到这个接口。
https://proxy.individual.githubcopilot.com/v1/engines/copilot-centralus-h100/speculation
这个接口请求一个专用的模型,用于修改文件。我们简单替换成 qwen-max。
但这里还需要一点点额外工作,apply 模型和标准的 openai 接口不太兼容,需要简单的转化下格式。
let requestBody = req.body; if (typeof requestBody === 'string') { try { requestBody = JSON.parse(requestBody); } catch (e) { console.error('解析请求体失败:', e); return res.status(400).send('无效的 JSON 格式'); } } // 提取提示词 if (!requestBody || !requestBody.prompt) { console.error('请求体中没有 prompt 字段'); return res.status(400).send('请求缺少 prompt 字段'); } const prompt = requestBody.prompt; // 解析系统提示和用户提示 let systemPrompt = "You are an AI programming assistant."; let userPrompt = prompt; const systemMatch = prompt.match(/<SYSTEM>(.*?)<\/SYSTEM>/s); if (systemMatch && systemMatch[1]) { systemPrompt = systemMatch[1].trim(); userPrompt = prompt.substring(prompt.indexOf("</SYSTEM>") + 10).trim(); } // 增强系统提示,确保返回格式正确 systemPrompt += "\n请确保返回完整的代码。如果是修改现有文件,请返回整个文件内容,并使用 </copilot-edited-file> 标记结束。请保持代码的完整性和正确的缩进。"; // 创建目标 API 请求体 const apiRequest = { model: "qwen-max-latest", messages: [ { role: "system", content: systemPrompt }, { role: "user", content: userPrompt } ], temperature: requestBody.temperature || 0, stream: true // 强制使用流式响应 }; console.log('发送到 API 的请求:', JSON.stringify(apiRequest, null, 2)); // 发送请求到目标 API const apiUrl = 'https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions';
由于 qwen-max 不太遵守原本的提示词,我特意在提示词后增加了一个中文强化:请确保返回完整的代码。如果是修改现有文件,请返回整个文件内容,并使用 </copilot-edited-file> 标记结束。请保持代码的完整性和正确的缩进。
测试 Agent功能
完美,可以出现类似 cursor 的那种光标效果,能够完整的利用工具调用,搜索、打开文件,实现自动编码。
结
至此,我们只需要把上面的 3 个请求指向任意离线、内部网络,就能实现模型安全可控,构建个人/企业版本 “cursor”。
————
更多后续
1. 由于 vscode insiders 仍处于不稳定,更新中,代码会持续变化,后续正式的 vscode 版本发布,我们可以利用其开源代码重新打包,编译成开箱即用的稳定版本。
2. 不同模型的提示词需要特别优化,比如针对qwen ,deepseek 和 sonnet 3.5 的提示词应该是有所区别,才能更好的提高 Agent 性能。
3. 长期看,复用开源 vscode 的基础设施(包括AI 聊天和应用等能力),实现企业内部的 Copilot 扩展(针对内部模型调优提示词,以及强化 Agent策略)才是最佳策略。
4. 目前智能 IDE非常卷,但都是基于 vscode 进行分叉(已知的有cursor/windsurf/trea/viod等),这是因为 vscode 官方面对新型工具出现动作太慢导致,长期看智能 IDE 体验最终会走向平稳和趋同,分叉 vscode 的必要性会越来越低。 未来很可能是 vscode 仍提供公共的智能 IDE 能力,提供接口让扩展优化和微调自己的 LLM 策略。