8

可执行单元校验:在 IDE 中提升 AI 智能体代码的准确性

 4 weeks ago
source link: https://www.phodal.com/blog/executable-unit-validation/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Posted by: Phodal Huang April 1, 2024, 6:02 p.m.

在开发 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 的功能和实践可执行单元校验,我们可以采取以下措施:

  1. 持续优化算法和策略:借助用户反馈和实际测试结果,不断优化校验算法和策略。这包括改进测试生成方法、错误处理策略以及代码检验机制,以提高校验效率和准确性。
  2. 增强并行处理能力:利用并行处理技术,同时对多个生成的代码进行校验,以加快校验速度和提高效率。通过合理的并行处理策略,可以更有效地利用计算资源,加速校验过程。
  3. 智能化的重试机制:设计智能的重试机制,针对无法通过初次校验的代码,自动进行多次尝试修复或重新生成。通过分析校验失败的原因,动态调整重试策略,提高可执行率。
  4. 基于历史数据的预测优化:利用历史数据和模型分析,预测哪些类型的生成代码更可能通过校验,并优先进行校验。通过建立预测模型,可以更有效地指导校验过程,提高整体的校验通过率。
  5. 持续学习和改进机制:建立持续学习和改进机制,不断收集用户反馈和校验结果,进行数据分析和模型更新。通过持续的学习和改进,不断优化校验算法和策略,以适应不断变化的开发需求和环境。
  6. 引入沙盒机制:为了保障安全性,可以考虑引入沙盒机制,限制 AI 直接执行生成的代码。通过沙盒机制,可以在一定程度上减少潜在的安全风险,确保开发者的数据和系统安全。
  7. 加强开发者教育和支持:为开发者提供相关的教育培训和技术支持,帮助他们更好地理解和使用 AutoDev 插件。通过培训课程、文档资料和技术支持平台,提升开发者的技能水平和使用体验。

通过以上改进措施的实施和实践,我们可以进一步提升 AutoDev 插件的功能和性能,提高生成代码的准确性和可执行性,为开发者提供更优质的开发体验。

事实上,我们在 AutoDev 中已经实现了一部分的功能,但是由于时间和精力有限,还有很多功能需要进一步完善和优化。我们将继续努力,不断改进和提升 AutoDev ,欢迎大家关注和支持我们的工作:https://github.com/unit-mesh/auto-dev


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK