提示词的艺术:通过工程化引导生成式AI产出符合SOLID原则的代码
我们都已经见识过这种魔力。你让 ChatGPT“写一个Python脚本来抓取网站并保存到CSV”,三十秒后,你就得到了可运行的代码。这令人印象深刻,速度飞快,甚至令人陶醉。
但是,当你仔细审视时,问题出现了。
整个逻辑——HTTP请求、HTML解析、数据转换和文件I/O——都被塞进了一个长达200行的函数里。硬编码的依赖随处可见。将输出格式从CSV改为JSON需要重写一半的脚本。
AI给了你能够运行的代码,但它没有给你易于维护的代码。它从第一天起就给了你技术债务。
这就是当前在专业软件工程中使用生成式AI的核心挑战。大型语言模型(LLMs)是在互联网的全部代码上训练的,其中包含大量不良习惯。它们默认选择阻力最小的路径,而这通常意味着紧密耦合的一团糟。
如果我们想使用AI来构建严肃的系统,就必须停止仅仅要求它“写代码”,而是开始要求它工程设计解决方案。我们通过将架构约束——特别是SOLID原则——直接融入到我们的提示词中来实现这一点。
提示词带来的差异:一个案例研究
让我们看一个简单的需求:我们需要一个从外部API获取用户数据并发送欢迎邮件的服务。
懒惰的提示词(及其导致的混乱)
提示词:
“编写一个TypeScript类,从
https://api.example.com/users/{id}获取用户,然后使用SES向他们发送欢迎邮件。”
AI输出(“初级开发工程师”特供版):
|
|
为何这违反了SOLID原则:
- 单一职责原则(SRP):这个类做了两件不同的事情:获取数据和发送邮件。它有两个改变的理由。
- 开闭原则(OCP):如果我想从AWS SES切换到SendGrid,我必须修改这个类。我无法在不修改其代码的情况下扩展其行为。
- 依赖倒置原则(DIP):高层策略(
registerUser)严重依赖于低层细节(Axios、AWS SDK)。不模拟网络调用就无法对这个类进行单元测试。
工程化的提示词(“高级架构师”方法)
要解决这个问题,我们不需要自己动手写代码。我们只需要明确告诉AI要遵循哪种架构模式。
提示词:
“我需要一个TypeScript类
UserRegistrationService,来协调获取用户和发送欢迎邮件的过程。关键点在于,这个类必须遵守SOLID原则,特别是依赖倒置原则。
首先,为
UserRepository(用于获取数据)和EmailSender(用于发送邮件)定义接口。UserRegistrationService必须通过其构造函数(构造函数注入)接收这些接口的具体实现。 在服务类本身内部,不要使用任何具体的库(如axios或aws-sdk)。只使用接口。请展示服务类和接口。”
AI输出(SOLID黄金版):
|
|
看看这其中的区别。AI不仅仅是编写了代码;它设计了一个小型系统。
- SRP:服务类现在只有一个职责:协调。获取的工作被推给了存储库实现;发送邮件的工作被推给了发送器实现。
- OCP:我们现在可以创建一个实现
EmailSender接口的SendGridEmailSender类,并将其注入,而无需改动UserRegistrationService的任何一行代码。系统对扩展开放,对修改关闭。 - DIP:高层模块(
UserRegistrationService)现在依赖于抽象(接口),而不是具体细节。测试变得轻而易举——我们只需注入满足接口的模拟对象。
设计SOLID提示词的蓝图
你可以将这种方法应用于几乎任何代码生成任务。以下是为获得高质量架构而设计提示词的清单:
- 定义角色:首先设定上下文。“扮演一位痴迷于编写干净、可维护代码的高级软件架构师。”
- 明确命名原则:不要拐弯抹角。“确保此代码遵守单一职责原则。如有必要,请分解大型函数。”
- 要求抽象:如果你的代码涉及外部系统(数据库、API、文件系统),明确要求先定义接口。“在实现业务逻辑之前,先为数据层定义一个接口。”
- 强制依赖注入:这是最有效的一个技巧。“主要的业务逻辑类不得自行实例化其依赖项。必须通过构造函数注入来提供。”
结论
生成式AI是一面镜子。如果你给它一个懒惰、模糊的提示词,它会反射回懒惰、模糊的代码。但如果你提供清晰的架构约束,它就能成为一个强大的生产力倍增器,用于产出高质量的专业软件。
不要仅仅要求AI写代码。要求它进行架构设计。