AI 编程代理可以读代码、修改文件、运行命令,甚至连续完成多个开发步骤。但“代理说已经完成”并不等于改动值得合并。真正的 Agentic Engineering,不是让模型更大胆地写代码,而是建立一条可验证的交付链:需求变成验收条件,改动接受类型约束,行为由测试证明,最终差异由人审查。
核心转变可以概括为一句话:不要评价代理看起来有多聪明,要检查它留下了什么证据。
把任务描述改造成可执行契约
“修复折扣计算”对人和代理都过于模糊。它没有说明负数金额是否合法、折扣范围是多少、金额如何舍入,也没有定义哪些现有行为不能改变。代理很容易生成一个表面合理、边界条件却不明确的补丁。
更适合交给代理的任务,应包含四类信息:
- 目标行为:输入、输出和业务规则。
- 允许的改动范围:可以修改哪些文件,是否允许增加依赖。
- 验证命令:代理完成前必须运行什么。
- 停止条件:遇到需求冲突、测试异常或大规模重构时,不得自行猜测。
可以直接使用下面这样的任务提示词:
任务:为 pricing.final_price 增加百分比折扣支持。
验收条件:
1. subtotal 必须是非负 Decimal。
2. discount_pct 必须是 0 到 100 之间的整数。
3. 返回值按 ROUND_HALF_UP 保留两位小数。
4. 不改变现有公开函数名,不增加运行时依赖。
5. 为正常输入、边界值和非法输入补充测试。
允许修改:pricing.py、tests/test_pricing.py。
禁止修改:现有测试断言、项目工具配置。
完成前必须运行:
python -m pytest
python -m mypy pricing.py
python -m ruff check .
如果验收条件互相冲突,或者必须修改范围外文件,请停止并说明原因,不要猜测。
最终回复必须列出:改动文件、验证命令、验证结果、未解决风险。
这里最重要的不是提示词写得长,而是它把“完成”定义成了可观察的状态。限定文件范围还能降低代理顺手重构、升级依赖或改写无关模块的风险。
建立一个可运行的证据最小集
下面是一个可以直接复制的最小项目。它使用 pytest 验证运行行为,用 mypy 检查静态类型,再用 ruff 捕获常见代码问题。
运行前需要 Python 3.11 或更高版本,以及 Bash 兼容的终端:
mkdir agentic-evidence-demo
cd agentic-evidence-demo
cat > pyproject.toml <<'EOF'
[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"
[project]
name = "agentic-evidence-demo"
version = "0.1.0"
requires-python = ">=3.11"
[project.optional-dependencies]
dev = [
"pytest>=8",
"mypy>=1.10",
"ruff>=0.5",
]
[tool.pytest.ini_options]
addopts = "-q"
[tool.mypy]
strict = true
[tool.ruff]
line-length = 88
EOF
cat > pricing.py <<'EOF'
from decimal import Decimal, ROUND_HALF_UP
CENT = Decimal("0.01")
def final_price(subtotal: Decimal, discount_pct: int = 0) -> Decimal:
"""Return a non-negative price rounded to two decimal places."""
if subtotal < 0:
raise ValueError("subtotal must be non-negative")
if not 0 <= discount_pct <= 100:
raise ValueError("discount_pct must be between 0 and 100")
multiplier = Decimal(100 - discount_pct) / Decimal(100)
return (subtotal * multiplier).quantize(CENT, rounding=ROUND_HALF_UP)
EOF
mkdir -p tests
cat > tests/test_pricing.py <<'EOF'
from decimal import Decimal
import pytest
from pricing import final_price
def test_applies_discount_and_rounds_half_up() -> None:
assert final_price(Decimal("10.05"), 50) == Decimal("5.03")
def test_accepts_boundary_discounts() -> None:
assert final_price(Decimal("12.34"), 0) == Decimal("12.34")
assert final_price(Decimal("12.34"), 100) == Decimal("0.00")
def test_rejects_negative_subtotal() -> None:
with pytest.raises(ValueError, match="subtotal"):
final_price(Decimal("-0.01"))
@pytest.mark.parametrize("discount", [-1, 101])
def test_rejects_invalid_discount(discount: int) -> None:
with pytest.raises(ValueError, match="discount_pct"):
final_price(Decimal("10.00"), discount)
EOF
python -m venv .venv
. .venv/bin/activate
python -m pip install -e '.[dev]'
python -m pytest
python -m mypy pricing.py
python -m ruff check .
这三道检查覆盖的问题并不相同:
pytest证明列出的示例行为当前成立。mypy检查函数调用和返回值是否符合类型契约。ruff提供快速、稳定的静态规则检查。
它们互相补充,却不能互相替代。类型正确的函数仍可能算错折扣;测试通过也可能只是因为漏掉了关键边界;代码格式整洁更不代表业务安全。
让代理提交“补丁加证据”,而不是结论
代理最不可靠的输出之一,是自然语言中的成功声明。它可能说“所有测试都通过”,但实际只运行了一个测试文件;也可能为了通过检查而删除断言、扩大忽略规则,甚至把错误吞掉。
因此,评审时应直接查看仓库状态和差异:
git status --short
git diff --stat
git diff -- pricing.py tests/test_pricing.py
git diff -- pyproject.toml
python -m pytest
python -m mypy pricing.py
python -m ruff check .
建议把生产代码和测试改动分开审查:
- 先读生产代码,不看新增测试:判断实现是否符合业务规则,是否出现过度抽象、静默异常处理或隐藏副作用。
- 再单独读测试差异:确认代理没有删除断言、把精确比较改成宽松比较,或只覆盖自己的实现路径。
- 重新运行命令:不要只接受代理粘贴的日志,尤其不要信任无法对应当前提交的结果。
- 检查改动边界:任务只涉及两个文件,却出现锁文件、CI 配置和多个模块变化时,应要求解释或拆分补丁。
还可以要求代理先展示计划,再获得许可后修改。对于数据库迁移、认证、支付、权限和基础设施代码,这个暂停点尤其重要。
“测试通过”仍然不是安全证明
工程证据有强弱之分。单元测试证明的是有限样例,类型系统证明的是一部分结构约束,代码审查则依赖审查者的领域知识。它们提高可信度,却不能证明程序在所有输入、所有环境下绝对安全。
常见盲区包括:
- 测试与实现由同一个代理同时生成,两者可能共享同一种误解。
- 代理可能修改测试、快照或类型忽略配置,让检查失去约束力。
- 本地测试无法覆盖生产数据规模、并发、网络失败和权限差异。
- 新增依赖可能引入供应链、许可证或版本兼容风险。
- 一个很小的补丁也可能改变公开 API、日志内容或异常类型。
高风险项目可以继续增加独立证据,例如属性测试、模糊测试、集成环境验证、安全扫描、性能基准和分阶段发布。关键原则是:证据应尽量来自彼此独立的检查,而不是让同一个模型既出题、答题,又宣布满分。
合并前的实用检查表
将代理生成的改动保留下来之前,可以逐项确认:
- 验收条件已经写成测试或可重复执行的命令。
- 类型检查针对新增或修改的代码启用,且没有新增无理由的忽略项。
- 代理没有删除、跳过或弱化原有测试。
- 实际差异与任务范围一致,没有夹带重构或依赖升级。
- 验证命令由开发者或 CI 在干净环境中重新执行。
- 异常路径、边界输入和兼容性影响已被检查。
- 安全敏感和不可逆操作仍需人工批准。
- 合并请求记录了已知限制,而不是只写“测试通过”。
Agentic Engineering 的价值,不在于把代码审查交给代理,而在于让代理进入现有工程控制体系。模型负责加速探索和实现;测试、类型、差异审查与人工判断负责决定这些改动是否值得信任。这样,团队保留的就不再是一段“感觉不错”的代码,而是一组可以复查、可以重复、也可以拒绝的工程证据。