19-模块三-AI编码规范体系 第19讲-从零设计企业级 AI 编码规范 - 安全规则 测试要求与反模式清单

4 阅读20分钟

模块三-AI编码规范体系 | 第19讲:从零设计企业级 AI 编码规范 - 安全规则、测试要求与反模式清单

开场:企业级规范不是“更多条款”,而是“可审批、可执行、可拦截”

当你把 AI 引入企业的日常研发,规范文档如果只停留在 wiki 上,很快会沦为装饰品:工程师在聊天窗口里一句“先实现功能”,模型就可能生成 硬编码密钥拼接 SQL在控制器里写业务规则吞掉异常——这些都是生产事故与合规风险的温床。企业级 AI 编码规范的核心,不是把字写多,而是建立一条闭环:方法论 → 条款化 → 工具化 → 评审与审批 → 度量改进

本讲从方法论出发,系统给出 安全规则测试要求反模式黑名单 三类内容的写法与落地要点;并设计 规范变更审批工作流,让 MUST 的升级像 API 变更一样可控。最后我们在 CodeSentinel 场景下实现一个可配置的 SecurityRuleChecker:从 YAML 加载规则,对 Python 源码做静态扫描,输出结构化结果,便于接入 CI 或 PR 评论。你会拿到 完整可运行代码,并理解如何把“黑名单反模式”翻译成可检测模式。


全局视角:企业级规范治理闭环

1. 治理闭环(从意图到拦截)

flowchart TB
  M["方法论与风险清单"] --> D["条款化 MUST/SHOULD"]
  D --> A["审批与版本发布"]
  A --> Y["YAML/配置落地"]
  Y --> C["SecurityRuleChecker<br/>静态扫描"]
  C --> CI["CI / PR 门禁"]
  CI --> FB["反馈与度量"]
  FB --> M

2. 安全规则分层:预防、检测、响应

flowchart LR
  P["预防:默认安全脚手架"] --> T["检测:规则扫描+依赖扫描"]
  T --> R["响应:封禁合并+复盘"]

3. 规则评估流水线(CodeSentinel 视角)

flowchart TB
  IN["输入:文件路径 + 源码文本"] --> L["加载 rules.yaml"]
  L --> E["逐条评估 Rule"]
  E -->|regex| R1["正则命中"]
  E -->|ast import| R2["import 黑名单"]
  E -->|ast call| R3["危险函数调用"]
  R1 --> OUT["Findings 列表"]
  R2 --> OUT
  R3 --> OUT

核心原理:企业级 AI 编码规范设计方法

1. 方法论总览:先风险,后条款,再自动化

企业级规范的起点应是 风险清单:数据泄露、越权访问、供应链攻击、日志污染、提示词注入、审计缺失等。每条风险映射到一组 MUST,再映射到 可自动化检测必须人工评审 两类。不要试图把所有事都自动化:例如复杂业务授权往往需要人工看场景,但 密钥硬编码SQL 拼接 这类高优先级问题应当自动化优先。

2. 安全规则(建议写入企业标准的条目示例)

下列条目适合作为 MUST,结合你们行业合规要求扩展:

  • 密钥与凭证:禁止在源码、测试、文档中硬编码生产密钥;使用密钥管理系统或 CI Secret;轮换策略与最小权限原则写清楚。
  • 输入校验:所有外部输入(HTTP 参数、header、消息队列 payload)必须校验类型、长度与枚举范围;对文件路径注意路径穿越。
  • SQL:禁止字符串拼接构造 SQL;必须使用参数化查询或 ORM 安全 API。
  • XSS 与模板注入:若存在服务端渲染或富文本,必须明确编码策略与 CSP(如适用)。
  • CORS:禁止 Access-Control-Allow-Origin: * 搭配携带凭证的配置;应白名单化来源。
  • 依赖治理:锁定版本或锁文件;高危 CVE 的升级 SLA;禁止从未知源安装私有包(供应链)。
  • 日志与隐私:日志字段白名单;禁止记录 token、密码、完整信用卡号等;提示词日志需脱敏策略。
  • 错误响应:对外返回错误信息不得包含堆栈与内部路径;对内可记录 trace id。

3. 测试要求(企业落地常用的“门槛式”表达)

  • 单元测试:新增业务逻辑默认要求单元测试;关键模块设置覆盖率阈值(阈值要随历史债务治理渐进提高)。
  • 集成测试:对外部系统使用 testcontainers 或 mock;禁止依赖生产环境。
  • 契约测试:服务间接口变更必须更新契约测试或 OpenAPI 校验。
  • 命名规范test_<场景>_<期望>,fixture 命名可读,避免魔法数字。
  • 测试数据:禁止使用真实用户数据;必要时合成数据或脱敏样本。
  • 性能与稳定性:对关键路径提供基准或负载测试门禁(按业务需要)。

4. 反模式黑名单(AI 绝不应当生成的模式)

本课程强调以下反模式进入 黑名单,应在规范与检查器里双重体现:

  1. 展示层直接访问数据库(绕过应用层与端口)。
  2. 业务逻辑写在控制器/路由(状态机、规则判断散落在边界)。
  3. 捕获并吞掉异常except Exception: pass 或只 log 不处理)。
  4. 可变全局状态(模块级可变单例承载业务状态)。
  5. 忽略错误返回值(不检查 OptionalResult、错误码)。
  6. 字符串拼接 SQL(即使“看起来安全”也不允许)。
  7. 在 domain 引入框架(FastAPI/SQLAlchemy/Redis)。
  8. 记录密钥与敏感 payload

5. 规范变更审批工作流(建议)

  1. 提案:提交规范变更 PR,说明动机、风险、迁移计划。
  2. 评审:安全、架构、测试负责人至少一方审批 MUST 变更。
  3. 试运行:可选“警告模式”两周,收集误报与漏报。
  4. 正式发布:更新版本号,公告到研发群与内部门户。
  5. 回溯:若引发大规模 CI 失败,允许回滚版本并复盘。

6. 如何把黑名单变成“可配置规则”

每条黑名单应包含:idseveritymatcher(正则或 AST 规则)、messagesuggestion。YAML 适合作为 非程序员也可参与 的规则载体(在安全同事审核的前提下)。重规则逻辑仍建议代码化,YAML 负责编排与开关。

7. 企业落地时的组织对齐:法务、安全、研发三方共识

企业级规范不是工程团队单方面文件。至少在三类角色之间对齐:法务与合规关心数据分类、留存、跨境;安全关心密钥、漏洞、红蓝对抗;研发关心效率、工具链、技术债。对齐会议不必频繁,但 MUST 变更前必须有一次“三方签字”记录。签字不是形式主义,而是为了避免事后互相指责:出事时大家回到文档,看当时承诺了什么。

8. 风险分级:P0/P1/P2 与规则 severity 的映射建议

建议把风险分级映射到规则严重级别:P0 风险(可导致大规模数据泄露或资金损失)对应 error无警告期;P1 风险(可导致局部越权或服务不可用)对应 error 但允许短期警告期;P2 风险(可维护性问题、潜在技术债)对应 warning 或仅文档要求。AI 生成代码最容易踩的是 P1:看起来能跑,但边界条件错误。此类问题需要 测试门槛静态规则 双管齐下。

9. 供应链与第三方模型:企业规范必须覆盖“外部能力”

当代码路径引入第三方模型 API、向量库、插件市场组件时,规范应明确:允许清单数据出境评估日志留存策略密钥轮换。否则会出现“业务同学在脚本里塞 Key”的灰色地带。CodeSentinel 作为治理平台,未来可以对 PR 中的新增依赖与新增外部 endpoint 做自动标注,提醒评审关注。

10. 训练与考试:让工程师理解规则而不是背诵规则

黑名单如果只靠扫描器执行,团队会产生对抗心理。建议每季度一次 开卷考试:给五段代码,要求指出违反哪些 MUST,并写出修复思路。考试结果不用于惩罚,用于发现 文档歧义。歧义一旦定位,优先改文档与规则,而不是指责个人。

11. 与采购流程结合:工具链变更也要过规范门禁

当企业引入新的 AI 编码工具,采购评审应检查:是否能读取项目规范文件、是否支持审计日志、数据是否出境、是否可与 CI 规则兼容。否则会出现“工具很强但治理接不住”的割裂。架构师要把工具能力映射到治理闭环图上,缺一环就要求供应商补方案或内部补中间件。

12. 度量体系:除了拦截次数,还要看“修复时长”

企业常统计拦截次数,但更重要的是 从发现到修复的中位时间。如果规则导致大量误报,修复时长会飙升,团队会抵触。建议为每条规则记录:命中次数、误报反馈次数、平均修复时间。季度复盘时淘汰低价值规则,优化高价值规则。


代码实战:YAML 规则 + SecurityRuleChecker 完整实现

1. 示例 rules.yaml(放在仓库 tools/security_rules/rules.yaml

version: 1
rules:
  - id: SEC-001
    title: 禁止硬编码疑似密钥
    severity: error
    kind: regex
    pattern: "(?i)(api[_-]?key|secret|password|token)\\s*=\\s*[\"'][^\"']{8,}[\"']"
    message: 检测到疑似硬编码密钥赋值,请改用环境变量或密钥管理系统。

  - id: SEC-002
    title: 禁止字符串拼接 SQL
    severity: error
    kind: regex
    pattern: "(?i)execute\\(\\s*f[\"']"
    message: 检测到可能的 f-string SQL,请使用参数化查询。

  - id: ARCH-001
    title: presentation 不应直接 import sqlalchemy
    severity: error
    kind: forbidden_import
    file_glob: "app/presentation/*.py"
    modules: ["sqlalchemy", "sqlalchemy.orm"]

  - id: ARCH-002
    title: domain 不应 import fastapi
    severity: error
    kind: forbidden_import
    file_glob: "app/domain/*.py"
    modules: ["fastapi"]

  - id: BAD-001
    title: 禁止吞掉异常
    severity: warning
    kind: regex
    pattern: 'except\s+Exception\s*:\s*pass'
    message: "禁止 except Exception: pass;请记录日志并转换为可控错误。"

  - id: BAD-002
    title: 禁止显式 print 调试残留(警告)
    severity: warning
    kind: regex
    pattern: '(?m)^\s*print\('
    message: 请使用 logging 代替 print(教学项目可按需关闭此规则)。

2. 完整可运行脚本:security_rule_checker.py

将下列文件保存为 tools/security_rule_checker.py,并与 rules.yaml 同级目录或传入 --rules 路径。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
SecurityRuleChecker:从 YAML 加载规则,对 Python 文件做基础静态检查。

能力:
- regex 规则:按行匹配(适合快速黑名单)
- forbidden_import 规则:解析 AST,检查 import 模块前缀

说明:
- 这不是替代 bandit/semgrep 的完整安全引擎,而是教学用“可插拔规则骨架”。
- 真实企业建议:regex 规则谨慎使用,优先 AST/专用引擎;本示例兼顾可读与可运行。
"""

from __future__ import annotations

import argparse
import ast
import fnmatch
import json
import re
import sys
from dataclasses import dataclass, asdict
from pathlib import Path
from typing import Any, Iterable

import yaml


@dataclass
class Finding:
    rule_id: str
    severity: str
    path: str
    line: int
    message: str
    title: str


def load_rules(rules_path: Path) -> dict[str, Any]:
    data = yaml.safe_load(rules_path.read_text(encoding="utf-8")) or {}
    if not isinstance(data, dict):
        raise ValueError("rules.yaml 根节点必须是 mapping")
    return data


def path_matches_glob(file_path: Path, glob_pattern: str | None) -> bool:
    if not glob_pattern:
        return True
    cwd = Path.cwd().resolve()
    try:
        rel = file_path.resolve().relative_to(cwd)
    except ValueError:
        rel = file_path
    posix = rel.as_posix().replace("\\", "/")
    pat = glob_pattern.replace("\\", "/")
    return fnmatch.fnmatch(posix, pat)


def iter_py_files(targets: Iterable[Path]) -> list[Path]:
    files: list[Path] = []
    for t in targets:
        if t.is_file() and t.suffix == ".py":
            files.append(t)
        elif t.is_dir():
            files.extend(sorted(p for p in t.rglob("*.py") if p.is_file()))
    return files


def eval_regex_rule(rule: dict[str, Any], file_path: Path, text: str) -> list[Finding]:
    findings: list[Finding] = []
    pattern = rule.get("pattern")
    if not isinstance(pattern, str):
        return findings
    try:
        rx = re.compile(pattern)
    except re.error as e:
        raise ValueError(f"规则 {rule.get('id')} 正则非法:{e}") from e

    for idx, line in enumerate(text.splitlines(), start=1):
        if rx.search(line):
            findings.append(
                Finding(
                    rule_id=str(rule.get("id", "UNKNOWN")),
                    severity=str(rule.get("severity", "warning")),
                    path=file_path.as_posix(),
                    line=idx,
                    message=str(rule.get("message", "")),
                    title=str(rule.get("title", "")),
                )
            )
    return findings


def module_startswith(module: str, banned: str) -> bool:
    return module == banned or module.startswith(banned + ".")


def eval_forbidden_import_rule(rule: dict[str, Any], file_path: Path, text: str) -> list[Finding]:
    findings: list[Finding] = []
    modules = rule.get("modules") or []
    if not isinstance(modules, list) or not all(isinstance(m, str) for m in modules):
        return findings

    if not path_matches_glob(file_path, rule.get("file_glob")):
        return findings

    try:
        tree = ast.parse(text)
    except SyntaxError:
        # 语法错误交给编译/其他工具;这里忽略以免噪音
        return findings

    banned = list(modules)

    class Visitor(ast.NodeVisitor):
        def __init__(self) -> None:
            self.hits: list[tuple[int, str]] = []

        def visit_Import(self, node: ast.Import) -> None:
            for alias in node.names:
                name = alias.name
                for b in banned:
                    if module_startswith(name, b):
                        self.hits.append((node.lineno, name))
            self.generic_visit(node)

        def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
            if node.module is None:
                self.generic_visit(node)
                return
            mod = node.module
            for b in banned:
                if module_startswith(mod, b):
                    self.hits.append((node.lineno, mod))
            self.generic_visit(node)

    visitor = Visitor()
    visitor.visit(tree)

    for lineno, mod in visitor.hits:
        findings.append(
            Finding(
                rule_id=str(rule.get("id", "UNKNOWN")),
                severity=str(rule.get("severity", "warning")),
                path=file_path.as_posix(),
                line=lineno,
                message=f"禁止导入模块:{mod}{rule.get('message', '')})",
                title=str(rule.get("title", "")),
            )
        )
    return findings


def eval_rule(rule: dict[str, Any], file_path: Path, text: str) -> list[Finding]:
    kind = str(rule.get("kind", "regex"))
    if kind == "regex":
        return eval_regex_rule(rule, file_path, text)
    if kind == "forbidden_import":
        return eval_forbidden_import_rule(rule, file_path, text)
    return []


def main(argv: list[str]) -> int:
    parser = argparse.ArgumentParser(description="SecurityRuleChecker - YAML driven static checks")
    parser.add_argument(
        "--rules",
        default=str(Path(__file__).with_name("rules.yaml")),
        help="rules.yaml 路径",
    )
    parser.add_argument(
        "paths",
        nargs="+",
        help="要扫描的文件或目录(Python)",
    )
    parser.add_argument(
        "--json",
        action="store_true",
        help="以 JSON 输出 findings",
    )
    args = parser.parse_args(argv)

    rules_path = Path(args.rules)
    if not rules_path.exists():
        print(f"找不到规则文件:{rules_path}", file=sys.stderr)
        return 2

    doc = load_rules(rules_path)
    rules = doc.get("rules", [])
    if not isinstance(rules, list):
        print("rules.yaml: rules 必须是列表", file=sys.stderr)
        return 2

    targets = [Path(p) for p in args.paths]
    files = iter_py_files(targets)
    findings: list[Finding] = []

    for file_path in files:
        text = file_path.read_text(encoding="utf-8", errors="replace")
        for rule in rules:
            if not isinstance(rule, dict):
                continue
            findings.extend(eval_rule(rule, file_path, text))

    if args.json:
        print(json.dumps([asdict(f) for f in findings], ensure_ascii=False, indent=2))
    else:
        for f in findings:
            print(f"[{f.severity}] {f.rule_id} {f.path}:{f.line} {f.title} - {f.message}")

    # error severity causes non-zero exit for CI
    if any(f.severity == "error" for f in findings):
        return 1
    return 0


if __name__ == "__main__":
    raise SystemExit(main(sys.argv[1:]))

3. 运行示例

cd e:\mycode\column\AI架构师与代码审核实战\codesentinel-clean-lab
python tools\security_rule_checker.py --rules tools\rules.yaml app

本课程实验工程已内置同名脚本与规则文件:codesentinel-clean-lab/tools/security_rule_checker.pycodesentinel-clean-lab/tools/rules.yaml,并已把 pyyaml 写入 requirements.txt 便于环境一致。若扫描无输出且退出码为 0,表示未发现命中;若存在 error 级别命中,退出码为 1,可用于 CI 门禁。建议先在干净仓库验证,再逐步加严规则,避免一次性全团队阻塞,节奏很关键。


生产环境实战:把检查器接入 CI 与 PR 评论

1. CI 策略:失败门槛与警告门槛分离

建议 severity: error 直接失败;warning 允许先收集指标。两周后把最稳定的 warning 升级为 error。

2. 与 Semgrep/Bandit 的组合关系

SecurityRuleChecker 适合承载 组织特有规则(与目录结构强相关)。通用漏洞扫描仍建议交给专业工具。组合方式是:Bandit/Semgrep 管通用类问题,YAML 规则管架构黑名单。

3. 误报治理:每条规则要有 owner

为每条规则指定 owner(安全或架构),误报/issue 直达 owner,避免“没人敢关规则”。

4. 性能:大仓库扫描

全仓库 AST 解析可能耗时。可按变更文件增量扫描:CI 只扫描 git diff 涉及的 .py。教学脚本未内置增量模式,但你可以在外层用 git diff --name-only 传文件列表。

5. 审计:输出 JSON 供 CodeSentinel 入库

--json 输出可写入审查记录表:关联 pr_idcommitrule_idpathline。这就是治理平台闭环。

6. 与 LLM 生成协同:把 findings 回灌给修复代理

当检查器失败时,把 JSON findings 作为提示词的一部分交给修复代理,要求“只修复违规,不改变行为”。注意脱敏。

7. 合同与合规:把规则映射到控制点

金融、医疗等行业常需要把技术控制映射到合规控制点。YAML 里可加 control: SOC2-CC6.1 等字段(示例),便于审计导出。

8. 红队抽检:每月生成对抗样本

每月用红队方式刻意让模型生成违规代码,验证规则是否覆盖。未覆盖则补规则或补培训。

9. 版本化:rules.yaml 也需要 semver

version 字段变更应伴随公告。检查器应打印加载的版本,避免“本地与 CI 规则不一致”。

10. 灾难预案:规则导致全团队停摆怎么办

保留 bypass 机制(极谨慎):仅允许维护者用标签或环境变量临时跳过,并必须登记工单与复盘。不要把 bypass 变成常态。

11. 走查案例:一条 error 规则从提案到上线的全过程

假设要新增 ARCH-003:禁止 application 层直接 import requests。第一步,架构组在 PR 提出动机:我们要把外部 HTTP 调用限制在 infrastructure 适配器,避免用例层出现难测试的 IO。第二步,安全组评估:该规则不直接降低数据泄露风险,但降低供应链误用概率,定为 P1。第三步,测试组评估:是否导致大量历史代码失败——若失败超过阈值,先警告两周。第四步,合并规则并在 CI 启用。第五步,观察误报与修复时长。第六步,复盘并写 ADR。这个流程看似重,但对关键系统是值得的。

12. 规则编写技巧:message 字段如何写才利于自动修复

message 不应只骂“违规了”,而应包含 下一步:应迁移到哪一层、参考哪个模块示例、运行什么命令验证。对 AI 自动修复尤其重要:把 suggestion 作为可选字段加入 YAML,并在 CodeSentinel 的 PR 评论模板里展示。人类评审也能因此节省解释成本。

13. 与 bandit 的对比:什么时候用自研检查器

bandit 擅长通用 Python 安全问题;自研检查器擅长 与目录结构强绑定的架构规则。两者组合覆盖更广。不要在自研检查器里重复实现 bandit 已有规则,除非你需要定制消息与治理流程。重复实现会带来维护负担与结果不一致。

14. Windows 与路径:为什么示例使用 relative_to(cwd)

在 Windows 上绝对路径与盘符会导致 glob 匹配不稳定。以仓库工作目录为基准计算相对路径,再用 fnmatch 匹配,是教学示例的折中方案。更严谨的做法是传入 --root 参数固定仓库根。你可以把此改进作为课后作业:为 SecurityRuleChecker 增加 --root 并写测试。

15. 规则测试:给检查器写测试用例的重要性

检查器本身也是代码,也会出 bug。建议为每条规则准备 最小正例与反例文件(fixtures),在 CI 里运行 pytest 验证检查器输出稳定。否则会出现“规则改了但检查器没跟上”的二次事故。fixtures 还能作为培训材料,让新同学快速理解规则语义。

16. 性能再谈:AST 解析缓存

对大仓库,可以按文件 hash 缓存 AST 解析结果。教学脚本保持简单;企业实现可按需优化。优化前先 profiling,避免过早复杂化。

17. 与 IDE 集成:让开发者在提交前看到同样结果

本地 pre-commit 与 CI 使用同一命令同一规则文件,能显著降低“本地过了 CI 挂了”的挫败感。AI 代理在本地生成代码时,也可以被提示先运行检查器。统一入口是体验问题,也是治理问题。

18. 国际化团队:规则消息的多语言策略

若团队双语,至少保证 message 中文或英文其一为权威版本,另一语言作为可选字段 message_en。不要在同一字符串里中英混杂导致解析歧义。CodeSentinel 若服务多地区团队,应在平台层做消息映射,而不是把翻译堆进 YAML 失去可读性。

19. 最后的工程纪律:规则可以慢,但不能假

如果暂时无法自动化某条 MUST,应在规范里标注 人工评审点,并在 PR 模板用 checklist 强制勾选。最怕的是写了一条看起来很强但从未执行的规则,它会腐蚀团队对规范的信任。信任一旦流失,AI 与人类都会把规范当背景噪音。治理的本质是可信度,而不是条目数量。


本讲小结:脑图

mindmap
  root((企业级规范))
    方法
      风险驱动
      条款化
      自动化
    安全
      密钥
      输入输出
      依赖
    测试
      分层
      门槛
      数据
    反模式
      黑名单
      双重约束
    流程
      审批
      试运行
      正式发布
    工具
      YAML
      Checker
      CI

思考题

  1. 你们企业最关键的三条安全 MUST 是什么?它们能否映射到自动化规则?
  2. 你会把哪些反模式设为 error,哪些设为 warning?依据是什么?
  3. 如果业务紧急要求绕过规则,你会设计怎样的审计与回滚机制?

下一讲预告

模块三将继续把 规范 → 检查 → 评论 → 学习闭环 串起来:我们会把检查器输出接入 CodeSentinel 的评审流水线,讨论如何在 LLM 总结中引用规则 ID,让架构治理从“人眼盯”升级为“平台守”。同时会对比商业扫描工具与自研规则的边界,帮助你做出成本与覆盖率的理性组合。


4. 依赖说明

脚本需要安装 PyYAML:pip install pyyaml。若你希望将其纳入 requirements-dev.txt,请与团队约定固定版本号,避免 CI 漂移。


深度扩展:如何把测试要求写成“可签字的 SLA”

在企业里,测试门槛常常不是技术问题,而是 承诺问题。建议把测试要求写成三类 SLA:P0 变更必须附集成测试;P1 变更必须附单元测试;P2 文档/注释可豁免。SLA 需要业务方签字,否则研发会被无限挤压测试时间。AI 代理在高压提示下更容易跳过测试,因此 SLA 要写进规范 MUST,并在 CI 强制执行。强制执行初期会痛,但这是把质量从个人自律升级为系统能力的唯一路径。


深度扩展:反模式黑名单背后的架构价值观

黑名单不是刁难,而是对 可维护性可观测性 的投资。展示层直连数据库会破坏端口抽象,导致替换数据库或加缓存时成本爆炸;吞异常会让线上问题不可诊断;可变全局状态会让并发与测试变成噩梦。把价值观翻译成规则,团队才会在争论时有共同语言。共同语言一旦建立,AI 输出也会更稳定,因为模型收到的约束是明确且一致的。


深度扩展:安全规则的“默认安全”与“最小权限”如何写进提示词

除了 YAML 与 CI,企业还应在代理系统提示中重复三条:默认拒绝(未授权则不允许访问)、最小权限(只授予完成任务所需权限)、可审计(关键操作留痕)。这三条与具体代码规则互补:前者管行为,后者管实现。CodeSentinel 未来若支持多代理协作,系统提示层与企业规范层必须一致,否则会出现“嘴上说不记录密钥,代码里却打印环境变量”的分裂。分裂是治理失败的前兆,必须从文档与工具两侧同时收紧。

本讲至此把企业级规范的“制度面”和“工具面”连接起来:制度面解决谁有权改 MUST,工具面解决改了之后如何保证执行。制度与工具缺一不可,缺一就会出现“规范写得漂亮,落地一塌糊涂”的常见困局。希望你带着 YAML 规则与检查器脚本回到团队,先做小范围试点,再逐步扩大覆盖面。试点阶段重点关注误报率与开发者体验,不要让工具成为阻力,而要让它成为加速器。加速器跑起来之后,再把更复杂的 Semgrep 规则、依赖扫描、容器镜像扫描逐步纳入同一套治理闭环。闭环越大,风险越小;风险越小,AI 才越敢用在关键系统上。关键系统用上 AI 之后,架构师的价值会更体现在治理与取舍,而不是堆代码速度。把这一讲当作模块三的“硬门槛演练”,下一讲我们把它接入平台流水线,让规则真正跑进每一次评审。