
AI 研发团队的代码审查流程与质量门禁设计AI 项目的代码审查有它的特殊性除了常规的代码质量你还需要关注 Prompt 版本管理、模型行为的可测试性、推理成本的合理性。本文分享一套经过实践验证的 AI 研发团队代码审查流程以及配套的质量门禁Quality Gate设计方案。一、AI 项目 Code Review 的特殊挑战传统软件的 Code Review 主要关注逻辑、性能、安全。AI 项目在此基础上额外增加了挑战具体表现Prompt 版本管理Prompt 改了一行模型行为可能翻天覆地但 diff 看不出来模型行为不确定性同样的代码不同时间跑出来结果不同推理成本监控一次功能改动可能让 Token 消耗翻倍幻觉和安全风险LLM 调用结果是否有充分的校验依赖版本LLM API 版本、模型版本的变化影响行为测试困难怎么测回答质量不能只靠断言二、代码审查清单AI 项目专项检查项在常规 Code Review 清单基础上增加以下 AI 专项2.1 LLM 调用层检查## LLM 调用 Review Checklist ### Prompt 质量 - [ ] Prompt 是否有版本号/注释说明上次修改原因 - [ ] System Prompt 是否有足够的边界约束拒绝不合理请求 - [ ] 是否避免了模糊指令写一段代码 vs 写一个 Python 函数接受...返回... - [ ] Few-shot 示例质量是否经过验证 ### 错误处理 - [ ] 是否处理了 API Rate Limit 错误429并实现了指数退避 - [ ] 是否处理了模型输出格式错误非 JSON、截断输出等 - [ ] 是否处理了内容过滤触发Moderation的情况 - [ ] 网络超时是否有合理设置推荐 30-60s ### 成本控制 - [ ] max_tokens 是否设置了合理上限 - [ ] 是否有 Token 消耗的监控埋点 - [ ] 是否避免了不必要的重复调用能缓存的结果有没有缓存 - [ ] 模型选择是否合理简单任务用便宜模型 ### 安全 - [ ] 用户输入是否经过清洗防止 Prompt 注入 - [ ] 模型输出是否经过验证才用于后续业务逻辑 - [ ] 是否避免了把敏感信息密钥、PII送入 LLM2.2 Agent / 工具调用检查## Agent 代码 Review Checklist ### 工具设计 - [ ] 工具描述是否准确模型看描述就能正确使用工具 - [ ] 工具参数类型是否有验证Pydantic 或 JSONSchema - [ ] 工具执行是否有超时保护 - [ ] 工具失败是否有降级策略 ### Agent 循环 - [ ] 是否有最大迭代次数限制防止无限循环 - [ ] 是否有总时长/成本上限 - [ ] 关键操作是否有人工确认步骤 ### 状态管理 - [ ] Agent 状态是否持久化服务重启后能恢复 - [ ] 多 Agent 共享状态是否有并发保护三、质量门禁Quality Gate设计质量门禁是 CI/CD 流程中的自动化检查节点代码不过门禁不允许合并。3.1 整体架构PR 提交 ↓ [Gate 1: 基础代码质量] - 代码格式 (Black, isort) - 静态分析 (Pylint, mypy) - 安全扫描 (Bandit) ↓ Pass [Gate 2: AI 专项检查] - Prompt 版本追踪 - Token 成本估算 - 模型调用安全检查 ↓ Pass [Gate 3: 测试覆盖] - 单元测试 (pytest) - AI 行为测试 (自定义评估) - 覆盖率 80% ↓ Pass [Gate 4: 性能回归] - P95 延迟对比 - Token 消耗对比与 main 分支比较 ↓ Pass 允许合并3.2 Gate 1基础代码质量GitHub Actions 实现# .github/workflows/quality-gate.ymlname:Quality Gateon:[pull_request]jobs:code-quality:runs-on:ubuntu-lateststeps:-uses:actions/checkoutv4-name:Setup Pythonuses:actions/setup-pythonv5with:python-version:3.12-name:Install dependenciesrun:|pip install black isort pylint mypy bandit pytest pytest-cov pip install -r requirements.txt# 代码格式检查-name:Black format checkrun:black--check--line-length 120 src/-name:Import sort checkrun:isort--check-only--profile black src/# 类型检查-name:Type checkrun:mypy src/--ignore-missing-imports--strict# 安全扫描-name:Security scanrun:bandit-r src/-ll# 只报告中级以上问题# 基础测试-name:Run testsrun:pytest tests/unit/-v--covsrc--cov-reportxmlenv:OPENAI_API_KEY:${{secrets.OPENAI_API_KEY}}# 覆盖率门禁-name:Coverage checkrun:|coverage report --fail-under803.3 Gate 2AI 专项检查脚本# scripts/ai_quality_check.py AI 项目专项质量检查脚本 在 CI 中运行检查 LLM 相关代码的质量问题 importastimportsysimportrefrompathlibimportPathfromdataclassesimportdataclass,fieldfromtypingimportListdataclassclassAIQualityIssue:file:strline:intseverity:str# error | warning | infomessage:strclassAICodeChecker:def__init__(self,source_dir:str):self.source_dirPath(source_dir)self.issues:List[AIQualityIssue][]defcheck_all(self)-List[AIQualityIssue]:运行所有检查forpy_fileinself.source_dir.rglob(*.py):self._check_file(py_file)returnself.issuesdef_check_file(self,file_path:Path):检查单个文件try:sourcefile_path.read_text(encodingutf-8)treeast.parse(source)exceptException:returnself._check_llm_calls(source,file_path,tree)self._check_prompt_versioning(source,file_path)self._check_error_handling(source,file_path,tree)self._check_sensitive_data(source,file_path)def_check_llm_calls(self,source:str,file_path:Path,tree):检查 LLM 调用质量linessource.split(\n)fori,lineinenumerate(lines,1):# 检查是否设置了 max_tokensifclient.chat.completions.createinlineor\client.messages.createinline:# 向下找 max_tokenscontext\n.join(lines[i:i15])ifmax_tokensnotincontext:self.issues.append(AIQualityIssue(filestr(file_path),linei,severitywarning,messageLLM 调用未设置 max_tokens可能导致不必要的 Token 消耗))# 检查超时设置ifopenai.OpenAI(inlineoranthropic.Anthropic(inline:iftimeoutnotinline:self.issues.append(AIQualityIssue(filestr(file_path),linei,severitywarning,messageLLM 客户端初始化未设置超时建议设置 timeout60))def_check_prompt_versioning(self,source:str,file_path:Path):检查 Prompt 是否有版本/变更注释linessource.split(\n)fori,lineinenumerate(lines,1):# 找到长 Prompt 字符串ifinlineorinline:# 检查前后是否有 PROMPT_VERSION 或 # v 注释context\n.join(lines[max(0,i-3):i1])if(system_messageincontext.lower()orsystem_promptincontext.lower()orSYSTEMincontext)and\# vnotincontextand\PROMPT_VERSIONnotincontextand\# updatednotincontext.lower():self.issues.append(AIQualityIssue(filestr(file_path),linei,severityinfo,messageSystem Prompt 建议添加版本注释# v1.2 - 2024-xx方便追踪变更))breakdef_check_sensitive_data(self,source:str,file_path:Path):检查是否有敏感数据被送入 LLMsensitive_patterns[(rpassword\s*,password 变量可能被送入 LLM),(rapi_key\s*,API Key 可能被送入 LLM),(rsecret\s*,secret 变量可能被送入 LLM),]linessource.split(\n)fori,lineinenumerate(lines,1):forpattern,msginsensitive_patterns:ifre.search(pattern,line,re.IGNORECASE):# 检查附近是否有 LLM 调用nearby\n.join(lines[max(0,i-5):min(len(lines),i5)])ifmessagesinnearbyand(invokeinnearbyorcreateinnearby):self.issues.append(AIQualityIssue(filestr(file_path),linei,severityerror,messagef⚠️ 潜在安全问题{msg}))def_check_error_handling(self,source:str,file_path:Path,tree):检查 LLM 调用是否有错误处理linessource.split(\n)fori,lineinenumerate(lines,1):if.invoke(inlineor.create(inline:# 检查是否在 try-except 块中# 简化检查看前10行是否有 try:context_before\n.join(lines[max(0,i-10):i])iftry:notincontext_beforeandtry :notincontext_before:self.issues.append(AIQualityIssue(filestr(file_path),linei,severitywarning,messageLLM 调用缺少 try-except 错误处理建议捕获 API 异常))defmain():checkerAICodeChecker(src/)issueschecker.check_all()errors[iforiinissuesifi.severityerror]warnings[iforiinissuesifi.severitywarning]forissueinissues:icon{error:❌,warning:⚠️,info:ℹ️}[issue.severity]print(f{icon}{issue.file}:{issue.line}-{issue.message})print(f\n检查完成{len(errors)}个错误{len(warnings)}个警告)iferrors:print(❌ 存在严重问题质量门禁不通过)sys.exit(1)print(✅ 质量门禁通过)if__name____main__:main()3.4 Gate 3AI 行为测试框架# tests/ai_behavior/test_llm_responses.py AI 行为测试测试 LLM 调用的输出质量 使用 LLM-as-Judge 方式评估而非固定断言 importpytestimportjsonfromunittest.mockimportpatch,MagicMockfromsrc.agents.code_reviewerimportCodeReviewAgentclassTestCodeReviewAgent:pytest.fixturedefagent(self):returnCodeReviewAgent(modelclaude-3-5-sonnet-20241022)deftest_detects_sql_injection(self,agent):测试SQL 注入代码必须被检测出来vulnerable_code def get_user(user_id): query fSELECT * FROM users WHERE id {user_id} return db.execute(query) resultagent.review(vulnerable_code,languagepython)# 断言结果中必须包含 SQL 注入相关信息assertresult[score]60,有 SQL 注入漏洞的代码评分不应超过 60security_issues_text .join(result.get(security_issues,[])).lower()assertany(keywordinsecurity_issues_textforkeywordin[sql,injection,注入,sql注入]),\必须检测出 SQL 注入漏洞deftest_format_consistency(self,agent):测试输出格式必须符合规范simple_codedef add(a, b): return a bresultagent.review(simple_code,languagepython)# 验证返回结构assertisinstance(result,dict),返回值必须是字典assertscoreinresult,必须包含 score 字段assertsecurity_issuesinresult,必须包含 security_issues 字段assertisinstance(result[score],int),score 必须是整数assert0result[score]100,score 必须在 0-100 之间deftest_handles_api_error_gracefully(self,agent):测试API 错误时有优雅降级withpatch.object(agent._client,messages)asmock_client:mock_client.create.side_effectException(API Error: 429 Too Many Requests)# 不应该抛出异常应该返回错误信息resultagent.review(def foo(): pass,languagepython)asserterrorinresultorresult.get(score)isnotNone,\API 错误时应该返回错误信息而不是抛出异常pytest.mark.parametrize(good_code,min_score,[(def add(a: int, b: int) - int:\n \\\Add two numbers.\\\\n return a b,80),(import hashlib\n\ndef hash_password(pwd: str) - str:\n return hashlib.sha256(pwd.encode()).hexdigest(),75),])deftest_good_code_gets_high_score(self,agent,good_code,min_score):测试质量良好的代码应该得到高分resultagent.review(good_code,languagepython)assertresult[score]min_score,\f良好代码评分{result[score]}低于预期{min_score}四、Prompt 版本管理方案4.1 用代码管理 Prompt推荐# src/prompts/code_review_prompts.py 代码审查相关 Prompt 配置 所有 Prompt 修改必须更新版本号并注明修改原因 # v1.3 - 2025-03-15 - 增加对 async/await 模式的检查CODE_REVIEW_SYSTEM_PROMPT你是一位资深代码审查专家专注于代码质量、安全漏洞和性能优化。 审查重点 1. 安全漏洞SQL 注入、XSS、命令注入、敏感信息泄露 2. 代码质量可读性、可维护性、单一职责原则 3. 性能问题不必要的循环、N1 查询、内存泄漏 4. Python 最佳实践类型注解、异常处理、async/await 规范使用 返回格式严格 JSON { score: 0-100的整数, security_issues: [问题1严重度高/中/低], suggestions: [建议1, 建议2] } 评分标准 - 90-100优秀无明显问题 - 70-89良好有少量改进空间 - 50-69一般存在明显问题 - 0-49不合格存在严重问题如安全漏洞# v1.1 - 2025-02-01 - 初始版本CODE_REVIEW_USER_TEMPLATE请审查以下{language}代码 {language}{code}只返回 JSON不要其他解释。“”PROMPT_VERSIONS {“code_review_system”: “1.3”,“code_review_user”: “1.1”}### 4.2 Prompt 变更追踪钩子 bash # .git/hooks/pre-commit #!/bin/bash # 检查 Prompt 文件变更是否更新了版本号 changed_files$(git diff --cached --name-only | grep prompts/) for file in $changed_files; do if git diff --cached $file | grep -q SYSTEM_PROMPT\|USER_TEMPLATE; then if ! git diff --cached $file | grep -q # v; then echo ⚠️ 警告$file 中的 Prompt 被修改但未更新版本注释 echo 请在修改的 Prompt 上方添加版本注释例如 echo # v1.x - YYYY-MM-DD - 修改原因 exit 1 fi fi done exit 0五、Token 成本监控与告警# src/monitoring/token_monitor.py Token 消耗监控中间件 importfunctoolsimporttimefromdataclassesimportdataclassfromtypingimportOptionalimportlogging loggerlogging.getLogger(__name__)# 价格配置美元/1M TokenTOKEN_PRICES{claude-3-5-sonnet-20241022:{input:3.0,output:15.0},claude-3-haiku-20240307:{input:0.25,output:1.25},gpt-4o:{input:2.5,output:10.0},gpt-4o-mini:{input:0.15,output:0.6},}# 告警阈值每次调用COST_ALERT_THRESHOLD_USD0.5# 单次超 $0.5 告警dataclassclassTokenUsage:model:strinput_tokens:intoutput_tokens:intlatency_ms:floatcost_usd:floatdefmonitor_llm_call(func):LLM 调用监控装饰器functools.wraps(func)defwrapper(*args,**kwargs):start_timetime.time()resultfunc(*args,**kwargs)latency_ms(time.time()-start_time)*1000# 提取 Token 使用信息modelkwargs.get(model,unknown)usagegetattr(result,usage,None)ifusage:pricesTOKEN_PRICES.get(model,{input:0,output:0})cost_usd(usage.input_tokens/1_000_000*prices[input]usage.output_tokens/1_000_000*prices[output])token_usageTokenUsage(modelmodel,input_tokensusage.input_tokens,output_tokensusage.output_tokens,latency_mslatency_ms,cost_usdcost_usd)# 记录到监控系统logger.info(f[TOKEN_USAGE] model{model}finput{usage.input_tokens}foutput{usage.output_tokens}fcost${cost_usd:.4f}flatency{latency_ms:.0f}ms)# 成本告警ifcost_usdCOST_ALERT_THRESHOLD_USD:logger.warning(f⚠️ 单次 LLM 调用成本过高: ${cost_usd:.4f})returnresultreturnwrapper六、总结AI 研发团队的 Code Review 和质量门禁核心原则是Prompt 也是代码必须像代码一样版本管理、code review、测试行为测试优于单元测试测 AI 行为而不是测某个函数的返回值成本是工程指标Token 消耗必须纳入性能监控体系自动化门禁降低摩擦能自动检查的不要靠人工人工 Review 专注在 AI 自动化难以判断的维度参考文献Google Engineering Practices. “Google’s Code Review Developer Guide.” https://google.github.io/eng-practices/review/Anthropic. “Claude API Reference.” https://docs.anthropic.com/en/api/OWASP Foundation. “LLM Top 10 Security Risks.” https://owasp.org/www-project-top-10-for-large-language-model-applications/Microsoft. “Responsible AI Standard.” https://www.microsoft.com/en-us/ai/responsible-aiGitHub Actions Documentation. https://docs.github.com/en/actions