目前大模型在编程领域应用具有确定性,是大模型应用的明确赛道,也吸引了各大厂商一拥而入。
但是各种大模型辅助代码生成的实践中,代码生成效果都是依赖于私域知识的多少和代码架构的clean architecture的程度。 为什么这么说呢?其实很好理解,大模型训练时通用知识代码的占比很高,所以对通用知识的生成效果好理所当然。而私域知识都是在行业内部而且其中隐形知识占比很大,这些知识通用大模型中训练强度和频度都不足,效果自然不会好。 即使是私域训练,受制于算力和算法的约束,效果也很可能不及通用大模型。 另外在大模型推理侧,无论私域代码还是通用代码,作为知识库或提示词喂给大模型时,大模型都需要理解后才能更好转化生成代码,这个过程跟程序员去维护别人代码一样,都需要先读懂才能去修改。 如果存量代码结构清晰,条理分明,上下分层,左右分模块,更重要的都是合规依赖(单向依赖)从而实现业务流程和业务逻辑分离较好,这样的代码就很容易理解。 阅读别人代码天然会碰到三高两难的问题,再加上如果代码一团乱麻,私搭乱建,循环依赖,都会导致存量代码的理解难上加难,大模型生成代码的效率会大打折扣。 存量代码使用大模型进行辅助编程前,重构代码就变得非常必须。 而面对海量存量代码,使用大模型重构就又成了必选项。
重构代码一般都会从两个层面进行:
1、代码级层面 主要针对22种代码坏味道,特别是危害较大的几种坏味道,比如过长函数、重复代码、发散式变化、散弹式修改等。
2、组件级层面 主要是依赖治理,依赖一般又可以从两个方面看:
a、合规依赖 主要特征是上层依赖下层,下层不依赖上层,没有反向依赖 同一层中没有横向依赖,横向依赖通过上层调用。 没有循环依赖(上层下层互相调用,纵向环;同一层中互相调用,横向环) b、依赖原则
- 缩小依赖范围
- 向稳定方向依赖
说白了,遵守依赖原则就是对TDA法则的应用。 依赖是由依赖点构成,依赖点包括:API、DT(data type)、knowledge 例如,a模块要依赖b模块来实现某个意图,它不需要知道b的具体知识,b模块把所有知识都封装在自己内部,尽对外暴露接口(API+DT),a调用b的接口去实现意图即可。
反例是A调用b的接口获取很多b的内部状态,然后又使用b的知识来编排组合这些b的内部状态来实现业务意图,这就是典型的同时违反缩小依赖范围、向稳定方向依赖两原则 另外a依赖b的时候,一定要依赖抽象(接口、DT、抽象类、函数指针等),而不是依赖具体实现。 从代码的阅读理解的角度来看,我们读代码时,都会碰到三高两难的问题,基本都和代码元素依赖过多和违规依赖相关。 从我们的实践中看,大模型自动重构代码违规依赖中,无论使用各种通用私域大模型,无一例外都会碰到一个相同的问题,就是对依赖识别的确实和错误的情况很多,幻觉严重。 目前我们在大模型自动生成结构化覆盖率(来自航电DO178c安全标准,包括行覆盖、判定覆盖、mcdc覆盖)的ut实践中,使用了一种playground方式,即给出一个可编译工程的任意一个文件中的任一函数,都可以找出并仅找出它的所有直接和间接外部依赖,包括不限于:
- 接口
- 数据类型
- 值宏
- 过程宏
- 枚举
- 全局变量
。。。 从而形成代码依赖图,示意图如下所示:
实际代码案例如下图:
示例代码实例来看下:
main函数调用fuctionA,fuctionA调用fuctionB和fuctionC,就可以生成4个playground,每个函数一个工程目录。
其中每个playground目录都可以独立编译成dll,并可以和测试框架+外部依赖进行link生成测试exe,完成ut防护网。
playground中所有的直接依赖、间接依赖都是通过工具脚本和算法扫描源代码实现的,不引入大模型操作,不会产生大模型幻觉问题。
内部依赖本身就在本模块之中,不需要额外处理。
对于外部依赖,就是我们重点关注的地方。
tips:
这里对外部依赖做一个约定,外部依赖的范围可以自定义,所谓外部,知道是本模块的外部,不仅包括三方依赖,也可以是本工程中本模块外的模块,这样可以采用分治的手段,按不同粒度灵活治理所有模块的依赖问题。
人工治理外部依赖的方式:
参照人工治理依赖的方式,就可以使用大模型自动进行依赖的自动治理,大模型依赖治理的整体流程如下:
从上图看,llm自动依赖治理整体分两部分:
1、依赖治理
针对外部依赖的数据类型和接口,生成外部依赖适配层;同时实现依赖注入(可以通过抽象注入或者编译开关的方式)
2、ut防护网
a、ut生成
llm根据本模块接口自动生成测试用例,然后根据外部依赖类型和接口原型,选择合适的mocker框架,进行外部依赖的mock桩(含fake),然后依据mocker完成ut用例。
b、结构化覆盖率统计
- 覆盖率插桩
使用自研结构化覆盖率工具进行代码插桩
- 覆盖率日志生成
根据playground梳理好的所有内部依赖+外部依赖适配层+外部依赖mock,就可以编译链接成测试exe,运行exe后就可以生成覆盖率日志。
- 统计覆盖率 使用结构化覆盖率工具分析覆盖率日志从而统计覆盖率,并生成缺失mdcd覆盖率缺失的分支和mcdc pair,供大模型进行ut补充。
c、ut修复 llm修复不通过ut,并补充缺失ut,形成预期覆盖率的ut防护网,从而确保依赖治理后功能正确性。
综上,通过playground梳理出的工程的所有内外部依赖,使用大模型对外部依赖通过适配层进行自动依赖隔离、依赖注入;并自动生成mock,完善ut防护网,从而攻克了大模型重构之中最大障碍依赖治理环节,高效治理架构腐化,为大模型大规模代码提效铺平道路。
声明:来自丁辉的软件架构说,仅代表创作者观点。链接:https://eyangzhen.com/3832.html