大促备战中的代码评审困境与破局
双十一大促是系统稳定性的终极“大考”。为规避上线风险,技术侧会启动系统封板管控,主动将非紧急需求的发布窗口前置。这一举措在保障系统稳定性的同时,也必然导致研发需求的前置与集中,使得封板前的代码评审任务量显著增加。我们面临着一个严峻的“量与质”的挑战:
如何在时间紧、任务重的双重压力下,确保代码评审的效率与质量,从而前置发现潜在风险,有效拦截线上BUG?
传统的代码评审模式在此场景下效率低、质量差(风险遗漏的可能性高),而现有的AI辅助工具又因误报率高而陷入尴尬:产生的多数评审意见并无实质帮助,工程师仍需花费大量时间进行判断与筛选。
正是在此背景下,【供应链技术部-商家导入研发组】在AI代码评审方面进行了一些探索,尝试将知识工程代码知识检索能力与AutoBots(已更名为:JoyAgent)知识库检索能力相结合,构建了一套代码评审系统。这套双RAG架构为我们的代码评审工作提供了一些新思路,在此分享出来,希望与各位同行交流探讨,共同进步。
现有技术方案的局限性
技术1:基于流水线的AI代码评审方案
核心技术路径: 在通过公共流程(Webhook触发、解析MR、获取Diff)得到代码变更内容后,该方案的核心处理流程如下:
核心问题识别:
技术2:基于JoyAgent知识库的RAG代码评审
核心技术路径: 在通过公共流程获取代码差异后,该方案的核心流程如下:
核心问题识别:
从线上问题到技术突破
问题1:三方系统空值处理异常
示例:
// 问题代码:三方系统地址编码字段处理
request.setAddressCode(String.valueOf(address.getCode()));
// 当address.getCode()为null时,String.valueOf(null)返回"null"字符串
// 导致三方系统Integer.parseInt("null")抛出NumberFormatException
技术1的问题:
理论上,可以通过在Prompt中硬编码“三方接口地址编码须为数字类型字符串” 的规则来识别此问题。然而,随着业务场景增多,所有规则都被挤压在有限的上下文窗口内竞争。当代码变更触发自动压缩(如截断至10行)时,被保留的上下文具有极大的随机性,与当前评审强相关的评审规则很可能被其他无关规则挤掉或因自动压缩而被截掉,导致其无法被稳定触发,从而漏报。
技术2的问题:
该方案虽然理论上能够通过知识库检索到相关规则,但其不稳定的知识归纳过程导致代码上下文的理解时好时坏,使得规则检索的准确性波动较大。同时,未对检索结果进行重排序,进一步放大了这种不确定性。最终,由于缺乏稳定、可靠的上下文支撑,系统无法持续、准确地识别此类问题,其评审结果表现出显著的随机性。
问题2:EDI项目中的语法错误
示例:
<!-- 错误:使用变量而非字面常量 -->
<case value="${orderType}">
<!-- 正确应使用字面值:<case value="NORMAL"> -->
EDI平台介绍:
EDI(电子数据交换)是用来解决京东物流与多样化商家系统间的对接难题的技术,其关键功能包括协议转换、数据格式转换、数据校验和流程编排。这意味着EDI配置文件必须严格遵守预定义的语法和标准,任何偏差都可能导致平台的核心转换与校验功能失效。
技术1的问题:
由于其缺乏对EDI配置语法与规范的领域知识,如果自定义规则,会遇到问题1一样的提示词天花板和上下文截断的问题。
技术2的问题:
除了上面提到的知识归纳过程的不稳定问题,技术2也面临一个更前置的的挑战:它缺乏对项目身份的感知能力。系统在处理一个XML配置文件时,无法自动识别它隶属于“EDI项目”而非普通Java应用。因此,在后续的RAG检索过程中,它极有可能使用通用的Java代码评审规则,而无法精准命中“EDI专用配置规范”这一关键上下文,导致检索方向错误,最终无法识别出必须使用字面常量这一特定于EDI领域的合规性要求。
解决方案:双RAG架构

1. 识别项目类型
2. 代码分块处理
2.1 Token估算算法
由于我们使用的底层大模型是JoyAI,并没有公开tokenizer的细节,根据官网文档提供的token计算API: http://api.chatrhino.jd.com/api/v1/tokenizer/estimate-token-count
测试了几组数据:
| 字符长度 | 实际Token数 | 内容Token增量 | |
|---|---|---|---|
| 空字符串 | 0 | 63 | 0 |
| "a" | 1 | 64 | +1 |
| "hello" | 5 | 64 | +1 |
| "code" | 4 | 64 | +1 |
| "hello world" | 11 | 65 | +2 |
| "测试" | 2 | 64 | +1 |
| "编程编程" | 4 | 65 | +2 |
| "测试测试测试测试测试" | 10 | 68 | +5 |
| "hello世界" | 7 | 65 | +2 |
| "programming代码" | 13 | 66 | +3 |
| 重复"programming代码"3次 | 39 | 72 | +9 |
推导过程
通过分析测试数据,我们发现了以下关键规律:
基于上述规律,我们构建了以下估算公式:
总Tokens = 63 + ∑(单词token)单词token计算:
- 单字符单词: 1 token
- 英文单词(≤5字符): 1 token
- 英文单词(6-10字符): 2 tokens
- 英文单词(≥11字符): 3 tokens
- 中文文本: (字符数 + 1) / 2 tokens
- 混合内容: 分段计算后求和
2.2 分块阈值与安全设计

2.3 智能分块策略
系统采用两级分块策略,确保代码语义的完整性:
2.3.1 文件级分割
通过git diff指令识别文件边界,确保单个文件的代码完整性,避免跨文件分割。
Pattern.compile("diff --git a/(.+?) b/(.+?)\n")
2.3.2 代码结构感知分割
利用方法签名模式识别代码结构边界:
Pattern methodPattern = Pattern.compile("([+-]\\s*((public|private|protected)\\s+)?(\\w+\\s+)?\\w+\\s*\\([^)]*\\)\\s*\\{)",Pattern.MULTILINE);
在方法或类的自然边界处进行分割,最大限度保持代码块的语义完整性。
3. RAG增强与重排序机制
3.1 基于知识工程的代码片段、业务上下文的检索
在 RAG增加服务中实现多维度检索增强:
重排序优化:对检索结果使用BGE模型进行重排序,提升相关性。
3.2 重排序
在RAG系统中,检索(召回)这一步通常使用向量相似度搜索。这种方法追求的是高召回率——即尽可能不遗漏任何可能相关的文档。但这就带来了一个问题:
例如检索“如何做番茄炒蛋”,向量相似度查询结果可能会找到:
如果不经处理,把这四篇文档塞给大模型,模型需要费力地从大量文本中辨别哪些是真正有用的信息,不仅增加了Token消耗,更严重的是,无关信息会形成“噪声”,干扰模型的判断,导致生成质量下降——模型幻觉。
为了节省成本,我们使用了本地重排序方案:
// 核心流程
public List<Map.Entry<String, Float>> rerankBatch(String query, List<String> documents) {// 1. 文本预处理和分词// 2. 构建查询-文档对// 3. ONNX模型推理// 4. 相关性评分计算// 5. 按分数降序排序// 6. 返回排序结果
}
示例:

4. 实际应用效果验证
案例1:成功预防空值处理事故

案例2:EDI配置规范检查

总结与展望
我们探索出的双RAG架构,其价值核心并非追求极致的简单或敏捷,而是它既能像资深的一线研发一样,深度理解业务及代码变更的具体语境与潜在影响,又能像严谨的架构师一样,严格遵循成文的规范与最佳实践。
通过结构化的协同机制,系统将两种不同质、不同源的知识(深度的代码语义与精准的评审规则)进行融合,实现了 “1+1 > 2” 的智能涌现,从而具备了识别并预防那些复杂、隐蔽代码缺陷的深度推理能力。这正是我们在高并发、高可用要求极为严苛的大促等场景下,为夯实系统稳定性基石所做出的关键性架构决策。
这一成功实践,为我们奠定了代码评审工作中坚实的技术基石,并清晰地指明了未来的演进路径:
虽然探索的道路并非坦途,我们曾在具体的技术细节中陷入困境,例如,为了在 CentOS 7.9 的环境中支持高版本 ONNX 运行时以启用重排序功能,不得不手动编写docker脚本从源码编译高版本的cglib依赖。这段经历,恰恰印证了弗雷德里克·布鲁克斯在《人月神话》中所揭示的那句箴言:
The only way to accelerate software work is to simplify the product and the process, and to face the essential complexity of the software task itself with courage and skill.