ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

LLM+规则引擎:混合架构如何落地AI代码审查与CI门禁

LLM+规则引擎:混合架构如何落地AI代码审查与CI门禁 代码审查这件事绝大多数工程团队都处于“嘴上说重要手上没时间”的状态。我这些年见过太多仓库的PR列表——要么是机器人在下面刷一堆格式警告人被折腾得麻木要么是什么检查都没有合并全靠交情和运气。所以当LLM开始写代码的时候我心里冒出的第一个念头就是能不能让LLM把代码审查这活儿真正扛起来而不是停留在拿个diff“玩两下”的演示demo阶段。open-code-review正是在这个背景下出现的工程化思路。它的核心不是又封装了一个“让AI看diff”的脚本而是把“确定性流水线”和“LLM Agent”组合成一套混合架构确定性流水线处理那些机器本来就能干好的事LLM Agent再接手那些需要语义理解、逻辑推断的开放性问题。说人话就是把代码审查拆成“规矩检查”和“智能判断”两段让各自最擅长的那一层去干各自的活。这篇文章就围绕open-code-review这套混合架构来拆我会讲清楚为什么纯规则和纯LLM都走不通、整体模块怎么划分、核心流程每一步怎么落地、以及真正上了生产环境之后一定会碰到的坑。如果你正在做AI代码审查工具想在团队CI里引入AI审查能力或者纯粹想看看LLM怎么在严格的工程约束下落地这篇值得读完。1. 为什么代码审查必须走向混合架构1.1 纯LLM审查看着聪明实则没法当门禁我先讲讲自己最开始用纯LLM做代码审查的真实体验。当时的想法很简单把git diff拿过来拼进prompt让模型“帮我看看有没有问题”。演示的时候效果确实惊艳模型能指出空指针隐患、能分析并发风险、甚至能给重构建议。但一旦正经挂到CI里问题就全冒出来了。第一个是上下文瓶颈。一个中型项目的MR动辄十几个文件、上千行变更即便是支持超长上下文的模型硬塞进去也经常是回答到一半就开始“失忆”——前面的文件忘了、后面的文件没看完最后给的意见东一榔头西一棒槌。第二个是幻觉。模型真会一本正经指出一个“错误”但对应的代码行可能根本不存在或者把A文件的变量记到了B文件头上。第三个是最要命的不确定性。同一个diff同一个模型跑三次可能给出三种不同倾向的结论。如果审查结果本身就不稳定那它就不可能成为一个可信赖的工程门禁。不是模型不够好而是用法有问题。代码审查本质上是一个“确定性优先”的场景要求可重复、可追溯、可解释这几条恰好是当前纯LLM最不擅长的地方。1.2 确定性流水线笨但是可靠跟LLM形成鲜明对比的是另一类我们其实非常熟悉的工具——静态分析器、AST解析器、规则引擎。这些工具在AI热起来之后显得特别“不性感”但它们的优势摆在那确定性、可重复、毫秒级执行、接近零成本还有一个容易被忽略的点——可解释性。它说某某文件第几行有问题一定能给出一条可复现的判定路径贴出规则ID和说明人去看一眼就知道对不对。我在团队里做审查工具的时候越来越觉得这些确定性工具才是地基。拿安全漏洞扫描来说硬编码密钥、SQL拼接、反序列化入口这类问题本质上是模式匹配问题只要规则写得好机器检出率能做到接近百分之百而且不会疲劳。再比如代码风格、命名规范、明显的空指针路径这些通通适合用规则锁死根本不需要动用一个几毫秒就要烧掉大量token的LLM来“思考”。但如果只靠确定性工具又会走向另一个极端覆盖面有限只能抓表面对“逻辑是否合理”“设计是否恰当”“边界条件是否遗漏”这类开放性问题规则完全无能为力。于是我们需要第二层。1.3 混合架构到底怎么分工open-code-review这套架构的逻辑其实很朴素让规则先跑一遍把“机器能判断的事”全部处理掉剩下的才是需要“理解”的地方再交给LLM Agent。两者不是简单叠加而是有清晰的优先级和边界。打个比方这就像一个高效的评审会议室。房间里站着两个角色一个是拿着检查清单的质检员逐一核对提交物是否满足硬性标准不放过任何一条另一个是评审方案的技术专家不看格式、拼写问题精力全部放在“这个设计走不走得通”“这段并发逻辑有没有隐患”“这个接口抽象合不合理”上。质检员先过一遍把基础问题全部处理完技术专家只需盯着剩下真正值得讨论的部分。落在代码里这套分工体现在数据流上变更信息进入流水线经过规则引擎和静态分析先产出一批结构化的“可疑点”这些可疑点连同代码片段作为上下文送入LLM AgentLLM Agent的职责不是复述规则而是在规则基础上做更进一步的语义判断——比如一个告警到底是真的风险还是误报一个逻辑缺陷是否值得阻断合并。最后由聚合模块合并、分级、写回PR。这套逻辑还有一个重要优点系统可以“抛弃”LLM仍然能工作只是退回纯规则模式而LLM只处理它真正不可替代的部分把不确定性和成本压到最低。很多团队把“AI原生”这四个字看得太重反而把一个稳定的系统做成了离不开模型的脆弱系统这是很可惜的。2. open-code-review 整体架构拆解2.1 从一次提交到一份审查报告的完整链路从功能视角看open-code-review可以拆成几个职责单一的模块彼此通过结构化数据衔接。变更采集模块负责从代码仓库拿到本次提交或MR的全量信息包括git diff、变更文件清单、提交说明并识别代码变更类型新增、修改、删除和关联的工程上下文。静态分析模块是确定性流水线的核心对变更文件做AST解析、重放类型信息、跑内置和可扩展的代码规则产出结构化问题列表。智能审查模块也就是LLM Agent层接收前面产出的结构化结果结合代码片段做更深层的语义分析输出带理由的判断。聚合与报告模块负责把规则引擎结果和LLM结果做合并、去重、去冲突按严重级别排序最终生成一份统一的审查意见。集成适配模块则对接不同代码托管平台和CI系统把结果发布成MR评论、状态检查或Webhook通知。这套拆分有一个明显的好处模块之间完全松耦合。静态分析模块可以不依赖LLM服务独立运行LLM Agent模块也可以单独对任意代码片段做离线分析。哪怕你暂时没配模型API这套工具体系依然能作为高质量的传统静态分析工具使用。LLM是可选增强项不是唯一主干。2.2 确定性流水线的真实职责边界在open-code-review里确定性流水线不只是一个“前置过滤器”它有四个非常具体的职责。第一变更结构和上下文提取。永远不要让LLM自己去看原始仓库流水线会把diff解析成结构化对象按文件类型、变更幅度、依赖关系打标签决定哪些内容值得送进审查哪些只是格式化调整可以直接忽略。第二硬性规则执行。默认内置了一批规则集基础安全性规则密钥泄露、依赖注入风险、命令执行入口、整洁性规则函数过长、圈复杂度过高、魔法数字、一致性规则命名、格式化、导入排序。每条规则有规则ID、检查脚本、触发阈值。这一层跑完整体审查报告中大约六到七成的“问题点”已经产出而且不消耗任何token。第三AST级别的事实抽取。这个很多人会忽略但极其重要。通过解析语法树系统能为后续LLM审查提供精确的“事实基础”这个文件有几个公共API、这个函数引用了哪些外部依赖、某个变量在哪些分支里被重新赋值。把事实交给LLM而不是把源码原样丢给LLM能显著降低幻觉概率。第四输入裁剪与Token预算控制。这也是工程化的关键一步。它计算每个文件、每个目标的预估token消耗决定缓存哪些上下文、切分哪些大文件、丢弃哪些无关导入让后续LLM Agent运行成本可控、延迟可控。很多自研AI审查项目之所以又贵又慢问题往往就出在这一层没做好。2.3 LLM Agent的职责边界只做“理解”不做“查找”LLM Agent这一层的职责需要被刻意收敛。在open-code-review的配置里LLM Agent不会去做“在代码里找特定模式”的活儿那是规则引擎的擅长区让LLM去跑等于花大钱办小事还带着幻觉风险。LLM Agent真正介入的是三类任务。第一类是多重信号确认对规则引擎产出的可疑点做二次判断。比如静态分析发现某个函数可能存在资源泄漏但这类规则实际命中率可能只有百分之四十左右直接爆出来会非常烦人。这时LLM会结合上下文判断这个文件里是否存在合适的释放逻辑异常路径上有没有补偿措施如果确实有这条告警就会被降级或过滤掉。第二类是深层次逻辑审查比如并发控制、状态一致性、边界条件、跨接口调用链风险。这些问题的特征是没有固定的代码模式可匹配需要理解业务意图是人类评审中最花时间的部分也是LLM相对规则引擎最不可替代的部分。第三类是语义解释与建议生成。对已确认的问题LLM需要输出人能看懂的解释、复现路径和修复建议。这不是锦上添花而是让审查结果具备可执行性的关键一环。很多审查工具输出一个“高风险”等级就完了开发者根本不知道下一步该干什么审查的落地价值就大打折扣。另外贯穿始终的一个原则是LLM的输出必须是结构化、可约束的。open-code-review要求Agent以固定的JSON schema输出结果必须包含文件路径、起始行号、结束行号、严重级别、问题类别、置信度、解释和修复建议任何缺失字段的响应都会被重试而不是被采用。这是整套架构能够与聚合、门禁逻辑顺利衔接的前提。3. 核心流程实操实现3.1 变更采集与上下文构建不要想把整个仓库递给模型讲链路实操先说第一步。变更采集在open-code-review里有两个要点一是只处理当前变更二是重构上下文。只处理当前变更意味着要把基线版本和变更版本的差异精确算出来而不是简单把整个文件丢过去。实际执行用git diff配合前置过滤把文件类型、路径、变更行数都读出来过滤掉锁文件、生成的schema、打包产物这类无意义文件。单个文件变更行数特别多的还需要做窗口裁剪只保留靠近变更区间的上下文代码和相关函数定义。重构上下文是决定LLM审查质量的核心环节。经验上给LLM的第一个输入单元不应该是“变更行前前后后”的朴素摘录而应该是这样组合出来的包变更发生所在函数体、被变更调用的上游函数签名、变更涉及的数据库表或外部接口的简要说明、规则引擎产生的候选问题列表。以这种组合包输入LLM才真正具备了“看懂一处修改到底在干什么”的外部知识。这里还会用到一项很实用的技术按语义单元裁剪。AST解析出函数和类的边界系统只提取与变更行相关的语义单元import语句、工具函数、整块注释全部丢弃。我见过一个项目在没做裁剪前一次MR审查要烧掉近二十万token做完语义裁剪后同样的审查只需要五万左右而结论质量不降反升。LLM的依据更聚焦噪音减少幻觉率随之下降。3.2 规则引擎与静态分析的协同策略再往下一层是规则引擎的实现。open-code-review内置规则的检查逻辑基本是AST配合代码属性图去跑的每一条配置包含目标、检查方式、参数阈值和严重级别。举三个典型例子。性能规则函数圈复杂度超过阈值且包含深层嵌套循环标记为可能存在性能隐患。这条规则在AST上统计分支节点数和循环节点数就能完成不需要LLM参与。安全规则检测外部输入拼接进入命令执行或SQL执行方法的数据流路径用数据流追踪实现确定性极高。一致性规则团队要求所有对外REST接口入参必须做显式校验被翻译成“在public方法入口处是否出现校验调用”属于简单模式匹配。实际搭配中更有效的做法是给规则引擎配置一个“报告倾向”参数。open-code-review的思路是规则引擎保守地报全LLM Agent负责过滤而不是反过来让规则引擎为了压低误报而牺牲召回。宁可规则层把可能有问题的十处都标出来也不要因为怕打扰人而漏掉关键的一处。后续有LLM做二次确认多标出来的不准确点还可以当作训练Agent判断的样本一举两得。3.3 LLM Agent提示词与输出约束设计到了LLM这一环提示词设计的优先级比很多人想象的要低真正决定成败的是输出约束和上下文组织。输出约束我用JSON Schema加两层校验先在prompt里把schema明确写进去要求模型只输出JSON不要输出任何多余的解释字段然后把模型原始输出交给一个校验器解析如果字段缺失或者行号越界直接判定本次请求失败并触发重试。千万不要相信“模型应该能理解我的格式要求”大概率会翻车校验器才是真正的契约。提示词骨架大致包含五段角色定位资深代码评审专家只对提供的代码片段负责、任务目标给定变更和相关规则报告判断问题真假并给出解释、输入数据结构化问题清单和目标代码段、输出约束JSON字段描述、示例一到两个few-shot示例。特别推荐放一个“规则报告说有问题但实际没问题”的反例这能显著提升模型对误报的识别能力。值得注意提示词里要明确禁止模型自行扩大审查范围。它必须只围绕给定的代码片段和问题清单输出不主动去提“建议重构整个模块”之类的发散性意见否则聚合阶段会收到一堆无法对号入座的意见非常痛苦。3.4 结果聚合、分级与门禁集成所有环节跑完进入聚合和汇报阶段。这个阶段open-code-review有两个原则一个是宁缺毋滥一个是人可以反驳机器。宁缺毋滥体现在严重级别分级上。问题统一划为四个等级P0阻断合并必须修复、P1强烈建议修复通常限期处理、P2建议修复不强制、P3提示信息仅供讨论。P0和P1的判定不能只靠LLM一句话必须由规则引擎的可复现证据加上LLM的高置信度共同支持。证据不足时一律不得标成P0。人可以反驳机器体现在反馈闭环上。审查报告推送到MR之后开发者可以对每一条意见执行“确认”“忽略”“标记误报”三个动作。这些反馈会被记录下来定期汇总成新的过滤规则或few-shot示例后续审查准确率会随反馈积累越来越高。我强烈建议任何准备做AI审查工具的人把反馈闭环考虑进去——一套没有反馈学习能力的审查系统误报率会始终停在同一个水平上永远到不了“顺手好用”的程度。门禁集成方面推荐的做法是P0级别的规则引擎命中直接由CI强制执行LLM的P0意见默认只作为“阻塞建议”而不是“强制阻断”。这也是一个很务实的取舍把确定性的问题交给自动化门禁把不确定的判断留给人做最终决定。既控制了风险又不至于让AI意见变成阻碍团队协作的噪音。4. 工程化落地中的常见问题与排查4.1 上下文总是超长怎么办这是被问到最多的问题。处理超长提交不要直接上“更长的上下文窗口”——那是花钱买罪受需要的是分而治之的策略。open-code-review里约定单个Agent请求的目标上下文不超过八千个token。超出的部分先按文件粒度切片每个文件单独构建Agent请求如果单个文件仍然太大再按语义单元切片把文件内函数块拆开分别请求。切完之后会有一个汇总层Agent把这些零散的审查结果合并成一份不重复、不冲突的整体报告。这个汇总层并不复杂只需基于结构化的结果列表做合并去重不需要再看源码。为了控制成本汇总请求通常用一个比主审查弱一些的模型就足够。实测心得还有一个上下文超长的根源往往不在代码量而在raw diff撕裂了代码的结构。一个函数只是加了两行raw diff会把整个函数体都打印出来真正的变更点只有两行。做好“变更点定位”非常重要只围绕变更行及其紧邻的函数体重建上下文而不是把变更过的文件浑身扫一遍。这个动作能把token消耗至少打一个七折。4.2 误报率怎么持续压低误报是AI审查工具能不能在团队里活下来的生死线。第一周大家可能还觉得新鲜两周后如果有超过三成意见是错的这工具基本会被禁掉。我压误报的经验可以总结成四层。第一层是规则先行能用规则的不用LLM规则的误报是可控的。第二层是置信度门槛open-code-review要求LLM为每条结论输出置信度低于阈值的意见默认降级为P3提示甚至直接丢弃。第三层是上下文充足性检查系统在送LLM之前检查上下文是否足够如果发现某个文件缺失关键调用方信息宁可跳过这条也不让模型盲猜。第四层是反馈闭环前面提到的“忽略/误报”标记要定期进入规则库和示例库。还有一个细节给LLM配置“保守倾向”。在提示词里通过少量示例引导模型只在证据充分时输出高严重级别拿不准的标注为“待确认”而不是“存在问题”。这个倾向性设计配合置信度过滤能让整体误报率在真实项目上稳定低于百分之十五左右这是一个很可用的水平。4.3 成本与延迟AI审查也要讲性价比说到成本很多人会焦虑每次MR都调用大模型一个中型团队一个月得烧多少钱实际算下来如果做了规则先行和数据裁剪开销远没想象中夸张。按一次常规MR计算经过规则引擎过滤后能进入LLM阶段的问题候选通常不超过十个加上按语义单元构建的上下文单次提交总token消耗通常在四万到八万之间。按商用模型价格折算单次成本也就几毛到一两块钱一个月几万次提交几千块钱到头。这个费用放在工程师人工评审的成本面前几乎可以忽略。延迟优化重点是异步化和缓存。代码托管平台的Webhook触发后在后台异步执行整条流水线不阻塞合并主流程结果出来后再通过评论推送。规则引擎的结论按文件哈希缓存文件没变就不重复计算。同一个MR被反复推送新commit时只对增量部分重新跑LLM历史结论直接复用。这些做好之后大多数MR能在三分钟以内拿到完整审查意见完全够用。4.4 可解释性怎么让开发者信服最后一个坑也是很多技术人容易忽略的开发者对AI审查结果天然不信任。如果只丢一句“这里可能有问题”大概率直接被划走。所以报告必须以开发者熟悉的语言来讲。我要求报告中每条意见都挂载三样东西证据位置精确到文件和行号、触发依据规则引擎给规则ID和判定条件LLM给关键推理路径、修复建议一个可以直接操作的方向最好带代码示例。这样开发者看到意见的第一反应是去定位而不是先怀疑AI在瞎说。这个习惯还带来另一个好处日后调整规则或提示词全量历史报告还能拿来做回归对比排查某条意见为什么变了——这也是可解释性在维护层面上的价值。5. 一套可复用的配置模板与实测效果5.1 起步配置参考我把实测中比较满意的一套基础配置贴出来可以当成起步模板直接抄。规则引擎部分启用安全性、整洁性、一致性三套规则集圈复杂度阈值设为10单函数最大行数设为80单文件候选问题上限设为20条。LLM Agent部分温度固定为0避免任何随机波动单请求超时90秒最大并发4置信度阈值为0.75低于此值的自动降级。聚合模块部分P0必须同时满足规则引擎可复现证据和LLM置信度大于0.9两个条件P1要求置信度大于0.8或规则引擎中高严重度命中其余均进P2或P3。集成仓库时建议先用一个测试MR跑通全链路人工核对一遍输出再逐步扩大灰度范围。不要一开始就在主干仓库上全量开启AI审查工具的引入节奏和代码规范落地是一样的需要给团队适应时间。5.2 实测两个月的数据我带着这套架构在一个几十人规模的研发团队里跑了两个月一些数据可以分享规则引擎平均每个MR生成5到8条硬性意见LLM Agent从中二次过滤后保留3到5条另外独立识别出1到2条规则抓不到的深层问题。最终开发者接受确认或修复的比例在六成左右。作为对比之前纯LLM方式接受率不到三成主要因为误报太多且意见发散。还有个反直觉的发现LLM Agent过滤掉的那部分规则告警恰恰是团队最感激的功能。以前静态检查报出一堆“疑似问题”大家麻木后连真正的问题也一起忽略。现在把确定的问题提炼出来噪音少了反而比纯增加审查条数更能提升代码质量的一致性。我个人在实际操作中的体会是这套混合架构最值钱的不是模型本身而是边界划分的克制。哪些事必须交给确定性机器去扛、哪些事值得花钱请LLM来“想一想”这个分寸想清楚了哪怕以后模型换了一茬又一茬架构也能一直适用。如果你打算在团队里落地AI代码审查别急着去追求“更聪明的模型”先把你自己的确定性流水线夯实再把LLM这只聪明但偶尔走神的助手请进来你会看到效率和质量同时提升。
返回列表