前言:从简单到复杂的AI编程之旅
还记得上一篇博客中,我兴致勃勃地分享了如何用Cursor在短短半天内完成AI工具的从0到1实现。那时的我,就像刚学会骑自行车的孩子,对这个"新玩具"充满了热情。但很快,我就意识到这只是冰山一角。单纯的代码生成固然令人兴奋,但在实际开发中,我们面临的挑战远不止于此。
于是,我开始了一段更深入的探索之旅。特别是在代码重构这个领域,我发现Cursor展现出了令人惊喜的潜力。当我给它一些重构指令时,它不仅能理解我的意图,还能按照预期重构代码,而且速度快得惊人。这让我不禁幻想:如果能把这项技能应用到日常开发中,那岂不是能在别人还在埋头重构时,我已经可以优哉游哉地"摸鱼"了?
现实给的一记重拳:复杂项目重构的困境
然而,现实总是喜欢给我们一记重拳。当我满怀信心地将这种简单直接的重构方式应用到实际项目中时,很快就碰了壁。由于真实项目的复杂度远超简单的AI工具代码项目,简单的prompt在这里就像是用牙签撬动大象。
遇到的主要问题:
- 改动范围失控:就像是没有地图的探险,每次重构都像是在随机漫步。
- 重构策略混乱:完全依赖大模型自动选择重构策略,就像是把方向盘交给了蒙着眼睛的司机。
- 上下文理解障碍:即使使用了@Codebase,Cursor依然像是对整个项目背景一无所知,重构起来漫无目标。
- 代码审查负担:每次改动都需要仔细审查,否则就像是在刀尖上跳舞,提心吊胆。
- 恶性循环:越重构越乱,最后不得不推倒重来,陷入了一个"推倒重来-再次失败"的死循环。
问题根源分析:
经过深入思考,总结出了几个核心原因:
- 逻辑复杂度过高:现实项目往往包含复杂的业务逻辑和条件判断。
- 框架知识欠缺:没有给AI进行适当的框架Onboarding。
- 代码风格多样性:不同开发者的编码风格差异带来的挑战。
- 上下文负担过重:相互关联的代码太多,形成了复杂的依赖网络。
- 行为不可预测:无法准确预知AI的下一步操作。
人机对比:重构思维的差异
为了找到突破口,我开始思考:为什么同样是重构,人类开发者能够相对顺利地完成,而AI却频频踩坑?
人类开发者的重构方式:
- 系统性分析:首先识别和消除code smell,提升代码的可读性和可扩展性。
- 面向对象优化:基于面向对象的最佳实践进行重构。
- 架构层面调整:将代码重新组织到合适的层次结构中。
- 测试验证:确保重构后的代码仍然符合预期行为。
AI的重构方式:就像一个机械工人,简单地重复"修改代码"这个动作,缺乏我们期望的系统性思维和策略性规划。
灵感突现:ReAct框架的启发
在寻找解决方案的过程中,我接触到了Agent和ReAct以及COT,这让我眼前一亮。如果能让AI像人类一样思考并且按照人类期望的方式进行思考,是否就能克服当前的困境?
ReAct框架简介
ReAct 框架是由 Shunyu Yao 等人在 2022 年提出的一种方法,旨在结合推理与行动,以提高大型语言模型(LLMs)的性能。该框架允许 LLMs 交替生成推理轨迹和任务特定操作,从而使模型能够诱导、跟踪和更新操作计划,并有效处理异常情况。
- 与外部工具的交互:ReAct 框架使 LLMs 能够与外部知识库或环境进行交互,从而获取额外信息,提供更可靠的回应。
- 性能提升:研究表明,ReAct 在语言和决策任务上表现优于多个先进的基线模型,同时提高了 LLMs 的可解释性和可信度。
- 与链式思考(CoT)的结合:将 ReAct 与链式思考结合使用,可以在推理过程中同时利用内部知识和外部信息,从而取得最佳效果。
实践:构建重构专用的Prompt
重构步骤设计:
- 基础清理:识别和处理基本的code smell
- 面向对象优化:应用面向对象的最佳实践
- 架构调整:根据六边形架构的规范重组代码
- 错误修复:处理明显的编译错误
- 人工复查:补充细节优化
- 测试修复:确保所有测试用例通过
重构Prompt模板设计:
- 上下文说明:提供架构规范和最佳实践指南
- 思考步骤:引导AI进行系统性分析
- 观察记录:记录发现的问题
- 行动指南:明确具体的重构步骤
基于ReAct和COT的启发,设计了一个用于当前项目上DDD重构的Prompt(包含重构设计的前3步):
Prompt for Refactoring
Context: 在六边形架构的DDD实现模式中,Application Service、Domain Service、Query 以及 Port 的设计和实现有一些最佳实践和经验。以下是总结的经验和适合的重构提示:
1. Application Service
- 主要负责用例编排和事务管理,不包含业务逻辑,而是调用 Domain Service 执行具体操作。
2. Domain Service
- 包含核心业务逻辑,处理复杂业务规则和操作,是领域模型的一部分。
3. Port
- 定义应用程序与外部系统之间接口,包括 inbound 和 outbound 包中的 Repository 接口定义。
基于上述上下文。请参照 @AcademyCourseDomainService.java 对当前文件进行重构,如果涉及 @EnrollmentApplicationService.java 的修改,也可以进行修改。使用 ReAct 框架按照下面的思考步骤来指导此过程,以分析、观察、执行并最终完成代码重构。
Thought-1:是否有明显的code smell?
Observation-1:有code smell:xxxx
Action-1:使用这个code smell 对应的最佳实践进行重构
Thought-2:是否有特别需要注意的code smell,比如说面向对象的5大基本原则,单一职责原则、开放封闭原则、里氏替换原则、依赖倒置原则和接口隔离原则?
Observation-2:这几个地方xxxx,不符合单一职责原则
Action-2:了解单一职责原则的最佳实践,并进行代码重构
Thought-3:当前文件是什么Layer,需要遵循哪些职责?
Observation-3:当前的文件是Domain service,里面有些实现xxxx做的事情不属于domain service的职责
Action-3:根据Domain service职责,对这几个实现进行重构,分析这些做的事情属于哪些layer的职责,将实现挪到对应的Layer中去
Cursor composer的输出:
实验效果与经验总结
通过多轮实验,这种基于ReAct的重构Prompt编写方法展现出了明显的优势:
- 更高的结果正确性: 这种重构方法能够在仅需两到三轮的互动中,快速达成预期结果。与传统方法相比,ReAct和COT的结合使得模型能够更有效地整合信息和推理过程,从而减少了错误和不确定性。这种高效性使得用户在处理复杂问题时,能够更快地获得准确答案。
- 目标导向的思考过程: 采用ReAct框架后,模型的思考方向更加符合用户的期望,而不是受到幻觉(即生成不实信息)的影响。通过引入额外信息源(观察的步骤会引入需要的信息),模型能够实时验证和更新其推理路径,从而确保所生成的信息是基于事实而非虚构。这一特性显著提升了用户对模型输出的信任度。
- 可视化的思考与修改过程: 在Cursor的composer模式中,用户可以清晰地看到思考和修改的中间过程。这种透明性让开发者能够追踪每一步的修改步骤,从而更好地理解模型如何得出最终结论。通过展示具体的推理轨迹和所采取的行动,用户不仅能审视模型的决策过程,还能对其进行必要的调整和优化。
效果提升的关键因素:
- 边界清晰:通过Context限定了AI的行为范围
- 步骤明确:让AI按照预定的思维方式进行重构
- 反馈闭环:每个步骤都有明确的观察和行动指南
思维方式的转变:
这个过程让我意识到,使用AI的关键在于转变思维方式:从"问题导向"转向"引导式合作"。我们需要做的是:
- 人负责分析和规划
- AI负责执行具体细节
- 通过精心设计的Prompt建立桥梁
结语:AI编码助手使用的新范式
这次的探索不仅帮助我解决了代码重构的难题,更重要的是让我领悟到了一个道理:AI不是万能的,但通过正确的引导,它可以成为我们强大的助手,起到能力放大器的作用。当我们学会用"教练"而不是"提问者"的身份与AI互动时,往往能获得更好的结果。当然,这也给我们带来了新的要求:在与AI进行协作时,了解最佳实践和方法论成为了前提条件,只有这样,我们才能更有效地引导AI以正确的方式完成工作。
这种思维方式的转变,让我们能够更好地发挥人类的创造力和AI的执行力,从而提升代码质量和开发效率(在理想情况下,提升开发效率应当将节省下来的时间来促进代码质量的提高,而不是单纯追求高产出,否则我们可能会陷入另一个无法自拔的漩涡。追逐产出而忽视质量,最终仍然会导致技术债务高筑,增加维护成本,甚至影响项目的可持续性。因此,平衡效率与质量至关重要。通过注重代码的可读性、可维护性和合理的使用最佳实践,我们不仅能够提高开发速度,还能确保软件的长期健康和稳定性)。在未来,这种人机协作的模式也许会成为软件开发的一种新范式。
最后:Prompt分享
经过实践,我们总结出了一个专门针对代码基础异味(code smell)重构的提示模板。如果你对代码基础异味的重构感兴趣,欢迎尝试使用这个站点下的Prompt模板。这个重构Prompt模版站点是为开发者量身打造的资源分享平台,旨在提供高效可行的Prompt模版,以提升使用Cursor进行重构的体验,主要面向希望通过AI优化重构过程的同事。其Prompt特色在于能够显著降低AI在重构时可能出现的幻觉。大家可以通过参考站点的Quick Start操作快速上手,从而提升重构的准确率和效率,让大家更专注于最佳实践和方法论的思考。
到此,我们的讨论就告一段落了。感谢大家的阅读和关注!