LLM 与架构新纪元:适应代码生成模式,突破软件开发瓶颈
TL;DR 版本:
今年 2 月,我们在 QCon 上分享了《组织级架构治理的正确落地方式》,其背后的一个核心思想是:架构即代码。围绕这个核心思想,我们构建了 ArchGuard 的治理功能,即架构规范转换为代码。
今年 5 月,我们在 QCon 上分享了《探索软件开发新工序:LLM 赋能研发效能提升》,一次关于 LLM 与研发效能的试验、探索与洞察等。
而如果你对于 AI 代码生成的进一步探索,你也会发现:
编码速度(自动生成代码)加快,架构/骨架的合理性和牢靠性更加重要。
架构设计及演进的速度将成为研发速度的新瓶颈。
架构知识需要工具化,以充分利用大模型能力。
于是乎,围绕着上述的几点,我们开始探讨:如何改善代码生成的质量?如何避免架构成为 AIGC 的瓶颈?什么是 AIGC 友好的软件架构?如何利用 AIGC 来更好的赋能软件架构?
引子:架构规范内建,改善生成质量
“众所周知” GitHub Copilot 在生成代码时,会根据 IDE/编辑器的编辑历史、文件信息,动态构建所需要的上下文,以此生成贴近用户编码习惯的代码。但是呢,如果原有的代码不规范,那么生成的代码也就是不符合规范的。
幸运的是,在国内的大部分公司是用不了 GitHub Copilot;不幸的是,我们需要找到一个更贴近的工具。
于是乎,在四月份,我们构建了一个 IDE 插件 AutoDev ,其中的一部分内容便是探索:结合架构风格(三层架构)的自动化需求分析与编码。而在 ArchGuard Co-mate 中构建架构治理和架构设计功能时,我们提倡的是动态的上下文管理,也就是围绕不同的场景,所需要的上下文是不同的。
因此,我们也可以将架构规范直接集成到编码工具中,如下图是开发中的 AutoDev 新功能(新版本正在审核中):
即将规范直接内建到工具中,便可以直接生成更贴近架构规范的代码。不过,要生成符合业务需求的代码,还依赖于具体的需求,也就是由先前讨论研发效能时的话题。
设计思想:LLM 增强的架构全生命周期
谈及架构的全生命周期,我们定义了 "三态两方" 这一概念。这里的“三态”指的是设计态、开发态和运行态,它们代表架构发生变化时会经历的三个阶段;“两方”则指的是团队方和交互方,它们代表了架构的主要利益相关者:架构的维护团队和架构的使用者。在这个全生命周期中,大型语言模型 (LLM) 能够在各个阶段发挥其独特的作用,进一步增强架构并使得各利益相关者受益。
设计态是架构生命周期中的第一阶段,在这个阶段,架构师需要根据诸多业务需求进行架构设计。LLM 的自然语言处理能力在此阶段显得尤为重要。它可以从需求描述中准确地提取出业务概念(名词)和业务行为(动词),并识别出它们之间的关系。这样,架构师就可以更方便地进行架构建模,从而提高设计效率。
除此之外,我们还可以 Think big,例如利用 LLM 的推理能力进行架构设计的预先验证。假设LLM 可以向团队提供更多的模拟场景,那么它就可以在一定程度上预测架构在这些模拟场景和负载下的表现,为优化设计提供重要参考。
开发态:按架构图索骥,生成符合规范的代码
在开发态阶段,LLM 通过将软件架构规范转化为实际的代码,为“按图索骥”这一理念赋予了实际的工作效能。这主要依赖于LLM的两大关键能力:抽象理解和模式识别。
抽象理解,从模型到DSL:LLM 能够理解架构设计中的抽象概念和模型,包括各类设计模式、接口、组件及其间的交互关系等。这使得 LLM 能够理解架构设计师的意图,并将其转化为符合特定编程语言和风格的待填充「模版」。
模式识别,填充符合规范的代码:LLM 能够识别和学习大量现有的编码规范和模式。这使得LLM能够生成的代码不仅符合架构设计,而且遵循最佳的编程实践和规范。这样可以保证生成的代码不仅功能正确,而且代码质量高,便于阅读和维护。
充分运用 LLM 的这两大能力,可以使得在开发态阶段,架构设计与代码生成相辅相成,减轻开发团队的工作负担,提高开发效率,并且保证实际代码与架构设计的一致性和可维护性。
运行态:动态拓展架构的开放能力成为可能
在运行态阶段,自然语言处理的接口有可能成为未来架构的一个重要组成部分,为机器到机器API提供了全新的范式。这种范式让架构的开放层能力在被调用时具有更大的灵活性和拓展性。
具体来说,在运行态阶段,LLM 的自然语言处理能力能够理解外部系统或用户的请求意图,识别并处理涉及综合资源的复杂请求。这些请求可能涉及到未明确开放的多个服务或模块。LLM理解请求后,会构建相应的上下文并确认资源权限,确保所有相关资源的安全访问。之后,LLM将利用其深度学习和推理能力,分析、整合这些资源,生成满足请求需求的响应。如此,LLM即使面对综合化资源或服务的请求,也能动态扩展架构的开放层,提供所需功能。
这种动态扩展架构的开放能力,让架构能够更灵活地应对各种复杂的请求,提供更丰富的服务,同时保证了系统的安全性和可控性。这无疑为架构的运行态提供了全新的可能。
除此之外,在软件运行阶段,系统会遇到各种预期内或预期外的问题,这就需要对架构进行调整和优化。LLM可以辅助这一过程,提供智能化的解决方案,包括且不限智能优化及自动修复等能力。并且随着软件系统的运行和需求的变化,LLM还可以帮助理解和适应新的架构,生成符合新架构的代码,以及验证和优化新架构。
面向两个干系方,LLM 在架构全生命周期中的横向价值
在阐明了 LLM 如何在架构的不同阶段提供增强效果之后,我们可以进而思考从两大干系方,即团队方和交互方的角度出发,将如何在架构全生命周期中,从 LLM 的应用中受益。
对于团队方而言,LLM 的能力可以帮助团队在知识传递和共享方面带来显著的提升。例如,LLM可以利用其自然语言处理和知识图谱的技术,以直观易懂的方式描绘出架构设计中的重要概念和关系,从而加强团队间的知识传递。此外,LLM 可以作为协调者,帮助团队成员理解架构设计的决策,从而提升整个团队的协作效率。
对于交互方,LLM 的应用可以带来用户体验的跃迁。这包括更自然的交互方式,用户可以通过自然语言来描述他们的需求;架构的动态扩展能力,使得用户可以更灵活地使用系统,不再受限于固定的接口和服务;以及系统在面对复杂请求时所展现出的强大自适应性。这些都将为用户带来了前所未有的体验。
综上,通过在架构的不同阶段引入 LLM,不仅可以增强架构的设计、开发和运行效果,也为架构的干系方带来了明显的横向受益。
设计原则:适配于软件架构的 AIGC
随着,LLM 能力的逐步提升,我们相信国内会有接近于 ChatGPT 的 LLM 出现。而现在为了进一步提升 AIGC 代码的可用性,我们需要让 AIGC 能适配软件架构(诸如于分层、编码等等)。
所以,根据我们的探索经验,总结了三个核心要素:
架构知识显性化,除去约定俗成
借助 DSL 精炼架构上下文
全流程引导 AIGC,朝向目标架构
未来,随着我们的进一步探索以及 LLM 的演进,相信会出现一些新的变化。
要素:架构知识显性化,除去约定俗成
动机:若想借助 AIGC 来生成代码,就需要将隐性的架构知识显性化表达出来,以文本、DSL 等形式表示。
在软件开发中,架构知识包含了架构决策记录、架构设计文档、架构规范等等,它们分散于组织的不同团队中:
架构规范。在统一的架构库或者团队知识库中。
代码规范。在流水线平台、IDE 插件中等。
分层规范。隐藏在代码中,以约定俗成的方式存在。
……
更惨的是,在我们构建ArchGuard 的初期,与不同公司的架构师进行了交流,发现架构规范在多数组织里是空中楼阁,或者说只能是没人看的参考建议。有点类似于电器产品的说明书,你翻一下只是为了看一下:保修卡有没有在里面。
所以,我们会把团队对架构规范的采纳度分为三类:
没有显性化规范。对于小团队来说,规范化的代码、分层等,依赖于个人的能力、技术追求;又或者是靠约定俗成,结合 code review 的方式所完成的。
有规范,不落地。对于中大型公司的开发团队来说,他们都有非常多的规范,但是没人看。
规范工具化。这个便是我们在 ArchGuard 时的核心关注点,特别是不易显形的分层架构、MyBatis 中的 SQL、API 规范等。
架构规范是架构知识中最易于 LLM 相结合的,也是最适合去实践落地的。
要素:借助 DSL 精炼架构上下文
动机:代码生成时,需要一系列的代码上下文、架构知识等等。在多数场景下,都可能超出 LLM 的上下文边界,或者上下文过长可能会迷失,因此需要精炼出上下文,而 DSL 是一种比较理想的方式。诸如于生成 Controller-Service 代码时,需要提供 Entity、DTO、Mapper 等代码信息,并要结合业务系统的规范。
架构 DSL 示例:在 2022 年,我们尝试构建 Fklang 作为一个架构描述语言。它是一个基于软件开发工业化思想,设计的架构设计 DSL。以确保软件系统描述与实现的一致性。通过显式化的软件架构设计,用于支持 AI 代码生成系统的嵌入。
我们将分层架构的规范,变成了简化的 DSL 形式,并且它是可以直接在 IDE 中执行的。在没有 LLM 时,它需要人去编写这个 DSL,而现在可以直接由 LLM 根据模板生成,直接降低人的学习成本。
PS:由于 Fklang 是一个外部 DSL,所以我们在 Co-mate 中使用 Kotlin DSL 重新设计成内部 DSL。
要素:全流程引导 AIGC,朝向目标架构
架构设计是贯穿在整个软件开发生命周期里,所以我们的挑战也会变成了,如何在全流程中融入我们的架构设计?
诸如于,在先前的分享里,我们绘制了一个大致的软件开发生命周期:
我们需要在需求阶段考虑架构设计,诸如于根据需求的软件建模、模型设计、表设计等等。在我们拿到一个大的特性之后,便可以结合 AIGC 进行设计:
在需求系统,让 AIGC 进行初步的设计,由人去 review 这个设计。
从 IDE 中获取架构设计系统中的表设计、规范等。
按不同的分层(诸如 Controller-Entity-Service-Repository),集成对应的规范
……
稍有差异的是,与 GitHub Copilot 相比,在固定分层的业务系统之下,我们更容易调配出适合于组织上下文的 AI 辅助生成代码工具。
示例:LLM 赋能的架构生成探索
在有了上述的示例和思考之后,我们就可以做一些探索性的尝试。
示例 1:端到端架构生成工具设计
在 AutoDev 中,我们便想做这方面的尝试,接入 GitHub issue 作为一个简单的需求系统;结合编码规范,在不同的分层中,接入不同的规范,以更贴切于对应的上下文。
而我们所要做的就是,更拆解规范,让他们在各自的上下中工作。如在梳理软件开发的工序,将规范配置到不同的场景中:
Controller 编写。结合 API 设计规范、分层规范等。
Service 编写。结合 Service 编码规范。
Repository 编写。结合 …… 规范。
上述的三条规则是我从网上 copy/paste 的,完全没有任何的恶意。所以,如果幸运的话,LLM 就能一次性生成符合规范的代码;不幸运的话,你就再生成一次吧。
示例 2:LLM 辅助架构治理
如我们在先前的《语言接口:探索大模型优先架构的新一代 API 设计》所总结的,在 ArchGuard Co-mate 中,我们所做的尝试便是:将规范转换为 DSL,并根据不同的团队来动态生成。
同样的场景,适用于架构分层规范、API 规范、模型设计等等的 DSL 化。
示例 3:AIGC 辅助架构设计
相似的,为了有限控制上下文,我们也在 ArchGuard Co-mate 中构建了用于转换需求的 DSL,以借助于大模型的能力,将完善需求。并将需求压缩到 DSL 中,以更好地控制上下文。
caseflow("MovieTicketBooking", defaultActor = "User") {
activity("AccountManage") {
task("UserRegistration") {
stories = listOf("Register with email", "Register with phone")
}
task("UserLogin") {
stories += "Login to the website"
// actor = "Admin" // if some task is actor-specific, you can specify it here
}
}
}
当然,生成这样的用户旅程 DSL 是不够的,还需要提供一个 UI 界面来交互。
总结
来自 ChatGPT 的总结:
本文介绍了利用大型语言模型改善软件架构的方法。关键点包括架构即代码、LLM在设计、开发和运行阶段的应用、团队和交互方的受益、显性化架构知识、DSL精炼架构上下文、全流程引导AIGC朝向目标架构。这些思想和工具为提高代码生成质量和软件架构效能提供了指导。
微信扫码关注该文公众号作者