MonkeyCode 代码质量管理:从静态分析到 AI 驱动的质量保障体系
引言
"质量不是测试出来的,是构建出来的——而 AI 可以帮你更好地构建。"
在软件工程中,代码质量管理是一个永恒的话题。传统的静态分析工具(如 ESLint、SonarQube)擅长发现模式化的问题,但面对业务逻辑错误、架构设计缺陷、安全漏洞等更深层次的质量问题往往力不从心。MonkeyCode 作为开源 AI 编程助手,正在重新定义"代码质量"的边界——将人工智能的能力融入软件开发生命周期的每一个环节。
本文将全面介绍如何利用 MonkeyCode 构建一套完整的、AI 驱动的代码质量保障体系。
🎯 核心信息
- GitHub 仓库: https://github.com/monkeycode-ai/monkeycode
- 开源协议: Apache License 2.0
- 欢迎提交 Issue: 质量管理相关问题请标记
quality标签- 官方 CLI:
monkeycode quality --check
一、代码质量的维度演进
1.1 质量定义的四代演变
┌─────────────────────────────────────────────────────────────┐
│ 代码质量管理的四代演进 │
├──────────────┬──────────────────┬───────────────────────────┤
│ 代际 │ 核心理念 │ 代表工具/方法 │
├──────────────┼──────────────────┼───────────────────────────┤
│ │ │ │
│ 第一代 │ "能跑就行" │ 手工 Code Review │
│ (手工时代) │ │ 打印代码 + 红笔批注 │
│ │ │ │
│ 第二代 │ "符合规范" │ Lint 工具 │
│ (规则时代) │ │ ESLint, Checkstyle, PMD │
│ │ │ SonarQube, Coverity │
│ │ │ │
│ 第三代 │ "可度量" │ 度量驱动 │
│ (度量时代) │ │ 圈复杂度 + 覆盖率 + 技术债务 │
│ │ │ DORA 指标 + DevEx │
│ │ │ │
│ 第四代 ⭐ │ "智能理解" │ AI 驱动 │
│ (AI 时代) │ │ MonkeyCode │
│ │ │ 语义理解 + 上下文感知 │
│ │ │ 预测性质量保障 │
│ │ │ │
└──────────────┴──────────────────┴───────────────────────────┘
1.2 传统工具 vs MonkeyCode 的能力对比
| 质量维度 | Lint/静态分析 | SonarQube | MonkeyCode AI |
|---|---|---|---|
| 语法/格式错误 | ✅ 强项 | ✅ 支持 | ✅ 同样支持 |
| 编码规范违规 | ✅ 强项 | ✅ 强项 | ✅ 支持并可自动修复 |
| 潜在 Bug(空指针等) | ⚠️ 基于规则 | ✅ 数据流分析 | ✅ 语义理解 |
| 性能反模式 | ❌ 不支持 | ⚠️ 有限 | ✅ 上下文感知 |
| 安全漏洞 | ❌ 不支持 | ✅ 规则引擎 | ✅ 语义+上下文 |
| 架构设计问题 | ❌ 不支持 | ⚠️ 有限 | ✅ 全局理解 |
| 业务逻辑错误 | ❌ 不可能 | ❌ 不可能 | ✅ 核心能力 |
| API 设计质量 | ❌ 不支持 | ⚠️ 有限 | ✅ 最佳实践匹配 |
| 可维护性评估 | ⚠️ 指标化 | ✅ 技术债务 | ✅ 多维评估 |
| 修复建议生成 | ❌ 仅报错 | ⚠️ 通用建议 | ✅ 上下文精准建议 |
二、MonkeyCode 质量检查核心能力
2.1 八大质量检查引擎
# ===== monkeycode/quality/checker.py =====
"""
MonkeyCode 代码质量检查器
八大引擎协同工作,全方位保障代码质量
"""
import ast
import re
from dataclasses import dataclass, field
from typing import Optional
from enum import Enum
from pathlib import Path
class Severity(Enum):
CRITICAL = "critical" # 必须立即修复
HIGH = "high" # 应尽快修复
MEDIUM = "medium" # 建议修复
LOW = "low" # 可选修复
INFO = "info" # 信息提示
class QualityCategory(Enum):
CORRECTNESS = "correctness" # 正确性
SECURITY = "security" # 安全性
PERFORMANCE = "performance" # 性能
MAINTAINABILITY = "maintainability" # 可维护性
RELIABILITY = "reliability" # 可靠性
TESTABILITY = "testability" # 可测试性
DOCUMENTATION = "documentation" # 文档完整性
ARCHITECTURE = "architecture" # 架构合规
@dataclass
class QualityIssue:
"""质量问题记录"""
file_path: str
line_number: int
column_number: int
category: QualityCategory
severity: Severity
title: str # 问题标题
description: str # 详细描述
code_snippet: str # 问题代码片段
# AI 增强信息
suggested_fix: str # 建议的修复代码
explanation: str # 为什么这是个问题
confidence: float # AI 置信度 0-1
# 元数据
rule_id: str # 规则 ID
effort_to_fix: str # 修复工作量估计
references: list[str] = field(default_factory=list)
@dataclass
class QualityReport:
"""质量报告"""
file_path: str
total_issues: int
issues_by_severity: dict[str, int]
issues_by_category: dict[str, int]
# 分数
quality_score: float # 总体质量分 0-100
scores_by_category: dict[str, float]
# 详情
issues: list[QualityIssue]
# 趋势(如果有历史数据)
trend: str = "stable" # improving / stable / degrading
class MonkeyCodeQualityChecker:
"""MonkeyCode 代码质量检查器"""
def __init__(self):
self.engines = {
'correctness': CorrectnessEngine(),
'security': SecurityEngine(),
'performance': PerformanceEngine(),
'maintainability': MaintainabilityEngine(),
'reliability': ReliabilityEngine(),
'testability': TestabilityEngine(),
'documentation': DocumentationEngine(),
'architecture': ArchitectureEngine(),
}
def check_file(self, file_path: str) -> QualityReport:
"""检查单个文件"""
content = Path(file_path).read_text(encoding='utf-8', errors='ignore')
all_issues = []
for engine_name, engine in self.engines.items():
try:
issues = engine.check(content, file_path)
all_issues.extend(issues)
except Exception as e:
print(f"[warn] 引擎 {engine_name} 执行失败: {e}")
return self._generate_report(file_path, all_issues)
def check_project(self, project_path: str) -> list[QualityReport]:
"""检查整个项目"""
reports = []
source_files = self._scan_source_files(project_path)
for file_path in source_files:
try:
report = self.check_file(str(file_path))
reports.append(report)
# 实时输出进度
if report.total_issues > 0:
print(f"[check] {file_path}: "
f"{report.total_issues} issues "
f"(score: {report.quality_score:.0f})")
except Exception as e:
print(f"[error] 检查失败 {file_path}: {e")
# 输出项目级摘要
self._print_project_summary(reports)
return reports
def _generate_report(
self,
file_path: str,
issues: list[QualityIssue]
) -> QualityReport:
"""生成单文件质量报告"""
# 按严重程度统计
by_severity = {}
for s in Severity:
by_severity[s.value] = sum(1 for i in issues if i.severity == s)
# 按类别统计
by_category = {}
for c in QualityCategory:
by_category[c.value] = sum(1 for i in issues if i.category == c)
# 计算总体质量分数
score = self._calculate_quality_score(issues)
# 计算各类别分数
category_scores = {}
for cat in QualityCategory:
cat_issues = [i for i in issues if i.category == cat]
cat_scores[cat.value] = self._calculate_category_score(cat_issues)
return QualityReport(
file_path=file_path,
total_issues=len(issues),
issues_by_severity=by_severity,
issues_by_category=by_category,
quality_score=score,
scores_by_category=category_scores,
issues=sorted(issues, key=lambda x: (
['critical', 'high', 'medium', 'low', 'info'].index(x.severity.value),
x.line_number
)),
)
def _calculate_quality_score(self, issues: list[QualityIssue]) -> float:
"""
计算质量分数 (0-100)
加权扣分模型:
- critical: -15 分
- high: -8 分
- medium: -3 分
- low: -1 分
- info: 不扣分
"""
score = 100.0
penalties = {
Severity.CRITICAL: 15,
Severity.HIGH: 8,
Severity.MEDIUM: 3,
Severity.LOW: 1,
Severity.INFO: 0,
}
for issue in issues:
penalty = penalties.get(issue.severity, 0)
# 根据 AI 置信度调整扣分
adjusted_penalty = penalty * issue.confidence
score -= adjusted_penalty
return max(0.0, min(100.0, round(score, 1)))
def _calculate_category_score(self, issues: list[QualityIssue]) -> float:
"""计算单个类别的分数"""
if not issues:
return 100.0
base_score = 100.0
for issue in issues:
if issue.severity == Severity.CRITICAL:
base_score -= 20 * issue.confidence
elif issue.severity == Severity.HIGH:
base_score -= 10 * issue.confidence
elif issue.severity == Severity.MEDIUM:
base_score -= 4 * issue.confidence
return max(0.0, round(base_score, 1))
def _print_project_summary(self, reports: list[QualityReport]):
"""打印项目级摘要"""
print("\n" + "=" * 70)
print(" MonkeyCode 代码质量检查报告 — 项目总览")
print("=" * 70)
total_files = len(reports)
files_with_issues = sum(1 for r in reports if r.total_issues > 0)
total_issues = sum(r.total_issues for r in reports)
avg_score = sum(r.quality_score for r in reports) / max(total_files, 1)
# 严重程度汇总
severity_totals = {}
for r in reports:
for sev, count in r.issues_by_severity.items():
severity_totals[sev] = severity_totals.get(sev, 0) + count
print(f"\n📊 项目概览:")
print(f" 扫描文件数: {total_files}")
print(f" 有问题文件: {files_with_issues} ({files_with_issues*100//max(total_files,1)}%)")
print(f" 问题总数: {total_issues}")
print(f" 平均质量分: {avg_score:.1f}/100")
print(f"\n🚨 严重程度分布:")
for sev in ['critical', 'high', 'medium', 'low', 'info']:
count = severity_totals.get(sev, 0)
emoji = {'critical': '🔴', 'high': '🟠', 'medium': '🟡', 'low': '🔵', 'info': '⚪'}[sev]
print(f" {emoji} {sev}: {count}")
# Top 10 最严重问题
all_issues = []
for r in reports:
all_issues.extend(r.issues)
all_issues.sort(key=lambda x: (
['critical', 'high', 'medium', 'low', 'info'].index(x.severity.value),
-x.confidence
))
if all_issues:
print(f"\n🔥 Top 10 关键问题:")
for i, issue in enumerate(all_issues[:10], 1):
print(f" {i}. [{issue.severity.value.upper()}] "
f"{issue.title}"
f"\n 📄 {issue.file_path}:{issue.line_number}")
print("\n" + "=" * 70)
def _scan_source_files(self, project_path: str) -> list[Path]:
"""扫描项目源文件"""
extensions = {'.py', '.ts', '.js', '.tsx', '.jsx', '.java', '.go', '.rs'}
project = Path(project_path)
files = []
for ext in extensions:
files.extend(project.rglob(f'*{ext}'))
exclude = {'node_modules', '__pycache__', '.git', 'dist', 'build',
'vendor', '.venv', 'bin', 'obj', '.next'}
return sorted([
f for f in files
if not any(part in exclude for part in f.relative_to(project).parts)
])
# ===== 各引擎实现 =====
class CorrectnessEngine:
"""正确性检查引擎:检测逻辑错误和潜在 Bug"""
def check(self, content: str, file_path: str) -> list[QualityIssue]:
issues = []
# 检测1: 空指针/None 检查缺失
issues.extend(self._check_null_dereference(content, file_path))
# 检测2: 资源泄漏(未关闭的文件/连接)
issues.extend(self._check_resource_leak(content, file_path))
# 检测3: 异常处理不当
issues.extend(self._check_exception_handling(content, file_path))
# 检测4: 并发安全问题
issues.extend(self._check_concurrency(content, file_path))
# 检测5: 边界条件处理
issues.extend(self._check_boundary_conditions(content, file_path))
return issues
def _check_null_dereference(self, content: str, file_path: str) -> list[QualityIssue]:
"""检测可能的空指针解引用"""
issues = []
lines = content.split('\n')
# Python: 使用变量前未检查 None
none_access_pattern = re.compile(
r'^(\s*)(\w+)\.(?:\w+)' # obj.attr 访问
r'.*?(?!\s*(if|or|and)\s+\2\s+(?:is\s+)?(?:not\s+)?None)',
re.MULTILINE
)
for match in none_access_pattern.finditer(content):
line_num = content[:match.start()].count('\n') + 1
var_name = match.group(2)
# 简化的启发式:如果变量来自可能为 None 的来源
suspicious_sources = ['get(', 'find(', 'fetch(', 'query(',
'request.', 'session.', 'config.']
if any(src in content[max(0, match.start()-200):match.start()]
for src in suspicious_sources):
issues.append(QualityIssue(
file_path=file_path,
line_number=line_num,
column_number=match.start(2) - content.rfind('\n', 0, match.start()),
category=QualityCategory.CORRECTNESS,
severity=Severity.HIGH,
title=f"可能的空指针解引用: '{var_name}'",
description=(
f"变量 '{var_name}' 在使用前可能未被 None 检查。"
f"如果该变量来自可能返回 None 的函数调用或外部输入,"
f"直接访问其属性可能导致 AttributeError/NullPointerException。"
),
code_snippet=lines[line_num - 1].strip() if line_num <= len(lines) else '',
suggested_fix=(
f"# 建议在使用前添加 None 检查:\n"
f"if {var_name} is not None:\n"
f" {lines[line_num - 1].strip()}\n"
f"else:\n"
f" # 处理 {var_name} 为 None 的情况\n"
f" ..."
),
explanation="空指针解引用是最常见的运行时错误之一。在 Python 中会导致 AttributeError,在 Java 中会导致 NullPointerException。",
confidence=0.75,
rule_id="CORRECTNESS-001",
effort_to_fix="< 5 min",
references=["PEP 8: E721", "Google Python Style Guide"],
))
return issues
def _check_resource_leak(self, content: str, file_path: str) -> list[QualityIssue]:
"""检测资源泄漏"""
issues = []
lines = content.split('\n')
# 检测: open() 但没有使用 with 语句
open_without_with = re.compile(
r'(?<!with\s)(?<!\()\s*\w+\s*=\s*open\s*\(',
re.MULTILINE
)
for match in open_without_with.finditer(content):
line_num = content[:match.start()].count('\n') + 1
# 检查附近是否有 close() 调用
nearby_code = content[match.start():match.start()+500]
has_close = bool(re.search(r'\.close\(\)', nearby_code))
has_with = 'with' in content[max(0, match.start()-100):match.start()]
if not has_close and not has_with:
issues.append(QualityIssue(
file_path=file_path,
line_number=line_num,
column_number=0,
category=QualityCategory.RELIABILITY,
severity=Severity.MEDIUM,
title="资源泄漏风险: 文件未使用 with 语句管理",
description="使用 open() 打开文件但未使用 with 语句,可能导致文件句柄泄漏。",
code_snippet=lines[line_num - 1].strip() if line_num <= len(lines) else '',
suggested_fix=(
"# 推荐使用 with 语句自动管理资源:\n"
"with open('filename.txt', 'r') as f:\n"
" content = f.read()\n"
"# 文件会在 with 块结束后自动关闭"
),
explanation="Python 的 with 语句会确保文件在操作完成后被正确关闭,即使在发生异常的情况下也是如此。",
confidence=0.85,
rule_id="RELIABILITY-001",
effort_to_fix="< 2 min",
))
return issues
def _check_exception_handling(self, content: str, file_path: str) -> list[QualityIssue]:
"""检测异常处理问题"""
issues = []
# 裸 except 子句
bare_except = re.compile(r'except\s*:', re.MULTILINE)
for match in bare_except.finditer(content):
line_num = content[:match.start()].count('\n') + 1
issues.append(QualityIssue(
file_path=file_path,
line_number=line_num,
column_number=0,
category=QualityCategory.CORRECTNESS,
severity=Severity.HIGH,
title="裸 except 子句:捕获所有异常包括 SystemExit/KeyboardInterrupt",
description="裸 except 会捕获所有异常,包括应该传播的系统退出信号和键盘中断。",
suggested_fix="except Exception as e: # 只捕获常规异常",
confidence=0.95,
rule_id="CORRECTNESS-002",
effort_to_fix="< 1 min",
))
return issues
def _check_concurrency(self, content: str, file_path: str) -> list[QualityIssue]:
"""检测并发安全问题"""
issues = []
# 共享可变状态无锁保护
global_mutation = re.compile(
r'global\s+(\w+)\s*\n.*\1\s*=',
re.MULTILINE | re.DOTALL
)
for match in global_mutation.finditer(content):
line_num = content[:match.start()].count('\n') + 1
var_name = match.group(1)
issues.append(QualityIssue(
file_path=file_path,
line_number=line_num,
column_number=0,
category=QualityCategory.RELIABILITY,
severity=Severity.HIGH,
title=f"并发安全隐患: 全局变量 '{var_name}' 可能存在竞态条件",
description="全局可变状态在多线程环境下不安全,可能导致数据竞争。",
suggested_fix=(
f"# 使用线程安全的数据结构或加锁:\n"
f"import threading\n"
f"_lock = threading.Lock()\n\n"
f"def update_{var_name}(new_value):\n"
f" global {var_name}\n"
f" with _lock:\n"
f" {var_name} = new_value"
),
confidence=0.72,
rule_id="RELIABILITY-002",
effort_to_fix="10-30 min",
))
return issues
def _check_boundary_conditions(self, content: str, file_path: str) -> list[QualityIssue]:
"""检测边界条件处理缺失"""
issues = []
# 数组/列表访问缺少边界检查
unchecked_index = re.compile(
r'(\w+)\[(?!-?\d+\])(\w+)\]',
re.MULTILINE
)
for match in unchecked_index.finditer(content):
var_name = match.group(1)
index_var = match.group(2)
# 如果索引是用户输入或外部数据
if index_var in ('user_input', 'request', 'index', 'i'):
line_num = content[:match.start()].count('\n') + 1
issues.append(QualityIssue(
file_path=file_path,
line_number=line_num,
column_number=0,
category=QualityCategory.CORRECTNESS,
severity=Severity.MEDIUM,
title=f"数组访问越界风险: '{var_name}[{index_var}]' 缺少边界检查",
description="使用动态索引访问数组/列表时,应验证索引在有效范围内。",
suggested_fix=(
f"if 0 <= {index_var} < len({var_name}):\n"
f" value = {var_name}[{index_var}]\n"
f"else:\n"
f" raise IndexError('Index out of range')"
),
confidence=0.68,
rule_id="CORRECTNESS-003",
effort_to_fix="< 5 min",
))
return issues
class SecurityEngine:
"""安全检查引擎"""
def check(self, content: str, file_path: str) -> list[QualityIssue]:
issues = []
# SQL 注入检测
issues.extend(self._detect_sql_injection(content, file_path))
# XSS 检测
issues.extend(self._detect_xss(content, file_path))
# 硬编码密钥检测
issues.extend(self._detect_hardcoded_secrets(content, file_path))
# 不安全的随机数
issues.extend(self._detect_insecure_random(content, file_path))
# 路径遍历
issues.extend(self._detect_path_traversal(content, file_path))
return issues
def _detect_sql_injection(self, content: str, file_path: str) -> list[QualityIssue]:
"""检测 SQL 注入漏洞"""
issues = []
# 危险模式:字符串拼接 SQL
sql_injection_patterns = [
(r'(execute|query|raw)\s*\(\s*f["\'].*%s', 'f-string SQL 拼接'),
(r'(execute|query|raw)\s*\(\s*["\'].*\+.*SELECT', '字符串拼接 SQL'),
(r'(execute|query|raw)\s*\(\s*["\'].*\.format\s*\(', 'format SQL 拼接'),
(r'"SELECT.*FROM.*WHERE.*"\s*\+\s*', 'SQL 字符串拼接'),
]
for pattern, desc in sql_injection_patterns:
for match in re.finditer(pattern, content, re.IGNORECASE | re.MULTILINE):
line_num = content[:match.start()].count('\n') + 1
issues.append(QualityIssue(
file_path=file_path,
line_number=line_num,
column_number=0,
category=QualityCategory.SECURITY,
severity=Severity.CRITICAL,
title=f"SQL 注入漏洞: {desc}",
description="直接将用户输入拼接到 SQL 语句中,攻击者可以通过构造恶意输入执行任意 SQL 命令。",
suggested_fix=(
"# 使用参数化查询:\n"
"cursor.execute(\n"
" 'SELECT * FROM users WHERE id = %s',\n"
" (user_id,)\n"
")"
),
explanation="SQL 注入是 OWASP Top 10 之一,位列第三。它允许攻击者读取、修改或删除数据库中的数据。",
confidence=0.92,
rule_id="SECURITY-001",
effort_to_fix="< 10 min",
references=["OWASP Top 10 - A03:2021 Injection", "CWE-89"],
))
return issues
def _detect_hardcoded_secrets(self, content: str, file_path: str) -> list[QualityIssue]:
"""检测硬编码的密钥和凭据"""
issues = []
secret_patterns = [
(r'api_key\s*=\s*["\'][A-Za-z0-9]{20,}["\']', 'API Key'),
(r'secret_key\s*=\s*["\'][A-Za-z0-9]{20,}["\']', 'Secret Key'),
(r'password\s*=\s*["\'][^"\']{4,}["\']', 'Password'),
(r'AKIA[A-Z0-9]{16}', 'AWS Access Key'),
(r'sk-[a-fA-F0-9]{32}', 'OpenAI API Key'),
(r'ghp_[a-zA-Z0-9]{36}', 'GitHub Personal Token'),
(r'-----BEGIN (RSA |EC )?PRIVATE KEY-----', 'Private Key'),
]
for pattern, secret_type in secret_patterns:
for match in re.finditer(pattern, content):
line_num = content[:match.start()].count('\n') + 1
masked = match.group()[:8] + '*' * 12 + match.group()[-4:]
issues.append(QualityIssue(
file_path=file_path,
line_number=line_num,
column_number=0,
category=QualityCategory.SECURITY,
severity=Severity.CRITICAL,
title=f"硬编码敏感信息: {secret_type}",
description=f"发现硬编码的 {secret_type},这会导致严重的安全风险。代码提交到版本控制后,凭据将永久泄露。",
suggested_fix=(
f"# 使用环境变量存储敏感信息:\n"
f"import os\n"
f"{secret_type.lower().replace(' ', '_')} = os.getenv('{secret_type.upper()}')\n\n"
f"# 或使用密钥管理系统 (KMS/Vault)"
),
explanation="硬编码凭据是导致数据泄露的最常见原因之一。一旦代码被推送到公开仓库,凭据必须被视为已泄露并立即轮换。",
confidence=0.98,
rule_id="SECURITY-002",
effort_to_fix="< 5 min",
references=["OWASP Top 10 - A07:2021 Identification and Authentication Failures", "CWE-798"],
))
return issues
class PerformanceEngine:
"""性能检查引擎"""
def check(self, content: str, file_path: str) -> list[QualityIssue]:
issues = []
# N+1 查询模式
issues.extend(self._detect_n_plus_one(content, file_path))
# 循环内重复计算
issues.extend(self._detect_repeated_computation(content, file_path))
# 低效数据结构选择
issues.extend(self._detect_inefficient_data_structures(content, file_path))
# 内存泄漏风险
issues.extend(self._detect_memory_leak_risk(content, file_path))
return issues
def _detect_n_plus_one(self, content: str, file_path: str) -> list[QualityIssue]:
"""检测 N+1 查询模式"""
issues = []
# 循环内的数据库查询
loop_query_pattern = re.compile(
r'(for|while|foreach)\s*\(.*?\)\s*\{[^}]*'
r'(db\.|session\.|connection\.|cursor\.|execute|query|select)',
re.MULTILINE | re.DOTALL
)
for match in loop_query_pattern.finditer(content):
line_num = content[:match.start()].count('\n') + 1
issues.append(QualityIssue(
file_path=file_path,
line_number=line_num,
column_number=0,
category=QualityCategory.PERFORMANCE,
severity=Severity.HIGH,
title="N+1 查询性能问题: 循环内执行数据库查询",
description="在循环中执行数据库查询会导致 N+1 查询问题,随着数据量增长性能急剧下降。",
suggested_fix=(
"# 使用批量查询替代循环查询:\n"
"# Bad (N+1):\n"
"for item in items:\n"
" detail = db.query(item.id) # N queries!\n"
"\n"
"# Good (batch query):\n"
"item_ids = [item.id for item in items]\n"
"details = db.batch_query(item_ids) # 1 query!"
),
explanation="N+1 查询是 Web 应用中最常见的性能问题之一。一个有 100 条数据的页面可能触发 101 次数据库查询。",
confidence=0.80,
rule_id="PERFORMANCE-001",
effort_to_fix="15-30 min",
))
return issues
# ... 其他引擎类似实现(省略)...
class MaintainabilityEngine: pass
class ReliabilityEngine: pass
class TestabilityEngine: pass
class DocumentationEngine: pass
class ArchitectureEngine: pass
2.2 质量门禁(Quality Gate)
# ===== .monkeycode/quality-gate.yaml =====
# MonkeyCode 质量门禁配置
version: "1.0"
gates:
# 门禁1: 提交前检查(本地)
pre_commit:
enabled: true
fail_on: ["critical", "high"]
warn_on: ["medium"]
rules:
# 硬性规则
- id: "no-hardcoded-secrets"
severity: "critical"
description: "禁止硬编码密钥和凭据"
- id: "no-sql-injection"
severity: "critical"
description: "禁止 SQL 注入风险代码"
- id: "no-bare-except"
severity: "high"
description: "禁止裸 except 子句"
- id: "max-complexity"
severity: "high"
params:
max_cyclomatic: 10
max_cognitive: 25
- id: "min-test-coverage"
severity: "medium"
params:
min_coverage: 60
new_code_min_coverage: 80
# 门禁2: PR 合并检查
pull_request:
enabled: true
fail_on: ["critical"]
block_merge_on: ["critical", "high"]
rules:
- id: "quality-score-threshold"
severity: "block"
params:
min_score: 75
new_code_min_score: 85
- id: "no-quality-regression"
severity: "block"
description: "不允许质量分数下降超过 5 分"
params:
max_regression: 5
- id: "security-scan"
severity: "block"
params:
scan_dependencies: true
scan_code: true
- id: "test-coverage-check"
severity: "warn"
params:
min_line_coverage: 70
min_branch_coverage: 60
# 门禁3: 发布前检查
release:
enabled: true
fail_on: ["critical", "high", "medium"]
rules:
- id: "release-quality-gate"
params:
min_quality_score: 85
zero_critical: true
zero_high: true
max_medium_per_file: 3
- id: "documentation-complete"
severity: "medium"
params:
require_api_docs: true
require_readme_update: true
require_changelog_entry: true
- id: "performance-baseline"
severity: "warn"
params:
max_regression_percent: 5
benchmark_tests_required: true
# 自定义规则
custom_rules:
- name: "business-rule-validation"
pattern: "所有涉及金额计算的函数必须有精度控制"
severity: "high"
check: |
检查所有包含 amount/price/money 参数的函数:
- 是否使用了 Decimal 而非 float
- 是否有 round() 调用
- 是否有范围校验
- name: "error-message-standardization"
pattern: "所有对外暴露的错误消息必须使用统一格式"
severity: "medium"
check: |
错误消息格式要求:
- 包含错误码 (ERR_xxxx)
- 包含用户友好的中文描述
- 不包含内部技术细节
三、质量度量与可视化
3.1 质量仪表盘数据
// ===== monkeycode/quality/dashboard.ts =====
/**
* MonkeyCode 质量度量仪表盘
*/
export interface QualityMetrics {
// 整体指标
overallScore: number; // 0-100
trend: 'improving' | 'stable' | 'degrading';
trendPercent: number; // 变化百分比
// 分维度指标
dimensions: {
correctness: DimensionMetric;
security: DimensionMetric;
performance: DimensionMetric;
maintainability: DimensionMetric;
reliability: DimensionMetric;
testability: DimensionMetric;
documentation: DimensionMetric;
architecture: DimensionMetric;
};
// 项目健康度
health: {
technicalDebt: TechnicalDebtMetric;
codeCoverage: CoverageMetric;
duplicationRate: number; // 0-1
avgComplexity: number;
hotspots: HotspotFile[];
};
// 团队效率
teamEfficiency: {
meanTimeToFix: number; // 平均修复时间(小时)
reviewTurnaround: number; // Review 周转时间(小时)
escapeRate: number; // 生产环境 Bug 逃逸率
};
}
interface DimensionMetric {
score: number; // 0-100
issueCount: number;
criticalCount: number;
trend: string;
}
interface TechnicalDebtMetric {
hours: number; // 技术债务(人时)
ratio: number; // 占新开发时间的比例
categories: Record<string, number>;
}
interface CoverageMetric {
lineCoverage: number;
branchCoverage: number;
functionCoverage: number;
}
interface HotspotFile {
path: string;
score: number; // 变更频率 × 复杂度
churn: number; # 近期变更次数
complexity: number;
riskLevel: 'critical' | 'high' | 'medium';
}
// 质量趋势分析算法
export function analyzeQualityTrend(
historicalScores: Array<{ date: string; score: number }>
): { trend: string; percent: number } {
if (historicalScores.length < 3) {
return { trend: 'stable', percent: 0 };
}
const recent = historicalScores.slice(-5);
const older = historicalScores.slice(-10, -5);
const recentAvg = recent.reduce((s, x) => s + x.score, 0) / recent.length;
const olderAvg = older.reduce((s, x) => s + x.score, 0) / older.length;
const change = ((recentAvg - olderAvg) / olderAvg) * 100;
let trend: string;
if (change > 3) trend = 'improving';
else if (change < -3) trend = 'degrading';
else trend = 'stable';
return { trend, percent: Math.abs(change) };
}
3.2 质量报告输出格式
## 📊 MonkeyCode 质量检查报告
**项目**: my-awesome-project
**扫描时间**: 2026-06-30 11:45:00
**扫描版本**: commit a1b2c3d
---
### 🎯 总体评分: **78/100** 🟡
| 维度 | 得分 | 问题数 | 趋势 |
|------|------|--------|------|
| 正确性 | 82 | 12 | 📈 +3% |
| 安全性 | 65 | 5 | 📉 -5% ⚠️ |
| 性能 | 88 | 3 | ➡️ 稳定 |
| 可维护性 | 71 | 18 | 📈 +8% |
| 可靠性 | 79 | 6 | ➡️ 稳定 |
| 可测试性 | 62 | 8 | 📉 -2% |
| 文档完整 | 74 | 11 | 📈 +12% |
| 架构合规 | 85 | 2 | ➡️ 稳定 |
---
### 🔴 Critical Issues (2)
| # | 文件 | 行号 | 问题 | 建议 |
|---|------|------|------|------|
| 1 | `src/auth/login.py` | 45 | **SQL 注入漏洞**: 用户输入直接拼接到 SQL | 使用参数化查询 |
| 2 | `src/config.py` | 12 | **硬编码 API 密钥**: AWS AK 泄露风险 | 迁移至环境变量/KMS |
### 🟠 High Issues (8)
| # | 文件 | 行号 | 问题 | 建议 |
|---|------|------|------|------|
| 1 | `src/api/orders.py` | 134 | N+1 查询: 循环内数据库查询 | 批量查询优化 |
| 2 | `src/utils/cache.py` | 28 | 竞态条件: 全局缓存字典无锁保护 | 使用线程安全容器 |
| ... | ... | ... | ... | ... |
---
### 📈 趋势分析
质量分数趋势 (近30天):
100 ┤
90 ┤ ╭──╮
80 ┤ ╭──╮ ╭──╯ ╰──╮
70 ┤ ╭──╮ ╭──╮ ╭──╯ ╰──╯ ╰──╯ ╰── 📍 当前
60 ┤ ╭──╯ ╰──╯╭──╯
50 ┤╯
└──────────────────────────────────────
6/01 6/05 6/10 6/15 6/20 6/25 6/30
---
### 💡 AI 改进建议
#### 优先级 P0 (本周完成)
1. **修复 SQL 注入漏洞** (`src/auth/login.py:45`)
- 预计影响: 消除严重安全风险
- 预计工时: 30 分钟
- MonkeyCode 已生成修复方案,一键应用
2. **移除硬编码密钥** (`src/config.py:12`)
- 预计影响: 符合安全合规要求
- 预计工时: 15 分钟
- ⚠️ 请立即轮换已泄露的密钥!
#### 优先级 P1 (两周内完成)
3. **优化 N+1 查询** → 预期提升 API 响应速度 40%
4. **补充核心模块单元测试** → 目标覆盖率从 62% → 80%
5. **降低高复杂度函数圈复杂度** → 3 个函数需要拆分
---
### 🏆 团队质量排行榜
| 开发者 | 本周提交 | 平均质量分 | 修复及时率 |
|--------|---------|-----------|-----------|
| @alice | 15 | 92 | 95% |
| @bob | 12 | 85 | 88% |
| @charlie | 8 | 78 | 72% ⚠️ |
四、与 DevOps 流水线集成
4.1 质量门禁流水线
# ===== .github/workflows/quality-gate.yml =====
name: MonkeyCode Quality Gate
on:
pull_request:
types: [opened, synchronize]
push:
branches: [main]
jobs:
quality-check:
name: AI Quality Analysis
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run MonkeyCode Quality Check
uses: monkeycode-ai/quality-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
config_file: .monkeycode/quality-gate.yaml
output_format: |
sarif
markdown-summary
json-detailed
- name: Upload SARIF to GitHub Security
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: monkeycode-quality.sarif
category: monkeycode-quality
- name: Comment PR with Quality Report
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const report = fs.readFileSync('quality-report.md', 'utf8');
github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: report
});
- name: Quality Gate Decision
run: |
echo "Checking quality gate status..."
# 读取质量结果
RESULT=$(cat quality-result.json)
SCORE=$(echo $RESULT | jq '.overall_score')
CRITICAL=$(echo $RESULT | jq '.critical_count')
HIGH=$(echo $RESULT | jq '.high_count')
echo "Quality Score: $SCORE"
echo "Critical: $CRITICAL"
echo "High: $HIGH"
# 门禁判断
if [ "$CRITICAL" -gt 0 ]; then
echo "::error::❌ Quality Gate FAILED: $CRITICAL critical issues found!"
exit 1
fi
if [ "$HIGH" -gt 5 ]; then
echo "::warning::⚠️ Quality Gate WARNING: $HIGH high issues found"
fi
if [ "$SCORE" -lt 75 ]; then
echo "::warning::⚠️ Quality Score ($SCORE) below threshold (75)"
fi
echo "✅ Quality Gate PASSED!"
五、最佳实践总结
5.1 质量保障金字塔
┌──────────────┐
│ AI Review │ ← MonkeyCode 核心
│ (语义理解) │
├──────────────┤
│ 静态分析 │ ← Lint / Type Checker
├──────────────┤
│ 单元测试 │ ← 测试框架
├──────────────┤
│ 集成测试 │ ← API 测试
├──────────────┤
│ E2E 测试 │ ← 端到端测试
└──────────────┘
5.2 DO & DON'T
## ✅ 推荐
1. **在 CI 中强制执行质量门禁**
- Critical 问题阻断合并
- High 问题需要审批
2. **定期审查质量趋势**
- 每周查看质量仪表盘
- 关注技术债务增长率
3. **让 AI 成为编码伙伴而非警察**
- 利用 MonkeyCode 的修复建议快速改进
- 将常见问题转化为团队培训材料
4. **建立质量文化**
- 质量分数纳入团队 KPI(但不作为唯一指标)
- 表彰持续改进的开发者
## ❌ 避免
1. **追求 100 分而忽视业务价值**
- 80 分以上即可,重点解决 Critical/High
2. **忽略误报**
- 定期调整规则减少噪音
- 对误报进行标记帮助 AI 学习
3. **只在发布前检查**
- 左移:在编写时实时反馈
- 每次提交都运行检查
结语
"好的代码不是写出来的,是不断打磨出来的——而 AI 让打磨变得高效且有趣。"
MonkeyCode 的质量管理能力不是为了替代开发者判断,而是为了放大你的专业能力。当繁琐的模式匹配交给机器,你就可以专注于真正重要的事情:设计优雅的架构、写出清晰的业务逻辑、创造有价值的功能。
开源的力量在于共同进步。欢迎通过 GitHub Issue 和 Discussions 分享你的质量管理经验!
💡 快速开始:
- 📦 安装:
pip install monkeycode[quality]- 📖 文档: Quality Management Guide
- 🐛 问题反馈: GitHub Issues (标签:
quality)- 💬 社区讨论: Discussions
MonkeyCode — 让每一行代码都经得起考验。 ✅🛡️
浙公网安备 33010602011771号