可执行单元校验:在 IDE 中提升 AI 智能体代码的准确性
在开发 IDE 插件 AutoDev 时,我们一直遵循着 Unit Mesh 的基本思想,即 AI 所生成的应该是可执行的单元(Unit)。在底层构建丰富的各类单元/工具, 再结合 DevIns 来构建强大的智能体能力。
在初步完成了 AutoDev 的整体蓝图(MVP)之后,我们开始强化原来的准确性问题,即 AI 所生成的代码是否可以被编译器编译,是否可以被测试覆盖等。在这些 功能中,我们最想解决的是:AI 所生成的代码单元是否可执行?
为什么是可执行率?从 0.8 到 0.96 的提升
在去年,我们在进行微调试验的时候,曾经使用 GPT-3.5 API 根据 3000 个场景生成 3000 个 PlantUML 代码,而后通过调用 PlantUML 编译器来生成图片。最终,我们发现在不考虑准确性的情况下, 3000 个代码中,大概只有 600 个代码是可以被编译器编译的, 即可执行的代码比例大概是 20%。对于剩下的 600 个数据,我们需要再次调用 GPT-3.5 API 来生成代码。重复,直到所有生成的代码都可以被编译器编译。
20% 的失败率对于个人的感知太明显了,而根据概率论的知识,我们可以通过多次尝试来提升可执行率。诸如:
执行两次。1 - 0.2 * 0.2 = 0.96
执行三次。1 - 0.2 * 0.2 * 0.2 = 0.992
执行四次。1 - 0.2 * 0.2 * 0.2 * 0.2 = 0.9984
考虑到成本以及生成式 AI 并没有那么聪明,所以我们选择了执行两次,即可执行率提升到 0.96。当然了,在一些高 ROI 的价值,大家可以考虑执行三次。
可执行单元校验
可执行单元校验是指通过对生成的代码进行测试和验证,确保其能够被编译器编译和执行。可执行单元校验旨在提高生成代码的准确性和可执行性, 以确保生成的代码单元符合预期并能够被有效地使用。
在 AutoDev 中,对应有结合数据库的 SQL、单元测试、功能代码生成的功能,所以我们初步设计了以下的校验机制:
单元测试语法(TODO):检查生成的单元测试代码是否符合语言语法规范,确保其能够被编译器正确编译。
单元测试执行:执行生成的单元测试用例,对生成的代码进行测试,确保其能够被编译器正确编译并执行。
SQL 语法检验:根据不同的模型能力生成 SQL 语句,并处理由此产生的错误。
SQL schema 检验(TODO):结合连接的数据库,对生成的 SQL 语句进行检查,确保其符合数据库的 schema 规范。
功能代码生成检验(TODO):采用测试驱动的检验机制,对生成的代码进行检查,确保其符合开发需求并能够被编译器正确编译。
前端代码生成检验(TODO):对生成的前端代码进行检查,以确保 import、语法等正确。
考虑到单元测试是直接可执行的,因此在 AutoDev 我们是直接执行单元测试( RunService
)的,只要 IDE 速度够快,基本上是能快速检验的。通过上述的检验机制, 可以有效地提高生成代码的准确性和可执行性,确保生成的代码单元符合预期并能够被有效地使用。
当然了,如果模型能力不够强,那么可能会带来负面的体验。
实现 AutoDev 的可执行单元校验
根据上述的思想,我们可以打开看看 AutoDev 中对应功能的实现。
单元测试生成:执行单文件测试
由于,不同语言在对于测试的管理是有差异的,诸如于:
Python 会在
tests
目录下创建一个test_*.py
文件。Java 会在
src/test/java
目录下创建一个*.java
文件。Rust 直接在当前的
*.rs
里有一个mod tests
。JavaScript 就什么奇奇怪怪的都有。
所以,在 AutoDev 中,我们创建了 AutoTestService
接口作为不同语言的扩展点,以支持不同语言的单元测试执行。其次在执行上:
对于文件级别生成而言,只有在常见的 case 下,生成的单个测试类才能被执行。对于更复杂的测试,基本上就 GG(没有测试过 GPT 4)。
对于函数级代码生成而言,效果还是不错的 —— 毕竟 AutoDev 拥有很强的上下文机制
当然从最终效果来说,在 Java 测试中,AI 生成的诸多 import 函数都是有问题的,对于接受率更差的其它语言来说,效果就更差了。
SQL 生成:获取错误信息
我们在 AutoDev 中设计的 SQL 生成,主要是用来辅助我这种学过多种 SQL 变体,但是 SQL 反而写得更差的人。其次,也只适用于项目中的 SQL 语句, 即没有太多的复杂逻辑。
根据不同的模型能力,在 SQL 生成的可执行校验设计上还是应该所区别的:
修复。即根据错误信息,尝试修复。
重新生成。即重新生成 SQL 语句。
动态处理。寻找合适的阈值,以根据错误信息的数量,来决定是否重新生成。
由于是在 IDE 中,并且依赖于项目连接到数据库,所以我们可以:
获取所有的表名,以及表的字段信息。
对 SQL 语句进行语法检查,以及 schema 检查。
执行 SQL 语句,以确保其能够正确执行。
如 AI 生成一个不可执行的 SQL,诸如于: SELECT * FROM table where id =;
,在 IDE 中,进行语法检查时,就会返回: Syntax error at position 30: <expression>, ALL, ANY or SOME expected, got ';'
的错误。因此,只需要使用 PsiElementVisitor
来遍历 SQL 语句,然后进行语法检查即可。最后,如果出现错误,就会直接作为第三次对话,发送给模型。
顺便一提,由于还没有设计沙盒机制,当前并不会让 AI 直接执行 SQL 语句,而是通过生成 SQL 语句,然后由开发者来执行。这个锅,必须由开发者来背。
进一步改进和实践
根据上述的内容,GPT 3.5 对我们进行了一些建议。
为了进一步改进 AutoDev 的功能和实践可执行单元校验,我们可以采取以下措施:
持续优化算法和策略:借助用户反馈和实际测试结果,不断优化校验算法和策略。这包括改进测试生成方法、错误处理策略以及代码检验机制,以提高校验效率和准确性。
增强并行处理能力:利用并行处理技术,同时对多个生成的代码进行校验,以加快校验速度和提高效率。通过合理的并行处理策略,可以更有效地利用计算资源,加速校验过程。
智能化的重试机制:设计智能的重试机制,针对无法通过初次校验的代码,自动进行多次尝试修复或重新生成。通过分析校验失败的原因,动态调整重试策略,提高可执行率。
基于历史数据的预测优化:利用历史数据和模型分析,预测哪些类型的生成代码更可能通过校验,并优先进行校验。通过建立预测模型,可以更有效地指导校验过程,提高整体的校验通过率。
持续学习和改进机制:建立持续学习和改进机制,不断收集用户反馈和校验结果,进行数据分析和模型更新。通过持续的学习和改进,不断优化校验算法和策略,以适应不断变化的开发需求和环境。
引入沙盒机制:为了保障安全性,可以考虑引入沙盒机制,限制 AI 直接执行生成的代码。通过沙盒机制,可以在一定程度上减少潜在的安全风险,确保开发者的数据和系统安全。
加强开发者教育和支持:为开发者提供相关的教育培训和技术支持,帮助他们更好地理解和使用 AutoDev 插件。通过培训课程、文档资料和技术支持平台,提升开发者的技能水平和使用体验。
通过以上改进措施的实施和实践,我们可以进一步提升 AutoDev 插件的功能和性能,提高生成代码的准确性和可执行性,为开发者提供更优质的开发体验。
其它
事实上,我们在 AutoDev 中已经实现了一部分的功能,但是由于时间和精力有限,还有很多功能需要进一步完善和优化。我们将继续努力,不断改进和提升 AutoDev ,欢迎大家关注和支持我们的工作:https://github.com/unit-mesh/auto-dev
微信扫码关注该文公众号作者