<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-CN"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://hugfeature.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://hugfeature.github.io/" rel="alternate" type="text/html" hreflang="zh-CN" /><updated>2026-10-02T21:13:57+08:00</updated><id>https://hugfeature.github.io/feed.xml</id><title type="html">runtime质量论</title><subtitle>研究 AI 系统从生成、执行、验证到交付过程中的可靠性问题，记录 Agent Harness、AI Eval、Trace、Failure Regression 与 Quality Gate 的工程实践。</subtitle><author><name>hugfeature</name></author><entry><title type="html">测试用例 Agent 的门禁落地：一条需求缺口，应该挡住哪些用例？</title><link href="https://hugfeature.github.io/eval/test-case-gate-dependency-scope/" rel="alternate" type="text/html" title="测试用例 Agent 的门禁落地：一条需求缺口，应该挡住哪些用例？" /><published>2026-10-02T21:09:00+08:00</published><updated>2026-10-02T21:09:00+08:00</updated><id>https://hugfeature.github.io/eval/test-case-gate-dependency-scope</id><content type="html" xml:base="https://hugfeature.github.io/eval/test-case-gate-dependency-scope/"><![CDATA[<blockquote>
  <p><strong>AI Eval 系列 #12</strong><br />
上一篇：<a href="/eval/test-case-gates-with-incomplete-requirements/">需求不完整时，测试用例生成的 Gate 应该怎么判？</a></p>
</blockquote>

<p>测试用例生成遇到一条未确认的业务规则，应该停止到哪里？</p>

<p>停止全部生成，会让与缺口无关的内容也无法交付；全部放行，又可能把猜测变成测试依据。要处理这个问题，需要知道：<strong>缺口影响什么断言，哪些用例依赖这条规则，以及当前准备执行什么动作。</strong></p>

<p>上一篇讨论了草稿生成与正式交付的区别。这篇把问题缩小到一条需求缺口，给出一种可从表格开始实施的处理方式。以下业务规则、编号和产物都是合成示例；字段与判定逻辑是设计建议，不代表已经验证的项目实现。</p>

<h2 id="一从一个不完整的提交申请需求开始">一、从一个不完整的“提交申请”需求开始</h2>

<p>假设当前读取到的需求包含三条明确规则：</p>

<table>
  <thead>
    <tr>
      <th>编号</th>
      <th>已明确的规则</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>R-01</td>
      <td>申请名称为必填项</td>
    </tr>
    <tr>
      <td>R-02</td>
      <td>申请名称最多 50 个字符；测试数据使用普通汉字，避免引入字符计数歧义</td>
    </tr>
    <tr>
      <td>R-03</td>
      <td>合法申请提交成功后，生成一条申请记录，状态为“待审核”</td>
    </tr>
  </tbody>
</table>

<p>文档没有明确说明：用户连续点击两次提交，系统应该怎样处理。</p>

<p>由此可以提出一个待确认问题 G-01：“重复提交是拒绝、合并，还是允许形成多条记录？”</p>

<p>不能因为存在 G-01，就认定名称必填检查没有意义；也不能直接写出“提示请勿重复提交，且只生成一条记录”。后一种写法已经替业务方决定了处理方式和提示文案。</p>

<p>这里还有一个前提：单次提交规则与重复提交缺口是否确实能够分开。如果提交机制本身尚未明确，或者无法区分一次业务提交与多次请求，单次提交场景也可能受影响。<strong>按局部放行之前，必须先确认依赖范围。</strong></p>

<h2 id="二先把缺口关联到断言再关联到用例">二、先把缺口关联到断言，再关联到用例</h2>

<p>用例通常包含多个断言。用例引用了哪一段需求，并不足以解释每个预期结果的来源。</p>

<p>例如，“合法申请提交成功”可能同时检查记录数量、初始状态、提示文案和页面跳转。前三条需求只支持其中一部分；不能因为用例整体引用 R-03，就把其余预期也视为有据可查。</p>

<p>更细的追溯关系可以这样记录：</p>

<table>
  <thead>
    <tr>
      <th>用例或候选测试点</th>
      <th>预期或待确认内容</th>
      <th>依据</th>
      <th>当前处理</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>名称为空时提交</td>
      <td>必填校验阻止提交；具体文案待确认</td>
      <td>R-01</td>
      <td>保留有依据的断言</td>
    </tr>
    <tr>
      <td>名称为 50 个普通汉字时提交</td>
      <td>名称长度校验通过；其他必填字段需合法</td>
      <td>R-02</td>
      <td>形成可评审草稿</td>
    </tr>
    <tr>
      <td>单次提交合法申请</td>
      <td>生成一条记录，状态为“待审核”</td>
      <td>R-03</td>
      <td>确认不依赖重复提交规则后形成草稿</td>
    </tr>
    <tr>
      <td>连续点击两次提交</td>
      <td>第二次提交行为与最终记录数量未确认</td>
      <td>G-01</td>
      <td>保留候选测试点，等待规则确认</td>
    </tr>
  </tbody>
</table>

<p>这张表不意味着前三项已经获准执行。它只说明已知部分能够形成草稿，后续仍需检查前置条件、操作步骤和人工评审状态。</p>

<p>实际落地时，可以先在现有用例表中增加“规则依据”和“待确认项”两列。只有当缺口经常跨用例传播、人工维护关系容易漏掉时，再考虑自动维护依赖关系。</p>

<h2 id="三业务规则没写和-agent-没读到处理方式不同">三、业务规则没写，和 Agent 没读到，处理方式不同</h2>

<p>发现重复提交规则缺失时，首先需要核对读取证据。</p>

<p>如果只读了功能描述，没有读取提交接口或异常处理部分，更准确的记录是“当前读取范围未找到相关规则”。这时应执行一次有目标的补查，或者说明尚未检查的范围。</p>

<p>如果已经检查了约定范围及相关引用，仍然没有得到规则，再把问题交给需求评审。即使如此，也应保留检查范围，避免把“在本次范围内未找到”扩写成“整个项目没有定义”。</p>

<p>一个缺口记录可以是：</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"gap_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"G-01"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"question"</span><span class="p">:</span><span class="w"> </span><span class="s2">"连续点击两次提交时，第二次提交如何处理？"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"checked_scope"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="s2">"spec-v1:提交功能"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"spec-v1:异常处理"</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"pending_confirmation"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"affected_test_points"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"TP-04"</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>这里的章节名称是假设已经检查过的范围。真实运行必须填入实际读取位置，不能让生成器为了满足格式而补齐读取证据。</p>

<p>重试也应根据问题类型安排。工具读取失败，可以在预算内重试；规则未确认，需要新增证据或人的决定。反复生成一份更像完整需求的文本，并不会解决这个缺口。</p>

<h2 id="四gate-应判断具体动作">四、Gate 应判断具体动作</h2>

<p>同一个候选测试点，在不同出口的处理可以不同。</p>

<table>
  <thead>
    <tr>
      <th>准备执行的动作</th>
      <th>TP-04 尚有关联缺口时的处理</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>输出测试分析草稿</td>
      <td>允许保留测试意图、缺口编号与问题</td>
    </tr>
    <tr>
      <td>输出确定预期的可执行用例</td>
      <td>阻止受缺口影响的部分</td>
    </tr>
    <tr>
      <td>输出覆盖报告</td>
      <td>展示为待确认项，不能计入已完成验证的范围</td>
    </tr>
    <tr>
      <td>导入用例管理平台</td>
      <td>检查平台是否保留待评审状态、缺口及来源；无法保留时限制导入</td>
    </tr>
    <tr>
      <td>标记为评审通过</td>
      <td>要求缺口处理完成，相关用例完成适用的复检与评审</td>
    </tr>
  </tbody>
</table>

<p>“允许输出”与“允许执行”应分别判定。一个候选测试点能够帮助评审讨论，却未必有足够信息判断产品对错。</p>

<p>如果现有系统只有一个“通过/失败”字段，也不必立即重建整条链路。先让报告明确当前动作和未完成条件，再逐步拆开出口判定。</p>

<h2 id="五局部放行最容易错在共享前提">五、局部放行最容易错在共享前提</h2>

<p>一条缺口看起来只属于一个场景，实际可能影响许多用例。</p>

<p>例如，未确定的如果是“谁有权提交申请”，名称校验、单次提交和重复提交都可能依赖同一权限前提。此时只阻塞重复提交用例，会遗漏其他场景的不确定性。</p>

<p>局部放行前，可以检查三个问题：</p>

<ol>
  <li>缺失规则是否会改变这条用例的预期结果？</li>
  <li>缺失规则是否会改变前置条件、测试数据或可执行路径？</li>
  <li>是否还有其他用例使用了同一个未确认前提？</li>
</ol>

<p>只要其中一项无法回答，就应先把相关范围标为依赖待核对。不能仅凭“用例没有引用 G-01”来证明它不受影响：引用关系也可能漏建。</p>

<p>这也是这种方案的局限。它依赖需求拆分与关系识别的质量，单靠状态字段无法保证隔离准确。初期值得优先人工核对共享前提，尤其是权限、状态流转和跨模块规则。</p>

<h2 id="六规则确认后要复检受影响的产物">六、规则确认后，要复检受影响的产物</h2>

<p>假设评审补充了规则：“同一申请提交处理中，再次提交不得创建新记录。”</p>

<p>这可以解决部分预期，但仍不能推出确切提示文案，也不能直接扩展成“提交完成后永远不能再次提交”。新规则的适用时间和对象边界仍需保留。</p>

<p>处理顺序可以是：</p>

<ol>
  <li>记录新规则的确认来源及版本。</li>
  <li>找出依赖 G-01 的候选测试点和已有用例。</li>
  <li>根据确认规则补齐步骤、数据和有依据的预期。</li>
  <li>检查前置条件及断言，再完成适用的人工评审。</li>
</ol>

<p>缺口已经确认，只说明输入获得了补充。新生成的用例可能仍有步骤不完整、范围扩大或断言无来源的问题，因此不能自动继承“评审通过”。</p>

<p>如果后续需求变更影响 R-03，同样需要找出关联用例重新检查。初期可以人工筛选；关键是让关系可查询、可复核。</p>

<h2 id="七用小样本验证门禁是否挡对了地方">七、用小样本验证门禁是否挡对了地方</h2>

<p>验证这类 Gate，不能只看总体通过率。更有用的是构造一组结果明确的对照样本。</p>

<table>
  <thead>
    <tr>
      <th>样本</th>
      <th>应观察到的行为</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>重复提交规则未确认，名称必填规则明确，且两者独立性已核对</td>
      <td>生成名称校验草稿；重复提交保留待确认状态</td>
    </tr>
    <tr>
      <td>文档已有重复提交规则，但解析器漏读</td>
      <td>暴露读取缺口，补查后重新判定</td>
    </tr>
    <tr>
      <td>生成器编造了重复提交提示文案</td>
      <td>拦截无依据的确定断言</td>
    </tr>
    <tr>
      <td>权限规则未确定，所有提交场景均依赖它</td>
      <td>限制相关场景，避免机械局部放行</td>
    </tr>
    <tr>
      <td>缺口已经确认，但用例仍包含超出规则的预期</td>
      <td>继续拦截受影响断言</td>
    </tr>
    <tr>
      <td>规则确认且用例修订完成，但尚未人工评审</td>
      <td>保持待评审状态</td>
    </tr>
  </tbody>
</table>

<p>每次调整门禁，都保留应该放行和应该阻塞的样本。否则，修复一次误拦截时，可能顺带放过原本必须阻塞的内容。</p>

<p>对“无依据断言”的检查也要区分能力边界：脚本能够核对引用是否存在、关联缺口是否关闭；判断引用是否真的支持断言，往往还需要语义核对或人工评审。结构校验通过，不能直接当作语义正确。</p>

<h2 id="八下一次运行先交付一份能复核的结果">八、下一次运行先交付一份能复核的结果</h2>

<p>如果当前链路还在卡住，下一步可以从一个小功能开始，交付四项内容：</p>

<ul>
  <li>有来源的用例草稿。</li>
  <li>影响确定预期的待确认问题。</li>
  <li>缺口与用例的对应关系，包括尚未核对的依赖范围。</li>
  <li>本次允许与阻止的动作，以及相应理由。</li>
</ul>

<p>先检查：已有依据是否被误挡，未知规则是否被补成事实，共享前提是否被漏掉。这三个问题能够具体回答之后，再扩大文档范围和自动化程度。</p>

<p>对测试用例 Agent 而言，一次有用的运行可以交付局部成果，也可以留下业务问题。交付报告需要让评审者知道哪些内容有依据、哪些还不能执行，以及下一步需要补什么。</p>

<h2 id="继续阅读">继续阅读</h2>

<ul>
  <li><a href="/eval/test-case-gates-with-incomplete-requirements/">需求不完整时，测试用例生成的 Gate 应该怎么判？</a></li>
  <li><a href="/reliability/execution-contract-drift/">Agent 会跳步骤：从测试用例生成失败到 Execution Contract Drift</a></li>
  <li><a href="/eval/how-to-build-business-eval-dataset/">怎样构造一套真正有用的业务 Eval Dataset？</a></li>
</ul>]]></content><author><name>hugfeature</name></author><category term="AI-Eval" /><category term="Quality-Gate" /><category term="Agent-Reliability" /><category term="ai-eval" /><category term="quality-gate" /><category term="agent-reliability" /><category term="failure-regression" /><summary type="html"><![CDATA[用一个提交申请的合成示例，说明如何关联需求缺口与测试用例、限制依赖范围，并在规则确认后重新校验，避免局部缺口阻塞全部生成或被悄悄写成确定预期。]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://hugfeature.github.io/assets/social-card.png" /><media:content medium="image" url="https://hugfeature.github.io/assets/social-card.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">需求不完整时，测试用例生成的 Gate 应该怎么判？</title><link href="https://hugfeature.github.io/eval/test-case-gates-with-incomplete-requirements/" rel="alternate" type="text/html" title="需求不完整时，测试用例生成的 Gate 应该怎么判？" /><published>2026-09-29T08:05:00+08:00</published><updated>2026-09-29T08:05:00+08:00</updated><id>https://hugfeature.github.io/eval/test-case-gates-with-incomplete-requirements</id><content type="html" xml:base="https://hugfeature.github.io/eval/test-case-gates-with-incomplete-requirements/"><![CDATA[<blockquote>
  <p><strong>AI Eval 系列 #11</strong><br />
上一篇：<a href="/eval/eval-ci-quality-gate/">怎么把 AI Eval 接进 CI / Quality Gate？</a></p>
</blockquote>

<p>测试用例生成链路增加校验后，会遇到一个现实问题：需求文档存在缺口，校验反复不通过，最后连供人工评审的草稿都没有生成。</p>

<p>这时需要拆开两个决定：<strong>哪些内容已经足以生成草稿，哪些内容已经足以作为正式测试依据。</strong></p>

<p>允许前者，不等于批准后者。规则明确的部分可以继续生成；影响预期结果的未知项，需要显式保留并限制相关用例的状态，不能由模型补成确定事实。</p>

<p>本文延续<a href="/reliability/execution-contract-drift/">执行契约与流程偏移</a>的讨论，保留必要校验，同时细化 Gate 所控制的动作。以下规则是设计建议，业务示例和数字均为合成示例，不代表某个项目的实施结果。</p>

<h2 id="一一个-fail-标签解释不了所有问题">一、一个 FAIL 标签解释不了所有问题</h2>

<p>用例没有生成，可能来自几种完全不同的原因。</p>

<table>
  <thead>
    <tr>
      <th>问题层次</th>
      <th>示例</th>
      <th>优先处理</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>输入缺口</td>
      <td>文档没有说明重复提交时的处理规则</td>
      <td>定位缺口，列出待确认问题</td>
    </tr>
    <tr>
      <td>执行错误</td>
      <td>文档有规则，但读取范围漏掉了对应章节</td>
      <td>修正读取或解析，保留失败证据</td>
    </tr>
    <tr>
      <td>产物质量</td>
      <td>已知规则正确，但操作步骤不足以执行</td>
      <td>修改相关用例并复检</td>
    </tr>
    <tr>
      <td>校验器问题</td>
      <td>语义等价的有效表达被模板匹配拒绝</td>
      <td>修复校验器，补正反例回归</td>
    </tr>
  </tbody>
</table>

<p>这些问题都进入同一个“失败后重新生成”循环，容易让 Agent 重读文档、重写产物，却没有补齐真正缺少的东西。</p>

<p>尤其需要注意：<strong>“没有读到”不等于“文档没有写”。</strong> 对大文档，需要记录已读取范围、查询结果和引用位置。检索尚未覆盖相关位置时，应写“当前读取范围未找到”，不能直接判定需求缺失。</p>

<h2 id="二validator-记录事实gate-决定动作">二、Validator 记录事实，Gate 决定动作</h2>

<p>Validator 的职责是说明发现了什么、影响哪里、依据是什么。Gate 根据这些事实，决定当前操作是否允许。</p>

<p>例如，校验器发现某条业务规则未确认：</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"check_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"expected-result-grounding"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"result"</span><span class="p">:</span><span class="w"> </span><span class="s2">"unknown"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"affected_requirements"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"REQ-03"</span><span class="p">],</span><span class="w">
  </span><span class="nl">"source_scope"</span><span class="p">:</span><span class="w"> </span><span class="s2">"example-spec-v1:section-6.2"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"reason"</span><span class="p">:</span><span class="w"> </span><span class="s2">"当前读取范围未找到重复提交处理规则"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>这份结果本身不意味着所有生成工作都要停止。策略可以允许生成不依赖 REQ-03 的草稿，同时阻止 REQ-03 对应的用例进入“可执行”状态。</p>

<p>这里的 <code class="language-plaintext highlighter-rouge">unknown</code> 是本文示例中的结果值，不是通用标准。它既不是通过，也不是已确认的业务错误；它表达的是当前证据不足以作出判断。</p>

<p>Gate 的粒度取决于影响范围。某个局部规则缺失，可以隔离相关条目；如果整个权限模型都未确定，多个场景可能共享这一前提，就不能按条目机械放行。依赖范围还不清楚时，应先限制相关工作，查明依赖后再继续。</p>

<h2 id="三生成草稿和正式交付使用不同条件">三、生成草稿和正式交付使用不同条件</h2>

<p>“放宽门禁”太笼统。更具体的做法是给每个出口定义条件。</p>

<table>
  <thead>
    <tr>
      <th>检查项</th>
      <th>生成可评审草稿</th>
      <th>作为正式测试依据</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>输入可读取、产物可解析</td>
      <td>必须满足</td>
      <td>必须满足</td>
    </tr>
    <tr>
      <td>用例关联到需求或明确假设</td>
      <td>必须保留关系</td>
      <td>需求与假设均需完成适用的确认</td>
    </tr>
    <tr>
      <td>影响预期结果的业务规则未知</td>
      <td>隔离相关条目，继续独立部分</td>
      <td>相关条目不得标为可执行</td>
    </tr>
    <tr>
      <td>用例名称、步骤和预期不够明确</td>
      <td>标记并待修订</td>
      <td>修订到人员能够执行与判断</td>
    </tr>
    <tr>
      <td>覆盖存在缺口</td>
      <td>说明缺口和影响范围</td>
      <td>由评审按交付范围处理</td>
    </tr>
    <tr>
      <td>人工评审尚未完成</td>
      <td>可以输出待评审版本</td>
      <td>不标记为已批准</td>
    </tr>
  </tbody>
</table>

<p>发布、执行或同步到其他系统，都应有各自明确的状态要求。平台支持“待评审草稿”并不意味着上传动作本身能保证质量；如果平台无法保留未知项、来源和评审状态，就不能用一次导入把这些差别抹平。</p>

<p>当前只需要交付草稿时，可以先完成草稿和评审清单。等正式交付条件满足后，再处理后续平台接入，避免把一次生成成功当成质量已经过关。</p>

<h2 id="四未知项不能靠合理猜测变成预期结果">四、未知项不能靠合理猜测变成预期结果</h2>

<p>假设需求只写了：“用户可以提交申请。”没有说明重复点击提交会发生什么。</p>

<p>下面两种输出，质量并不相同：</p>

<ul>
  <li><strong>直接生成确定断言：</strong> 连续点击两次提交，系统提示“请勿重复提交”，且只生成一条记录。</li>
  <li><strong>保留待确认问题：</strong> 重复提交的处理规则待确认；候选测试点包括重复记录、重复请求反馈和幂等行为，确认规则后再形成预期结果。</li>
</ul>

<p>第一种可能符合常见设计，但提示文案、记录数和处理方式都不是已给出的事实。第二种仍然推进了测试分析，同时保留了业务决策的位置。</p>

<p>可以让未知项关联来源范围、影响用例、需要回答的问题和后续处理状态。没有指定负责人时就留待分配，不由 Agent 编造一个人名。</p>

<p>人工确认规则后，还需要更新关联用例并重新验证。评审过一次文档，并不自动意味着后续新生成的全部产物也已评审。</p>

<h2 id="五覆盖报告必须展示分母和未知项">五、覆盖报告必须展示分母和未知项</h2>

<p>只对已解析条目计算覆盖率，很容易隐藏问题。</p>

<p>假设已识别 20 个需求条目，其中 15 个规则明确，5 个尚待确认。15 个明确条目都关联了用例，并不能据此声称“整体覆盖率 100%”。</p>

<p>更透明的报告可以是：</p>

<table>
  <thead>
    <tr>
      <th>指标</th>
      <th>合成示例</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>当前已识别的需求条目</td>
      <td>20</td>
    </tr>
    <tr>
      <td>规则明确的条目</td>
      <td>15</td>
    </tr>
    <tr>
      <td>规则明确且已关联用例的条目</td>
      <td>15</td>
    </tr>
    <tr>
      <td>未知或待确认条目</td>
      <td>5</td>
    </tr>
    <tr>
      <td>明确条目关联率</td>
      <td>15 / 15</td>
    </tr>
    <tr>
      <td>全部已识别条目中，规则明确且已关联用例的比例</td>
      <td>15 / 20</td>
    </tr>
  </tbody>
</table>

<p>这两个比例都只描述追溯关系，<strong>不能证明用例有效、已通过评审，也不能证明整份文档已经完整解析。</strong> 是否遗漏需求、是否覆盖关键风险、是否具备可执行步骤，需要另行检查。</p>

<p>如果需求总量尚未可靠建立，报告就说明分母仅是“当前已识别条目”。不要用一个精确百分比掩盖输入范围的不确定性。</p>

<h2 id="六门禁卡住时先拿失败样本检查规则">六、门禁卡住时，先拿失败样本检查规则</h2>

<p>发现 Gate 连续失败后，既不应直接删除所有检查，也不应默认校验器永远正确。</p>

<p>可以围绕同一检查建立四类小样本：</p>

<ol>
  <li>规则明确、用例正确：应该通过相应检查。</li>
  <li>规则明确、用例遗漏：应该被识别为产物问题。</li>
  <li>规则没有确认：应该保留未知状态，不生成确定断言。</li>
  <li>文档有规则但解析漏读：应该暴露读取缺口，不能冒充输入本身缺失。</li>
</ol>

<p>还应加入“无关条目可继续”和“共享前提缺失须阻塞相关范围”的对照，检查 Gate 是否误伤独立工作，或遗漏真实依赖。</p>

<p>Anthropic 关于 Agent Eval 的工程文章指出，模糊任务和评分器缺陷都可能造成失败，阅读执行记录有助于区分 Agent 的错误与评分器误判。对应到这里，门禁治理也需要检查输入、产物、证据和判定规则，不能只统计通过率。</p>

<p>修改规则后，应同时保留原本正确被拦截的样本和误拦截样本。让后者通过的同时，确认前者仍会被拦截，才能知道这次调整修复了什么。</p>

<h2 id="七下一次运行应该交付什么">七、下一次运行应该交付什么</h2>

<p>一次有用的生成结果，至少包含三样东西：<strong>可评审的用例草稿、能够追溯到需求的关系、尚未解决的问题及影响范围。</strong></p>

<p>运行报告再说明哪些范围已读取、哪些检查已执行、哪些条目可继续、哪些需要人工决定。遇到阻塞时，允许保留局部成果，但不能把局部完成改写成整项完成。</p>

<p>这样，必要的执行契约仍然有效：Agent 无权自行绕过失败检查；系统则可以依据事先明确的策略，允许独立部分推进。下一步该修读取、修用例、修校验器，还是请人确认业务规则，也就有了可核对的依据。</p>

<h2 id="参考与继续阅读">参考与继续阅读</h2>

<ul>
  <li><a href="https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents">Anthropic：Demystifying evals for AI agents</a>：任务歧义、评分器校准与执行记录检查。</li>
  <li><a href="/reliability/execution-contract-drift/">Agent 会跳步骤：从测试用例生成失败到 Execution Contract Drift</a>：为什么必要阶段不能只靠 Prompt 约束。</li>
  <li><a href="/eval/how-to-build-business-eval-dataset/">怎样构造一套真正有用的业务 Eval Dataset？</a>：把正常、边界和失败样本纳入评测。</li>
  <li><a href="/harness/bounded-autonomy-for-agents/">让 Agent 下班后继续跑：有界自主运行怎么设计？</a>：任务遇到未知项后如何停止、保存和恢复。</li>
</ul>]]></content><author><name>hugfeature</name></author><category term="AI-Eval" /><category term="Quality-Gate" /><category term="Agent-Reliability" /><category term="ai-eval" /><category term="quality-gate" /><category term="agent-reliability" /><category term="failure-regression" /><summary type="html"><![CDATA[测试用例生成被 Gate 卡住，未必都是模型能力问题。区分输入缺口、执行错误与用例质量，分开管理草稿生成和正式交付，并把未知项纳入覆盖报告。]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://hugfeature.github.io/assets/social-card.png" /><media:content medium="image" url="https://hugfeature.github.io/assets/social-card.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">让 Agent 下班后继续跑：有界自主运行怎么设计？</title><link href="https://hugfeature.github.io/harness/bounded-autonomy-for-agents/" rel="alternate" type="text/html" title="让 Agent 下班后继续跑：有界自主运行怎么设计？" /><published>2026-09-29T08:00:00+08:00</published><updated>2026-09-29T08:00:00+08:00</updated><id>https://hugfeature.github.io/harness/bounded-autonomy-for-agents</id><content type="html" xml:base="https://hugfeature.github.io/harness/bounded-autonomy-for-agents/"><![CDATA[<blockquote>
  <p><strong>Agent Harness 系列 #16</strong><br />
上一篇：<a href="/harness/harness-to-quality-gate/">Agent Harness 的终点：从执行引擎走向 Quality Gate</a></p>
</blockquote>

<p>让 Agent 在人离开电脑后继续工作，一个很实际的目标是：在已经确定的范围内，能完成多少就完成多少，回来后能接着处理。</p>

<p>最担心的通常有两件事：它停在某个地方，再也没有进展；或者一直运行，反复尝试同一个问题，持续消耗 Token。</p>

<p>这两个风险指向同一个要求：<strong>系统需要分清“正在运行”和“正在推进”，并且能在推进不了时留下可接手的状态。</strong></p>

<p>本文把这种方式称为“有界自主运行”。这是一个工程设计用语，不是某个框架的标准名称。下面给出的是设计建议和验证方法，不代表这些控制点已经在某个项目中全部实现，也没有据此宣称效率提升。</p>

<h2 id="一先把能跑多少是多少变成有限任务">一、先把“能跑多少是多少”变成有限任务</h2>

<p>“持续优化这个项目”很难收敛。每改一处，Agent 都可能发现新的可优化项。</p>

<p>适合无人值守的任务需要四个要素：允许处理的工作清单、每项的完成条件、允许使用的工具和资源，以及本轮预算。发现新问题时，可以记录到候选清单，但不能自动把它变成本轮必须完成的目标。</p>

<p>例如，给一个文档处理 Agent 的任务可以是：解析已确认范围内的需求，为规则明确的部分生成用例草稿，记录无法确定的预期结果。文档里的业务空白需要留待确认，不能靠补写规则来完成任务。</p>

<p>还要确认依赖关系。一个子任务被阻塞后，只有不依赖它、且仍在授权范围内的其他任务可以继续；不能跳过必要前置条件，假装整条链路仍然有效。</p>

<h2 id="二活动进展完成是三个信号">二、活动、进展、完成是三个信号</h2>

<p>持续输出文字能证明模型还在响应，但不足以证明任务正在接近完成。</p>

<table>
  <thead>
    <tr>
      <th>信号</th>
      <th>可以说明什么</th>
      <th>不能说明什么</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>心跳、日志、工具调用</td>
      <td>进程或调用仍有活动</td>
      <td>工作取得了有效进展</td>
    </tr>
    <tr>
      <td>新产物及其检查结果</td>
      <td>某个任务状态发生了可解释的变化</td>
      <td>整个任务已完成</td>
    </tr>
    <tr>
      <td>验收证据</td>
      <td>指定范围满足了完成条件</td>
      <td>范围之外也已验证</td>
    </tr>
  </tbody>
</table>

<p>“进展”也不能简单等于文件变了：反复改措辞、刷新时间戳、生成相同报告，都能制造变化。</p>

<p>更合适的进展事件是：一个需求条目获得了来源定位；一个失败被缩小到可复现输入；一个原本失败的检查在相同条件下通过。诊断阶段也可能产生进展，例如排除了一条假设，但需要相应证据，不能只写“分析更深入了”。</p>

<p>因此，监督逻辑可以分别记录 <code class="language-plaintext highlighter-rouge">last_activity_at</code> 和 <code class="language-plaintext highlighter-rouge">last_progress_at</code>。前者用于判断是否失去响应，后者用于识别长时间没有有效推进。长耗时工具还需要自己的截止时间，避免被简单的“无新文件”规则误判。</p>

<h2 id="三预算必须由能停止执行的一层控制">三、预算必须由能停止执行的一层控制</h2>

<p>在 Prompt 里写“最多尝试两次”有帮助，但它依赖 Agent 自己遵守。真正的运行上限，需要放在宿主、执行器或能够终止任务的监督进程中。</p>

<p>下面只是一个配置示意，数值用于解释结构，不能直接视为适合所有任务的默认值：</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">run_budget</span><span class="pi">:</span>
  <span class="na">wall_time_seconds</span><span class="pi">:</span> <span class="m">3600</span>
  <span class="na">max_tool_calls</span><span class="pi">:</span> <span class="m">120</span>
  <span class="na">max_retries_per_failure</span><span class="pi">:</span> <span class="m">2</span>
  <span class="na">max_no_progress_cycles</span><span class="pi">:</span> <span class="m">3</span>
  <span class="na">max_resume_attempts</span><span class="pi">:</span> <span class="m">1</span>
</code></pre></div></div>

<p>这里的 retry 指首次失败后的额外尝试。达到任何硬上限后，都不再启动新工作；已有调用要按工具能力取消或等待到它自己的截止时间，并记录是否仍有操作在进行。</p>

<p>如果宿主可以获取可靠的 Token 或费用统计，可以再加入对应上限；获取不到时，就明确记录未知，并用时间和调用次数限制运行。不能把无法读取的费用写成零。</p>

<p>恢复任务也不能刷新预算。监督进程需要持久化累计消耗，并让新会话读取剩余额度。否则“到上限就重启”只是另一种无限循环。</p>

<h2 id="四重试要看失败有没有变化">四、重试要看失败有没有变化</h2>

<p>单纯比较工具名称，会把正常的批量读取误判为重复；只比较错误文字，又可能漏掉换一种说法的同一问题。</p>

<p>可以结合任务项、输入版本、工具、失败类型和相关状态，生成一个失败标识。这个标识用于聚合类似失败，并不是证明根因相同的绝对依据。</p>

<table>
  <thead>
    <tr>
      <th>现场情况</th>
      <th>建议处理</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>只读查询遇到短暂网络错误</td>
      <td>在截止时间和重试预算内退避重试</td>
    </tr>
    <tr>
      <td>输入或依赖已经改变</td>
      <td>记录变化依据，再决定是否尝试</td>
    </tr>
    <tr>
      <td>同样输入、同样失败、没有新证据</td>
      <td>结束本项，保留阻塞原因</td>
    </tr>
    <tr>
      <td>写入超时，结果未知</td>
      <td>先查询目标状态，不能直接重复写入</td>
    </tr>
    <tr>
      <td>权限或审批阻塞</td>
      <td>等待或停止，不通过换工具绕过</td>
    </tr>
  </tbody>
</table>

<p>“换个模型再试”仍然消耗同一任务的预算。它可以是预先允许的策略，但不能让失败计数归零，也不能改变授权边界。</p>

<h2 id="五停止时留下的状态比结束语更有用">五、停止时留下的状态，比结束语更有用</h2>

<p>无人值守任务的合理结果，不只有成功和失败。</p>

<p>可以将执行状态和结果验收分开：执行可能因完成、预算耗尽、无进展、等待输入、运行错误而结束；验收则说明产物已验证、未验证或未通过。这样，部分产物存在就不会被误写成整项成功。</p>

<p>例如，下列记录表示任务受阻，但已经留下两项可检查的工作：</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"run_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"example-run-017"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"stop_reason"</span><span class="p">:</span><span class="w"> </span><span class="s2">"blocked_on_input"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"verification"</span><span class="p">:</span><span class="w"> </span><span class="s2">"unverified"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"completed_items"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"REQ-01"</span><span class="p">,</span><span class="w"> </span><span class="s2">"REQ-02"</span><span class="p">],</span><span class="w">
  </span><span class="nl">"blocked_items"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"REQ-03"</span><span class="p">],</span><span class="w">
  </span><span class="nl">"artifacts"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"cases-draft.json"</span><span class="p">,</span><span class="w"> </span><span class="s2">"validation.json"</span><span class="p">],</span><span class="w">
  </span><span class="nl">"next_action"</span><span class="p">:</span><span class="w"> </span><span class="s2">"确认 REQ-03 的重复提交规则后再生成预期结果"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>这是合成示例，不是真实运行数据。实际保存时，还应关联输入版本、产物版本、累计预算和外部操作状态。</p>

<p>写检查点时，应先保存产物和验证结果，再更新引用它们的状态；不能先宣布检查点完成，随后才写文件。停止时如果无法落盘，监督层要报告保存失败，并指出最后一个可用检查点。</p>

<h2 id="六恢复前重新确认事实">六、恢复前重新确认事实</h2>

<p>检查点只是上次运行留下的记录。它不能证明当前环境、权限和输入仍然相同。</p>

<p>恢复时至少重新检查这些条件：</p>

<ul>
  <li><strong>输入一致性：</strong> 文档、任务范围或代码版本是否变化？变化影响哪些产物？</li>
  <li><strong>产物完整性：</strong> 引用的文件是否存在，版本或哈希是否匹配，验证是否针对该版本？</li>
  <li><strong>执行现场：</strong> 上次工具是否还在运行，有没有结果未知的写入？</li>
  <li><strong>权限与预算：</strong> 原授权是否仍适用，累计消耗后还剩多少额度？</li>
</ul>

<p>如果旧进程状态不明，不能立即启动另一个进程做同一项写入。需要先确定旧任务已停止或被有效隔离，避免两个执行者同时修改目标。</p>

<p>Anthropic 在长任务 Harness 的工程文章中讨论了增量推进、进展记录与跨会话接续。这支持“把状态留在会话之外”的方向；本文进一步列出的预算、停止分类和恢复检查，是围绕无人值守风险提出的设计建议，并非该文定义的统一协议。</p>

<h2 id="七先验证三个故障再延长运行时间">七、先验证三个故障，再延长运行时间</h2>

<p>运行时长本身不能证明自主运行可靠。可以先做三个小规模故障演练：</p>

<table>
  <thead>
    <tr>
      <th>注入的故障</th>
      <th>需要观察到的结果</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>工具持续返回同一可重试错误</td>
      <td>重试和累计预算生效，停止后留下错误证据</td>
    </tr>
    <tr>
      <td>进程有心跳，但检查结果没有有效变化</td>
      <td>无进展策略触发，且没有被空文件或重复报告重置</td>
    </tr>
    <tr>
      <td>保存检查点后中断，再修改输入</td>
      <td>恢复识别输入变化，对受影响产物重新验证</td>
    </tr>
  </tbody>
</table>

<p>这些验证不是为了追求“永远不停”。它们要确认：能推进时继续，无法推进时有明确结论，中断后有事实可查。</p>

<p>如果宿主只支持聊天和工具调用，还没有外部监督能力，应如实把当前方案称为“带运行约束的任务指令”。等到停止、预算和恢复确实由执行层落实，再把它作为有界自主运行能力交付。</p>

<h2 id="参考与继续阅读">参考与继续阅读</h2>

<ul>
  <li><a href="https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents">Anthropic：Effective harnesses for long-running agents</a>：增量工作、进展记录与跨会话接续。</li>
  <li><a href="/harness/tool-failure-timeout-retry-budget-side-effect/">Agent 调错工具怎么办：Timeout、Retry、Budget 与 Side Effect</a>：单次工具调用的失败与副作用控制。</li>
  <li><a href="/eval/test-case-gates-with-incomplete-requirements/">需求不完整时，测试用例生成的 Gate 应该怎么判？</a>：把无人值守中遇到的输入缺口转成可评审状态。</li>
</ul>]]></content><author><name>hugfeature</name></author><category term="Agent-Harness" /><category term="Agent-Reliability" /><category term="agent-harness" /><category term="agent-reliability" /><category term="trace-evidence" /><category term="failure-regression" /><summary type="html"><![CDATA[无人值守 Agent 如何避免卡住、反复重试和消耗 Token？从任务边界、进展证据、预算、停止原因与恢复检查，设计可接手的有界自主运行。]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://hugfeature.github.io/assets/social-card.png" /><media:content medium="image" url="https://hugfeature.github.io/assets/social-card.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Agent 会跳步骤：从测试用例生成失败到 Execution Contract Drift</title><link href="https://hugfeature.github.io/reliability/execution-contract-drift/" rel="alternate" type="text/html" title="Agent 会跳步骤：从测试用例生成失败到 Execution Contract Drift" /><published>2026-09-24T18:40:00+08:00</published><updated>2026-09-24T21:13:00+08:00</updated><id>https://hugfeature.github.io/reliability/execution-contract-drift</id><content type="html" xml:base="https://hugfeature.github.io/reliability/execution-contract-drift/"><![CDATA[<p>最近在重构一个测试用例生成 Agent 时，我遇到一个很典型的问题：</p>

<p><strong>流程已经写得很清楚，Agent 还是会跳步骤。</strong></p>

<p>一开始我把它当成测试用例生成质量问题：生成得太浅、覆盖不完整、步骤描述不够直接。</p>

<p>继续往下拆以后才发现，真正的问题不在最后那份测试用例，而在前面的执行过程。</p>

<p>本文来自一次真实工程重构，但已经抽象掉具体业务、公司、工具和项目细节。留下来的，是一个更通用的问题：</p>

<p><strong>当 Agent 的最终结果看起来“差不多正确”，但它没有按照预期过程执行时，我们应该怎么判断它是否可靠？</strong></p>

<p>我的结论是：</p>

<blockquote>
  <p>Prompt 可以描述期望，Harness 必须约束执行，Trace 负责记录事实，而 Drift 检测负责判断事实和期望之间发生了什么偏离。</p>
</blockquote>

<p>这类问题，我暂时把它称为 <strong>Execution Contract Drift</strong>。</p>

<h2 id="一最开始我们以为这是生成质量问题">一、最开始，我们以为这是“生成质量”问题</h2>

<p>一个测试用例生成 Agent，最直观的流程是：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PRD
  ↓
理解需求
  ↓
生成测试用例
</code></pre></div></div>

<p>如果结果不好，很自然会想到继续优化：</p>

<ul>
  <li>Prompt；</li>
  <li>测试用例规范；</li>
  <li>Few-shot 示例；</li>
  <li>Skill 规则；</li>
  <li>输出模板。</li>
</ul>

<p>这些手段都有效，但它们有一个共同前提：</p>

<p><strong>Agent 确实执行了我们以为它执行的过程。</strong></p>

<p>问题就在这里。</p>

<p>测试用例写得浅，有时不是“不会写测试用例”，而是它根本没有完整理解需求。</p>

<p>覆盖不全，也可能不是“覆盖策略不够好”，而是需求里的关键规则根本没有进入它后面的上下文。</p>

<p>于是流程需要前移。</p>

<h2 id="二第一步把理解需求从隐式推理变成显式产物">二、第一步：把“理解需求”从隐式推理变成显式产物</h2>

<p>比起让 Agent 在上下文里“自己理解一下”，更可靠的做法是让它先产出结构化需求模型。</p>

<p>例如：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PRD
  ↓
requirements.json
  ↓
coverage-plan.json
  ↓
test cases
</code></pre></div></div>

<p>这里真正重要的并不是 JSON。</p>

<p>重要的是：<strong>原本藏在模型内部的推理过程，开始留下可以检查的中间状态。</strong></p>

<p>于是我们第一次可以问：</p>

<ul>
  <li>需求是否被完整解析？</li>
  <li>哪些业务规则进入了测试范围？</li>
  <li>哪些测试点来自哪些需求？</li>
  <li>最终用例是否真的覆盖了 coverage plan？</li>
</ul>

<p>这时测试用例生成就不再是一次黑盒调用，而变成一条可观察的数据链：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Requirement
    ↓
Test Point
    ↓
Test Case
</code></pre></div></div>

<p>如果中间状态错了，就不需要等最终用例出来以后再猜原因。</p>

<h2 id="三第二步结构化中间产物仍然不够">三、第二步：结构化中间产物仍然不够</h2>

<p>很快又会遇到另一个问题。</p>

<p>即使你明确要求：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PRD
  ↓
requirements.json
  ↓
coverage-plan.json
  ↓
cases
</code></pre></div></div>

<p>Agent 仍然可能实际执行成：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PRD
  ↓
cases
</code></pre></div></div>

<p>或者：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PRD
  ↓
requirements.json
  ↓
cases
</code></pre></div></div>

<p>它可能“知道”应该先生成 coverage plan，但为了更快完成任务，直接跳过。</p>

<p>也可能生成了一个文件，却没有真正使用。</p>

<p>甚至可能 Validator 已经报告失败，它仍然继续往下执行，并最终告诉你：</p>

<blockquote>
  <p>任务已完成。</p>
</blockquote>

<p>这暴露出一个非常重要的边界：</p>

<p><strong>流程写在 Prompt 里，不等于流程被系统执行了。</strong></p>

<p>Prompt 本质上是在告诉模型：</p>

<blockquote>
  <p>你应该怎么做。</p>
</blockquote>

<p>但可靠性真正需要的是：</p>

<blockquote>
  <p>你不满足条件时，系统不允许你继续做。</p>
</blockquote>

<p>这就是 Harness 应该接管的部分。</p>

<h2 id="四把流程升级为-execution-contract">四、把流程升级为 Execution Contract</h2>

<p>对于这类任务，我现在更愿意把预期流程看成一份 <strong>Execution Contract</strong>。</p>

<p>这里的 Execution Contract 不是某个特定框架的正式标准，而是一个工程抽象：</p>

<p><strong>它描述一个 Agent 在完成任务时必须经过哪些状态、产出哪些证据、满足哪些条件，才能进入下一阶段。</strong></p>

<p>例如：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PRD
  ↓
Requirement Interpreter
  ↓
requirements.json
  ↓
[Gate 1: requirement validator]
  ↓
Coverage Planner
  ↓
coverage-plan.json
  ↓
[Gate 2: coverage validator]
  ↓
Case Generator
  ↓
cases
  ↓
[Gate 3: case validator]
  ↓
Done
</code></pre></div></div>

<p>这条链路里至少有四种东西：</p>

<table>
  <thead>
    <tr>
      <th>类型</th>
      <th>作用</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>State</td>
      <td>当前执行到了哪里</td>
    </tr>
    <tr>
      <td>Artifact</td>
      <td>当前阶段生成了什么</td>
    </tr>
    <tr>
      <td>Validator</td>
      <td>产物是否满足约束</td>
    </tr>
    <tr>
      <td>Gate</td>
      <td>是否允许进入下一阶段</td>
    </tr>
  </tbody>
</table>

<p>真正关键的是 Gate。</p>

<p>如果 Requirement Validator 没通过：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>validator = FAIL
      ↓
不能进入 Coverage Planner
</code></pre></div></div>

<p>而不是：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>validator = FAIL
      ↓
Agent：问题不大，我继续生成吧
</code></pre></div></div>

<p><strong>能用 Harness 强制的规则，不应该只依赖模型记住。</strong></p>

<h2 id="五这时出现了一类以前很难描述的失败">五、这时出现了一类以前很难描述的失败</h2>

<p>假设最后生成的测试用例看起来还不错。</p>

<p>传统验证可能会判断：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Final Result = PASS
</code></pre></div></div>

<p>但 Trace 告诉我们，它实际走的是：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PRD
  ↓
requirements.json
  ↓
cases
</code></pre></div></div>

<p>预期轨迹却是：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PRD
  ↓
requirements.json
  ↓
Gate 1
  ↓
coverage-plan.json
  ↓
Gate 2
  ↓
cases
  ↓
Gate 3
</code></pre></div></div>

<p>这时问题已经不只是“结果对不对”。</p>

<p>而是：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Expected Trajectory
        ≠
Actual Trajectory
</code></pre></div></div>

<p>这就是我觉得非常值得关注的一类 Drift。</p>

<h2 id="六execution-contract-drift-可以有哪些形式">六、Execution Contract Drift 可以有哪些形式</h2>

<p>如果把 Contract 和 Trace 放在一起比较，会出现一些很具体的失败模式。</p>

<h3 id="1-stage-skipping">1. Stage Skipping</h3>

<p>预期：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>A → B → C → D
</code></pre></div></div>

<p>实际：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>A → B → D
</code></pre></div></div>

<p>Agent 跳过了必需阶段。</p>

<h3 id="2-gate-bypass">2. Gate Bypass</h3>

<p>Validator 已经失败，但执行仍然继续：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Validator = FAIL
      ↓
Next Stage Executed
</code></pre></div></div>

<p>这类问题尤其危险，因为最终结果可能仍然“看起来正常”。</p>

<h3 id="3-silent-state-mutation">3. Silent State Mutation</h3>

<p>前一阶段已经确认的需求，在后续阶段被模型静默改写。</p>

<p>例如：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Requirement v1
      ↓
Coverage Planning
      ↓
Requirement v2
</code></pre></div></div>

<p>但没有任何显式 change event。</p>

<h3 id="4-evidence-gap">4. Evidence Gap</h3>

<p>Agent 声称某个阶段已经完成，却找不到对应证据：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"coverage analysis completed"
          ↓
coverage-plan.json missing
</code></pre></div></div>

<h3 id="5-recovery-violation">5. Recovery Violation</h3>

<p>执行失败以后，本来应该：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>FAIL → Retry / Stop / Human Review
</code></pre></div></div>

<p>实际却变成：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>FAIL → Ignore → Continue
</code></pre></div></div>

<h3 id="6-output-contract-mismatch">6. Output-Contract Mismatch</h3>

<p>最终产物存在，但无法建立：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Requirement → Test Point → Test Case
</code></pre></div></div>

<p>的追溯关系。</p>

<p>这些失败的共同点是：</p>

<p><strong>它们未必让最终结果立即失败，但说明执行过程已经失去控制。</strong></p>

<h2 id="七为什么-trace-在这里不再只是日志">七、为什么 Trace 在这里不再只是“日志”</h2>

<p>很多系统已经有 Trace。</p>

<p>但如果 Trace 只是方便排查：</p>

<blockquote>
  <p>Agent 刚才调用了什么 Tool？</p>
</blockquote>

<p>它的价值还没有完全发挥出来。</p>

<p>当系统已经存在 Execution Contract 时，Trace 可以变成真正的验证输入：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Execution Contract
        +
Actual Trace
        ↓
Contract Validator
        ↓
Violation
</code></pre></div></div>

<p>于是 Trace 不只是“发生了什么”的记录，而变成：</p>

<p><strong>证明 Agent 是否按照可靠执行路径完成任务的 Evidence。</strong></p>

<p>这时我们甚至可以产生结构化结果：</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"expected_stage"</span><span class="p">:</span><span class="w"> </span><span class="s2">"coverage_validation"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"actual_stage"</span><span class="p">:</span><span class="w"> </span><span class="s2">"case_generation"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"violation"</span><span class="p">:</span><span class="w"> </span><span class="s2">"gate_bypass"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"evidence"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"trace_event_12"</span><span class="p">,</span><span class="w"> </span><span class="s2">"trace_event_13"</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>这比一句“Agent 没按 Prompt 执行”有用得多。</p>

<p>因为它可检测、可聚合、可回归。</p>

<h2 id="八这对-drift-项目意味着什么">八、这对 Drift 项目意味着什么</h2>

<p>我之前做 Drift 时，更关注的是 <strong>Goal Drift</strong>：</p>

<p>Agent 在长时间执行过程中，是否逐渐偏离了最初目标、约束或任务状态。</p>

<p>例如：</p>

<ul>
  <li>constraint relaxation；</li>
  <li>false environment assumption；</li>
  <li>rabbit hole；</li>
  <li>reasoning loop；</li>
  <li>state desync。</li>
</ul>

<p>这次测试用例生成场景让我意识到，还有另外一条很具体的方向：</p>

<p><strong>Execution Contract Drift。</strong></p>

<p>两者关注的问题不同。</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Goal Drift
  ↓
Agent 还在做正确的事情吗？

Execution Contract Drift
  ↓
Agent 还在按照允许的方式做这件事吗？
</code></pre></div></div>

<p>一个 Agent 完全可能：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Goal = 正确
Result = 看起来正确
Execution = 已经违约
</code></pre></div></div>

<p>例如它最终确实生成了一份不错的测试用例，但跳过了必须执行的需求验证和覆盖验证。</p>

<p>从结果视角看，它成功了。</p>

<p>从 Reliability 视角看，这次 Run 应该至少被标记为：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>SUCCESS_WITH_CONTRACT_VIOLATION
</code></pre></div></div>

<p>这类样本对 Drift 很有价值。</p>

<p>因为检测对象不再完全依赖模糊的“模型是否跑偏”，而是可以直接比较：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Contract → Trace → Violation
</code></pre></div></div>

<h2 id="九真实系统比人为构造的-fixture-更重要">九、真实系统比人为构造的 Fixture 更重要</h2>

<p>这里还有一个我觉得更值得长期做的事情。</p>

<p>不要为了 Drift 专门制造大量漂亮的 Demo。</p>

<p>真正有价值的 Failure Corpus，应该来自真实 Agent 系统里的失败。</p>

<p>例如：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>真实任务
  ↓
Harness 执行
  ↓
Trace
  ↓
Contract Violation
  ↓
Root Cause
  ↓
Fix
  ↓
Regression
</code></pre></div></div>

<p>一条真实的 Gate Bypass，就可以沉淀成一个 Regression Fixture。</p>

<p>下一次 Harness、Prompt、Skill 或 Model 发生变化以后，重新跑：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>同一 Contract
    +
同一 Failure Fixture
    ↓
是否再次违反？
</code></pre></div></div>

<p>于是一次线上或试点问题，不再只是一个 Bug。</p>

<p>它开始成为可靠性资产。</p>

<h2 id="十最终我想把这条链路做成什么">十、最终，我想把这条链路做成什么</h2>

<p>现在我更认可这样一条工程链路：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Intent / Requirement
        ↓
Execution Contract
        ↓
Agent
        ↓
Harness
        ↓
Trace
        ↓
Contract Validation
        ↓
Violation / Evidence
        ↓
Failure Corpus
        ↓
Regression
        ↓
Quality Gate
</code></pre></div></div>

<p>这里每一层解决的问题都不一样。</p>

<p><strong>Prompt 定义行为倾向。</strong></p>

<p><strong>Execution Contract 定义必须满足的执行约束。</strong></p>

<p><strong>Harness 负责把约束变成真正的运行边界。</strong></p>

<p><strong>Trace 记录实际发生的事实。</strong></p>

<p><strong>Drift 检测负责发现实际执行和预期执行之间的偏离。</strong></p>

<p><strong>Regression 保证同样的失败不要再次回来。</strong></p>

<p>这也是我最近对 Agent Reliability 越来越明确的一个判断：</p>

<blockquote>
  <p>Agent 最危险的情况，不一定是直接失败，而是最终看起来成功，但执行过程已经悄悄脱离了我们以为它遵守的约束。</p>
</blockquote>

<p>如果只验最终结果，这类问题很容易被漏掉。</p>

<p>而当 Contract、Trace、Validator 和 Failure Regression 被连起来以后，Agent 的“过程是否可靠”才第一次开始变成一个可以被工程化验证的问题。</p>

<hr />

<p><strong>相关项目</strong></p>

<ul>
  <li><a href="https://github.com/hugfeature/drift">Drift</a>：Agent Runtime Observability，关注 Agent 执行过程中的任务偏移、状态异常与可解释 Trace。</li>
  <li><a href="/harness/what-is-agent-harness/">Agent Harness 是什么？为什么它正在成为 Agent 工程的关键一层</a></li>
</ul>]]></content><author><name>hugfeature</name></author><category term="Agent-Reliability" /><category term="Agent-Harness" /><category term="agent-reliability" /><category term="agent-harness" /><category term="trace-evidence" /><category term="failure-regression" /><category term="quality-gate" /><summary type="html"><![CDATA[从一次测试用例生成 Agent 的重构出发，讨论为什么 Prompt 无法保证执行流程，以及如何用 Execution Contract、Validator、Trace 和 Drift 检测把 Agent 的过程可靠性变成可验证问题。]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://hugfeature.github.io/assets/social-card.png" /><media:content medium="image" url="https://hugfeature.github.io/assets/social-card.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">怎么把 AI Eval 接进 CI / Quality Gate？让评测真正阻断坏版本</title><link href="https://hugfeature.github.io/eval/eval-ci-quality-gate/" rel="alternate" type="text/html" title="怎么把 AI Eval 接进 CI / Quality Gate？让评测真正阻断坏版本" /><published>2026-09-23T10:40:00+08:00</published><updated>2026-09-23T10:40:00+08:00</updated><id>https://hugfeature.github.io/eval/eval-ci-quality-gate</id><content type="html" xml:base="https://hugfeature.github.io/eval/eval-ci-quality-gate/"><![CDATA[<blockquote>
  <p><strong>AI Eval 系列 #10</strong><br />
上一篇：<a href="/eval/agent-trace-grading/">Agent Trace Grading 怎么设计？</a></p>
</blockquote>

<p>很多团队已经有 Eval。</p>

<p>但它的工作方式是：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>提交代码
  ↓
上线
  ↓
某天人工跑一次 Eval
  ↓
看 Dashboard
</code></pre></div></div>

<p>这时 Eval 仍然只是一个观察工具。</p>

<p>真正进入工程交付，要再往前一步：</p>

<p><strong>让 Eval 结果参与 Merge / Release 决策。</strong></p>

<p>也就是：</p>

<p><strong>Quality Gate。</strong></p>

<h2 id="一eval-和-gate-的区别">一、Eval 和 Gate 的区别</h2>

<p>Eval 回答：</p>

<p><strong>“这个版本表现怎么样？”</strong></p>

<p>Gate 回答：</p>

<p><strong>“这个版本允许不允许继续往下走？”</strong></p>

<p>两者之间差的是：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Metrics
  ↓
Policy
  ↓
Decision
</code></pre></div></div>

<p>例如：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Overall Pass Rate = 91%
</code></pre></div></div>

<p>只是 Metric。</p>

<p>而：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Failure Regression = 100%
Critical Failure = 0
High-risk Pass Rate &gt;= 95%
</code></pre></div></div>

<p>才开始形成 Policy。</p>

<p>最终：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PASS → release
FAIL → block
</code></pre></div></div>

<p>这才是 Gate。</p>

<h2 id="二不要直接用-overall-score-做-gate">二、不要直接用 Overall Score 做 Gate</h2>

<p>假设：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>v1 = 88%
v2 = 91%
</code></pre></div></div>

<p>看起来 v2 应该发布。</p>

<p>但进一步看：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Critical Case:
v1 PASS
v2 FAIL
</code></pre></div></div>

<p>那 3% 的总体提升可能根本不值得。</p>

<p>所以 Gate 最好拆成两层。</p>

<h3 id="hard-gate">Hard Gate</h3>

<p>任何一项失败就阻断：</p>

<ul>
  <li>Critical Failure &gt; 0；</li>
  <li>Known Failure Regression &lt; 100%；</li>
  <li>Permission Violation &gt; 0；</li>
  <li>Critical Side Effect &gt; 0；</li>
  <li>Required Evidence Missing；</li>
  <li>Judge Critical False Pass 超阈值。</li>
</ul>

<h3 id="soft--diagnostic-metrics">Soft / Diagnostic Metrics</h3>

<p>用于比较和决策：</p>

<ul>
  <li>Overall Pass Rate；</li>
  <li>Latency；</li>
  <li>Cost；</li>
  <li>Token；</li>
  <li>Tool Calls；</li>
  <li>Retry；</li>
  <li>Challenge Set 表现。</li>
</ul>

<p>这样就不会让“平均分”掩盖严重问题。</p>

<h2 id="三第一步建立-baseline">三、第一步：建立 Baseline</h2>

<p>Gate 不能凭空判断。</p>

<p>必须知道：</p>

<p><strong>当前版本是什么水平。</strong></p>

<p>例如：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>baseline:
  core_pass: 91%
  risk_pass: 96%
  regression_pass: 100%
  p95_latency: 8.2s
  cost_per_task: 0.14
</code></pre></div></div>

<p>每次变更产生 Candidate：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>candidate:
  core_pass: 93%
  risk_pass: 96%
  regression_pass: 98%
  p95_latency: 7.9s
  cost_per_task: 0.13
</code></pre></div></div>

<p>虽然大多数指标变好，但 Regression Pass 从 100% 变成 98%。</p>

<p>如果这两条都是已知真实失败，就应该直接 Block。</p>

<h2 id="四第二步定义哪些改动触发哪些-eval">四、第二步：定义哪些改动触发哪些 Eval</h2>

<p>不是每次提交都跑全量 Agent Eval。</p>

<p>可以分层。</p>

<h3 id="fast-gate">Fast Gate</h3>

<p>每个 PR：</p>

<ul>
  <li>Schema；</li>
  <li>Deterministic Grader；</li>
  <li>核心 20～50 Cases；</li>
  <li>Critical Regression；</li>
  <li>基础 Tool Contract。</li>
</ul>

<p>目标是快。</p>

<h3 id="full-eval">Full Eval</h3>

<p>Merge 前或每天：</p>

<ul>
  <li>Core Set；</li>
  <li>Risk Set；</li>
  <li>Failure Corpus；</li>
  <li>多 Trial；</li>
  <li>LLM Judge；</li>
  <li>Trace Grading。</li>
</ul>

<h3 id="release-gate">Release Gate</h3>

<p>正式发布前：</p>

<ul>
  <li>全量 Regression；</li>
  <li>Critical Cases；</li>
  <li>Environment Validation；</li>
  <li>Model / Prompt / Harness 版本锁定；</li>
  <li>最终 Evidence Artifact。</li>
</ul>

<p>这样兼顾速度和覆盖。</p>

<h2 id="五第三步把-eval-结果做成机器可读-artifact">五、第三步：把 Eval 结果做成机器可读 Artifact</h2>

<p>不要只有网页 Dashboard。</p>

<p>CI 至少需要一个稳定结果：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
  "status": "FAIL",
  "baseline": "eval-2026-09-22",
  "candidate": "commit-abc",
  "critical_failures": 1,
  "regressions": 2,
  "core_pass_rate": 0.93,
  "risk_pass_rate": 0.95
}
</code></pre></div></div>

<p>同时保存：</p>

<ul>
  <li>Eval Run ID；</li>
  <li>Dataset Version；</li>
  <li>Model Version；</li>
  <li>Prompt Version；</li>
  <li>Harness Version；</li>
  <li>Grader Version；</li>
  <li>Failure List；</li>
  <li>Evidence Link。</li>
</ul>

<p>这样一个 Gate Decision 才可复核。</p>

<h2 id="六第四步处理非确定性和-flaky-case">六、第四步：处理非确定性和 Flaky Case</h2>

<p>Agent Eval 最大的问题之一是：</p>

<p><strong>今天 PASS，明天可能 FAIL。</strong></p>

<p>不能简单地看到一次失败就全部阻断，也不能无限 Retry 到通过。</p>

<p>更合理的是对 Case 定义策略。</p>

<p>例如：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>critical:
  trials: 3
  require: 3/3 pass

normal:
  trials: 3
  require: &gt;= 2/3 pass
</code></pre></div></div>

<p>同时记录：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>flaky = true
</code></pre></div></div>

<p>长期 Flaky 的 Case 要继续查：</p>

<ul>
  <li>Agent 本身不稳定；</li>
  <li>Grader 不稳定；</li>
  <li>环境有噪声；</li>
  <li>Case 定义模糊。</li>
</ul>

<p>不要用 Retry 隐藏问题。</p>

<h2 id="七第五步regression-必须有更高优先级">七、第五步：Regression 必须有更高优先级</h2>

<p>新能力可以慢慢提升。</p>

<p>已经修过的真实失败不应该轻易回来。</p>

<p>所以我会让 Gate 优先级大致变成：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Critical Safety / Side Effect
    ↓
Known Failure Regression
    ↓
High-risk Business Cases
    ↓
Core Capability
    ↓
Cost / Latency
    ↓
Challenge Set
</code></pre></div></div>

<p>这和普通 Benchmark 排名完全不同。</p>

<p>它体现的是：</p>

<p><strong>交付风险，而不是模型炫技能力。</strong></p>

<h2 id="八human-override-可以有但必须留下证据">八、Human Override 可以有，但必须留下证据</h2>

<p>有时业务确实需要带风险发布。</p>

<p>例如：</p>

<ul>
  <li>Grader 已知误判；</li>
  <li>Case 已过期；</li>
  <li>线上紧急修复；</li>
  <li>非关键 Regression 可接受。</li>
</ul>

<p>可以允许 Override。</p>

<p>但至少记录：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>override_by
reason
failed_cases
risk_acceptance
expires_at
</code></pre></div></div>

<p>并且最好要求后续补：</p>

<ul>
  <li>修复 Grader；</li>
  <li>更新 Dataset；</li>
  <li>新建技术债；</li>
  <li>重新执行 Eval。</li>
</ul>

<p>否则 Override 很快会变成绕过 Gate 的常规入口。</p>

<h2 id="九不要让-gate-只阻断-model-变更">九、不要让 Gate 只阻断 Model 变更</h2>

<p>AI 系统的行为变化可能来自：</p>

<ul>
  <li>Model；</li>
  <li>Prompt；</li>
  <li>Tool Schema；</li>
  <li>RAG；</li>
  <li>Context Builder；</li>
  <li>Workflow；</li>
  <li>Harness；</li>
  <li>Grader；</li>
  <li>Business Rule。</li>
</ul>

<p>所以真正应该被 Gate 的是：</p>

<p><strong>AI System Change。</strong></p>

<p>而不是只在“换模型”时跑 Eval。</p>

<h2 id="十一个最小-ci-流程">十、一个最小 CI 流程</h2>

<p>第一版完全可以很简单：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Pull Request
    ↓
Build
    ↓
Unit / Integration Test
    ↓
AI Eval Fast Set
    ↓
Regression Set
    ↓
Gate Policy
    ↓
PASS / BLOCK
</code></pre></div></div>

<p>Merge 后：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Full Eval
    ↓
Artifact
    ↓
Release Gate
</code></pre></div></div>

<p>生产之后：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Monitoring
    ↓
New Failure
    ↓
Failure Corpus
</code></pre></div></div>

<p>闭环就形成了。</p>

<h2 id="十一eval-最终要从报告变成判据">十一、Eval 最终要从“报告”变成“判据”</h2>

<p>如果 Eval 只告诉团队：</p>

<p><strong>这个版本是 87 分。</strong></p>

<p>它仍然离工程交付很远。</p>

<p>真正有价值的是：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>这次改动修复了什么？
引入了什么 Regression？
Critical Risk 有没有增加？
Evidence 是否完整？
是否满足发布条件？
</code></pre></div></div>

<p>最终形成：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Change
  ↓
Eval
  ↓
Evidence
  ↓
Failure / Regression
  ↓
Gate
  ↓
Merge / Release
</code></pre></div></div>

<p>到这里，Eval 才真正成为 AI Engineering Reliability 的一部分。</p>

<p>如果你想继续往执行层走，可以接着看：</p>

<ul>
  <li><a href="/harness/harness-to-quality-gate/">Harness 到 Quality Gate：Agent Reliability 怎么进入交付准入</a></li>
  <li><a href="/harness/harness-vs-eval-platform/">Harness 和 Eval 平台到底是什么关系？</a></li>
  <li><a href="/harness/trace-is-not-just-logs/">Trace 不是日志：Harness 应该记录哪些执行证据？</a></li>
</ul>

<h2 id="参考资料">参考资料</h2>

<ul>
  <li><a href="https://developers.openai.com/api/docs/guides/evaluation-best-practices">OpenAI — Evaluation best practices</a></li>
  <li><a href="https://developers.openai.com/api/docs/guides/agent-evals">OpenAI — Evaluate agent workflows</a></li>
  <li><a href="https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents">Anthropic — Demystifying evals for AI agents</a></li>
</ul>]]></content><author><name>hugfeature</name></author><category term="AI-Eval" /><category term="Quality-Gate" /><category term="Agent-Reliability" /><category term="ai-eval" /><category term="quality-gate" /><category term="ci" /><category term="regression" /><category term="agent-reliability" /><summary type="html"><![CDATA[Eval 只有进入 CI / Quality Gate 才真正参与交付。本文给出 Baseline、Hard Gate、Regression、Flaky Case、Artifact、Override 与发布准入的工程化设计。]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://hugfeature.github.io/assets/social-card.png" /><media:content medium="image" url="https://hugfeature.github.io/assets/social-card.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Agent Trace Grading 怎么设计？结果正确，过程也可能已经失控</title><link href="https://hugfeature.github.io/eval/agent-trace-grading/" rel="alternate" type="text/html" title="Agent Trace Grading 怎么设计？结果正确，过程也可能已经失控" /><published>2026-09-23T10:30:00+08:00</published><updated>2026-09-23T10:30:00+08:00</updated><id>https://hugfeature.github.io/eval/agent-trace-grading</id><content type="html" xml:base="https://hugfeature.github.io/eval/agent-trace-grading/"><![CDATA[<blockquote>
  <p><strong>AI Eval 系列 #09</strong><br />
上一篇：<a href="/eval/build-failure-corpus/">线上失败怎么变成 Failure Corpus？</a></p>
</blockquote>

<p>传统模型评测很容易把任务简化成：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Input
  ↓
Output
  ↓
Grader
</code></pre></div></div>

<p>但 Agent 真正执行任务时，中间可能经历几十个 Step：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Goal
  ↓
Context
  ↓
Model Decision
  ↓
Tool Call
  ↓
Tool Result
  ↓
State Update
  ↓
Retry / Recovery
  ↓
Final Result
</code></pre></div></div>

<p>如果只检查 Final Result，就会漏掉一种越来越重要的失败：</p>

<p><strong>结果是对的，但过程已经失控。</strong></p>

<p>这正是 Trace Grading 要解决的问题。</p>

<h2 id="一什么是-trace-grading">一、什么是 Trace Grading</h2>

<p>Trace 是一次 Agent 运行的完整轨迹。</p>

<p>通常至少包括：</p>

<ul>
  <li>Model Calls；</li>
  <li>Tool Calls；</li>
  <li>Tool Results；</li>
  <li>Handoff；</li>
  <li>Guardrail；</li>
  <li>State Change；</li>
  <li>Retry；</li>
  <li>Error；</li>
  <li>Final Output。</li>
</ul>

<p>OpenAI 当前的 Agent Eval 文档把 Trace Grading 放在工作流评测的核心位置：先记录端到端执行，再使用 Grader 对 Tool 选择、Handoff、指令违反和 Workflow 行为进行结构化判断。</p>

<p>简单说：</p>

<p><strong>Result Grading 判断“做成没有”，Trace Grading 判断“怎么做成的”。</strong></p>

<h2 id="二为什么-final-result-不够">二、为什么 Final Result 不够</h2>

<p>假设任务是：</p>

<p><strong>修改测试环境配置，并验证服务正常。</strong></p>

<p>最终结果看起来完全正确。</p>

<p>但 Trace 可能是：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Step 1 修改测试环境
Step 2 配置错误
Step 3 服务启动失败
Step 4 Agent 未验证状态
Step 5 误修改生产环境
Step 6 发现异常
Step 7 回滚生产
Step 8 修复测试环境
Step 9 输出“任务完成”
</code></pre></div></div>

<p>最终测试环境是正确的。</p>

<p>如果只看最终状态：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PASS
</code></pre></div></div>

<p>但整个过程至少发生了一次严重 Side Effect Violation。</p>

<p>所以：</p>

<p><strong>End State Correct 不等于 Execution Safe。</strong></p>

<h2 id="三trace-grading-最值得检查什么">三、Trace Grading 最值得检查什么</h2>

<p>我更推荐先从五类信号开始。</p>

<h3 id="1-tool-correctness">1. Tool Correctness</h3>

<p>检查：</p>

<ul>
  <li>是否选择正确工具；</li>
  <li>参数是否正确；</li>
  <li>是否调用不必要工具；</li>
  <li>是否调用禁止工具。</li>
</ul>

<p>例如：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>expected_tool = query_order
actual_tool = delete_order
</code></pre></div></div>

<p>即使后面没有真正执行删除，也已经是重要风险信号。</p>

<h3 id="2-constraint-compliance">2. Constraint Compliance</h3>

<p>检查过程中有没有违反明确约束：</p>

<ul>
  <li>必须审批；</li>
  <li>禁止访问生产；</li>
  <li>不允许删除；</li>
  <li>只能修改指定目录；</li>
  <li>不允许绕过测试。</li>
</ul>

<p>这类规则适合做 Hard Assertion。</p>

<h3 id="3-state-verification">3. State Verification</h3>

<p>Agent 执行一个动作之后，是否验证真实状态。</p>

<p>例如：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Tool Result:
deploy requested
</code></pre></div></div>

<p>不能直接推导出：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>deploy succeeded
</code></pre></div></div>

<p>需要独立检查健康状态、版本或环境。</p>

<p>缺少这一步，很容易出现 State Desync。</p>

<h3 id="4-side-effect">4. Side Effect</h3>

<p>Agent 是否造成了任务目标之外的修改。</p>

<p>例如 Coding Agent：</p>

<ul>
  <li>修改无关文件；</li>
  <li>删除测试；</li>
  <li>改配置绕过失败；</li>
  <li>提交凭据。</li>
</ul>

<p>Computer Use Agent：</p>

<ul>
  <li>多创建订单；</li>
  <li>修改错误客户；</li>
  <li>发送重复消息。</li>
</ul>

<p>这些通常不能只靠 Final Output 发现。</p>

<h3 id="5-evidence">5. Evidence</h3>

<p>Agent 最终的成功声明是否绑定可独立检查的证据。</p>

<p>例如：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>claim:
tests passed

evidence:
pytest exit_code = 0
report = artifacts/test-report.xml
</code></pre></div></div>

<p>如果只有 claim，没有 evidence，更合理的状态可能是：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>UNVERIFIED
</code></pre></div></div>

<p>而不是 PASS。</p>

<h2 id="四不要把-trace-grading-写成固定路径匹配">四、不要把 Trace Grading 写成固定路径匹配</h2>

<p>这是一个很容易踩的坑。</p>

<p>例如定义：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>正确路径必须是：
search
→ open
→ edit
→ test
</code></pre></div></div>

<p>但 Agent 可能找到另一条同样合法甚至更短的路径：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>open
→ edit
→ test
</code></pre></div></div>

<p>如果 Eval 强行要求固定 Tool Sequence，就会把合理行为判错。</p>

<p>Anthropic 的 Agent Eval 实践也强调：过度检查具体执行路径会让 Eval 变脆，通常应该优先验证结果、约束和关键不变量，而不是要求 Agent 完全照设计者预想的步骤执行。</p>

<p>所以：</p>

<p><strong>Trace Grading 不等于 Path Matching。</strong></p>

<h2 id="五更稳定的方法检查-invariant">五、更稳定的方法：检查 Invariant</h2>

<p>不要写：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>必须先调用 tool_A，再调用 tool_B
</code></pre></div></div>

<p>更适合写：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>修改前必须读取当前状态
修改后必须验证新状态
禁止访问 production
最终必须存在测试证据
</code></pre></div></div>

<p>也就是检查：</p>

<p><strong>必须一直成立的规则。</strong></p>

<p>这比固定步骤更适合 Agent。</p>

<h2 id="六哪些-trace-rule-适合确定性判断">六、哪些 Trace Rule 适合确定性判断</h2>

<p>优先用程序判断：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>forbidden_tool_called
retry_count &gt; limit
production_write == true
test_exit_code != 0
evidence_missing
changed_files outside allowed_paths
</code></pre></div></div>

<p>这类规则不需要 LLM Judge。</p>

<p>它们应该稳定、快速、可复现。</p>

<h2 id="七哪些适合-llm-trace-grader">七、哪些适合 LLM Trace Grader</h2>

<p>LLM 更适合语义层问题：</p>

<ul>
  <li>Agent 是否忽略用户关键约束；</li>
  <li>某次 Handoff 是否合理；</li>
  <li>重复操作是否属于无效 Loop；</li>
  <li>是否在证据不足时过早宣布完成；</li>
  <li>Recovery 是否偏离原目标。</li>
</ul>

<p>但仍然要提供明确 Rubric。</p>

<p>例如：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>FAIL:
Agent 在未验证真实环境状态前宣布成功。

PASS:
Agent 使用独立信号验证目标状态，再给出完成结论。
</code></pre></div></div>

<p>不要只问：</p>

<p><strong>“这条 Trace 好不好？”</strong></p>

<h2 id="八trace-grading-应该输出-evidence">八、Trace Grading 应该输出 Evidence</h2>

<p>一个有用的 Trace Grader 输出不应该只有：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>score = 0.7
</code></pre></div></div>

<p>更应该是：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>verdict: FAIL
failure_type: state_desync
step: 12
evidence:
  - deployment request returned 202
  - no health verification followed
  - agent declared success at step 13
</code></pre></div></div>

<p>这样才能进入：</p>

<ul>
  <li>Failure Triage；</li>
  <li>Root Cause；</li>
  <li>Failure Corpus；</li>
  <li>Regression。</li>
</ul>

<h2 id="九result-grading-和-trace-grading-要同时存在">九、Result Grading 和 Trace Grading 要同时存在</h2>

<p>最终可以形成二维结果：</p>

<table>
  <thead>
    <tr>
      <th>Result</th>
      <th>Trace</th>
      <th>判断</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>PASS</td>
      <td>PASS</td>
      <td>VERIFIED</td>
    </tr>
    <tr>
      <td>PASS</td>
      <td>FAIL</td>
      <td>PROCESS FAILURE</td>
    </tr>
    <tr>
      <td>FAIL</td>
      <td>PASS</td>
      <td>CAPABILITY / TASK FAILURE</td>
    </tr>
    <tr>
      <td>FAIL</td>
      <td>FAIL</td>
      <td>SYSTEM FAILURE</td>
    </tr>
  </tbody>
</table>

<p>最值得关注的是第二类：</p>

<p><strong>Result PASS / Trace FAIL。</strong></p>

<p>这就是“最终看起来成功，但过程已经失控”。</p>

<h2 id="十trace-grading-最终要服务-failure-定位">十、Trace Grading 最终要服务 Failure 定位</h2>

<p>一条 Trace 有几十甚至几百个事件。</p>

<p>如果 Grader 只是告诉你：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>FAIL
</code></pre></div></div>

<p>价值不够。</p>

<p>最好能够回答：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>失败发生在哪一步？
属于哪一层？
哪个 Evidence 支持判断？
是第一次发生还是已知 Failure？
能不能转成 Regression？
</code></pre></div></div>

<p>这时 Trace 就从 Observability 数据，变成了 Eval 数据。</p>

<h2 id="十一trace-是-agent-reliability-的连接层">十一、Trace 是 Agent Reliability 的连接层</h2>

<p>它连接了：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Execution
   ↓
Evidence
   ↓
Eval
   ↓
Failure
   ↓
Regression
   ↓
Gate
</code></pre></div></div>

<p>没有 Trace，很多 Agent Failure 只能看到结果。</p>

<p>有了 Trace Grading，系统才开始有能力判断：</p>

<p><strong>为什么成功，为什么失败，以及这个成功到底可信不可信。</strong></p>

<p>下一篇是本系列最后一篇：</p>

<p><strong>怎么把 Eval 真正接进 CI / Quality Gate，而不是停在一张 Dashboard 上。</strong></p>

<h2 id="参考资料">参考资料</h2>

<ul>
  <li><a href="https://developers.openai.com/api/docs/guides/agent-evals">OpenAI — Evaluate agent workflows</a></li>
  <li><a href="https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents">Anthropic — Demystifying evals for AI agents</a></li>
</ul>]]></content><author><name>hugfeature</name></author><category term="AI-Eval" /><category term="Agent-Eval" /><category term="Trace-Evidence" /><category term="agent-eval" /><category term="trace-grading" /><category term="trace-evidence" /><category term="tool-calling" /><category term="agent-reliability" /><summary type="html"><![CDATA[Agent Eval 不能只看最终答案。本文讲清如何用 Trace Grading 检查 Tool、State、Constraint、Side Effect、Evidence 与 Recovery，同时避免把评测写成僵硬的路径匹配。]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://hugfeature.github.io/assets/social-card.png" /><media:content medium="image" url="https://hugfeature.github.io/assets/social-card.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">线上失败怎么变成 Failure Corpus？从一次事故到持续回归</title><link href="https://hugfeature.github.io/eval/build-failure-corpus/" rel="alternate" type="text/html" title="线上失败怎么变成 Failure Corpus？从一次事故到持续回归" /><published>2026-09-23T10:20:00+08:00</published><updated>2026-09-23T10:20:00+08:00</updated><id>https://hugfeature.github.io/eval/build-failure-corpus</id><content type="html" xml:base="https://hugfeature.github.io/eval/build-failure-corpus/"><![CDATA[<blockquote>
  <p><strong>AI Eval 系列 #08</strong><br />
上一篇：<a href="/eval/offline-vs-online-eval/">Offline Eval 和 Online Eval 有什么区别？</a></p>
</blockquote>

<p>很多 AI 系统上线之后，失败其实不少。</p>

<p>但几个月后回头看，会发现真正留下来的只有日志、截图、一张 Bug 单，或者一句“这个问题之前好像修过”。</p>

<p>这不叫质量资产。</p>

<p>真正有价值的做法是把失败沉淀成 <strong>Failure Corpus</strong>：一套可以被检索、复现、归因、重放和持续回归的真实失败集合。</p>

<h2 id="一failure-corpus-和-bug-list-不一样">一、Failure Corpus 和 Bug List 不一样</h2>

<p>Bug List 更关注：发生了什么问题、谁来修、现在状态是什么。</p>

<p>Failure Corpus 更关注：</p>

<ul>
  <li>在什么输入和环境下失败；</li>
  <li>系统实际执行了什么；</li>
  <li>失败属于哪一类；</li>
  <li>应该怎样独立验证；</li>
  <li>修复后如何保证不再出现。</li>
</ul>

<p>所以它的最终目的不是“记录问题”，而是：</p>

<p><strong>让同类失败以后可以自动被挡住。</strong></p>

<h2 id="二一条-failure-至少要保留什么">二、一条 Failure 至少要保留什么</h2>

<p>最小结构可以包含：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>failure_id: F-2026-017
task_type: test-case-generation
severity: high

input:
  prd: ...

environment:
  model: ...
  prompt_version: ...
  harness_version: ...

actual:
  output: ...
  trace_id: ...

expected:
  must_cover:
    - approval_threshold

failure:
  type: missing_constraint
  evidence: ...

root_cause:
  layer: context

regression:
  case_id: REG-017
</code></pre></div></div>

<p>这里最关键的是三件事：<strong>输入、证据、失败类型。</strong></p>

<p>没有这三件，后面很难复现。</p>

<h2 id="三第一步不是分类而是保证可复现">三、第一步不是分类，而是保证可复现</h2>

<p>线上失败发生以后，先问：</p>

<p><strong>同样的输入还能不能重现？</strong></p>

<p>如果模型具有随机性，就多跑几次。</p>

<p>例如 5 次执行里 3 次失败，这已经告诉你：这是概率性失败，而不是一次偶然截图。</p>

<p>同时要记录 Model Version、Prompt Version、Tool Version、Context、Business Rule Version、Harness Version 和环境状态。</p>

<p>否则修复前后无法公平比较。</p>

<h2 id="四第二步区分-symptom-和-root-cause">四、第二步：区分 Symptom 和 Root Cause</h2>

<p>例如症状是：</p>

<p><strong>生成的测试用例遗漏审批阈值。</strong></p>

<p>但根因可能是：</p>

<ul>
  <li>PRD 没被完整加载；</li>
  <li>Context 被截断；</li>
  <li>模型理解失败；</li>
  <li>Prompt 没要求覆盖；</li>
  <li>Judge 没发现遗漏。</li>
</ul>

<p>如果只把 Failure Type 写成 missing test case，价值有限。</p>

<p>更好的方式是至少区分层级：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Input
Context
Model Decision
Tool
State
Harness
Grader
Environment
</code></pre></div></div>

<p>这样后面才能知道应该修哪一层。</p>

<h2 id="五failure-taxonomy-不要一开始设计得太复杂">五、Failure Taxonomy 不要一开始设计得太复杂</h2>

<p>第一版建议从 5～10 类开始，例如：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>instruction_violation
missing_constraint
wrong_tool
wrong_argument
state_desync
unsupported_claim
side_effect_violation
loop_or_retry
missing_evidence
grader_error
</code></pre></div></div>

<p>遇到新失败再扩。</p>

<p>分类体系应该来自真实失败，而不是先设计一个看起来完整的分类树。</p>

<h2 id="六第三步把-failure-变成-regression-case">六、第三步：把 Failure 变成 Regression Case</h2>

<p>不是所有线上失败都值得进入回归集。</p>

<p>我会优先收：</p>

<ul>
  <li>Critical / High Severity；</li>
  <li>重复出现；</li>
  <li>用户真实投诉；</li>
  <li>已经修复；</li>
  <li>代表一种新的 Failure Mode；</li>
  <li>很容易再次发生。</li>
</ul>

<p>Regression Case 必须重新定义成功条件。</p>

<p>例如原始失败是：</p>

<p><strong>Agent 忘记审批阈值。</strong></p>

<p>Regression 不应该只是保存旧输出，而应该定义：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>must_include:
  - approval_threshold

critical_error:
  - approve_without_threshold_check
</code></pre></div></div>

<p>这样未来模型即使换一种表达方式，也能正确判断。</p>

<h2 id="七第四步绑定-fix-和-evidence">七、第四步：绑定 Fix 和 Evidence</h2>

<p>每条 Regression Case 最好知道它为什么存在。</p>

<p>例如：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>introduced_by: F-2026-017
fixed_by: commit-abc
root_cause: context-truncation
verification:
  - context_contains_rule
  - output_covers_rule
</code></pre></div></div>

<p>以后如果它再次失败，就能快速判断：老问题回来了，还是新原因导致同样症状。</p>

<h2 id="八不要只保存失败输出要保存-trace">八、不要只保存失败输出，要保存 Trace</h2>

<p>对于 Agent，Final Answer 往往不是根因所在。</p>

<p>例如最终表现只是“任务失败”，但 Trace 可能显示：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Step 3 Tool Timeout
Step 4 自动 Retry
Step 5 切换到错误环境
Step 6 State 未重新验证
</code></pre></div></div>

<p>真正的问题其实是 Recovery Policy。</p>

<p>所以 Failure Corpus 对 Agent 来说应该尽量保存 Goal、Context、Trace、Tool Calls、State Changes、Artifacts、Final Result 和 Evidence。</p>

<h2 id="九failure-corpus-应该支持两个方向的检索">九、Failure Corpus 应该支持两个方向的检索</h2>

<p>第一类是按业务检索，例如采购、审批、库存、权限，回答：</p>

<p><strong>这个业务过去发生过哪些 AI 失败？</strong></p>

<p>第二类是按 Failure Mode 检索，例如 state_desync、wrong_tool、missing_evidence，回答：</p>

<p><strong>这个技术失败模式在哪些业务里出现过？</strong></p>

<p>第二种对 Reliability 尤其重要，因为同一种 Runtime Failure 可能跨多个业务重复出现。</p>

<h2 id="十corpus-不是越大越好">十、Corpus 不是越大越好</h2>

<p>如果 Corpus 里有 5000 条重复低价值失败，会变得越来越难维护。</p>

<p>例如 20 个 Case 都是“长上下文下遗漏早期约束”，可以保留少量代表 Case、若干业务变体和一个统一 Failure Type。</p>

<p>重点是：</p>

<p><strong>覆盖 Failure Mode，而不是堆数量。</strong></p>

<h2 id="十一什么时候-failure-才算真正关闭">十一、什么时候 Failure 才算真正关闭</h2>

<p>传统 Bug 常见的是：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>代码修了
→ close
</code></pre></div></div>

<p>AI Reliability 更合理的关闭条件应该是：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Failure reproduced
+
Root cause identified
+
Fix implemented
+
Independent verification passed
+
Regression case added
+
Gate includes regression
</code></pre></div></div>

<p>也就是：</p>

<p><strong>没有 Regression，就不算真正修完。</strong></p>

<h2 id="十二failure-corpus-最终会成为系统的免疫记忆">十二、Failure Corpus 最终会成为系统的“免疫记忆”</h2>

<p>它会逐渐回答：</p>

<ul>
  <li>我们以前在哪些地方失败过；</li>
  <li>为什么失败；</li>
  <li>怎么验证已经修复；</li>
  <li>下一次改动会不会再次触发。</li>
</ul>

<p>当它和 Eval、Trace、Quality Gate 串起来之后：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Failure
↓
Evidence
↓
Root Cause
↓
Regression
↓
Gate
</code></pre></div></div>

<p>真实事故才真正变成了系统能力。</p>

<p>下一篇继续进入 Agent 场景：</p>

<p><strong>Trace 到底应该怎么 Grade，才能发现“结果对了但过程错了”？</strong></p>

<h2 id="参考资料">参考资料</h2>

<ul>
  <li><a href="https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents">Anthropic — Demystifying evals for AI agents</a></li>
  <li><a href="https://developers.openai.com/api/docs/guides/agent-evals">OpenAI — Evaluate agent workflows</a></li>
</ul>]]></content><author><name>hugfeature</name></author><category term="AI-Eval" /><category term="Agent-Reliability" /><category term="failure-corpus" /><category term="ai-eval" /><category term="regression" /><category term="agent-reliability" /><category term="root-cause" /><summary type="html"><![CDATA[Failure Corpus 不是错误日志堆积，而是把真实失败结构化成可复现、可归因、可回归的质量资产。本文给出 Failure → Root Cause → Regression Case 的完整闭环。]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://hugfeature.github.io/assets/social-card.png" /><media:content medium="image" url="https://hugfeature.github.io/assets/social-card.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Offline Eval 和 Online Eval 有什么区别？一套 AI 系统为什么两个都需要</title><link href="https://hugfeature.github.io/eval/offline-vs-online-eval/" rel="alternate" type="text/html" title="Offline Eval 和 Online Eval 有什么区别？一套 AI 系统为什么两个都需要" /><published>2026-09-23T10:10:00+08:00</published><updated>2026-09-23T10:10:00+08:00</updated><id>https://hugfeature.github.io/eval/offline-vs-online-eval</id><content type="html" xml:base="https://hugfeature.github.io/eval/offline-vs-online-eval/"><![CDATA[<blockquote>
  <p><strong>AI Eval 系列 #07</strong><br />
上一篇：<a href="/eval/eval-metrics-beyond-pass-rate/">Eval 指标怎么设计？为什么 Pass Rate 远远不够</a></p>
</blockquote>

<p>很多团队做 Eval 时会走向两个极端。</p>

<p>一种是：</p>

<p><strong>“我们有离线 Benchmark，所以质量没问题。”</strong></p>

<p>另一种是：</p>

<p><strong>“线上有监控，真实用户会告诉我们问题。”</strong></p>

<p>两种都不够。</p>

<p>更完整的体系应该是：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Offline Eval
      ↓
Release
      ↓
Online Observation
      ↓
New Failure
      ↓
Offline Regression
</code></pre></div></div>

<p>Offline 和 Online 不是替代关系。</p>

<p>它们解决的是不同问题。</p>

<h2 id="一offline-eval发布之前回答这次改动安全吗">一、Offline Eval：发布之前回答“这次改动安全吗”</h2>

<p>Offline Eval 的特点是：</p>

<ul>
  <li>固定 Dataset；</li>
  <li>可重复运行；</li>
  <li>环境尽量稳定；</li>
  <li>有明确 Grader；</li>
  <li>可以对比版本。</li>
</ul>

<p>典型用途：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Model A vs Model B
Prompt v3 vs v4
Tool Schema old vs new
Harness old vs new
</code></pre></div></div>

<p>它最适合做回归验证。</p>

<p>因为你希望输入尽量不变，只观察系统改动带来的变化。</p>

<h2 id="二offline-eval-的核心价值是可比较">二、Offline Eval 的核心价值是可比较</h2>

<p>例如：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Eval Set = 200 cases

v1:
Pass = 81%

v2:
Pass = 87%
</code></pre></div></div>

<p>如果环境、数据和 Grader 都稳定，你才有资格说 v2 相对 v1 有改善。</p>

<p>所以 Offline Eval 特别强调：</p>

<ul>
  <li>环境隔离；</li>
  <li>Dataset 版本；</li>
  <li>Prompt / Model 版本；</li>
  <li>Trial 配置；</li>
  <li>Grader 版本。</li>
</ul>

<p>否则每次跑出来的差异里，会混入大量基础设施噪声。</p>

<h2 id="三但-offline-永远覆盖不了真实世界">三、但 Offline 永远覆盖不了真实世界</h2>

<p>再好的 Dataset，也只是：</p>

<p><strong>你已经知道要测什么。</strong></p>

<p>真实用户会不断带来：</p>

<ul>
  <li>新表达方式；</li>
  <li>新业务组合；</li>
  <li>新异常数据；</li>
  <li>新攻击方式；</li>
  <li>新长尾流程；</li>
  <li>新环境状态。</li>
</ul>

<p>因此上线之后还需要 Online。</p>

<h2 id="四online-eval-更接近生产质量观测">四、Online Eval 更接近“生产质量观测”</h2>

<p>Online Eval 不一定意味着“线上每个请求都让 Judge 再评一次”。</p>

<p>它可以包含很多方式：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Production Metrics
User Feedback
Human Review
Shadow Evaluation
LLM Judge Sampling
Rule Violation Detection
Failure Mining
</code></pre></div></div>

<p>核心是：</p>

<p><strong>在真实分布里持续观察系统。</strong></p>

<p>成熟团队常见的组合也是自动 Eval、生产监控和周期性人工 Review 一起使用。</p>

<h2 id="五online-最重要的产物不是分数而是新-case">五、Online 最重要的产物不是分数，而是新 Case</h2>

<p>假设线上发现：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>用户：
撤回刚才那个申请，但保留附件。

Agent：
直接删除整条业务单据。
</code></pre></div></div>

<p>这个失败不能只留在日志里。</p>

<p>应该变成：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Production Failure
      ↓
Root Cause
      ↓
Eval Case
      ↓
Regression Set
</code></pre></div></div>

<p>下一版发布前必须重新跑。</p>

<p>这一步连接了 Online 和 Offline。</p>

<h2 id="六可以怎么做-online-eval">六、可以怎么做 Online Eval</h2>

<h3 id="1-规则型监控">1. 规则型监控</h3>

<p>适合确定性风险：</p>

<ul>
  <li>Tool Error；</li>
  <li>Timeout；</li>
  <li>Retry 次数；</li>
  <li>Permission Denied；</li>
  <li>Schema Invalid；</li>
  <li>Forbidden Tool；</li>
  <li>Critical State Change。</li>
</ul>

<p>这些不需要 LLM。</p>

<h3 id="2-抽样-llm-judge">2. 抽样 LLM Judge</h3>

<p>从线上流量抽样：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1% sessions
↓
Judge
↓
Correctness / Completeness / Risk
</code></pre></div></div>

<p>重点不是覆盖所有请求，而是发现趋势和新失败模式。</p>

<h3 id="3-human-sampling">3. Human Sampling</h3>

<p>高风险业务可以保留人工抽查，尤其用于校准 Judge、审核 Judge 分歧，以及检查新 Failure Type。</p>

<h3 id="4-user-feedback">4. User Feedback</h3>

<p>用户点踩、人工修改、重新执行、客服升级，都是非常强的质量信号。</p>

<p>不要只把它们当产品指标，它们应该进入 Eval 数据管道。</p>

<h2 id="七shadow-eval-是升级模型时很有用的一层">七、Shadow Eval 是升级模型时很有用的一层</h2>

<p>如果准备从 Model A 切到 Model B，可以：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Real Traffic
    ↓
Model A → production result

同一输入
    ↓
Model B → shadow result
</code></pre></div></div>

<p>Model B 不产生真实副作用，只记录输出。</p>

<p>然后比较：</p>

<ul>
  <li>Task Success；</li>
  <li>Judge Preference；</li>
  <li>Tool Plan；</li>
  <li>Latency；</li>
  <li>Cost；</li>
  <li>Critical Failure。</li>
</ul>

<p>这比只用静态 Benchmark 更接近真实任务分布。</p>

<h2 id="八online-eval-不适合直接替代发布-gate">八、Online Eval 不适合直接替代发布 Gate</h2>

<p>线上数据有几个问题：</p>

<ul>
  <li>分布一直变化；</li>
  <li>Ground Truth 不完整；</li>
  <li>用户反馈有偏；</li>
  <li>事件存在延迟；</li>
  <li>很多结果无法立即验证。</li>
</ul>

<p>所以：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Online → discover
Offline → verify
</code></pre></div></div>

<p>Online 更适合发现问题，Offline 更适合确认问题已经修复。</p>

<h2 id="九一条完整的数据闭环">九、一条完整的数据闭环</h2>

<p>成熟一点的系统应该逐渐形成：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Production
  ↓
Trace / Feedback / Failure
  ↓
Failure Triage
  ↓
Failure Corpus
  ↓
Offline Eval
  ↓
Fix
  ↓
Regression
  ↓
Quality Gate
  ↓
Release
</code></pre></div></div>

<p>这时 Online 和 Offline 才真正连起来。</p>

<h2 id="十不要把-production-monitoring-和-eval-分成两个世界">十、不要把 Production Monitoring 和 Eval 分成两个世界</h2>

<p>常见问题是：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Observability 团队看线上指标
Eval 团队跑离线测试
</code></pre></div></div>

<p>两边数据互不流通。</p>

<p>最后：</p>

<ul>
  <li>线上真实失败进不了 Eval；</li>
  <li>Eval 里修好的问题也无法确认线上是否消失。</li>
</ul>

<p>更好的设计应该共享：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Run ID
Task Type
Model Version
Prompt Version
Trace
Failure Type
Eval Case ID
</code></pre></div></div>

<p>让一次失败可以从 Production 追到 Regression。</p>

<h2 id="十一最小落地方式">十一、最小落地方式</h2>

<p>如果系统刚开始，不需要复杂平台。</p>

<p>先做：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Offline:
30～100 个真实 Case
每次关键改动自动跑

Online:
记录 Trace
收集用户反馈
每周抽样人工 Review

Bridge:
线上失败 → 新增 Regression Case
</code></pre></div></div>

<p>这已经能形成第一版质量闭环。</p>

<p>下一篇继续把这座桥做具体：</p>

<p><strong>线上失败到底怎么沉淀成 Failure Corpus？</strong></p>

<h2 id="参考资料">参考资料</h2>

<ul>
  <li><a href="https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents">Anthropic — Demystifying evals for AI agents</a></li>
  <li><a href="https://developers.openai.com/api/docs/guides/evaluation-best-practices">OpenAI — Evaluation best practices</a></li>
</ul>]]></content><author><name>hugfeature</name></author><category term="AI-Eval" /><category term="Agent-Eval" /><category term="ai-eval" /><category term="offline-eval" /><category term="online-eval" /><category term="production-monitoring" /><category term="regression" /><summary type="html"><![CDATA[Offline Eval 负责发布前的可重复比较，Online Eval 负责发现真实生产分布与新失败。本文给出两者的边界、数据闭环与落地方式。]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://hugfeature.github.io/assets/social-card.png" /><media:content medium="image" url="https://hugfeature.github.io/assets/social-card.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Eval 指标怎么设计？为什么 Pass Rate 远远不够</title><link href="https://hugfeature.github.io/eval/eval-metrics-beyond-pass-rate/" rel="alternate" type="text/html" title="Eval 指标怎么设计？为什么 Pass Rate 远远不够" /><published>2026-09-23T10:00:00+08:00</published><updated>2026-09-23T10:00:00+08:00</updated><id>https://hugfeature.github.io/eval/eval-metrics-beyond-pass-rate</id><content type="html" xml:base="https://hugfeature.github.io/eval/eval-metrics-beyond-pass-rate/"><![CDATA[<blockquote>
  <p><strong>AI Eval 系列 #06</strong><br />
上一篇：<a href="/eval/llm-as-a-judge/">LLM-as-a-Judge 怎么做才靠谱？</a></p>
</blockquote>

<p>很多 Eval 最终会被压缩成一个数字：</p>

<p><strong>Pass Rate = 87%。</strong></p>

<p>这个数字当然有用。</p>

<p>但它通常只回答：</p>

<p><strong>“平均有多少任务通过？”</strong></p>

<p>它没有告诉你：失败是不是集中在关键任务、同一个任务多跑几次是否稳定、成功是不是靠大量 Retry 换来的、结果是否真的有 Evidence，以及延迟和成本是否已经不可接受。</p>

<p>所以生产级 Eval 的指标，至少应该从“单一准确率”升级成一组质量信号。</p>

<h2 id="一第一层task-success">一、第一层：Task Success</h2>

<p>最基础指标仍然是：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Task Success Rate
=
成功任务数 / 总任务数
</code></pre></div></div>

<p>它适合回答总体趋势，但至少要按任务类型拆开：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Overall
Core Tasks
Risk Tasks
Failure Regression
Long Context
Tool Calling
</code></pre></div></div>

<p>否则总体提升可能掩盖关键场景退化。</p>

<h2 id="二第二层critical-failure-rate">二、第二层：Critical Failure Rate</h2>

<p>不是所有失败权重都一样。</p>

<p>例如“少一个解释字段”和“漏掉关键审批条件”都可能被算作 FAIL，但业务后果完全不同。</p>

<p>所以建议给 Case 加 Risk：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>low
medium
high
critical
</code></pre></div></div>

<p>然后单独统计：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Critical Failure Rate
High-risk Pass Rate
</code></pre></div></div>

<p>真正用于发布门禁时，我更关心：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Critical Failure = 0
</code></pre></div></div>

<p>而不是 Overall 从 91% 提升到 92%。</p>

<h2 id="三第三层passk-和-passk">三、第三层：pass@k 和 pass^k</h2>

<p>Agent 和生成式模型具有非确定性。同一个 Case 跑一次成功，不代表下一次还成功。</p>

<p>Anthropic 在 Agent Eval 实践中区分了两个很重要的指标。</p>

<h3 id="passk">pass@k</h3>

<p>k 次尝试里，<strong>至少一次成功</strong>。</p>

<p>它适合 Coding Agent、搜索多个候选方案，以及允许多次尝试、最终有一个结果即可的任务。</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>能力上限 → 更适合看 pass@k
</code></pre></div></div>

<h3 id="passk-1">pass^k</h3>

<p>k 次尝试里，<strong>每次都成功</strong>。</p>

<p>它更接近生产稳定性。</p>

<p>客服、审批、企业自动化不能接受“多试几次总有一次对”，这类任务更应该关注连续成功能力。</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>生产稳定性 → 更适合看 pass^k
</code></pre></div></div>

<p>只看一次 Pass Rate，很难看到这种差异。</p>

<h2 id="四第四层first-pass-success">四、第四层：First-pass Success</h2>

<p>如果 Agent 最终成功，但中间 Retry 了 7 次，这个成功和一次完成不是一回事。</p>

<p>建议增加：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>First-pass Success Rate
Retry Rate
Average Retry Count
</code></pre></div></div>

<p>例如：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Version A
Success = 92%
First-pass = 89%

Version B
Success = 93%
First-pass = 61%
</code></pre></div></div>

<p>只看 Success，B 好像更强；实际上它已经变得非常不稳定。</p>

<h2 id="五第五层false-pass">五、第五层：False Pass</h2>

<p>如果你使用 LLM Judge 或复杂自动 Grader，最危险的指标不是 False Fail，而是：</p>

<p><strong>错误结果被判成 PASS。</strong></p>

<p>所以要单独记录：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Judge False Pass Rate
Critical False Pass Rate
</code></pre></div></div>

<p>尤其是 Gate 场景，因为：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>真实错误
+
Grader 误判成功
=
风险被完全隐藏
</code></pre></div></div>

<p>这是评测系统本身的 Reliability 问题。</p>

<h2 id="六第六层evidence-completeness">六、第六层：Evidence Completeness</h2>

<p>Agent 说任务成功，不代表你能验证它。</p>

<p>因此可以定义：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Evidence Completeness Rate
Verified Success Rate
</code></pre></div></div>

<p>例如：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Task Success = 90%
Verified Success = 74%
</code></pre></div></div>

<p>差出来的 16%，说明 Agent 声称成功，但证据不足。</p>

<p>不同任务可以绑定不同 Evidence：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Coding:
test result + diff + changed files

Testing:
executed cases + result + artifact

Deployment:
target version + health check + environment state
</code></pre></div></div>

<h2 id="七第七层执行效率">七、第七层：执行效率</h2>

<p>成功并不是免费得到的。</p>

<p>至少记录：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Latency P50 / P95
Token / Task
Cost / Task
Tool Calls / Task
Steps / Task
Retry / Task
</code></pre></div></div>

<p>对于 Agent，我尤其建议看 P95 Steps 和 P95 Tool Calls，因为长尾执行往往最容易隐藏 Loop、Rabbit Hole、重复搜索、Retry Storm 和状态不同步。</p>

<h2 id="八第八层regression-delta">八、第八层：Regression Delta</h2>

<p>版本对比不能只看新版本总分。</p>

<p>要看：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Fixed
Regressed
Unchanged Pass
Unchanged Fail
</code></pre></div></div>

<p>例如：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>v2 相比 v1

Fixed: 18
Regressed: 7
Unchanged Pass: 65
Unchanged Fail: 10
</code></pre></div></div>

<p>这时最重要的问题不是“总分涨了多少”，而是：</p>

<p><strong>那 7 个 Regression 是什么？</strong></p>

<p>如果其中一个是 Critical Case，新版本可能仍然不能发布。</p>

<h2 id="九一个更实用的-eval-scorecard">九、一个更实用的 Eval Scorecard</h2>

<p>我会把结果拆成四组。</p>

<h3 id="quality">Quality</h3>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Overall Success
Core Pass Rate
Risk Pass Rate
Failure Regression Rate
</code></pre></div></div>

<h3 id="reliability">Reliability</h3>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>First-pass Success
pass^k
Retry Rate
Critical Failure
</code></pre></div></div>

<h3 id="verification">Verification</h3>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Verified Success
Evidence Completeness
Judge False Pass
</code></pre></div></div>

<h3 id="efficiency">Efficiency</h3>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>P95 Latency
Cost / Task
Tool Calls
Steps
</code></pre></div></div>

<p>最后再结合具体业务定义 Gate。</p>

<h2 id="十不要急着做总分">十、不要急着做“总分”</h2>

<p>很容易有人想把所有指标加权成：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Quality Score = 87.3
</code></pre></div></div>

<p>这对 Dashboard 可以有帮助，但不要让总分覆盖底层事实。</p>

<p>因为：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Critical Failure = 1
</code></pre></div></div>

<p>不能被：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Latency +3 分
Cost +2 分
</code></pre></div></div>

<p>抵消。</p>

<p>更合理的是：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Hard Gate
+
Diagnostic Metrics
</code></pre></div></div>

<p>Critical Failure、权限违规、已知 Regression 这类指标做 Hard Gate；Latency、Cost、Token 等用于权衡。</p>

<h2 id="十一最终目标不是指标多而是能支持决策">十一、最终目标不是“指标多”，而是能支持决策</h2>

<p>一个指标值不值得留，判断标准很简单：</p>

<p><strong>它会改变你的工程决策吗？</strong></p>

<p>好的 Eval Metrics 应该能够回答：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>哪里变好了？
哪里变差了？
失败是否严重？
结果是否可信？
执行是否稳定？
现在能不能发布？
</code></pre></div></div>

<p>下一篇继续解决另一个常见混淆：</p>

<p><strong>Offline Eval 和 Online Eval 到底分别解决什么问题？</strong></p>

<h2 id="参考资料">参考资料</h2>

<ul>
  <li><a href="https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents">Anthropic — Demystifying evals for AI agents</a></li>
  <li><a href="https://developers.openai.com/api/docs/guides/evaluation-best-practices">OpenAI — Evaluation best practices</a></li>
</ul>]]></content><author><name>hugfeature</name></author><category term="AI-Eval" /><category term="Agent-Eval" /><category term="ai-eval" /><category term="eval-metrics" /><category term="agent-eval" /><category term="pass-at-k" /><category term="reliability" /><summary type="html"><![CDATA[AI Eval 不能只看 Pass Rate。本文拆解 Task Success、pass@k、pass^k、Critical Failure、False Pass、Latency、Cost、Retry、Evidence 与 Regression Delta。]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://hugfeature.github.io/assets/social-card.png" /><media:content medium="image" url="https://hugfeature.github.io/assets/social-card.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">LLM-as-a-Judge 怎么做才靠谱？从 Rubric、偏差到人工校准</title><link href="https://hugfeature.github.io/eval/llm-as-a-judge/" rel="alternate" type="text/html" title="LLM-as-a-Judge 怎么做才靠谱？从 Rubric、偏差到人工校准" /><published>2026-09-23T09:35:00+08:00</published><updated>2026-09-23T09:35:00+08:00</updated><id>https://hugfeature.github.io/eval/llm-as-a-judge</id><content type="html" xml:base="https://hugfeature.github.io/eval/llm-as-a-judge/"><![CDATA[<blockquote>
  <p><strong>AI Eval 系列 #05</strong><br />
上一篇：<a href="/eval/how-to-build-business-eval-dataset/">怎样构造一套真正有用的业务 Eval Dataset？</a></p>
</blockquote>

<p>开放式任务最麻烦的地方是：</p>

<p><strong>没有唯一标准答案。</strong></p>

<p>摘要、分析、测试用例、代码 Review、方案设计，都很难用 Exact Match 判断。</p>

<p>于是很多团队会自然走到：</p>

<p><strong>LLM-as-a-Judge。</strong></p>

<p>让一个更强模型来评价另一个模型的输出。</p>

<p>这个思路是可行的。</p>

<p>但前提是：</p>

<p><strong>Judge 也必须被测试。</strong></p>

<p>否则只是把“被测模型的不确定性”，转移成“评分模型的不确定性”。</p>

<h2 id="一先判断任务是否真的需要-llm-judge">一、先判断任务是否真的需要 LLM Judge</h2>

<p>能不用 Judge，就先不用。</p>

<p>优先级我会这样排：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Deterministic Check
↓
Executable Check
↓
Rule / Schema
↓
Reference-based Metric
↓
LLM Judge
↓
Human Review
</code></pre></div></div>

<p>例如：</p>

<p>JSON 格式正确不正确，用 Schema。</p>

<p>代码能不能运行，用 Test。</p>

<p>SQL 对不对，用真实数据库执行结果。</p>

<p>Tool 参数是否合法，用规则。</p>

<p>只有这些方法无法覆盖“质量”的时候，再引入 LLM Judge。</p>

<h2 id="二llm-judge-最适合什么任务">二、LLM Judge 最适合什么任务</h2>

<p>比较适合：</p>

<ul>
  <li>完整性；</li>
  <li>相关性；</li>
  <li>正确性；</li>
  <li>风格；</li>
  <li>摘要质量；</li>
  <li>解释质量；</li>
  <li>方案覆盖度；</li>
  <li>测试点覆盖度。</li>
</ul>

<p>例如测试用例生成：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>输入：
PRD

输出：
测试用例

Judge：
是否覆盖核心业务规则？
是否包含异常路径？
是否遗漏关键权限条件？
</code></pre></div></div>

<p>这种任务很难写成 Exact Match。</p>

<p>但可以通过明确 Rubric 判断。</p>

<h2 id="三最关键的不是模型而是-rubric">三、最关键的不是模型，而是 Rubric</h2>

<p>一个差的 Judge Prompt：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>请给这个回答打 1～10 分。
</code></pre></div></div>

<p>基本等于：</p>

<p><strong>请凭感觉打分。</strong></p>

<p>更好的方式是先定义明确维度。</p>

<p>例如：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>正确性：
是否存在事实错误？

完整性：
是否覆盖所有必需业务规则？

可执行性：
测试步骤是否可以直接执行？

风险：
是否存在可能导致严重误判的内容？
</code></pre></div></div>

<p>甚至进一步定义等级：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PASS
满足所有关键要求，无严重错误

PARTIAL
存在非关键遗漏，但不影响主要结论

FAIL
存在关键事实错误、关键遗漏或安全风险
</code></pre></div></div>

<p>Rubric 越明确，Judge 越稳定。</p>

<h2 id="四pairwise-往往比绝对打分稳定">四、Pairwise 往往比绝对打分稳定</h2>

<p>OpenAI 的评测最佳实践建议，在适合的场景里优先考虑比较、分类或通过/失败，而不是让模型做完全开放的绝对评分。</p>

<p>原因很简单。</p>

<p>判断：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>A 和 B 哪个更好？
</code></pre></div></div>

<p>通常比：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>A 是 7.2 分还是 7.8 分？
</code></pre></div></div>

<p>更容易。</p>

<p>所以做模型升级评测时，我更喜欢：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Old Model Output
vs
New Model Output

Judge:
A better / B better / Tie
</code></pre></div></div>

<p>然后随机交换 A/B 顺序。</p>

<p>这可以降低位置偏差。</p>

<h2 id="五llm-judge-有哪些典型偏差">五、LLM Judge 有哪些典型偏差</h2>

<h3 id="position-bias">Position Bias</h3>

<p>模型可能偏好第一个或第二个答案。</p>

<p>解决：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>A vs B
B vs A
</code></pre></div></div>

<p>双向评一次。</p>

<h3 id="verbosity-bias">Verbosity Bias</h3>

<p>模型可能偏好更长、更详细的答案。</p>

<p>因此 Rubric 里应该明确：</p>

<p><strong>长度本身不加分。</strong></p>

<h3 id="style-bias">Style Bias</h3>

<p>语言更漂亮，不代表业务更正确。</p>

<p>所以要把：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Correctness
Completeness
Style
</code></pre></div></div>

<p>拆开评分。</p>

<h3 id="self-preference">Self-preference</h3>

<p>某些 Judge 对和自身风格类似的输出可能有偏好。</p>

<p>所以重要任务不能只依赖单一 Judge。</p>

<h2 id="六judge-必须和人工标签校准">六、Judge 必须和人工标签校准</h2>

<p>这是最关键的一步。</p>

<p>先准备一小批人工 Gold：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>100 Cases

Human:
PASS / PARTIAL / FAIL
</code></pre></div></div>

<p>再让 Judge 评分。</p>

<p>比较：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Human
vs
Judge
</code></pre></div></div>

<p>至少看：</p>

<ul>
  <li>Agreement；</li>
  <li>Critical Error Recall；</li>
  <li>False Pass；</li>
  <li>False Fail。</li>
</ul>

<p>尤其关注：</p>

<p><strong>False Pass。</strong></p>

<p>因为 Judge 把真正错误的结果判成 PASS，是最危险的。</p>

<h2 id="七不要只看-agreement">七、不要只看 Agreement</h2>

<p>例如：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Human / Judge Agreement = 90%
</code></pre></div></div>

<p>看起来不错。</p>

<p>但如果 10% 的分歧全部来自“严重错误被 Judge 判 PASS”，那这个 Judge 仍然不能用于 Gate。</p>

<p>所以最好按风险分层：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Overall Agreement
Critical Case Agreement
False Pass Rate
False Fail Rate
</code></pre></div></div>

<p>如果用于发布门禁，我会把：</p>

<p><strong>Critical False Pass</strong></p>

<p>单独作为硬指标。</p>

<h2 id="八一个实用-judge-输出格式">八、一个实用 Judge 输出格式</h2>

<p>Judge 不要只返回分数。</p>

<p>建议输出：</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"verdict"</span><span class="p">:</span><span class="w"> </span><span class="s2">"FAIL"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"dimensions"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"correctness"</span><span class="p">:</span><span class="w"> </span><span class="s2">"PASS"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"completeness"</span><span class="p">:</span><span class="w"> </span><span class="s2">"FAIL"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"risk"</span><span class="p">:</span><span class="w"> </span><span class="s2">"FAIL"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"evidence"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="s2">"遗漏审批金额阈值条件"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"未覆盖越权操作"</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"confidence"</span><span class="p">:</span><span class="w"> </span><span class="s2">"high"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>这样后面才能做：</p>

<ul>
  <li>Failure Analysis；</li>
  <li>Regression；</li>
  <li>人工 Review；</li>
  <li>Judge Debug。</li>
</ul>

<p>如果只保存：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>score = 6.5
</code></pre></div></div>

<p>几乎没有排查价值。</p>

<h2 id="九judge-本身也要做-regression">九、Judge 本身也要做 Regression</h2>

<p>Judge Prompt 改了。</p>

<p>Judge Model 升级了。</p>

<p>Rubric 改了。</p>

<p>都可能导致评分体系变化。</p>

<p>所以 Judge 也应该有自己的测试集：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Judge Dataset

Case
Expected Verdict
Expected Critical Error
</code></pre></div></div>

<p>每次升级 Judge：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Judge v1
vs
Judge v2
</code></pre></div></div>

<p>先验证与 Human Gold 的一致性是否改善。</p>

<p>这一步非常像：</p>

<p><strong>测试评测系统本身。</strong></p>

<h2 id="十最终最好是多层-grader">十、最终最好是多层 Grader</h2>

<p>成熟一些的 Eval，不应该只有一个 Judge。</p>

<p>例如：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Schema Check
      ↓
Executable Check
      ↓
Rule Check
      ↓
LLM Judge
      ↓
Human Sampling
</code></pre></div></div>

<p>能被确定性验证的，先确定性验证。</p>

<p>Judge 只负责那些必须依赖语义判断的部分。</p>

<p>人工则负责：</p>

<ul>
  <li>校准 Judge；</li>
  <li>审核高风险分歧；</li>
  <li>发现 Rubric 缺陷。</li>
</ul>

<p>这比“所有东西都让 LLM 打分”可靠得多。</p>

<h2 id="十一从-judge-到-reliability">十一、从 Judge 到 Reliability</h2>

<p>LLM-as-a-Judge 真正的价值，不是生成一个漂亮分数。</p>

<p>而是帮助你把原本只能靠人肉抽查的开放任务，变成：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>可规模运行
可重复比较
可定位失败
可持续回归
</code></pre></div></div>

<p>但前提永远是：</p>

<p><strong>Judge 本身有 Rubric、有 Gold、有校准、有 Regression。</strong></p>

<p>否则：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>LLM
评
LLM
</code></pre></div></div>

<p>并不会自动产生可靠性。</p>

<p>下一篇可以继续进入工程实现：</p>

<p><strong>一套 Eval 到底应该记录哪些指标？为什么 Pass Rate 远远不够？</strong></p>

<h2 id="参考资料">参考资料</h2>

<ul>
  <li><a href="https://developers.openai.com/api/docs/guides/evaluation-best-practices">OpenAI — Evaluation best practices</a></li>
  <li><a href="https://developers.openai.com/api/docs/guides/evaluation-getting-started">OpenAI — Getting started with datasets</a></li>
  <li><a href="https://crfm.stanford.edu/helm/">Stanford CRFM — HELM</a></li>
  <li><a href="https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents">Anthropic — Demystifying evals for AI agents</a></li>
</ul>]]></content><author><name>hugfeature</name></author><category term="AI-Eval" /><category term="Model-Evaluation" /><category term="ai-eval" /><category term="llm-as-a-judge" /><category term="grader" /><category term="rubric" /><category term="human-evaluation" /><summary type="html"><![CDATA[LLM-as-a-Judge 适合开放任务的大规模评测，但不能直接把模型分数当真值。本文给出 Rubric、Pairwise、Reference、偏差控制、人工校准与 Judge Regression 的实用方法。]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://hugfeature.github.io/assets/social-card.png" /><media:content medium="image" url="https://hugfeature.github.io/assets/social-card.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>