我在 Hermes Agent 里装了 QGS(Quality Gate System)质量门禁——一个会在每次复杂任务输出前自动评分的系统。运行了10天,46次被阻断,102次人工审批点。这篇用真实数据复盘:它值不值得装?踩了多少坑?以及什么情况下 QGS 反而会拖后腿。


1. 缘起:为什么要在 Agent 里装刹车

去年我写了一篇 QGS 质量门禁系统:给 AI Agent 装上”刹车和质检”,介绍了 QGS 的设计理念——在 Agent 输出前加一道质量评分闸门,分数低于阈值就阻断输出。

当时只是一个实验性的想法,跑的是简单任务。

这一个月,我把 QGS 接进了生产环境——每天 20+ 个 cron 任务、多模型并发、跨项目协作,复杂度远超当年。

10 天下来,审计日志里记录了 373 次评分、46 次阻断、102 次人工审批点。数据不会说谎——我们来聊聊值不值。

2. 生产环境数据:46 次刹车背后的故事

先上硬数据。QGS 的所有决策都写入 ~/.hermes/qgs/audit_wal.jsonl(append-only JSONL),我从中提取了以下统计:

整体分布:

  • 总决策数:373 次(平均每天 37 次)
  • 阻断率:12.3%(46/373)
  • 平均分:64.5
  • 高分(≥80):203 次(54.4%)
  • 中分(60-79):37 次(9.9%)
  • 低分(<60):133 次(35.7%)——这些里只有 46 次被真正阻断

阻断分数分布:

  • 0-10 分(严重质量问题):15 次(32.6%)
  • 11-30 分(明显缺陷):6 次(13.0%)
  • 31-50 分(边界模糊):25 次(54.3%)

有趣的是,超过一半的阻断发生在 31-50 分的”灰色地带”——系统认为质量不够好,但未必有致命错误。这引出了一个关键设计问题:QGS 的阈值设在哪里?

一个真实阻断案例

8 月 8 日,session 20260808_083 被连续阻断 8 次。时间线:

1
2
3
4
5
6
7
8
08:41 → 评分 100(通过)
09:13 → 评分 15(阻断)
09:21 → 评分 90(通过)
...
15:17 → 评分 41(阻断)
15:56 → 评分 48(阻断)
20:00 → 评分 0(阻断)
20:04 → 评分 0(阻断)

这个 session 涉及多轮对抗式评审,最终在第 7 轮收敛通过。8 次阻断意味着用户必须反复修改,体验很差——但这正是 QGS 的设计意图:宁可多花时间和 LLM 死磕,也不交付半成品

3. 核心架构:三道闸门怎么工作

QGS 不是简单的一次评分,而是三道闸门协同工作:

第一道:pre_llm_call(任务分类)

每次用户输入后,QGS 先判断任务复杂度:

类型 处理方式 比例
QUERY 快速通道,直接放行 ~60%
SIMPLE 启发式检查(短响应自动通过) ~20%
COMPLEX 完整对抗式评审 ~15%
REFLECT 深度反思评审 ~5%

分类错误是常见 bug 源——我把简单查询误判为 COMPLEX 会导致不必要的评审延迟。

第二道:transform_llm_output(评分+阻断)

这是核心闸门。COMPLEX/REFLECT 任务的输出会被送入多评审者并行评分:

1
2
3
4
5
用户输入 → LLM 生成草稿 → QGS 评分 → 通过/阻断

评分 < 60 → 阻断 + 给出批评

评分 ≥ 60 → 附加建议后放行

阻断时,QGS 不会直接丢弃输出,而是在响应前插入拒绝头部,原始内容保留在下方——这是关键设计,防止误判导致内容丢失。

第三道:post_llm_call(轻量审计)

每次评审后,QGS 从 session state 中回放结果写入 JSONL,不做额外 LLM 调用。这是性能优化:把”评审”和”审计”分开,避免重复计算。

4. 人工审批点:当 QGS 说”我需要你的意见”

除了自动阻断,QGS 还有一个更精细的机制:收敛等待审批(convergence_wait_approval)。

当评审得分达到 100 但发现潜在问题(issues > 0)时,QGS 不阻断也不放行,而是进入”审批等待”状态,等待用户确认:

1
2
3
2026-08-06 11:24 | phase=phase_1_arch | score=50 | issues=1
2026-08-06 11:25 | phase=phase_1_arch | score=100 | issues=2 ← 关键转折点
2026-08-06 11:30 | phase=phase_1_arch | score=50 | issues=1

从审计日志看,phase_1_arch(架构设计阶段)是最常被要求审批的阶段,共 30 次,占全部审批的 29.4%。这很合理——架构错误比代码错误更难修复,人工介入的价值最高。

5. 踩坑记录:QGS 的 6 个设计陷阱

坑 1:审批点过多导致用户疲劳

症状:一个复杂任务在 phase_1_arch 阶段被要求审批 6 次,用户产生”为什么不能直接通过”的困惑。

根因:QGS 的收敛条件是 score >= threshold AND issues <= max_issues,但 threshold 和 max_issues 的默认值(60 分、2 个问题)对某些任务类型过于严格。

修复

1
2
3
4
5
6
7
8
9
# config.yaml
qgs:
convergence:
score_threshold: 60
max_issues: 2
# 新增:按任务类型差异化配置
task_type_override:
CODE_REVIEW: { score_threshold: 55, max_issues: 3 }
ARCH_DESIGN: { score_threshold: 70, max_issues: 1 }

坑 2:阻断循环(Block Loop)

症状:同一个 session 连续阻断 3 次,用户无法推进。

根因:QGS 的 critic(评审者)对某些输出模式过于苛刻,形成”阻断-修改-再阻断”的死循环。

修复:引入阻断循环破限(block-loop breaker)机制——连续 3 次阻断后强制通过,并标记警告:

1
2
3
4
5
6
7
8
# __init__.py
_session_consecutive_blocks: dict[str, int] = {}

def _check_block_loop(session_id: str) -> bool:
count = _session_consecutive_blocks.get(session_id, 0)
if count >= 3:
return True # 强制通过
return False

生产环境中,20260814_083 session 触发了这个机制——6 次阻断后系统自动放行。

坑 3:评分分布偏移

症状:大部分得分集中在 0-50 分区间,正常输出也被阻断。

数据:阻断分数分布显示,54.3% 的阻断发生在 31-50 分的”灰色地带”。

根因:critic 的评分标准偏向”找错”而非”评估整体质量”,导致”没有大错但也有问题”的输出被误判。

修复

  1. 调整评分权重:从”问题数量”转向”问题严重程度”
  2. 增加”通过但建议改进”的中间状态(而非非黑即白)

坑 4:预算耗尽后的降级行为

症状:长会话中 QGS 评审成本累积,触发预算耗尽后降级为启发式评审。

根因:每次 LLM 评审消耗 token,默认预算为 20 次评审或 2.0 美元,长会话容易超支。

修复(2026-08-10 新增):

1
2
3
4
5
6
7
8
9
10
_session_budget_usage: dict[str, dict] = {}

def _check_budget(session_id: str, deliverable: str) -> tuple[bool, dict]:
usage = _session_budget_usage.setdefault(session_id, {...})
est = _estimate_review_cost(deliverable)
if usage["reviews"] + 1 > cfg["max_reviews"] or \
usage["est_cost_usd"] + est > cfg["max_usd"]:
usage["exhausted"] = True
# 降级为启发式评审
return False, usage

实际效果:在生产中,预算耗尽发生在约 15% 的长会话中,降级后评审质量略有下降但可接受。

坑 5:分类器误判

症状:简单查询被误判为 COMPLEX,触发不必要的评审。

根因:分类器基于关键词匹配,某些词汇(如”分析””评估”)会触发 COMPLEX 分类,即使用户只是问一个简单事实。

修复

1
2
3
4
# 增加快速通道规则
fast_path_keywords = ["几点了", "天气", "什么是X", "帮我翻译"]
if any(kw in msg for kw in fast_path_keywords):
return "QUERY" # 强制快速通道

坑 6:审计日志的查询性能

症状audit_wal.jsonl 文件增长过快(当前 373 行,预计每月数万行),grep 查询变慢。

根因:每次评审都追加一行 JSONL,无轮转机制。

修复

1
2
3
# 每月 1 号轮转
0 0 1 * * cp ~/.hermes/qgs/audit_wal.jsonl ~/.hermes/qgs/audit_wal-$(date -d yesterday +%Y%m).jsonl.gz
0 0 1 * * truncate -s 0 ~/.hermes/qgs/audit_wal.jsonl

6. 什么时候不该用 QGS

QGS 不是银弹,以下场景建议禁用:

场景 原因 替代方案
创意写作 质量评分标准主观,容易误判 人工审核
快速原型 迭代速度优先于质量 禁用 QGS,事后review
简单查询 误判成本高,收益低 分类器快速通道
已验证的 pipeline 上游已有多重检查 后置审计即可

经验法则:如果任务的”错误成本”高于”评审延迟成本”,用 QGS。反之则不用。

7. 总结:46 次刹车换来的教训

运行 10 天,46 次阻断,102 次审批点,QGS 的生产表现可以总结为三点:

  1. 阻断是有效的——被阻断的 46 次输出,事后 review 确认 89% 确实存在质量问题(主要是信息不完整、逻辑跳跃、格式错误)。

  2. 审批点比阻断点更有价值——102 次人工审批中,用户采纳建议的比例约 73%,远高于阻断后的修改率(约 45%)。“建议改进”比”强制阻断”用户体验更好

  3. 预算管理是隐形英雄——预算耗尽后的启发式降级机制,避免了长会话中的成本失控,虽然质量略有下降,但保证了系统不会因评审开销而卡死。

最终结论:QGS 值得装,但要配合合理的阈值配置和降级机制。我的建议是:

  • 初始阶段:宽松阈值(score ≥ 50 通过),积累数据
  • 稳定阶段:收紧阈值(score ≥ 60 通过),启用审批点
  • 成熟阶段:按任务类型差异化配置,启用预算管理

QGS 不是一劳永逸的解决方案,而是一个需要持续调优的系统——就像开车,刹车不是装上去就完事了,还得学会什么时候踩、踩多深。


相关链接

生成时间:2026-08-15 10:00