Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

第四章:架构约束 —— 让 Agent 不走偏

如果说上下文工程是让 Agent "看得见",那么架构约束就是让 Agent "不走偏"

核心命题:

限制解决方案空间,才能增加输出的信任度和可靠性。 [2]

为什么约束是前提而非奢侈品

传统观念 vs AI 时代

观念传统理解AI 时代翻转
架构约束大团队才需要的奢侈品第一天就需要的先决条件
代码规范建议性的,可以灵活变通必须机械化强制执行
技术选型选"最好"的技术选 Agent 最能理解的技术
代码审查人类把关就够了Agent 产出太快,审查跟不上

速度悖论

没有约束,Agent 的速度越快,代码库的混乱就积累得越快。 最终速度会断崖式下降。

速度 × 无约束 = 快速崩溃 速度 × 有约束 = 持续加速

确定性规则 > 自然语言描述

核心原则

能用代码强制执行的约束,就不要用自然语言描述。

# ❌ 自然语言描述 — Agent 会形式遵守,实质忽略
# docs/CONVENTIONS.md
# "请确保所有服务之间的依赖方向是单向的,从 UI 到 Types。"

# ✅ 确定性规则 — Agent 无法违反
# custom_linter/dependency_direction.py
def check_dependency_direction(source_module, target_module):
    """检查依赖方向是否符合分层架构"""
    layers = {"Types": 0, "Config": 1, "Repo": 2, "Service": 3, "Runtime": 4, "UI": 5}
    
    source_layer = get_layer(source_module)
    target_layer = get_layer(target_module)
    
    if layers[source_layer] < layers[target_layer]:
        # 在错误信息中嵌入修复指令!
        raise DependencyError(
            f"违反单向依赖: {source_module}({source_layer}) → {target_module}({target_layer})\n"
            f"修复: {target_layer} 层不应依赖 {source_layer} 层。\n"
            f"请将共享逻辑提取到 {source_layer} 层中。"
        )

修复指令注入

OpenAI 团队的一个巧妙设计:在 lint 错误信息中直接嵌入 Agent 可理解的修复指导

传统 lint 错误:
  Error: Dependency violation at line 42

修复指令注入:
  Error: Dependency violation at line 42
  Fix: UI modules should not import Types directly.
       Use the Service layer as intermediary.
       See docs/ARCHITECTURE.md#dependency-rules

这让 Agent 遇到违规时可以立即自我纠正,而不是困惑地猜测该怎么办。

分层架构实践

依赖方向控制

OpenAI 团队的分层域架构模型:

依赖方向(严格单向):

Types → Config → Repo → Service → Runtime → UI
  ↓
Providers(横切关注点入口)
  ↓
认证、连接器、遥测、功能标志
# 分层依赖检查器实现
class LayerDependencyChecker:
    """分层架构依赖检查器"""
    
    LAYERS = {
        "types": 0,
        "config": 1,
        "repo": 2,
        "service": 3,
        "runtime": 4,
        "ui": 5,
    }
    
    ALLOWED_CROSS = {
        # 每层可以依赖同层及以下所有层
        "ui": {"runtime", "service", "repo", "config", "types"},
        "runtime": {"service", "repo", "config", "types"},
        "service": {"repo", "config", "types"},
        "repo": {"config", "types"},
        "config": {"types"},
        "types": set(),
    }
    
    def check_import(self, source_file: str, import_path: str) -> CheckResult:
        source_layer = self._get_layer(source_file)
        target_layer = self._get_layer(import_path)
        
        if target_layer not in self.ALLOWED_CROSS.get(source_layer, set()):
            return CheckResult(
                passed=False,
                error=self._generate_fix_message(source_layer, target_layer)
            )
        
        return CheckResult(passed=True)
    
    def _generate_fix_message(self, source: str, target: str) -> str:
        return (
            f"违反分层依赖: {source} 层不应直接依赖 {target} 层\n"
            f"修复建议: 将共享逻辑下移到 {target} 层,"
            f"或通过 Service 层作为中介层访问"
        )

结构测试

用测试来验证架构约束:

# tests/test_architecture.py
import pytest
from custom_linter.architecture import check_layer_dependencies, check_module_boundaries

class TestArchitectureConstraints:
    """架构约束测试 — 确保 Agent 不会违反架构规则"""
    
    def test_no_ui_imports_types_directly(self):
        """UI 层不应直接导入 Types 层"""
        violations = check_layer_dependencies(
            source_layer="ui",
            forbidden_targets=["types"],
        )
        assert len(violations) == 0, f"发现违规: {violations}"
    
    def test_no_circular_dependencies(self):
        """不应存在循环依赖"""
        cycles = find_circular_dependencies("src/")
        assert len(cycles) == 0, f"发现循环依赖: {cycles}"
    
    def test_service_isolation(self):
        """不同业务域的 Service 不应互相依赖"""
        violations = check_module_boundaries(
            module_pattern="service/*",
            allow_cross=False,
        )
        assert len(violations) == 0
    
    def test_file_size_limits(self):
        """单文件不应超过规定行数"""
        large_files = find_files_exceeding_lines("src/", max_lines=300)
        assert len(large_files) == 0, f"过大文件: {large_files}"

品味编码化

将人类品味转化为机器规则

架构约束不仅包括宏观的依赖方向,还包括微观的"品味"——代码风格、命名约定、日志格式等不变式。

# custom_linter/style_rules.py
class StyleLinter:
    """编码化的人类品味"""
    
    rules = [
        # 结构化日志记录
        {
            "id": "no-console-log",
            "pattern": r"console\.log\(",
            "message": "禁止使用 console.log,请使用结构化日志 logger.info()",
            "fix": "替换为: logger.info('message', {key: value})"
        },
        
        # 命名约定
        {
            "id": "schema-naming",
            "pattern": r"class\s+(?!.*Schema).*Model",
            "message": "Schema 类必须以 'Schema' 结尾",
            "fix": "重命名为: {ClassName}Schema"
        },
        
        # 文件大小限制
        {
            "id": "file-size",
            "check": "file_length",
            "max_lines": 300,
            "message": "文件超过 {max_lines} 行,请考虑拆分",
            "fix": "将职责拆分到独立模块中"
        },
        
        # 可靠性要求
        {
            "id": "error-handling",
            "pattern": r"await\s+\w+\([^)]*\)",
            "message": "异步调用必须有错误处理",
            "fix": "包裹在 try/except 中或使用 Result 类型"
        },
    ]

一个重要的认知转变

生成的代码不必符合人类的风格偏好

只要输出是:

  • 正确的 ✅
  • 可维护的 ✅
  • 对未来的 Agent 运行清晰可读的 ✅

就算达标。人类的品味通过审查评论、重构 PR 和 bug 修复被捕获, 然后编码到工具和规则中——一旦被捕获,就会持续应用于每一行代码。

分层设计

中央层面:强制执行边界
├── 正确性约束
├── 可重复性约束
└── 安全性约束

本地层面:允许自主权
├── 解决方案的表达方式
├── 具体算法选择
└── 实现细节

类比:
├── 平台团队定义铁律
└── 业务团队在铁律内自由发挥

CI 集成

约束即流水线

# .github/workflows/harness.yml
name: Harness Constraints

on: [pull_request]

jobs:
  architecture:
    name: 架构约束检查
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: 分层依赖检查
        run: python tools/check_layer_deps.py
        
      - name: 循环依赖检测
        run: python tools/check_circular_deps.py
        
      - name: 文件大小限制
        run: python tools/check_file_sizes.py --max 300
        
      - name: 命名规范检查
        run: python tools/check_naming.py
        
      - name: 结构化日志检查
        run: python tools/check_logging.py

  quality:
    name: 质量评估
    needs: architecture
    runs-on: ubuntu-latest
    steps:
      - name: 运行评估
        run: python -m harness evaluate --dataset golden_set
        
      - name: 质量阈值检查
        run: |
          score=$(cat results/latest.json | jq '.overall.score')
          if (( $(echo "$score < 0.75" | bc) )); then
            echo "Quality score $score below threshold 0.75"
            exit 1
          fi

实践指南

行业案例:约束的回报

不同公司在实践中验证了"约束 > 指令"的原则:

Vercel:工具从 15 个简化到 2 个
├── 问题:Agent 面对 15 个工具时经常选错
├── 方案:简化为 read_file + edit_file 两个核心工具
├── 效果:准确率从 80% 提升到 100%
└── 启示:减少选择空间 = 提高决策质量

Cursor:约束 > 指令
├── 问题:自然语言指令经常被 Agent 忽略
├── 方案:将关键规则编码为代码约束(linter/CI)
├── 效果:违规率从 30% 降到接近 0%
└── 启示:Agent 无法违反它不能绕过的规则

Stripe:CI 限速
├── 问题:Agent 在 CI 中反复修复失败,浪费时间
├── 方案:限制 CI 重试最多 2 轮
├── 效果:节省 40% CI 资源,Agent 学会一次做对
└── 启示:合理的约束反而提升效率

共性规律

三个案例指向同一个规律:

约束形式 效果 自然语言指令 → Agent 有时遵守,有时不遵守 linter 规则 → Agent 每次都遵守 CI 约束 → Agent 无法不遵守

确定性越强,合规率越高。

约束设计原则

原则说明
机械可执行规则应该是工具可以自动检查的
修复指令嵌入错误信息应包含 Agent 可理解的修复步骤
渐进加强先加最重要的约束,逐步增加
约束即文档约束本身就是最好的架构文档

快速检查清单

✅ 架构约定是否通过代码强制执行(而非仅文档描述)?
✅ lint 错误信息是否包含修复指令?
✅ 是否有分层依赖检查?
✅ 是否有结构测试?
✅ CI 是否集成所有约束检查?
✅ 约束是否与 Agent 协同设计(而非仅面向人类)?

小结

架构约束 Checklist

✅ 确定性规则 > 自然语言描述 ✅ 修复指令嵌入错误信息 ✅ 分层架构 + 单向依赖 ✅ 品味编码化为 lint 规则 ✅ CI 流水线强制执行所有约束

参考文献:

  • [1] Ryan Lopopolo, "Harness engineering: leveraging Codex in an agent-first world", OpenAI, 2026
  • [2] Birgitta Böckeler, "Harness Engineering", martinfowler.com, 2026
  • [3] "模型不是关键,Harness 才是", 微信公众号, 2026

下一章,我们将探讨垃圾收集——如何持续对抗 Agent 引入的熵增。