Nik Malykhin发现,让AI助手通过分析他的代码来编写自己的编码规则,然后要求它在使用它的过程中完善它们,这很有用。
想让AI编程助手更靠谱?别直接抄作业,得“处对象”!
你肯定觉得,想让AI写出更好的代码,就得给它立规矩,对吧?我也这么想,但我想得更“偷懒”一点:既然别人已经有现成的、特别牛的规矩清单了,我为啥不直接拿过来用呢?省事儿!
我本来以为这招能行,结果发现完全错了。这个“拿来主义”的实验彻底失败了,但它却让我明白了一个更好的办法——不是当AI的老板,而是当它的搭档。
我的情况:
我的日常工作流程以Visual Studio Code为中心。在这个实验中的关键球员是:
- GitHub Copilot:核心AI助手,用于内联和聊天模式。
- Copilot指令功能:通过.vscode/copilot-instructions.md文件提供自定义指导的能力。
- 我的目标:使Copilot的建议符合我的项目的特定编码标准,而不需要不断的手动修正。
我通常遵循一种务实的开发方法,重视一致性和清晰性,而不是教条地坚持任何单一的方法。我们的目标是将这种实用主义编入我的人工智能合作伙伴的规则。
我的目标很简单:让AI自动按我们团队的规矩写代码,别老让我手动改。
第一次尝试:直接“抄作业”,结果翻车了!
失败的实验:“通用规则手册”谬误
我之前用另一个叫 Cursor 的AI编辑器,用得挺爽,因为它有个叫 awesome-cursorrules
的超级厉害的“规矩库”。我就想,好家伙,这现成的宝贝不用白不用!于是,我把里面一大堆规矩,原封不动地复制粘贴到了我的 copilot-instructions.md
文件里。
结果呢?惨不忍睹。
1. AI变“杠精”了:上下文盲目执行
有个规矩说“React组件必须用箭头函数”。我们项目确实是这么干的,但AI理解不了上下文Context人情世故。它不管三七二十一,看到任何普通函数就想改成箭头函数,搞得代码乱七八糟,还破坏了我们团队原有的风格。它只会死记硬背,没有脑子。
2. AI变“啰嗦王”了:
还有个规矩说“所有函数都得写详细的注释”。结果我让它写一个简单的“首字母大写”函数,它生成的代码是这样的:
javascript
/
* @param {string} str 这是要被大写的字符串。
* @returns {string} 返回大写后的字符串。
*/
const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
你看,注释比代码本身还长!一个一眼就能看懂的简单函数,被它搞得特别复杂,看得人头疼。
总结一下,这次“抄作业”失败的原因:
代码质量变差:AI生成的代码看起来很“外人”,完全不顾我们项目已有的风格,以后维护起来要命。
干活更慢了:不是帮我加速,反而成了拖累。我得花更多时间去删它生成的“废话”或者手动修改,比我自己写还费劲。心流(写代码的最佳状态)全被它打断了。
可量化的根本问题:我把AI当成了一个可以“一键设置”的机器人,但忽略了上下文。每个项目、每个团队都有自己的“脾气”,直接套用别人的规矩,就像给南方人穿北方的厚棉袄,肯定不合适。我花了更多的时间来对抗、删除或手动编辑AI的“看上去有帮助”但被它误导的建议,而不是我自己写代码,我的心流状态不断被打破。
第二次尝试:教AI“入乡随俗”,效果起飞!
我一怒之下,把那个乱七八糟的“说明书”文件全删了。这次我学聪明了,用了两个绝招:
第一招:让AI自己“写”说明书!
VS Code有个功能叫“Generate instructions...”(生成说明书)。我点了一下,你猜怎么着?Copilot自己去扫描了我整个项目里的代码,然后根据我已经存在的代码风格,自动生成了一套“说明书”!
它确定了现有的模式,并提出了加强这些模式的规则。这与我的第一个实验完全相反:不是强迫我的代码符合规则,而是生成规则来符合我的代码。
这思路完全反过来了!
以前是我强迫代码去适应规矩,现在是规矩去适应我的代码。这就像“测试驱动开发”(TDD),先有结果,再定方法。好处是:生成的规矩天生就和项目合拍,接地气。
第二招:让AI自己“反思”和“进步”!
使用AI优化他自己的指令!
以前我把“说明书”当成一成不变的死文件。现在我发现,它应该是个活的、能进化的“合作备忘录”。
我的新工作流程是这样的:
每次和AI“结对编程”完,我就打开聊天窗口,对它说:
> “嘿,兄弟,回顾一下咱俩刚才的对话。根据我给你的那些修改意见和提醒,你觉得我那个 copilot-instructions.md
文件,该怎么改才能让我下次用得更爽?”
提示原文:
"Analyze our recent conversation. Based on the guidance and corrections I provided, suggest improvements to my .vscode/copilot-instructions.md file."
|
AI就会自己去分析:我什么时候让它“先说个大概再写代码”,什么时候纠正了它的风格……然后,它会主动建议我修改“说明书”!
这招太狠了,等于把“教育AI”这个活儿,外包给了AI自己。它越用越懂你,越用越聪明。
随着时间的推移,这是一种强大而有效的方式,可以使AI成为更好的合作者。
现在我的“说明书”长啥样?
经过几次这样的“反思”和修改,我的“说明书”不再是冷冰冰的规则,而是变成了我和AI之间一份活生生的“合作契约”。
<strong>AWS Serverless Infrastructure as Code Guidelines</strong>
This project implements a serverless application using AWS Lambda and API Gateway, with infrastructure defined in Terraform. Follow these guidelines when making changes:
<strong>Project Architecture</strong>
<strong>Component Structure</strong>
<pre class="displaycode"><code> src/ <strong>Lambda function implementations</strong> ├── hello_world.py <strong>Example Lambda handler</strong> └── [other_functions] <strong>Additional Lambda functions</strong> terraform/ <strong>Infrastructure definition</strong> ├── modules/ <strong>Reusable Terraform modules</strong> └── main.tf <strong>Main infrastructure configuration</strong> tests/ <strong>Integration tests</strong> └── test_api.py <strong>API endpoint tests</strong> </code></pre>
<strong>Key Design Patterns</strong>
1. </strong>Lambda Function Structure<strong>:
<pre class="displaycode"><code>python <strong>Standard Lambda handler pattern - follow this structure</strong> def lambda_handler(event, context): logger.info("Processing request") <strong>Always log entry</strong> <strong>... function logic ...</strong> return { 'statusCode': 200, 'body': json.dumps(result) } </code></pre>
2. </strong>Terraform Module Usage<strong>: <pre class="displaycode"><code>hcl <strong>Follow this pattern when adding new Lambda functions</strong> module "my_function" { source = "./modules/lambda" function_name = "<service>-<action>" source_file = "../src/<filename>.py" handler = "<filename>.lambda_handler" runtime = "python3.9" } </code></pre>
<strong>Code Quality Standards</strong>
- Write explicit, descriptive variable names over short, ambiguous ones - Follow the existing project's coding style for consistency - Use named constants instead of hardcoded values Example:
<pre class="displaycode"><code>python <strong>Good</strong> MAX_API_RETRIES = 3 is_api_healthy = retry_count < MAX_API_RETRIES
<strong>Avoid</strong> m = 3 healthy = n < m </code></pre>
<strong>Development Approach</strong>
- Don't invent changes beyond what's explicitly requested - Follow security-first approach in all code modifications - Don't modify files outside the requested scope - Don't suggest improvements to files not mentioned in the task Example of focused scope:
<pre class="displaycode"><code>typescript // Request: "Add email validation to User class" // Good - only modifying requested file class User { validateEmail(email: string): boolean { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(email); } }
// Avoid - suggesting changes to other files // ❌ "We should also update UserRepository.ts" // ❌ "Let's improve the existing validation in Utils.ts" </code></pre>
<strong>Communication Style & Protocol</strong>
<strong>Step-by-Step Communication Pattern</strong>
<pre class="displaycode"><code> 1. User Request User: "Need to implement email validation"
2. Copilot Overview Copilot: "Overview: Adding email validation - Will create test for invalid email (15 lines) - Will implement validator (20 lines) Let's start with the test?"
3. User Approval User: "Looks good"
4. Implementation Copilot: *provides code in proper format*
5. Confirmation User: "OK" or "Looks good" </code></pre>
<strong>Test-Driven Development Workflow</strong>
<strong>TDD Cycle</strong>
<pre class="displaycode"><code> ┌── 1. Discuss Test Requirements │ User: "Need password validation" │ Copilot: "Let's test minimum length first" │ ├── 2. Write Test (Red) │ describe('PasswordValidator', () => { │ it('requires minimum 8 characters', () => {...} │ }); │ ├── 3. Implement Code (Green) │ class PasswordValidator { │ isValid(password: string): boolean {...} │ } │ ├── 4. Optional: Refactor │ - Improve naming │ - Remove duplication │ - Enhance readability │ └── 5. Next Test or Complete - User approval required before proceeding </code></pre>
<strong>Testing Instructions</strong>
<strong>Test Guidelines</strong>
- Write focused, single-purpose API integration tests - Always use <code>get_api_url()</code> to fetch endpoints dynamically - Test edge cases explicitly and include proper delays - Use descriptive test names Example:
<pre class="displaycode"><code>python <strong>Good test names</strong> def test_endpoint_returns_200_on_valid_input(): def test_endpoint_handles_empty_payload(): def test_endpoint_returns_404_on_invalid_path():
<strong>Good patterns</strong> def test_new_endpoint(): api_url = get_api_url() time.sleep(5) <strong>Allow API Gateway to propagate</strong> response = requests.get(f"{api_url}/path") assert response.status_code == 200 assert "expected_value" in response.text </code></pre>
<strong>Running Tests</strong>
<pre class="displaycode"><code>bash cd tests python3 -m venv venv source venv/bin/activate pip install -r requirements.txt pytest -v </code></pre>
大概(简化版): <hr> </strong><strong>我们项目的规矩(AI协作版)<strong></strong>
</strong><strong>项目是干啥的?<strong></strong> 用AWS云服务(Lambda, API Gateway)搭了个应用,基础设施用Terraform代码来管。
</strong><strong>代码长啥样?<strong></strong> - </strong>Python函数<strong>要这么写(给个例子): <pre class="displaycode"><code>python def lambda_handler(event, context): logger.info("Processing request") <strong>进来先打个日志</strong> <strong>... 你的业务逻辑 ...</strong> return { 'statusCode': 200, 'body': json.dumps(result) } </code></pre> - </strong>加新功能<strong>要这么改配置(给个例子): <pre class="displaycode"><code>hcl module "my_function" { source = "./modules/lambda" function_name = "user-service-validate" source_file = "../src/validator.py" handler = "validator.lambda_handler" } </code></pre>
|
代码质量要求
- 变量名要写清楚,别用 a
, b
, c
这种鬼。
- 跟着项目现有的风格走。
- 别写“魔法数字”,用常量(比如 MAX_RETRIES = 3
)。
别瞎搞!
- 别自作聪明! 我让你改啥你就改啥,别动不动就说“我觉得XXX文件也得改”。
- 别越界! 没让你动的文件,一个字也别碰。
咱俩咋聊天?
咱俩合作有个固定流程:
1. 我说需求:“要加个邮箱验证。”
2. 你先给我个概览:“好嘞!我打算:
- 先写个测试(15行)
- 再写验证逻辑(20行)
- 我们先从测试开始?”
3. 我说“行”或者“不行”。
4. 你再写代码。
5. 我确认“OK”。
写代码的流程(TDD)
1. 先聊要测啥。
2. 你写个失败的测试(Red)。
3. 我让你改,你写代码让它通过(Green)。
4. (可选)再一起优化一下代码。
5. 我确认后,再进行下一步。
写测试的规矩
- 测试要小而专。
- 用 get_api_url()
拿地址。
- 别忘了 time.sleep(5)
等系统生效。
- 测试名字要写清楚,比如 test_endpoint_returns_400_on_invalid_email
。
最意外的发现:好“教练”比“天才”更重要!
你猜怎么着?我发现了一个惊人的事实:
一个被我调教得明明白白的 普通版GitHub Copilot,用起来居然比一个啥也不告诉它的顶级AI(比如Claude 3.5) 还要好!
为啥?因为顶级AI太“聪明”了,总想搞点“创新”,结果写的代码虽然技术上没问题,但根本不适合我的项目。而我的Copilot,虽然“脑子”没那么大,但它知道我的规矩,写出来的东西直接就能用。
这说明啥? 用AI助手,关键不是AI有多“牛”,而是你给它的指导有多“准”。一个好教练,能把普通球员带成冠军。
以下是最令人惊讶的见解:
一个经过良好指导的 GitHub Copilot,即使使用标准(技术上免费)模型,也能比无需指导的更高级模型(如 Claude 3.5 Sonnet)持续生成更有用、更具上下文感知的代码。
尖端大模型的原始能力往往会导致更具“创造性”的代码,但与手头的具体任务却不那么相关。我精心设计的指令集,运行在标准模型上,简直就是我项目里更优秀的结对编程者。
重要性:这证明了有效的人工智能辅助并非仅仅取决于大型语言模型的原始智能,而更取决于你提供的指导的质量。精心设计的工程和情境设定比仅仅花钱购买更强大的大脑更有价值。
最大的道理:想少动脑,就得先多动脑!
这听起来有点矛盾,但真就是这么回事。
你想让AI替你干活,省脑子,那你必须先花脑子,把自己的工作习惯、项目风格、合作方式,一条条地想清楚,然后教给AI。
AI不是人,它不会读心术。你不告诉它,它就只能按网上最通用的套路来,那肯定不靠谱。
所以,想用好AI,你得做到三点:
1. 了解自己: 搞清楚自己的编码风格和做事方式。
2. 持续改进: 把“说明书”当成一个会成长的项目文件,经常更新。
3. 当搭档,别当老板: 别命令它“你必须这么干”,而是引导它“咱们最好这么干”。
也就是说:
有效使用人工智能助手的核心悖论:要将认知工作转移给人工智能,您必须首先进行元认知工作,以编纂您自己的开发理念和协作风格。
你不能只是安装一个工具,就指望它能读懂你的想法。这种悖论之所以存在,是因为人工智能助手并非你的同事;它们是极其复杂的模式匹配器。如果没有你明确的上下文,它们会默认使用训练数据中最通用的模式。
有效使用实际上需要:
- 自我意识:清楚了解自己的编码模式和项目约定。
- 迭代细化:将人工智能的指令集视为随着时间推移而发展的项目工件。
- 协作思维:从“指挥”人工智能转变为“指导”其进程。
最后总结:
别再到处找“万能AI编程秘籍”了,那玩意儿不存在。那个 copilot-instructions.md
文件,就是你AI搭档的“基因”。它应该和你的代码一起,放进版本管理(比如Git),跟着项目一起进化。
正确的打开方式是:
1. 新建一个空的 copilot-instructions.md
文件。
2. 点“Generate instructions...”,让AI先根据你的代码生成初稿。
3. 每次用完,就问AI:“咱俩刚才合作得咋样?这说明书该咋改?”
4. 不断循环。
最厉害的AI搭档,从来不是买来的,而是你亲手“养”出来的。**