playground:攻克大模型系统重构中依赖治理的难题

目前大模型在编程领域应用具有确定性,是大模型应用的明确赛道,也吸引了各大厂商一拥而入。

    但是各种大模型辅助代码生成的实践中,代码生成效果都是依赖于私域知识的多少和代码架构的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

丁辉的软件架构说的头像丁辉的软件架构说

相关推荐

关注我们
关注我们
购买服务
购买服务
返回顶部