Harness Engineering 元年:给 AI Agent 装上交付门禁,从「能跑」到「敢上线」
AI Agent 的 Demo 越做越惊艳,可一接真实流量就翻车:改了提示词没回归、工具调用悄悄越权、效果好坏全靠人肉盯日志,交付节奏被一次次打回。
常规做法明显不够用——仅靠 Prompt 里写一句「禁止输出敏感信息」,模型根本不保证遵守;靠人工 review 每次改动,评审者看不到执行轨迹,只能凭直觉点头;出了问题再翻日志救火,等发现时事故已经发生。
本文分享一套可直接落地的 Harness Engineering 门禁实践:结构化留痕 + 离线评测打分 + 策略脚本阻断,配合分阶段灰度,让 Agent 从「能跑」变成「可控交付」。
一、门禁思维:Agent 交付要拦住三类风险
先明确一个前提:门禁不是为了卡人,而是为了把风险在合入前拦住:
- 效果回退:一次提示词或模型版本调整,让历史好用的能力悄悄变差,没有基线就发现不了。
- 行为越界:Agent 自主调用工具时读了不该读的数据、说了不该说的话,运行时才发现等于事故。
- 不可回溯:出问题时拿不出「当时跑了什么、模型答了什么、调了哪个工具」,只能靠猜。
| 维度 | 「能跑」阶段 | 「可控交付」阶段 |
|---|---|---|
| 判定标准 | 演示链路不报错 | 指标达阈值且可回滚 |
| 失败处理 | 上线后人工救火 | 门禁自动阻断合入 |
| 责任归属 | 出事再翻日志 | 每次改动有证据留痕 |
问题:靠 Demo 和口头确认推进交付;治理:把三类风险翻译成可执行的检查项,让机器来判定放不放行。
核心结论: 门禁的本质是把「我觉得没问题」换成「数据说可以合」。

二、可观测留痕:让每次执行变成可查证据
要让门禁有据可依,先给每次执行落一套结构化证据:
- 统一 Schema:固定字段记录 trace_id、span、状态、耗时与 token 消耗,跨版本才能横向比对。
- 全链路 Span:计划、工具调用、最终回答各占一个 span,失败时能直接定位到是哪一环。
- 原文入档:输入输出原文单独存储,脱敏后保留,供评测与事故复盘调用。
下面这段 Python 生成一份最小可用的执行轨迹,适配任何 Agent 框架的埋点后统一上报:
import json, time, uuid
def make_span(name, parent, status, latency_ms, tokens_in, tokens_out):
return {
"trace_id": uuid.uuid4().hex[:16],
"span": name, "parent": parent, "status": status,
"latency_ms": latency_ms,
"tokens": {"in": tokens_in, "out": tokens_out},
"ts": time.time(),
}
spans = [
make_span("plan", None, "ok", 820, 1200, 260),
make_span("tool:search", "plan", "ok", 1450, 400, 0),
make_span("answer", "tool:search", "ok", 960, 2100, 480),
]
print(json.dumps(spans, ensure_ascii=False, indent=2))
强埋点 → 可回放 → 有证据:没有轨迹的 Agent,任何门禁都是空转。
三、离线评测:用黄金集给每次改动打分
改动该不该合,不能凭手感,要交给固定评测集算分:
- 黄金集:从真实工单里挑 30~100 条问答对,标注期望答案,版本之间复用同一份。
- 门槛分:先跑一版拿到基线分,此后任何改动低于基线即阻断,防止一点点回退。
- 多维打分:除准确率外,把延迟、敏感词命中、工具调用正确率一并纳入门槛。
下面这段 Python 是可直接跑的最小编分器,替换成你的 runner 即可接入 CI:
GOLDEN = [
{"q": "退款多久到账?", "expect": "3-5 个工作日"},
{"q": "发票能重开吗?", "expect": "当月可申请一次"},
]
def score(runner, threshold=0.9):
hits = sum(runner(item["q"]) == item["expect"] for item in GOLDEN)
acc = hits / len(GOLDEN)
return {"acc": round(acc, 3), "pass": acc >= threshold}
print(score(lambda q: "3-5 个工作日" if "退款" in q else ""))
# {'acc': 0.5, 'pass': False}
| 指标 | 含义 | 建议门槛 |
|---|---|---|
| 回答准确率 | 与黄金集期望答案一致 | ≥ 基线分 |
| P95 延迟 | 95 分位端到端耗时 | ≤ 4000 ms |
| 越权调用率 | 触发未授权工具的比例 | = 0 |
核心结论: 评测集是唯一的度量衡,没有它,所谓优化只是换了个方向波动。

四、策略阻断:把护栏写成可执行脚本
分数达标只是一半,另一半是把红线规则固化成检查脚本:
- 钩子前置:把检查挂在 pre-commit 或 CI 首个 job,问题在合入前暴露,成本最低。
- 失败即阻断:任何一项返回非零就终止流水线,不给「先合了再补」留口子。
- 规则即代码:敏感词、延迟上限、调用白名单全部写进脚本,评审时 diff 可见。
下面是一份可直接放进仓库 .gates/ 目录的门禁脚本,适配 Linux + Python 环境:
#!/usr/bin/env bash
# 交付门禁:CI 或 pre-commit 调用,任一失败即退出非零
set -euo pipefail
python eval/golden_set.py --threshold 0.9 # 评测分达标
python lint/agent_trace.py --max-p95 4000 # P95 延迟上限 4s
python policy/leak_check.py --deny "身份证号" # 敏感输出拦截
echo "GATE PASSED"
脚本化红线 → 机器判定 → 零口头放行:规则一旦写成代码,就没有「这次特殊」。
五、灰度放行:分阶段收紧爆炸半径
静态检查全绿也别急着全量,放行要按阶段逐级加压:
- 影子流量:新版本只旁路记录不返回用户,先比对新旧回答差异,零用户风险。
- 小流量实测:切 5% 真实流量,盯评测分、延迟与人工投诉三条曲线。
- 一键回滚:开关与版本号绑定,任一指标越界秒级切回上一版本,不留中间态。
| 阶段 | 流量 | 放行条件 | 回滚动作 |
|---|---|---|---|
| 影子 | 0% | 新旧回答一致率 ≥ 95% | 丢弃新版本 |
| 小流量 | 5% | 指标无劣化持续 24h | 开关切回 |
| 全量 | 100% | 无告警持续 72h | 保留热备版本 |
核心结论: 灰度不是拖延上线,而是把「不可逆的大事故」拆成「可逆的小实验」。

六、两周落地:按四步把门禁装进流水线
门禁不必一步到位,两周即可跑出最小闭环:
- 第一周·留痕:接入统一 trace Schema,跑通一条链路的全 span 上报,导出 JSON 可查。
- 第二周前半·评测:从工单里筛出 30 条黄金集,跑出基线分并写入仓库。
- 第二周后半·阻断:把评测、延迟、敏感词三个检查挂进 CI,设为合入必过项。
- 随后两周·灰度:新版本先影子再 5% 流量,验证回滚开关真的能在秒级生效。
落地时最容易踩的坑,是把门禁做成「事后补作业」:先有证据,再有分数,最后才有阻断,顺序颠倒只会让团队绕开门禁。每道门都要能独立回答一个问题——出了事查得到吗、改了动更差吗、越了权拦得住吗。
核心结论: 顺序不能乱:留痕 → 评测 → 阻断 → 灰度,四步各解决一个不可控。
结语
Harness Engineering 的价值不在框架有多新,而在于把 Agent 交付从「玄学」变成「工程」:结构化留痕让事故可回溯,离线评测让效果可度量,策略脚本让红线可执行,灰度放行让风险可回滚。这套门禁不绑定任何特定 Agent 框架,脚本与表格都能直接抄进你自己的仓库。
先把最小闭环跑起来——一条 trace、一份黄金集、三个检查项,你会发现 Agent 的「敢上线」不是靠胆量,而是靠门禁。
能跑是运气,可控才是工程;门禁装上,Agent 才算真正交付。
更多推荐

所有评论(0)