<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>17哥</title>
    <link>https://wangwei1237.github.io/</link>
    <language>zh-CN</language>
    <copyright>All rights reserved 2026, Wang Wei</copyright>
    <lastBuildDate>Mon, 12 Oct 2026 02:50:00 GMT</lastBuildDate>
    <generator>Hexo</generator>
    <atom:link href="https://wangwei1237.github.io/feed.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>Harness of Harness 论文学习</title>
      <link>https://wangwei1237.github.io/2026/09/15/Reading-of-the-HoH-paper/</link>
      <description>
        <![CDATA[<html><head></head><body><p><img src="/2026/09/15/Reading-of-the-HoH-paper/1.jpg" alt=""></p>
<p>在 Coding Agent 的实践过程中，我时常在想：</p>
<ul>
<li>有了必要的知识、workspace、skill、tool 等，Coding Agent 是否就足够了呢？</li>
<li>人在面对同样的需求的时候，又是怎么开发的呢？</li>
</ul>
<p>恰好，最近发现上海人工智能实验室发表了论文 <a href="https://flesymeb.github.io/HarnessOfHarness/">Harness of Harness:Multi-Day Autonomous Software Development with Continual Improvement</a>，在论文中，研究者们利用 HoH 在无人工干预的情况下，花了 6 天时间、历经 70 多次 Loop 完成了一个 FPS 游戏。</p></body></html>]]>
      </description>
      <author>Wang Wei</author>
      <category domain="https://wangwei1237.github.io/categories/LLM/">LLM</category>
      <category domain="https://wangwei1237.github.io/tags/Harness-Engineering/">Harness Engineering</category>
      <category domain="https://wangwei1237.github.io/tags/Agent/">Agent</category>
      <category domain="https://wangwei1237.github.io/tags/paper/">paper</category>
      <pubDate>Tue, 15 Sep 2026 11:04:44 GMT</pubDate>
      <content:encoded>
        <![CDATA[<html><head></head><body><p><img src="/2026/09/15/Reading-of-the-HoH-paper/1.jpg" alt=""></p><p>在 Coding Agent 的实践过程中，我时常在想：</p><ul><li>有了必要的知识、workspace、skill、tool 等，Coding Agent 是否就足够了呢？</li><li>人在面对同样的需求的时候，又是怎么开发的呢？</li></ul><p>恰好，最近发现上海人工智能实验室发表了论文 <a href="https://flesymeb.github.io/HarnessOfHarness/">Harness of Harness:Multi-Day Autonomous Software Development with Continual Improvement</a>，在论文中，研究者们利用 HoH 在无人工干预的情况下，花了 6 天时间、历经 70 多次 Loop 完成了一个 FPS 游戏。</p><span id="more"></span><h2 id="给-agent-一个大需求-然后呢？">给 Agent 一个大需求，然后呢？</h2><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">当前 Harness Engineering 的现状</p><p>我们给 AI 一个需求：“做一个能玩的游戏”。第一版出来了，但有些功能没做完，有些地方不能用。接下来，在 Looping 中，Agent 会根据我们提供的知识、工具、测试结果来修复，然后不停的 Looping 整个循环……</p><p>于是，Agent 开始陷入了无休止的局部修复循环……</p><p><strong>接下来，需要人介入，并打破这个循环，并进行新的规划……</strong></p></div><p>在 HoH 的论文中，作者把这个现象称作：<strong>repetitive local repairs</strong>。</p><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">提示</p><p>人类工程师在开发一个需求的时候，不是一蹴而就的，而是每次完成一个小的功能点，然后不停的迭代直到完成整个需求……</p><p>团队在开发产品的时候，也是敏捷迭代开发。</p></div><p>论文中的 Harness of Harness 的核心思想其实就是借鉴了人类工程师开发过程中的小步快跑的迭代思想，并把这套思想抽象成了一层工程 Harness 层。</p><p>与其说是<strong>HoH</strong>，倒不如说是：围绕现有 Coding Agent 的能力（Codex、Claude Code……），对软件开发需求组织持续的规划、开发与测试，让一轮的结果能够指导下一轮，并最终完成需求。</p><h2 id="hoh-的具体应用案例">HoH 的具体应用案例</h2><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">HoH 的概念</p><p><strong>we introduce Harness-of-Harness (HoH), a framework that equips coding agents with continual improvement capabilities for autonomous software development.</strong></p></div><ul><li><strong>continual improvement capabilities</strong>：软件功能的持续增加和迭代</li><li><strong>autonomous</strong>：全自主的开发过程</li></ul><p><img src="/2026/09/15/Reading-of-the-HoH-paper/hoh-overview.png" alt="HoH 持续改进概览"></p><p>上海人工智能实验室团队用 HoH，用时 6 天，迭代了 70 多次，在无人接管的情况下完成一个可玩的 FPS 游戏。</p><p><img src="https://flesymeb.github.io/HarnessOfHarness/assets/figures/fusepoint-real-gameplay-poster.jpg" alt="Fusepoint 实机展示封面"></p><p><em>图：作者官网的 Fusepoint 实机展示封面。<a href="https://flesymeb.github.io/HarnessOfHarness/">打开官网视频</a>。</em></p><p>整个迭代的过程如下图所示：</p><p><img src="/2026/09/15/Reading-of-the-HoH-paper/fusepoint-iteration-progress.png" alt="Fusepoint 开发迭代与问题变化"></p><ul><li><strong>横轴</strong>是迭代次数，纵轴是尚未关闭的问题数量</li><li><strong>横轴上方</strong>的蓝色、紫色标记分别表示新发现问题和已关闭问题</li><li><strong>曲线与浅色点</strong>展示问题数量的趋势与记录值</li><li><strong>截图与标签</strong>标示开发成果，从初始原型、地图和武器，逐步扩展到音效、界面、动画、战斗 AI 和可玩版本。</li></ul><p>在迭代 70 个 Loop 之后，作者发现：开发并非单调改善，验证过的行为仍可能会因为后续的修改而破坏（<strong>人类工程师也存在这个问题</strong>）。</p><ul><li>81 个问题中关闭了 65 个，仍有 16 个未解决</li><li>有 17 个问题曾因回归测试而重新打开</li></ul><p>对如上的图的分析，我们可以得到如下的结论：</p><p><strong>1. 问题数量上升不能直接等同于质量下降。</strong> 一个尚未运行的空项目可能“没有已知问题”，但不代表质量高。新增功能扩大了可测试范围，也会暴露更多缺口。因此，应结合功能完成情况一起解读曲线。</p><p><strong>2. 问题关闭也不等于永久解决。</strong> 长期迭代需要保留验证历史，并持续检查回归。否则，下一轮可能在不知道的情况下破坏上一轮成果。</p><h2 id="hoh-管的是整个开发过程">HoH 管的是整个开发过程</h2><p>HoH 负责的其实是整个开发过程，也就是整个敏捷迭代过程中的非开发部分：</p><ul><li>拆分目标任务</li><li>评估具体整体目标的差距</li><li>独立验收</li><li>……</li></ul><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">两层 Harness，而不是对原有 Harness 的再次约束</p><p>HoH 部分的内容，恰恰是我们目前的 Workspace 中的知识、rules、Skills 中所缺失的内容。而 HoH 所做的就是把这一层也给工程化、Harness 化。</p></div><pre class="mermaid">%%{init: {"flowchart": {"nodeSpacing": 35, "rankSpacing": 25}, "themeVariables": {"fontSize": "16px"}}}%%flowchart TB    subgraph C["第一层：Codex"]        direction LR        CG["目标<br>执行当前交付的<br>具体任务"]        CP["执行能力<br>理解任务、读取项目、调用工具<br>编写代码、运行测试、检查结果"]        CO["交付结果<br>代码变更、执行结果<br>或分析报告"]        CG --- CP --- CO    end    subgraph H["第二层：HoH"]        direction LR        HG["目标<br>通过多轮迭代<br>逐步实现整体需求"]        HP["组织过程<br>拆分目标 → 开发 → 独立验收<br>根据证据规划下一轮"]        HS["持续保留<br>当前软件、验证证据<br>未解决问题"]        HG --- HP --- HS    end    C --&gt;|"返回结果，支持验收与下一轮决策"| H    H --&gt;|"下达本轮任务与上下文"| C    style H fill:#F2EDFC,stroke:#8064AD,stroke-width:2px    style C fill:#E8F4F1,stroke:#318575,stroke-width:2px</pre><p class="katex-block has-jax"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mo stretchy="false">(</mo><msub><mi>A</mi><mrow><mi>t</mi><mo>−</mo><mn>1</mn></mrow></msub><mo separator="true">,</mo><msub><mi mathvariant="script">E</mi><mrow><mi>t</mi><mo>−</mo><mn>1</mn></mrow></msub><mo stretchy="false">)</mo><mover><mo stretchy="true" minsize="3.0em">→</mo><mpadded width="+0.6em" lspace="0.3em"><mrow><mtext>loop&nbsp;</mtext><mi>t</mi><mtext>&nbsp;under&nbsp;</mtext><mi mathvariant="script">S</mi></mrow></mpadded></mover><mo stretchy="false">(</mo><msub><mi>A</mi><mi>t</mi></msub><mo separator="true">,</mo><msub><mi mathvariant="script">E</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(A_{t-1}, \mathcal{E}_{t-1}) \xrightarrow{\text{loop } t \text{ under } \mathcal{S}} (A_t, \mathcal{E}_t)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.3581em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">A</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">t</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2083em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathcal" style="margin-right:0.0894em;">E</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0894em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">t</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2083em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel x-arrow"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.1081em;"><span style="top:-3.322em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight x-arrow-pad"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">loop&nbsp;</span></span><span class="mord mathnormal mtight">t</span><span class="mord text mtight"><span class="mord mtight">&nbsp;under&nbsp;</span></span><span class="mord mathcal mtight" style="margin-right:0.075em;">S</span></span></span></span><span class="svg-align" style="top:-2.689em;"><span class="pstrut" style="height:2.7em;"></span><span class="hide-tail" style="height:0.522em;min-width:1.469em;"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="0.522em" viewBox="0 0 400000 522" preserveAspectRatio="xMaxYMin slice"><path d="M0 241v40h399891c-47.3 35.3-84 78-110 128-16.7 32-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20 11 8 0 13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7 39-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85-40.5-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67 151.7 139 205zm0 0v40h399900v-40z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.011em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">A</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathcal" style="margin-right:0.0894em;">E</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em;"><span style="top:-2.55em;margin-left:-0.0894em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span></p><ul><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>A</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">A_t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">A</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> denote the software artifact state after loop <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow><annotation encoding="application/x-tex">t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6151em;"></span><span class="mord mathnormal">t</span></span></span></span></li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="script">E</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">\mathcal{E}_t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathcal" style="margin-right:0.0894em;">E</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em;"><span style="top:-2.55em;margin-left:-0.0894em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> denote the execution evidence state obtained by evaluating <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>A</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">A_t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">A</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span></li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="script">S</mi></mrow><annotation encoding="application/x-tex">\mathcal{S}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathcal" style="margin-right:0.075em;">S</span></span></span></span> denote the software specification, PRD</li></ul><p>整个 HoH 的流程如下图所示：</p><p><img src="https://flesymeb.github.io/HarnessOfHarness/assets/figures/hoh-method-framework.webp" alt="HoH 架构图"></p><p><em>左上需求 → 上方 Planner → 右下 Developer → 左下 QA → 返回 Planner。</em></p><p>可以用“执行环境”和“项目组织”来理解两层分工：</p><figure class="highlight text"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">第二层：HoH</span><br><span class="line">决定本轮目标 → 安排开发 → 组织验收 → 根据结果重新规划</span><br><span class="line">                         │</span><br><span class="line">                         ▼</span><br><span class="line">第一层：已有 coding harness</span><br><span class="line">读文件、使用工具、修改代码、运行与检查程序</span><br></pre></td></tr></tbody></table></figure><p>图中央的 harness 是执行基础，外圈负责组织其参与整个项目。三个角色可以使用同一个模型，分别承担规划、开发与验收职责。</p><p>HoH 通过三个角色组织每轮迭代的开发：</p><table><thead><tr><th>角色</th><th>通俗理解</th><th>每轮交付</th></tr></thead><tbody><tr><td>Project Planner</td><td>想清楚这一轮值得做什么，基于上次迭代的产物和评估来规划</td><td>开发文档</td></tr><tr><td>Developer</td><td>把这一轮目标做出来</td><td>更新后的项目</td></tr><tr><td>QA Tester</td><td>实际检查做到什么程度</td><td>证据包</td></tr></tbody></table><p>Project Planner 从之前的迭代证据中生成本轮计划：</p><table><thead><tr><th>发现什么</th><th>计划怎么变</th><th>用人话说</th></tr></thead><tbody><tr><td>Unresolved Gaps</td><td>Prioritized Task Scope</td><td>没完成的事情，挑重要的做</td></tr><tr><td>Verified Behaviors</td><td>Preservation Constraints</td><td>已经能用的东西，别改坏</td></tr><tr><td>Task Specification</td><td>Validation Requirements</td><td>什么现象出现，才算做完</td></tr></tbody></table><p><strong>因此，开发计划不只是待办清单，还必须写“保留什么”和“怎么验收”。</strong></p><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition caution"><p class="admonition-title">为什么同时保存代码和证据</p><ul><li>代码说明“项目目前有什么”</li><li>证据说明“哪些功能实际验证过，还有哪些问题”</li></ul><p>只看代码，很难知道一个按钮是否真正点过，失败路径是否跑过。</p></div><blockquote><p>Throughout this process, HoH specifies the artifacts and evidence that agents must deliver, but does not prescribe a rigid workflow for producing them.</p></blockquote><h2 id="实际开发中-我们是怎么迭代的？">实际开发中，我们是怎么迭代的？</h2><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">整体需求</p><p><strong>做一个平台跳跃游戏，玩家能移动、跳跃、穿过障碍、到达终点，并重新开始。</strong></p></div><p>这个需求可以按“玩家能完成什么行为”拆成可验证的增量迭代开发。</p><h3 id="第一轮：先有一个能走通的小流程">第一轮：先有一个能走通的小流程</h3><p>目标可以很简单：</p><figure class="highlight text"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">目标：玩家可以移动、跳跃，并从起点走到终点。</span><br><span class="line">范围：单个测试关卡、基础碰撞、终点检测。</span><br><span class="line">验收：从启动游戏到到达终点，实际操作跑通一次。</span><br></pre></td></tr></tbody></table></figure><p>RD 做完后，QA 验收：</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>这里最重要的变化是：下一轮接收到具体问题和已验证行为，不必只依赖一句“基本完成”。</p><h3 id="第二轮：修复问题-同时补齐一段用户体验">第二轮：修复问题，同时补齐一段用户体验</h3><p>根据上轮证据更新计划：</p><figure class="highlight text"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">本轮任务：</span><br><span class="line">- 修复平台边缘穿透。</span><br><span class="line">- 到达终点后显示胜利界面。</span><br><span class="line"></span><br><span class="line">保留约束：</span><br><span class="line">- 已验证的左右移动和跳跃继续有效。</span><br><span class="line"></span><br><span class="line">验收：</span><br><span class="line">- 重放平台边缘跳跃路径。</span><br><span class="line">- 实际到达终点，确认胜利界面出现。</span><br></pre></td></tr></tbody></table></figure><p>整体画面美化可以留到后续轮次，当前范围聚焦于碰撞修复和胜利反馈。</p><p><strong>小增量的价值是让变化和结果能够对应。</strong> 如果一轮同时改碰撞、菜单、存档和地图，失败时就很难知道是哪一部分造成的。</p><h3 id="第三轮：把-可见-补齐为-可用">第三轮：把“可见”补齐为“可用”</h3><p>下一轮目标可以变成：进入胜利状态后停止角色输入，增加重新开始，并恢复初始状态。</p><p>验收需要连续执行：</p><figure class="highlight text"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">启动 → 移动与跳跃 → 到达终点 → 胜利 → 重开 → 再次移动</span><br></pre></td></tr></tbody></table></figure><h3 id="三轮之后-发生了什么？">三轮之后，发生了什么？</h3><table><thead><tr><th>轮次</th><th>软件增长</th><th>留给下一轮的知识</th></tr></thead><tbody><tr><td>1</td><td>有基础可玩路径</td><td>哪些控制正常、哪里会穿透</td></tr><tr><td>2</td><td>碰撞修复、完成提示出现</td><td>胜利状态仍有交互缺口</td></tr><tr><td>3</td><td>胜利与重开形成完整流程</td><td>基础闭环可作为后续关卡的保留要求</td></tr></tbody></table><p>需求仍是原来的需求，但实现顺序会随着观察结果调整。这就是“大需求逐步完成”最容易理解的过程。</p><h2 id="实验：结构化循环比简单续跑好吗？">实验：结构化循环比简单续跑好吗？</h2><h3 id="先看主结果">先看主结果</h3><p>论文比较了三种 harness–model 配置。<a href="https://flesymeb.github.io/HarnessOfHarness/">官方结果表</a>：</p><table><thead><tr><th>配置</th><th style="text-align:right">GameCraft 游戏质量分</th><th style="text-align:right">FrontierSWE Dominance</th><th style="text-align:right">ProgramBench 平均测试通过率</th></tr></thead><tbody><tr><td>Codex + GPT-5.5</td><td style="text-align:right">49.58 → <strong>71.52</strong></td><td style="text-align:right">44% → <strong>71%</strong></td><td style="text-align:right">60.41% → <strong>66.50%</strong></td></tr><tr><td>OpenCode + DeepSeek-V4-Pro</td><td style="text-align:right">26.90 → <strong>48.98</strong></td><td style="text-align:right">25% → <strong>44%</strong></td><td style="text-align:right">45.27% → <strong>57.56%</strong></td></tr><tr><td>Pi + MiniMax-M3</td><td style="text-align:right">42.16 → <strong>58.78</strong></td><td style="text-align:right">35% → <strong>64%</strong></td><td style="text-align:right">35.83% → <strong>52.68%</strong></td></tr></tbody></table><h3 id="最关键的追问：是不是因为多花了-token？">最关键的追问：是不是因为多花了 token？</h3><p>下面是论文对 Codex 的开发次数对照，均使用 GameCraft 质量分。<a href="https://arxiv.org/html/2609.01481v1#S4.SS3">论文表 2</a></p><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition info"><p class="admonition-title">HoH 的 Tokens 消耗量会更多，但是效果也更好。</p><p>HoH 的 Tokens 消耗量会更多，但是效果也更好。</p></div><table><thead><tr><th>方法</th><th>开发迭代轮次</th><th>得分</th><th>Token 数量（百万）</th></tr></thead><tbody><tr><td>Vanilla</td><td>1</td><td>49.58</td><td>2.59</td></tr><tr><td>Vanilla Continuation</td><td>2</td><td>54.99</td><td>4.56</td></tr><tr><td>Vanilla Continuation</td><td>3</td><td>58.24</td><td>6.33</td></tr><tr><td>HoH</td><td>1</td><td>59.71</td><td>2.88</td></tr><tr><td>HoH</td><td>2</td><td>64.84</td><td>5.67</td></tr><tr><td>HoH</td><td>3</td><td>71.52</td><td>8.41</td></tr></tbody></table><h2 id="我们可以借鉴什么？">我们可以借鉴什么？</h2><p>从工程分类上，可以把 HoH 理解为面向软件开发的一种具体循环设计。它的启发在于：<strong>为每轮规定清楚输入、交付和证据，让后续工作有可靠依据。</strong></p><ul><li><strong>这一轮做什么？</strong> 从整体需求和实际缺口中选择有限、完整的目标。</li><li><strong>哪些旧功能不能坏？</strong> 把已验证行为写入保留约束。</li><li><strong>拿什么证明完成？</strong> 用运行结果和可复查的证据支持验收。</li></ul><p>代码与证据共同积累，下一轮规划时，根据上一轮的迭代信息、整体的目标信息综合规划，而不仅仅是 TO-DO List。</p></body></html>]]>
      </content:encoded>
    </item>
    <item>
      <title>WikiSkill 论文学习</title>
      <link>https://wangwei1237.github.io/2026/08/31/Reading-of-the-Google-s-WikiSkill-Paper/</link>
      <description>
        <![CDATA[<html><head></head><body><p><img src="/2026/08/31/Reading-of-the-Google-s-WikiSkill-Paper/1.jpg" alt=""></p>
<p>谷歌刚发布的关于 <a href="https://arxiv.org/abs/2608.27454v1">WikiSkill: Compiling Agent Experience into Persistent Knowledge for Skill Evolution</a> 的论文值得认真的研究一下。</p>
<p>很多人都会说：这里面提到的好多观点我之前就提出过了。但是更为重要的是：谷歌没有停留在观点的浅薄层面，而是从实证的角度给出了可实践的理论支撑。</p></body></html>]]>
      </description>
      <author>Wang Wei</author>
      <category domain="https://wangwei1237.github.io/categories/LLM/">LLM</category>
      <category domain="https://wangwei1237.github.io/tags/Agent/">Agent</category>
      <category domain="https://wangwei1237.github.io/tags/Skill-Evolution/">Skill Evolution</category>
      <category domain="https://wangwei1237.github.io/tags/paper/">paper</category>
      <pubDate>Mon, 31 Aug 2026 10:12:45 GMT</pubDate>
      <content:encoded>
        <![CDATA[<html><head></head><body><p><img src="/2026/08/31/Reading-of-the-Google-s-WikiSkill-Paper/1.jpg" alt=""></p><p>谷歌刚发布的关于 <a href="https://arxiv.org/abs/2608.27454v1">WikiSkill: Compiling Agent Experience into Persistent Knowledge for Skill Evolution</a> 的论文值得认真的研究一下。</p><p>很多人都会说：这里面提到的好多观点我之前就提出过了。但是更为重要的是：谷歌没有停留在观点的浅薄层面，而是从实证的角度给出了可实践的理论支撑。</p><span id="more"></span><p>论文中的两个分离蛮值的思考的：</p><ol><li>在 Skills 进化的过程中，把原始执行路径、知识库、Skills 进化分离开来；</li><li>在 Skills 进化的过程中，把 Skills 发现 与 Skills 执行这两个阶段分离开来；</li></ol><p>同时，论文中提出的：Skills 进化与 LLM Scaling 的互补性、Skills 进化的可迁移性也是我们在 Agent 自进化过程中特别需要关注的地方。</p><h2 id="术语">术语</h2><ul><li>Self-evolved skills：模型 A 从自己的轨迹中演化 Skill，模型 A 再使用这个 Skill 执行任务。</li><li>Ablation studies（消融研究）：刻意去掉模型或系统中的某个组件、模块、特征、损失项，观察性能下降多少，以此证明这个组件是否有用、贡献有多大。简单来说，就是拿掉一部分，观察效果相差多少，从而验证它的价值。</li><li>Procedural knowledge：操作、流程、步骤、技能和执行序列；告诉 Agent <strong>如何完成一件事</strong>，是一种可执行的知识。</li></ul><h2 id="abstract">Abstract</h2><blockquote><p>Agent skills package <strong>specialized knowledge and workflows</strong> into reusable resources that extend AI agent capabilities. Recent work <strong>automatically discovers such skills</strong> from agent experience, which enables agents to progressively adapt through interaction. However, the <strong>insights that guide skill development</strong> typically remain scattered across optimization histories, limiting their systematic reuse across iterations.</p></blockquote><ul><li>Insights：并非泛指所有运行日志，而是从成功或失败中得到的、对未来有用的认识。原始轨迹是事实，Insight 则是从事实中抽象出的可复用规律。例如：<ul><li>Agent 为什么失败；</li><li>哪种操作顺序更可靠；</li><li>哪类错误反复出现；</li><li>某个 Skill 规则为什么有效；</li><li>某个修改为什么被拒绝；</li><li>某个方案只适用于小模型，还是具有普遍性。</li></ul></li></ul><figure class="highlight text"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">表面现象：</span><br><span class="line">Agent 重复拿起、检查、放回同一个物品。</span><br><span class="line"></span><br><span class="line">Insight：</span><br><span class="line">Agent 没有记录“某个物品已经执行过哪些操作”，</span><br><span class="line">所以它会把同一状态误判成新的任务进展。</span><br><span class="line"></span><br><span class="line">可执行规则：</span><br><span class="line">每种操作对同一物品最多执行一次。</span><br></pre></td></tr></tbody></table></figure><ul><li>Scattered across optimization histories：指 Skill 经历多轮迭代优化后，有用信息可能散落在不同地方。例如：</li></ul><figure class="highlight text"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">第 1 轮失败轨迹：发现 Agent 会重复操作</span><br><span class="line">  → 信息位于 trace</span><br><span class="line"></span><br><span class="line">第 2 轮分析日志：怀疑缺少状态追踪</span><br><span class="line">  → 信息位于分析器输出</span><br><span class="line"></span><br><span class="line">第 3 轮 Skill 提案：加入一个抽象的“面向目标行动”规则</span><br><span class="line">  → 信息位于 Skill diff</span><br><span class="line"></span><br><span class="line">第 3 轮验证记录：该规则没有提高成绩，被拒绝</span><br><span class="line">  → 信息位于拒绝记录</span><br></pre></td></tr></tbody></table></figure><blockquote><p>We introduce WikiSkill, a framework that co-evolves agent skills with a persistent knowledge base (wiki). At a high level, WikiSkill <strong>separates raw execution experience, accumulated knowledge, and executable skills</strong>, while continuously consolidating experience into the wiki, which subsequent skill updates can build on.</p></blockquote><pre class="mermaid">flowchart LR    A[原始执行轨迹] --&gt;|持续沉淀整合| B[Wiki 知识库]    C[可执行的 Skill] --&gt;|产生执行轨迹| A    B --&gt;|为后续 Skill 升级提供基础| C    subgraph WikiSkill        A        B        C    end</pre><blockquote><p>We find that skill evolution complements model scaling: larger models generally benefit more from evolved skills, while smaller models with skills can outperform substantially larger models without them.</p></blockquote><p>Skill 演化与模型 Scaling 之间具有互补关系，而非替代关系：更大的模型通常能从不断进化的 Skill 中获得更多收益。同时，具有优质 Skill 的小模型，也可能超过没有对应 Skill、参数规模更大的模型。对于 LLM + Skills 范畴下的 Agent，其能力提升可以表示为：</p><p class="katex-block has-jax"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>Agent_Capability</mtext><mo>=</mo><mtext>LLM_Scaling</mtext><mo>×</mo><mtext>Skills_Evolution</mtext></mrow><annotation encoding="application/x-tex">\text{Agent\_Capability} = \text{LLM\_Scaling} \times \text{Skills\_Evolution}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0044em;vertical-align:-0.31em;"></span><span class="mord text"><span class="mord">Agent_Capability</span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.0044em;vertical-align:-0.31em;"></span><span class="mord text"><span class="mord">LLM_Scaling</span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1.0044em;vertical-align:-0.31em;"></span><span class="mord text"><span class="mord">Skills_Evolution</span></span></span></span></span></span></p><table><thead><tr><th>能力来源</th><th>主要提供什么</th></tr></thead><tbody><tr><td>LLM Scaling</td><td>更强的理解、推理、规划、代码生成和指令执行能力</td></tr><tr><td>Skills Evolution</td><td>特定任务的流程、经验、失败规避规则和工具使用方法</td></tr></tbody></table><p>模型越大，越能释放 Skill 的能力。经验越丰富的工程师，越能理解条件、组合步骤并处理异常。因此，面对同样的操作手册时，他们更可能取得优秀的结果。</p><blockquote><p>We also find that evolved skills transfer effectively across models and model families, and skills evolved by other models can outperform self-evolved skills.</p></blockquote><p>Skill 不是模型内部参数的一部分，而是一种可以独立保存、共享和复用的外部程序性知识。因此，Skill 演化表现出较好的跨模型迁移能力。“跨模型迁移”意味着 Skill 学到的不只是某个模型的临时行为，而是模型无关、任务相关的程序性知识。</p><p>其他模型演化出的 Skill 可能更好，这意味着：对于大模型而言，“发现知识”和“执行知识”是两种不同的能力。最优秀的操作手册作者和最优秀的操作人员不一定是同一个人。</p><p>因此，在实际应用中，我们更应该将“Skill 演化”和“Skill 执行”区分开：用更适合知识发现的模型实现“Skill 演化”，用具备更强理解和执行能力的模型“执行 Skill”。</p><pre class="mermaid">flowchart LR    A["模型 A<br>发现经验"] --&gt; B["提炼 Skill"]    B --&gt; C["模型 B<br>执行 Skill"]    C --&gt; D["完成任务"]    E["不必是同一个模型"]    E -.-&gt; B    E -.-&gt; C    style B fill:#e8f3ff,stroke:#4285f4    style C fill:#e9f7ef,stroke:#34a853    style E fill:#f3e8fd,stroke:#9334e6</pre><blockquote><p>Finally, our ablation studies confirm that persistent knowledge accumulation in the wiki is critical for effective skill evolution.</p></blockquote><h2 id="introduction">Introduction</h2><blockquote><p>Developing effective skills, however, remains challenging. Most agent skills are <strong>manually authored</strong>, which requires anticipating the procedural knowledge and workflows that an agent will need. <strong>This challenge</strong> motivates recent work that iteratively develops agent skills by executing agents on training tasks, analyzing successful and failed trajectories, and refining skills based on the resulting experience.</p></blockquote><p>从人工编写 Skill 转向基于训练任务自动生成 Skill。</p><blockquote><p>A key design question is how to preserve and organize what an agent learns throughout skill evolution. However, these methods do not maintain what has been learned as <strong>a separate, evolving knowledge representation</strong>.</p></blockquote><ul><li>EvoSkill</li><li>Trace2Skill</li><li>SkillOpt</li></ul><blockquote><p><strong>Can agent experience be similarly compiled into persistent knowledge to support long-term skill evolution?</strong> We introduce WikiSkill, which adds a structured knowledge layer between raw experience and executable procedures. WikiSkill organizes the agent workspace into three layers: a Raw Layer that stores immutable execution traces, a Wiki Layer that maintains structured knowledge, and a Skill Layer that contains evolving <strong>procedural knowledge</strong>.</p></blockquote><p><img src="/2026/08/31/Reading-of-the-Google-s-WikiSkill-Paper/wiki-skill-architecture.png" alt=""></p><blockquote><p>These results suggest that skill discovery and skill execution are distinct capabilities.</p></blockquote></body></html>]]>
      </content:encoded>
    </item>
    <item>
      <title>Harness‑Bench 论文学习</title>
      <link>https://wangwei1237.github.io/2026/08/22/Reading-of-the-HarnessBenchPaper/</link>
      <description>
        <![CDATA[<html><head></head><body><p><img src="/2026/08/22/Reading-of-the-HarnessBenchPaper/harness-bench-framework.png" alt=""></p>
<p>2026 年 5 月，北京大学与启元科技的研究团队发布了论文 <a href="https://arxiv.org/abs/2605.27922">Harness-Bench: Measuring Harness Effects across Models in Realistic Agent Workflows</a>，用于研究并评测不同 Harness 对 Agent 实际执行能力的影响。在论文中，作者提出了 Harness-Bench——一个包含 106 个沙箱化离线任务、用于在统一任务环境下比较不同 Model–Harness 配置的评测基准。论文发现，Agent 表现不仅取决于 LLM，也取决于 Harness 的执行机制。</p>
<p>恰好最近在研究 Agent 评测，于是对这篇论文进行了精度，并做了如下的摘要。</p></body></html>]]>
      </description>
      <author>Wang Wei</author>
      <category domain="https://wangwei1237.github.io/categories/LLM/">LLM</category>
      <category domain="https://wangwei1237.github.io/tags/paper/">paper</category>
      <category domain="https://wangwei1237.github.io/tags/Harness-Bench/">Harness-Bench</category>
      <category domain="https://wangwei1237.github.io/tags/Agent-%E8%AF%84%E4%BC%B0/">Agent 评估</category>
      <pubDate>Sat, 22 Aug 2026 20:40:51 GMT</pubDate>
      <content:encoded>
        <![CDATA[<html><head></head><body><p><img src="/2026/08/22/Reading-of-the-HarnessBenchPaper/harness-bench-framework.png" alt=""></p><p>2026 年 5 月，北京大学与启元科技的研究团队发布了论文 <a href="https://arxiv.org/abs/2605.27922">Harness-Bench: Measuring Harness Effects across Models in Realistic Agent Workflows</a>，用于研究并评测不同 Harness 对 Agent 实际执行能力的影响。在论文中，作者提出了 Harness-Bench——一个包含 106 个沙箱化离线任务、用于在统一任务环境下比较不同 Model–Harness 配置的评测基准。论文发现，Agent 表现不仅取决于 LLM，也取决于 Harness 的执行机制。</p><p>恰好最近在研究 Agent 评测，于是对这篇论文进行了精度，并做了如下的摘要。</p><span id="more"></span><h2 id="术语">术语</h2><ul><li><strong>oracle-checkability（预言机可检验性 / 自动真值可验证性）</strong>：计算机科学、软件测试以及当前 AI Agent 评测与强化学习（RL）领域中最核心的概念之一。一个任务或问题，是否存在一个确定的、自动化的程序（Oracle / 预言机），能够客观、无歧义、无需人工介入地判定 Agent 的输出究竟是对还是错。</li><li><strong>execution alignment（推理与执行对齐）</strong>：智能体的推理、实际观察到的环境状态、通过工具采取的行动，以及评估器最终检查的成功条件之间，是否始终保持一致。</li><li><strong>fixtures（测试固件 / 预置测试环境）</strong>：预先准备好的测试资源集合，包括初始化数据、配置文件、数据库快照、沙箱状态、依赖文件、环境上下文。作用是让每个测试任务都从完全一致的初始状态开始运行，保证评测可复现。</li><li><strong>agent-computer interface（ACI）</strong>：指的是专门为 AI 智能体设计的一组观察方式、操作方式和反馈格式，使智能体能够理解计算机当前状态，并对计算机采取行动。就像 GUI 帮助人类用户操作计算机一样，ACI 帮助 AI 完成与计算机的交互。<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>ACI</mtext><mo>=</mo><mtext>智能体能看到什么</mtext><mo>+</mo><mtext>智能体能做什么</mtext><mo>+</mo><mtext>计算机如何反馈</mtext></mrow><annotation encoding="application/x-tex">\text{ACI} = \text{智能体能看到什么} + \text{智能体能做什么} + \text{计算机如何反馈}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord text"><span class="mord">ACI</span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.7667em;vertical-align:-0.0833em;"></span><span class="mord text"><span class="mord cjk_fallback">智能体能看到什么</span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.7667em;vertical-align:-0.0833em;"></span><span class="mord text"><span class="mord cjk_fallback">智能体能做什么</span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord text"><span class="mord cjk_fallback">计算机如何反馈</span></span></span></span></span></li></ul><!--more--><h2 id="abstract">Abstract</h2><blockquote><p>Each run records final artifacts, execution traces, usage statistics, and validator outputs, enabling analysis beyond final completion.</p></blockquote><blockquote><p>Our analysis further identifies recurring execution-alignment failures, where plausible reasoning becomes decoupled from tool feedback, workspace state, evidence, or verifiable output contracts.</p></blockquote><p>Agent 的推理过程看起来合理、流畅，甚至“知道应该做什么”，但 Agent 随后执行的操作和最终的产物却没有持续忠实地反映：工具返回的信息、工作区的真实状态、证据、任务规定的验收条件。</p><p>在 Agent 执行任务时，“想明白”不等于“完成任务”。Agent 必须确保每一步推理都由当前环境状态和证据支撑，工具结果会改变后续行动，而且最终意图会落实为符合验收规则、能够被独立验证的产物。</p><ol><li><p><strong>与工具反馈脱节</strong>：Agent 调用工具后，工具返回了错误或异常结果，但 Agent 没有根据异常信息调整执行计划，依旧沿着原来的推理继续执行。</p> <figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Agent：接下来我会读取配置文件。</span><br><span class="line">工具：文件不存在。</span><br><span class="line">Agent：接下来我将修改这个配置文件。</span><br></pre></td></tr></tbody></table></figure></li><li><p><strong>与工作区状态脱节</strong>：Agent 认为某个文件已经创建、修改或保存，但实际工作区中并没有发生对应变化，或者文件内容与 Agent 所说的不一致。</p></li><li><p><strong>与证据脱节</strong>：Agent 给出了一个结论，但没有获取实际的执行日志、实际的测试结果来支持这个结论，甚至忽略了与结论相矛盾的证据。</p> <figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Agent：测试已经通过，问题已解决。</span><br><span class="line">实际测试执行：测试用例根本就没有执行。</span><br></pre></td></tr></tbody></table></figure></li><li><p><strong>与可验证的输出契约脱节</strong>：用户规定了明确的输出格式、文件结构或验收条件，Agent 生成的结果虽然内容大致合理，但却依旧不满足这些要求。</p> <figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">任务明确要求输出格式：{"answer": "...", "confidence": 0.9}</span><br><span class="line">Agent 输出：我的答案是……我对此比较有信心。</span><br></pre></td></tr></tbody></table></figure></li></ol><h2 id="introduction">Introduction</h2><blockquote><p>In such executable workflows, practical performance depends not only on the underlying model, but also on the system layer that turns model capability into executable action. We refer to this layer as a harness: the mechanism that organizes context, tools, state, permissions, constraints, and recovery to mediate between model outputs and external actions.</p></blockquote><p><img src="/2026/08/22/Reading-of-the-HarnessBenchPaper/harness-layers.png" alt=""><br>模型层是推理能力；其他层是执行能力。目前测评测集涵盖了：模型的静态推理、Agent 在可执行环境下的最终表现、以及标准工作流下的表现。但是，对于其他层（Harness）层的单独覆盖目前依然是缺失的。</p><blockquote><p>Harnesses are therefore central to agent system design: they shape how model capability is exposed, constrained, and realized, affecting completion, cost, safety, robustness, and auditability.</p></blockquote><blockquote><p>However, existing efforts typically evaluate a particular agent system, standardize evaluation infrastructure, or compare heterogeneous agent stacks, rather than systematically varying harness configurations across shared task environments and model backends.</p></blockquote><blockquote><p>Rather than forcing all systems into an identical internal implementation, Harness-Bench fixes the task environment, budget, timeout, and evaluator while preserving each harness’s native execution behavior.</p></blockquote><blockquote><p>The resulting measurements should therefore be interpreted as configuration-level diagnostics of model–harness pairings, not as causal decompositions of individual harness mechanisms. Each run records execution evidence, enabling analysis beyond final completion scores.</p></blockquote><p>Harness-Bench 能告诉我们“哪一种 Model—Harness 组合整体表现更好”，但不能直接告诉我们“究竟是哪一个 Harness 机制导致了这种差异”。</p><p>因此，比较的实际上是类似：</p><p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mtext>GPT-5.4</mtext><mo separator="true">,</mo><mtext>NanoBot&nbsp;配置</mtext><mo stretchy="false">)</mo><mspace width="1em"></mspace><mtext>vs.</mtext><mspace width="1em"></mspace><mo stretchy="false">(</mo><mtext>GPT-5.4</mtext><mo separator="true">,</mo><mtext>Hermes&nbsp;配置</mtext><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(\text{GPT-5.4},\text{NanoBot 配置}) \quad\text{vs.}\quad (\text{GPT-5.4},\text{Hermes 配置})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord text"><span class="mord">GPT-5.4</span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord text"><span class="mord">NanoBot&nbsp;</span><span class="mord cjk_fallback">配置</span></span><span class="mclose">)</span><span class="mspace" style="margin-right:1em;"></span><span class="mord text"><span class="mord">vs.</span></span><span class="mspace" style="margin-right:1em;"></span><span class="mopen">(</span><span class="mord text"><span class="mord">GPT-5.4</span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord text"><span class="mord">Hermes&nbsp;</span><span class="mord cjk_fallback">配置</span></span><span class="mclose">)</span></span></span></span></p><ul><li>Harness-Bench 可以评估出：在这套任务和实验条件下，GPT-5.4 + NanoBot 的整体表现优于 GPT-5.4 + Hermes。</li><li>但不能进一步断言：NanoBot 更好，是因为它的状态管理机制贡献了 8 分，错误恢复机制贡献了 5 分。</li></ul><p>Harness-Bench 是一台“整机检测仪”，能够比较不同“模型 + harness”整机配置的表现，并根据执行轨迹诊断其失败症状；但它不是一套“因果实验”，不能仅凭这些结果确定某个提示词、工具接口或状态管理机制单独造成了多少性能变化。</p><p>Harness-Bench 的整体框架如下所示：</p><p><img src="/2026/08/22/Reading-of-the-HarnessBenchPaper/harness-bench-framework.png" alt=""></p><p>Harness-Bench 的三大贡献：</p><pre class="mermaid">flowchart LR    A["① Benchmark Asset<br><b>基准资源</b><br><br>106 个沙箱化离线任务<br>真实的端到端工作流<br>任务清单 · 测试材料<br>评估器 · 执行轨迹"]    B["② Evaluation Protocol<br><b>评估协议</b><br><br>固定外部任务条件<br>固定预算 · 超时 · 评估器<br>保留 Harness 原生执行行为<br>比较 Model–Harness 配置"]    C["③ Diagnostic Analysis<br><b>诊断分析</b><br><br>5,194 条执行轨迹<br>完成度 · 过程质量<br>执行效率 · 反复出现的失败症状"]    A --&gt;|"提供统一测试基础"| B    B --&gt;|"产生可比较的执行证据"| C    classDef asset fill:#E8F1FF,stroke:#3973C6,color:#14243A,stroke-width:2px;    classDef protocol fill:#E9F8F1,stroke:#2A9365,color:#142E24,stroke-width:2px;    classDef analysis fill:#FFF2E2,stroke:#D77B22,color:#382313,stroke-width:2px;    class A asset;    class B protocol;    class C analysis;</pre><h2 id="related-work">Related Work</h2><blockquote><p>LLM evaluation has progressed from static language and reasoning benchmarks to executable agent benchmarks</p></blockquote><p><strong>executable</strong> 不仅指 LLM 可以调用工具，更为重要的是：Agent 可以改变环境中的对象、改变环境的状态、并且结果具备可验证性。</p><p>LLM 评估已经从测试模型“能否用文本答对问题”，逐渐发展到测试 Agent “能否在真实或模拟环境中采取行动并完成任务”。</p><table><thead><tr><th>评估范式</th><th>静态基准</th><th>可执行基准</th></tr></thead><tbody><tr><td>评估对象</td><td>LLM 能力</td><td>Agent 能力</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><tr><td>代表基准</td><td>MMLU、GSM8K、BIG-bench</td><td>SWE-bench、OSWorld、Terminal-Bench</td></tr></tbody></table><blockquote><p>More recent workflow-agent benchmarks, including AgentBench, GAIA, and Claw-Eval, further emphasize multi-step execution, external state, traceability, safety, robustness, and cost-aware evaluation.</p><p>ClawMark pushes this direction to long-horizon, multimodal coworker settings, coupling multi-turn and multi-day tasks with persistent tool-backed services and drifting external state.</p></blockquote><blockquote><p>These benchmarks are essential for measuring model or end-to-end agent capability, but they do not directly evaluate the harness as the variable of interest: they either abstract away execution, evaluate a complete submitted agent stack, or hold the execution setup fixed to compare models.</p></blockquote><p>目前的评测要么仅评测模型能力，要么仅评测 Agent 的端到端能力，并没有对 Agent 的 harness 层进行直接评测。</p><blockquote><p>Harness-Bench is complementary: it controls external task conditions while varying harness configurations, enabling diagnostic comparison of completion, token cost, execution safety, robustness, and traceability.</p></blockquote><p>Harness-Bench 的实验结构可以表示为：</p><p class="katex-block has-jax"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><munder><munder><mtext>相同任务、环境、预算、评估器</mtext><mo stretchy="true">⏟</mo></munder><mtext>受控的外部条件</mtext></munder><mo>+</mo><munder><munder><mtext>不同&nbsp;Harness&nbsp;配置、Model</mtext><mo stretchy="true">⏟</mo></munder><mtext>实验中变化的部分</mtext></munder></mrow><annotation encoding="application/x-tex">\underbrace{\text{相同任务、环境、预算、评估器}}_{\text{受控的外部条件}} + \underbrace{\text{不同 Harness 配置、Model}}_{\text{实验中变化的部分}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:2.0097em;vertical-align:-1.3263em;"></span><span class="minner munder"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6833em;"><span style="top:-1.6737em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord cjk_fallback mtight">受控的外部条件</span></span></span></span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="minner munder"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6833em;"><span class="svg-align" style="top:-2.352em;"><span class="pstrut" style="height:3em;"></span><span class="stretchy" style="height:0.548em;min-width:1.6em;"><span class="brace-left" style="height:0.548em;"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="0.548em" viewBox="0 0 400000 548" preserveAspectRatio="xMinYMin slice"><path d="M0 6l6-6h17c12.688 0 19.313.3 20 1 4 4 7.313 8.3 10 13 35.313 51.3 80.813 93.8 136.5 127.5 55.688 33.7 117.188 55.8 184.5 66.5.688 0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7-331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z"></path></svg></span><span class="brace-center" style="height:0.548em;"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="0.548em" viewBox="0 0 400000 548" preserveAspectRatio="xMidYMin slice"><path d="M199572 214c100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14 53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3 11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0-5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z"></path></svg></span><span class="brace-right" style="height:0.548em;"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="0.548em" viewBox="0 0 400000 548" preserveAspectRatio="xMaxYMin slice"><path d="M399994 0l6 6v35l-6 11c-56 104-135.3 181.3-238 232-57.3 28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237-174 2.7-5 6-9 10-13 .7-1 7.3-1 20-1h17z"></path></svg></span></span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord text"><span class="mord cjk_fallback">相同任务、环境、预算、评估器</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.648em;"><span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.3263em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:2.0208em;vertical-align:-1.3263em;"></span><span class="minner munder"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6944em;"><span style="top:-1.6737em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord cjk_fallback mtight">实验中变化的部分</span></span></span></span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="minner munder"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6944em;"><span class="svg-align" style="top:-2.352em;"><span class="pstrut" style="height:3em;"></span><span class="stretchy" style="height:0.548em;min-width:1.6em;"><span class="brace-left" style="height:0.548em;"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="0.548em" viewBox="0 0 400000 548" preserveAspectRatio="xMinYMin slice"><path d="M0 6l6-6h17c12.688 0 19.313.3 20 1 4 4 7.313 8.3 10 13 35.313 51.3 80.813 93.8 136.5 127.5 55.688 33.7 117.188 55.8 184.5 66.5.688 0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7-331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z"></path></svg></span><span class="brace-center" style="height:0.548em;"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="0.548em" viewBox="0 0 400000 548" preserveAspectRatio="xMidYMin slice"><path d="M199572 214c100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14 53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3 11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0-5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z"></path></svg></span><span class="brace-right" style="height:0.548em;"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="0.548em" viewBox="0 0 400000 548" preserveAspectRatio="xMaxYMin slice"><path d="M399994 0l6 6v35l-6 11c-56 104-135.3 181.3-238 232-57.3 28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237-174 2.7-5 6-9 10-13 .7-1 7.3-1 20-1h17z"></path></svg></span></span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord text"><span class="mord cjk_fallback">不同</span><span class="mord">&nbsp;Harness&nbsp;</span><span class="mord cjk_fallback">配置、</span><span class="mord">Model</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.648em;"><span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.3263em;"><span></span></span></span></span></span></span></span></span></span></p><blockquote><p>Work on agent-computer interfaces, tool-use protocols such as the Model Context Protocol, stateful and multi-agent frameworks, tracing, guardrails, memory, budget control, and recovery mechanisms reflects growing attention to the infrastructure that <strong>turns model outputs into external actions</strong>.</p></blockquote><p>不同的 Agent 对 ACI 中的不同部分具有不同的实现策略。</p><h2 id="the-harness-bench-benchmark">The Harness-Bench Benchmark</h2><blockquote><p>Harness-Bench is a diagnostic benchmark for studying model–harness configurations in executable agent workflows. Each evaluation consists of a task, a model backend, a harness configuration, a sandboxed environment, and an evaluator. The benchmark fixes external task conditions while varying the harness surrounding the model, and records both final artifacts and execution traces.</p></blockquote><blockquote><p>We use <strong>harness</strong> to denote the system layer that conditions model calls and turns model outputs into actions in an external workspace. A harness may include prompt templates, action formats, context construction, tool invocation, workspace access, permissions, budget control, tracing, and recovery.</p></blockquote><h3 id="harness-level-evaluation-setting">Harness-Level Evaluation Setting</h3><blockquote><p>For each task and model backend, Harness-Bench fixes the user-facing task, initial sandbox state, budget, timeout, and evaluator, while varying the harness configuration.</p><p>We do not force all systems into a common internal policy or runtime. Instead, each harness runs with its native execution behavior under the same task resources and evaluation protocol.</p><p>The resulting measurements should therefore be interpreted as configuration-level diagnostics of model–harness pairings, not as causal decompositions of individual harness mechanisms.</p></blockquote><p>Harness-Bench 可以评测不同的“LLM + harness”配置的整体表现，并根据执行轨迹诊断其失败症状，但无法评测 Agent 整体表现的“实际归因”。Harness-Bench 无法给出究竟是 Agent 中的哪些部分导致了最终的结果。</p><h3 id="task-suite-design-and-validation">Task Suite Design and Validation</h3><blockquote><p>Harness-Bench contains 106 local, sandboxed tasks designed to evaluate end-to-end agent workflows rather than isolated tool calls. Each task requires a deliverable and is paired with an oracle or rubric that checks completion from the final workspace state and, when needed, the execution trace.</p></blockquote><p>评测样本的任务清单组织结构如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">task_id: "006-access-bilibili"</span><br><span class="line">title: "Access A Local Bilibili-Style Mock Page"</span><br><span class="line">class: "Workspace, Tool Use &amp; Multimodal Operations"</span><br><span class="line">prompt_file: "prompt.txt"</span><br><span class="line">fixtures_dir: "fixtures"</span><br><span class="line">oracle_module: "oracle_grade.py"</span><br><span class="line">hooks_module: "hooks.py"</span><br><span class="line">timeout_sec: 900</span><br><span class="line">tags:</span><br><span class="line">  - "browser"</span><br><span class="line">  - "local-http"</span><br><span class="line">  - "web-extraction"</span><br></pre></td></tr></tbody></table></figure><p>Harness-Bench 中覆盖的 8 个 Agent 场景：</p><table><thead><tr><th>Category</th><th style="text-align:right">#</th></tr></thead><tbody><tr><td>Software Engineering &amp; Codebase Maintenance</td><td style="text-align:right">22</td></tr><tr><td>Data, BI &amp; Finance Analytics</td><td style="text-align:right">14</td></tr><tr><td>Workspace, Tool Use &amp; Multimodal Operations</td><td style="text-align:right">15</td></tr><tr><td>Knowledge, Evidence &amp; Retrieval</td><td style="text-align:right">13</td></tr><tr><td>Office &amp; Business Communication</td><td style="text-align:right">12</td></tr><tr><td>Vertical Professional Workflows</td><td style="text-align:right">12</td></tr><tr><td>Long‑running Autonomy &amp; State Adaptation</td><td style="text-align:right">11</td></tr><tr><td>SRE, DevOps &amp; Release Ops</td><td style="text-align:right">7</td></tr><tr><td><strong>Total</strong></td><td style="text-align:right"><strong>106</strong></td></tr></tbody></table><blockquote><p>Each candidate task is manually reviewed before inclusion. We retain tasks only when they satisfy four criteria:</p><p><strong>Realism</strong>, reflecting a plausible user workflow;<br><strong>Solvability</strong>, meaning the task can be completed using the provided sandbox resources;<br><strong>Oracle-checkability</strong>, meaning success can be verified by deterministic checks or a specified rubric;<br><strong>Integrity</strong>, meaning agents cannot obtain credit by reading hidden answers, modifying protected fixtures, or bypassing constraints.</p></blockquote><p>每个任务都必须经过人工审核才能加入到评测集中，每个任务都必须满足 4 个条件：真实；可解决；可判别；正直诚实。</p><h3 id="run-protocol-and-evidence-collection">Run Protocol and Evidence Collection</h3><blockquote><p>Each run produces four sources of evidence: the final workspace state, execution trace, usage statistics, and validator outputs. These support completion scoring, process diagnostics, cost analysis, permission checks, and failure analysis.</p></blockquote><p><img src="/2026/08/22/Reading-of-the-HarnessBenchPaper/run-evidence.png" alt=""></p><p>每个任务的执行都会做如下的操作：</p><p class="katex-block has-jax"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>R</mi><mo>=</mo><mrow><mi mathvariant="normal">R</mi><mi mathvariant="normal">u</mi><mi mathvariant="normal">n</mi></mrow><mo stretchy="false">(</mo><mi>M</mi><mo separator="true">,</mo><mi>H</mi><mo separator="true">,</mo><mi>E</mi><mo separator="true">,</mo><mi>T</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><mspace width="1em"></mspace><mrow><mi mathvariant="normal">T</mi><mi mathvariant="normal">a</mi><mi mathvariant="normal">s</mi><mi mathvariant="normal">k</mi><mi mathvariant="normal">S</mi><mi mathvariant="normal">c</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">r</mi><mi mathvariant="normal">e</mi></mrow><mo>=</mo><mrow><mi mathvariant="normal">E</mi><mi mathvariant="normal">v</mi><mi mathvariant="normal">a</mi><mi mathvariant="normal">l</mi></mrow><mo stretchy="false">(</mo><mi>R</mi><mo separator="true">;</mo><mi>J</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">R = \mathrm{Run}(M, H, E, T), \quad \mathrm{TaskScore} = \mathrm{Eval}(R; J)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.0077em;">R</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathrm">Run</span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.109em;">M</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.0813em;">H</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.0576em;">E</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.1389em;">T</span><span class="mclose">)</span><span class="mpunct">,</span><span class="mspace" style="margin-right:1em;"></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathrm">TaskScore</span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathrm">Eval</span></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0077em;">R</span><span class="mpunct">;</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.0962em;">J</span><span class="mclose">)</span></span></span></span></span></p><p>where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>M</mi></mrow><annotation encoding="application/x-tex">M</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.109em;">M</span></span></span></span> is the model, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>H</mi></mrow><annotation encoding="application/x-tex">H</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.0813em;">H</span></span></span></span> is the harness configuration, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>E</mi></mrow><annotation encoding="application/x-tex">E</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.0576em;">E</span></span></span></span> is the sandboxed environment, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>T</mi></mrow><annotation encoding="application/x-tex">T</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.1389em;">T</span></span></span></span> is the task, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>R</mi></mrow><annotation encoding="application/x-tex">R</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.0077em;">R</span></span></span></span> is the final results, and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>J</mi></mrow><annotation encoding="application/x-tex">J</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.0962em;">J</span></span></span></span> is the evaluator.</p><h3 id="scoring-and-metrics">Scoring and Metrics</h3><p>For task <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>i</mi></mrow><annotation encoding="application/x-tex">i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6595em;"></span><span class="mord mathnormal">i</span></span></span></span>, the overall score is:</p><p class="katex-block has-jax"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mtable rowspacing="0.16em" columnspacing="1em"><mtr><mtd class="mtr-glue"></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><msub><mrow><mi mathvariant="normal">T</mi><mi mathvariant="normal">a</mi><mi mathvariant="normal">s</mi><mi mathvariant="normal">k</mi><mi mathvariant="normal">S</mi><mi mathvariant="normal">c</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">r</mi><mi mathvariant="normal">e</mi></mrow><mi>i</mi></msub><mo>=</mo><msub><mrow><mi mathvariant="normal">S</mi><mi mathvariant="normal">e</mi><mi mathvariant="normal">c</mi><mi mathvariant="normal">u</mi><mi mathvariant="normal">r</mi><mi mathvariant="normal">i</mi><mi mathvariant="normal">t</mi><mi mathvariant="normal">y</mi></mrow><mi>i</mi></msub><mo>⋅</mo><msub><mrow><mi mathvariant="normal">C</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">m</mi><mi mathvariant="normal">p</mi><mi mathvariant="normal">l</mi><mi mathvariant="normal">e</mi><mi mathvariant="normal">t</mi><mi mathvariant="normal">i</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">n</mi></mrow><mi>i</mi></msub><mo>⋅</mo><msub><mrow><mi mathvariant="normal">P</mi><mi mathvariant="normal">r</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">c</mi><mi mathvariant="normal">e</mi><mi mathvariant="normal">s</mi><mi mathvariant="normal">s</mi></mrow><mi>i</mi></msub><mo separator="true">,</mo></mrow></mstyle></mtd><mtd class="mtr-glue"></mtd><mtd class="mml-eqn-num"></mtd></mtr></mtable><annotation encoding="application/x-tex">\begin{equation}\mathrm{TaskScore}_i = \mathrm{Security}_i \cdot \mathrm{Completion}_i \cdot \mathrm{Process}_i,\end{equation}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.2em;vertical-align:-0.35em;"></span><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord"><span class="mord mathrm">TaskScore</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord"><span class="mord"><span class="mord mathrm" style="margin-right:0.0139em;">Security</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2175em;"><span style="top:-2.4559em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2441em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord"><span class="mord mathrm">Completion</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2175em;"><span style="top:-2.4559em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2441em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord"><span class="mord mathrm">Process</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span></span></span><span class="tag"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-2.85em;"><span class="pstrut" style="height:2.84em;"></span><span class="eqn-num"></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span></span></span></span></p><p class="katex-block has-jax"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mtable rowspacing="0.16em" columnspacing="1em"><mtr><mtd class="mtr-glue"></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><msub><mrow><mi mathvariant="normal">P</mi><mi mathvariant="normal">r</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">c</mi><mi mathvariant="normal">e</mi><mi mathvariant="normal">s</mi><mi mathvariant="normal">s</mi></mrow><mi>i</mi></msub><mo>=</mo><mfrac><mrow><msub><mrow><mi mathvariant="normal">R</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">b</mi><mi mathvariant="normal">u</mi><mi mathvariant="normal">s</mi><mi mathvariant="normal">t</mi><mi mathvariant="normal">n</mi><mi mathvariant="normal">e</mi><mi mathvariant="normal">s</mi><mi mathvariant="normal">s</mi></mrow><mi>i</mi></msub><mo>+</mo><msub><mrow><mi mathvariant="normal">T</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">l</mi><mi mathvariant="normal">U</mi><mi mathvariant="normal">s</mi><mi mathvariant="normal">e</mi></mrow><mi>i</mi></msub><mo>+</mo><msub><mrow><mi mathvariant="normal">C</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">n</mi><mi mathvariant="normal">s</mi><mi mathvariant="normal">i</mi><mi mathvariant="normal">s</mi><mi mathvariant="normal">t</mi><mi mathvariant="normal">e</mi><mi mathvariant="normal">n</mi><mi mathvariant="normal">c</mi><mi mathvariant="normal">y</mi></mrow><mi>i</mi></msub></mrow><mn>3</mn></mfrac></mrow></mstyle></mtd><mtd class="mtr-glue"></mtd><mtd class="mml-eqn-num"></mtd></mtr></mtable><annotation encoding="application/x-tex">\begin{equation}\mathrm{Process}_i = \frac{\mathrm{Robustness}_i + \mathrm{ToolUse}_i + \mathrm{Consistency}_i}{3}\end{equation}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:2.0574em;vertical-align:-0.7787em;"></span><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.2787em;"><span style="top:-3.2787em;"><span class="pstrut" style="height:3.3714em;"></span><span class="mord"><span class="mord"><span class="mord"><span class="mord mathrm">Process</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3714em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">3</span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord"><span class="mord mathrm">Robustness</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord"><span class="mord mathrm">ToolUse</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord"><span class="mord mathrm" style="margin-right:0.0139em;">Consistency</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2175em;"><span style="top:-2.4559em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2441em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.7787em;"><span></span></span></span></span></span></span></span><span class="tag"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.2787em;"><span style="top:-3.2787em;"><span class="pstrut" style="height:3.3714em;"></span><span class="eqn-num"></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.7787em;"><span></span></span></span></span></span></span></span></span></p><p>where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi mathvariant="normal">S</mi><mi mathvariant="normal">e</mi><mi mathvariant="normal">c</mi><mi mathvariant="normal">u</mi><mi mathvariant="normal">r</mi><mi mathvariant="normal">i</mi><mi mathvariant="normal">t</mi><mi mathvariant="normal">y</mi></mrow><mi>i</mi></msub><mo>∈</mo><mo stretchy="false">{</mo><mn>0</mn><mo separator="true">,</mo><mn>1</mn><mo stretchy="false">}</mo></mrow><annotation encoding="application/x-tex">\mathrm{Security}_i \in \{0,1\}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9275em;vertical-align:-0.2441em;"></span><span class="mord"><span class="mord"><span class="mord mathrm" style="margin-right:0.0139em;">Security</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2175em;"><span style="top:-2.4559em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2441em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">{</span><span class="mord">0</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">1</span><span class="mclose">}</span></span></span></span> and all non‑binary scores are normalized to <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">[</mo><mn>0</mn><mo separator="true">,</mo><mn>1</mn><mo stretchy="false">]</mo></mrow><annotation encoding="application/x-tex">[0,1]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">[</span><span class="mord">0</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">1</span><span class="mclose">]</span></span></span></span>.</p><p><img src="/2026/08/22/Reading-of-the-HarnessBenchPaper/scoring-metrics.png" alt=""></p><h3 id="harness-dependence">Harness Dependence</h3><blockquote><p>We use <strong>harness dependence</strong> to describe how much a model backend’s performance varies across harness configurations under otherwise shared benchmark conditions.</p></blockquote><blockquote><p>This pattern suggests that stronger models may be more tolerant of differences in prompting, tool interfaces, state management, and recovery behavior.</p></blockquote><p>更强大的模型，对于不同 Harness 之间的兼容性更好。</p></body></html>]]>
      </content:encoded>
    </item>
    <item>
      <title>面向 Coding Agent 的多仓库 Git Worktree</title>
      <link>https://wangwei1237.github.io/2026/07/18/Git-Worktree-Based-on-Multiple-Repositories-for-Coding-Agent/</link>
      <description>
        <![CDATA[<html><head></head><body><p><img src="/2026/07/18/Git-Worktree-Based-on-Multiple-Repositories-for-Coding-Agent/1.png" alt=""></p>
<p>在 <a href="/2026/06/28/It-is-time-to-learn-about-git-worktree/">是时候了解一下 Git worktree 了</a> 一文中，我们介绍了如何在 Codex、Claude Code 等 Coding Agent 中利用 Git worktree 实现并行开发。不过，那篇文章聚焦于单仓库场景。当我们尝试将 worktree 应用于采用多仓库架构的 LLM Arena 平台时，又遇到了一系列新问题。</p>
<p>本文记录我们如何为多仓库场景设计面向 Coding Agent 的 worktree 工作流，以及如何处理子模块初始化、需求编号冲突和多 Agent 编排等问题。</p></body></html>]]>
      </description>
      <author>Wang Wei</author>
      <category domain="https://wangwei1237.github.io/categories/LLM/">LLM</category>
      <category domain="https://wangwei1237.github.io/tags/Worktree/">Worktree</category>
      <category domain="https://wangwei1237.github.io/tags/Multirepo/">Multirepo</category>
      <category domain="https://wangwei1237.github.io/tags/Git-Submodule/">Git Submodule</category>
      <pubDate>Sat, 18 Jul 2026 20:55:42 GMT</pubDate>
      <content:encoded>
        <![CDATA[<html><head></head><body><p><img src="/2026/07/18/Git-Worktree-Based-on-Multiple-Repositories-for-Coding-Agent/1.png" alt=""></p><p>在 <a href="/2026/06/28/It-is-time-to-learn-about-git-worktree/">是时候了解一下 Git worktree 了</a> 一文中，我们介绍了如何在 Codex、Claude Code 等 Coding Agent 中利用 Git worktree 实现并行开发。不过，那篇文章聚焦于单仓库场景。当我们尝试将 worktree 应用于采用多仓库架构的 LLM Arena 平台时，又遇到了一系列新问题。</p><p>本文记录我们如何为多仓库场景设计面向 Coding Agent 的 worktree 工作流，以及如何处理子模块初始化、需求编号冲突和多 Agent 编排等问题。</p><span id="more"></span><h2 id="1-多仓库架构">1. 多仓库架构</h2><p>在 <a href="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/">如何利用 Harness “一句话交付产品功能”？</a> 一文中，我们介绍过 <a href="https://lj.baidu.com/sbs/">LLM Arena 平台</a> 的多仓库架构：使用 Git Submodule 管理多个代码仓库。</p><pre class="mermaid">flowchart LR    subgraph ParentRepo["versus 父仓库"]        direction TB        Root["versus/"]        Config[".gitmodules"]        subgraph ServerPath["versus-server/"]            direction TB            ServerRepo["Gitlink：<br>记录子仓库 commit"]        end        subgraph FrontendPath["versus-fe/"]            direction TB            FrontendRepo["Gitlink：<br>记录子仓库 commit"]        end        Root -.-&gt;|"定义子模块路径和子仓库的 URL"| Config        Root --&gt; ServerRepo        Config -.-&gt;|"path = versus-server<br>url = server repository"| ServerRepo        Config -.-&gt;|"path = versus-fe<br>url = frontend repository"| FrontendRepo        Root --&gt; FrontendRepo    end    Server[versus-server<br> 独立 Git 仓库]    Frontend[versus-fe<br>独立 Git 仓库]    FrontendRepo --&gt; Frontend    ServerRepo --&gt; Server</pre><p>在这种架构下，仅在 <code>versus</code> 父仓库中执行 <code>git worktree add</code>，并不能得到可直接开发的完整环境。父仓库的 worktree 只会创建 <code>versus</code> 的工作目录；其中的 Submodule 仍需单独初始化，并根据父仓库记录的 Gitlink 检出对应的子仓库 commit。</p><p>假设要同时开发两个需求：</p><ul><li>REQ-001</li><li>REQ-002</li></ul><p>并希望形成如下的目录结构：</p><figure class="highlight text"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">├── versus/          # 主仓库</span><br><span class="line">│     ├── versus-server/</span><br><span class="line">│     └── versus-fe/</span><br><span class="line">├── versus-REQ-001/  # REQ-001 的 worktree</span><br><span class="line">│     ├── versus-server/</span><br><span class="line">│     └── versus-fe/</span><br><span class="line">└── versus-REQ-002/  # REQ-002 的 worktree</span><br><span class="line">      ├── versus-server/</span><br><span class="line">      └── versus-fe/</span><br></pre></td></tr></tbody></table></figure><h3 id="1-1-为父仓库创建-worktree">1.1 为父仓库创建 worktree</h3><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> versus</span><br><span class="line"></span><br><span class="line">git worktree add \</span><br><span class="line">  -b req-001 \</span><br><span class="line">  ../REQ-001 \</span><br><span class="line">  origin/master</span><br><span class="line"></span><br><span class="line">git worktree add \</span><br><span class="line">  -b req-002 \</span><br><span class="line">  ../REQ-002 \</span><br><span class="line">  origin/master</span><br></pre></td></tr></tbody></table></figure><p>此时，每个父仓库的 worktree：</p><ul><li>有独立工作目录</li><li>有独立索引和当前分支</li><li>共享父仓库的 Git 对象库</li><li>可以由不同的 Coding Agent 并行操作</li><li><strong>但此时子模块目录可能还是空的，或者尚未初始化</strong></li></ul><p><img src="/2026/07/18/Git-Worktree-Based-on-Multiple-Repositories-for-Coding-Agent/2.png" alt=""></p><h3 id="1-2-在-worktree-中初始化子模块">1.2 在 worktree 中初始化子模块</h3><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> ../REQ-001</span><br><span class="line"></span><br><span class="line">git submodule <span class="built_in">sync</span> --recursive</span><br><span class="line">git submodule update --init --recursive</span><br></pre></td></tr></tbody></table></figure><p>上述命令可以完成子模块初始化，但每次手动执行都很烦琐。如果让 Coding Agent 临时推理并生成操作步骤，不仅会消耗额外的 token，还容易因上下文差异产生不稳定行为。因此，我们编写了 <code>wt.sh</code>，将 worktree 创建、子模块初始化、端口分配和环境配置固化为确定性流程。</p><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition warning"><p class="admonition-title">Git Submodule 的限制</p><p>Git 官方文档指出，多 worktree 场景下的 Submodule 支持仍不完整，并不推荐对 superproject 进行多重检出。因此，这套方案需要结合实际使用的 Git 版本、子模块结构和更新方式进行验证，不能直接视为通用方案。详见 <a href="https://git-scm.com/docs/git-worktree#_bugs">git-worktree 官方文档</a>。</p></div><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># wt.sh - Versus 多需求并行开发 worktree 管理脚本</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># 设计目标:</span></span><br><span class="line"><span class="comment">#   - 每个需求一个独立 Git worktree，代码任务真正并行、互不干扰</span></span><br><span class="line"><span class="comment">#   - 每个 worktree 分配独立端口（后端 9000+N / 前端 4000+N），接口测试可并行</span></span><br><span class="line"><span class="comment">#   - 数据库共享 dev，不隔离；E2E 手动 / 顺序执行</span></span><br><span class="line"><span class="comment">#   - 不改入库业务代码：端口与代理只在 worktree 内本地改 + assume-unchanged</span></span><br><span class="line"><span class="comment">#   - 代码提交复用 scripts/commit.sh（百度 iCode Gerrit 评审流程）</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># 用法:</span></span><br><span class="line"><span class="comment">#   ./scripts/wt.sh new &lt;id&gt;                         # 创建 worktree（id 如 req-030 / REQ-030 / 030）</span></span><br><span class="line"><span class="comment">#   ./scripts/wt.sh list                             # 列出所有 worktree + 端口 + 状态</span></span><br><span class="line"><span class="comment">#   ./scripts/wt.sh go &lt;id&gt;                          # 切换到 worktree（需 shell-init 的 function）</span></span><br><span class="line"><span class="comment">#   ./scripts/wt.sh remove &lt;id&gt; [--keep-branch]      # 删除 worktree（默认连带本地分支）</span></span><br><span class="line"><span class="comment">#   ./scripts/wt.sh info &lt;id&gt;                        # 查看单个 worktree 详情</span></span><br><span class="line"><span class="comment">#   ./scripts/wt.sh current                          # 查看当前目录属于哪个 worktree</span></span><br></pre></td></tr></tbody></table></figure><pre class="mermaid">flowchart TD    WT["wt.sh<br>worktree 编排器"]    Pull["pull-all.sh<br>代码更新"]    Dev["dev.sh<br>固定端口启动服务"]    Status["status.sh<br>代码库状态"]    Commit["commit.sh<br>代码库提交"]    WT --&gt; |实现 wt pull| Pull    WT --&gt; |实现 wt start/stop| Dev    WT --&gt; |实现 wt status| Status    WT --&gt; |实现 wt submit| Commit</pre><h2 id="2-构建-worktree-manager-agent">2. 构建 worktree-manager Agent</h2><p>在 <a href="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/">如何利用 Harness “一句话交付产品功能”？</a> 的“4.2 Harness 闭环设计”一节中，我们使用 6 个 Sub-Agent 实现需求开发闭环。</p><p><img src="https://wangwei1237.github.io/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-16.png" alt=""></p><p>现在，我们还需要把 worktree 管理能力加入整个 Harness 闭环。为此，我们新增了 <code>worktree-manager</code> Agent。</p><p>每个需求都有一个贯穿开发流程的唯一 <code>REQ-ID</code>。引入 <code>worktree-manager</code> 后，<code>REQ-ID</code> 的生成也需要从需求设计阶段前移到 worktree 创建阶段。</p><h3 id="2-1-定义-worktree-manager">2.1 定义 worktree-manager</h3><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">name: <span class="string">"worktree-manager"</span></span><br><span class="line">description: <span class="string">"Use this agent when a new requirement or modification task needs to be started and a dedicated worktree should be created for isolated development. This agent should be triggered BEFORE the requirement-designer agent in the collaboration pipeline, ensuring that each requirement gets its own isolated Git Worktree with independent ports and runtime environment. This is essential for parallel development of multiple requirements without code or port conflicts."</span></span><br><span class="line">---</span><br><span class="line"></span><br><span class="line"><span class="comment">## Operational Procedures</span></span><br><span class="line"><span class="comment">### Step 1: Check Existing Worktrees</span></span><br><span class="line">Before creating anything, check <span class="keyword">if</span> a worktree already exists <span class="keyword">for</span> the given requirement:</span><br><span class="line"></span><br><span class="line">```bash</span><br><span class="line"><span class="comment"># Check current worktree status（在项目根目录执行）</span></span><br><span class="line">bash scripts/wt.sh list</span><br><span class="line">```</span><br><span class="line"></span><br><span class="line">Also verify with git:</span><br><span class="line">```bash</span><br><span class="line">git worktree list</span><br><span class="line">```</span><br><span class="line">......</span><br></pre></td></tr></tbody></table></figure><p>如上所示，<code>worktree-manager</code> 本质上是对 <code>wt.sh</code> 的 Agent 化封装：Agent 负责理解意图和编排步骤，脚本负责执行确定性的底层操作。</p><h3 id="2-2-调整-sub-agent-编排流程">2.2 调整 Sub-Agent 编排流程</h3><p>在原有流程前增加 <code>worktree-manager</code>，具体编排如下：</p><pre class="mermaid">flowchart LR    A0["0. worktree-manager<br>创建隔离 worktree"]    A1["1. requirement-designer<br>生成 PRD"]    A2["2. go-api-implementer<br>实现后端 API"]    A3["3. frontend-engineer<br>实现前端功能"]    A4["4. test-case-designer<br>设计测试用例"]    A5["5. integration-test-runner<br>执行 API 测试"]    A6["6. e2e-test-executor<br>执行 E2E 测试"]    A0 --&gt; A1    A1 --&gt; A2    A2 --&gt; A3    A3 --&gt; A4    A4 --&gt; A5    A5 --&gt; A6</pre><p><img src="/2026/07/18/Git-Worktree-Based-on-Multiple-Repositories-for-Coding-Agent/7-agents.jpg" alt=""></p><p>接下来，我们开启两个 Session，通过不同的 worktree 并行开发两个需求。</p><p><img src="/2026/07/18/Git-Worktree-Based-on-Multiple-Repositories-for-Coding-Agent/3.png" alt=""></p><p><img src="/2026/07/18/Git-Worktree-Based-on-Multiple-Repositories-for-Coding-Agent/4.png" alt=""></p><h2 id="3-req-id-冲突">3. REQ-ID 冲突</h2><p>当我们真正利用 worktree 并行开发多个需求时，新问题出现了：两个 Session 分别进入不同的 worktree，却生成了相同的 <code>REQ-ID</code>。</p><p>当 <code>docs/requirements/</code> 中最新的需求目录为 <code>REQ-30</code> 时，两个 Coding Agent 都读取到相同状态，并将下一个编号计算为 <code>REQ-31</code>。</p><p><img src="/2026/07/18/Git-Worktree-Based-on-Multiple-Repositories-for-Coding-Agent/5.png" alt=""></p><p>单独看每个需求，两者都能继续开发；但当代码合并回 <code>versus</code> 父仓库时，相同的需求目录和文件路径必然产生冲突。</p><p><strong>很多实践看起来简单，真正落地时却会暴露一连串问题，而解决这些问题正是创新的一部分。</strong></p><p>这是一个典型的并发竞争：多个进程读取相同状态，各自计算出相同的下一个编号，然后尝试写入结果。它与数据库中的“丢失更新”问题具有相似结构。</p><p><img src="/2026/07/18/Git-Worktree-Based-on-Multiple-Repositories-for-Coding-Agent/6.jpg" alt=""></p><p>我们的解决思路是复用 Git 已有的互斥保护，而不是另外实现一套锁：</p><ul><li>所有 worktree 共享同一个 Git 引用空间。</li><li><code>git worktree add -b &lt;branch&gt;</code> 会创建新分支；如果该分支已经存在，命令默认失败。</li><li>Git 也默认禁止同一个本地分支同时被多个 worktree 检出。</li></ul><p>因此，我们让 <code>REQ-ID</code> 同时参与分支命名，把创建 <code>feat/&lt;REQ-ID&gt;</code> 分支作为编号预占操作。即使两个进程计算出相同的 <code>REQ-ID</code>，也只有一个能成功创建对应分支；失败的一方重新扫描并分配新编号。</p><p>同时，<code>wt.sh</code> 会在主仓库的 <code>.wt</code> 目录中记录已分配的 <code>REQ-ID</code>。后续分配先扫描这些元数据，再尝试通过 Git 创建分支完成最终确认。</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">ROOT_DIR=<span class="string">"<span class="subst">$(cd <span class="string">"<span class="subst">$(dirname <span class="string">"<span class="variable">$0</span>"</span>)</span>/.."</span> &amp;&amp; pwd)</span>"</span></span><br><span class="line">WT_ROOT=<span class="string">"<span class="variable">${WT_ROOT:-<span class="variable">$ROOT_DIR</span>/..}</span>"</span>  <span class="comment"># worktree 存放根目录，默认为主仓库父目录</span></span><br><span class="line">WT_META_DIR=<span class="string">"<span class="variable">$ROOT_DIR</span>/.wt"</span>         <span class="comment"># 注册表目录（.gitignore 已忽略）</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">write_meta</span></span>() {</span><br><span class="line">    <span class="built_in">local</span> <span class="built_in">id</span>=<span class="variable">$1</span> path=<span class="variable">$2</span> branch=<span class="variable">$3</span> be=<span class="variable">$4</span> fe=<span class="variable">$5</span> req_id=<span class="variable">${6:-}</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"WT_REQ_ID=<span class="variable">$req_id</span>"</span> &gt; <span class="string">"<span class="variable">$WT_META_DIR</span>/<span class="variable">$id</span>.meta"</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>创建 worktree 时，脚本使用 <code>feat/$id</code> 作为分支名。若同名分支已经存在，<code>-b</code> 会拒绝覆盖，脚本便终止本次创建。上层 Agent 识别出同名分支冲突后，再重新分配编号并重试；其他错误则直接上报，避免无意义重试。</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">id</span>=$(allocate_req_id)</span><br><span class="line"></span><br><span class="line">info <span class="string">"[1/6] 创建 Git Worktree（基于 master）..."</span></span><br><span class="line"><span class="keyword">if</span> ! git -C <span class="string">"<span class="variable">$ROOT_DIR</span>"</span> worktree add -b <span class="string">"feat/<span class="variable">$id</span>"</span> <span class="string">"<span class="variable">$wt_path</span>"</span> master 2&gt;&amp;1; <span class="keyword">then</span></span><br><span class="line">    error <span class="string">"创建 worktree 失败"</span></span><br><span class="line">    <span class="built_in">exit</span> 1</span><br><span class="line"><span class="keyword">fi</span></span><br></pre></td></tr></tbody></table></figure><p>整体流程如下所示：</p><pre class="mermaid">flowchart LR    START["Coding Agent 创建 worktree"] --&gt; SCAN["扫描主仓库 .wt/*.meta<br>读取 WT_REQ_ID"]    SCAN --&gt; ALLOC["计算候选 REQ-ID"]    ALLOC --&gt; BRANCH["生成 feat/REQ-ID 分支名"]    BRANCH --&gt; CREATE{"git worktree add -b"}    CREATE -- "成功" --&gt; META["写入 .wt/&lt;worktree&gt;.meta<br>记录 REQ-ID 和 worktree 信息"]    META --&gt; DONE["新 worktree"]    CREATE -- "分支已存在" --&gt; RETRY["重新分配编号"]    RETRY --&gt; SCAN</pre><p>下面，我们启动两个 Coding Agent 会话，并行执行两个需求。从实际结果看，两个 Agent 分别获得 <code>REQ-42</code> 和 <code>REQ-43</code>，并成功创建了对应的 worktree 与需求分支。</p><p><img src="/2026/07/18/Git-Worktree-Based-on-Multiple-Repositories-for-Coding-Agent/m_1.png" alt="启动两个 Session 并行执行两个任务"></p><p><img src="/2026/07/18/Git-Worktree-Based-on-Multiple-Repositories-for-Coding-Agent/m_2.png" alt="分别生成 REQ-42、REQ-43，未发生 REQ-ID 冲突"></p><p><img src="/2026/07/18/Git-Worktree-Based-on-Multiple-Repositories-for-Coding-Agent/m_3.png" alt="两个 worktree 分别绑定 REQ-42、REQ-43，避免因 REQ-ID 重复而产生路径冲突"></p><p><img src="/2026/07/18/Git-Worktree-Based-on-Multiple-Repositories-for-Coding-Agent/m_4.png" alt="PRD Agent 完成对应需求的扩充与澄清"></p><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition warning"><p class="admonition-title">注意</p><p>这套互斥机制依赖同一份本地 Git 引用空间，只能解决同一台机器、同一个父仓库中的并发分配问题。多名工程师在不同机器上并行开发时，本地分支无法形成全局互斥，仍可能生成相同的 <code>REQ-ID</code>。</p><p>当前场景的目标是在一台开发机器上并行运行多个 Agent，因此该方案已经满足需求。如果未来需要跨机器分配编号，应改用中心化服务、数据库唯一约束或远程原子引用等全局协调机制。</p></div><h2 id="4-多-worktree-并行开发">4. 多 worktree 并行开发</h2><p>LLM Arena 平台已经能够满足基本评测需求，但仍有一些交互体验问题需要优化，例如：</p><ul><li>分类管理页面缺少分页；创建分类时，父分类 ID 仍需手动填写，操作不够友好。</li><li>模型效果页面滑动到底部后，媒体资源卡片可能无法对齐，页面会出现大块空白。</li></ul><p><img src="/2026/07/18/Git-Worktree-Based-on-Multiple-Repositories-for-Coding-Agent/prds.jpg" alt=""></p><p>过去，我们使用 Harness Engineering 串行处理这些需求。将其升级为 worktree 模式后，不同需求可以在相互隔离的环境中并行推进。</p><p><img src="/2026/07/18/Git-Worktree-Based-on-Multiple-Repositories-for-Coding-Agent/prds_1.png" alt=""></p><p><img src="/2026/07/18/Git-Worktree-Based-on-Multiple-Repositories-for-Coding-Agent/prds_2.png" alt=""></p><p><img src="/2026/07/18/Git-Worktree-Based-on-Multiple-Repositories-for-Coding-Agent/prds_3.png" alt=""></p><p><img src="/2026/07/18/Git-Worktree-Based-on-Multiple-Repositories-for-Coding-Agent/prds_4.png" alt=""></p><p>当 Coding Agent 能够在不同且相互隔离的 worktree 中并行开发时，多个独立需求不必再排队等待，整体开发吞吐量得到显著提升。</p><p><img src="/2026/07/18/Git-Worktree-Based-on-Multiple-Repositories-for-Coding-Agent/prds_5.png" alt=""></p><h2 id="5-teamwork-preview-编排">5. <code>/teamwork-preview</code> 编排</h2><p>最初，7 个 Sub-Agent 的编排规则都定义在 <code>versus/CLAUDE.md</code> 中。</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">## 自动化工作流</span></span><br><span class="line"></span><br><span class="line"><span class="comment">### 流水线概览</span></span><br><span class="line"></span><br><span class="line">```</span><br><span class="line">用户: <span class="string">"开启 7 个 Agent 协作实现 XXX 需求"</span></span><br><span class="line">│</span><br><span class="line">▼</span><br><span class="line">[0] worktree-manager       →  创建隔离 worktree + 分配端口</span><br><span class="line">│</span><br><span class="line">▼ status=completed（主会话 EnterWorktree(path=...) 切换）</span><br><span class="line">[1] requirement-designer   →  产出 PRD.md + 分配需求 ID</span><br><span class="line">│</span><br><span class="line">▼ status=awaiting_review</span><br><span class="line">⏸ 人工审核 PRD（用户确认后继续）</span><br><span class="line">│</span><br><span class="line">▼ 用户确认 → status=completed</span><br><span class="line">[2] go-api-implementer     →  产出 Go 代码 + api-summary.md</span><br><span class="line">│</span><br><span class="line">▼ status=completed（自动流转）</span><br><span class="line">[3] frontend-engineer      →  产出 React 代码</span><br><span class="line">│</span><br><span class="line">▼ status=completed（自动流转）</span><br><span class="line">[4] test-case-designer     →  产出 api-test-cases.json + e2e-test-cases.json</span><br><span class="line">│</span><br><span class="line">▼ status=completed（自动流转，串行启动）</span><br><span class="line">[5] integration-test-runner (worktree端口)  →  [6] e2e-test-executor (worktree端口+1)</span><br><span class="line">│                                           │</span><br><span class="line">▼                                           ▼</span><br><span class="line">status=has_bugs → Bug修复循环               status=all_passed → 流水线完成</span><br><span class="line">```</span><br></pre></td></tr></tbody></table></figure><p>每次启动流程时，我们都要重复输入相同的指令：</p><blockquote><p>开启 7 个 Agent 协作，实现：……</p></blockquote><p>这条指令虽然不长，但仍暴露出一个问题：工作流依赖用户记住并重复输入约定，而不是由系统提供稳定入口。恰好，7 月 14 日，Google Antigravity 发布了 Agent Teams 的演示：运行 <code>/teamwork-preview</code>，即可启动一个由专业 Sub-Agent 组成的动态团队，在后台协调、规划、构建并验证复杂任务。</p><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/antigravity/status/2076720528937611363"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><p>受此启发，我们也实现了 <code>/teamwork-preview</code>，用一条稳定指令启动完整协作流程：</p><blockquote><p>/teamwork-preview ……</p></blockquote><p>在 Claude Code 中，可以通过 Skill 封装这类工作流。于是，我们把 <code>CLAUDE.md</code> 中的 Sub-Agent 编排规则迁移到 <code>teamwork-preview</code> Skill，将触发方式、执行顺序、状态流转和异常恢复集中管理。</p><p><img src="/2026/07/18/Git-Worktree-Based-on-Multiple-Repositories-for-Coding-Agent/teamwork.png" alt=""></p><p>因此，<code>/teamwork-preview</code> 本质上是一个基于 Skill 编排 Sub-Agent 的工作流：</p><ul><li>使用 Skill 组织领域知识和操作流程，降低重复上下文的成本。</li><li>使用外部状态文件记录执行进度，增强主 Agent 的任务恢复能力。</li><li>使用管理类 Skill 编排工作流，使用能力类 Skill 增强各个 Sub-Agent。</li></ul><p><img src="/2026/07/18/Git-Worktree-Based-on-Multiple-Repositories-for-Coding-Agent/teamwork-arch.png" alt=""></p><h2 id="6-总结">6. 总结</h2><p>Coding Agent 提升了代码生成和任务执行的速度，却不会让并发竞争、状态一致性和资源隔离等经典工程问题自动消失。Agent 越多、并行度越高，这些问题反而越容易暴露。</p><p>这次实践最终沉淀出三个原则：</p><ul><li><strong>将推理与执行分离。</strong> Agent 负责理解需求和编排流程，脚本负责执行可重复、可验证的底层操作。</li><li><strong>优先复用已有约束。</strong> 使用 Git 分支创建的失败保护完成本地互斥，比另行维护一套脆弱的锁机制更简单。</li><li><strong>明确方案边界。</strong> 本地 Git 引用只能协调单机并发；跨机器场景需要真正的全局协调机制。</li></ul><p>AI 可以替我们完成更多操作，但不能替我们理解系统。恰恰因为开发速度更快、并行规模更大，我们更需要掌握底层原理，并用清晰的架构约束 Agent 的行为。</p></body></html>]]>
      </content:encoded>
    </item>
    <item>
      <title>是时候了解一下 git worktree 了</title>
      <link>https://wangwei1237.github.io/2026/06/28/It-is-time-to-learn-about-git-worktree/</link>
      <description>
        <![CDATA[<html><head></head><body><p><img src="/2026/06/28/It-is-time-to-learn-about-git-worktree/1.png" alt=""></p>
<p>在 <a href="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/">如何利用 Harness “一句话交付产品功能”？</a> 一文中，我们已经讨论过如何在单个 <code>clone</code> 仓库里借助 Harness Engineering 自动完成需求开发。但问题也随之出现：当这个目录正在被 Coding Agent 执行任务、修改代码、运行测试时，我们能做的往往只剩下等待。</p>
<p>如果还想并行开发另一个功能，最直观的办法是重新 <code>clone</code> 一份仓库到新目录。这个方案能用，但并不优雅：重复下载、重复安装依赖、重复同步分支，最后还要在多个副本之间来回合并。我们需要一种更轻、更原生、更适合并行开发的方案。于是，git worktree 重新进入了视野。</p></body></html>]]>
      </description>
      <author>Wang Wei</author>
      <category domain="https://wangwei1237.github.io/categories/LLM/">LLM</category>
      <category domain="https://wangwei1237.github.io/tags/git/">git</category>
      <category domain="https://wangwei1237.github.io/tags/worktree/">worktree</category>
      <category domain="https://wangwei1237.github.io/tags/branch/">branch</category>
      <pubDate>Sun, 28 Jun 2026 12:58:05 GMT</pubDate>
      <content:encoded>
        <![CDATA[<html><head></head><body><p><img src="/2026/06/28/It-is-time-to-learn-about-git-worktree/1.png" alt=""></p><p>在 <a href="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/">如何利用 Harness “一句话交付产品功能”？</a> 一文中，我们已经讨论过如何在单个 <code>clone</code> 仓库里借助 Harness Engineering 自动完成需求开发。但问题也随之出现：当这个目录正在被 Coding Agent 执行任务、修改代码、运行测试时，我们能做的往往只剩下等待。</p><p>如果还想并行开发另一个功能，最直观的办法是重新 <code>clone</code> 一份仓库到新目录。这个方案能用，但并不优雅：重复下载、重复安装依赖、重复同步分支，最后还要在多个副本之间来回合并。我们需要一种更轻、更原生、更适合并行开发的方案。于是，git worktree 重新进入了视野。</p><span id="more"></span><p>git worktree 是 Git 中一个长期被低估的能力。它随 Git 2.5 在 2015 年发布，至今已经存在十多年<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>，但依旧鲜为人知。如果不是 AI Coding Agent 的告诉发展，git worktree 很可能仍然仅仅停留在更新日志中。</p><h2 id="git-worktree-简介">Git Worktree 简介</h2><p>根据 Git 官方文档<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>，<code>git worktree</code> 用于在同一个仓库之上管理多个工作目录。</p><p>在传统 Git 工作流中，我们通常有两种并行方式：在同一个目录里不断切换分支，或者为同一个仓库创建多个 <code>clone</code>。前者会频繁打断当前上下文，尤其是在工作区还存在未提交修改时；后者虽然隔离性更好，但每个克隆都拥有独立的 Git 数据，需要额外同步远端、分支和提交。</p><p>git worktree 提供了第三种选择：为现有仓库创建一个新的工作目录。这个目录看起来像一个独立仓库，有自己的文件树和当前分支；但它并不是一份完整克隆，而是复用主仓库背后的 Git 对象、引用和历史记录。在 linked working tree 中，<code>.git</code> 通常不是目录，而是一个指向主仓库 Git 元数据的普通文件。</p><p><img src="/2026/06/28/It-is-time-to-learn-about-git-worktree/2.png" alt=""></p><h2 id="linked-working-tree">Linked Working Tree</h2><p>理解 git worktree，需要先区分两个概念：</p><ul><li><strong>主工作目录（Main Working Tree）</strong>：通过 <code>git clone</code> 或 <code>git init</code> 创建的原始仓库目录。它包含完整的 <code>.git</code> 目录，是其他链接工作目录共享 Git 数据的基础。</li><li><strong>链接工作目录（Linked Working Tree）</strong>：通过 <code>git worktree add</code> 创建的额外工作目录。它拥有独立的代码文件和工作区状态，但通过 <code>.git</code> 文件关联到主仓库的 Git 元数据。</li></ul><p>换句话说，worktree 不是“再克隆一份仓库”，而是在同一个仓库历史之上打开多个互相隔离的工作现场。</p><h2 id="创建-worktree">创建 Worktree</h2><p>假设我们正在 <code>feature</code> 分支上开发一个功能，这时线上突然出现一个需要基于 <code>main</code> 紧急修复的 Bug。如果不使用 worktree，通常需要这样操作：</p><ol><li>暂存 <code>feature</code> 分支上的所有改动，并让工作区恢复干净状态</li><li>切换到 <code>main</code> 分支，再创建新的 <code>hotfix</code> 分支</li><li>在 <code>hotfix</code> 分支上修复 Bug 并提交</li><li>切回 <code>feature</code> 分支，并恢复之前暂存的改动</li></ol><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">git stash</span><br><span class="line">git checkout main</span><br><span class="line">git checkout -b hotfix</span><br><span class="line"><span class="comment"># 修复 Bug，提交修复</span></span><br><span class="line">git checkout feature</span><br><span class="line">git stash pop</span><br></pre></td></tr></tbody></table></figure><p>这个流程并不复杂，但很容易打断思路。如果 <code>stash pop</code> 发生冲突，原本只想修一个小 Bug，最后可能变成上下文切换和冲突处理。</p><p>使用 worktree 可以把这个过程变成一次目录创建：</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git worktree add ../project-hotfix -b hotfix main</span><br></pre></td></tr></tbody></table></figure><p>执行后，我们会得到两个工作目录：</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">project/            <span class="comment"># 继续开发 feature</span></span><br><span class="line">project-hotfix/     <span class="comment"># 专门处理 hotfix</span></span><br></pre></td></tr></tbody></table></figure><p>两个目录共享同一份 Git 历史，但工作区彼此独立。我们可以在 <code>project</code> 中继续开发功能，在 <code>project-hotfix</code> 中修复线上问题，不需要来回 <code>checkout</code>，也不需要临时清空当前工作现场。</p><h2 id="查看-worktree">查看 Worktree</h2><p>若要查看当前仓库关联的所有 worktree，可以使用 <code>git worktree list</code>：</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git worktree list</span><br></pre></td></tr></tbody></table></figure><p>这个命令会列出每个工作目录的本地路径、当前提交哈希，以及该目录正在检出的分支或 HEAD 状态。对于同时处理多个任务的项目来说，它相当于一张本地并行工作区清单。</p><p><img src="/2026/06/28/It-is-time-to-learn-about-git-worktree/3.png" alt=""></p><h2 id="删除-worktree">删除 Worktree</h2><p>为了保持本地目录整洁，完成任务后应及时删除不再需要的 worktree。例如，当 <code>hotfix</code> 修复完成并提交后，可以执行：</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git worktree remove ../project-hotfix</span><br></pre></td></tr></tbody></table></figure><p>这条命令会完成两件事：</p><ol><li>删除该 worktree 目录下的代码文件</li><li>从仓库的 worktree 注册表中清除对应记录</li></ol><p>如果目标工作目录中仍有未提交修改，Git 默认会拒绝删除，以避免误删本地工作。确实需要删除时，可以追加 <code>--force</code>：</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git worktree remove --force ../project-hotfix</span><br></pre></td></tr></tbody></table></figure><p>和所有 <code>-f</code> / <code>--force</code> 参数一样，执行前务必确认里面没有需要保留的修改、未跟踪文件或本地提交。</p><h2 id="worktree-与-branch">Worktree 与 Branch</h2><p>git branch 和 git worktree 不是同一层面的概念<sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>。</p><ul><li>branch 是一条版本线，本质上是指向某个提交的引用。它解决的是“基于哪个版本继续演进”的问题。</li><li>worktree 是一个工作目录，它把某条版本线检出到独立目录中。它解决的是“如何同时打开多个工作现场”的问题。</li></ul><figure class="highlight text"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Git 仓库</span><br><span class="line">├── branch: main</span><br><span class="line">├── branch: feature/1</span><br><span class="line">├── branch: fix/bug-123</span><br><span class="line">└── worktree:</span><br><span class="line">    ├── 当前目录        -&gt; main</span><br><span class="line">    ├── ../f1-dev      -&gt; feature/1</span><br><span class="line">    └── ../bugfix      -&gt; fix/bug-123</span><br></pre></td></tr></tbody></table></figure><table><thead><tr><th>对比项</th><th>branch</th><th>worktree</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>分支可以有很多，但当前目录一次只能 checkout 一个</td><td>可以在多个目录中同时 checkout 不同分支</td></tr><tr><td>典型命令</td><td><code>git branch</code> / <code>git checkout</code> / <code>git switch</code></td><td><code>git worktree add</code> / <code>git worktree list</code> / <code>git worktree remove</code></td></tr></tbody></table><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">branch 是“版本线”，worktree 是“工作现场”</p><p>worktree 不是 branch 的替代品。更准确地说，worktree 通常是把某个 branch 检出到另一个目录，让它成为一个独立工作现场。</p></div><h2 id="何时使用-worktree">何时使用 Worktree</h2><p>在 AI Coding Agent 普及之前，工程师通常只会并行推进一个或少数几个任务，branch 已经足够。但当我们需要同时处理多个任务，尤其是三个以上彼此独立的任务时，worktree 的优势会非常明显：</p><ul><li>一个目录继续主需求开发</li><li>一个目录修复线上 Bug</li><li>一个目录验证依赖升级</li><li>一个目录复现或审查 Pull Request</li><li>一个目录交给 Coding Agent 跑长期任务</li></ul><p>worktree 的关键价值不是“多一个 Git 命令”，而是把研发现场从单线程切换升级成多工作区并行。</p><h2 id="coding-agent-中的-worktree">Coding Agent 中的 Worktree</h2><p>在 Coding Agent 场景下，worktree 的价值会进一步放大。Agent 不只是帮我们写几行代码，它往往会执行一个完整任务链：阅读代码、修改文件、安装依赖、运行测试、分析失败、继续修复。如果多个 Agent 或多个任务挤在同一个工作目录里，文件修改、未提交变更、依赖状态和本地服务都很容易互相干扰。</p><p>因此，Codex 和 Claude Code 都把 worktree 作为并行编码的重要基础设施。简单来说：</p><ul><li>对人来说，worktree 是多个独立的工作现场。</li><li>对 Coding Agent 来说，worktree 是多个互不干扰的任务沙箱。</li></ul><h3 id="codex-中的-worktree">Codex 中的 Worktree</h3><p>Codex 中的 worktree 主要用于让 Codex 在同一个项目里并行执行多个独立任务，同时不影响我们正在使用的本地工作目录。Codex 文档把这个本地目录称为 <strong>Local checkout</strong>，也就是我们自己创建并日常使用的仓库；Codex 从它创建出来的后台工作目录则称为 <strong>Worktree</strong><sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup>。</p><p>可以把这个模型理解为：</p><figure class="highlight text"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Local checkout   # 前台工作区：我们正在 IDE、终端、开发服务器中使用</span><br><span class="line">Worktree A       # 后台任务：Codex 实现 feature-a</span><br><span class="line">Worktree B       # 后台任务：Codex 修复 bug-b</span><br><span class="line">Worktree C       # 后台任务：Codex 处理代码评审反馈</span><br></pre></td></tr></tbody></table></figure><p>在 Codex 中创建新线程时，可以选择让线程运行在 Worktree 中。Codex 会基于我们选择的起始分支创建一个 Git worktree。这个起始分支可以是 <code>main</code> / <code>master</code>，也可以是某个功能分支，甚至可以是当前带有未暂存改动的分支。默认情况下，Codex 会在 detached HEAD 状态下工作；当我们确认这条任务值得保留时，再在该 worktree 上创建分支、提交代码、推送远端并创建 Pull Request。</p><p><img src="/2026/06/28/It-is-time-to-learn-about-git-worktree/4.png" alt=""></p><p>Codex 还有一个关键概念：<strong>Handoff</strong>。Handoff 用于在线程的 Local 和 Worktree 之间移动任务。也就是说，当 Codex 在后台 worktree 中完成一批修改后，如果我们想用熟悉的 IDE 和本地开发服务器继续检查，可以把这个线程 handoff 到 Local；反过来，如果一个任务最初在 Local 中启动，但希望放到后台继续执行，也可以 handoff 到 Worktree。</p><p>这个机制解决的是 Git 的一个天然限制：同一个分支不能同时被 checkout 到多个 worktree 中。Codex 的 Handoff 会处理必要的 Git 操作，避免我们手动搬运修改、切换分支和处理由此引入的冲突。对于使用者来说，真正需要决定的是：这个任务现在应该留在前台，还是交给后台环境继续推进。</p><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">Codex 的核心抽象</p><p>Local 是前台工作现场，Worktree 是后台任务现场，Handoff 是二者之间的交接机制。</p></div><p>Codex 的 worktree 特别适合这些场景：</p><ul><li>让 Codex 在后台实现一个功能，同时我们继续在 Local 中开发另一个功能。</li><li>把自动化任务运行在独立 worktree 中，避免影响当前工作目录。</li><li>在 worktree 中完成修改后，创建分支并提交 PR。</li><li>将 worktree 中的任务 handoff 到 Local，用日常 IDE 和开发服务器做最终检查。</li></ul><p>需要注意的是：worktree 是一个新的 checkout，所以 <code>.gitignore</code> 中忽略的文件默认不会自动出现在 worktree 中，例如 <code>.env</code>、<code>.env.local</code>、本地密钥配置等。如果 Agent 任务依赖这些文件，就需要额外处理。Codex 支持通过 <code>.worktreeinclude</code> 把指定的已忽略文件复制到本地托管的 worktree 中。</p><h3 id="claude-code-中的-worktree">Claude Code 中的 Worktree</h3><p>Claude Code 对 worktree 的支持更偏 CLI 工作流。它提供 <code>--worktree</code> / <code>-w</code> 参数，用于在独立 worktree 中启动一个 Claude Code 会话<sup class="footnote-ref"><a href="#fn5" id="fnref5">[5]</a></sup>：</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">claude --worktree feature-auth</span><br><span class="line">claude --worktree bugfix-123</span><br></pre></td></tr></tbody></table></figure><p>默认情况下，Claude Code 会在仓库根目录下的 <code>.claude/worktrees/&lt;name&gt;/</code> 创建 worktree，并创建名为 <code>worktree-&lt;name&gt;</code> 的分支。例如：</p><figure class="highlight text"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">.claude/</span><br><span class="line">└── worktrees/</span><br><span class="line">    ├── feature-auth/   # branch: worktree-feature-auth</span><br><span class="line">    └── bugfix-123/     # branch: worktree-bugfix-123</span><br></pre></td></tr></tbody></table></figure><p>这样，我们可以在多个终端中同时启动多个 Claude Code 会话。一个会话实现认证功能，另一个会话修复线上 Bug；它们分别修改不同目录中的文件，因此不会互相覆盖。</p><p>Claude Code 默认从仓库的默认远端分支 <code>origin/HEAD</code> 创建新的 worktree，这意味着每个任务通常会从一个干净的远端状态开始。如果希望新的 worktree 基于当前本地 <code>HEAD</code> 创建，可以在设置中配置 <code>worktree.baseRef</code> 为 <code>"head"</code>：</p><figure class="highlight json"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line">  <span class="attr">"worktree"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">    <span class="attr">"baseRef"</span><span class="punctuation">:</span> <span class="string">"head"</span></span><br><span class="line">  <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure><p>这个设置适合一种常见场景：我们已经在本地完成了一部分改动或提交，希望 Claude 的子任务基于当前进度继续拆分，而不是从远端默认分支重新开始。</p><p>Claude Code 还支持将子代理（subagent）隔离到 worktree 中。我们可以要求 Claude “use worktrees for your agents”，也可以在自定义 subagent 的 Front Matter 中配置 <code>isolation: worktree</code>。这样，每个子代理都会获得自己的临时 worktree，适合并行探索多个方案、拆分多个模块，或者让不同代理分别修复不同测试失败。</p><p>同样需要注意 <code>.gitignore</code> 文件。Claude Code 支持在仓库根目录创建 <code>.worktreeinclude</code> 文件，用 <code>.gitignore</code> 语法声明哪些已被 Git 忽略的本地文件需要复制进新 worktree。例如：</p><figure class="highlight text"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">.env</span><br><span class="line">.env.local</span><br><span class="line">config/secrets.json</span><br></pre></td></tr></tbody></table></figure><p>这对前端项目、后端服务，以及依赖本地密钥的测试环境尤其重要。否则 Agent 在 worktree 中运行测试或启动服务时，可能会因为缺少环境变量而失败。</p><p>在清理策略上，Claude Code 会根据 worktree 中是否存在未提交修改、未跟踪文件或新提交来决定是否自动清理。没有变更的临时 worktree 可以被自动删除；有变更的 worktree 会提示保留或删除；非交互式 <code>claude -p --worktree</code> 创建的 worktree 不会自动清理，需要使用 <code>git worktree remove</code> 手动删除。</p><h3 id="二者的共同点与差异">二者的共同点与差异</h3><p>Codex 和 Claude Code 使用 worktree 的目标是一致的：让多个 Agent 任务在同一个仓库中并行运行，并隔离文件修改。二者的差异更多体现在产品形态上：</p><table><thead><tr><th>对比项</th><th>Codex</th><th>Claude Code</th></tr></thead><tbody><tr><td>主要入口</td><td>Codex 中新建线程时选择 Worktree</td><td>CLI 中使用 <code>claude --worktree</code></td></tr><tr><td>核心抽象</td><td>Local、Worktree、Handoff</td><td>worktree 会话、子代理 worktree</td></tr><tr><td>典型用法</td><td>后台运行 Codex 任务，再 handoff 到 Local 检查</td><td>多个终端并行运行多个 Claude Code 会话</td></tr><tr><td>默认分支行为</td><td>创建线程时选择起始分支，默认在 detached HEAD 工作</td><td>默认基于 <code>origin/HEAD</code>，可配置为本地 <code>HEAD</code></td></tr><tr><td>本地忽略文件</td><td>本地托管 worktree 场景可通过 <code>.worktreeinclude</code> 处理</td><td>通过 <code>.worktreeinclude</code> 复制已被 Git 忽略的文件</td></tr><tr><td>清理方式</td><td>通过 App 工作流管理 worktree 和线程</td><td>根据会话状态提示或自动清理，也可手动 <code>git worktree remove</code></td></tr></tbody></table><p>这也解释了为什么 AI Coding Agent 让 git worktree 重新变得重要：过去使用 worktree，主要是为了让人类工程师并行开发；现在使用 worktree，是为了让人和多个 Agent 能够同时在同一个仓库里协作，而不互相污染工作现场。</p><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">Agent 时代的 worktree 使用原则</p><p>只要一个任务可能长时间运行、可能修改大量文件、可能启动服务或运行测试，就应该优先考虑把它放进独立 worktree。即使 Agent 走错方向，也不会破坏我们当前的工作现场。</p></div><h2 id="参考文献">参考文献</h2><hr class="footnotes-sep"><section class="footnotes"><ol class="footnotes-list"><li id="fn1" class="footnote-item"><p><a href="https://github.blog/open-source/git/git-2-5-including-multiple-worktrees-and-triangular-workflows/">Git 2.5, including multiple worktrees and triangular workflows</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p></li><li id="fn2" class="footnote-item"><p><a href="https://git-scm.com/docs/git-worktree">git-worktree</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p></li><li id="fn3" class="footnote-item"><p><a href="https://www.worktreewise.com/blog/git-worktree-vs-branch">Git Worktree vs Branch - What Developers Actually Need to Know</a> <a href="#fnref3" class="footnote-backref">↩︎</a></p></li><li id="fn4" class="footnote-item"><p><a href="https://developers.openai.com/codex/app/worktrees">Worktrees - Codex app</a> <a href="#fnref4" class="footnote-backref">↩︎</a></p></li><li id="fn5" class="footnote-item"><p><a href="https://code.claude.com/docs/en/worktrees">Run parallel sessions with worktrees - Claude Code</a> <a href="#fnref5" class="footnote-backref">↩︎</a></p></li></ol></section></body></html>]]>
      </content:encoded>
    </item>
    <item>
      <title>如何评估 Skill？</title>
      <link>https://wangwei1237.github.io/2026/06/12/How-to-evaluate-a-Skill/</link>
      <description>
        <![CDATA[<html><head></head><body><p><img src="/2026/06/12/How-to-evaluate-a-Skill/1.jpg" alt=""></p>
<p><a href="/2025/12/24/Reflections-on-Agent-Skills/">对 Agent Skills 的认知与思考</a> 这篇文章对 Agent Skills 做了详细介绍。自 Anthropic 发布 Agent Skills 以来，Skills 已经逐渐成为 Agentic 系统构建中的一种共识范式。无论是公开的 Skills Marketplace、OpenClaw 的 <a href="https://clawhub.ai/skills">ClawHub</a>，还是团队内部沉淀的专用能力库，Skills 的数量都在快速增长。</p>
<p><a href="https://skillsmp.com/">Skills Marketplace</a> 平台已经托管了 172W+ 的 Agent Skills；而在年初红极一时的 OpenClaw 的 <a href="https://clawhub.ai/skills">ClawHub</a> 平台也已经托管了 7W+ 的 Agent Skills；在我们内部，也已经积累了成千上百的 Skills。</p></body></html>]]>
      </description>
      <author>Wang Wei</author>
      <category domain="https://wangwei1237.github.io/categories/LLM/">LLM</category>
      <category domain="https://wangwei1237.github.io/tags/Skill/">Skill</category>
      <category domain="https://wangwei1237.github.io/tags/%E8%AF%84%E4%BC%B0/">评估</category>
      <pubDate>Fri, 12 Jun 2026 13:33:23 GMT</pubDate>
      <content:encoded>
        <![CDATA[<html><head></head><body><p><img src="/2026/06/12/How-to-evaluate-a-Skill/1.jpg" alt=""></p><p><a href="/2025/12/24/Reflections-on-Agent-Skills/">对 Agent Skills 的认知与思考</a> 这篇文章对 Agent Skills 做了详细介绍。自 Anthropic 发布 Agent Skills 以来，Skills 已经逐渐成为 Agentic 系统构建中的一种共识范式。无论是公开的 Skills Marketplace、OpenClaw 的 <a href="https://clawhub.ai/skills">ClawHub</a>，还是团队内部沉淀的专用能力库，Skills 的数量都在快速增长。</p><p><a href="https://skillsmp.com/">Skills Marketplace</a> 平台已经托管了 172W+ 的 Agent Skills；而在年初红极一时的 OpenClaw 的 <a href="https://clawhub.ai/skills">ClawHub</a> 平台也已经托管了 7W+ 的 Agent Skills；在我们内部，也已经积累了成千上百的 Skills。</p><span id="more"></span><p>随着 Agent 加载的 Skills 越来越多，我们也开始观察到一个新问题：不同 Skills 之间会发生冲突、覆盖和干扰，导致 Agent 的行为更难预测，调试成本也随之上升。</p><p>同时，当我们更新某个 Skill 之后，往往很难判断这次修改是否破坏了原有能力，也很难证明它真的带来了优化。</p><p>问题不再是“能不能写一个 Skill”，而是“能不能知道这个 Skill 什么时候有效、什么时候失效、什么时候应该回滚”。</p><p><strong>于是，我们认为，Skill 的快速评估与测试是保障 Agent 系统可控的必要手段。</strong></p><h2 id="重新认识-skill">重新认识 Skill</h2><p>Anthropic 的官方文档 <a href="https://code.claude.com/docs/en/features-overview#extend-claude-code"><em>Extend Claude Code</em></a><sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> 对相关概念之间的区别进行了如下阐述：</p><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">核心概念</p><ul><li>Skills add reusable knowledge and invocable workflows</li><li>Subagents run their own loops in isolated context, returning summaries</li></ul><p>Skills are the most flexible extension. A skill is a markdown file containing knowledge, workflows, or instructions. You can invoke skills with a command like /deploy, or Claude can load them automatically when relevant. <strong>Skills can run in your current conversation or in an isolated context via subagents.</strong></p></div><p>在上面的描述中，Skills 就是：</p><ul><li>可复用的知识</li><li>可调用的工作流</li></ul><p>“invocable workflows” 是一个很容易被误读的描述：它可能让人误以为 Skills 是可以独立运行的操作单元。</p><p>但更准确地说，“invocable workflows” 是一套可被按需调用的结构化操作步骤，是被封装好的行动方案。它赋予 Agent “怎么做”的能力，而具体执行仍然由 Agent 按照工作流中描述的路线与外部世界交互完成。</p><p>此处的 “invocable” 更合理的理解是“可以被 Agent 调用”，而不是“可以独立运行”。即便某个 Skill 包含可执行的 <code>scripts</code> 脚本，真正的运行顺序也是：Agent 读取 Skill 中的说明，再按需调用指定脚本。Skill 本身不能自主执行，必须由 Agent 在真实环境中按照其定义的流程完成任务。</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">my-skill/</span><br><span class="line">├── SKILL.md          <span class="comment"># Required: metadata + instructions</span></span><br><span class="line">├── scripts/          <span class="comment"># Optional: executable code</span></span><br><span class="line">├── references/       <span class="comment"># Optional: documentation</span></span><br><span class="line">├── assets/           <span class="comment"># Optional: templates, resources</span></span><br><span class="line">└── ...               <span class="comment"># Any additional files or directories</span></span><br></pre></td></tr></tbody></table></figure><p>如果从这个角度理解 “invocable workflows”，它本质上就是一种特殊形式的知识。</p><h2 id="agent-vs-skills">Agent VS. Skills</h2><p>因此，我们不但需要重新认识 Skills，也需要重新认识 Agent 与 Skills 之间的关系。</p><p><img src="/2026/06/12/How-to-evaluate-a-Skill/2.jpg" alt=""></p><p>正如 <a href="https://code.claude.com/docs/en/features-overview#extend-claude-code"><em>Extend Claude Code</em></a><sup class="footnote-ref"><a href="#fn1" id="fnref1:1">[1:1]</a></sup> 中所述：</p><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">Skills and subagents solve different problems</p><ul><li><strong>Skills</strong> are reusable content you can load into any context</li><li><strong>Subagents</strong> are isolated workers that run separately from your main conversation</li></ul></div><h2 id="重新认识知识">重新认识知识</h2><p>谷歌发布的 <a href="https://www.kaggle.com/whitepaper-context-engineering-sessions-and-memory">Context Engineering: Sessions &amp; Memory</a><sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup> 白皮书中的 <em>Types of information</em> 这一节，对知识进行了分类：</p><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">Types of information</p><p>Beyond their basic structure, memories can be classified by <strong>the fundamental type of knowledge</strong> they represent. This distinction, crucial for understanding how an agent uses memories, separates memory into two primary functional categories derived from cognitive science: <strong>declarative memories</strong> (“knowing what”) and <strong>procedural memories</strong> (“knowing how”).</p><ul><li><p><strong>Declarative memory</strong> is the agent's knowledge of facts, figures, and events. It's all the information that the agent can explicitly state or "declare." If the memory is an answer to a "what" question, it's declarative. This category encompasses both general world knowledge (Semantic) and specific user facts (Entity/Episodic).</p></li><li><p><strong>Procedural memory</strong> is the agent's knowledge of skills and workflows. It guides the agent's actions by demonstrating implicitly how to perform a task correctly. If the memory helps answer a "how" question—like the correct sequence of tool calls to book a trip—it's procedural.</p></li></ul></div><p>从这个层面来看，“invocable workflows” 其实就是 <strong>Procedural Knowledge</strong>，即一种用于存储操作步骤、执行流程和动作技能的程序性知识。</p><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">Skill 与普通知识的区别</p><p>普通知识回答的是“是什么”，而 Skill 回答的是“怎么做”。</p></div><p>一个 API 文档会以普通知识的形式告诉模型接口相关信息：</p><figure class="highlight text"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">登录接口是 POST /api/login</span><br><span class="line">请求字段包含 username、password</span><br><span class="line">返回字段包含 token、expireTime</span><br></pre></td></tr></tbody></table></figure><p>而一个 API 测试 Skill 则会告诉模型：</p><figure class="highlight text"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">当用户要求验证 API 是否符合 PRD 时，必须按照以下流程执行：</span><br><span class="line">1. 读取 PRD；</span><br><span class="line">2. 读取 API 文档；</span><br><span class="line">3. 生成主路径、异常路径、边界路径测试用例；</span><br><span class="line">4. 执行或规划 API 测试；</span><br><span class="line">5. 解析测试结果；</span><br><span class="line">6. 输出 api_test_report.json；</span><br><span class="line">7. 失败时标记 suspected_owner，并给出 next_action。</span><br></pre></td></tr></tbody></table></figure><p>Skill 本身虽然不会自主执行，但作为 <strong>Procedural Knowledge</strong>，它会直接影响 AI 的执行路径和输出质量。</p><ul><li>如果 Skill 写得不清楚，后续 Agent 执行就会不稳定；</li><li>如果 Skill 的产物契约不明确，Hook 和 Workflow 就无法做自动判断；</li><li>如果 Skill 缺少失败处理策略，多 Agent 闭环就会在失败处停住。</li></ul><h2 id="agent-vs-skills-vs-knowledge">Agent VS. Skills VS. Knowledge</h2><p>Agent、Skills、Knowledge 三者之间的关系如下所示：</p><p><img src="/2026/06/12/How-to-evaluate-a-Skill/ask.jpg" alt=""></p><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">Skills 更是结构化的知识</p><p>Skills 不仅是程序性知识，更是对 “invocable workflows” 的结构化封装。通过这种结构化方式，Agent 可以更高效地发现、加载和使用相关知识。</p></div><h2 id="skill-究竟要测什么？">Skill 究竟要测什么？</h2><p>既然 Skills 在本质上是一种特殊的知识，那么 Skill 测试的目的就不是验证“模型知道了什么”，而是验证：<strong>Skills 提供的程序性知识能不能稳定指导模型完成一类任务。</strong></p><p>更进一步，Skill 测试不是“跑业务功能”，而是“测试方法是否可靠”。它的目的不是验证真实业务最终是否成功，而是验证：</p><ul><li>Skill 是否在正确场景下被触发；</li><li>模型是否理解 Skill 的执行流程；</li><li>输出产物是否符合约定；</li><li>信息缺失、失败、边界场景下是否处理合理；</li><li>Skill 输出是否能被 Agent、Hook 或 Workflow 后续消费；</li><li>……</li></ul><p>根据上面的描述，我们可以得到 Skills 测试的边界：</p><figure class="highlight text"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">1. 触发是否准确；</span><br><span class="line">2. 流程是否完整；</span><br><span class="line">3. 输出契约是否稳定；</span><br><span class="line">4. 边界处理是否合理；</span><br><span class="line">5. 失败信息是否可回流；</span><br><span class="line">6. 是否避免幻觉和越权行为；</span><br><span class="line">7. ……</span><br></pre></td></tr></tbody></table></figure><p>例如，测试 <code>api-test-flow</code> Skill，不是要真的启动服务跑接口测试，而是验证模型：</p><figure class="highlight text"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">1. 是否会在相关 Query 下加载该 Skill</span><br><span class="line">2. 是否会按照该 Skill 的要求：</span><br><span class="line">    1. 读取 PRD；</span><br><span class="line">    2. 读取 API 文档；</span><br><span class="line">    3. 生成 API 测试用例；</span><br><span class="line">    4. 规划或执行 API 测试；</span><br><span class="line">    5. 解析测试结果；</span><br><span class="line">    6. 输出 api_test_report.json；</span><br><span class="line">    7. 失败时给出 suspected_owner 和 next_action；</span><br></pre></td></tr></tbody></table></figure><p>Skills 测试更像单元测试：它可以不依赖真实业务环境，而是验证 Skill 是否能稳定提供正确的方法、步骤和约束。这与 Agent 测试不同。Agent 测试的目标是验证 Agent 是否能在真实环境中正确完成任务。</p><p>例如，测试 <code>coding-agent</code> 时，就必须准备一个代码仓库，让 <code>coding-agent</code> 修复一个明确的编译错误，并期望结果是：</p><figure class="highlight text"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">正确修改错误代码；</span><br><span class="line">编译通过；</span><br><span class="line">测试用例通过；</span><br><span class="line">没有修改无关文件；</span><br><span class="line">……</span><br></pre></td></tr></tbody></table></figure><h2 id="skills-测试规范">Skills 测试规范</h2><p>根据上面的描述，在 Skills 测试中，一般需要覆盖如下场景<sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>：</p><table><thead><tr><th>类型</th><th>说明</th></tr></thead><tbody><tr><td><code>trigger</code></td><td>验证 Skill 是否应该触发，以及是否避免误触发</td></tr><tr><td><code>plan</code></td><td>验证 dry-run 模式下是否能给出符合 Skill 的执行计划</td></tr><tr><td><code>contract</code></td><td>验证输出产物、字段和格式是否稳定</td></tr><tr><td><code>edge</code></td><td>验证信息缺失、输入模糊、文档不完整等边界场景</td></tr><tr><td><code>failure</code></td><td>验证失败诊断、失败分类和回流策略</td></tr></tbody></table><p>我们可以用 YAML 文件组织一个 Skill 的测试用例：</p><figure class="highlight yaml"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">version:</span> <span class="string">"1.0"</span></span><br><span class="line"></span><br><span class="line"><span class="attr">skill:</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">"api-test-flow"</span></span><br><span class="line">  <span class="attr">description:</span> <span class="string">"验证 API 测试类 Skill 的触发、计划和输出契约"</span></span><br><span class="line"></span><br><span class="line"><span class="attr">cases:</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">"plan_001"</span></span><br><span class="line">    <span class="attr">type:</span> <span class="string">"plan"</span></span><br><span class="line">    <span class="attr">title:</span> <span class="string">"API 测试 Skill 应给出正确执行计划"</span></span><br><span class="line">    <span class="attr">query:</span> <span class="string">&gt;</span></span><br><span class="line"><span class="string">      请根据 PRD 验证登录接口是否符合预期。</span></span><br><span class="line"><span class="string"></span>    <span class="attr">inputs:</span></span><br><span class="line">      <span class="attr">files:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="string">"examples/login_prd.md"</span></span><br><span class="line">        <span class="bullet">-</span> <span class="string">"examples/login_api_doc.md"</span></span><br><span class="line">    <span class="attr">expected:</span></span><br><span class="line">      <span class="attr">skill:</span></span><br><span class="line">        <span class="attr">should_use:</span> <span class="string">"api-test-flow"</span></span><br><span class="line">        <span class="attr">should_not_use:</span> []</span><br><span class="line">      <span class="attr">must_include:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="string">"读取 PRD"</span></span><br><span class="line">        <span class="bullet">-</span> <span class="string">"读取 API 文档"</span></span><br><span class="line">        <span class="bullet">-</span> <span class="string">"生成 API 测试用例"</span></span><br><span class="line">        <span class="bullet">-</span> <span class="string">"执行或规划 API 测试"</span></span><br><span class="line">        <span class="bullet">-</span> <span class="string">"解析测试结果"</span></span><br><span class="line">        <span class="bullet">-</span> <span class="string">"输出 api_test_report"</span></span><br><span class="line">      <span class="attr">must_not_include:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="string">"直接修改业务代码"</span></span><br><span class="line">        <span class="bullet">-</span> <span class="string">"调用 UI 验收流程"</span></span><br><span class="line">    <span class="attr">assertions:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">"selected_skill == 'api-test-flow'"</span></span><br></pre></td></tr></tbody></table></figure><p>更详细的 Skills 测试规范可以参考 <a href="/Skills-Test-Specification/">Skill Test Case Specific</a>。</p><h2 id="skill-测试实践">Skill 测试实践</h2><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">skill-testing 介绍</p><p><code>skill-testing</code> 是一个用于测试 Skill 的 Skill，具体实现参见：<a href="https://github.com/wangwei1237/agent-skills/tree/main/skill-testing">https://github.com/wangwei1237/agent-skills/tree/main/skill-testing</a>。</p><p><strong>该 Skill 目前仅在 Codex 中完成验证，不保证在其他 Agent 中也能正常运行。如果使用过程中遇到问题，可以提交 Pull Request 或 Issue，我会尽快处理。</strong></p></div><p>我们把上面描述的 Skill 测试规范封装到了 <code>skill-testing</code> Skill 中。你可以直接将 <code>skill-testing</code> 安装到 Codex 或 Claude Code 中，用它来测试其他 Skills。</p><p>下面继续以 <a href="/2025/12/24/Reflections-on-Agent-Skills/">对 Agent Skills 的认知与思考</a><sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup> 这篇文章中的 <a href="https://github.com/wangwei1237/agent-skills/tree/main/pdf-lowercase-skill">pdf-case-converter</a> 为例，展示如何使用 <code>skill-testing</code> 测试 <code>pdf-case-converter</code>。</p><p><code>pdf-case-converter/tests/skill_cases.yaml</code> 的内容如下所示：</p><figure class="highlight yaml"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">version:</span> <span class="string">"1.0"</span></span><br><span class="line"></span><br><span class="line"><span class="attr">skill:</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">"pdf-case-converter"</span></span><br><span class="line">  <span class="attr">description:</span> <span class="string">"pdf-case-converter 的测试用例"</span></span><br><span class="line"></span><br><span class="line"><span class="attr">defaults:</span></span><br><span class="line">  <span class="attr">mode:</span> <span class="string">"dry-run"</span></span><br><span class="line">  <span class="attr">execution:</span> <span class="string">"no_tool_call"</span></span><br><span class="line">  <span class="attr">output_format:</span> <span class="string">"markdown"</span></span><br><span class="line"></span><br><span class="line"><span class="attr">cases:</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">"trigger_001"</span></span><br><span class="line">    <span class="attr">type:</span> <span class="string">"trigger"</span></span><br><span class="line">    <span class="attr">title:</span> <span class="string">"PDF 大小写转换场景应触发 Skill"</span></span><br><span class="line">    <span class="attr">query:</span> <span class="string">"请把 example/test.pdf 文档中的大写英文字母转换成小写"</span></span><br><span class="line">    <span class="attr">inputs:</span></span><br><span class="line">      <span class="attr">files:</span> []</span><br><span class="line">      <span class="attr">context:</span> {}</span><br><span class="line">    <span class="attr">expected:</span></span><br><span class="line">      <span class="attr">skill:</span></span><br><span class="line">        <span class="attr">should_use:</span> <span class="string">"pdf-case-converter"</span></span><br><span class="line">        <span class="attr">should_not_use:</span> []</span><br><span class="line">      <span class="attr">must_include:</span> []</span><br><span class="line">      <span class="attr">must_not_include:</span> []</span><br><span class="line">      <span class="attr">artifacts:</span> []</span><br><span class="line">      <span class="attr">routing:</span> {}</span><br><span class="line">    <span class="attr">assertions:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">"selected_skill == 'pdf-case-converter'"</span></span><br><span class="line">    <span class="attr">tags:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">"trigger"</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">"positive"</span></span><br><span class="line"></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">"trigger_002"</span></span><br><span class="line">    <span class="attr">type:</span> <span class="string">"trigger"</span></span><br><span class="line">    <span class="attr">title:</span> <span class="string">"图片生成场景不应触发 Skill"</span></span><br><span class="line">    <span class="attr">query:</span> <span class="string">"请生成一张夏季的海边风景的图片，并转换为 pdf 格式"</span></span><br><span class="line">    <span class="attr">inputs:</span></span><br><span class="line">      <span class="attr">files:</span> []</span><br><span class="line">      <span class="attr">context:</span> {}</span><br><span class="line">    <span class="attr">expected:</span></span><br><span class="line">      <span class="attr">skill:</span></span><br><span class="line">        <span class="attr">should_use:</span> <span class="string">""</span></span><br><span class="line">        <span class="attr">should_not_use:</span></span><br><span class="line">          <span class="bullet">-</span> <span class="string">"pdf-case-converter"</span></span><br><span class="line">      <span class="attr">must_include:</span> []</span><br><span class="line">      <span class="attr">must_not_include:</span> []</span><br><span class="line">      <span class="attr">artifacts:</span> []</span><br><span class="line">      <span class="attr">routing:</span> {}</span><br><span class="line">    <span class="attr">assertions:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">"selected_skill != 'pdf-case-converter'"</span></span><br><span class="line">    <span class="attr">tags:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">"trigger"</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">"negative"</span></span><br></pre></td></tr></tbody></table></figure><ol><li>在当前 Codex 中安装 <code>skill-testing</code> 和 <code>pdf-case-converter</code> 两个 Skill。</li><li>在 Codex 中输入测试提示词：请测试 skill: pdf-case-converter。<br><img src="/2026/06/12/How-to-evaluate-a-Skill/pdf_testing_1.png" alt=""></li><li>Codex 会根据 <code>skill-testing</code> 中的说明，加载 <code>pdf-case-converter/tests/skill_cases.yaml</code> 中的测试用例，生成 dry-run 测试响应，并对响应进行评分。<br><img src="/2026/06/12/How-to-evaluate-a-Skill/pdf_testing_2.png" alt=""></li><li>最后生成测试报告。<br><img src="/2026/06/12/How-to-evaluate-a-Skill/pdf_testing_3.png" alt=""></li></ol><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition warning"><p class="admonition-title">注意</p><p>默认情况下，我们会发现 Codex 会提示：<strong>多 agent 工具存在，但当前规则只允许在用户明确要求 sub-agent 时使用</strong>；所以这次我会按 skill-testing 的 fallback 路径，用当前 agent 生成干跑响应，并在 Downloads 下保存可复现的响应文件。<img src="/2026/06/12/How-to-evaluate-a-Skill/codex_1.png" alt=""></p><p>因此，为了强制让 Codex 生成 Sub-Agent 并在 Sub-Agent 中测试 Skills 的用例，我们需要改 SKill 测试的提示词：</p><blockquote><p>请使用 sub-agent 测试 skill: pdf-case-converter</p></blockquote><p><img src="/2026/06/12/How-to-evaluate-a-Skill/codex_2.png" alt=""></p></div><h2 id="总结">总结</h2><p>Skill 是一种 <strong>Procedural Knowledge</strong>。它不是普通文档知识，也不是可独立执行的 Agent，而是模型完成某类任务时需要遵循的方法、流程和契约。只有明确 Skills 与 Agents 之间的区别，认识到 Skills 的本质，才能将 Skills 从一份提示词文档升级为可测试、可维护、可复用的 AI 工程资产。</p><p>当然，我们的实践还刚刚开始。当 Skills 变成 Agent 系统的基础设施，评估能力就不再是可选项，而是让 Agent 工程可维护、可回归、可扩展的前提。</p><h2 id="参考文献">参考文献</h2><hr class="footnotes-sep"><section class="footnotes"><ol class="footnotes-list"><li id="fn1" class="footnote-item"><p><a href="https://code.claude.com/docs/en/features-overview#extend-claude-code">Extend Claude Code</a> <a href="#fnref1" class="footnote-backref">↩︎</a> <a href="#fnref1:1" class="footnote-backref">↩︎</a></p></li><li id="fn2" class="footnote-item"><p><a href="https://www.kaggle.com/whitepaper-context-engineering-sessions-and-memory">Context Engineering: Sessions &amp; Memory</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p></li><li id="fn3" class="footnote-item"><p><a href="/Skills-Test-Specification/">Skill Test Case Specific</a> <a href="#fnref3" class="footnote-backref">↩︎</a></p></li><li id="fn4" class="footnote-item"><p><a href="/2025/12/24/Reflections-on-Agent-Skills/">对 Agent Skills 的认知与思考</a> <a href="#fnref4" class="footnote-backref">↩︎</a></p></li></ol></section></body></html>]]>
      </content:encoded>
    </item>
    <item>
      <title>如何利用 Harness “一句话交付产品功能”？</title>
      <link>https://wangwei1237.github.io/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/</link>
      <description>
        <![CDATA[<html><head></head><body><p>为了更快的发布 <a href="https://lj.baidu.com/sbs/">大模型评测平台</a>，我们借鉴 <code>OpenAI</code> 的思路搭建了一套 <code>Harness</code> 模式，之前需要 1 天开发完的功能，现在基本上 2 个小时左右就可以完成。</p>
<p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-08.png" alt=""></p></body></html>]]>
      </description>
      <author>Wang Wei</author>
      <category domain="https://wangwei1237.github.io/categories/LLM/">LLM</category>
      <category domain="https://wangwei1237.github.io/tags/Harness-Engineering/">Harness Engineering</category>
      <pubDate>Thu, 28 May 2026 16:43:01 GMT</pubDate>
      <content:encoded>
        <![CDATA[<html><head></head><body><p>为了更快的发布 <a href="https://lj.baidu.com/sbs/">大模型评测平台</a>，我们借鉴 <code>OpenAI</code> 的思路搭建了一套 <code>Harness</code> 模式，之前需要 1 天开发完的功能，现在基本上 2 个小时左右就可以完成。</p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-08.png" alt=""></p><span id="more"></span><h2 id="1-背景">1. 背景</h2><p>在 <a href="/2026/03/15/I-use-OpenClaw-to-manage-LLMs-evals-tasks/">我让 OpenClaw 🦞 管理大模型评测任务</a> 中提到，我们通过自建的 <code>Side-by-Side</code> 平台来实现多模态大模型的偏好打分以及对抗榜单结果。在 2026 年 2 月平台刚上线的时候，还只支持最基本的 <code>Side-by-Side</code> 的功能，比如只支持生图大模型的评测，并且对于图生图大模型而言，仅支持参考图片为单图的评估场景……因此，我们的评测平台属于是一个功能不断丰富、扩展的状态。平台功能的升级一方面来自参与到实际评估中的用户的真实反馈，另一方面来自我们对平台功能点的规划。</p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-23.png" alt="Side-by-Side 效果"></p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-27.png" alt="用户反馈"></p><p>在实现上，平台由 <code>React</code> 的前端服务 <code>versus-fe</code> 和 <code>Go</code> 的后端服务 <code>versus-server</code> 构成，并且对于前后端的这 2 个服务，我们分别安排了一个同学来负责。整体看，这个系统也属于：麻雀虽小、五脏俱全。</p><h2 id="2-vibe-coding">2. Vibe Coding</h2><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p><code>Vibe Coding</code> 是“AI 代写代码，人来统筹状态”；而 <code>Harness</code> 是“代码库即唯一事实，<code>Agent</code> 自治流转状态”。</p></div><p>最开始的时候，我们的前后端开发人员各自使用 <code>DUCC</code> 通过 <code>Vibe Coding</code> 的方式来编码前后端服务，然后再进行联调测试，最后再利用我们内部的上线平台发布新功能。</p><pre class="mermaid">flowchart LR  A[新功能] --&gt; B[FE 模块]  A --&gt; C[Server 模块]  B --&gt; D[Vibe Coding]  C --&gt; E[Vibe Coding]  D --&gt; F[联调]  E --&gt; F  F --&gt; G[功能发布]</pre><p>可以看到，<code>Vibe Coding</code> 模式下，开发过程中的唯一的变化就是代码的实现由人变成了 AI。而新功能的拆解，模块之间的联调，代码的迭代调整还都是人来驱动的，不同的是，人的产物不再是代码，而是自然语言。我们最开始并不太懂 <code>React</code>，但是整个平台在 AI 的帮助下，也就这么构建起来了。无疑，<code>Vibe Coding</code> 极大地降低了产品开发的门槛。</p><pre class="mermaid">graph    %% 新模式 下半区    subgraph 新AI开发模式        N1[人] --自然语言--&gt; N2[DUCC]        N2 -- 代码 --&gt; N3[产品]    end            %% 旧模式 上半区    subgraph 旧开发模式        A[人] -- 代码 --&gt; B[产品]    end</pre><p>慢慢地，我们发现，虽然我们在 <code>CLAUDE.md</code> 中补充了很多的知识，虽然 <code>Vibe Coding</code> 确实提升了我们的开发效率，但是有时候我们总是会为了调整页面的某个元素而浪费几十分钟的时间，而人去改动这些代码可能只需要几分钟。更让人接受不了的是，整个的改动过程会出现越改越糟的问题，这实在是令人抓狂。</p><p>当提示词比较长的时候，我们希望增加一个展开全部的功能，避免过长的提示词占据屏幕的整个区域，从而提升打分过程的交互体验。就是这么一个小小的改动，我们来来回回和 <code>DUCC</code> 沟通了 2 个多小时，中途多少次我都萌生了亲自去写这块代码的冲动……后来，<code>/clear</code> 了上下文，还原了所有的代码改动，重新给新的提示词，才完成了最后的效果。</p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-22.png" alt=""></p><p>麻了，我再也不想和 AI 废话了，我不想写代码，同时我也不想和 AI 来来回回的聊天了。在我用 <code>Harness Engineering</code> 来进行书籍翻译工作之后，我想，是时候让 <code>Harness</code> 发挥更大的价值了。</p><h2 id="3-对-harness-的思考">3. 对 Harness 的思考</h2><p>最开始有这个想法的时候，我的心里也犯嘀咕：</p><ul><li><p>项目架构并不复杂，有必要搞 <code>Harness</code> 吗？</p></li><li><p><code>Harness</code> 还并没有跑出一套可复制的成功路线，有必要探索吗？</p></li><li><p><code>Harness</code> 真的靠谱吗？</p></li><li><p>……</p></li></ul><p>但是，在再次精读了 <code>OpenAI</code> 的 <a href="https://openai.com/index/harness-engineering/">Harness engineering: leveraging Codex in an agent-first world</a> 与 <code>Anthropic</code> 的 <a href="https://www.anthropic.com/engineering/harness-design-long-running-apps">Harness design for long-running application development</a> 后，我认为这个事情有搞头。</p><ul><li><p>在 <code>OpenAI</code> 的 Case 中，他们也是基于内部的一个工具来实验的，这个和我们的平台的发展阶段类似。</p></li><li><p>从 <code>UI</code> 交互复杂性而言，我们的平台不涉及特别复杂的交互，因此在 <code>Harness</code> 的校验闭环中，更具备可操作性。</p></li><li><p>对于如此轻量级架构的工程，如果 <code>Harness</code> 都无法胜任，那么 <code>Harness</code> 也不可能胜任更复杂的系统。</p></li></ul><h2 id="4-harness-初探">4. Harness 初探</h2><h3 id="4-1-构建单仓代码库">4.1 构建单仓代码库</h3><p>有了基本的判断之后，我们小组 4 个同学凑到了一起开始讨论具体的技术路线。我们采用了 <code>OpenAI</code> 的：“代码库作为 <code>Agent</code> 的唯一知识来源” 的思路来构建我们的 <code>Harness</code>，当我们发现 <code>Agent</code> 行为出现偏差的时候，我们就在对应的地方补充适当的约束来对 <code>Agent</code> 进行限制。</p><blockquote><p>From the agent’s point of view, anything it can’t access in-context while running effectively doesn’t exist. Knowledge that lives in Google Docs, chat threads, or people’s heads are not accessible to the system. Repository-local, versioned artifacts (e.g., code, markdown, schemas, executable plans) are all it can see.</p></blockquote><p>但是，与 <code>OpenAI</code> 从一个空代码库开始构建不同，我们的系统已经拆分为 <code>versus-fe</code> 和 <code>versus-server</code> 两个代码仓库了，并且我们已经把代码库的相关知识写入了各自的 <code>CLAUDE.md</code> 文件中。而对于两个仓库之间的知识，则存储在了人的经验中，而这恰好是 <code>Agent</code> 无法访问到的知识。而暴力地把两个已经存在的代码库强制合并到一个仓库中，又涉及到比较大的改动：</p><ul><li><p>目录结构的调整</p></li><li><p>CI 任务的调整</p></li><li><p>上线方式的调整</p></li></ul><p>正如 Peter Pang 在文章 <a href="https://x.com/intuitiveml/status/2043545596699750791">Why Your “AI-First” Strategy Is Probably Wrong</a> 中所述：</p><blockquote><p>I had to unify all the code into a single monorepo. One reason: so AI could see everything.</p></blockquote><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p><code>AI-first</code> 意味着我们必须以 AI 为核心重塑我们的流程、架构、组织模式，而不是让 AI 来适配当下的架构。根据“康威法则”，团队怎么分工、如何沟通，代码仓库就会变成对应的结构。因此，要实现 <code>OpenAI</code> 的博客中的 <code>Harness</code> 模式，必须首先重构代码仓库的组织方式。</p></div><p>理念是个好理念，但是具体实施起来，看起来有点麻烦。我们想改，但是又不想花费太大的力气。</p><p>最终，我们采用 <code>Git Submodule</code> 的方式实现了代码仓库的 <code>AI-First</code> 重构。我们首先建了一个主仓库 <code>versus</code>，<code>versus-fe</code> 和 <code>versus-server</code> 仓库则以子模块的形式整合到主仓库中。</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line">versus/                    <span class="comment"># 父仓库</span></span><br><span class="line">├── versus-server/         <span class="comment"># 服务端子模块 (Go + Gin)</span></span><br><span class="line">|   └── CLAUDE.md              <span class="comment"># 服务端 Claude Code 项目指令</span></span><br><span class="line">├── versus-fe/             <span class="comment"># 前端子模块 (React + Vite)</span></span><br><span class="line">|   └── CLAUDE.md              <span class="comment"># 前端 Claude Code 项目指令</span></span><br><span class="line">├── scripts/               <span class="comment"># 管理脚本</span></span><br><span class="line">│   ├── setup.sh               <span class="comment"># 初始化开发环境</span></span><br><span class="line">│   ├── pull-all.sh            <span class="comment"># 拉取所有子模块</span></span><br><span class="line">│   ├── status.sh              <span class="comment"># 查看仓库状态</span></span><br><span class="line">│   └── sync-user-config.sh    <span class="comment"># 同步用户配置</span></span><br><span class="line">├── .claude/               <span class="comment"># Claude Code 配置与 Agent 定义</span></span><br><span class="line">│   ├── settings.json          <span class="comment"># Agent 与 MCP 插件全局配置</span></span><br><span class="line">│   ├── settings.local.json    <span class="comment"># 本地工具权限授权（不入库）</span></span><br><span class="line">│   ├── agents/                <span class="comment"># 6 个协作 Agent 定义文件</span></span><br><span class="line">│   │   ├── requirement-designer.md</span><br><span class="line">│   │   ├── go-api-implementer.md</span><br><span class="line">│   │   ├── frontend-engineer.md</span><br><span class="line">│   │   ├── test-case-designer.md</span><br><span class="line">│   │   ├── integration-test-runner.md</span><br><span class="line">│   │   └── e2e-test-executor.md</span><br><span class="line">│   └── agent-memory/          <span class="comment"># Agent 跨会话记忆</span></span><br><span class="line">│       ├── requirement-designer/MEMORY.md</span><br><span class="line">│       ├── go-api-implementer/MEMORY.md</span><br><span class="line">│       ├── frontend-engineer/MEMORY.md</span><br><span class="line">│       ├── test-case-designer/MEMORY.md</span><br><span class="line">│       ├── integration-test-runner/MEMORY.md</span><br><span class="line">│       └── e2e-test-executor/MEMORY.md</span><br><span class="line">├── docs/                  <span class="comment"># 项目文档与需求产物</span></span><br><span class="line">│   ├── system-architecture.md           <span class="comment"># 系统架构说明</span></span><br><span class="line">│   ├── 6-agent-collaboration-summary.md <span class="comment"># 6-Agent 协作流水线总结</span></span><br><span class="line">│   └── requirements/                    <span class="comment"># 各需求流水线产物（按 REQ-ID 组织）</span></span><br><span class="line">│       ├── REQ-001/      </span><br><span class="line">│       ├── REQ-CASE-RESOURCE/ </span><br><span class="line">│       ├── REQ-VIDEO-TYPES/   </span><br><span class="line">│       ├── REQ-020/      </span><br><span class="line">│       └── REQ-ERROR-CODE/</span><br><span class="line">├── .gitmodules            <span class="comment"># 子模块配置（已被 .gitignore 忽略）</span></span><br><span class="line">├── .gitignore             <span class="comment"># Git 忽略规则</span></span><br><span class="line">├── CLAUDE.md              <span class="comment"># 项目整体知识</span></span><br><span class="line">├── dev.sh                 <span class="comment"># 一键启动前后端服务</span></span><br><span class="line">├── README.md              <span class="comment"># 项目说明</span></span><br><span class="line">└── FEATURES.md            <span class="comment"># 功能清单</span></span><br></pre></td></tr></tbody></table></figure><ul><li><p><code>versus</code> 目录下的 <code>CLAUDE.md</code> 主要存放跨前后端、跨 <code>Agent</code>、跨需求都需要知道的全局知识，相当于整个项目的作战地图 + 协作规则 + 产品背景 + 全局约束。</p></li><li><p><code>AI Coding</code> 和 <code>AI Testing</code> 过程中需要的相关 <code>Agent</code>、产生的需求文档、测试用例列表、测试执行结果均存储于 <code>versus</code> 的主仓库目录下，从而真正实现了“一切皆在代码库，代码库之外，别无他物”的目标。</p></li><li><p><code>versus-fe</code> 和 <code>versus-server</code> 目录下的 <code>CLAUDE.md</code> 则存放前端和服务端相关的知识，包括架构、约束、编码规范等知识。</p></li><li><p><code>Git Submodule</code> 增加了仓库操作的复杂性，比如拉取主仓库代码时，需要递归拉取各子仓库的代码；而当子仓库的代码有修改需要提交时，也需要同时提交主仓库以更新主仓库对子仓库的版本索引。因此，为了降低仓库的操作复杂度，我们同时让 <code>DUCC</code> 帮我们实现了一系列的仓库管理脚本，从而实现通过自然语言交互就能稳定实现相关功能的目的。</p></li></ul><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-11.png" alt="一句话更新主仓库、子仓库的代码"></p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-07.png" alt="一句话部署前后端代码，并用浏览器打开首页"></p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-24.png" alt=""></p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-06.png" alt=""></p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-10.png" alt=""></p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-26.png" alt=""></p><h3 id="4-2-harness-闭环设计">4.2 Harness 闭环设计</h3><p><code>Anthropic</code> 的 <a href="https://www.anthropic.com/engineering/harness-design-long-running-apps">Harness design for long-running application development</a> 博客中提到当前的模型具备如下的问题：</p><ul><li><p>上下文焦虑：当模型感知到即将达到自身上下文上限时，就会出现模型提前结束任务的现象。</p></li><li><p>自视甚高：当智能体评价自己完成的工作时，即便这些产出在人类看来平平无奇时，他们也会自信地大加夸赞。</p></li></ul><p>为了解决如上的问题，我们按照软件开发的端到端流程拆分了 6 个基本的 <code>Sub-Agent</code>。</p><table><thead><tr><th><code>Sub-Agent</code> 名称</th><th>职责</th><th>输入</th><th>输出</th></tr></thead><tbody><tr><td><code>requirement-designer</code></td><td>将简要需求描述转化为企业级 <code>PRD</code> 文档</td><td>用户原始的一句话需求描述</td><td><code>PRD.md</code> + 需求 ID</td></tr><tr><td><code>go-api-implementer</code></td><td>根据 <code>PRD.md</code> 实现 <code>Go</code> 后端 <code>API</code></td><td><code>PRD.md</code></td><td><code>Go</code> 代码 + <code>api-summary.md</code></td></tr><tr><td><code>frontend-engineer</code></td><td>根据 <code>PRD.md</code> + <code>api-summary.md</code> 实现 <code>React</code> 前端页面</td><td><code>PRD.md</code> + <code>api-summary.md</code></td><td><code>FE</code> <code>React</code> 代码</td></tr><tr><td><code>test-case-designer</code></td><td>设计 <code>API</code> 接口测试用例和 <code>E2E</code> 测试用例</td><td><code>PRD</code> + 后端代码 + 前端代码</td><td><code>api-test-cases.json</code> + <code>e2e-test-cases.json</code></td></tr><tr><td><code>integration-test-runner</code></td><td>启动服务、执行 <code>API</code> 集成测试、产出报告</td><td><code>api-test-cases.json</code></td><td><code>integration-test-report.md</code></td></tr><tr><td><code>e2e-test-executor</code></td><td>通过 <code>Chrome DevTools MCP</code> 执行浏览器 <code>E2E</code> 测试</td><td><code>e2e-test-cases.json</code></td><td>截图 + <code>result.md</code> + <code>summary.md</code></td></tr></tbody></table><p>通过如上 6 个 <code>Sub-Agent</code> 的全流程自动流转和验证，形成完整的、自我完善的 <code>Harness</code> 开发闭环。</p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-16.png" alt=""></p><p>整个的闭环流程，除了 <code>PRD</code> 阶段需要人工审核以外，其他阶段均不需要人工介入。尤其是在前、后端开发过程中，<code>Harness</code> 避免了前后端 <code>RD</code> 之间的沟通与协作成本，极大地提升了开发的效率。</p><pre class="mermaid">flowchart TD  A[用户触发: 启动 6 个 Agent 协作实现需求] --&gt; B[1. requirement-designer\n产出 PRD.md, 分配 REQ-ID]  B --&gt; C[人工审核 PRD\n唯一的用户确认节点]  C --&gt;|用户确认通过| D[2. go-api-implementer\n产出 Go 后端代码 + api-summary.md]  D --&gt;|status=completed 自动流转| E[3. frontend-engineer\n产出 React 前端代码]  E --&gt;|status=completed 自动流转| F[4. test-case-designer\n产出 api-test-cases.json + e2e-test-cases.json]  F --&gt; G[5. integration-test-runner\n端口: 8899/3000\nAPI 接口测试]  F --&gt; H[6. e2e-test-executor\n端口: 8901/3001\n浏览器 E2E 测试]  G --&gt;|has_bugs| I[后端 Bug]  H --&gt;|has_bugs| J[前端 Bug]  I --&gt; D  J --&gt; E  G --&gt;|all_passed| K[编码任务完成]  H --&gt;|all_passed| K</pre><p>以我们的一个实际的需求——为视频大模型增加评估任务类型——为例，具体的操作如下所示：</p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-14.png" alt="启动任务"></p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-21.png" alt="requirement-designer Agent 启动，生成需求文档"></p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-15.png" alt="等待人工确认需求文档"></p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-01.png" alt="需求确认，修改必要的需求约束"></p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-03.png" alt="确认修改后的 PRD 之后，自动启动后端 AI Coding Agent"></p><h3 id="4-3-agent-handoff-协议">4.3 Agent Handoff 协议</h3><p>采用 <code>Sub-Agent</code> 的方式可以解决单 <code>Agent</code> 中存在的“上下文焦虑”和“自视甚高”的问题，但也带来了新的问题：如何在不同的 <code>Sub-Agent</code> 之间安全、结构化地转移“控制权”与“上下文状态”？如果无法有效地解决 <code>Sub-Agent</code> 之间的交接，那么 <code>Agent</code> 之间就成了 “哑巴交流”——任务断连、状态错乱、故障恢复、链路追溯——都将导致异常灾难，最终完全失去了 <code>Sub-Agent</code> 分布式协作的价值。</p><p>为此，我们实现了一个简洁的 <code>Agent Handoff</code> 协议：当上一个 <code>Agent</code> 完成任务后，它会通过 <code>Handoff</code> 协议将任务状态、已收集的信息以及下一步的执行意图，规范地传递给下一个 <code>Agent</code>。</p><p>每个 <code>Agent</code> 在结束之前，必须附加如下所示的 <code>Handoff</code> 块，以完成任务的交接：</p><figure class="highlight md"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">---AGENT-HANDOFF---</span><br><span class="line">requirement-id: REQ-001</span><br><span class="line">status: completed | awaiting<span class="emphasis">_review | has_</span>bugs | all<span class="emphasis">_passed</span></span><br><span class="line"><span class="emphasis">output: 产出的文件路径</span></span><br><span class="line"><span class="emphasis">next-step: 下一步动作描述</span></span><br><span class="line"><span class="emphasis">next-step-prompt: 启动下一个 Agent 时使用的 prompt</span></span><br><span class="line"><span class="emphasis">after-approval-next-step: 审核通过后的动作（仅 awaiting_</span>review）</span><br><span class="line">after-approval-prompt: 审核通过后的 prompt（仅 awaiting<span class="emphasis">_review）</span></span><br><span class="line"><span class="emphasis">bugs: Bug 列表（仅 has_</span>bugs）</span><br><span class="line">review-message: 审核提示（仅 awaiting<span class="emphasis">_review）</span></span><br><span class="line"><span class="emphasis">---END-HANDOFF---</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight md"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">---AGENT-HANDOFF---</span><br><span class="line">requirement-id: REQ-021</span><br><span class="line">status: awaiting<span class="emphasis">_review</span></span><br><span class="line"><span class="emphasis">output: docs/requirements/REQ-021/PRD.md</span></span><br><span class="line"><span class="emphasis">next-step: wait_</span>for<span class="emphasis">_human_</span>approval</span><br><span class="line">after-approval-next-step: launch go-api-implementer</span><br><span class="line">after-approval-prompt: \"根据需求 REQ-021 的 PRD 实现后端接口。PRD路径: docs/requirements/REQ-021/PRD.md\"</span><br><span class="line">review-message: \"PRD 已生成，请审核 docs/requirements/REQ-021/PRD.md，确认无误后回复'继续'启动后端实现\"</span><br><span class="line">---END-HANDOFF---</span><br></pre></td></tr></tbody></table></figure><p><code>Handoff</code> 协议中各状态的含义如下：</p><table><thead><tr><th><code>status</code></th><th>含义</th><th>主会话动作</th></tr></thead><tbody><tr><td><code>awaiting_review</code></td><td>需要人工审核</td><td>暂停流水线，展示 <code>review-message</code>，等待用户确认</td></tr><tr><td><code>completed</code></td><td>当前阶段完成</td><td>自动按 <code>next-step</code> 启动下一个 <code>Agent</code></td></tr><tr><td><code>has_bugs</code></td><td>测试发现缺陷</td><td>根据 <code>bugs</code> 列表路由给对应 <code>Agent</code> 修复</td></tr><tr><td><code>all_passed</code></td><td>所有测试通过</td><td>流水线结束，向用户报告完成</td></tr></tbody></table><p>当测试报告结果为 <code>has_bugs</code> 状态时：</p><ol><li><p>主会话解析 <code>bugs</code> 列表，确定 <code>Bug</code> 类型（后端/前端）；</p></li><li><p>根据 <code>Bug</code> 类型分发给对应的 <code>Agent</code> 进行缺陷修复：</p></li></ol><ul><li><p>后端 <code>Bug</code> → <code>go-api-implementer</code></p></li><li><p>前端 <code>Bug</code> → <code>frontend-engineer</code></p></li></ul><ol start="3"><li><p>修复完成后，重新启动对应的测试 <code>Agent</code> 来验证修复结果</p></li><li><p>循环如上过程，直到所有测试用例全部通过，测试报告达到 <code>all_passed</code> 状态为止。</p></li></ol><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p>在我们的实现中，<code>Handoff</code> 块的内容会直接写入到主 <code>session</code>，而不会写入本地文件。没有必要引入过多的存储结构来增加交接过程的复杂度。</p></div><p>主 <code>Agent</code> 通过 <code>Handoff</code> 协议来调度各 <code>Sub-Agent</code> 的自动化执行。</p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-02.png" alt="编码 Agent 与 测试 Agent"></p><h3 id="4-4-测试用例生成">4.4 测试用例生成</h3><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p>没有必要手写 <code>Agent</code> 的具体内容，可以直接使用 <code>DUCC</code> 的 <code>/agents</code> 让 <code>DUCC</code> 自动为我们实现相关的 <code>Agent</code>。</p></div><p>如前所述，在我们的工程中，我们把工程用到的所有的 <code>Agent</code> 描述都放在了 <code>versus/.claude/agents</code> 目录下，作为团队资产公用。<code>test-case-designer</code> <code>Agent</code> 的描述如下所示：</p><figure class="highlight md"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">name: test-case-designer</span><br><span class="line">description: |</span><br><span class="line"><span class="section">  Use this agent when you need to generate comprehensive test cases for a feature based on requirement documents, technical specifications, API implementations, and frontend implementations. This agent designs both API interface test cases and end-to-end (E2E) web test cases in JSON format. It does NOT execute tests - it only creates test case specifications.</span></span><br><span class="line"><span class="section">---</span></span><br><span class="line"></span><br><span class="line">You are an elite Software Quality Assurance Engineer and Test Architect with deep expertise in software testing methodologies, test case design, and quality engineering. You specialize in creating comprehensive, well-structured test cases that ensure software reliability and quality.</span><br><span class="line"></span><br><span class="line"><span class="section">## Your Core Mission</span></span><br><span class="line"></span><br><span class="line">You design test cases for features based on:</span><br><span class="line"><span class="bullet">-</span> Requirement documents (需求文档)</span><br><span class="line"><span class="bullet">-</span> Technical specifications (技术方案)</span><br><span class="line"><span class="bullet">-</span> API implementations (接口实现)</span><br><span class="line"><span class="bullet">-</span> Frontend implementations (前端实现)</span><br><span class="line"></span><br><span class="line">You produce two types of test cases:</span><br><span class="line"><span class="bullet">1.</span> <span class="strong">**API Interface Test Cases**</span> (接口测试用例)</span><br><span class="line"><span class="bullet">2.</span> <span class="strong">**End-to-End Web Test Cases**</span> (端到端测试用例)</span><br><span class="line"></span><br><span class="line"><span class="strong">**IMPORTANT**</span>: You ONLY design test cases - you do NOT execute them. Your output is JSON-formatted test case specifications.</span><br><span class="line"></span><br><span class="line"><span class="section">## 产物规范</span></span><br><span class="line"></span><br><span class="line"><span class="section">### 输入</span></span><br><span class="line"><span class="bullet">-</span> PRD 文档：<span class="code">`docs/requirements/{REQ-ID}/PRD.md`</span>（由 requirement-designer 产出）</span><br><span class="line"><span class="bullet">-</span> API 摘要：<span class="code">`docs/requirements/{REQ-ID}/api-summary.md`</span>（由 go-api-implementer 产出）</span><br><span class="line"><span class="bullet">-</span> 后端代码：<span class="code">`versus-server/`</span> 目录（由 go-api-implementer 产出）</span><br><span class="line"><span class="bullet">-</span> 前端代码：<span class="code">`versus-fe/`</span> 目录（由 frontend-engineer 产出）</span><br><span class="line"></span><br><span class="line"><span class="section">### 输出</span></span><br><span class="line"><span class="bullet">-</span> API 测试用例：<span class="strong">**必须**</span>保存到 <span class="code">`docs/requirements/{REQ-ID}/api-test-cases.json`</span></span><br><span class="line"><span class="bullet">-</span> E2E 测试用例：<span class="strong">**必须**</span>保存到 <span class="code">`docs/requirements/{REQ-ID}/e2e-test-cases.json`</span></span><br><span class="line"></span><br><span class="line"><span class="section">### 下游触发</span></span><br><span class="line">测试用例设计完成后，提示主会话<span class="strong">**并行启动**</span> integration-test-runner 和 e2e-test-executor，传入需求 ID。</span><br><span class="line"></span><br><span class="line"><span class="section">## Handoff 输出（MUST）</span></span><br><span class="line"></span><br><span class="line">测试用例设计完成后，你 MUST 在输出末尾输出以下格式的 Handoff 块：</span><br><span class="line"></span><br><span class="line"><span class="code">```</span></span><br><span class="line"><span class="code">---AGENT-HANDOFF---</span></span><br><span class="line"><span class="code">requirement-id: {当前REQ-ID}</span></span><br><span class="line"><span class="code">status: completed</span></span><br><span class="line"><span class="code">output: docs/requirements/{REQ-ID}/api-test-cases.json, docs/requirements/{REQ-ID}/e2e-test-cases.json</span></span><br><span class="line"><span class="code">next-step: launch integration-test-runner AND e2e-test-executor in parallel</span></span><br><span class="line"><span class="code">next-step-prompt-integration: "启动服务(后端8899/前端3000)并执行需求 {REQ-ID} 的 API 集成测试。测试用例路径: docs/requirements/{REQ-ID}/api-test-cases.json"</span></span><br><span class="line"><span class="code">next-step-prompt-e2e: "启动服务(后端8901/前端3001)并执行需求 {REQ-ID} 的浏览器 E2E 测试。测试用例路径: docs/requirements/{REQ-ID}/e2e-test-cases.json"</span></span><br><span class="line"><span class="code">---END-HANDOFF---</span></span><br><span class="line"><span class="code">```</span></span><br><span class="line">...</span><br><span class="line">...</span><br></pre></td></tr></tbody></table></figure><p>最终生成的接口测试用例如下所示：</p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-05.png" alt="接口测试用例"></p><h3 id="4-5-e2e-浏览器测试">4.5 E2E 浏览器测试</h3><p>我们使用 <code>Chrome DevTools MCP</code> 来实现端到端的浏览器自动化测试。直接在 <code>DUCC</code> 的对话框输入：</p><blockquote><p>安装 <a href="https://github.com/ChromeDevTools/chrome-devtools-mcp">https://github.com/ChromeDevTools/chrome-devtools-mcp</a> 插件</p></blockquote><p>就可以完成 <code>Chrome DevTools MCP</code> 的安装。关于 <code>Chrome DevTools MCP</code> 更详细的介绍可以参见：通过 <code>Chrome DevTools MCP</code> 增强 <code>Agent</code> 的浏览器操控能力。</p><p><code>E2E</code> 测试中使用的 <code>Chrome DevTools MCP</code> 工具分为三类：</p><ul><li><p>导航与页面管理</p><ul><li><p><code>navigate_page</code>：导航到指定 URL</p></li><li><p><code>new_page</code>：打开新标签页</p></li><li><p><code>list_pages</code>：列出所有打开的页面</p></li><li><p><code>select_page</code>：选择当前操作的页面</p></li><li><p><code>close_page</code>：关闭页面</p></li></ul></li><li><p>页面内容获取</p><ul><li><p><code>take_snapshot</code>：获取页面 <code>a11y</code> 树文本快照（含元素 uid）</p></li><li><p><code>take_screenshot</code>：截取页面或元素截图</p></li><li><p><code>list_console_messages</code>：列出控制台消息</p></li><li><p><code>list_network_requests</code>：列出网络请求</p></li><li><p><code>get_network_request</code>：获取网络请求详情</p></li><li><p><code>evaluate_script</code>：在页面中执行 JavaScript</p></li></ul></li><li><p>页面交互</p><ul><li><p><code>click</code>：点击页面元素</p></li><li><p><code>fill</code>：在输入框中填写内容</p></li><li><p><code>hover</code>：悬停在页面元素上</p></li><li><p><code>press_key</code>：按键或组合键操作</p></li><li><p><code>type_text</code>：在当前焦点输入框中输入文本</p></li></ul></li></ul><p><code>e2e-test-executor</code> <code>Agent</code> 的执行过程分为三个阶段：</p><ul><li><p>环境准备：<code>Agent</code> 首先在独立端口（后端 8901、前端 3001）上构建并启动前后端服务，确保与集成测试环境互不干扰。服务就绪后，通过 <code>Chrome DevTools MCP</code> 建立与浏览器的连接。</p></li><li><p>逐用例执行：对每个测试用例，<code>Agent</code> 按以下步骤操作——先 <code>navigate_page</code> 导航到目标页面，再 <code>take_snapshot</code> 获取页面 <code>a11y</code> 树快照以定位元素，然后按用例定义的步骤逐步执行交互操作（<code>click</code>、<code>fill</code>、<code>type</code> 等）。每执行完一步，会调用 <code>take_screenshot</code> 截图留存，最后对比实际页面状态与预期结果，并分析用例执行结果。</p></li><li><p>结果判定与反馈：所有用例执行完毕后，若无用例执行失败则生成 <code>summary.md</code> 并对截图进行归档，流水线以 <code>all_passed</code> 结束。若存在执行失败的用例，则根据 <code>Bug</code> 类型分发给对应的 <code>Coding Agent</code> 修复（后端 <code>Bug</code> → <code>go-api-implementer</code>，前端 <code>Bug</code> → <code>frontend-engineer</code>），修复后重新启动服务并重跑失败用例，循环直到全部通过。</p></li></ul><p>整体过程如下所示：</p><pre class="mermaid">flowchart TD    A["e2e-test-executor 启动"] --&gt; C["启动前、后端服务"]    C --&gt; D["通过 Chrome DevTools MCP 控制浏览器"]    D --&gt; E    subgraph E ["对每个测试用例"]        direction LR        E1["1. 进入指定页面"] --&gt; E2["2. take_snapshot 获取页面"]        E2 --&gt; E3["3. 按步骤执行"]        E3 --&gt; E4["4. 截图"]        E4 --&gt; E5["5. 验证并记录结果"]    end    E --&gt; G{"是否有失败用例?"}    G --&gt;|全部通过| F["生成 summary.md + 截图归档<br>status=all_passed"]    G --&gt;|存在失败| H{"Bug 类型?"}    H --&gt;|后端 Bug| I["go-api-implementer 修复"]    H --&gt;|前端 Bug| J["frontend-engineer 修复"]    I --&gt; K["重新启动服务 &amp; 重跑失败用例"]    J --&gt; K    K --&gt; E</pre><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-13.png" alt="API 测试&amp;修复的自闭环流程"></p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-17.png" alt="API 测试&amp;修复的自闭环流程"></p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-25.png" alt="E2E 执行过程截图结果"></p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-09.png" alt="主 Agent 任务完成并输出总结内容"></p><h2 id="5-文档更新">5. 文档更新</h2><p>随着项目不停的迭代，新增加的功能越来越多，对旧需求的修改也越来越多。慢慢地，项目代码库中的文档知识就开始出现“熵增”的问题了。例如：我们已经把平台的登录系统由 <code>UUAP</code> 升级到了 <code>Passport</code>，但是 <code>versus/CLAUDE.md</code> 中的描述却没有及时更新，这将严重影响 <code>Agent</code> 后续的执行性能。</p><p>这是一个比较合适的 <code>/goal</code> 应用场景，于是，我打开了 <code>Codex CLI</code>，并输入了如下的检查要求：</p><figure class="highlight md"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">/goal 执行一次“文档与代码一致性同步”任务，直到满足完成条件。</span><br><span class="line"></span><br><span class="line">  目标：</span><br><span class="line">  检查并更新 CLAUDE.md、README.md、docs/<span class="strong">**/<span class="emphasis">*.md 以及其他 Markdown 文档，使其与当前代码、测试、配置、脚本、类型定义、API 路由、CLI 命令、环境变量和实际目录结构保持一致。</span></span></span><br><span class="line"><span class="emphasis"><span class="strong"></span></span></span><br><span class="line"><span class="emphasis"><span class="strong">  事实源优先级：</span></span></span><br><span class="line"><span class="emphasis"><span class="strong">  1. 当前代码实现</span></span></span><br><span class="line"><span class="emphasis"><span class="strong">  2. 测试用例</span></span></span><br><span class="line"><span class="emphasis"><span class="strong">  3. package.json / Makefile / CI 配置 / 脚本</span></span></span><br><span class="line"><span class="emphasis"><span class="strong">  4. 类型定义、API schema、路由定义、配置 schema</span></span></span><br><span class="line"><span class="emphasis"><span class="strong">  5. .env.example 或配置示例</span></span></span><br><span class="line"><span class="emphasis"><span class="strong">  6. 实际目录结构</span></span></span><br><span class="line"><span class="emphasis"><span class="strong"></span></span></span><br><span class="line"><span class="emphasis"><span class="strong">  硬性约束：</span></span></span><br><span class="line"><span class="emphasis"><span class="strong">  - 只允许修改 Markdown 文档文件，包括 CLAUDE.md、README.md、docs/*</span><span class="emphasis">*/*</span>.md、<span class="emphasis">*.md、*</span>.markdown。</span></span><br><span class="line"><span class="strong">  - 不允许修改业务代码、测试、配置文件、lock 文件、生成文件或二进制文件。</span></span><br><span class="line"><span class="strong">  - 不要为了匹配文档而修改代码；代码是事实源，文档是派生物。</span></span><br><span class="line"><span class="strong">  - 不要重写整篇文档，只做最小必要修改。</span></span><br><span class="line"><span class="strong">  - 不确定的内容不要编造，列入最终报告的“需要人工确认”。</span></span><br><span class="line"><span class="strong">  - 禁止修改 .claude 目录下的任何内容。</span></span><br><span class="line"><span class="strong"></span></span><br><span class="line"><span class="strong">  执行步骤：</span></span><br><span class="line"><span class="strong">  1. 建立项目事实清单：包管理器、启动命令、构建命令、测试命令、lint/typecheck 命令、目录结构、主要模块、API/路由/CLI、环境变量。</span></span><br><span class="line"><span class="strong">  2. 扫描所有 Markdown 文档，排除 .claude 目录，识别与代码事实不一致的地方。</span></span><br><span class="line"><span class="strong">  3. 对有明确事实依据的不一致，只更新 Markdown 文档。</span></span><br><span class="line"><span class="strong">  4. 修改后运行：</span></span><br><span class="line"><span class="strong">     - git diff --name-only</span></span><br><span class="line"><span class="strong">     - 如存在子模块，也运行 git -C <span class="language-xml"><span class="tag">&lt;<span class="name">submodule</span>&gt;</span></span> diff --name-only</span></span><br><span class="line"><span class="strong">  5. 如发现非 Markdown 文件被修改，立即回滚这些非文档变更。</span></span><br><span class="line"><span class="strong">  6. 输出最终报告。</span></span><br><span class="line"><span class="strong"></span></span><br><span class="line"><span class="strong">  完成条件：</span></span><br><span class="line"><span class="strong">  - 所有有明确代码事实依据的文档不一致都已修复。</span></span><br><span class="line"><span class="strong">  - 父仓库和所有子模块的实际文件级 diff 只包含 Markdown 文件。</span></span><br><span class="line"><span class="strong">  - 最终报告必须列出：</span></span><br><span class="line"><span class="strong">    1. 修改了哪些文档</span></span><br><span class="line"><span class="strong">    2. 每个文档修正了什么</span></span><br><span class="line"><span class="strong">    3. 每项修正依据的代码/配置/脚本事实</span></span><br><span class="line"><span class="strong">    4. 哪些内容仍需要人工确认</span></span><br><span class="line"><span class="strong">    5. 确认没有修改非文档文件</span></span><br></pre></td></tr></tbody></table></figure><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-20.png" alt=""></p><p>最终，经过了 8 分钟多的检查，消耗了大约 435W 的 <code>token</code>，<code>Codex</code> 完成了文档与代码的一致性升级。</p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-19.png" alt=""></p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-04.png" alt=""></p><p>文档与代码的一致性校验目前尚未整合到当前的 <code>Harness</code> 自闭环流程中，后续我们也会像 <code>OpenAI</code> 的操作一样：起一个后台服务来周期性地进行清理，并自动合并到代码仓库中。</p><h2 id="6-真的是完美的吗？">6. 真的是完美的吗？</h2><p>如上的 <code>Harness</code> 过程确实帮我们实现了 “一句话实现产品功能” 的目标，也确实避免了不同角色之间的沟通，提升了开发的效率，但是我们也在实践中发现我们的 <code>Harness</code> 模式并不是 100% 完美的，它也有它的问题存在。</p><h3 id="6-1-e2e-测试的检测能力相对较弱">6.1 E2E 测试的检测能力相对较弱</h3><p>通过如下的一个真实功能在不同阶段的耗时分布，我们发现：用例执行阶段的耗时是最长的，并且 <code>E2E</code> 测试决定着用例执行时间。但是在我们的实践中：<code>E2E</code> 测试很少报告缺陷，大部分 90%+ 的缺陷均是由 <code>API</code> 接口测试发现。</p><table><thead><tr><th>阶段</th><th>分析历史功能</th><th>生成 <code>PRD</code></th><th><code>PRD</code> 人工审核</th><th>代码编写</th><th>测试用例设计</th><th>用例执行</th><th>修复闭环</th></tr></thead><tbody><tr><td>耗时</td><td>4 mins</td><td>5 mins</td><td>5 mins</td><td>20 mins</td><td>12 mins</td><td>30 mins</td><td>5 mins</td></tr></tbody></table><p>是真的没有样式 <code>BUG</code> 吗？修改视频任务分类之后，我们确实也发现了前端样式的 <code>BUG</code>：在任务名比较长的时候，任务类型出现了元素遮挡的问题。</p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-12.png" alt=""></p><p>目前，我们的 <code>E2E Testing Agent</code> 对于质量的把控能力相较于人工测试而言还存在差距，一方面测试 <code>Agent</code> 对 <code>Bug</code> 的认定比人类宽松，一方面测试 <code>Agent</code> 缺少对布局、美感等的评判（当然，我们也没有对这方面进行限定）。因此，当前的 <code>Harness</code> 能力只保证功能的可用性，并不保证页面符合人类的审美。不过，对于我们这样的非复杂交互的系统而言，这样已经足以满足要求了。</p><h3 id="6-2-需求改动存在遗漏">6.2 需求改动存在遗漏</h3><p>在视频任务分类拆分的升级中，我们还发现 <code>Agent</code> 在实现任务的过程中遗漏了“模型管理”这个页面的改动。我们重新 <code>Review</code> 了对应的 <code>PRD</code>，发现在 <code>PRD</code> 中就已经存在遗漏了。虽然我们会对 <code>Agent</code> 生成的 <code>PRD</code> 内容进行审核，但是确实在审核的时候也发生了遗漏，毕竟一次审核 600+ 行的文本内容成本确实有点大。</p><p><img src="/2026/05/28/How-to-Achieve-One-Command-Feature-Delivery-with-Harness/harness-18.png" alt=""></p><h2 id="7-总结">7. 总结</h2><p>尽管当前的 <code>Harness</code> 实践在 <code>E2E</code> 测试环节仍有局限，但其带来的 <code>ROI</code> 已极其可观。根据我们在 4 个功能升级上的数据统计，当前的 <code>Harness</code> 已经让我们的功能发布效率提升了 4 倍，虽然与 <code>OpenAI</code> 博客中的 10 倍相比还有不少差距，但是已经让我们成功从“人工调优代码”跨越到“治理 <code>Agent</code> 流水线”，彻底打破了前后端联调的沟通壁垒。</p></body></html>]]>
      </content:encoded>
    </item>
    <item>
      <title>用 Harness Engineering 来进行书籍翻译工作</title>
      <link>https://wangwei1237.github.io/2026/04/19/I-use-harness-engineering-to-help-me-translate-books/</link>
      <description>
        <![CDATA[<html><head></head><body><p>为了强化对某项特定技术的理解与实践，我经常会利用空余时间阅读并翻译该技术领域的相对较前沿的英文书籍，自从 ChatGPT 发布以来，翻译外文书籍越来越方便。即便如此，翻译一本书还是需要非常多的繁杂的规则，例如：交叉引用、公式编号、字体使用规范、图片引用规范、排版规范……这些规范在我翻译书籍的过程中往往占据了大量的时间。</p>
<p>恰好最近在研究 Harness Engineering，突然我想：写代码与翻译在本质上不都是生成吗？Transformer 架构最初不也是为机器翻译而提出的吗？于是我就想把 Harness Engineering 的思想应用到书籍翻译上来，并依此来分析一下 Harness Engineering 的效果。</p>
<p><img src="/2026/04/19/I-use-harness-engineering-to-help-me-translate-books/harness_1.png" alt=""></p></body></html>]]>
      </description>
      <author>Wang Wei</author>
      <category domain="https://wangwei1237.github.io/categories/LLM/">LLM</category>
      <category domain="https://wangwei1237.github.io/tags/Harness-Engineering/">Harness Engineering</category>
      <category domain="https://wangwei1237.github.io/tags/Gemini-CLI/">Gemini CLI</category>
      <category domain="https://wangwei1237.github.io/tags/Translation/">Translation</category>
      <pubDate>Sun, 19 Apr 2026 16:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<html><head></head><body><p>为了强化对某项特定技术的理解与实践，我经常会利用空余时间阅读并翻译该技术领域的相对较前沿的英文书籍，自从 ChatGPT 发布以来，翻译外文书籍越来越方便。即便如此，翻译一本书还是需要非常多的繁杂的规则，例如：交叉引用、公式编号、字体使用规范、图片引用规范、排版规范……这些规范在我翻译书籍的过程中往往占据了大量的时间。</p><p>恰好最近在研究 Harness Engineering，突然我想：写代码与翻译在本质上不都是生成吗？Transformer 架构最初不也是为机器翻译而提出的吗？于是我就想把 Harness Engineering 的思想应用到书籍翻译上来，并依此来分析一下 Harness Engineering 的效果。</p><p><img src="/2026/04/19/I-use-harness-engineering-to-help-me-translate-books/harness_1.png" alt=""></p><span id="more"></span><h2 id="harness-engineering">Harness Engineering</h2><p>2026 年 2 月 5 日，Mitchell Hashimoto 在他的博客 <em>My AI Adoption Journey</em><sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> 中提出了 Harness Engineering 这个概念。</p><blockquote><p>I don’t know if there is a broad industry-accepted term for this yet, but I’ve grown to calling this <strong>“harness engineering”</strong>.</p><p>It is the idea that <strong>anytime you find an agent makes a mistake, you take the time to engineer a solution such that the agent never makes that mistake again</strong>. I don’t need to invent any new terms here; if another one exists, I’ll jump on the bandwagon.</p></blockquote><p>这就是 Harness Engineering 最初的含义：当你发现 Agent 犯错的时候，需要采用系统化的方案来确保 Agent 不再犯类似的错误。</p><p>然后，Cursor、OpenAI、Anthropic 先后发布了各自在 Agent-First 软件开发上的实践报告<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup><sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup><sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup>。这三篇实践报告均指向了 Mitchell Hashimoto 文章中的一个术语：Harness Engineering，从而引爆了 Harness Engineering 的浪潮。</p><p>于是一个混乱的时代到来了，就像我在 <a href="/monolith-to-microservices/docs/What_Are_Microservices.html">什么是微服务</a> 里提到的那样：虽然我们是在讨论同一个术语，但是实际上我们却是在讨论不同的东西。</p><p>仔细阅读 Cursor、OpenAI、Anthropic 的实践报告之后，我发现，虽然他们都提到了 Harness Engineering，虽然这三篇文章的读者群高度重叠，虽然用的术语高度一致，但各自回答的工程问题截然不同<sup class="footnote-ref"><a href="#fn5" id="fnref5">[5]</a></sup>:</p><ul><li>时间维度：Anthropic 研究的是如何让 Agent 更长时间的自主运行。</li><li>空间维度：Cursor 研究的是如何让几百个 Agent 能并行自主工作。</li><li>协作维度：OpenAI 研究的则是当 Agent 的产出速度远超人类的注意力时，人应该通过什么方式来控制整个系统。</li></ul><p>后来，我想了好久，终于想通了——其实理解 Harness Engineering 可以从多个维度切入：</p><ul><li>从问题空间来看：它主要解决智能体重复犯相同错误、自主运行时长有限、输出结果不符合预期等问题。</li><li>从解决空间来看：它为大模型系统性地构建约束空间、引导模型沿着有限路径、高效朝着预设目标推进。</li><li>从最终效果来看：它实现了智能体自主化、长时程、可管控的稳定运行。</li></ul><p>所以，Harness Engineering 也可以看作是一种面向失败、面向不稳定性开发的有效实践范式。</p><p><img src="/2026/04/19/I-use-harness-engineering-to-help-me-translate-books/harness_arch.png" alt=""></p><h2 id="翻译书籍的复杂性">翻译书籍的复杂性</h2><p>在 Chat 模式时，大模型可以帮助我完成从英文到中文的初步翻译过程，这个过程也就仅仅是把内容从英文变成中文，除此之外剩下的大部分的工作还需要我来处理：</p><ul><li>字体样式的统一</li><li>交叉引用的统一</li><li>翻译术语的前后统一</li><li>……</li></ul><p>如果涉及到数学公式，还需要做更多的工作……</p><p><strong>仔细分析，就会发现，这些复杂性恰好是 Harness Engineering 最开始要解决的问题</strong>：</p><blockquote><p>当你发现 Agent 犯错的时候，需要采用系统化的方案来确保 Agent 不再犯类似的错误</p></blockquote><p>只要告诉 Agent，在翻译过程中需要遵循什么样的规范，遇到图要怎么处理、遇到公式要怎么处理、遇到交叉引用要怎么处理…… Agent 自己就可以按照这个规范生成正确的内容。</p><p>想想我自己的翻译过程，也是如此：当我间隔很长时间没有翻译的时候，在继续翻译的时候我也会翻一下之前的规则，来保障整本书的统一。Harness Engineering 只是把我脑子中的那些规则固化下来而已，从而解决了 Agent 重复犯错的问题。</p><h2 id="我的实践">我的实践</h2><p>为了验证我的想法，我找到了 2 年前的一个翻译项目：<a href="https://github.com/wangwei1237/introduction_to_probability_and_statistics">Introduction to Probability and Statistics for Engineers and Scientists(Sixth Edition)</a>。这本书从 2025 年 2 月开始就没有持续翻译了，也算是一个有着历史包袱的遗留项目，要在这种项目上引入 Harness Engineering 并让 Agent 继续工作下去，说实话，心里确实没底。</p><p>说干就干，开搞。我用到的所有的内容如下：</p><ul><li><a href="https://github.com/wangwei1237/introduction_to_probability_and_statistics">GitHub 仓库</a></li><li>Gemini CLI（模型选择gemini-3.1-pro-preview）</li><li>chrome-devtools-mcp 工具</li><li>英文原版 PDF 书籍（book_en.pdf）</li></ul><h3 id="花若盛开-蝴蝶自来">花若盛开，蝴蝶自来</h3><p>在与 AI 协作的时候，我们有时候表现得就像我们讨厌的 “老登” 一样，我们会没有任何依据的先给 AI 灌输一堆的知识、信息，并号称 AI 需要这些知识，否则他就没法生成符合预期的结果。这听起来非常有道理的样子……</p><p>但是，我比较懒，在重新翻译的时候，因为中间隔了 1 年之久了，我也不想再去翻看之前的翻译、去回忆总结那些规则作为知识并灌输给 AI……</p><p>我甚至都不想告诉 Gemini CLI 需要怎么分页，用什么工具可以分页……</p><p>我坚信，不试试怎么知道 Gemini CLI 会不会呢？在不探测别人的能力时强行输入一堆知识确实像一个 “老登”，还好，这次我忍住了想当一个 “老登” 的欲望。</p><p>我在 Gemini CLI 中输入的如下的内容，我仅仅给出了任务描述，没有提前输入任何的额外的知识：</p><blockquote><p>按照要求，翻译 @book_en.pdf 的 14.2 的内容。</p></blockquote><p>我观察到，在没有输入任何知识的时候，Gemini CLI 自己会去搜索我翻译的其他章节的内容，并从中自己提炼相关的规则。</p><p><img src="/2026/04/19/I-use-harness-engineering-to-help-me-translate-books/t1_1.png" alt="I will read chapter_14/14_1.qmd to examine how equations and examples are formatted, ensuring that my translation of section 14.2 maintains consistency with the established style."></p><p><img src="/2026/04/19/I-use-harness-engineering-to-help-me-translate-books/t1_2.png" alt="I will read chapter_13/13.qmd to see if the chapter title is translated, which will help me maintain consistency when updating chapter 14/14.qmd."></p><p><img src="/2026/04/19/I-use-harness-engineering-to-help-me-translate-books/t1_3.png" alt="I will write the translated content of section 14.2 to chapter_14/14_2.qmd, using Quarto Markdown and LaTeX for equations, and ensuring proper formatting conventions."></p><p><img src="/2026/04/19/I-use-harness-engineering-to-help-me-translate-books/t1_4.png" alt="最终的翻译结果"></p><p>效果非常惊艳，我只是给了明确的任务，Gemini CLI 大概花了 40 分钟的样子自己完成了所有的工作，中间我没有做任何的修改和提示。</p><ul><li>自己读取 PDF 的内容，并非常精准地提取了 14.2 节的原内容</li><li>自己去回顾我翻译过的历史章节内容，提取对应的规则</li></ul><p>最终的效果参见 <a href="/introduction_to_probability_and_statistics/chapter_14/14.html#sec-14_2">14.2 失效率函数</a>，完全符合预期。</p><h3 id="保存-ai-总结的知识">保存 AI 总结的知识</h3><p>翻译整本书不是一个 session 就能解决的，如果明天我重新打开 Gemini CLI 开始新的任务时，我可不想让 Gemini 再浪费时间去学习那些规则，是时候记住一些东西了。于是，我在 Gemini CLI 中先让 Gemini 自己反思一下：</p><blockquote><p>从这次翻译过程中，你学习到了哪些项目的规范，请逐一列举，不要遗漏。</p></blockquote><p><img src="/2026/04/19/I-use-harness-engineering-to-help-me-translate-books/t2_1.png" alt="从这次翻译过程中，你学习到了哪些项目的规范，请逐一列举，不要遗漏。"></p><p><img src="/2026/04/19/I-use-harness-engineering-to-help-me-translate-books/t2_2.png" alt="把这些规则补充到 @GEMINI.md 中。"></p><h3 id="除非必须-绝不多嘴">除非必须，绝不多嘴</h3><p>然后，我继续让 Gemini CLI 来翻译 14.3 节，但是这一节中增加了很多上一节中不存在的规则，例如：</p><ul><li>图的处理</li><li>有的习题题目和答案是放在一起的，没有区分 <code>练习</code> 部分和 <code>答案</code> 部分</li><li>复杂公式</li></ul><p>我不想做 “老登”，我只在观察到 AI 明显犯错的时候才系统化地给出知识和规则，于是在和 Gemini 交互了多次后，<code>GEMINI.md</code> 中的知识也不断地丰富了起来。</p><p><img src="/2026/04/19/I-use-harness-engineering-to-help-me-translate-books/t3_1.png" alt=""></p><p><img src="/2026/04/19/I-use-harness-engineering-to-help-me-translate-books/t3_2.png" alt=""></p><p>随着规则越来越多，我与 Gemini CLI 的交互也越来越少，Gemini CLI 可以更长时间的自主翻译。对于 14.4 和 14.5 章节的翻译，我只是给出了任务，中间没有做过任何的干预，Gemini CLI 的翻译产出已经能够做到 PASS@1。 现在 <code>GEMINI.md</code> 的内容如下所示：</p><pre><code>## 开发规范（翻译与排版约定）### 1. 文件组织- **章节主文件**：每个章节的主文件（如 `14.qmd`）的一级标题应翻译为中文，并保留锚点（如 `# 寿命测试 ｛#sec-14}`）。- **小节标题层级**：在子章节文件（如 `14_2.qmd`）中，小节标题使用 `##`，更深层级依次递增。### 2. 术语翻译- **中英对照**：专业术语首次出现或定义时，使用 **中文加粗** 后接 (*英文斜体*) 括号标注。示例：**失效率函数**（*hazard rate function*）。- **关键术语**：正文中提到的核心概念应使用加粗。保持全书一致性，如 **最大似然估计量** (*maximum likelihood estimator*)。### 3. 交叉引用与锚点- **标题锚点**：每个标题后需紧跟 ID 锚点，格式为 `｛#sec-章节号_小节号}`。- **公式锚点**：公式需编号，格式为 `｛#eq-章节号_小节号_序号}`。- **定理锚点**：定理需编号，格式为 `｛#thm-章节号_小节号_序号}`。- **定义锚点**：定义需编号，格式为 `｛#def-章节号_小节号_序号}`。- **推论锚点**：推论需编号，格式为 `｛#cor-章节号_小节号_序号}`。- **表格锚点**：表格需编号，格式为 `｛#tbl-章节号_小节号_序号}`。- **图形锚点**：图形需编号，格式为 `｛#fig-章节号_小节号_序号}`。- 示例锚点：习题与解答成对出现时，习题编号为 `｛#exr-章节号_小节号_序号}`。- 示例答案锚点：习题与解答成对出现时，解答编号为 `｛#sol-章节号_小节号_序号}`。- 独立示例锚点：只有习题或例题（无独立解答块）时，使用 `｛#exm-章节号_小节号_序号}`。- 引用语法：使用 `@sec-ID`、`@eq-ID`、`@thm-ID`、`@def-ID`、`@cor-ID`、`@exr-ID`（习题）、`@sol-ID`（解答）、`@exm-ID`（独立示例）、`@tbl-ID`（表格）、`@fig-ID`（图形）等进行自动交叉引用。**禁止使用纯文本“示例 X.X”进行引用**。### 4. 示例与解答格式- **专用块 ID**：示例（Example）和解答（Solution）必须包裹在 Quarto 自定义块中：    - 习题与解答配对：        - 习题：`::: ｛#exr-章节号_小节号_序号}`        - 解答：`::: ｛#sol-章节号_小节号_序号}`    - 独立示例（无独立解答块）：        - 示例：`::: ｛#exm-章节号_小节号_序号}`### 5. 数学公式与排版- **独立定界符**：行外公式的 `$$` 定界符必须**各自独占一行**。- **对齐环境**：复杂推导优先使用 `\begin{align} ... \end{align}`，且必须包裹在 `$$ ... $$` 中以确保公式编号正常显示，示例：  <figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$$</span><br><span class="line">\begin{align}</span><br><span class="line">公式内容...</span><br><span class="line">\end{align}</span><br><span class="line">$$ ｛#eq-ID}</span><br></pre></td></tr></tbody></table></figure>- **符号约定**：    - 均值：`\overline{X}` 或 `\bar{x}`    - 分布：`\sim`    - 正态分布：`\mathcal{N}`- **中西文间距**：中文与行内公式 `$ ... $` 之间应保持一个半角空格。### 6. 绘图规范 (R Graphics)- **代码绘图**：原书中的示意图（如时间轴、分布图等）应优先使用 R 代码块（`ggplot2`）生成，而非使用静态图片。- **标签语言**：R 代码块生成的图形中，所有文本标签（label）、轴标题（axis title）和数学符号应**保持英文**。- **图形引用**：需配置 `#| label: fig-ID` 并使用 `@fig-ID` 引用。### 7. 语言风格与校对- **语气**：保持严谨、客观的学术叙述风格。- **标点**：正文使用全角中文标点；数学环境内使用西文标点。- **OCR 校验**：严格审查 OCR 识别导致的同音错别字（如将“贝叶斯”识别为“贝育”）。</code></pre><h3 id="自动校验与评估">自动校验与评估</h3><p>Anthropic 的博客中提到了模型 <em>自我评价失灵</em> 的问题，也就是说：当我们要求模型评价自己的作品时，即便这些作品非常一般，Agent 也倾向于自信地赞美。</p><blockquote><p>A second issue, which we haven’t previously addressed, is <strong>self-evaluation</strong>. When asked to evaluate work they’ve produced, agents tend to respond by confidently praising the work—even when, to a human observer, the quality is obviously mediocre. This problem is particularly pronounced for subjective tasks like design, where there is no binary check equivalent to a verifiable software test. Whether a layout feels polished or generic is a judgment call, and agents reliably skew positive when grading their own work.</p></blockquote><p>但是，当 Gemini CLI 翻译完之后，我也不想去 Review 翻译的内容对不对、格式对不对、图对不对……</p><p>于是，我想让 Gemini CLI 在完成编译之后自动触发 <code>gemini-3-flash-preview</code> 模型来进行自动校验，如果存在不符合规范的地方，Gemini CLI 再自动修正……</p><pre class="mermaid">flowchart LR  A[启动 Gemini CLI 翻译] --&gt; B1[加载 GEMINI.md]  B1 --&gt; B[gemini-3.1-pro-preview 完成翻译]  B --&gt; C[Hooks 自动触发 gemini-3-flash-preview 校验（skills）]  C --&gt; D{是否符合规范?}  D --&gt;|不符合| E[自动修正内容]  E --&gt; C    D --&gt;|符合| F[继续翻译]</pre><p><img src="/2026/04/19/I-use-harness-engineering-to-help-me-translate-books/t4_1.png" alt="创建一个 Hooks"></p><p><img src="/2026/04/19/I-use-harness-engineering-to-help-me-translate-books/t4_2.png" alt="测试 Hooks 是否生效"></p><p><img src="/2026/04/19/I-use-harness-engineering-to-help-me-translate-books/t4_3.png" alt="根据校验结果修正"></p><p>关于 Hooks 和 Skills 的详细内容，可以参见 GitHub 代码仓库：</p><ul><li>Hooks：<a href="https://github.com/wangwei1237/introduction_to_probability_and_statistics/blob/main/.gemini/settings.json">settings.json</a></li><li>Skills：<a href="https://github.com/wangwei1237/introduction_to_probability_and_statistics/blob/main/.gemini/skills/translation-reviewer/SKILL.md">translation-reviewer</a></li><li>Script：<a href="https://github.com/wangwei1237/introduction_to_probability_and_statistics/blob/main/scripts/validate_edit.sh">validate_edit.sh</a></li></ul><h2 id="模式的转变">模式的转变</h2><p>在 Harness Engineering 的模式下，确实如 OpenAI 的博客中所述的 <em>Humans steer. Agents execute</em> 那样，我真的：</p><ul><li>再也没有手动翻译过任何的一个单词</li><li>再也没有手动把原书中的图转换成 <code>R</code> 代码</li><li>再也没有手动处理过任何一个 LaTeX 公式</li><li>……</li></ul><p>在翻译过程中，我所做的工作变成了：</p><ul><li>在必要的时候给 Gemini CLI 提供知识与规则</li><li>构建自动化评估的机制</li><li>编写评估所用的 Skill（其实 Skill 本身也是 Gemini 生成的）</li><li>给 Gemini CLI 明确的任务</li><li>……</li></ul><p>当然，与 AICoding 相比，AITranslating 的复杂度确实低很多，但是模式是那么的相似、目标也是那么的一致：<em>Humans steer. Agents execute</em>。</p><p>更理想的状态应该是构造完知识、规则、环境与工具后，把一本英文的 PDF 书籍丢给 Gemini CLI，Agent 自己就会拆解为多个子任务（按照章节粒度翻译），然后按照如上的流程逐章节进行翻译并自动校验。于是，一个更长程的翻译任务也就跑起来了。</p><h2 id="参考文献">参考文献</h2><hr class="footnotes-sep"><section class="footnotes"><ol class="footnotes-list"><li id="fn1" class="footnote-item"><p><a href="https://mitchellh.com/writing/my-ai-adoption-journey#step-5-engineer-the-harness">My AI Adoption Journey</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p></li><li id="fn2" class="footnote-item"><p><a href="https://cursor.com/blog/self-driving-codebases">Towards self-driving codebases</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p></li><li id="fn3" class="footnote-item"><p><a href="https://openai.com/index/harness-engineering/">Harness engineering: leveraging Codex in an agent-first world</a> <a href="#fnref3" class="footnote-backref">↩︎</a></p></li><li id="fn4" class="footnote-item"><p><a href="https://www.anthropic.com/engineering/harness-design-long-running-apps">Harness design for long-running application development</a> <a href="#fnref4" class="footnote-backref">↩︎</a></p></li><li id="fn5" class="footnote-item"><p><a href="https://yage.ai/share/harness-engineering-scalability-20260330.html">Harness Engineering 在讨论什么：三个 Scaling 维度的统一框架</a> <a href="#fnref5" class="footnote-backref">↩︎</a></p></li></ol></section></body></html>]]>
      </content:encoded>
    </item>
    <item>
      <title>Agent Client Protocol（ACP）: AI Coding Agents 的新时代</title>
      <link>https://wangwei1237.github.io/2026/04/13/Agent-Client-Protocol-The-New-Era-for-AI-Coding-Agents/</link>
      <description>
        <![CDATA[<html><head></head><body><p><img src="/2026/04/13/Agent-Client-Protocol-The-New-Era-for-AI-Coding-Agents/1.png" alt=""></p>
<p>10 年前，也就是 2016 年，为了解决 VS Code 多语言支持重复开发的问题，微软团队基于 <a href="https://github.com/Microsoft/TypeScript/tree/main/src/server">TypeScript Server</a> 的通信机制，开始设计通用协议，以解决编辑器与语言服务解耦。<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> 同年的 6 月 27 日，微软在 DevNation 大会上正式宣布开源 LSP，从而解决了“针对同一语言，每个编辑器都要重写一遍智能提示”的噩梦，也标志着现代 IDE 进入了新的时代。<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup></p>
<p>多年以后，同样的情况出现在了 AI Coding Agent 的领域——2025 年 8 月 27 日，Zed Industries 在博客 <a href="https://zed.dev/blog/bring-your-own-agent-to-zed">Bring Your Own Agent to Zed — Featuring Gemini CLI</a> 中正式公开 ACP 协议，并推出 Zed 中整合的 Gemini CLI 作为首个 ACP 的参考实现。<sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup></p></body></html>]]>
      </description>
      <author>Wang Wei</author>
      <category domain="https://wangwei1237.github.io/categories/LLM/">LLM</category>
      <category domain="https://wangwei1237.github.io/tags/ACP/">ACP</category>
      <category domain="https://wangwei1237.github.io/tags/AI-Coding-Agent/">AI Coding Agent</category>
      <category domain="https://wangwei1237.github.io/tags/Agent-Client-Protocol/">Agent Client Protocol</category>
      <pubDate>Mon, 13 Apr 2026 14:58:15 GMT</pubDate>
      <content:encoded>
        <![CDATA[<html><head></head><body><p><img src="/2026/04/13/Agent-Client-Protocol-The-New-Era-for-AI-Coding-Agents/1.png" alt=""></p><p>10 年前，也就是 2016 年，为了解决 VS Code 多语言支持重复开发的问题，微软团队基于 <a href="https://github.com/Microsoft/TypeScript/tree/main/src/server">TypeScript Server</a> 的通信机制，开始设计通用协议，以解决编辑器与语言服务解耦。<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> 同年的 6 月 27 日，微软在 DevNation 大会上正式宣布开源 LSP，从而解决了“针对同一语言，每个编辑器都要重写一遍智能提示”的噩梦，也标志着现代 IDE 进入了新的时代。<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup></p><p>多年以后，同样的情况出现在了 AI Coding Agent 的领域——2025 年 8 月 27 日，Zed Industries 在博客 <a href="https://zed.dev/blog/bring-your-own-agent-to-zed">Bring Your Own Agent to Zed — Featuring Gemini CLI</a> 中正式公开 ACP 协议，并推出 Zed 中整合的 Gemini CLI 作为首个 ACP 的参考实现。<sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup></p><span id="more"></span><h2 id="为什么需要-acp？">为什么需要 ACP？</h2><p>截至目前，市面上已有至少 17 款成熟的 AI Coding Agent（Claude Code、Cursor、Codex CLI、Gemini CLI、Windsurf、Cline、goose、Kimi CLI、OpenCode……）。同时，至少有 6~7 款主流的编码 IDE（VS Code、IntelliJ/JetBrains、Zed、Neovim、Cursor……）。</p><p>每一个 AI Coding Agent 都需要与不同的 IDE 进行集成。如果想在 JetBrains 中使用 Claude Code？JetBrains 团队就得在 JetBrains IDE 中开发适配 Claude Code。想在 Neovim 中使用 Claude Code？不好意思，也得重新适配。</p><p>这种重复的集成工作，反反复复地出现在整个工具链生态，不仅造成了厂商锁定效应，还拖慢了创新步伐。</p><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">厂商锁定效应</p><p>厂商锁定效应是指用户在选择了某款 AI Coding Agent 后，如果只有特定的 IDE 支持该 Coding Agent，则用户后续更换 Coding Agent 或者 IDE 的 <strong>转换成本</strong> 就会非常昂贵，导致其在未来一段时期内不得不依赖特定的 Coding Agent 或者 IDE。</p><p>通俗地说，就是“一旦上了贼船，就很难下来”。</p></div><p>而 ACP 协议就是为了解决这个问题而提出，它的作用和 10 年前的 LSP 协议一样，都是为了建立一套通用标准，从而实现 AI Coding Agent 与 IDE 之间的解耦。</p><h2 id="acp-的时间节点">ACP 的时间节点</h2><p>2025 年初，Zed Industries 团队正在优化 “agentic editing” 功能，恰在此时，谷歌方面联系到他们，希望与 Zed 探讨如何在 Zed 中更深度地集成 Gemini CLI。尽管当时 Zed 已经可以在其嵌入式终端中运行 Gemini CLI 了，但通过 ANSI 转义码进行通信的方式，无法满足现代 IDE 丰富交互的需求。</p><p>正如 Zed 在博客中说的那样<sup class="footnote-ref"><a href="#fn3" id="fnref3:1">[3:1]</a></sup>：“我们需要一种比 ANSI 转义码更结构化的通信方式。” 这促成了 ACP 的诞生——一个极简的 JSON-RPC 框架，它允许任意 Client 通过定义完善的 schema 与任意 Agent 进行通信。</p><blockquote><p>After shipping agentic editing earlier this year, the next big task on our roadmap was making that experience extensible.</p><p>As luck would have it, that’s around the time when Google decided to reach out. The Gemini CLI team was having a great experience using their agent in Zed’s integrated terminal, and they wanted to explore what deeper integration could look like.</p><p>Command-line agents are cool because their simplicity makes them easy to run anywhere—including as a subprocess of another application. Zed was already running Gemini CLI inside our embedded terminal emulator, but we needed a more structured way of communicating than ANSI escape codes.</p><p>So we defined a minimal set of JSON-RPC endpoints to relay user requests to the agent and render its responses. The result is the <strong>Agent Client Protocol</strong>, a lean framework that lets any client talk to any agent, as long as they follow the schema.</p></blockquote><p>ACP 协议于 2025 年 8 月推出，以 Gemini CLI 作为参考实现，同年 9 月便迅速集成了 Claude Code，同年 10 月又迅速集成了 Codex……</p><p>如今，ACP 是一款采用 Apache 2.0 开源许可协议的项目，背后有 Zed、谷歌、JetBrains 等行业巨头以及日益壮大的 IDE 插件开发者社区提供支持。我们可以在 ACP 协议的官网查看目前所有的 <a href="https://agentclientprotocol.com/get-started/agents">Clients</a> 和 <a href="https://agentclientprotocol.com/get-started/clients">Agents</a>。</p><h2 id="acp-协议简介">ACP 协议简介</h2><p>ACP 是一种 JSON-RPC 2.0 协议，其核心原则简单明了：</p><ul><li>Client：各类编辑器，如 Zed、JetBrains、VS Code 等，主要负责管理用户的编程环境</li><li>Agent：各类 AI Coding Agent，如 Claude Code、Gemini CLI、OpenClaw 等，主要负责思考与工具执行</li><li>Client 与 Agent 之间通过标准输入输出流通信（智能体以子进程形式运行），或通过 HTTP 协议通信（远程智能体）</li></ul><p>关于 ACP 协议的详细内容，此处不再过多介绍了，具体可以参见：<a href="https://agentclientprotocol.com/get-started/introduction">ACP 协议的官网</a>。</p><h2 id="acp-中的核心概念与流程">ACP 中的核心概念与流程</h2><h3 id="ide-与-agent-的-acp-通信">IDE 与 Agent 的 ACP 通信</h3><pre class="mermaid">flowchart LR  subgraph ClientEditor ["Client (Editor)"]      Editor["Zed / JetBrains / Neovim"]  end  subgraph AgentAI ["Agent (AI)"]      Agent["Claude Code / Gemini CLI"]  end  Editor -- "session/prompt" --&gt; Agent  Agent -- "session/update" --&gt; Editor  Agent -- "fs/read_text_file" --&gt; Editor  Agent -- "terminal/create" --&gt; Editor  Agent -- "request_permission" --&gt; Editor</pre><h3 id="acp-的全生命周期图">ACP 的全生命周期图</h3><pre class="mermaid">sequenceDiagram  participant User  participant Client as Client (Zed)  participant Agent as Agent (Gemini CLI)  participant LLM  Note over Client, Agent: 1. Initialization  Client-&gt;&gt;Agent: initialize  Agent--&gt;&gt;Client: capabilities + version  Note over Client, Agent: 2. Session Setup  Client-&gt;&gt;Agent: session/new  Agent--&gt;&gt;Client: sessionId  Note over Client, Agent: 3. Prompt Turn  User-&gt;&gt;Client: "Run tests and fix failures"  Client-&gt;&gt;Agent: session/prompt  Agent-&gt;&gt;LLM: Process prompt  LLM--&gt;&gt;Agent: Response + tool calls  Agent-&gt;&gt;Client: session/update (plan)  Agent-&gt;&gt;Client: session/update (tool_call: pending)  Note over Client, Agent: 4. Tool Execution  Agent-&gt;&gt;Client: terminal/create  Client--&gt;&gt;Agent: terminalId  Agent-&gt;&gt;Client: session/update (tool_call: in_progress)  Note left of Client: Shows live terminal output  Agent-&gt;&gt;Client: terminal/output  Client--&gt;&gt;Agent: output + exitStatus  Agent-&gt;&gt;Client: session/update (tool_call: completed)  Note over Client, Agent: 5. File Operations  Agent-&gt;&gt;Client: fs/read_text_file  Client--&gt;&gt;Agent: file content  Agent-&gt;&gt;Client: session/request_permission  Client--&gt;&gt;Agent: allowed  Agent-&gt;&gt;Client: fs/write_text_file  Client--&gt;&gt;Agent: success  Agent-&gt;&gt;Client: session/update (diff)  Note over Client, Agent: 6. Completion  Agent--&gt;&gt;Client: session/prompt response (end_turn)</pre><h3 id="terminals-命令的时序图">Terminals 命令的时序图</h3><pre class="mermaid">sequenceDiagram  participant Agent  participant Client  Agent-&gt;&gt;Client: terminal/create  Note right of Client: command: "npm test"<br>args: ["--coverage"]<br>cwd: "/project"  Client--&gt;&gt;Agent: { terminalId: "term_001" }  Agent-&gt;&gt;Client: session/update  Note left of Agent: tool_call_update<br>status: in_progress<br>content: [terminal embed]  Note right of Client: Displays live output  Agent-&gt;&gt;Client: terminal/wait_for_exit  Note right of Client: Command runs...  Client--&gt;&gt;Agent: { }  Agent-&gt;&gt;Client: terminal/output  Client--&gt;&gt;Agent: { output: "...", exitStatus: { exitCode: 0 } }  Agent-&gt;&gt;Client: terminal/release  Client--&gt;&gt;Agent: { }</pre><h3 id="权限请求时序图">权限请求时序图</h3><pre class="mermaid">sequenceDiagram  participant Agent  participant Client  participant User  Agent-&gt;&gt;Client: session/request_permission  Note right of Client: toolCall: { title: "Delete old_file.txt" }<br>options: [allow_once, allow_always, reject]  Client-&gt;&gt;User: Show permission dialog  User--&gt;&gt;Client: Click "Allow"  Client--&gt;&gt;Agent: { outcome: "selected", optionId: "allow_once" }  Note left of Agent: Proceeds with tool execution</pre><h2 id="acp-协议的优点">ACP 协议的优点</h2><h3 id="1-摆脱厂商绑定">1. 摆脱厂商绑定</h3><p>还记得我们当初不得不在 Cursor、VS Code 或是特定终端代理之间艰难抉择的情况吗？有了 ACP 协议，我们完全可以根据自己的喜好选择最趁手的编辑器，再根据当前的任务需求选择最合适的代理：</p><ul><li><strong>开发复杂功能</strong>：选用擅长架构设计和逻辑推理的代理。</li><li><strong>调试疑难杂症</strong>：切换到更擅长解析堆栈追踪和日志信息的代理。</li><li><strong>编写测试用例</strong>：选择专攻测试驱动开发的专业代理。</li></ul><p>我们的编辑器对此毫无感知，所有的底层交互均由 ACP 协议统一处理，真正实现了工具链的自由组合。</p><h3 id="2-适配任务的最佳工具">2. 适配任务的最佳工具</h3><p>LLM 的迭代速度极快，往往快到令人应接不暇。昨天还表现最优的模型，可能短短一周后就被新的模型所超越。如果采用专属的集成方案，我们只能被动等待编辑器厂商的支持。</p><p>而借助 ACP，我们获得了前所未有的敏捷性：</p><ul><li><strong>即插即用</strong>：新智能体发布 → 接入 ACP → 即可在我们的编辑器中无缝使用。</li><li><strong>零等待成本</strong>：无需等待插件更新，无需更换编辑器，更无需重新适应操作习惯。</li></ul><h3 id="3-提升团队协作的灵活性">3. 提升团队协作的灵活性</h3><p>在实际的开发团队中，我们经常会看到这样的场景：前端开发者钟爱 Claude Code，后端工程师对一款针对技术栈微调的定制化 Agent 深信不疑，而运维人员则习惯于另一套完全不同的工具。</p><p>ACP 协议能够确保无论个人偏好如何，所有人都能顺畅协作。每个人都可以使用自己得心应手的编辑器和信赖的 Agent。这同时也让团队的入职流程变得更加简便——新开发者加入时，只需告诉他们：“用你最顺手的编辑器就行，这些是我们团队在用的智能代理，它们全都兼容可用。”</p><h3 id="4-面向未来的前瞻性设计">4. 面向未来的前瞻性设计</h3><p>我们正处于智能体时代的早期阶段，新功能和新范式每周都在不断涌现。未来的智能体不仅能写代码，还能够实现：</p><ul><li>复杂的浏览器自动化操作</li><li>深度的数据库查询与分析</li><li>执行跨系统的复杂工作流</li><li>与其他智能体进行多模态协同工作</li></ul><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p>没有什么事情是不能通过抽象一层来解决的，如果有，那就再抽象一层。</p></div><h2 id="参考文献">参考文献</h2><hr class="footnotes-sep"><section class="footnotes"><ol class="footnotes-list"><li id="fn1" class="footnote-item"><p><a href="https://github.com/microsoft/language-server-protocol/wiki/Protocol-History">LSP History</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p></li><li id="fn2" class="footnote-item"><p><a href="https://code.visualstudio.com/blogs/2016/06/27/common-language-protocol">A Common Protocol for Languages</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p></li><li id="fn3" class="footnote-item"><p><a href="https://zed.dev/blog/bring-your-own-agent-to-zed">Bring Your Own Agent to Zed — Featuring Gemini CLI</a> <a href="#fnref3" class="footnote-backref">↩︎</a> <a href="#fnref3:1" class="footnote-backref">↩︎</a></p></li></ol></section></body></html>]]>
      </content:encoded>
    </item>
    <item>
      <title>通过 Chrome DevTools MCP 增强 Agent 的浏览器操控能力</title>
      <link>https://wangwei1237.github.io/2026/03/28/Enhance-Agents-browser-control-via-Chrome-DevTools-MCP/</link>
      <description>
        <![CDATA[<html><head></head><body><p>当我们用类似 Claude Code、Codex、Gemini CLI 等 AICoding Agent 写完代码，然后让它自动生成测试计划、自动调起浏览器来测试刚刚生成的代码，并根据反馈结果重新优化代码。在这个循环中，不需要人手动干预，完全依赖 AICoding Agent 自主操控，这不是一件非常美妙的事情吗？</p>
<p>这不就是当下爆火的 Harness Engineering 吗？正如 <a href="https://x.com/heynavtoor/status/2037200578842157462">Harness Engineering: Why the Best AI Engineers in 2026 Stopped Writing Code</a> 中说的那样<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>：</p>
<blockquote>
<p>A researcher tested the same AI model on the same coding benchmark twice. The first time, it scored 42%. The second time, it scored 78%. Same model. Same test. Same everything.</p>
</blockquote>
<p>如上的框架的核心点在于：AICoding Agent 可以有效的操控浏览器。 本文将介绍三种浏览器控制方案：Playwright、Browser-use 以及伴随 Chrome M144 推出的最新利器 Chrome DevTools MCP，并通过多维度的对比，为大模型应用的架构选型提供参考。</p></body></html>]]>
      </description>
      <author>Wang Wei</author>
      <category domain="https://wangwei1237.github.io/categories/LLM/">LLM</category>
      <category domain="https://wangwei1237.github.io/tags/Chrome-DevTools-MCP/">Chrome DevTools MCP</category>
      <category domain="https://wangwei1237.github.io/tags/Browser-use/">Browser use</category>
      <category domain="https://wangwei1237.github.io/tags/playwright/">playwright</category>
      <pubDate>Sat, 28 Mar 2026 08:38:18 GMT</pubDate>
      <content:encoded>
        <![CDATA[<html><head></head><body><p>当我们用类似 Claude Code、Codex、Gemini CLI 等 AICoding Agent 写完代码，然后让它自动生成测试计划、自动调起浏览器来测试刚刚生成的代码，并根据反馈结果重新优化代码。在这个循环中，不需要人手动干预，完全依赖 AICoding Agent 自主操控，这不是一件非常美妙的事情吗？</p><p>这不就是当下爆火的 Harness Engineering 吗？正如 <a href="https://x.com/heynavtoor/status/2037200578842157462">Harness Engineering: Why the Best AI Engineers in 2026 Stopped Writing Code</a> 中说的那样<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>：</p><blockquote><p>A researcher tested the same AI model on the same coding benchmark twice. The first time, it scored 42%. The second time, it scored 78%. Same model. Same test. Same everything.</p></blockquote><p>如上的框架的核心点在于：AICoding Agent 可以有效的操控浏览器。 本文将介绍三种浏览器控制方案：Playwright、Browser-use 以及伴随 Chrome M144 推出的最新利器 Chrome DevTools MCP，并通过多维度的对比，为大模型应用的架构选型提供参考。</p><span id="more"></span><h2 id="一个简单的例子">一个简单的例子</h2><p>我们可以把如上提到的 Harness Engineering 的过程简化成如下的流程图：</p><pre class="mermaid">graph TD    Start([需求输入]) --&gt; A        subgraph 规划与编码        A[Codex 编写/修改目标代码] --&gt; B[解析代码 &amp; 生成测试计划]        B --&gt; C[构建可执行测试脚本]    end        subgraph 自动化执行与环境交互阶段        C --&gt; D[环境调度与协议打通]        D --&gt;|通过 CDP/MCP 控制本地 Mac 或远程集群浏览器| E[自动执行测试 / UI 交互]    end        subgraph 反馈与自我进化循环        E --&gt; F[捕获运行时反馈]        F --&gt;|提取控制台日志、DOM 状态、截图、网络抓包等| G{大模型结果评估}        G --&gt;|测试通过指标达标| H([任务完成 / 代码合入])        G --&gt;|测试失败 / 发现缺陷| I[AI 深度剖析错误根因]        I --&gt;|生成代码优化与修复策略| A    end        %% 定义节点样式    classDef aiNode fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px;    classDef envNode fill:#fff3e0,stroke:#ff9800,stroke-width:2px;    classDef evaluateNode fill:#fce4ec,stroke:#e91e63,stroke-width:2px;        class A,B,C,I aiNode;    class D,E,F envNode;    class G evaluateNode;</pre><p>当然，现实中，很多的应用并非全部都依赖浏览器来做校验。比如：iOS、Android 等的客户端的开发还需要驱动对应的模拟器或者真机来调试。但是，本文主要介绍的还是 AI Coding Agent 与浏览器的交互。</p><h2 id="playwright">Playwright</h2><p>Playwright 原本是微软开源的现代 Web 自动化测试框架。在 Agent 爆发的早期，它成为了几乎所有 LLM 控制浏览器的底层“发动机”。大模型输出 Python 或 Node.js 代码，由 Playwright 翻译为底层的 WebSocket 指令发送给浏览器。</p><p>Playwright 的设计哲学是隔离与确定性。默认情况下，它每次启动都会拉起一个全新、无头的沙盒浏览器实例。</p><p><strong>Playwright 的优势：</strong></p><p>极高的执行速度和并发能力。由于是纯代码驱动，它在执行预定义好的大批量测试流水线时，稳定性无可匹敌。它提供了完善的 Cookie 注入、网络拦截等底层 API。</p><p><strong>但是，在 AI 辅助场景下，Playwright 存在致命的问题：</strong></p><ul><li>“登录墙”噩梦： 当我们希望 Agent 帮我们排查内部系统或抓取受保护的数据时，Playwright 拉起的干净实例会被瞬间挡在登录页外。面对复杂的扫码或滑块验证码，AI 几乎束手无策。</li><li>UI 脆弱性： 一旦网页的 DOM 结构或 CSS 选择器发生微小变动，底层生成的 Playwright 代码就会直接报错崩溃，容错率极低。</li><li>高昂的通信延迟： 传统的 Python -&gt; Node.js Server -&gt; 浏览器 架构，在需要高频次拉取几万个 DOM 节点供 LLM 分析时，会产生严重的 IPC（进程间通信）延迟。</li></ul><p>辅助场景下，Playwright 是流水线上的“无情机器”，适合确定性的 E2E 测试和后台爬虫，但不适合作为需要与人类环境共享上下文的“AI 副驾驶”。</p><h2 id="browser-use">Browser-use</h2><p>Browser-use 是目前开源社区极具代表性的高层 Agent 框架。值得注意的是，从 <a href="https://github.com/browser-use/browser-use/blob/0.6.0rc1/pyproject.toml">0.6.0</a> 版本开始，Browser-use 作了一次重大底层重构：彻底抛弃了 Playwright，全面转向直接使用纯 CDP (Chrome DevTools Protocol) 与浏览器进行通信，以追求更极致的性能。</p><p>Browser-use 的核心哲学是视觉驱动。它不关心网页底层用了什么前端框架，而是直接对网页进行截图，提取出可交互元素的边界框（Bbox），并将这些带有坐标标签的图片喂给具备视觉能力的大模型（如 Claude 3.5 Sonnet）。大模型像人类一样“看”网页，然后输出诸如“点击带有购物车图标的按钮”的自然语言指令。</p><p><strong>Browser-use 的优势：</strong></p><p>极强的鲁棒性（抗干扰）： 网页改版、按钮换位置、甚至底层的 React 状态乱了都没关系，只要人眼能看懂，模型就能看懂并操作。</p><p>开发门槛极低： 开发者无需编写复杂的 DOM 选择器代码，完全通过自然语言 Prompt 驱动复杂的探索性任务。</p><p><strong>Browser-use 的劣势：</strong></p><p>Token 成本与延迟双高： 每执行一步操作，都需要向多模态大模型传输庞大的图像 Token，这在进行大规模自动化评测或高频系统调度时，成本是难以接受的。</p><p>浅层交互限制： 它只能看到网页的“表象”。如果页面由于底层跨域问题加载失败，或者 JavaScript 抛出了深层的堆栈错误，Browser-use 是无法察觉的。</p><p>Browser-use 是灵活的“AI 司机”，非常适合处理面向消费者的非结构化网页任务（如机票比价、数据探索），但在企业级深水区显得有些昂贵且不够深入。</p><h2 id="chrome-devtools-mcp">Chrome DevTools MCP</h2><p>根据谷歌的开发者文档<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>：</p><blockquote><p>We shipped an enhancement to the <a href="https://github.com/ChromeDevTools/chrome-devtools-mcp">Chrome DevTools MCP</a> server that many of our users have been asking for: the ability for coding agents to directly connect to active browser sessions.</p></blockquote><p>使用该特性，我们的 AI Coding Agent 可以实现如下的功能：</p><ul><li><strong>复用现有的 Chrome 浏览器会话</strong>：AI Coding Agent 可以直接访问当前的浏览器会话，直接使用当前会话中的登录信息，而不需要在自动化测试的过程中增加额外的登录操作。</li><li><strong>访问正在使用浏览器控制台会话</strong>：当在 Chrome DevTools 网络面板中发现一个失败的网络请求时，可以直接选择该请求并让 AI Coding Agent 进行分析，这种 AI Coding Agent 与 人之间无缝协同的新能力让我感到非常兴奋。</li></ul><p>如上的功能主要受益于谷歌 在 Chrome M144（beta）版本中新增的一项远程调试的功能：该功能允许 Chrome DevTools MCP 服务器接受远程调试请求。默认情况下，Chrome 会禁用远程调试连接，可以通过 <code>chrome://inspect#remote-debugging</code> 来启用该功能。</p><h2 id="gemini-cli-控制-chrome">Gemini CLI 控制 Chrome</h2><p>1.<strong>开启远程调试</strong></p><p>在 Chrome 中访问 <code>chrome://inspect#remote-debugging</code> 并启用远程调试功能。</p><p><img src="/2026/03/28/Enhance-Agents-browser-control-via-Chrome-DevTools-MCP/2.png" alt=""></p><p>2.<strong>安装 <a href="https://github.com/ChromeDevTools/chrome-devtools-mcp">Chrome DevTools MCP</a></strong></p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gemini mcp add -s user chrome-devtools npx chrome-devtools-mcp@latest</span><br></pre></td></tr></tbody></table></figure><p>3.<strong>修改配置文件</strong><br>打开 <code>~/.gemini/settings.json</code> 文件，修改 <code>chrome-devtools</code> 配置，在 <code>args</code> 中增加 <code>--autoConnect</code> 参数，这样 Gemini 就可以打开你当前正在使用的 Chrome 浏览器。如果不增加 <code>--autoConnect</code> 参数，Gemini 会打开一个新的 Chrome 浏览器实例。</p><figure class="highlight json"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">"mcpServers"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">  <span class="attr">"chrome-devtools"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">    <span class="attr">"command"</span><span class="punctuation">:</span> <span class="string">"npx"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"args"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">      <span class="string">"chrome-devtools-mcp@latest"</span><span class="punctuation">,</span></span><br><span class="line">      <span class="string">"--autoConnect"</span></span><br><span class="line">    <span class="punctuation">]</span></span><br><span class="line">  <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure><blockquote><p>如果指定 <code>--autoConnect</code> 参数，Chrome DevTools MCP 将自动连接到由 channel 参数标识的、本地运行的 Chrome 浏览器（channel 默认为 stable）。</p></blockquote><p>4.<strong>自动连接与授权</strong><br>当 Gemini 认为需要操作 Chrome 时，Chrome DevTools MCP 会自动检测并连接到正在运行的 Chrome 实例，并弹出一个请求远程调试权限的对话框。用户授权后，Agent 便可获得当前浏览器会话的控制权。此时，浏览器会显示一个“Chrome 正由自动化测试软件控制”的横幅，以提示用户。</p><blockquote><p>打开 github 网站，并进入到我的个人页面。</p></blockquote><p><img src="/2026/03/28/Enhance-Agents-browser-control-via-Chrome-DevTools-MCP/4.jpg" alt=""></p><p><img src="/2026/03/28/Enhance-Agents-browser-control-via-Chrome-DevTools-MCP/5.jpg" alt=""></p><p>Gemini Code Assist 利用 Chrome DevTools MCP 操控 Chrome 浏览器的整体的流程如下所示：</p><pre class="mermaid">sequenceDiagram    participant User as 用户    participant Agent as Gemini Code Assist    participant MCPServer as Chrome DevTools MCP Server    participant Chrome as Chrome 浏览器    User-&gt;&gt;Chrome: 启用远程调试 (chrome://inspect)    User-&gt;&gt;Agent: "打开 github 网站，并进入到我的个人页面，并分析加载性能"    Agent-&gt;&gt;MCPServer: 启动并传入 --autoConnect    MCPServer-&gt;&gt;Chrome: 请求连接到活跃会话    Chrome--&gt;&gt;User: 弹出授权对话框    User-&gt;&gt;Chrome: 同意授权    Chrome--&gt;&gt;MCPServer: 建立调试会话    MCPServer--&gt;&gt;Agent: "连接已建立，可以开始调试"    Agent-&gt;&gt;MCPServer: 调用工具 (例如：network.getRequests)    MCPServer-&gt;&gt;Chrome: 通过 CDP 执行指令    Chrome--&gt;&gt;MCPServer: 返回网络请求数据    MCPServer--&gt;&gt;Agent: 返回网络请求数据    Agent-&gt;&gt;Agent: 分析数据并定位问题</pre><div class="bili_video"><div style="position: relative; width: 100%; height: 0; padding-bottom: 75%;"><iframe src="https://player.bilibili.com/player.html?aid=116311841640072&amp;page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true" style="position: absolute; width: 100%; height: 100%; left: 0; top: 0;"> </iframe></div></div><p><strong>Chrome DevTools MCP 的优势：</strong></p><ul><li><p><strong>复用活跃会话</strong>: 这是真正的杀手锏。当你在 Mac 上已经登录了公司内部的复杂集成系统或评测看板时，Agent 可以直接通过 MCP 连入你当前正在使用、带有完整 Cookie 和身份态的浏览器标签页。无需让 AI 去学习如何登录，实现了人类与 Agent 之间调试工作的无缝切换。</p></li><li><p><strong>深达内核的数据获取</strong>: 你的 Agent 可以直接调用底层协议，抓取 <code>Network</code> 面板的失败请求、读取 <code>Console</code> 报错日志、查看 <code>Elements</code> 面板的 DOM 树、甚至执行性能审计。这使得 AI 具备了高级前端工程师的排错能力。</p></li><li><p><strong>极简且安全的架构</strong>: 不依赖庞大的中间层，且每次连接都会有系统级的安全授权弹窗，非常契合企业对权限隔离的严格要求。</p></li></ul><p><strong>Chrome DevTools MCP 的劣势：</strong></p><p>对大模型的逻辑推理和代码编写能力要求极高，因为它操作的不再是直观的 UI，而是底层的 JSON 数据和 JS 运行时。</p><p>整体看，Chrome DevTools MCP 配合 AI Coding Agent 是构建大模型自动化评测流水线、进行复杂集群环境调试的首选协议层方案。</p><h2 id="不同方式之间的对比">不同方式之间的对比</h2><table><thead><tr><th style="text-align:left">评估维度</th><th style="text-align:left">Playwright</th><th style="text-align:left">Browser-use (纯 CDP 版)</th><th style="text-align:left">Chrome DevTools MCP</th></tr></thead><tbody><tr><td style="text-align:left"><strong>抽象层级</strong></td><td style="text-align:left">底层通信</td><td style="text-align:left">高层业务 Agent</td><td style="text-align:left">标准协议中间件</td></tr><tr><td style="text-align:left"><strong>驱动方式</strong></td><td style="text-align:left">纯代码驱动</td><td style="text-align:left">视觉与自然语言</td><td style="text-align:left">Function Calling</td></tr><tr><td style="text-align:left"><strong>应对 UI 变化的稳定性</strong></td><td style="text-align:left">极低（高度依赖 DOM 结构）</td><td style="text-align:left"><strong>极高（像人类一样视觉识别）</strong></td><td style="text-align:left">中等（依赖模型动态生成查询脚本）</td></tr><tr><td style="text-align:left"><strong>状态共享与免登录能力</strong></td><td style="text-align:left">困难（默认沙盒环境）</td><td style="text-align:left">弱（需 AI 自主操作登录表单）</td><td style="text-align:left"><strong>极强（直接复用已登录会话）</strong></td></tr><tr><td style="text-align:left"><strong>Token 消耗</strong></td><td style="text-align:left">零消耗（无需 LLM 介入）</td><td style="text-align:left">极高（大量多模态图像计算）</td><td style="text-align:left">极低（仅传递结构化的短文本和 JSON）</td></tr><tr><td style="text-align:left"><strong>核心适用场景</strong></td><td style="text-align:left">CI/CD 流水线、纯确定性爬虫</td><td style="text-align:left">跨应用 C 端操作、非结构化任务辅助</td><td style="text-align:left">内部复杂系统集成、实时异常排查调试</td></tr></tbody></table><p>3 月 29 日，OpenClaw 的作者 Peter Steinberger 发了一篇推文，推文中也提到：</p><blockquote><p>The Chrome extension has been removed since Google added native access in 144+, which is simpler, but yes, it does require a one-time setting change.</p></blockquote><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/steipete/status/2037988925818519763"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><h2 id="参考文献">参考文献</h2><hr class="footnotes-sep"><section class="footnotes"><ol class="footnotes-list"><li id="fn1" class="footnote-item"><p><a href="https://x.com/heynavtoor/status/2037200578842157462">Harness Engineering: Why the Best AI Engineers in 2026 Stopped Writing Code</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p></li><li id="fn2" class="footnote-item"><p><a href="https://developer.chrome.com/blog/chrome-devtools-mcp-debug-your-browser-session">Let your Coding Agent debug your browser session with Chrome DevTools MCP</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p></li></ol></section></body></html>]]>
      </content:encoded>
    </item>
    <item>
      <title>我让 OpenClaw 🦞 管理大模型评测任务</title>
      <link>https://wangwei1237.github.io/2026/03/15/I-use-OpenClaw-to-manage-LLMs-evals-tasks/</link>
      <description>
        <![CDATA[<html><head></head><body><p><img src="/2026/03/15/I-use-OpenClaw-to-manage-LLMs-evals-tasks/1.png" alt=""></p>
<p>虽然 LLM 的能力现在已经非常强大，但是对于多模态大模型的评测而言，仍然需要进行大量的人工评测工作。所以，我们也会看到，类似 <a href="https://arena.ai/leaderboard">Arena</a> 平台的这种基于人类偏好打分的榜单，依然是我们评估大模型性能的有效参考。</p>
<p>我们参考 LMArena 团队开源的榜单算法 <a href="https://arena.ai/blog/arena-rank/">Arena Rank</a> 构建了自己的大模型 Side-by-Side 评测系统，来满足我们对大模型评测的需求。</p></body></html>]]>
      </description>
      <author>Wang Wei</author>
      <category domain="https://wangwei1237.github.io/categories/LLM/">LLM</category>
      <category domain="https://wangwei1237.github.io/tags/OpenClaw/">OpenClaw</category>
      <category domain="https://wangwei1237.github.io/tags/%E4%BB%BB%E5%8A%A1%E7%AE%A1%E7%90%86/">任务管理</category>
      <category domain="https://wangwei1237.github.io/tags/Task-Management/">Task Management</category>
      <pubDate>Sun, 15 Mar 2026 19:20:25 GMT</pubDate>
      <content:encoded>
        <![CDATA[<html><head></head><body><p><img src="/2026/03/15/I-use-OpenClaw-to-manage-LLMs-evals-tasks/1.png" alt=""></p><p>虽然 LLM 的能力现在已经非常强大，但是对于多模态大模型的评测而言，仍然需要进行大量的人工评测工作。所以，我们也会看到，类似 <a href="https://arena.ai/leaderboard">Arena</a> 平台的这种基于人类偏好打分的榜单，依然是我们评估大模型性能的有效参考。</p><p>我们参考 LMArena 团队开源的榜单算法 <a href="https://arena.ai/blog/arena-rank/">Arena Rank</a> 构建了自己的大模型 Side-by-Side 评测系统，来满足我们对大模型评测的需求。</p><span id="more"></span><h2 id="side-by-side-评测系统">Side-by-Side 评测系统</h2><p>与 LMArena 不同，我们 Side-by-Side 评测系统的样本是预先准备好的，而非用户上传。在我们的系统中，我们会把待用户打分的样本组织成一个任务，同时我们会招募打分用户为这个任务打分。</p><p>以文生图任务为例，平台的打分过程如下图所示：</p><p><img src="/2026/03/15/I-use-OpenClaw-to-manage-LLMs-evals-tasks/2.png" alt="Side-by-Side 偏好打分"></p><p>当用户打分完成之后，我们会利用 <a href="https://arena.ai/blog/arena-rank/">Arena Rank</a> 算法来更新所有模型的评分与榜单信息。</p><p>所以，在我们的系统中，一个评测任务的基本流程如下：</p><pre class="mermaid">graph LR    A[创建评估任务] --&gt; B[发布招募信息]    B --&gt; C[用户报名]    C --&gt; D[创建群聊]    D --&gt; E[群聊中发布任务链接]    E --&gt; F[定期跟进进度]    F --&gt; G[计算 Elo 排序]    style A fill:#f9f9f9,stroke:#333,stroke-width:2px    style B fill:#f9f9f9,stroke:#333,stroke-width:2px    style C fill:#f9f9f9,stroke:#333,stroke-width:2px    style D fill:#f9f9f9,stroke:#333,stroke-width:2px    style E fill:#f9f9f9,stroke:#333,stroke-width:2px    style F fill:#f9f9f9,stroke:#333,stroke-width:2px    style G fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px</pre><p>在 OpenClaw 出现之前，评测任务的创建、发布、招募、跟进和计算等全流程都需要专人负责。有时候，我们一天也会创建多个评测任务，这个时候负责管理评测任务的同学就会焦头烂额。</p><p>在用了一段时间的 OpenClaw 之后，我发现 OpenClaw 具备如下的四大特性，这四大特性让 OpenClaw 非常适合用来管理大模型的评测任务。</p><ul><li>更强大的记忆能力（跨天级别长程任务）</li><li>更强大的主机控制能力（系统管理员级别）</li><li>更强大的交互能力（多端 IM 触达，随时、随地的触达）</li><li>更强大的 Agent 蜂群能力（多 Agent 协同）</li></ul><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/wangwei1237/status/2033849102954598823"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><p>于是，我们心动了，我们决定用技术手段来解决我们评测专员的烦恼，我们决定让 OpenClaw 来管理我们的评测任务。</p><h2 id="openclaw-的基本能力">OpenClaw 的基本能力</h2><p>这是一个典型的 <code>pipeline skill</code> <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>。在开始之前，我们需要梳理 <code>agent</code> 所需的能力以及 OpenClaw 能提供的支持，以便于后续的 <code>skill</code> 开发。</p><p><img src="/2026/03/15/I-use-OpenClaw-to-manage-LLMs-evals-tasks/agent_types.jpeg" alt=""></p><h3 id="workspace-文件加载时机">Workspace 文件加载时机</h3><p>OpenClaw 的行为主要由 <code>workspace</code> 文件而非隐藏的 <code>prompt</code> 文件定义。正确设置 <em><a href="http://SOUL.md">SOUL.md</a></em>、<em><a href="http://AGENTS.md">AGENTS.md</a></em>、<em><a href="http://USER.md">USER.md</a></em>、<em><a href="http://MEMORY.md">MEMORY.md</a></em> 以及 <em><a href="http://TOOLS.md">TOOLS.md</a></em> 等文件以及了解每个文件的作用及其加载时机，对于实现我们的目标至关重要。</p><p>OpenClaw 会在 session 启动的时候去读取 <code>~/.openclaw/workspace/</code> 下的相关文件，具体的区别和作用如下所示<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>：</p><table><thead><tr><th>文件</th><th>作用</th><th>加载时机</th></tr></thead><tbody><tr><td><a href="http://AGENTS.md">AGENTS.md</a></td><td>Operating instructions, priorities, workflow rules</td><td>Every session</td></tr><tr><td><a href="http://SOUL.md">SOUL.md</a></td><td>Personality, tone, values, behavioral constraints</td><td>Every session</td></tr><tr><td><a href="http://USER.md">USER.md</a></td><td>About you — name, preferences, style</td><td>Every session</td></tr><tr><td><a href="http://IDENTITY.md">IDENTITY.md</a></td><td>Agent name, role, goals, voice</td><td>Every session</td></tr><tr><td><a href="http://TOOLS.md">TOOLS.md</a></td><td>Local tool notes, calendar IDs, conventions</td><td>Every session</td></tr><tr><td><a href="http://HEARTBEAT.md">HEARTBEAT.md</a></td><td>Checklist for periodic heartbeat runs</td><td>Heartbeat only</td></tr><tr><td><a href="http://BOOTSTRAP.md">BOOTSTRAP.md</a></td><td>First-run interview — auto-deleted after</td><td>First run</td></tr><tr><td><a href="http://MEMORY.md">MEMORY.md</a></td><td>Long-term curated memory (optional)</td><td>Main DM only</td></tr><tr><td>memory/YYYY-MM-DD.md</td><td>Daily logs — today + yesterday loaded</td><td>Every session</td></tr></tbody></table><p>因此，如果我们想在群聊启动时、或者在群聊中通过 <code>/new</code> 命令创建一个新的群聊 <code>session</code> 的时候，让群聊能够记起之前的内容，那么最好不要把记忆放在 <code>MEMORY.md</code> 文件中。<code>TOOLS.md</code> 文件是一个不错的选择。</p><h3 id="跨-session-通信能力">跨 session 通信能力</h3><p>在 OpenClaw 中，默认情况下，所有的 “私聊（DM）” 共用一个“主会话（main session）”，以保证对话的连贯性；而不同的 “群聊（Group）” 或者 “频道” 则会拥有各自的“独立会话（isolated sessions）”。<sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup></p><p>如果你的 OpenClaw 可以接收来自多个用户的私信消息，此时，所有用户将共享相同的“主会话”上下文，这会导致用户之间的私人信息泄露，进而带来安全风险。比如我的 OpenClaw 授权给了我的很多同事，他们每天都会和我用同一个 🦞 来私聊并处理工作上的事情，而我不想让其他的同事知道我和 🦞 都聊了什么。</p><p>这个时候，就需要使用 <code>session.dmScope</code> 来控制私聊会话的隔离范围，保证不同用户之间的会话隔离。<sup class="footnote-ref"><a href="#fn3" id="fnref3:1">[3:1]</a></sup></p><figure class="highlight json"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ~/.openclaw/openclaw.json</span></span><br><span class="line"><span class="punctuation">{</span></span><br><span class="line">  session<span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">    <span class="comment">// Secure DM mode: isolate DM context per channel + sender.</span></span><br><span class="line">    dmScope<span class="punctuation">:</span> <span class="string">"per-channel-peer"</span><span class="punctuation">,</span></span><br><span class="line">  <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">* main (default): all DMs share the main session <span class="keyword">for</span> continuity.</span><br><span class="line">* per-peer: isolate by sender <span class="built_in">id</span> across channels.</span><br><span class="line">* per-channel-peer: isolate by channel + sender (recommended <span class="keyword">for</span> multi-user inboxes).</span><br><span class="line">* per-account-channel-peer: isolate by account + channel + sender (recommended <span class="keyword">for</span> multi-account inboxes). </span><br></pre></td></tr></tbody></table></figure><p>当多个私聊和群聊以独立的 session 运行时，如何让这些相互隔离的 session 保持必要的信息互通，就成了一个需要解决的问题。整个评测的过程是在不同的私聊、群聊中不停的切换，没有一定的跨 session 能力，🦞 就会给人一种暂时性失忆的印象。</p><p><strong>是的，有时候就是这么矛盾，既要保证信息隔离，又要保证一定的信息互通。</strong></p><h3 id="文件读写能力">文件读写能力</h3><p>文件操作的基础是文件路径的访问能力，根据 OpenClaw 的 <em>官方文档</em>：<code>agent</code> 的 <code>workspace</code> 目录就是当前 <code>agent</code> 的默认 <code>CWD</code>，在 <code>agent</code> 的 <code>context</code> 中，所有的 <em>相对路径</em> 均基于 <code>workspace</code> 进行解析。当然，绝对路径可以访问主机的其他位置。<sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup><sup class="footnote-ref"><a href="#fn5" id="fnref5">[5]</a></sup><sup class="footnote-ref"><a href="#fn6" id="fnref6">[6]</a></sup></p><blockquote><p>OpenClaw uses a single agent workspace directory (agents.defaults.workspace) as the agent’s only working directory (cwd) for tools and context.</p><p>The workspace is the default cwd, not a hard sandbox. Tools resolve relative paths against the workspace, but absolute paths can still reach elsewhere on the host unless sandboxing is enabled.</p><p>Workspace note: each agent’s workspace is the default cwd, not a hard sandbox. Relative paths resolve inside the workspace, but absolute paths can reach other host locations unless sandboxing is enabled.</p></blockquote><p>这一点非常重要，因为接下来我们在写 <code>skill</code> 的时候，会经常涉及到文件路径的解析问题：</p><ul><li>在 <code>SKILL.md</code> 中引用其他参考文件；</li><li>在 <code>SKILL.md</code> 中执行 <code>scripts</code> 路径下的某个脚本；</li><li>……</li></ul><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition warning"><p class="admonition-title">CWD 路径问题</p><p>在 Claude Code 中编写 <code>skill</code> 时，<code>CWD</code> 路径即为该 <code>skill</code> 的路径。因此，<code>SKILL.md</code> 中的所有<em>相对路径</em>都基于该 <code>skill</code> 的路径进行解析。</p><p>但是，在 OpenClaw 中，规则变了：<code>workspace</code> 目录才是 <code>agent</code> 的默认 <code>CWD</code>。因此，对于 OpenClaw 来说，<code>SKILL.md</code> 中所有的 <em>相对路径</em> 均基于 <code>workspace</code> 进行解析。</p></div><p>从 3.2 版本开始，为解决安全问题，OpenClaw 加强了对工具调用的权限控制。默认情况下，禁止对主机文件系统进行写或执行操作，如需此类权限，要在配置文件中显式授权。<sup class="footnote-ref"><a href="#fn7" id="fnref7">[7]</a></sup></p><figure class="highlight json"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">"tools"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">  <span class="attr">"profile"</span><span class="punctuation">:</span> <span class="string">"full"</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure><h3 id="多-agent-协同能力">多 Agent 协同能力</h3><p>整个评估过程核心分为三个阶段：发起任务、跟进状态和销毁任务。虽然单个 Agent 可以完成所有阶段，但拆分为多个 Agent 更易于维护。</p><ul><li>每次任务都会重新执行 Main Agent，Main Agent 只负责发起任务并生成 Status Agent（跟进任务） 和 Killer Agent（销毁任务）<ul><li>每一次评估任务都会创建单独的 Status Agent 来跟踪当前的评估任务状态</li><li>如果 Killer Agent 已经存在了就不需要重新创建</li><li>Main Agent 完成任务后就退出了</li></ul></li><li>Status Agent 和 Killer Agent 会周期性的醒来并执行相应的任务。</li><li>当 Killer Agent 检测到任务完成时，会终止相应的 Status Agent，以释放资源。</li></ul><p><img src="/2026/03/15/I-use-OpenClaw-to-manage-LLMs-evals-tasks/8.png" alt=""></p><p>OpenClaw 中的 Cron 即是调度器、更是一个非常强大的 Agent Spawn 工具<sup class="footnote-ref"><a href="#fn8" id="fnref8">[8]</a></sup>。</p><blockquote><p>Cron is the Gateway’s built-in scheduler. It persists jobs, wakes the agent at the right time, and can optionally deliver output back to a chat.</p></blockquote><p>当我们使用 <code>--session isolated</code> 和 <code>--message</code> 创建定时任务时，OpenClaw 会以 <code>--message</code> 指定的内容作为提示词创建一个新的 Agent，并以 <code>--cron</code> 中指定的调度周期定期唤醒该 Agent。</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">openclaw cron add \</span><br><span class="line">--name <span class="string">"<span class="variable">${job-name}</span>"</span> \</span><br><span class="line">--cron <span class="string">"*/3 * * * *"</span> \</span><br><span class="line">--tz <span class="string">"Asia/Shanghai"</span> \</span><br><span class="line">--session isolated \</span><br><span class="line">--message <span class="string">"<span class="variable">${MESSAGE}</span>"</span></span><br></pre></td></tr></tbody></table></figure><p>此时，每次定时任务触发，OpenClaw 都会强制生成一个全新的 Session ID（以 cron:&lt;<a href="http://job.id">job.id</a>&gt; 作为 Session Key），并且完全不会复用之前的闲置会话，从而避免任务多次执行过程中的上下文污染。</p><h2 id="工程实现">工程实现</h2><h3 id="长短期记忆分离">长短期记忆分离</h3><p>整个评估流程是相对固定的、需要长期记住的。但是每个评估任务是一个相对短期的概念，评测任务结束之后，OpenClaw 不需要对这个任务有任何的记忆。在这种场景下：</p><ul><li>长期记忆是整个业务流程，不依赖任何短期状态</li><li>短期记忆是任务的状态，当 Status Agent、Killer Agent 唤醒时，需要依赖任务的状态来保持任务的连续性</li></ul><p>于是，在我们的设计中：长期记忆以 <a href="http://SKILL.md">SKILL.md</a> 的形式存在，而需要在多 sessions/Agents 之间通信的短期任务状态信息，我们放在一个共享文件中——类似多进程架构中的共享内存。</p><p><a href="http://SKILL.md">SKILL.md</a> 中详细描述了评测业务流程，并包含了构建 Status Agent 和 Killer Agent 的策略。</p><p>于是，整体的目录结构就成了如下的样子：</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">llm-evals/</span><br><span class="line">├── eval_tasks.json  <span class="comment"># 短期的任务状态文件，在 Skill 首次执行时创建</span></span><br><span class="line">├── SKILL.md <span class="comment"># Main Agent</span></span><br><span class="line">├── status-sub-agent.md  <span class="comment"># Status Agent</span></span><br><span class="line">├── killer-sub-agent.md  <span class="comment"># Killer Agent</span></span><br><span class="line">└── scripts/ <span class="comment"># CLIs</span></span><br><span class="line">    ├── check_eval_progress_tool.py </span><br><span class="line">    ├── create_im_group_tool.py</span><br><span class="line">    └── trigger_report_tool.py</span><br></pre></td></tr></tbody></table></figure><h3 id="main-agent-skill-md">Main Agent(<a href="http://SKILL.md">SKILL.md</a>)</h3><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">name: llm-evals-skill</span><br><span class="line">description: 当用户提供任务参数时，使用此技能初始化一个新的 LLM 评估任务。它会持久化任务状态、发布招募消息，并创建子代理 sub_agent 执行任务。</span><br><span class="line">---</span><br><span class="line"></span><br><span class="line"># LLM 评估代理</span><br><span class="line"></span><br><span class="line">## 何时使用</span><br><span class="line">当用户提供新盲测或评估任务的参数时（例如，评估类型、评估链接、目标人数、奖励描述和截止日期），立即触发此技能。**请勿使用此技能来检查进度。**</span><br><span class="line"></span><br><span class="line">有关如何创建 *status-sub-agent* 并为其配置 cron 任务的详细信息，请参阅 `./skills/llm-Arena/status-sub-agent.md`。</span><br><span class="line"></span><br><span class="line">有关如何创建 *killer-sub-agent* 并为其配置 cron 任务的详细信息，请参阅 `./skills/llm-Arena/killer-sub-agent.md`。</span><br><span class="line"></span><br><span class="line">## eval_tasks.json 文件格式</span><br><span class="line">执行此技能时，请确保已从用户提示中提取以下参数：</span><br><span class="line">- `task_id`: 任务的唯一标识符,必填。</span><br><span class="line">- `creator`: 任务的创建人，即用户的用户名，必填。</span><br><span class="line">- `platform_task_id`: 评估平台任务的 ID，例如 `1234567`，可后继更新。</span><br><span class="line">- `status`: 任务状态，`INIT`——任务创建初始化, `RECRUITING`——招募众测用户, `EVALUATING`——评估打分中, `DONE`——任务完成，必填且只能是这 4 项。</span><br><span class="line">……</span><br><span class="line"></span><br><span class="line">## 指令</span><br><span class="line"></span><br><span class="line">严格按顺序执行以下步骤：</span><br><span class="line">1. **创建 Killer Agent**</span><br><span class="line">  请检查是否存在 `./skills/llm-Arena/llm-Arena/eval_tasks.json` 文件，如果文件不存在，则做如下的操作：</span><br><span class="line">  - 创建一个新的文件 `./skills/llm-Arena/llm-Arena/eval_tasks.json`，其内容为 "{}"</span><br><span class="line">  - 使用 cron 命令创建一个新的 Killer Agent，**openclaw cron 命令** 如下：</span><br><span class="line"></span><br><span class="line">   ```bash</span><br><span class="line">      FILE_CONTENT=$(cat "./skills/llm-Arena/killer-sub-agent.md")</span><br><span class="line">      MESSAGE="请严格执行如下任务流程： $FILE_CONTENT"</span><br><span class="line">      openclaw cron add \</span><br><span class="line">      --name "任务名称" \</span><br><span class="line">      --cron "*/3 * * * *" \</span><br><span class="line">      --tz "Asia/Shanghai" \</span><br><span class="line">      --session isolated \</span><br><span class="line">      --message "$MESSAGE"</span><br><span class="line">   ```</span><br><span class="line"></span><br><span class="line">2. **持久化任务状态**：</span><br><span class="line">   - 打开本地状态文件 `./skills/llm-Arena/llm-Arena/eval_tasks.json`。</span><br><span class="line">   - 为此 `task_id` 创建一个新的 JSON 对象，不要覆盖之前的对象，要在之前的对象基础上追加一个新的对象。</span><br><span class="line">   - 将初始 `status` 设置为 `"INIT"`。</span><br><span class="line">   - 将必须的输入保存到此对象中。</span><br><span class="line">   - 将更新后的 JSON 安全地写回磁盘。</span><br><span class="line">   - 汇报已经成功持久化此次任务状态文件给任务创建人，即给我发一条创建成功的消息，并给出状态文件内容。</span><br><span class="line"></span><br><span class="line">3. **创建 Status Agent**</span><br><span class="line">   - **定时任务创建**</span><br><span class="line">     1. 执行下面 `openclaw cron命令` 创建定时任务，注意，需要把 `task_id` 替换成本次任务的ID。</span><br><span class="line">     # …… &lt;其他的限制条件&gt;</span><br><span class="line"></span><br><span class="line">   - **openclaw cron命令**</span><br><span class="line"></span><br><span class="line">   ```bash</span><br><span class="line">      FILE_CONTENT=$(cat "./skills/llm-Arena/status-sub-agent.md")</span><br><span class="line">      MESSAGE="当前任务ID 为 task_id, 请为这个任务严格执行如下任务流程： $FILE_CONTENT"</span><br><span class="line">      openclaw cron add \</span><br><span class="line">      --name "任务名称" \</span><br><span class="line">      --cron "*/3 * * * *" \</span><br><span class="line">      --tz "Asia/Shanghai" \</span><br><span class="line">      --session isolated \</span><br><span class="line">      --message "$MESSAGE"</span><br><span class="line">   ```</span><br><span class="line">   - **openclaw cron命令行执行之后的返回信息**</span><br><span class="line">    1. `openclaw cron` 命令如果执行成功后会返回显示类似如下格式的 json 信息：</span><br><span class="line">         ```json</span><br><span class="line">               {</span><br><span class="line">                  "id": "b818d9ca-3cea-48de-805e-2b42c9ae0d60",</span><br><span class="line">                  "name": "test-cron-wukong",</span><br><span class="line">                  "enabled": true,</span><br><span class="line">                  "createdAtMs": 1773210163484,</span><br><span class="line">                  "updatedAtMs": 1773210163484,</span><br><span class="line">                  "schedule": {</span><br><span class="line">                     "kind": "cron",</span><br><span class="line">                     "expr": "0 10 * * *",</span><br><span class="line">                     "tz": "Asia/Shanghai"</span><br><span class="line">                  },</span><br><span class="line">                  "sessionTarget": "isolated",</span><br><span class="line">                  "wakeMode": "now",</span><br><span class="line">                  "payload": {</span><br><span class="line">                     "kind": "agentTurn",</span><br><span class="line">                     "message": "test message"</span><br><span class="line">                  },</span><br><span class="line">                  "delivery": {</span><br><span class="line">                     "mode": "announce",</span><br><span class="line">                     "channel": "last"</span><br><span class="line">                  },</span><br><span class="line">                  "state": {</span><br><span class="line">                     "nextRunAtMs": 1773280800000</span><br><span class="line">                  }</span><br><span class="line">               }</span><br><span class="line">         ```</span><br><span class="line">    2. **关键返回字段说明** id任务唯一ID（后续编辑/删除需要用到, name任务名称, enabled是否启用, createdAtMs创建时间（毫秒时间戳）, schedule调度配置（cron 表达式 + 时区）, sessionTarget运行模式（main 或 isolated）, payload任务内容（message 即你要执行的指令）, delivery交付方式, state.nextRunAtMs下次执行时间。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">4. **发布招募消息**：</span><br><span class="line">   - 根据 `theme` 和 `reward_desc` 撰写一条吸引人的招募消息，注意，务必带上本次任务的ID`task_id` 信息。</span><br><span class="line">   - **请勿在消息中包含 `eval_link` 信息**。相反，请指示感兴趣的用户通过回复消息或遵循特定的行动号召来报名。</span><br><span class="line">   - 将此消息发送到 `im_recruit_group_id`。</span><br><span class="line">   - 修改任务状态为 `RECRUITING` 招募中。</span><br><span class="line"></span><br><span class="line">## 约束与边界情况</span><br><span class="line">1. **错误反馈**:</span><br><span class="line">   - 如果出现任何失败, 需要立刻通知用户, 并说明失败原因。</span><br></pre></td></tr></tbody></table></figure><h3 id="eval-tasks-文件">eval_tasks 文件</h3><p><img src="/2026/03/15/I-use-OpenClaw-to-manage-LLMs-evals-tasks/task_json.png" alt=""></p><h3 id="status-agent">Status Agent</h3><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"># 评估生命周期心跳</span><br><span class="line"></span><br><span class="line">本文档是评测任务生命周期任务 Status Agent 的指导说明文档。</span><br><span class="line">使用本文档作为系统提示词，来控制评测任务的整体生命周期和状态，包括检查报名情况、创建 IM 群组、向待处理用户发送提醒、生成报告。</span><br><span class="line"></span><br><span class="line">## 何时使用</span><br><span class="line">维护评测任务的生命周期时需要严格按照本文档指导执行任务。它需要一个 `task_id` 来知道需要推进哪个评估任务的状态机。 所有任务相关的信息都在这个文档中：`./skills/llm-Arena/eval_tasks.json`。</span><br><span class="line"></span><br><span class="line">## 指令</span><br><span class="line"></span><br><span class="line">1. **读取任务状态**：</span><br><span class="line">   ……</span><br><span class="line"></span><br><span class="line">2. **处理 `RECRUITING` 状态**：</span><br><span class="line">   ……</span><br><span class="line"></span><br><span class="line">3. **处理 `EVALUATING` 状态**：</span><br><span class="line">   ……</span><br><span class="line"></span><br><span class="line">4. **处理 `DONE` 状态**:</span><br><span class="line">   - 保持静默，不再处理任何操作。</span><br><span class="line"></span><br><span class="line">## 约束与边界情况</span><br><span class="line">- **幂等性**：切勿多次创建 IM 群组。在创建之前始终验证 `im_sub_group_id` 是否为 null。</span><br><span class="line">- **数据隔离**：确保仅查询和发送消息给与传递给此技能的特定 `task_id` 相关的用户。不要在不同任务之间交叉污染数据。</span><br><span class="line">……</span><br></pre></td></tr></tbody></table></figure><h3 id="killer-agent">Killer Agent</h3><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"># Killer Agent</span><br><span class="line"></span><br><span class="line">使用本文档来对 Status Agent 任务完成情况进行检测，当 Status Agent 对应的任务完成时，就需要结束对应的 Status Agent 任务。</span><br><span class="line"></span><br><span class="line">## 指令</span><br><span class="line"></span><br><span class="line">1. **读取任务状态**：</span><br><span class="line">   ……</span><br><span class="line"></span><br><span class="line">2. **处理 `DONE` 状态**：</span><br><span class="line">   从 `./skills/llm-Arena/eval_tasks.json` 中提取 `cron_task_id`，使用如下的命令删除对应的 Status Agent 任务。</span><br><span class="line"></span><br><span class="line">   ```bash</span><br><span class="line">   openclaw cron rm cron_task_id</span><br><span class="line">   ```</span><br></pre></td></tr></tbody></table></figure><h3 id="报名消息处理">报名消息处理</h3><p>默认情况下，当用户在群聊中 @OpenClaw 报名时，OpenClaw 会自动回复，这并非理想的交互体验。我们希望它能默默记录报名信息而无需回复。</p><p><img src="/2026/03/15/I-use-OpenClaw-to-manage-LLMs-evals-tasks/baoming.png" alt=""></p><p>由于创建任务和招募打分人员是在不同的会话，所以这两个对话的上下文是割裂的，因此如果仅在 <a href="http://SKILL.md">SKILL.md</a> 中定义规则（私聊发布任务时加载）无法满足我们的需求。如前所述，我们可以把对应的指令放在 <code>TOOLS.md</code> 文件中：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">## LLM 评估任务创建与用户报名处理规范</span><br><span class="line"></span><br><span class="line">当用户在群聊中回复"报名"、"+1"或其他表示有空参与的消息时：</span><br><span class="line">1. **不需要回复** - 用户的报名消息不需要回复</span><br><span class="line">2. **判断用户参加的任务id** - 根据上下文对话信息，` 中的以及 `./skills/llm-Arena/eval_tasks.json任务记录，判断用户参加的是哪个任务，用于更新对应任务的记录。</span><br><span class="line">3. **默默更新**</span><br><span class="line">   - 将用户ID以 list 形式写入 `./skills/llm-Arena/eval_tasks.json` 的 `participants` 字段</span><br><span class="line">   - 格式：`"participants": ["user1", "user2"]`</span><br><span class="line">4. **禁止操作** - 禁止修改 `./skills/llm-Arena/eval_tasks.json` 中任何一个任务的 `status` 字段。</span><br></pre></td></tr></tbody></table></figure><p><img src="/2026/03/15/I-use-OpenClaw-to-manage-LLMs-evals-tasks/baoming_1.png" alt=""></p><h2 id="最终效果">最终效果</h2><p>一句话驱动评测流程，全程不再需要我们的评测专员人工盯盘了。<br><img src="/2026/03/15/I-use-OpenClaw-to-manage-LLMs-evals-tasks/result.png" alt=""></p><h2 id="写好-skill-真不容易">写好 Skill 真不容易</h2><p>编写 Skill 比写代码更具挑战，入门虽易，精通却难。</p><p>我们在开发如上的 Skill 的时候，其实只花了不到半个小时就写好了最初版本的 Skill，但是调试、优化却花了 3 天多的时间。</p><p>调试 Skill 与调试代码不同，过程更为繁琐。代码的语法错误能通过编译立即发现，而 Skill 的逻辑错误则需要更长的反馈路径才能识别，因为即使模型能运行，结果也可能不符合预期。</p><p>此外，不同模型对 Skill 的理解和支持粒度各异，导致在某个模型上运行良好的 Skill，在其他模型上可能失效。</p><p>因此，调试 Skill 时，不能只关注最终结果，还需仔细检查和分析所有中间过程（如工具调用、执行结果等），以寻找优化空间。</p><p>在调试过程中，我们可以利用 OpenClaw 的 WEBUI 查看每次对话的详细信息，以观察其在任务执行过程中的具体行为。</p><p><img src="/2026/03/15/I-use-OpenClaw-to-manage-LLMs-evals-tasks/debug.png" alt=""></p><h2 id="后记">后记</h2><p>后来，在学习 Anthropic 的 <a href="https://www.anthropic.com/engineering/harness-design-long-running-apps">Harness design for long-running application development</a> 时，里面对解决模型所面临的上下文窗口焦虑问题时所提到的方案<sup class="footnote-ref"><a href="#fn9" id="fnref9">[9]</a></sup>：</p><blockquote><p>Context resets—clearing the context window entirely and starting a fresh agent, combined with a structured handoff that carries the previous agent’s state and the next steps—addresses both these issues.<br>A reset provides a clean slate, at the cost of the handoff artifact having enough state for the next agent to pick up the work cleanly.</p></blockquote><p>这与我们在 Main Agent 中通过 Cron 来定期创建新的 Status Agent 有异曲同工之处。</p><h2 id="参考文献">参考文献</h2><hr class="footnotes-sep"><section class="footnotes"><ol class="footnotes-list"><li id="fn1" class="footnote-item"><p><a href="https://x.com/GoogleCloudTech/status/2033953579824758855">5 Agent Skill design patterns every ADK developer should know</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p></li><li id="fn2" class="footnote-item"><p><a href="https://openclawcheatsheet.com/">OpenClaw Cheat Sheet</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p></li><li id="fn3" class="footnote-item"><p><a href="https://docs.openclaw.ai/concepts/session#secure-dm-mode-recommended-for-multi-user-setups">OpenClaw: Session Management</a> <a href="#fnref3" class="footnote-backref">↩︎</a> <a href="#fnref3:1" class="footnote-backref">↩︎</a></p></li><li id="fn4" class="footnote-item"><p><a href="https://docs.openclaw.ai/concepts/agent#workspace-required">OpenClaw: Workspace Required</a> <a href="#fnref4" class="footnote-backref">↩︎</a></p></li><li id="fn5" class="footnote-item"><p><a href="https://docs.openclaw.ai/concepts/agent-workspace">OpenClaw: Agent Workspace</a> <a href="#fnref5" class="footnote-backref">↩︎</a></p></li><li id="fn6" class="footnote-item"><p><a href="https://docs.openclaw.ai/concepts/multi-agent">OpenClaw: Multi Agent</a> <a href="#fnref6" class="footnote-backref">↩︎</a></p></li><li id="fn7" class="footnote-item"><p><a href="https://docs.openclaw.ai/tools">OpenClaw: Tools</a> <a href="#fnref7" class="footnote-backref">↩︎</a></p></li><li id="fn8" class="footnote-item"><p><a href="https://docs.openclaw.ai/automation/cron-jobs">OpenClaw Cron Jobs</a> <a href="#fnref8" class="footnote-backref">↩︎</a></p></li><li id="fn9" class="footnote-item"><p><a href="https://www.anthropic.com/engineering/harness-design-long-running-apps">Harness design for long-running application development</a> <a href="#fnref9" class="footnote-backref">↩︎</a></p></li></ol></section></body></html>]]>
      </content:encoded>
    </item>
    <item>
      <title>我让 OpenClaw 🦞 帮我部署模型</title>
      <link>https://wangwei1237.github.io/2026/03/13/I-asked-OpenClaw-to-help-deploy-the-LLMs/</link>
      <description>
        <![CDATA[<html><head></head><body><p><img src="/2026/03/13/I-asked-OpenClaw-to-help-deploy-the-LLMs/1.png" alt=""></p>
<p>在 <a href="/2026/03/08/I-let-OpenClaw-do-OP-for-me/">我让 OpenClaw 🦞 为我当 OP</a> 这篇文章中，我介绍了我是如何让 OpenClaw 帮我管理服务器集群的。</p>
<p>过了几天，我就开始想搞一点更复杂的事情。既然 OpenClaw 都可以帮我管理服务器集群了，那为什么不能让它帮我部署模型呢？反正我每次部署模型不也是先登录 GPU 服务器，然后在上面执行各种 CLI 操作（下载模型、配置虚拟环境、下载依赖……）？并且每次遇到部署问题时，我也是截个图直接丢给大模型……</p></body></html>]]>
      </description>
      <author>Wang Wei</author>
      <category domain="https://wangwei1237.github.io/categories/LLM/">LLM</category>
      <category domain="https://wangwei1237.github.io/tags/OpenClaw/">OpenClaw</category>
      <category domain="https://wangwei1237.github.io/tags/%E6%A8%A1%E5%9E%8B%E9%83%A8%E7%BD%B2/">模型部署</category>
      <pubDate>Fri, 13 Mar 2026 17:53:42 GMT</pubDate>
      <content:encoded>
        <![CDATA[<html><head></head><body><p><img src="/2026/03/13/I-asked-OpenClaw-to-help-deploy-the-LLMs/1.png" alt=""></p><p>在 <a href="/2026/03/08/I-let-OpenClaw-do-OP-for-me/">我让 OpenClaw 🦞 为我当 OP</a> 这篇文章中，我介绍了我是如何让 OpenClaw 帮我管理服务器集群的。</p><p>过了几天，我就开始想搞一点更复杂的事情。既然 OpenClaw 都可以帮我管理服务器集群了，那为什么不能让它帮我部署模型呢？反正我每次部署模型不也是先登录 GPU 服务器，然后在上面执行各种 CLI 操作（下载模型、配置虚拟环境、下载依赖……）？并且每次遇到部署问题时，我也是截个图直接丢给大模型……</p><span id="more"></span><h2 id="调教之旅">调教之旅</h2><p>于是，我开始了我的调教 OpenClaw 并让它帮我部署模型之旅。整体的体验过程还是非常不错的。</p><p>虽然 LLMs 能力对 Linux 命令很熟悉，对模型部署的流程也很熟悉，对部署报错的排查也很熟悉……但是它对我服务器集群的细节并不熟悉。</p><p>于是我先开始告诉 OpenClaw 我的服务器集群的细节，包括：</p><ul><li>如何在远程服务器上执行命令</li><li>服务器的初始环境配置都有哪些：比如 Conda 配置、网络代理配置、可用的 vllm 环境……</li></ul><p>但是，在告诉它这些信息的时候，我没有把这些信息一股脑直接丢给 OpenClaw，我讨厌“填鸭式”教学，我也不想我的 🦞 接受这种方式的知识灌输。</p><p><strong>1. 我先从一个问题开始了我的调教之旅：</strong></p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">给你一台 8 卡的 A100 机器，你会部署一个 qwen3.5 的模型吗？</span><br></pre></td></tr></tbody></table></figure><p><img src="/2026/03/13/I-asked-OpenClaw-to-help-deploy-the-LLMs/3.png" alt=""></p><p>你看，虽然我给他的输入还有错别字，但是它已经可以理解我的意思了，并且开始尝试回答了。然后它根据自己的知识，给了我一个部署流程。</p><p><img src="/2026/03/13/I-asked-OpenClaw-to-help-deploy-the-LLMs/4.png" alt=""></p><p><strong>2. 输入服务器知识，让 🦞 优化部署步骤：</strong></p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">1. 机器上是多人共用的，所以为了避免影响其他人，我们用conda工具为每个带部署的模型提供一个conda 虚拟环境。</span><br><span class="line">2. 我们采用vllm的方式来部署模型。</span><br><span class="line">3. 模型文件，我们采用modelscope下载到本地的方式，下载根路径统一放在 /home/work/models/ 中。</span><br><span class="line"></span><br><span class="line">请根据这些信息重新整理一下模型部署的步骤。</span><br></pre></td></tr></tbody></table></figure><p><img src="/2026/03/13/I-asked-OpenClaw-to-help-deploy-the-LLMs/5.png" alt=""></p><p><strong>3. 在执行中提供更多信息：</strong><br>部署流程对不对，得执行一下才知道，然后我让 OpenClaw 去目标服务器执行它写的部署流程，然后我再根据执行的结果不听的告诉它其他的额外信息。</p><p><img src="/2026/03/13/I-asked-OpenClaw-to-help-deploy-the-LLMs/6_7.jpg" alt=""></p><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">除非必要，绝不多嘴</p><p>只有当执行过程出现网络异常的时候，我才会告诉它：服务器上要如何配置网络代理。否则，我是绝对不会提供这种冗余的信息的。</p></div><p>就像下面要讲到的端口占用的问题一样，OpenClaw 自己就可以解决掉，根本不需要我多嘴。</p><p><img src="/2026/03/13/I-asked-OpenClaw-to-help-deploy-the-LLMs/8.png" alt=""></p><p>就这样，经过几次尝试，大概 30 分钟之后，OpenClaw 就在和我的聊天中，帮我把 QWen3.5-0.8B 部署到了我的服务器。而我在整个过程中，除了和 OpenClaw 聊天外，我什么也没做。我没有登录我的服务器，我也没有手写任何的命令，连部署之后的服务可用性测试都是 OpenClaw 帮我做的。</p><p><img src="/2026/03/13/I-asked-OpenClaw-to-help-deploy-the-LLMs/2.png" alt=""></p><h2 id="沉淀成-skill">沉淀成 Skill</h2><p>虽然我的 🦞 又获得了一项新技能，但是对于没有经过类似调教的 OpenClaw 来说，再经过一次类似的过程还是比较麻烦的。如果能把模型部署的 SOP 沉淀为 <a href="/2025/12/24/Reflections-on-Agent-Skills/">Skill</a>，别的 🦞 挂载上这个 Skill，就会即刻拥有模型部署的能力。</p><p>在 <a href="https://ejholmes.github.io/2026/02/28/mcp-is-dead-long-live-the-cli.html">MCP is dead. Long live the CLI</a> 这篇文章中，作者提到：</p><blockquote><p>The best tools are the ones that work for both humans and machines. CLIs have had decades of design iteration. They’re composable, debuggable, and they piggyback on auth systems that already exist.</p><p>MCP tried to build a better abstraction. Turns out we already had a pretty good one.</p></blockquote><blockquote><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/wangwei1237/status/2028715565985255635"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></blockquote><p>我们已经看到，越来越多的框架或者产品都开始弃用 MCP 转而支持 CLI：</p><ul><li>OpenClaw 不支持 MCP</li><li>Pi 也不支持 MCP</li><li>甚至连卖 MCP 服务器的 <a href="https://mp.weixin.qq.com/s/lsAbbq3VSOubE6P_7vCFgg">Perplexity 也不再支持 MCP</a></li></ul><blockquote><p>3月11日，Perplexity 的联合创始人兼 CTO Denis Yarats 在自家开发者大会上说，公司内部已经不用 MCP 了，转向了 API 和 CLI。</p></blockquote><p>所以，在我和 OpenClaw 的聊天过程中，我就让他把部署的流程写成了一个 shell 脚本，实际执行部署时执行的就是那个部署脚本。于是，当我让 OpenClaw 根据聊天内容生成模型部署 Skill 时，生成的 Skill 本就是一个 <code>业务流说明</code> + <code>CLI</code> 的组合。</p><p>我也不想给他一系列的接口文档和命令说明，让他在运行时实时写代码并执行，而是直接给他一个它自己写的 CLI 脚本，让它直接执行。这样，可控性和稳定性会更高。</p><p>于是，我让 OpenClaw 帮我生成了一个模型部署的 Skill：</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">你能把刚才咱们的过程整理成一个模型部署的skill吗？</span><br><span class="line">把shell作为改shell的一个脚本文件。</span><br><span class="line">把整理好的skill放在/home/work/wangwei17/model-deploy目录</span><br></pre></td></tr></tbody></table></figure><p>并且，很快，OpenClaw 就生成了一个模型部署的 Skill：</p><p><img src="/2026/03/13/I-asked-OpenClaw-to-help-deploy-the-LLMs/10_11.jpg" alt=""></p><p>我稍加修改，就得到了一个可以直接使用的模型部署的 Skill——<a href="https://github.com/wangwei1237/wangwei1237.github.io_src/tree/master/source/_posts/I-asked-OpenClaw-to-help-deploy-the-LLMs/model-deploy">model-deploy</a>。</p><blockquote><p>备注：该 Skill 在 MiniMax-M2.5 上测试通过，可以点击 <a href="https://github.com/wangwei1237/wangwei1237.github.io_src/tree/master/source/_posts/I-asked-OpenClaw-to-help-deploy-the-LLMs/model-deploy">链接</a> 获取该 Skill 的代码。或者使用 <code>clawhub search model-deploy</code>、<code>clawhub install model-deploy</code> 来安装该 Skill。</p></blockquote><p>我把该 Skill 挂载到我的 OpenClaw 实例上，然后让它帮我部署模型。为了避免大模型使用 Session 会话中的对话历史获取知识，而没有从 <code>model-deploy</code> 技能获取知识，我首先用 <code>/new</code> 指令创建了一个新的对话 Session。</p><p><img src="/2026/03/13/I-asked-OpenClaw-to-help-deploy-the-LLMs/12.jpg" alt=""></p><p>🦞 能够知道它可以用 <code>model-deploy</code> 技能来部署模型，并且当部署过程中遇到 GPU 内存不足时，他还会问我怎么处理……<br>整个过程，除了给 🦞 发布部署任务外，我就只和它通信了一次：告诉他在卡 2 上部署，仅此而已。</p><h2 id="思考与总结">思考与总结</h2><p>当然，<code>model-deploy</code> 这个 Skill 并不是完美的，它只能在 vLLM 推理引擎上部署 Qwen 系模型，但是这已经是一个非常有趣的开端了。</p><p>只能控制一台服务器的 🦞 再厉害也仅仅是一个玩具。<strong>链接即智能</strong>，当 🦞 可以控制更大规模的集群的时候，它才能释放出无限的潜能，给与我们更大的想象空间。</p><p>实践之后我发现：<strong>Skill 是看起来简单，但是要写好是一件非常难的事情</strong>。虽然，我在文章中说：</p><blockquote><p>我稍加修改，就得到了一个可以直接使用的模型部署的 Skill。</p></blockquote><p>但是，说实话，我还是花了一些时间来修改这个 Skill 的，并没有像我说的那样那么轻松、简单。</p><p>另外，这个 Skill 我也仅仅在 MiniMax-M2.5 模型上测试通过，还没有在更大范围的模型上测试，有可能换个模型该 Skill 就不能用了。</p><p>所以，如何构建出一个可用的、稳健的、好用的 Skill 是一个值得深入探讨的问题，还需要做更多的尝试。正如谷歌在 <a href="https://arxiv.org/html/2602.12670v1">SkillsBench: Benchmarking How Well Agent Skills Work Across Diverse Tasks</a> 中发现的那样：</p><blockquote><p>our evaluation yields four key findings:<br>(1) <strong>curated Skills</strong> provide substantial but variable benefit (+16.2 percentage points average, with high variance across domains and configurations);<br>(2) <strong>self-generated Skills</strong> provide negligible or negative benefit (–1.3pp average), demonstrating that effective Skills require human-curated domain expertise;</p></blockquote><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/wangwei1237/status/2030430467829444670"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></body></html>]]>
      </content:encoded>
    </item>
    <item>
      <title>我让 OpenClaw 🦞 为我当 OP</title>
      <link>https://wangwei1237.github.io/2026/03/08/I-let-OpenClaw-do-OP-for-me/</link>
      <description>
        <![CDATA[<html><head></head><body><p><img src="/2026/03/08/I-let-OpenClaw-do-OP-for-me/1.png" alt=""><br>
正如我在 <a href="/2026/03/04/Add-Chrome-browser-as-a-Browser-tool-for-OpenClaw/">为 OpneClaw 增加 Browser 工具</a> 中提到的那样：</p>
<blockquote>
<p>在整个的部署过程中，遇到了太多的坑，每遇到一个坑，我就让 Gemini 帮我解决。</p>
</blockquote>
<p>突然，我意识到：目前 LLMs 对于 Linux 系统的理解应该比人类要强得多，对 Linux 系统的各种命令操作也应该比人类要强得多。</p>
<p>既然如此，为什么不让 OpenClaw 帮我解决 Linux 系统的运维呢？恰好，我有一个 3 台机器的小集群，每天都要登录上去做一些手工操作，我不想再这样继续下去了。我想做出一些改变，于是，我决定让 OpenClaw 来当 OP，帮我管理集群。我要做的就是：通过 IM 下发指令。</p></body></html>]]>
      </description>
      <author>Wang Wei</author>
      <category domain="https://wangwei1237.github.io/categories/LLM/">LLM</category>
      <category domain="https://wangwei1237.github.io/tags/OpenClaw/">OpenClaw</category>
      <category domain="https://wangwei1237.github.io/tags/%E5%B7%A5%E5%85%B7/">工具</category>
      <pubDate>Sun, 08 Mar 2026 21:31:35 GMT</pubDate>
      <content:encoded>
        <![CDATA[<html><head></head><body><p><img src="/2026/03/08/I-let-OpenClaw-do-OP-for-me/1.png" alt=""><br>正如我在 <a href="/2026/03/04/Add-Chrome-browser-as-a-Browser-tool-for-OpenClaw/">为 OpneClaw 增加 Browser 工具</a> 中提到的那样：</p><blockquote><p>在整个的部署过程中，遇到了太多的坑，每遇到一个坑，我就让 Gemini 帮我解决。</p></blockquote><p>突然，我意识到：目前 LLMs 对于 Linux 系统的理解应该比人类要强得多，对 Linux 系统的各种命令操作也应该比人类要强得多。</p><p>既然如此，为什么不让 OpenClaw 帮我解决 Linux 系统的运维呢？恰好，我有一个 3 台机器的小集群，每天都要登录上去做一些手工操作，我不想再这样继续下去了。我想做出一些改变，于是，我决定让 OpenClaw 来当 OP，帮我管理集群。我要做的就是：通过 IM 下发指令。</p><span id="more"></span><h2 id="1-搭建集群">1. 搭建集群</h2><p>说干就干，LFG，🦞。</p><p>于是，我又找了一台机器作为 Manager Node，并且在 Manager Node 安装了 OpenClaw。然后，我为我的 IM 配置了 OpenClaw 的 Webhook，这样，我就可以通过 IM 发送指令给 OpenClaw。最后，通过 <code>ssh-copy-id</code> 命令，在 Manager Node 上配置了 Worker Node 的免密登录。</p><p>于是，我的 🦞 看起来是如下的样子：</p><p><img src="/2026/03/08/I-let-OpenClaw-do-OP-for-me/arch_machine.png" alt=""></p><p>现在，我们可以让 🦞 帮我管理集群了，比如我要查看 <code>Node-1</code> 机器的 CPU 负载信息，🦞 就会在 <code>Manager Node</code> 上执行如下的命令，并返回结果。</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssh user@Node-1 <span class="string">"uptime"</span></span><br></pre></td></tr></tbody></table></figure><h2 id="2-告诉-🦞-如何管理集群">2. 告诉 🦞 如何管理集群</h2><p>我希望 🦞 可以知道它现在可以管理的机器列表，并且当有新的机器节点加入集群时，🦞 也能够知道如何处理新增加的机器。于是，我通过 IM 把这些知识以聊天的方式告诉了它：</p><figure class="highlight markdown"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">悟空，你现在可以在受信任的机器上通过给定账号执行命令，从而实现集群管理的目的。</span><br><span class="line">但是现在有个问题：别人不知道究竟有那些收信任的机器可以管理，</span><br><span class="line">所以你需要通过配置的方式把受信任的机器和账号以username:host的格式存储起来。</span><br><span class="line">当别人问你类似：你可以管理哪些机器的时候，你就从这个配置里告诉他就可以。</span><br><span class="line">当别人让你在某个 host 上执行命令时，如果不提供 username，那么就默认用配置中的配置的用户执行。</span><br><span class="line">把这些内容存入你的记忆中，永远记下来。</span><br></pre></td></tr></tbody></table></figure><p><img src="/2026/03/08/I-let-OpenClaw-do-OP-for-me/machine_rules.png" alt=""></p><p>看起来，一起都非常的完美。现在，🦞 已经知道如何管理集群了，同时它也确实具备了管理集群所需要的所有的能力了。</p><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">不用担心 OpenClaw 的能力</p><p>不用担心，🦞 是否能够听懂我们的 <strong>自然语言</strong> 指令 ，或者担心它无法翻译成合适的 Linux 命令。实际上，这种担心是多余的。</p><p>如今的 LLMs 已经不是 2023 年的 LLMs 了，它们已经可以理解自然语言，它们已经对 Linux 系统了如指掌了。反正每次遇到系统的坑，我都会让 Gemini 帮我解决，我完全相信 🦞 的能力。</p><p>如果我们也不知道，而恰巧 LLMs 也不清楚，那么我们就亲自弄明白然后告诉 🦞 吧，相信它记得会比我们更牢固。</p></div><h2 id="3-测试-🦞-的集群管理能力">3. 测试 🦞 的集群管理能力</h2><p>现在，我想知道我的集群中的机器的负载情况。之前，我必须登录到每台机器上，然后执行 <code>uptime</code> 命令。好麻烦。即便是在中控机执行命令，我也得手动写命令。</p><p><strong>现在，我不需要登机器、甚至不需要登中控机，我只需要打开 IM 并告诉 🦞：</strong></p><blockquote><p>查看下这三台机器的cpu负载，并进行对比分析。务必简要，可以用表格来呈现。</p></blockquote><p>就可以了，剩下的所有的事情，🦞 都会自动完成。</p><p><img src="/2026/03/08/I-let-OpenClaw-do-OP-for-me/openclaw_cmd.png" alt=""></p><h2 id="4-安全风险的思考">4. 安全风险的思考</h2><p>赋予 AI <code>root</code> 权限，是一件便利但是却极其危险的事情。就像 Elon Musk 的 <a href="https://x.com/elonmusk/status/2030814137811341589">推文</a> 中描述的那样：</p><blockquote><p>Giving people agentic AI be like …</p></blockquote><blockquote><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/elonmusk/status/2030814137811341589"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></blockquote><p>在实践中，当 OpenClaw 为我们提供便利的同时，我们必须严格控制安全风险，务必采用最小权限原则：<br>绝对不要直接让 OpenClaw 使用 <code>root</code> 权限，更高级别的权限，必须掌控在我们自己手中。创建一个专用的 <code>openclaw_ops</code> 用户，只赋予其执行特定运维命令的 <code>sudo</code> 权限。尤其是对于像 <code>rm -rf /</code> 这样的毁灭性命令，必须物理禁止。</p><p>好在是，我们总会有办法来控制 OpenClaw 的权限，只要我们想这样做。</p><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p><strong>我们不能因为担心风险而放弃使用 OpenClaw，而是应该想尽办法来控制风险。</strong></p></div><h2 id="5-理想的未来">5. 理想的未来</h2><p>3 台机器的集群虽小，但它却验证了一个宏大的架构路径。基于 GitOps 和 Agentic Memory 的理念，我们完全可以对未来做进一步的畅想：</p><ul><li><p><strong>分布式联邦记忆（Distributed Federated Memory）：</strong><br>在更大的团队中，可以有多个 OpenClaw 实例在不同的数据中心或业务集群中工作。它们可以通过 GitOps 架构，定期将自己的本地记忆 Push 到同一个中央 Git 仓库，并 Pull 其他 Agent 的新记忆。通过大模型的 <strong>语义融合（Semantic Fusion）</strong> 能力，实现整个组织经验的全局共享和异地演进。</p></li><li><p><strong>从 Passive Ops 到 Proactive Ops：</strong><br>未来的 OpenClaw 将不再是被动等待指令或告警，而是可以通过连接 Prometheus 等监控系统实时分析性能趋势。在磁盘空间即将耗尽前，它能主动根据沉淀的 SOP 执行无害日志清理；在业务高峰来临时，它能自动根据历史经验执行云端扩容操作……</p></li><li><p><strong>沉淀团队真正的灵魂：</strong><br>服务器硬件和模型肉体都是暂时的，唯有在一次次碰撞和纠错中沉淀下来的版本化知识，才是个人、团队、组织的灵魂。当新成员加入团队时，他们不再需要翻阅过时的文档，只需要坐在 OpenClaw 旁边，OpenClaw 会带着团队所有的历史经验，辅助他完成新的任务。</p></li></ul><p><img src="/2026/03/08/I-let-OpenClaw-do-OP-for-me/future.png" alt=""></p></body></html>]]>
      </content:encoded>
    </item>
    <item>
      <title>为 OpneClaw 增加 Browser 工具</title>
      <link>https://wangwei1237.github.io/2026/03/04/Add-Chrome-browser-as-a-Browser-tool-for-OpenClaw/</link>
      <description>
        <![CDATA[<html><head></head><body><p><img src="/2026/03/04/Add-Chrome-browser-as-a-Browser-tool-for-OpenClaw/1.png" alt=""></p>
<p>在 <a href="/2026/02/22/First-Experience-with-OpenClaw/">OpenClaw 初体验</a> 中，我在阿里云的 ECS 主机上部署了 OpenClaw，并用它做了些初步的尝试。有时候，可能想浏览某些网页，但是这些网页在手机上的浏览体验又不好，所以此时就可以通过 IM 软件让 OpenClaw 调用 Chrome 浏览器来帮我们打开页面并截图。</p>
<p>我想每天上班的时候扫一眼 Github Trending 的内容（我在手机上没法直接访问这个页面，当然也有方法，但是我嫌麻烦），这个时候就可以通过 OpenClaw 调用 Chrome 浏览器来打开这个页面并截图发给我了。</p></body></html>]]>
      </description>
      <author>Wang Wei</author>
      <category domain="https://wangwei1237.github.io/categories/LLM/">LLM</category>
      <category domain="https://wangwei1237.github.io/tags/OpenClaw/">OpenClaw</category>
      <category domain="https://wangwei1237.github.io/tags/Chrome/">Chrome</category>
      <category domain="https://wangwei1237.github.io/tags/Browser/">Browser</category>
      <pubDate>Wed, 04 Mar 2026 20:40:37 GMT</pubDate>
      <content:encoded>
        <![CDATA[<html><head></head><body><p><img src="/2026/03/04/Add-Chrome-browser-as-a-Browser-tool-for-OpenClaw/1.png" alt=""></p><p>在 <a href="/2026/02/22/First-Experience-with-OpenClaw/">OpenClaw 初体验</a> 中，我在阿里云的 ECS 主机上部署了 OpenClaw，并用它做了些初步的尝试。有时候，可能想浏览某些网页，但是这些网页在手机上的浏览体验又不好，所以此时就可以通过 IM 软件让 OpenClaw 调用 Chrome 浏览器来帮我们打开页面并截图。</p><p>我想每天上班的时候扫一眼 Github Trending 的内容（我在手机上没法直接访问这个页面，当然也有方法，但是我嫌麻烦），这个时候就可以通过 OpenClaw 调用 Chrome 浏览器来打开这个页面并截图发给我了。</p><span id="more"></span><p><img src="/2026/03/04/Add-Chrome-browser-as-a-Browser-tool-for-OpenClaw/github_trends.png" alt=""></p><p>在纯净的 Ubuntu 22.04 云主机上以 <code>root</code> 用户安装 Chrome 浏览器并配置 OpenClaw 时，我遇到了不少的坑。本文将详细记录在云主机上从零配置 OpenClaw + Chrome 环境的全过程，避免大家遇到那些让人抓狂的“权限坑”。</p><h2 id="1-chrome-安装避坑">1. Chrome 安装避坑</h2><p>我在 Ubuntu 22.04 上安装 Chrome 时最开始的方式是执行：<code>apt install chromium-browser</code>。<strong>务必立刻停止这个操作！</strong></p><p><strong>deb to snap transition</strong>：</p><ul><li><p>从 Ubuntu 19.10 开始，Canonical 将 chromium-browser 做成了过渡包（transitional package），该包并不包含真正的 DEB 程序，仅包含包装脚本、桌面文件和依赖声明，并明确依赖 snapd 和 chromium Snap 包。<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p></li><li><p>因此 <code>apt install chromium-browser</code> 命令安装的 Chrome 实际上是 <a href="https://snapcraft.io/">Snap</a> 版本的 Chrome。若系统未安装 snapd，该过渡包会先通过 apt 安装 snapd，再从 Snap Store 拉取 Chromium。</p></li></ul><p>而 Snap 拥有极其严苛的 AppArmor 沙箱隔离<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>，其默认规则就是：禁止访问任何系统敏感目录和其他用户目录。<code>/root</code> 目录是超级用户目录，属于最高敏感路径，因此，AppArmor 会直接拦截对其目录下的文件访问请求<sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>：</p><ul><li>读：deny</li><li>写：deny</li><li>执行：deny</li><li>列出文件：deny</li></ul><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">安装官方原生 DEB 版 Google Chrome</p><ol><li><p>彻底卸载可能存在的 Snap 版 Chromium</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt remove --purge chromium-browser -y</span><br><span class="line"><span class="built_in">sudo</span> snap remove chromium</span><br></pre></td></tr></tbody></table></figure><p></p></li><li><p>下载并安装官方原生 Chrome</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 下载 Google Chrome 官方最新稳定版</span></span><br><span class="line">wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb</span><br><span class="line"></span><br><span class="line"><span class="comment"># 通过 apt 安装（自动解析并安装底层的图形/字体依赖）</span></span><br><span class="line"><span class="built_in">sudo</span> apt install ./google-chrome-stable_current_amd64.deb -y</span><br></pre></td></tr></tbody></table></figure><p></p></li><li><p>安装中文字体（防止截图变“豆腐块”）</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt install -y fonts-noto-cjk</span><br></pre></td></tr></tbody></table></figure><p></p></li></ol></div><h2 id="2-配置-openclaw">2. 配置 OpenClaw</h2><p>安装完 Chrome 浏览器后，必须在 OpenClaw 的网关配置中为 Browser 工具开放权限，并解决云主机 <code>root</code> 运行的核心安全冲突。</p><h3 id="2-1-开启-browser-工具权限">2.1 开启 browser 工具权限</h3><p>OpenClaw 从 3.2 版本开始对工具调用实施了更严格的权限控制，从而希望有效解决饱受诟病的安全问题。针对工具调用的安全控制能力，OpenClaw 3.2 版本增加了：精细化的工具访问控制、执行环境隔离、运行时行为约束等安全策略<sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup>。</p><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/wangwei1237/status/2028981937843650602"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><p>另外，根据 OpenClaw 官方文档的描述<sup class="footnote-ref"><a href="#fn5" id="fnref5">[5]</a></sup>：</p><blockquote><p>OpenClaw can run a dedicated Chrome/Brave/Edge/Chromium profile that the agent controls.<br>It is isolated from your personal browser and is managed through a small local control service inside the Gateway (loopback only).</p></blockquote><p>OpenClaw 支持在 <code>openclaw.json</code> 中通过 <code>tools.allow</code>、<code>tools.deny</code> 全局配置工具的黑白名单，并且黑名单优先级高于白名单，从而阻止没有权限的工具发送给模型，进而避免无权限的工具的执行。</p><p>因此，需要首先在全局配置文件 <code>~/.openclaw/openclaw.json</code> 中把 <code>browser</code> 工具加入白名单：</p><figure class="highlight json"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line">  <span class="attr">"agents"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">    <span class="attr">"defaults"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">      <span class="attr">"tools"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">        <span class="attr">"allow"</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="string">"exec"</span><span class="punctuation">,</span> <span class="string">"shell"</span><span class="punctuation">,</span> <span class="string">"browser"</span><span class="punctuation">,</span> <span class="string">"web_fetch"</span><span class="punctuation">]</span> </span><br><span class="line">      <span class="punctuation">}</span></span><br><span class="line">    <span class="punctuation">}</span></span><br><span class="line">  <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure><h3 id="2-2-关闭沙箱-no-sandbox-限制">2.2 关闭沙箱（No-Sandbox）限制</h3><p>沙箱是 Chrome 的核心安全机制。当 Chrome 打开一个网页时，它会把网页里的 HTML 渲染、JavaScript 执行等操作，全都关进一个“隔离的 Sandbox” 里运行。<sup class="footnote-ref"><a href="#fn6" id="fnref6">[6]</a></sup></p><p>而在 Linux 的世界中，<code>root</code> 是拥有绝对权限的超级用户，如果以 <code>root</code> 身份运行 Chrome，这就产生了一个巨大的安全悖论：</p><blockquote><p>沙箱的存在是为了限制权限，但 <code>root</code> 账号却偏偏拥有无限权限</p></blockquote><p>因此，在 Chrome 中，有一条硬性规定：检测到当前用户是 <code>root</code> 账号时，浏览器会直接退出，绝不在 <code>root</code> 下带着沙箱运行，没有意义。<sup class="footnote-ref"><a href="#fn7" id="fnref7">[7]</a></sup></p><blockquote><p>Running as root without --no-sandbox is not supported.</p></blockquote><p>但是对于 docker 容器来说，容器已经对安全性做了控制与隔离，因此在容器内以 <code>root</code> 用户运行 Chrome 是没有问题的。此时可以使用 <code>--no-sandbox</code> 参数来关闭沙箱，从而以 <code>root</code> 用户身份运行 Chrome。</p><p>同样的，当在 <code>root</code> 账号下用 OpenClaw 控制浏览器时，也必须在 OpenClaw 配置中显式关闭沙箱限制：</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">openclaw config <span class="built_in">set</span> browser.noSandbox <span class="literal">true</span></span><br></pre></td></tr></tbody></table></figure><h3 id="2-3-重启网关">2.3 重启网关</h3><p>配置完成后，重启 OpenClaw 网关服务：</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">openclaw gateway restart</span><br></pre></td></tr></tbody></table></figure><h2 id="3-snapshot-与-screenshot">3. snapshot 与 screenshot</h2><p>可以用如下的命令来验证浏览器工具是否可用：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">openclaw browser --browser-profile openclaw status</span><br><span class="line">openclaw browser --browser-profile openclaw start</span><br><span class="line">openclaw browser --browser-profile openclaw open https://www.baidu.com</span><br><span class="line">openclaw browser --browser-profile openclaw snapshot</span><br></pre></td></tr></tbody></table></figure><ul><li><p><strong>Snapshot 提取结构化文本</strong>：如果需要提取页面的可读内容，我们可以执行 <code>snapshot</code> 动作。这个操作会返回当前网页完整的 HTML 字符串、JSON 格式的 DOM 树结构，非常适合让 LLM 对其进行总结与分析。</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">openclaw browser --browser-profile openclaw snapshot</span><br></pre></td></tr></tbody></table></figure></li><li><p><strong>Screenshot 截取视觉画面</strong>：如果需要真实的网页截图，需要执行 <code>screenshot</code> 动作。该动作会在底层执行截屏，并返回一个包含媒体路径的图像块（image block + MEDIA:<path>）。执行命令后，控制台会返回图片在服务器上的绝对路径，我们可以通过 <code>cp</code> 命令将其拷贝并下载查看。</path></p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">openclaw browser --browser-profile openclaw screenshot</span><br></pre></td></tr></tbody></table></figure></li></ul><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">为什么作者中要加入 Gemini？</p><p>在整个的部署过程中，遇到了太多的坑，每遇到一个坑，我就让 Gemini 帮我解决。我为整个的安装过程专门新建了一个 Gemini Session。当 Gemini 帮我解决一个坑后，我让 Gemini 把整个对话的内容进行梳理和总结，并形成了这篇文章的初稿。可以说，Gemini 是这篇文章的幕后英雄，有必要在作者栏中加上 Gemini。</p></div><h2 id="参考文献">参考文献</h2><hr class="footnotes-sep"><section class="footnotes"><ol class="footnotes-list"><li id="fn1" class="footnote-item"><p><a href="https://ubuntu.com/blog/chromium-in-ubuntu-deb-to-snap-transition">Chromium in Ubuntu – deb to snap transition</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p></li><li id="fn2" class="footnote-item"><p><a href="https://snapcraft.io/docs/reference/system-architecture/">Snap System architecture</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p></li><li id="fn3" class="footnote-item"><p><a href="https://snapcraft.io/docs/explanation/security/snap-confinement/index.html">Snap confinement</a> <a href="#fnref3" class="footnote-backref">↩︎</a></p></li><li id="fn4" class="footnote-item"><p><a href="https://docs.openclaw.ai/tools">OpenClaw Tools</a> <a href="#fnref4" class="footnote-backref">↩︎</a></p></li><li id="fn5" class="footnote-item"><p><a href="https://docs.openclaw.ai/tools/browser">OpenClaw Browser Tool</a> <a href="#fnref5" class="footnote-backref">↩︎</a></p></li><li id="fn6" class="footnote-item"><p><a href="https://chromium.googlesource.com/chromium/src/+/HEAD/docs/design/sandbox.md">Chrome Sandbox</a> <a href="#fnref6" class="footnote-backref">↩︎</a></p></li><li id="fn7" class="footnote-item"><p><a href="https://issues.chromium.org/issues/40480798">Exit instead of crashing when running as root without --no-sandbox</a> <a href="#fnref7" class="footnote-backref">↩︎</a></p></li></ol></section></body></html>]]>
      </content:encoded>
    </item>
    <item>
      <title>OpenClaw 初体验</title>
      <link>https://wangwei1237.github.io/2026/02/22/First-Experience-with-OpenClaw/</link>
      <description>
        <![CDATA[<html><head></head><body><p><img src="/2026/02/22/First-Experience-with-OpenClaw/1.jpg" alt=""></p>
<p>2026 年初，除了基础大模型争相斗艳之外，在技术圈，一个名叫 OpenClaw 的 个人 AI 助手火出了天际，引爆了新一轮的 Agent 热潮。OpenClaw 的核心理念是让 AI 直接接管系统的读写与执行权限，并通过 Telegram、Discord 或 WhatsApp 等 IM 软件接收自然语言指令，然后再服务器上自动执行代码编写、文件管理、收发邮件等任务。通过 IM 软件下达任务，OpenClaw 会自己分析任务，然后自动执行任务，并返回执行结果。这才是我们一直在苦苦追求的 AI 个人助理。</p></body></html>]]>
      </description>
      <author>Wang Wei</author>
      <category domain="https://wangwei1237.github.io/categories/LLM/">LLM</category>
      <category domain="https://wangwei1237.github.io/tags/OpenClaw/">OpenClaw</category>
      <category domain="https://wangwei1237.github.io/tags/Clawdbot/">Clawdbot</category>
      <pubDate>Sun, 22 Feb 2026 10:06:08 GMT</pubDate>
      <content:encoded>
        <![CDATA[<html><head></head><body><p><img src="/2026/02/22/First-Experience-with-OpenClaw/1.jpg" alt=""></p><p>2026 年初，除了基础大模型争相斗艳之外，在技术圈，一个名叫 OpenClaw 的 个人 AI 助手火出了天际，引爆了新一轮的 Agent 热潮。OpenClaw 的核心理念是让 AI 直接接管系统的读写与执行权限，并通过 Telegram、Discord 或 WhatsApp 等 IM 软件接收自然语言指令，然后再服务器上自动执行代码编写、文件管理、收发邮件等任务。通过 IM 软件下达任务，OpenClaw 会自己分析任务，然后自动执行任务，并返回执行结果。这才是我们一直在苦苦追求的 AI 个人助理。</p><span id="more"></span><h2 id="1-什么是-openclaw">1. 什么是 OpenClaw</h2><p>OpenClaw 是奥地利程序员 <a href="https://steipete.me/">Peter Steinberger</a> 在 2025 年 11 月发布的开源项目。项目的最初命名为 Clawdbot，是一个能连通各种 API 和本地 Shell 的轻量级对话机器人原型。2026 年 1 月，Clawbot 在 GitHub 上突然爆火出圈，同时也顺便带火了 Mac mini，甚至有用户一口气买了 40 台 Mac mini，全部用来运行 Clawdbot。</p><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/mikecantmiss/status/2015450752378908835"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><p>就在 Clawdbot 爆发式增长的时候，Peter 收到了 Anthropic 的律师函：因为 Clawdbot 与 Anthropic 的大语言模型 Claude 太像，因此涉嫌商标侵权。2026 年 1 月 27 日，Peter 在 Discord 频道里召集社区成员进行头脑风暴，并最终为项目改名为 Moltbot。</p><p><img src="/2026/02/22/First-Experience-with-OpenClaw/clawdbot.png" alt=""></p><p>3 天后，Peter 觉得 Moltbot 读音感觉像是嘴里含了块石头，同时为了彻底规避未来的版权争议并彰显开源和自由的属性，再次为项目更名为 OpenClaw 并沿用至今。改名后的 OpenClaw 像火箭一样起飞：</p><ul><li>1 月底：2K GitHub stars</li><li>2 月初：突破 100K  stars</li><li>2 月中旬：突破 180K  stars</li><li>2 月底：突破 200K stars</li></ul><p><img src="/2026/02/22/First-Experience-with-OpenClaw/star-history-2026222.png" alt=""></p><p>2 月 4 日，Founder Park 搞了一场闭门活动，聚集了 130 多位 AI 创业者，来自 AI 游戏、AI Coding、Agent Memory、AI 硬件、AI 语音等不同赛道，分享了他们在上手用 Clawdbot 的感受和思考。<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p><blockquote><p>Clawdbot 带来了一些和聊天不一样的「增量」。它的本质，是为你配了一台「云电脑」。第一次可以真正地指挥一个 AI 去增、删、改、查文件。这让 AI 从一个信息工具，变成了一个执行工具。</p></blockquote><p>2 月 14 日，Peter 宣布加入 OpenAI，领导“下一代个人代理”的开发，OpenClaw 则转由开源基金会维护，确保项目永远自由和独立。<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup></p><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/steipete/status/2023154018714100102"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><p>直到今天，OpenClaw 的热度依然不减：在 Google Trends 上，搜索趋势热度依然在 60 以上；微信公众号上关于 OpenClaw 的文章也多如牛毛。<br><img src="/2026/02/22/First-Experience-with-OpenClaw/google_trends.png" alt=""></p><p>即便如此，我想，依然有非常多的人和我一样，对 OpenClaw 的了解和认知还主要在别人的讨论中，依然没有亲自上手、真正体验。纸上得来终觉浅，绝知此事要躬行。</p><p>于是，在春节前的几天，我花了一些时间，亲自上手部署了 OpenClaw，并记录下了整个体验过程。</p><h2 id="2-为什么选择-ecs">2. 为什么选择 ECS</h2><ol><li>首先，我个人并不喜欢云厂商提供的一键部署服务，相反，我更喜欢在一台干净的服务器上，从零开始搭建自己的环境。</li><li>其次，在 <a href="/2025/06/14/Deploy-vui-audio-model/">在阿里云上部署 Vui 语音模型</a> 这篇文章中，我介绍了我在阿里云的 PAI-DSW 上部署 Vui 模型的经验，那时我就被阿里云的使用体验所折服。</li><li>然后，对于新用户而言，阿里云提供了 <a href="https://www.aliyun.com/solution/tech-solution/clawdbot?spm=5176.29619931.nav-v2-dropdown-menu-2.d_main_0_0.45a610d7nAKirZ&amp;scm=20140722.M_10963134.P_102.MO_4509-ID_10950062-MID_10950062-CID_10950062-ST_14271-V_1">价值 300 元的免费 ECS 额度</a>。</li><li>最后，阿里旗下的钉钉机器人已经有 Channel 插件支持通过钉钉和 OpenClaw 交互。</li></ol><p>因此，当我决定要亲自体验一下 OpenClaw 的时候，我选择了阿里云的 ECS。</p><h2 id="3-在-ecs-上部署-openclaw">3. 在 ECS 上部署 OpenClaw</h2><h3 id="3-1-申请-ecs-主机">3.1 申请 ECS 主机</h3><p>根据自己的需求选择合适的 ECS 实例配置，我选择了 2 核 4G 的 Ubuntu 22.04 作为 ECS 实例，在创建 ECS 实例时，阿里云会提供对应的公网 IP，这极大方便了后续的部署和访问。</p><p><img src="/2026/02/22/First-Experience-with-OpenClaw/ECS.png" alt=""></p><p>点击 <code>远程链接</code> 按钮，可以配置登录方式，我选择使用 SSH 密钥对登录并设置了登录的 <code>root</code> 密码。通过提供的公网 IP 和设置的密码，就可以通过 SSH 登录到 ECS 实例了。</p><p><img src="/2026/02/22/First-Experience-with-OpenClaw/ECS_login.png" alt=""></p><h3 id="3-2-配置-ecs-的安全组">3.2 配置 ECS 的安全组</h3><p>OpenClaw 运行依赖两个核心端口——18789 和 80：</p><ul><li>18789 端口为网关通信与控制界面访问端口</li><li>80 端口用于部分 IM 平台的回调通信</li></ul><p>因此，为了保障 OpenClaw 的可用性，ECS 主机启动后，需要在 <code>网络与安全组</code> 中对这两个端口进行配置，避免因端口拦截导致服务不可用。</p><p><img src="/2026/02/22/First-Experience-with-OpenClaw/ecs_port.png" alt=""></p><h3 id="3-3-安装-node-环境">3.3 安装 Node 环境</h3><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">snap install node --classic</span><br><span class="line">npm config <span class="built_in">set</span> registry https://registry.npmmirror.com</span><br></pre></td></tr></tbody></table></figure><h3 id="3-4-安装-openclaw">3.4 安装 OpenClaw</h3><p>根据 <a href="https://github.com/openclaw/openclaw">OpenClaw 官方文档</a> 安装 OpenClaw：</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">npm install -g openclaw@latest</span><br><span class="line">openclaw onboard --install-daemon</span><br></pre></td></tr></tbody></table></figure><p><code>向导程序</code> 会安装 OpenClaw 的网关守护进程（launchd/systemd 用户服务），并使其保持运行状态。在安装过程中，<code>向导程序</code> 会让我们配置各种参数：例如 IM 平台、所用的大模型、安装的插件……根据自己的需求进行配置即可。</p><p>安装完成之后，可以通过 <code>openclaw status</code> 查看 OpenClaw 的运行状态。</p><p><img src="/2026/02/22/First-Experience-with-OpenClaw/status.png" alt=""></p><h3 id="3-5-配置-ssh-本地端口转发">3.5 配置 SSH 本地端口转发</h3><p>为了方便本地访问 OpenClaw 的控制界面，可以通过 SSH 的本地端口转发功能，将 ECS 实例的 18789 端口转发到本地。</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssh -L 18789:localhost127.0.0.1:18789 root@&lt;ECS公网IP&gt;</span><br></pre></td></tr></tbody></table></figure><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">为什么要配置端口转发</p><p>由于 OpenClaw 的控制界面是以 18789 端口作为访问端口，而该端口并非标准的 HTTP 协议端口，而是一个普通的 TCP 端口。因此，如果直接在本地浏览器访问：http://&lt;ECS公网IP&gt;:18789，无法访问 ECS 主机中的 OpenClaw 的控制界面。</p></div><p><img src="/2026/02/22/First-Experience-with-OpenClaw/openclaw_ui.png" alt=""></p><h3 id="3-6-配置大模型">3.6 配置大模型</h3><p>在阿里云的百炼平台申请对应模型的 <code>API-KEY</code>，在 openclaw.json 中增加大模型配置。</p><p>1.在 <code>models</code> 字段中增加大模型配置：</p>  <figure class="highlight json"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">"models"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">  <span class="attr">"providers"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">    <span class="attr">"alibaba-cloud"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">      <span class="attr">"baseUrl"</span><span class="punctuation">:</span> <span class="string">"https://dashscope.aliyuncs.com/compatible-mode/v1"</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">"apiKey"</span><span class="punctuation">:</span> <span class="string">"&lt;your-api-key&gt;"</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">"api"</span><span class="punctuation">:</span> <span class="string">"openai-completions"</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">"models"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">        <span class="punctuation">{</span></span><br><span class="line">          <span class="attr">"id"</span><span class="punctuation">:</span> <span class="string">"qwen3-coder-plus"</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"Qwen Coder"</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">"reasoning"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">"input"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">            <span class="string">"text"</span></span><br><span class="line">          <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">"cost"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">            <span class="attr">"input"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"output"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"cacheRead"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"cacheWrite"</span><span class="punctuation">:</span> <span class="number">0</span></span><br><span class="line">          <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">"contextWindow"</span><span class="punctuation">:</span> <span class="number">128000</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">"maxTokens"</span><span class="punctuation">:</span> <span class="number">8192</span></span><br><span class="line">        <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line">        <span class="punctuation">{</span></span><br><span class="line">          <span class="attr">"id"</span><span class="punctuation">:</span> <span class="string">"qwen3-vl-plus"</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"Qwen Vision"</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">"reasoning"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">"input"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">            <span class="string">"text"</span><span class="punctuation">,</span></span><br><span class="line">            <span class="string">"image"</span></span><br><span class="line">          <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">"cost"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">            <span class="attr">"input"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"output"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"cacheRead"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">"cacheWrite"</span><span class="punctuation">:</span> <span class="number">0</span></span><br><span class="line">          <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">"contextWindow"</span><span class="punctuation">:</span> <span class="number">128000</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">"maxTokens"</span><span class="punctuation">:</span> <span class="number">8192</span></span><br><span class="line">        <span class="punctuation">}</span></span><br><span class="line">      <span class="punctuation">]</span></span><br><span class="line">    <span class="punctuation">}</span></span><br><span class="line">  <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure><p>2.在 <code>agents</code> 字段中增加大模型配置：</p>  <figure class="highlight json"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">"agents"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">  <span class="attr">"defaults"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">    <span class="attr">"model"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">      <span class="attr">"primary"</span><span class="punctuation">:</span> <span class="string">"alibaba-cloud/qwen3-coder-plus"</span></span><br><span class="line">    <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"models"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">      <span class="attr">"alibaba-cloud/qwen3-coder-plus"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">        <span class="attr">"alias"</span><span class="punctuation">:</span> <span class="string">"qwen"</span></span><br><span class="line">      <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">"alibaba-cloud/qwen3-vl-plus"</span><span class="punctuation">:</span> <span class="punctuation">{</span><span class="punctuation">}</span></span><br><span class="line">    <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line">  <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure><h3 id="3-7-配置钉钉机器人">3.7 配置钉钉机器人</h3><p>根据 <a href="https://developer.aliyun.com/article/1710314">阿里云 ECS 云服务器部署OpenClaw（Clawdbot）详细步骤流程</a> 中的 <code>钉钉对接流程</code> 部分创建钉钉机器人并进行配置。</p><p>根据 <a href="https://github.com/soimy/openclaw-channel-dingtalk">openclaw-channel-dingtalk</a> 中的说明安装 DingTalk Channel 插件，并重启 OpenClaw 服务。</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">openclaw gateway restart</span><br></pre></td></tr></tbody></table></figure><p>重启 OpenClaw 服务后，在 OpenClaw 控制界面中的 <code>Control</code>→<code>Channels</code>，用创建的钉钉机器人信息对 <code>DingTalk</code> 进行配置。</p><p><img src="/2026/02/22/First-Experience-with-OpenClaw/dingtalk.png" alt=""></p><h3 id="3-8-测试">3.8 测试</h3><p>把钉钉机器人添加到钉钉群中，并在钉钉群中发送指令，测试 OpenClaw 的功能。如果一切正常，我们现在就可以利用钉钉机器人通过 OpenClaw 来控制这台 ECS 主机啦。</p><p><img src="/2026/02/22/First-Experience-with-OpenClaw/dingtalk_test1.png" alt=""></p><h2 id="4-更多的应用场景">4. 更多的应用场景</h2><p>除了像如上的获取系统负载信息的简单场景外，我尝试了一些更复杂的应用场景，比如：读写文件、修复代码 BUG、编译复杂软件等。</p><h3 id="4-1-读写文件">4.1 读写文件</h3><p>Linux 的设计哲学就是“一切皆文件”，因此，如果 OpenClaw 可以完全接管系统的文件管理，这将是一件非常有趣的事情。我通过钉钉的机器人助手，用自然语言描述文件操作，OpenClaw 可以非常丝滑的完成对应的文件操作。整体的体验真是棒极了。</p><div class="bili_video"><div style="position: relative; width: 100%; height: 0; padding-bottom: 75%;"><iframe src="https://player.bilibili.com/player.html?aid=116066793619828&amp;page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true" style="position: absolute; width: 100%; height: 100%; left: 0; top: 0;"> </iframe></div></div><h3 id="4-2-修复代码-bug">4.2 修复代码 BUG</h3><p>既然 OpenClaw 已经接管了文件管理，那么，如果配合上适当的 LLMs，是否意味着 OpenClaw 自动修复代码 BUG 也不在话下？还要什么 IDE？理解代码、分析代码、修复代码、编译运行…… OpenClaw 都可以轻松完成。</p><p>先编写一个简单的 C 程序，故意写几个 BUG，然后通过钉钉机器人助手，让 OpenClaw 修复这些 BUG。</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span> {</span><br><span class="line">    <span class="type">int</span> a[<span class="number">5</span>] = {<span class="number">-1</span>, <span class="number">-1</span>, <span class="number">-1</span>, <span class="number">-1</span>, <span class="number">-1</span>};</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">6</span>; i++) {</span><br><span class="line">        a[i] = i;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"%d \n"</span>, a[<span class="number">10</span>]);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"Finish~\n"</span>);</span><br><span class="line">    *(<span class="type">int</span> *<span class="number">0</span>) = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><div class="bili_video"><div style="position: relative; width: 100%; height: 0; padding-bottom: 75%;"><iframe src="https://player.bilibili.com/player.html?aid=116066793621178&amp;page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true" style="position: absolute; width: 100%; height: 100%; left: 0; top: 0;"> </iframe></div></div><p>从文件系统的文件变化也能看出，OpenClaw 不断修复了代码中的 BUG，还自动编译并运行了程序。</p><p><img src="/2026/02/22/First-Experience-with-OpenClaw/bugfix_1.png" alt=""></p><h3 id="4-3-编译复杂项目">4.3 编译复杂项目</h3><p>工作中，我们经常需要编译依赖较为复杂的项目，而这种项目足以令一名新手程序员望而却步和头疼。恰好，我在 GitHub 上维护了一个开源统计学外文书籍的翻译项目（<a href="https://github.com/wangwei1237/introduction_to_probability_and_statistics">Introduction to Probability and Statistics for Engineers and Scientists</a>），该项目通过 <a href="https://quarto.org/">Quarto</a> 工具来构建书籍（👉🏻👉🏻👉🏻<a href="http://wangwei1237.github.io/introduction_to_probability_and_statistics/">点击查看效果📚</a>）。整个书籍的编译过程涉及到 Quarto 工具的安装、R 语言环境与各种开发包的安装、各种 Quarto 扩展的安装等一系列的编译依赖。</p><p>我用自然语言给钉钉机器人发送了任务：</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">@TestOpenClaw 请拉取https://github.com/wangwei1237/introduction_to_probability_and_statistics </span><br><span class="line">代码库到/root/17/book 目录，并根据check.yml文件的内容安装quarto环境并编译成html书籍。</span><br></pre></td></tr></tbody></table></figure><p><img src="/2026/02/22/First-Experience-with-OpenClaw/book_task.png" alt=""></p><p>编译的相关步骤和依赖知识都位于 <code>check.yml</code> 中，并且这还是一个多轮的长程任务，整个过程，我除了发送：继续、好的等信息确认指令以外，基本上没有在机器上做任何手工操作。在经过 1 个多小时的反复确认与多轮沟通之后，OpenClaw 完成了编译任务。</p><div class="bili_video"><div style="position: relative; width: 100%; height: 0; padding-bottom: 75%;"><iframe src="https://player.bilibili.com/player.html?aid=116066793818350&amp;page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true" style="position: absolute; width: 100%; height: 100%; left: 0; top: 0;"> </iframe></div></div><p>最后，OpenClaw 还对整个编译过程中的操作进行了记录与总结，整体体验下来发现，OpenClaw 的记忆系统也相当不错。</p><p><img src="/2026/02/22/First-Experience-with-OpenClaw/book_2.png" alt=""></p><h3 id="4-4-从源码编译-ffmpeg">4.4 从源码编译 FFmpeg</h3><p>在音视频领域，FFmpeg 是神一样存在的复杂系统。在一台干净的机器上从零编译 FFmpeg 是一个高度考验 Linux 系统管理、依赖关系梳理以及构建工具链（Make/CMake/Meson）功底的过程。整个的编译过程虽然繁琐，但是只要按照正确的顺序去解决编译依赖，就能顺利通关。我时常想起当年在手动编译 FFmpeg 时那种“柳暗花明又一村”的烦恼与喜悦。</p><p>这次，我尝试让 OpenClaw 从零编译 FFmpeg，整个过程依然非常丝滑。除了信息确认指令之外，我几乎没有做任何手工操作。令人惊喜的是，OpenClaw 还自己解决了动态链接库路径的问题，并成功运行了 FFmpeg。</p><div class="bili_video"><div style="position: relative; width: 100%; height: 0; padding-bottom: 75%;"><iframe src="https://player.bilibili.com/player.html?aid=116067783412862&amp;page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true" style="position: absolute; width: 100%; height: 100%; left: 0; top: 0;"> </iframe></div></div><h2 id="5-总结">5. 总结</h2><p>我的 OpenClaw 并没有采用能力强大的模型，而是选择了 <code>qwen3-coder-plus</code>。即便如此，OpenClaw 的表现依然令我惊叹。对于 Token 消耗量来说，由于我尝试的这些场景均涉及到非常多的本地文件和命令操作，因此输入 Token 量比较大，而输出 Token 量这相对较少。</p><p><img src="/2026/02/22/First-Experience-with-OpenClaw/openclaw_tokens.png" alt=""></p><p>OpenClaw 的强大能力确实将 AI 推向了新的范式与场景，它的出现，让我们看到了 AI 助手在复杂场景下的无限潜力与可能性。这也难怪有用户在社区中发帖称：</p><blockquote><p>this week on twitter, i’ve read:<br>saas is dead<br>UI is dead<br>programming is dead<br>what else is dead?</p></blockquote><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/siddharthkp/status/2024844345564684601"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><p>恰好，刚看完春节贺岁档《飞驰人生3》，比赛最后的冲刺阶段，张弛在四驱分配系统失效、发动机电脑损坏、混合动力系统损坏的情况下，利用传统的手动机械四驱动力分配装置，依靠赛车手的感觉与经验，完美的控制了赛车，赢得了最后的胜利。这一点，我感触很大。</p><p>叶经理说：“<em><strong>我们现在完全没有数据，现在所有的一切，都要靠你自己去感觉</strong></em>”。</p><p>这让我对最近持续爆火的 LLM 有了新的思考：确实，AI 很强大，强大到在大多数时候可以完美的替代人类。<strong>但是，人类那 1% 的想象才是整个创造力的星星之火与动力之源。当没有数据的时候，当 AI 失效的时候，人是否还能够突破极限、力挽狂澜？</strong></p><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/wangwei1237/status/2025818908402626757"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><h2 id="参考文献">参考文献</h2><hr class="footnotes-sep"><section class="footnotes"><ol class="footnotes-list"><li id="fn1" class="footnote-item"><p><a href="https://mp.weixin.qq.com/s/9MKq3jRNsorCVnA6vWQIDg">闭门探讨：130位AI创业者，对Clawdbot和下一代AI产品的39条思考</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p></li><li id="fn2" class="footnote-item"><p><a href="https://steipete.me/posts/2026/openclaw">OpenClaw, OpenAI and the future</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p></li></ol></section></body></html>]]>
      </content:encoded>
    </item>
    <item>
      <title>如何提升大模型评测数据集的质量？</title>
      <link>https://wangwei1237.github.io/2026/02/06/How-to-Improve-the-Dataset-Quality-for-LLM-Evals/</link>
      <description>
        <![CDATA[<html><head></head><body><p><img src="/2026/02/06/How-to-Improve-the-Dataset-Quality-for-LLM-Evals/dsquality.jpeg" alt=""></p>
<p>在 <a href="/2025/03/29/Seven-Principles-for-Large-Model-Evaluation/">大模型评测 7 条原则</a> 中，我们把大模型评测过程中遇到的影响结果置信度的问题进行了总结。在 2025 年，这些实践总结也确实帮助我们提升了评测的质量。但是，在 2025 年末的时候，随着模型 SOTA 的不断突破（例如： <em>Nano Banana Pro</em>、<em>Seedream-4.5</em>、……），我们慢慢发现：我们的评测区分度存在不断下降的趋势，这导致我们很难通过评测来区分不同模型之间的性能差异。</p>
<p>然而，实际的情况并非我们的结果所呈现的那样，尤其在图片生成、视频生成等场景，不同的模型之间还是存在区分度的。在 Arena 平台的图生图对抗榜单显示：模型之间确实存在比较明显的差异。</p></body></html>]]>
      </description>
      <author>Wang Wei</author>
      <category domain="https://wangwei1237.github.io/categories/LLM/">LLM</category>
      <category domain="https://wangwei1237.github.io/tags/%E6%95%B0%E6%8D%AE%E9%9B%86%E8%B4%A8%E9%87%8F/">数据集质量</category>
      <pubDate>Fri, 06 Feb 2026 16:41:18 GMT</pubDate>
      <content:encoded>
        <![CDATA[<html><head></head><body><p><img src="/2026/02/06/How-to-Improve-the-Dataset-Quality-for-LLM-Evals/dsquality.jpeg" alt=""></p><p>在 <a href="/2025/03/29/Seven-Principles-for-Large-Model-Evaluation/">大模型评测 7 条原则</a> 中，我们把大模型评测过程中遇到的影响结果置信度的问题进行了总结。在 2025 年，这些实践总结也确实帮助我们提升了评测的质量。但是，在 2025 年末的时候，随着模型 SOTA 的不断突破（例如： <em>Nano Banana Pro</em>、<em>Seedream-4.5</em>、……），我们慢慢发现：我们的评测区分度存在不断下降的趋势，这导致我们很难通过评测来区分不同模型之间的性能差异。</p><p>然而，实际的情况并非我们的结果所呈现的那样，尤其在图片生成、视频生成等场景，不同的模型之间还是存在区分度的。在 Arena 平台的图生图对抗榜单显示：模型之间确实存在比较明显的差异。</p><span id="more"></span><p><img src="/2026/02/06/How-to-Improve-the-Dataset-Quality-for-LLM-Evals/i2i_arena_rank.png" alt=""></p><p><strong>当评测的精度无法有效区分模型之间的差异时，评测工作本身也就失去了意义。</strong> 于是，接下来的一段时间内，我们开始思考：<strong>如何提升评测精度这一问题？</strong></p><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition warning"><p class="admonition-title">注意事项</p><p>本文中的例子多会引用 𝕏 平台上的推文，因此如果您可以访问 𝕏，那么您将获得更好的阅读体验。</p></div><h2 id="评测精度的本质">评测精度的本质</h2><p>正如下图所示，大模型评测体系一般由如下五个部分构成：评测数据集、评测方法、打分与排序策略、评测指标、评测场景。</p><p><img src="/2026/02/06/How-to-Improve-the-Dataset-Quality-for-LLM-Evals/Evals_system.jpeg" alt=""></p><p>要系统化的研究评测精度，必须像拆解精密仪器的零件一样自顶向下的拆解评测系统的构件，并进行分析。</p><h3 id="评测数据集">评测数据集</h3><p>评测数据集是评测的基石，决定评测系统精度的“物理上限”。</p><ul><li>如果数据集中充满歧义问题或错误答案，评测系统就会把模型的“正确推理”误判为错误，引入巨大的评估噪音。</li><li>如果模型在预训练阶段已经记住了评测数据集中的数据，那么评测精度将直接归零。因此，在评测过程中，必须动态更新、或者使用非公开的私有数据集来保持评测精度。</li><li>数据集难度梯度必须有合理的分布。如果数据集太简单，所有模型都得高分（天花板效应），评测精度就无法区分前沿模型的微小能力差。反之，如果数据集太难，则会导致地板效应，评测精度也无法区分模型的优势区。</li></ul><p>在 <a href="/2025/03/29/Seven-Principles-for-Large-Model-Evaluation/">大模型评测 7 条原则</a> 中，我们已经解决了评测数据集的前 2 个问题。所以在评测数据集上，我们还需要解决数据集的难度梯度分布问题的问题，从而提升评测数据集的质量，进而提升评测精度。</p><h3 id="评测方法">评测方法</h3><p>评测方法是决定评测精度的“观测探针”。评测方法定义了我们如何向模型提问以及如何捕获模型的表现。在 <a href="/2026/01/25/Study-Anthropic-s-agent-evaluation-practices-thoroughly/">深入研究 Anthropic 的 Agent 评估实践</a> 中，我们深入研究了 Anthropic 的 Agent 评测方法，其中也提到了不同评测方法之间的特点与差异。</p><p><img src="/2026/02/06/How-to-Improve-the-Dataset-Quality-for-LLM-Evals/evals_methods.png" alt=""></p><h3 id="打分与排序策略">打分与排序策略</h3><p>打分与排序策略决定评测精度的“统计显著性”。当我们拿到了一堆原始分数或胜负关系后，如何将它们聚合为最终的榜单，直接关系到结果的置信区间。我们发现，在模型盲测中，经常会出现“A 胜 B，B 胜 C，C 胜 A”的情况。简单的算术平均运算无法处理这种动态博弈。采用类似 LMArena 的 Bradley-Terry 模型或 Elo 积分系统，能够将离散的胜负关系平滑地映射为连续的能力值以及对应的置信区间。置信区间能明确告诉我们：两个模型相差 10 分，到底是因为真正的能力差距，还是仅仅因为采样带来的噪音波动。</p><h3 id="评测指标">评测指标</h3><p>评测指标决定评测精度的“刻度粒度”，是将模型的表现量化为数字的函数。评测指标越粗糙，丢失的信息就越多。例如，不仅评测一段代码是否 Pass，还要引入圈复杂度、运行耗时、安全性等细粒度指标。这种多维度的切割，使得评测从“一维的标量”变成了“高维的向量”，极大提升了对模型能力的刻画精度。随着评测指标的增加，虽然信息保留的越多、结果的可解释性越强，但是“高维向量”带来的“维灾”也导致了打分区分度不断降低的问题。</p><p><img src="/2026/02/06/How-to-Improve-the-Dataset-Quality-for-LLM-Evals/evals_metrics.png" alt=""></p><h3 id="评测场景">评测场景</h3><p>评测场景决定了我们的评测结果在实际业务中是否真的“好用”。脱离场景谈精度，往往是刻舟求剑。高精度的评测场景必须与实际的产品形态和业务流量分布高度对齐。</p><h2 id="数据集质量模型">数据集质量模型</h2><p>经过综合分析之后，我们决定先从“评测的基石”——评测数据集质量入手，来提升评测精度，进而提升评测结果的区分度。</p><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/wangwei1237/status/2011248314256326845"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><p>我们设计了一个模型来评估数据集的质量，并基于这个模型对数据集进行了新的调整。</p><p><img src="/2026/02/06/How-to-Improve-the-Dataset-Quality-for-LLM-Evals/ds_quality.png" alt=""></p><ul><li><p>数据覆盖的分类：分类的覆盖面决定了我们能否探测到模型的“能力盲区”，如果分类存在缺失，得出的评测结论就是片面的。</p></li><li><p>每类数据的分布：分布的均衡性直接决定着最终的打分结果，如果数据分布不均（例如 80% 都是简单的单人肖像生成），评测结果就会被这些“送分题”主导，导致各家模型的分数都挤在 90 分以上，无法真实反映模型应对复杂、边缘场景的鲁棒性。</p></li><li><p>提示词的复杂度（长度）：在多模态评测中，提示词的复杂度直接对应了对模型的 <strong>指令遵循能力</strong> 的压力测试。简单的提示词（如“一只狗”）只能测试基础生成能力。高复杂度提示词则会叠加多个维度的约束：主体描述 + 动作 + 背景 + 环境光 + 风格 + 镜头参数。例如：“一只戴着红色飞行员护目镜的柯基（主体+服饰），正从老旧的双翼飞机上跳下（动作+背景），背景是黄昏时的粉色云层（环境光），使用鱼眼镜头拍摄，具有强烈的运动模糊效果（镜头参数）。”随着各大厂商基础画质的提升，高质量的评测数据集必须包含大量高复杂度提示词，用来检验模型是否会“漏词”（Omission，比如画了柯基但忘了护目镜）或“概念混淆”（Concept Bleeding，比如把狗的毛变成了红色）。复杂度越高，越能筛选出真正理解自然语言语义。而越复杂的提示词，其长度也会越大。但是，并不意味着提示词越长、就越复杂，一味的堆叠提示词的长度反而会让模型抓不住重点，导致性能下降。<br><img src="/2026/02/06/How-to-Improve-the-Dataset-Quality-for-LLM-Evals/complex.png" alt=""></p></li><li><p>提示词的专业度：复杂的提示词不一定很长，也可能很短、但是包含大量专业术语。专业的提示词不会使用“看起来很真实”这种模糊表述，而是会使用摄影、摄像、超写实等专业术语。专业度高的数据集能够探测模型能力的“天花板”。它不仅要求模型懂日常语言，还要求模型深刻理解专业视觉参数的映射关系。如果数据集缺乏专业提示词，就无法评估该模型是否具备作为生产力工具（如影视分镜、广告素材生成）的潜力</p></li></ul><h2 id="提示词的复杂度">提示词的复杂度</h2><p>直到我在 𝕏 上看到 <a href="https://x.com/berryxia">@berryxia</a> 生成的如下的图片，我才意识到，我们的提示词写的太简单了。<a href="https://x.com/berryxia">@berryxia</a> 用 2600+ 长度的提示词生成了如下的这种炫酷的效果。带着好奇，我检查了我们的评测数据的长度分布：90% 的提示词长度 &lt; 90 个字。过于简短的数据集很难生成更为复杂的图片，因此也无法有效评测到更优秀的的模型的实际能力。</p><p><img src="/2026/02/06/How-to-Improve-the-Dataset-Quality-for-LLM-Evals/xihongshi.jpeg" alt="2600个字的提示词生成的图片效果"></p><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/berryxia/status/2005843077098004853"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><p>接下来，我疯狂的在 𝕏 上学习优秀创作者（<a href="https://x.com/ZaraIrahh">@ZaraIrahh</a>、<a href="https://x.com/yyyole">@yyyole</a>、<a href="https://x.com/sidona">@sidona</a>、<a href="https://x.com/VoxcatAI">@VoxcatAI</a>、<a href="https://x.com/94vanAI">@94vanAI</a>……）的提示词是如何构建的。然后我发现，这些更优秀的效果的提示词的长度大多都在几百、甚至上千。</p><p><img src="/2026/02/06/How-to-Improve-the-Dataset-Quality-for-LLM-Evals/all_1.png" alt=""></p><ul><li><p>人与动漫角色的合照：</p><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/ZaraIrahh/status/2005816508602278010"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><p></p></li><li><p>植物百科照片：</p><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/yyyole/status/2007332916200702179"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><p></p></li><li><p>人物特写照片：</p><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/sidona/status/2011354796746494335"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><p></p></li><li><p>OOTD照片：</p><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/sidona/status/2011116211518517461"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><p></p></li><li><p>厚涂油画风格照片：</p><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/VoxcatAI/status/1999554624257446249"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><p></p></li><li><p>创意图片：</p><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/94vanAI/status/2006537483111247899"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><p></p></li></ul><h2 id="提示词的专业度">提示词的专业度</h2><p>随着对优秀效果的提示词的学习，我还发现：除了提示词的长度外，优秀的提示词还包含大量专业术语。这些专业术语不仅包括摄影、摄像、超写实等专业术语，还包括了镜头参数、环境光、风格、构图等专业术语。这些专业术语不仅能够帮助模型更好的理解提示词，还能够帮助模型更好的生成图片。</p><p>有时候简单的几个专业词汇的描述，就能够让模型生成出非常炫酷、专业、符合需求的高质量图片，例如：“16mm 超广定焦，高强度直闪灯光，富士经典负片（NC）”。</p><ul><li><strong>果冻风格的可爱卡通效果</strong>：“生成图片，<code>日系软萌涂鸦</code>、<code>Q版简笔</code>、<code>粉彩马卡龙</code>、果冻半透明、<code>高光反射</code>、轻厚涂、贴纸感、低细节治愈系，「主题」「比例」”。</li></ul><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/VoxcatAI/status/2008582973659234741"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><ul><li><strong>现代风格的室内自拍效果</strong>：在不到 100 个字的提示词中，包含了大量的专业术语，例如：<code>ultra-realistic modern</code>、<code>front-facing selfie camera angle</code>、<code>realistic proportions</code>、 <code>minimalist </code>、<code>cinematic lighting</code>、<code>high detail</code>、<code>sharp focus</code>、<code>instagram aesthetic</code>、<code>photorealistic</code>。</li></ul><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/xmiiru_/status/2011726482427523235"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><ul><li><strong>OOTD 怀旧风格效果</strong>：在 100 多个字的提示词中，也包含了大量的专业术语，例如：<code>golden hour sunlight</code>、<code>vintage</code>、<code>late afternoon warm light</code>、<code>Kodak Gold 200</code>、<code>nostalgic tones</code>、<code>heavy film grain</code>、<code>faded vintage look</code>、<code>magazine editorial aesthetic</code></li></ul><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/sidona/status/2015820392993374542"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><h2 id="优化效果">优化效果</h2><p>根据如上的评测数据质量标准，我们对评测数据集进行了优化，并重新对模型进行的新的评测，最终我们发现评测精度得到了显著提升。很多之前无法区分的模型，现在已经能够明显区分开来。</p><p>例如，我们有一个风格转换的 Query：<em>将图片转换成热血动漫风格</em>。在优化之前，模型之间的区分度非常低。但是增加专业度之后，模型之间的区分度立竿见影。</p><p><img src="/2026/02/06/How-to-Improve-the-Dataset-Quality-for-LLM-Evals/yuanshi.png" alt="原提示词：将图片转换成热血动漫风格"><br><img src="/2026/02/06/How-to-Improve-the-Dataset-Quality-for-LLM-Evals/youhua.png" alt="优化后提示词：生成极具辨识度的2D扁平卡通插画风格，极粗的黑色轮廓线描边，手绘矢量感，Q版可爱人物造型，赛璐珞平涂上色（大色块，无渐变），高对比度配色。纯粹的插画作品，无任何游戏UI界面。"></p><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/wangwei1237/status/2011065814955507895"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><ul><li>唐僧师徒 4 人的生成效果：</li></ul><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/wangwei1237/status/2011082920027644321"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><ul><li>刺绣的生成效果：</li></ul><div class="twitter-wrapper"><blockquote class="twitter-tweet"><a href="https://twitter.com/wangwei1237/status/2011209082133496153"></a></blockquote></div><script async="" defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></body></html>]]>
      </content:encoded>
    </item>
    <item>
      <title>深入研究 Anthropic 的 Agent 评估实践</title>
      <link>https://wangwei1237.github.io/2026/01/25/Study-Anthropic-s-agent-evaluation-practices-thoroughly/</link>
      <description>
        <![CDATA[<html><head></head><body><p><img src="/2026/01/25/Study-Anthropic-s-agent-evaluation-practices-thoroughly/evals_1.png" alt=""></p>
<p>2026 年 1 月 9 日，Anthropic 发布了一篇题为 <em>Demystifying evals for AI agents</em> <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>的博文，详细介绍了他们在评估 Agent 方面的思考、实践和方法。恰巧，我们在 LLM 评估领域也做了一些工作，并且也遇到了很多亟待解决的问题，因此对这篇博文产生了浓厚的兴趣。于是，花了几天时间，认真研习了这篇博文，并对其中的内容进行了整理和总结。</p></body></html>]]>
      </description>
      <author>Wang Wei</author>
      <category domain="https://wangwei1237.github.io/categories/LLM/">LLM</category>
      <category domain="https://wangwei1237.github.io/tags/Agent-%E8%AF%84%E4%BC%B0/">Agent 评估</category>
      <category domain="https://wangwei1237.github.io/tags/Anthropic/">Anthropic</category>
      <pubDate>Sun, 25 Jan 2026 21:47:35 GMT</pubDate>
      <content:encoded>
        <![CDATA[<html><head></head><body><p><img src="/2026/01/25/Study-Anthropic-s-agent-evaluation-practices-thoroughly/evals_1.png" alt=""></p><p>2026 年 1 月 9 日，Anthropic 发布了一篇题为 <em>Demystifying evals for AI agents</em> <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>的博文，详细介绍了他们在评估 Agent 方面的思考、实践和方法。恰巧，我们在 LLM 评估领域也做了一些工作，并且也遇到了很多亟待解决的问题，因此对这篇博文产生了浓厚的兴趣。于是，花了几天时间，认真研习了这篇博文，并对其中的内容进行了整理和总结。</p><span id="more"></span><h2 id="1-与-gemini-3-协同学习">1. 与 Gemini-3 协同学习</h2><p>在如今 LLM 能力持续突破天花板的背景下，我想，整理和总结的方式也需要做出变革。于是，这次，我们有采用文字版本的方式来呈现自己对这边博文的理解。借助谷歌的 Nano Banana Pro 生图大模型，我把我的理解整理成了图的形式来更直观的呈现我的思考和理解。</p><p>整体的思考涵盖了以下内容，每个点都对应了一张图：</p><ul><li>Agent 评估中的 8 大基本概念</li><li>3 种基本打分器</li><li>能力评估与回归评估的区别</li><li>对话 Agent 与其他 Agent 的多轮交互的区别</li><li>不同类型 Agent 评估设计上的差异</li><li>如何用 pass@k、pass^k 衡量 Agent 的不确定性</li><li>如何设计评估器</li><li>监控能力评估的饱和度的必要性</li><li>什么是 EDD（Evals-Driven-Development）</li><li>构建 Agent 评估的 9 大流程</li><li>如何系统化的理解 Agent 交付端到端评估</li></ul><h2 id="2-pdf-版本的思考内容">2. PDF 版本的思考内容</h2><div class="row">    <embed src="evals.pdf" width="100%" height="550" type="application/pdf"></div><h2 id="参考文献">参考文献</h2><hr class="footnotes-sep"><section class="footnotes"><ol class="footnotes-list"><li id="fn1" class="footnote-item"><p><a href="https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents">Demystifying evals for AI agents</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p></li></ol></section></body></html>]]>
      </content:encoded>
    </item>
    <item>
      <title>对 Agent Skills 的认知与思考</title>
      <link>https://wangwei1237.github.io/2025/12/24/Reflections-on-Agent-Skills/</link>
      <description>
        <![CDATA[<html><head></head><body><p><img src="/2025/12/24/Reflections-on-Agent-Skills/1.png" alt=""></p>
<p>自从 12 月 9 日，Anthropic 的 Barry Zhang 和 Mahesh Murag 在内部做了一个名为 <em>Stop Building Agents, Build Skills Instead</em><sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> 的分享之后，<code>Skills</code> 突然就火了起来。微信公众号、小红书等平台上涌现出了大量的介绍 <code>Skills</code> 的文章。一开始对这个概念也是迷迷糊糊的，于是就花了点时间阅读了 Claude 官方对 <code>Skills</code> 的所有博客与文档，自己也写了一个简单的 <code>Skills</code> 体验了一把这个火得不能再火的技术。</p>
<style>.admonition {
    margin: 1.5625em 0;
    padding: .6rem;
    overflow: hidden;
    font-size: 1.6rem;
    page-break-inside: avoid;
    border-left: .6rem solid #42b983;
    border-radius: .6rem;
    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);
    background-color: #fafafa;
  }
  
  p.admonition-title {
    position: relative;
    margin: -.6rem -.6rem .8em -.6rem !important;
    padding: .4rem .6rem .4rem .4rem;
    font-weight: 700;
    background-color:rgba(66, 185, 131, .1);
  }
  
  /*
  .admonition-title::before {
    position: absolute;
    top: .9rem;
    left: 1rem;
    width: 12px;
    height: 12px;
    background-color: #42b983;
    border-radius: 50%;
    content: ' ';
  }
  */
  
  .info>.admonition-title, .todo>.admonition-title {
    background-color: rgba(0,184,212,.1);
  }
  
  .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {
    background-color: rgba(255,145,0,.1);
  }
  
  .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {
    background-color: rgba(255,82,82,.1);
  }
  
  .admonition.info, .admonition.todo {
    border-color: #00b8d4;
  }
  
  .admonition.warning, .admonition.attention, .admonition.caution {
    border-color: #ff9100;
  }
  
  .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {
    border-color: #ff5252;
  }
  
  .info>.admonition-title::before, .todo>.admonition-title::before {
    background-color: #00b8d4;
    border-radius: 50%;
  }
  
  .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {
    background-color: #ff9100;
    border-radius: 50%;
  }
  
  .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{
    background-color: #ff5252;;
    border-radius: 50%;
  }
  
  .admonition>:last-child {
    margin-bottom: 0 !important;
  }
  </style>
<div class="admonition note"><p class="admonition-title">Skills 的本质
</p><ul>
<li><code>Agent Skills</code> 通过工程层面的优化，改变了 <strong>如何管理</strong>、<strong>如何交付</strong>、<strong>如何使用</strong> 大模型能力的方式，让 <code>Agent</code> 的构建更加简便、可控。</li>
<li>从技术层面看，<code>Agent Skills</code> 并没有改变 <em>LLM</em> 的底层原理，Transformer 架构没变、权重没变、预测下一个 token 的逻辑也没变……</li>
</ul>
</div></body></html>]]>
      </description>
      <author>Wang Wei</author>
      <category domain="https://wangwei1237.github.io/categories/LLM/">LLM</category>
      <category domain="https://wangwei1237.github.io/tags/Claude/">Claude</category>
      <category domain="https://wangwei1237.github.io/tags/Claude-Code/">Claude Code</category>
      <category domain="https://wangwei1237.github.io/tags/Agent-Skills/">Agent Skills</category>
      <pubDate>Wed, 24 Dec 2025 10:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<html><head></head><body><p><img src="/2025/12/24/Reflections-on-Agent-Skills/1.png" alt=""></p><p>自从 12 月 9 日，Anthropic 的 Barry Zhang 和 Mahesh Murag 在内部做了一个名为 <em>Stop Building Agents, Build Skills Instead</em><sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> 的分享之后，<code>Skills</code> 突然就火了起来。微信公众号、小红书等平台上涌现出了大量的介绍 <code>Skills</code> 的文章。一开始对这个概念也是迷迷糊糊的，于是就花了点时间阅读了 Claude 官方对 <code>Skills</code> 的所有博客与文档，自己也写了一个简单的 <code>Skills</code> 体验了一把这个火得不能再火的技术。</p><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">Skills 的本质</p><ul><li><code>Agent Skills</code> 通过工程层面的优化，改变了 <strong>如何管理</strong>、<strong>如何交付</strong>、<strong>如何使用</strong> 大模型能力的方式，让 <code>Agent</code> 的构建更加简便、可控。</li><li>从技术层面看，<code>Agent Skills</code> 并没有改变 <em>LLM</em> 的底层原理，Transformer 架构没变、权重没变、预测下一个 token 的逻辑也没变……</li></ul></div><span id="more"></span><p>等等，我好像想起了另一个类似的技术——<em>微服务</em>。很多工程师会把 <em>微服务</em> 作为一种技术或者一种架构，但实际上，<em>微服务</em> 只是为了让交付节奏更可控、更稳定、更高效的组织结构。为了适应 <em>微服务</em> 这种组织结构，才产生了诸如 <em>Kubernetes</em>、<em>服务网关</em> 等新技术。</p><h2 id="1-skills-的发展节奏">1. Skills 的发展节奏</h2><ul><li><p>2025 年 10 月 16 日，Anthropic 发布了 <code>Agent Skills</code> 以扩展 Claude 在执行特定任务场景下的性能<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>。<code>Agent Skills</code> 是一个打包好的目录，该目录下包含了与特定任务相关的指令、脚本和资源。Claude 可以在需要时加载这些内容以完成对应的特定任务。</p></li><li><p>2025 年 12 月 9 日，Anthropic 的 Barry Zhang 和 Mahesh Murag 在内部分享 <a href="https://www.youtube.com/watch?v=CEvIs9y1uog">Stop Building Agents, Build Skills Instead</a>（也可以在小宇宙上获取对应的中文播客内容：<a href="https://www.xiaoyuzhoufm.com/episode/6943bf87c4f10bdeea0d738b">别再从头造 Agent 了：Anthropic 专家揭秘“Agent Skill”如何重塑 AI 协作</a>）中提出了构建 <code>Agent</code> 的新范式——<code>Agent Skills</code>，从而引爆了社区。</p><p><img src="/2025/12/24/Reflections-on-Agent-Skills/17664071212332.jpg" alt=""></p></li><li><p>2025 年 12 月 18 日，Anthropic 发布了跨平台的、具备兼容性的 <code>Agent Skills</code> 开放标准<sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>。在 <code>Agent Skills</code> 标准下，非 Cluade 生态的 <em>LLM</em>、<em>Agent</em>、平台也可以使用 <code>Skills</code> 来更方便的扩展自身的能力。</p></li><li><p>2025 年 12 月 19 日，OpenAI 宣布 <a href="https://developers.openai.com/codex/changelog">在 Codex CLI 和 Codex IDE 中增加了 <code>Skills</code> 能力支持</a>，<code>SKills</code> 从名义上的开放标准走进了实质上的开放标准。我们也在 OpenAI 的开发者文档中发现和 <code>Agent Skills</code> 相关的描述<sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup>：</p><blockquote><p>Agent Skills let you extend Codex with task-specific capabilities. A skill packages instructions, resources, and optional scripts so Codex can perform a specific workflow reliably. You can share skills across teams or the community, and they build on the open Agent Skills standard.</p></blockquote></li></ul><h2 id="2-agent-skills">2. Agent Skills</h2><p>根据 Anthropic 的介绍，<code>Agent Skills</code> 就是一个整理并打包好的、包含指令、脚本和资源的目录，<code>Agent</code> 可以动态发现并加载 <code>Agent Skills</code> 目录下的内容，从而使得 <code>Agent</code> 可以在特定任务上有更好的表现。</p><p><code>Agent Skills</code> 的详细的目录规范可以参见：<a href="https://agentskills.io/specification">Agent Skills-Specification</a>。</p><p>通过 <code>Agent Skills</code>，我们可以把某领域的专业知识打包成可组合、可扩展、可迁移的资源，Claude 可以使用这些资源来扩展能力，从而把通用 <code>Agent</code> 变为符合特定领域需求的专业 <code>Agent</code>。</p><p>为 <code>Agent</code> 构建 <code>Skills</code>，就如同为新员工整理的入职指南文档。我们无需为每个场景构建零散的、定制化的 <code>Agent</code>，我们可以通过沉淀和分享自己的专业知识，并通过可组合的 <code>Skills</code> 以允许我们的 <code>Agent</code> 具备专业化能力<sup class="footnote-ref"><a href="#fn5" id="fnref5">[5]</a></sup>。</p><p><img src="/2025/12/24/Reflections-on-Agent-Skills/e447e5048f150fe4459d8f2585cf3066.png" alt=""></p><h2 id="3-skills-与渐进式信息披露">3. Skills 与渐进式信息披露</h2><p><code>Agent Skills</code> 的核心设计原则就是 <em>渐进式信息披露</em>，该设计原则使得 <code>Agent Skills</code> 更加灵活性，也使其扩展性更好，最重要的是可以保持与大模型交互过程中的 <code>Context</code> 内容始终保持精简。</p><ol><li><code>SKILL.md</code> 文件中的 <em>YAML</em> 格式的元数据是 <em>渐进式信息披露</em> 的第一层级：Claude 仅根据元数据中提供的信息，就足以知道需要在何时使用哪种 <code>skill</code>。</li><li><code>SKILL.md</code> 文件中的剩余信息是 <em>渐进式信息披露</em> 的第二层级：该内容提供了如何使用该 <code>skill</code> 的全部内容，包括 <code>skill</code> 的概述、代码示例…… 如果 Claude 认为某 <code>skill</code> 与当前任务有关，Claude 就会读取完整的 <code>SKILL.md</code> 并将其加载到上下文中。</li><li>随着 <code>Agent Skills</code> 越来越复杂，<code>SKILL.md</code> 文件可能会包含越来过多的背景信息，而根据 <a href="/2025/11/29/context-engineering-for-AI-agents/">Context Engineering for AI Agents</a> 中的 <em>Context Rot</em> 的问题，我们无法把所有的这些信息都放到单个 <code>SKILL.md</code>文件。有时候，有些背景信息仅与特定的应用场景有关，例如 <a href="https://github.com/anthropics/skills/tree/main/skills/pdf">PDF skill</a> 中的表单填充能力，此时，把这些仅与特定场景关联的信息全部放到所有场景的背景信息中，不但会导致上下文不断增加，还容易因为 <em>Context Rot</em> 导致模型性能下降。我们可以把这些信息进行拆分并将其置于 <code>Agent Skills</code> 目录下的单独文件，同时在 <code>SKILL.md</code> 中通过文件名来引用这些单独的信息文件。而类似这些附加的链接文件则属于 <em>渐进式信息披露</em> 的第三层级：Claude 可以根据需要来选择浏览和发现这些特殊场景的 <code>skill</code> 信息。</li></ol><p><img src="/2025/12/24/Reflections-on-Agent-Skills/17663818747722.jpg" alt=""></p><p><em>渐进式信息披露</em> 就像一本精心组织、策划的书籍一样，先从序言开始，然后是目录，接下来是具体章节内容，最后是相关的附录内容。这种精心安排的方式，能够让 Claude 只在需要时才加载必要的信息，从而有效的避免了 <code>Context Rot</code>，即为大模型提供了足够的信息、同时又让大模型保持较高的性能。</p><p><img src="/2025/12/24/Reflections-on-Agent-Skills/17663826189780.jpg" alt=""></p><p>实际上，人类在通过翻阅工具类书籍、手册完成任务时，也不会从头到尾阅读完整本书：</p><ol><li>我们首先会了解下这本手册的适用范围，是 Java 语言还是 C++ 语言，是底层网络库还是应用层的传输协议，是面相用户的还是面相开发者的……</li><li>然后我们会通过目录了解手册都包含哪些内容，并于我们当前的人物进行匹配，圈定需要使用哪部分内容才能解决当前的任务。</li><li>最后，我们加载圈定的章节内容，并详细阅读，最终尝试利用新学到的知识解决当前的任务。</li></ol><p>例如，如果我想了解 <em>假设检验</em> 的相关内容，我只需要阅读 <a href="/introduction_to_probability_and_statistics/chapter_8/8.html"><em>第 8 章</em></a> 的内容就可以了，没必要阅读完整的一本书。</p><p>以 <code>PDF skill</code> 为例，在 <code>Agent Skills</code> 的 <em>渐进式信息披露</em> 机制下，随着与用户之间的交互，Claude 的 <code>Context Window</code> 的变化如下图所示：</p><p><img src="/2025/12/24/Reflections-on-Agent-Skills/17663862891763.jpg" alt=""></p><ol><li>最开始的时候，<code>Context Window</code> 仅包含 <em>系统提示词</em>、<em>每个已安装 <code>Skills</code> 的元数据</em>、<em>用户的初始消息</em></li><li>Claude 通过调用 <code>Bash</code> 工具读取 <code>pdf/SKILL.md</code> 的内容来触发 <code>PDF skill</code></li><li>根据 <code>SKILL.md</code> 的描述，Claude 继续选择读取 <code>PDF skill</code> 的 <code>forms.md</code> 文件内容来获取如何填充表单的信息</li><li>最后，Claude 从 <code>PDF skill</code> 加载相关指令后，继续处理用户的任务</li></ol><h2 id="4-skills-与-代码执行">4. Skills 与 代码执行</h2><p>以本地文件系统构建起来的 <code>Skills</code> 与生俱来的最大优势就是可以直接利用本地文件系统提供的强大能力：<em>Bash 能力</em>、<em>Command 能力</em>、<em>代码脚本能力</em>……</p><p>虽然直接使用大模型也可以完成一项任务，但有的任务更适合通过代码、程序来完成。例如，我们可以让大模型对数据进行排序，但是直接调用本地文件系统中的排序程序反而是一种更高效的任务执行方式。尤其是，当我们需要保持任务结果的确定性时，代码、程序是更为合适的选择，毕竟大模型的执行存在概率性的问题。</p><p>以 <code>PDF skill</code> 中的表单填充能力为例，在 <code>pdf/forms.md</code> 中就明确告知了大模型：如何使执行本地的 python 脚本来完成表单填充任务。</p><p><img src="/2025/12/24/Reflections-on-Agent-Skills/17665550024178.jpg" alt=""></p><p>作为非英语母语的人，每次在阅读全是大写字母的英文文献时我总是感觉会非常吃力：</p><blockquote><p>HELLO, MY NAME IS WANG WEI. I AM A COMPUTER ENGINEER.</p></blockquote><blockquote><p>hello, my name is wang wei. i am a computer engineer.</p></blockquote><p>于是，我就想用 <code>Skills</code> 来实现：把 PDF 文献中的大写字母转成小写字母，从而提升阅读的效率。当然，这可能并不是一个痛点，但是我们就假定以这个场景来介绍 <code>Skills</code> 如何执通过行本地代码解决这个假定的任务。</p><h3 id="4-1-创建-skill-文件">4.1 创建 SKILL 文件</h3><p><img src="/2025/12/24/Reflections-on-Agent-Skills/cc_skill_demo.png" alt=""></p><h3 id="4-2-实现-pdf-lowercase-py">4.2 实现 pdf_lowercase.py</h3><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    <span class="string">"""主函数"""</span></span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">len</span>(sys.argv) != <span class="number">3</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">"用法: python pdf_lowercase.py &lt;输入PDF路径&gt; &lt;输出PDF路径&gt;"</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">"示例: python pdf_lowercase.py input.pdf output.pdf"</span>)</span><br><span class="line">        sys.exit(<span class="number">1</span>)</span><br><span class="line">    </span><br><span class="line">    input_path = sys.argv[<span class="number">1</span>]</span><br><span class="line">    output_path = sys.argv[<span class="number">2</span>]</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 确保输出目录存在</span></span><br><span class="line">    output_dir = os.path.dirname(output_path)</span><br><span class="line">    <span class="keyword">if</span> output_dir <span class="keyword">and</span> <span class="keyword">not</span> os.path.exists(output_dir):</span><br><span class="line">        os.makedirs(output_dir)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 处理PDF</span></span><br><span class="line">    success = process_pdf(input_path, output_path)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> success:</span><br><span class="line">        sys.exit(<span class="number">0</span>)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        sys.exit(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line">    main()</span><br></pre></td></tr></tbody></table></figure><h3 id="4-3-部署">4.3 部署</h3><p>按照 <code>Skills</code> 的规范组织好目录，并将整个目录放到如下的目录 <code>${PROJECT_BASE_DIR}/.claude/skills/</code>，我们就可以让 Claude 调用该 <code>Skills</code> 实现字母大小写转换的任务了。</p><p>关于 <code>pdf-case-converter</code> 的全部代码，可以参考：<a href="https://github.com/wangwei1237/agent-skills/tree/main/pdf-lowercase-skill">pdf-lowercase-skill</a>。</p><p>试想一下，如果有一个 100 页的文档需要转换（当然，实际生活中可能不存在这种场景），如果让大模型来完成转换，首先要面对的问题就是 <code>tokens</code> 成本的问题，其次还有可能会导致 <code>context window</code> 爆炸的问题。但是，如果采用 <code>Skills</code> 调用本地代码的方式，就完美的解决了成本与 <code>context window</code> 的问题。</p><!--video example--><div class="bili_video"><div style="position: relative; width: 100%; height: 0; padding-bottom: 75%;"><iframe src="https://player.bilibili.com/player.html?aid=115772269527051&amp;page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true" style="position: absolute; width: 100%; height: 100%; left: 0; top: 0;"> </iframe></div></div><h2 id="5-skills-与-mcp">5. Skills 与 MCP</h2><p>作为 <code>MCP</code> 的有效补充，<code>Skills</code> 也大大增加了 <em>LLM</em> 的能力边界，让 <em>LLM</em> 可以完成的任务规模得以扩张，<em>LLM</em> 从单点任务跨越到了完成更复杂、更垂直、更专业任务的 <em>pipeline</em>。</p><p>2025 年，12 月 19 日，当 <code>Skills</code> 概念大火的时候，为了避免开发者对 <code>MCP</code> 和 <code>Skills</code> 之间的混淆和误用，Anthropic 发布了一篇博客 <em><a href="https://claude.com/blog/extending-claude-capabilities-with-skills-mcp-servers">Extending Claude’s capabilities with skills and MCP servers</a></em> 来阐述二者之间的区别与差异，同时还给出了 2 个例子来阐述如何在实际应用中联合使用 <code>MCP</code> 和 <code>Skills</code> 技术完成特定的任务<sup class="footnote-ref"><a href="#fn6" id="fnref6">[6]</a></sup>。</p><blockquote><p>Since launching Skills, two of the biggest questions we’ve heard from customers are: “How do skills and MCP work together? When should I use one versus the other?”</p></blockquote><p><img src="/2025/12/24/Reflections-on-Agent-Skills/17664670800710.jpg" alt=""></p><p>作为协议层，<code>MCP</code> 统一了真实环境中的所有工具与 <em>LLM</em> 的交互方式；作为能力层，<code>MCP</code> 允许大模型通过自主的调用工具来获取实时信息，例如天气信息、科技新闻……</p><p>但是，对于某个垂类行业，例如 <em>科技跟踪日报</em> ——根据每天的各大媒体报道的科技新闻对当前的各方向的技术进行跟踪报道——这种非常垂直的任务，单纯使用 <code>MCP</code> 是不够的。</p><p><code>MCP</code> 只能获取到最新的科技信息，如果根据这些信息撰写出最终的报告，需要一套复杂的业务编排流程。而 <code>Agent Skills</code> 的目标就是用来解决类似规模的垂类任务。</p><p><code>MCP</code> 是工具的开放，而 <code>Agent Skills</code> 则是业务流的开放。</p><h2 id="6-skills-的本质">6. Skills 的本质</h2><h3 id="6-1-工程上的优化">6.1 工程上的优化</h3><p>在 <code>Skills</code> 之前，<em>科技跟踪日报</em> 可以通过构建一个 <code>Agent</code> 来实现，实现该 <code>Agent</code> 的 <em>系统提示词</em>、<em>业务流</em>、<em>信息处理</em> 等所有的信息都通过硬编码封装在 <code>Agent</code> 中，很难与别人分享，也很难形成规模化的复用。更重要的是，如果要更新 <code>Agent</code> 的能力，还需要手动修改 <em>提示词</em> 或者代码，管理和维护的成本也就相对较高。</p><p>但是，在 <code>Agent Skills</code> 机制下，原来硬编码的 <em>业务流程</em>、<em>系统提示词</em> 等信息都以模块化的方式组织在了 <code>Skills</code> 对应的目录下。我们可以非常方便的把 <em>科技跟踪日报</em> <code>skill</code> 集成到其他的 <code>Agent</code> 以实现对应的能力，我们也可以非常方便的通过修改 <code>SKILL.md</code> 文件中的描述来更新 <code>Agent</code> 的能力。</p><p>正如 Anthropic 在 <code>Agent Skills</code> 的 <a href="https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills">博客</a> 中所说的那样：对于 <code>Skills</code> 这一功能，我们感到无比兴奋。<code>Skills</code> 可以帮助企业和个人更方便的分享他们所处行业的 <em>背景信息</em> 和 <em>工作流程</em>。</p><blockquote><p>We’re especially excited about the opportunity for Skills to help organizations and individuals share their context and workflows with Claude.</p></blockquote><p>本质上讲：<code>Agent Skills</code> 通过工程层面的优化，改变了 <strong>如何管理</strong>、<strong>如何交付</strong>、<strong>如何使用</strong> 大模型能力的方式，让 <code>Agent</code> 的构建更加简便、可控。从技术层面看，<code>Agent Skills</code> 并没有改变 <em>LLM</em> 的底层原理，Transformer 架构没变、权重没变、预测下一个 token 的逻辑也没变……</p><h3 id="6-2-上下文工程策略">6.2 上下文工程策略</h3><p><code>Agent Skills</code> 通过本地文件系统的组织结构，帮我们实现了 Agent 的高效管理与交付。同时，<code>Agent Skills</code> 的 <em>渐进式信息披露</em> 机制，让大模型在处理复杂任务时，只需要加载必要的信息即可。因此，从另一个角度讲，<code>Agent Skills</code> 其实还是一种非常有效的 <em>上下文工程策略</em>，从而避免了 <code>Context Rot</code> 的问题。</p><p><code>Skills</code> 与 <code>MCP</code> 的机制完全不同。在 <code>MCP</code> 架构中，无论模型是否会用到某个工具，都需要将所有的工具定义加载到 <code>Context Window</code> 中，从而会导致 <code>Context Window</code> 的无效膨胀。Cluade 在 <em><a href="https://www.anthropic.com/engineering/advanced-tool-use">Introducing advanced tool use on the Claude Developer Platform</a></em><sup class="footnote-ref"><a href="#fn7" id="fnref7">[7]</a></sup> 中提出了 <em>Tool Search Tool</em> 能力来避免 <code>MCP</code> 中加载所有工具定义带来的 <code>Context Window</code> 膨胀的问题。</p><p><img src="/2025/12/24/Reflections-on-Agent-Skills/tst.webp" alt=""></p><p>在 <code>Skills</code> 之前，上下文工程的机制主要是如何在对话过程中精简 <code>Context Window</code>，而 <code>Skills</code> 则反其道而行之，采用 <em>非必要不加载</em> 的方式来精简 <code>Context Window</code>。</p><p><img src="/2025/12/24/Reflections-on-Agent-Skills/ce-skills.jpg" alt=""></p><p><code>Skills</code> 的出现，扩展了上下文工程的思路，让我们在构建大模型应用时，有了更多的选择。传统的 <em>上下文工程</em> 主要解决长程任务的多轮对话过程因为 <em>对话历史</em> 和 <em>工具调用</em> 导致的 <code>Context Window</code> 膨胀，而 <code>Skills</code> 则主要解决系统提示词增加导致的 <code>Context Window</code> 膨胀问题。</p><p>同时，我们也需要认识到：<code>Skills</code> 通过 <em>非必要不加载</em>（把不用的规则卸载）的方式解决系统提示词的膨胀，但是对于历史对话和工具调用的膨胀问题，<code>Skills</code> 仍然无能为力。</p><h2 id="参考文献">参考文献</h2><hr class="footnotes-sep"><section class="footnotes"><ol class="footnotes-list"><li id="fn1" class="footnote-item"><p><a href="https://www.youtube.com/watch?v=CEvIs9y1uog">Stop Building Agents, Build Skills Instead</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p></li><li id="fn2" class="footnote-item"><p><a href="https://claude.com/blog/skills">Introducing Agent Skills</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p></li><li id="fn3" class="footnote-item"><p><a href="https://agentskills.io/home"> Agent Skills Open Standard</a> <a href="#fnref3" class="footnote-backref">↩︎</a></p></li><li id="fn4" class="footnote-item"><p><a href="https://developers.openai.com/codex/skills">OpenAI Developers: Agent Skills</a> <a href="#fnref4" class="footnote-backref">↩︎</a></p></li><li id="fn5" class="footnote-item"><p><a href="https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills">Equipping agents for the real world with Agent Skills</a> <a href="#fnref5" class="footnote-backref">↩︎</a></p></li><li id="fn6" class="footnote-item"><p><a href="https://claude.com/blog/extending-claude-capabilities-with-skills-mcp-servers">Extending Claude’s capabilities with skills and MCP servers</a> <a href="#fnref6" class="footnote-backref">↩︎</a></p></li><li id="fn7" class="footnote-item"><p><a href="https://www.anthropic.com/engineering/advanced-tool-use">Introducing advanced tool use on the Claude Developer Platform</a> <a href="#fnref7" class="footnote-backref">↩︎</a></p></li></ol></section></body></html>]]>
      </content:encoded>
    </item>
    <item>
      <title>Context Engineering for AI Agents</title>
      <link>https://wangwei1237.github.io/2025/11/29/context-engineering-for-AI-agents/</link>
      <description>
        <![CDATA[<html><head></head><body><p><img src="/2025/11/29/context-engineering-for-AI-agents/CE.jpeg" alt=""></p>
<p>2025 年 6 月 25 日，在 Andrej Karpathy <a href="https://x.com/karpathy/status/1937902205765607626">推文</a> 的推动下，<em>上下文工程</em>（<em>Context Engineering</em>）一词迅速在 AI 开发者社区中爆火。</p>
<blockquote>
<p>+1 for “context engineering” over “prompt engineering”.</p>
</blockquote>
<p>但是当时主要精力放在了大模型评估的工作上，因此一直没有时间来深入研究 <em>上下文工程</em>（<em>Context Engineering</em>）。直到最近，在我们构建端到端的 TestingAgent 时，我们遇到了因上下文窗口限制，多轮交互后初始重要字段丢失，导致 TestingAgent 执行失败的问题。我想，是时候来深入研究一下 <em>上下文工程</em>（<em>Context Engineering</em>）了。</p></body></html>]]>
      </description>
      <author>Wang Wei</author>
      <category domain="https://wangwei1237.github.io/categories/LLM/">LLM</category>
      <category domain="https://wangwei1237.github.io/tags/Context-Engineering/">Context Engineering</category>
      <category domain="https://wangwei1237.github.io/tags/Context-Rot/">Context Rot</category>
      <category domain="https://wangwei1237.github.io/tags/Long-Horizon-Tasks/">Long-Horizon Tasks</category>
      <category domain="https://wangwei1237.github.io/tags/%E4%B8%8A%E4%B8%8B%E6%96%87%E5%B7%A5%E7%A8%8B/">上下文工程</category>
      <pubDate>Sat, 29 Nov 2025 10:50:51 GMT</pubDate>
      <content:encoded>
        <![CDATA[<html><head></head><body><p><img src="/2025/11/29/context-engineering-for-AI-agents/CE.jpeg" alt=""></p><p>2025 年 6 月 25 日，在 Andrej Karpathy <a href="https://x.com/karpathy/status/1937902205765607626">推文</a> 的推动下，<em>上下文工程</em>（<em>Context Engineering</em>）一词迅速在 AI 开发者社区中爆火。</p><blockquote><p>+1 for “context engineering” over “prompt engineering”.</p></blockquote><p>但是当时主要精力放在了大模型评估的工作上，因此一直没有时间来深入研究 <em>上下文工程</em>（<em>Context Engineering</em>）。直到最近，在我们构建端到端的 TestingAgent 时，我们遇到了因上下文窗口限制，多轮交互后初始重要字段丢失，导致 TestingAgent 执行失败的问题。我想，是时候来深入研究一下 <em>上下文工程</em>（<em>Context Engineering</em>）了。</p><span id="more"></span><p><img src="/2025/11/29/context-engineering-for-AI-agents/lm_trend.png" alt="2025 年每周发布的大模型数量趋势图"></p><h2 id="1-workflow-agent">1. Workflow &amp; Agent</h2><p><em>Building effective agents</em><sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> 介绍了 <em>workflow</em> 和 <em>agent</em> 的区别，这两者之间最本质的区别在于：究竟是由谁来控制执行流程。</p><p>如果是由代码编写的规则、流程来控制大语言模型（<em>LLM</em>），那么这种架构就是 <em>workflow</em>；如果是大语言模型（<em>LLM</em>）自主的与环境进行互动、并根据新的信息来自主的决定后续的执行动作，那么这种架构就是 <em>Agent</em>。</p><p><img src="/2025/11/29/context-engineering-for-AI-agents/17643155839477.jpg" alt="左图为 workflow，右图为 Agent"></p><table><thead><tr><th>维度</th><th>Workflow (工作流)</th><th>Agent (智能体)</th></tr></thead><tbody><tr><td>结构</td><td>线性或树状 (DAG)<br>A → B → C</td><td>循环 (Loop)<br>思考 → 行动 → 观察 → 再思考</td></tr><tr><td>确定性</td><td>高。<br>路径是固定的，输出结果通常比较稳定。</td><td>低。<br>同样的任务，模型可能会选择不同的路径。</td></tr><tr><td>灵活性</td><td>低。只能处理预设好的场景。</td><td>高。可以处理未知的、开放式的问题。</td></tr><tr><td>错误处理</td><td>需要在代码中预设 if/else 来处理错误。</td><td>模型自我反思：“哎呀报错了，换个方法试试”。</td></tr><tr><td>适用场景</td><td>翻译、数据提取、分类、内容生成等定义明确的任务。</td><td>开放式研究、复杂编码任务、多步推理等边界模糊的任务。</td></tr></tbody></table><blockquote><p><em>start with a simple workflow, move to an agent only when you need to.</em></p></blockquote><p>对于软件交付领域而言，端到端的 CodingAgent 或者 TestingAgent 因为需要多步迭代、并且在不同的迭代过程中需要根据当前的结果与状态来调整执行步骤进而完成代码编写或者软件测试工作，因此简单的 <em>workflow</em> 不足以完成这样的工作，必须采用 <em>Agent</em> 才能实现这种复杂的任务。</p><p><img src="/2025/11/29/context-engineering-for-AI-agents/ce_arch.png" alt=""></p><p>假设我们要测试一个电商网站的登录功能，TestAgent 会：</p><ol><li>先调用工具打开网页</li><li>再理解当前的网页，解析到用户名、密码的输入框以及登录按钮</li><li>再调用工具执行登录操作</li><li>判断状态→如果登录失败，需要根据错误信息以及调用工具获取更多的领域知识来决策如何解决当前的登录失败</li><li>……</li><li>随着 N 次迭代，这些工具调用信息、信息空间中查询到的信息、对话历史信息就会迅速撑爆上下文窗口……然后，TestAgent 就会因为接下来要讲到的 <em>Context Rot</em> 问题而导致执行性能急剧下降。</li></ol><h2 id="2-context-rot">2. Context Rot</h2><p>2025 年 7 月 14 日，Chroma 公司的研究员通过测试主流模型发现：随着输入 tokens 长度的增加，模型性能反而出现了持续下降的现象。Chroma 把这一现象称之为 <em>Context Rot</em>，并且把他们的发现整理为这篇技术报告 <em>Context Rot: How Increasing Input Tokens Impacts LLM Performance</em><sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>。</p><p><img src="/2025/11/29/context-engineering-for-AI-agents/17642451948931.jpg" alt=""></p><p>过去，我们总是期望大语言模型的上下文窗口能够变得越来越大，从而我们可以把更多的信息塞进提示词中，以期望提升模型的性能。</p><p>然而，就像人类的记忆容量也有限制一样，大型语言模型（LLMs）在解析大量上下文时也有一个 <em>注意力预算</em>（<em>attention budget</em>），每引入一个新的 token，就会在一定程度上消耗该 <em>注意力预算</em>。</p><p>因此，<em>Context Rot</em> 提醒我们：在一段持续的对话或长文本生成过程中，随着输入信息（Context）的不断累积，大语言模型会逐渐丧失对早期关键信息的“注意力”，导致回答质量下降、逻辑断层或指令遗忘。</p><p><img src="/2025/11/29/context-engineering-for-AI-agents/224c5f534db954028ae9e44474887478.jpg" alt=""></p><p>在 <em>Context Rot</em> 的限制下，大语言模型的上下文窗口是一种边际收益递减的有限资源。长上下文能力不应该仅仅看作是模型的一项技术指标，而更应该看作是一个需要精心设计和管理的系统工程。我们必须为大语言模型精心筛选信息，以避免 <em>Context Rot</em> 现象。</p><p><img src="/2025/11/29/context-engineering-for-AI-agents/17642475708110.jpg" alt=""></p><p>这种在 Agent 的迭代过程中，为大语言模型（<em>LLM</em>）精心筛选信息的过程就是我们将要介绍的 <em>上下文工程</em>。</p><h3 id="2-1-transformers-架构的天然缺陷">2.1 Transformers 架构的天然缺陷</h3><p>在 <em><a href="/2024/10/16/What-exactly-is-attention/">自注意力究竟是什么？</a></em> 这篇文章中，我们详细介绍了 Transformer 架构，并对其中的 <em>注意力机制</em>（<em>Attention Mechanism</em>）进行了详细的解释。</p><p class="katex-block has-jax"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>Attention</mtext><mo stretchy="false">(</mo><mi mathvariant="bold">Q</mi><mo separator="true">,</mo><mi mathvariant="bold">K</mi><mo separator="true">,</mo><mi mathvariant="bold">V</mi><mo stretchy="false">)</mo><mo>=</mo><mtext>softmax</mtext><mrow><mo fence="true">(</mo><mfrac><mrow><mi mathvariant="bold">Q</mi><msup><mi mathvariant="bold">K</mi><mi>T</mi></msup></mrow><msqrt><msub><mi>d</mi><mi>k</mi></msub></msqrt></mfrac><mo fence="true">)</mo></mrow><mi mathvariant="bold">V</mi></mrow><annotation encoding="application/x-tex">\text{Attention}(\mathbf{Q}, \mathbf{K}, \mathbf{V}) = \text{softmax}\left( \frac{\mathbf{Q}\mathbf{K}^T}{\sqrt{d_k}} \right) \mathbf{V}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord text"><span class="mord">Attention</span></span><span class="mopen">(</span><span class="mord mathbf">Q</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathbf">K</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathbf" style="margin-right:0.016em;">V</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:2.4684em;vertical-align:-0.95em;"></span><span class="mord text"><span class="mord">softmax</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size3">(</span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.5183em;"><span style="top:-2.2528em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8572em;"><span class="svg-align" style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord" style="padding-left:0.833em;"><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0315em;">k</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span><span style="top:-2.8172em;"><span class="pstrut" style="height:3em;"></span><span class="hide-tail" style="min-width:0.853em;height:1.08em;"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="1.08em" viewBox="0 0 400000 1080" preserveAspectRatio="xMinYMin slice"><path d="M95,702c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429c69,-144,104.5,-217.7,106.5,-221l0 -0c5.3,-9.3,12,-14,20,-14H400000v40H845.2724s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47zM834 80h400000v40h-400000z"></path></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1828em;"><span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathbf">Q</span><span class="mord"><span class="mord mathbf">K</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8413em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.1389em;">T</span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.93em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size3">)</span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathbf" style="margin-right:0.016em;">V</span></span></span></span></span></p><p>在推理阶段，模型需要计算当前生成的 token 与上下文中几千个 token 之间的注意力。在单个注意力头内，对于某个输入 token，它对所有 tokens 的注意力权重（经过 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi><mi>o</mi><mi>f</mi><mi>t</mi><mi>m</mi><mi>a</mi><mi>x</mi><mo stretchy="false">(</mo><mo>⋅</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">softmax(\cdot)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">so</span><span class="mord mathnormal" style="margin-right:0.1076em;">f</span><span class="mord mathnormal">t</span><span class="mord mathnormal">ma</span><span class="mord mathnormal">x</span><span class="mopen">(</span><span class="mord">⋅</span><span class="mclose">)</span></span></span></span> 后）会满足：</p><p class="katex-block has-jax"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>&nbsp;</mtext><munder><mo>∑</mo><mi>i</mi></munder><msub><mi>w</mi><mi>i</mi></msub><mo>=</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">&nbsp;\sum_i w_i = 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:2.3277em;vertical-align:-1.2777em;"></span><span class="mord">&nbsp;</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.05em;"><span style="top:-1.8723em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span><span style="top:-3.05em;"><span class="pstrut" style="height:3.05em;"></span><span><span class="mop op-symbol large-op">∑</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.2777em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0269em;">w</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0269em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1</span></span></span></span></span></p><p>因此当上文过长，token 之间的注意力结果就不容易区分，模型就很难精准地“聚焦”到那个最重要的 token 上。</p><p><img src="/2025/11/29/context-engineering-for-AI-agents/17642961219029.jpg" alt=""></p><h3 id="2-2-k-v-q-矩阵维度的溢出">2.2 K/V/Q 矩阵维度的溢出</h3><p>除了 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi><mi>o</mi><mi>f</mi><mi>t</mi><mi>m</mi><mi>a</mi><mi>x</mi><mo stretchy="false">(</mo><mo>⋅</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">softmax(\cdot)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">so</span><span class="mord mathnormal" style="margin-right:0.1076em;">f</span><span class="mord mathnormal">t</span><span class="mord mathnormal">ma</span><span class="mord mathnormal">x</span><span class="mopen">(</span><span class="mord">⋅</span><span class="mclose">)</span></span></span></span> 的注意力分散机制外，<em>KV Cache</em> 的量化损失也是导致 <em>Context Rot</em>  的物理根源。</p><p><em>K/V/Q</em> 矩阵的维度本身是固定的（以 <a href="https://huggingface.co/deepseek-ai/DeepSeek-R1-0528/blob/main/config.json">deepseek-R100528</a> 为例，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>d</mi><mi>Q</mi></msub></mrow><annotation encoding="application/x-tex">d_Q</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9805em;vertical-align:-0.2861em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">Q</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span></span> = 7168），不会因为输入 tokens 的变长而变大。但是，赋予这些向量的 <em>位置感</em> 的数学机制——<em>旋转位置编码</em>（<em>RoPE</em>）——却会因为上下文过长而失效。</p><p><em>RoPE</em> 通过在多维空间中对向量进行旋转来表示位置。模型在训练时，只见过比如 0 到 4000 的旋转角度。当上下文变成 10000 时，向量被旋转到了一个模型从未见过的高频角度。此时 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi><mi>o</mi><mi>f</mi><mi>t</mi><mi>m</mi><mi>a</mi><mi>x</mi><mo stretchy="false">(</mo><mo>⋅</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">softmax(\cdot)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">so</span><span class="mord mathnormal" style="margin-right:0.1076em;">f</span><span class="mord mathnormal">t</span><span class="mord mathnormal">ma</span><span class="mord mathnormal">x</span><span class="mopen">(</span><span class="mord">⋅</span><span class="mclose">)</span></span></span></span> 就不再能准确反映相对距离，模型也就无法区分 “第 9000 个词” 和 “第 9005 个词” 的区别。</p><p>就像 <em>uint8</em> 只能表示 0~255 这个 256 个数，但是当用 <em>uint8</em> 存储 256 这个整数时，结果就会变成 0。那么，在 <em>uint8</em> 下，如何区分 0 到底是 0 呢，还是溢出了的 256 呢？</p><h2 id="3-long-horizon-tasks">3. Long-Horizon Tasks</h2><p>在大模型领域，<em>长程任务</em>（<em>long-horizon tasks</em>）是指那些无法通过单次原子操作完成，而必须拆解为一系列具有逻辑依赖关系的连续决策步骤（<em>Sequential Decision Making</em>）才能解决的任务。</p><blockquote><p>我们在第一节中提到的 TestAgent 测试电商网站登录功能的任务，就属于 <em>长程任务</em>。</p></blockquote><p><em>长程任务</em> 不仅仅指“执行时间长”，更强调执行步骤多、步骤之间依赖性强、执行的状态空间大，其核心特征如下：</p><ol><li><p>多步推理与决策（<em>Multi-step Reasoning &amp; Decision Making</em>）：Agent 需要生成一个包含 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>T</mi></mrow><annotation encoding="application/x-tex">T</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.1389em;">T</span></span></span></span> 个步骤的计划（<em>Plan</em>），即 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>a</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>a</mi><mn>2</mn></msub><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><msub><mi>a</mi><mi>T</mi></msub></mrow><annotation encoding="application/x-tex">a_1, a_2, \dots, a_T</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner">…</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.1389em;">T</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>。</p></li><li><p>延迟奖励（<em>Delayed Sparse Rewards</em>）：在大多数中间步骤中，Agent 往往得不到及时的、明确的正反馈（<em>Reward</em>）。只有当整条链路执行完毕且成功时，才能判定任务完成。这意味着 Agent 需要具备很强的“自纠错能力”、“自决策能力”以及“规划能力”。</p></li><li><p>时序依赖性（<em>Temporal Dependency</em>）：第 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow><annotation encoding="application/x-tex">t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6151em;"></span><span class="mord mathnormal">t</span></span></span></span> 步的动作 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>a</mi><mi>t</mi></msub></mrow><annotation encoding="application/x-tex">a_t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 严格依赖于第 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi><mo>−</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">t-1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6984em;vertical-align:-0.0833em;"></span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1</span></span></span></span> 步的执行结果（<em>State</em>）<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>s</mi><mrow><mi>t</mi><mo>−</mo><mn>1</mn></mrow></msub></mrow><annotation encoding="application/x-tex">s_{t-1}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6389em;vertical-align:-0.2083em;"></span><span class="mord"><span class="mord mathnormal">s</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">t</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2083em;"><span></span></span></span></span></span></span></span></span></span>。一旦中间某一步出现异常，后续的所有步骤都可能失效，从而导致误差累积。</p></li><li><p>动态环境交互（<em>Dynamic Environment Interaction</em>）：Agent 需要不断观察环境的即时反馈来调整下一步动作，而不是盲目执行预设的静态脚本。</p></li></ol><p>因此，Agent 在面对 <em>长程任务</em> 时，常常面临着如下的困难：</p><ul><li>记忆负担：随着执行步骤 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>T</mi></mrow><annotation encoding="application/x-tex">T</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.1389em;">T</span></span></span></span> 的不断增加，历史轨迹会不断变长，历史信息也会不断增加，Agent 容易因为 <em>Context Rot</em> 现象而忘记最开始的任务目标。</li><li>误差滚雪球：如果 Agent 在第 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>a</mi><mn>5</mn></msub></mrow><annotation encoding="application/x-tex">a_5</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">5</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 时产生幻觉（<em>Hallucination</em>）或执行错误，但是由于没有及时纠正，直到第 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>a</mi><mn>25</mn></msub></mrow><annotation encoding="application/x-tex">a_{25}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">25</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 时，整个任务才彻底失败。假设每一步成功率是 95%，执行 20 步后的成功率仅为 $ 0.95^{20} ≈ 35% $。</li></ul><p><img src="/2025/11/29/context-engineering-for-AI-agents/17642216363819.jpg" alt=""></p><h2 id="4-context-engineering">4. Context Engineering</h2><p><em>上下文工程</em> 就是在大语言模型（LLMs）的 <em>有限注意力预算</em>（<em>finite attention budget</em>）的限制下，精心筛选并找到最小的 <em>high-signal tokens</em> 集合，以最大限度地提高实现某种预期结果的可能性。正如 Anthropic 在 <em>Effective context engineering for AI agents</em><sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup> 中说的那样：</p><blockquote><p>我们对于上下文的不同组成部分（系统提示、工具、示例、消息历史等）的总体指导原则是：要深思熟虑，确保上下文的内容即丰富又简洁。</p><p>Our overall guidance across the different components of context (system prompts, tools, examples, message history, etc) is to be thoughtful and keep your context informative, yet tight. Now let’s dive into dynamically retrieving context at runtime.</p></blockquote><ul><li>令 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="script">U</mi></mrow><annotation encoding="application/x-tex">\mathcal{U}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathcal" style="margin-right:0.0993em;">U</span></span></span></span> 表示所有可用的、潜在上下文中的 tokens 的全集（即原始、未过滤的“信息池”）。</li><li>令 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi></mrow><annotation encoding="application/x-tex">S</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.0576em;">S</span></span></span></span> 表示通过上下文工程筛选出的 tokens 子集，满足 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi><mo>⊆</mo><mi mathvariant="script">U</mi></mrow><annotation encoding="application/x-tex">S \subseteq \mathcal{U}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8193em;vertical-align:-0.136em;"></span><span class="mord mathnormal" style="margin-right:0.0576em;">S</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">⊆</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathcal" style="margin-right:0.0993em;">U</span></span></span></span>。</li><li>令 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∣</mi><mi>S</mi><mi mathvariant="normal">∣</mi></mrow><annotation encoding="application/x-tex">|S|</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">∣</span><span class="mord mathnormal" style="margin-right:0.0576em;">S</span><span class="mord">∣</span></span></span></span> 表示集合 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi></mrow><annotation encoding="application/x-tex">S</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.0576em;">S</span></span></span></span> 中的 tokens 的数量。</li><li>令 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi></mrow><annotation encoding="application/x-tex">B</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.0502em;">B</span></span></span></span> 表示“有限注意力预算（<em>finite attention budget</em>）”，即模型能有效处理的最大 token 数量。</li><li>令 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>θ</mi></mrow><annotation encoding="application/x-tex">\theta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span></span></span></span> 表示大语言模型（LLM）的固定参数。</li><li>令 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>Y</mi><mo>∗</mo></msup></mrow><annotation encoding="application/x-tex">Y^*</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6887em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.2222em;">Y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span></span></span></span> 表示“期望输出”，即目标输出字符串或答案。</li><li>令 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>P</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msup><mi>Y</mi><mo>∗</mo></msup><mi mathvariant="normal">∣</mi><mi>S</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">P_{\theta}(Y^*|S)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.1389em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em;">θ</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.2222em;">Y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span><span class="mord">∣</span><span class="mord mathnormal" style="margin-right:0.0576em;">S</span><span class="mclose">)</span></span></span></span> 表示模型计算结果的概率：在给定所选上下文 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi></mrow><annotation encoding="application/x-tex">S</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.0576em;">S</span></span></span></span> 的条件下，生成期望输出 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>Y</mi><mo>∗</mo></msup></mrow><annotation encoding="application/x-tex">Y^*</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6887em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.2222em;">Y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6887em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span></span></span></span> 的条件概率。</li></ul><p>因此，可以用如下的数学语言来表示 <em>上下文工程</em> 的含义：</p><p class="katex-block has-jax"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mtable rowspacing="0.25em" columnalign="right left" columnspacing="0em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mtext>ContextEngineering</mtext><mo>→</mo><msub><mi mathvariant="script">S</mi><mrow><mi>o</mi><mi>p</mi><mi>t</mi></mrow></msub><mo>=</mo><mi><munder><mo><mi mathvariant="normal">argmin</mi><mo>⁡</mo></mo><mrow><mi>S</mi><mo>∈</mo><msup><mi mathvariant="script">S</mi><mo lspace="0em" rspace="0em">∗</mo></msup></mrow></munder></mi><mi mathvariant="normal">∣</mi><mi>S</mi><mi mathvariant="normal">∣</mi></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mtext>for</mtext><mspace width="1em"></mspace><msup><mi mathvariant="script">S</mi><mo>∗</mo></msup><mo>=</mo><mi><munder><mo><mi mathvariant="normal">argmax</mi><mo>⁡</mo></mo><mrow><mi>S</mi><mo>⊆</mo><mi mathvariant="script">U</mi></mrow></munder></mi><mrow><mo fence="true">{</mo><msub><mi>P</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msup><mi>Y</mi><mo>∗</mo></msup><mi mathvariant="normal">∣</mi><mi>S</mi><mo stretchy="false">)</mo><mo fence="true">}</mo></mrow></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mspace width="1em"></mspace><mspace width="1em"></mspace><mtext>s.t.</mtext><mspace width="1em"></mspace><mi mathvariant="normal">∣</mi><mi>S</mi><mi mathvariant="normal">∣</mi><mo>≤</mo><mi>B</mi></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd></mtr></mtable><annotation encoding="application/x-tex">\begin{aligned}&amp; \text{ContextEngineering} \rightarrow  \mathcal{S}_{opt} = \underset{S \in \mathcal{S}^{*}}{\operatorname{argmin}} |S|  \\&amp; \\&amp; \text{for} \quad \mathcal{S}^* = \underset{S \subseteq \mathcal{U}}{\operatorname{argmax}} \left\{ P_{\theta}(Y^*|S) \right\} \\&amp; \quad \quad \text{s.t.} \quad |S| \leq B \\&amp; \\\end{aligned}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:8.4801em;vertical-align:-3.99em;"></span><span class="mord"><span class="mtable"><span class="col-align-r"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:4.49em;"><span style="top:-6.49em;"><span class="pstrut" style="height:2.84em;"></span><span class="mord"></span></span><span style="top:-4.3839em;"><span class="pstrut" style="height:2.84em;"></span><span class="mord"></span></span><span style="top:-2.8839em;"><span class="pstrut" style="height:2.84em;"></span><span class="mord"></span></span><span style="top:-0.71em;"><span class="pstrut" style="height:2.84em;"></span><span class="mord"></span></span><span style="top:0.79em;"><span class="pstrut" style="height:2.84em;"></span><span class="mord"></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:3.99em;"><span></span></span></span></span></span><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:4.49em;"><span style="top:-6.65em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mord text"><span class="mord">ContextEngineering</span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord"><span class="mord mathcal" style="margin-right:0.075em;">S</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em;"><span style="top:-2.55em;margin-left:-0.075em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">o</span><span class="mord mathnormal mtight">pt</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord"><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-2.1612em;margin-left:0em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0576em;">S</span><span class="mrel mtight">∈</span><span class="mord mtight"><span class="mord mathcal mtight" style="margin-right:0.075em;">S</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6183em;"><span style="top:-2.786em;margin-right:0.0714em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord mtight">∗</span></span></span></span></span></span></span></span></span></span></span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span><span class="mop"><span class="mop"><span class="mord mathrm">argmin</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.9661em;"><span></span></span></span></span></span></span><span class="mord">∣</span><span class="mord mathnormal" style="margin-right:0.0576em;">S</span><span class="mord">∣</span></span></span><span style="top:-4.5439em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span></span></span><span style="top:-3.0439em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mord text"><span class="mord">for</span></span><span class="mspace" style="margin-right:1em;"></span><span class="mord"><span class="mord mathcal" style="margin-right:0.075em;">S</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7387em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord"><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.4306em;"><span style="top:-2.1612em;margin-left:0em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0576em;">S</span><span class="mrel mtight">⊆</span><span class="mord mathcal mtight" style="margin-right:0.0993em;">U</span></span></span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span><span class="mop"><span class="mop"><span class="mord mathrm">argmax</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.0339em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;">{</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1389em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.1389em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em;">θ</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.2222em;">Y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7387em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span></span></span></span></span><span class="mord">∣</span><span class="mord mathnormal" style="margin-right:0.0576em;">S</span><span class="mclose">)</span><span class="mclose delimcenter" style="top:0em;">}</span></span></span></span><span style="top:-0.87em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:1em;"></span><span class="mspace" style="margin-right:1em;"></span><span class="mord text"><span class="mord">s.t.</span></span><span class="mspace" style="margin-right:1em;"></span><span class="mord">∣</span><span class="mord mathnormal" style="margin-right:0.0576em;">S</span><span class="mord">∣</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal" style="margin-right:0.0502em;">B</span></span></span><span style="top:0.63em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:3.99em;"><span></span></span></span></span></span></span></span></span></span></span></span></p><p>上下文工程是即是一门艺术、又是一门科学，上下文工程指导我们从不断演变的海量信息中精心筛选出所需要的信息，并将这些信息放入有限的上下文窗口中，从而让 Agent 产出我们最希望的结果。</p><h3 id="4-1-上下文工程与提示词工程之间的区别">4.1 上下文工程与提示词工程之间的区别</h3><p>在使用大型语言模型（LLMs）进行 AI 原生应用开发的早期阶段，编写并优化提示词是工程开发工作中的最主要的部分。</p><ul><li>2024 年，业界提供了各种不同的提示词模版以优化大模型的推理结果，例如 OpenAI 在 <em>Prompt engineering</em><sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup> 中，对如何组织提示词做了详尽的说明。</li><li>2024 年，提示词「神人」李继刚也爆红网络，他写的提示词在各大 AI 社群和提示词网站广为流传。</li><li>同时，为了提升提示词工程的效率，寻找在当前模型下最优的提示词，提示词的快速评估工作也如火如荼。</li></ul><p>提示词工程的核心主要在于如何编写有效的提示词（尤其是系统提示词）以让大模型能够准确的理解用户的意图，并输出用户期望的结果。然而，话术的优化仅限于精准描述与定义问题，这中优化在 LLM ChatBot 时代能够非常好的解决人与大模型沟通的问题。</p><p>但是，对于 <em>长程任务</em> 而言，仅仅优化话术还是不够的，此时必须为大模型提供更多的额外信息（例如：系统指令、工具、模型上下文协议（MCP）、外部数据、消息历史等）才能保证大模型产出我们希望的结果。</p><p><img src="/2025/11/29/context-engineering-for-AI-agents/17642429672199.jpg" alt=""></p><p>因此，从本质上讲：提示词工程是单次的话术优化，而上下文工程是迭代中的信息筛选，每当我们需要决定向模型传递什么内容时，筛选就会发生，上下文工程就会存在。</p><h2 id="5-context-engineering-in-action">5. Context Engineering in Action</h2><p>为了让 Agent 能够在 <em>长程任务</em> 上高效工作，Anthropic 开发了多种技术来解决 Agent 在面对 <em>长程任务</em> 时所面临的挑战，这些技术包括：压缩（<em>compaction</em>）、结构化笔记（<em>structured note-taking</em>）、多 Agent 架构（<em>multi-agent architectures</em>）。</p><h3 id="5-1-compaction">5.1 Compaction</h3><p><em>压缩</em> 是指当对话接近上下文窗口限制时，对其内容进行总结，并利用该总结重新开启一个新的上下文窗口的做法。<em>压缩</em> 通常是上下文工程中提升长期连贯性的首要手段。从本质上讲，<em>压缩</em> 以高保真的方式提炼上下文窗口的内容，使智能体能够在性能下降最小的情况下继续运行。</p><p><em>压缩</em> 的艺术在于选择保留什么信息以及决策舍弃什么信息。过度激进的压缩策略会导致那些细微但关键的上下文信息丢失，而这些信息的重要性往往只有在 Agent 执行到后面的时候才会显现出来。</p><p>当构建压缩系统时，我们必须在复杂的 Agent 执行轨迹上仔细调整我们的提示词（<em>prompt</em>）。在 <em>压缩</em> 过程中：</p><ul><li>我们必须首先保证最大限度地提高召回率（<em>recall</em>），以确保压缩提示词能从大量的轨迹信息中捕捉到每一个相关信息；</li><li>然后再通过多次迭代来删除多余的提示词内容，从而不断提高精确率（<em>precision</em>）。</li></ul><p>实际上，工具结果清理是最安全、最轻量级的压缩形式之一——确实，Agent 一旦调用过某个工具并得到了结果，在后续的执行中，Agent 为什么还需要再次查看这次工具调用的原始结果呢？在 <em>Managing context on the Claude Developer Platform</em><sup class="footnote-ref"><a href="#fn5" id="fnref5">[5]</a></sup> 这篇文章中，Anthropic 把工具清理作为特性集成到了Claude 开发者平台。</p><blockquote><p>Today, we’re introducing new capabilities for managing your agents’ context on the Claude Developer Platform: context editing and the memory tool.</p><p>With our latest model, Claude Sonnet 4.5, these capabilities enable developers to build AI agents capable of handling long-running tasks at higher performance and without hitting context limits or losing critical information.</p></blockquote><p>当 Agent 经过多轮的工具调用后，此时累积的历史信息导致提示词的数量已经接近大模型的上下文窗口大小限制。此时，<em>context editing</em> 会自动从上下文窗口中清除过时的工具调用和结果，从而可以在保留对话流程的同时移除过时内容，进而有效延长 Agent 在无需人工干预的情况下的运行时间。</p><p><img src="/2025/11/29/context-engineering-for-AI-agents/17642271057749.jpg" alt=""></p><h3 id="5-2-structured-note-taking">5.2 Structured Note-Taking</h3><p><em>笔记</em>（<em>note-taking</em>）与 <em>原始信息</em>（<em>raw information</em>）之间的核心区别，本质上是 <strong>认知加工（<em>Cognitive Processing</em>）</strong> 的介入程度。<em>原始信息</em> 是矿石——包含金子，但同时也包含了大量的泥土、杂质和石头）；而 <em>笔记</em> 是提炼后的金块——去除了杂质，改变了形态，并且还会打上了所有者的印记。从另一个角度讲：<em>笔记</em> 是我们思维的快照，具备一定的主观和个性化，是构建自己思维体系的有效形式之一。</p><p><img src="/2025/11/29/context-engineering-for-AI-agents/4792b51210851a049a66959f577b0dd0.jpg" alt=""></p><p>在 Agent 领域，<em>结构化笔记</em>——又称为 <em>Agent 记忆</em>——是一种 Agent 定期把 <em>笔记</em>（<em>note</em>）写入大模型上下文窗口之外的持久性存储介质中的技术。在 Agent 的后续执行过程中，Agent 会再次从这些持久性存储介质中把这些 <em>笔记</em> 拉回到上下文窗口。</p><p>利用 <em>结构化笔记</em>，我们能够以最小的开销为 Agent 提供持久记忆能力，从而允许 Agent 在 <em>长程任务</em> 中可以跟踪结果与进度、保留关键的上下文信息与依赖关系，以避免 Agent 在多次迭代之后遗忘原始任务目标的问题。</p><blockquote><p>我们已经走得太远，以至于忘记了为什么而出发。——纪伯伦</p></blockquote><p>在上下文重置后，Agent 会通过阅读之前的 <em>结构化笔记</em>，并继续进行任务执行与探索，这在仅依靠大语言模型（LLM）的上下文窗口来存储所有信息的场景下，是无法实现的。</p><p>同时，<em>结构化笔记</em> 还使得 Agent 能够随着时间的推移而建立自己的知识库，并在不同会话中保持项目状态，新启动的 Agent 也无需重新梳理所有的原始信息就可以学习到之前的、持久保存的知识。另外，对于多 Agent 架构而言，统一的 <em>Agent 记忆</em> 也会解决 Agent 之间的协同问题，Agent 之间不需要通过冗余而又复杂的信息的传输、而是通过共享的 <em>Agent 记忆</em> 来保持统一的系统状态。</p><h3 id="5-3-multi-agent-architectures">5.3 Multi-Agent Architectures</h3><p>多 Agent 架构是解决上下文限制的另一种有效的技术思路。在解决 <em>长程任务</em> 时，我们不再依赖单个Agent 来维持整个任务的状态，而是把任务进行拆分，并且由专门的 Sub-Agent 来处理特定的子任务。</p><p><img src="/2025/11/29/context-engineering-for-AI-agents/17642344199896.jpg" alt=""></p><p>在 Sub-Agent 处理子任务的时候，这些 Sub-Agent 会具有清晰的上下文窗口，从而避免了 <em>Context Rot</em> 的问题。</p><p>在 多 Agent 架构下，Lead-Agent 主要是在更高层级上对计划进行协调，而 Sub-Agent 负责具体执行更专精的技术工作或使用工具查找相关信息。每个 Sub-Agent 还可能会进行广泛的探索，使用数万个甚至更多的 tokens，但只给 Lead-Agent 返回最精简的、提炼后的摘要信息（通常为1000-2000个标记）作为执行结果。</p><p>在 <em>How we built our multi-agent research system</em><sup class="footnote-ref"><a href="#fn6" id="fnref6">[6]</a></sup> 中，Anthropic 提到，在复杂研究任务上，多 Agent 架构比单 Agent 系统有显著的改进。</p><h2 id="6-context-engineering-白皮书">6. Context Engineering 白皮书</h2><p>2025 年 11 月 16 日，谷歌发布了 <em>Context Engineering 白皮书</em><sup class="footnote-ref"><a href="#fn7" id="fnref7">[7]</a></sup>，白皮书中认为：Agent 真正的“智能”，并非仅仅源于底层模型，更重要还在于开发者如何为其“组装”上下文。</p><blockquote><p>The journey from a simple conversational turn to a piece of persistent, actionable intelligence is governed by this practice, which involves dynamically assembling all necessary information—including conversation history, memories, and external knowledge—into the LLM’s context window.</p><p>This entire process relies on the interplay between two distinct but interconnected systems: the immediate Session and the long-term Memory.</p></blockquote><p>谷歌在白皮书中详细介绍了现在 Agent 的两大基础：会话（<em>Sessions</em>） 与 记忆（<em>Memory</em>），同时也对构建 Agent 的众多底层基础设施的工程实践做了详细的探讨：</p><ul><li>压缩策略（<em>Compaction Strategies</em>）：如何让信息更紧凑。</li><li>数据溯源（<em>Provenance Tracking</em>）：知道每一条信息的来源。</li><li>记忆提取与整合工作流（<em>Extraction &amp; Consolidation</em>）：从对话中提炼价值。</li><li>检索评分（<em>Retrieval Scoring</em>）：决定提取哪段记忆的算法。</li><li>后台处理（<em>Background Processing</em>）：异步处理繁重的记忆整理工作。</li><li>多 Agent 互操作性（<em>Multi-agent Interoperability</em>）：通过共享的、与框架无关的记忆层，实现不同 Agent 之间的协作。</li></ul><p>2025 年 11 月 19 日，谷歌发布了 Gemini3，在 Gemini3 的开发文档 <em>Gemini 3 Developer Guide</em><sup class="footnote-ref"><a href="#fn8" id="fnref8">[8]</a></sup> 中，我们发现了如下的内容：</p><blockquote><p>To bypass strict validation in these specific scenarios, populate the field with this specific dummy string:</p><p>“thoughtSignature”: “context_engineering_is_the_way_to_go”</p></blockquote><p>由此，也能看出谷歌对于 <em>Context Engineering</em> 是多么的痴迷。</p><p>2025 年 11 月 7 日，谷歌发表了一篇被称为 <em>Attention is all you need (V2)</em> 的论文 <em>Introducing Nested Learning: A new ML paradigm for continual learning</em><sup class="footnote-ref"><a href="#fn9" id="fnref9">[9]</a></sup>，论文提出了 <em>Nested Learning</em> 框架，从而让模型拥有了自我修改权重与多时间尺度连续记忆的能力，从而也可以很少的解决 <em>Context Rot</em> 问题。在 <em>Nested Learning</em> 框架下，当我们和大语言模型（<em>LLM</em>）对话的时候：</p><ul><li>模型的高频层，在飞速处理我们所说的每个词，理解我们的意图，生成回复，这部分记忆是临时的，对话结束可能就忘了。</li><li>模型的中频层则在以一个稍慢的速度，分析整个对话的主题、我们的情绪、知识盲区，试图形成一个关于这次互动的概要记忆。</li><li>模型的低频层则更慢，它整合过去一段时间里，跟我们的所有互动，模型可能会发现：“哦，这个用户最近总是在问关于古典音乐的问题，而且他似乎对巴赫特别感兴趣，是时候把‘该用户是古典音乐爱好者’这个标签存入关于他的长期档案里了”。</li></ul><p>与上下文工程不同的是，所有的这些记忆不再依赖于上下文窗口而存在，而是通过修改模型的权重直接存储于模型之中。目前看来，在线修改模型的权重，依然是一个非常复杂且昂贵的过程。因此，我仍旧认为，在可遇见的一段时间内，上下文工程依然是提升 Agent 在 <em>长程任务</em> 上执行能力的最有效手段。</p><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">后记</p><p>当我在研究 <em>Context Engineering</em> 的时候，恰好在工作中遇到了一件事情，这件事情让我深刻体会到了 <em>Context Engineering</em> 的强大。对于同一个原始任务，我和同事对比了两种不同的上下文构建策略构建出的上下文的结果差异，结果发现，精心筛选并组织的上下文，竟然让 Agent 的效果跨越了一个层级！真的令人惊艳，又让我更加痴迷于对 <em>Context Engineering</em> 的研究。</p><p>当我在研究 <em>Context Engineering</em> 的时候，恰好是谷歌发布 <em>Gemini3</em> 与 <em>Nano Banana Pro</em> 的时间，我们第一时间对这两个模型做了深入、全面的评测，正如 <em><a href="https://lmarena.ai/leaderboard/">LMArena</a></em> 榜单上显示的那样，<em>Gemini3</em> 与 <em>Nano Banana Pro</em> 的表现确实非常亮眼。</p><p>得益于 <em>Gemini3</em> 与 <em>Nano Banana Pro</em> 的强大能力，我的<em>Context Engineering</em> 学习之旅也方便了很多，我不停的和 <em>Gemini3</em> 进行对话，询问它关于 <em>Context Engineering</em> 的各种问题，真的是收益匪浅。我还用 <em>Nano Banana Pro</em> 帮我生成了这篇文章中的所有漫画插图，效果真是令人惊艳。</p></div><h2 id="参考文献">参考文献</h2><hr class="footnotes-sep"><section class="footnotes"><ol class="footnotes-list"><li id="fn1" class="footnote-item"><p><a href="https://www.anthropic.com/engineering/building-effective-agents">Building effective agents</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p></li><li id="fn2" class="footnote-item"><p><a href="https://research.trychroma.com/context-rot">Context Rot: How Increasing Input Tokens Impacts LLM Performance</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p></li><li id="fn3" class="footnote-item"><p><a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents">Effective context engineering for AI agents</a> <a href="#fnref3" class="footnote-backref">↩︎</a></p></li><li id="fn4" class="footnote-item"><p><a href="https://platform.openai.com/docs/guides/prompt-engineering">Prompt engineering</a> <a href="#fnref4" class="footnote-backref">↩︎</a></p></li><li id="fn5" class="footnote-item"><p><a href="https://claude.com/blog/context-management">Managing context on the Claude Developer Platform</a> <a href="#fnref5" class="footnote-backref">↩︎</a></p></li><li id="fn6" class="footnote-item"><p><a href="https://www.anthropic.com/engineering/multi-agent-research-system">How we built our multi-agent research system</a> <a href="#fnref6" class="footnote-backref">↩︎</a></p></li><li id="fn7" class="footnote-item"><p><a href="https://www.kaggle.com/whitepaper-context-engineering-sessions-and-memory">Context Engineering: Sessions &amp; Memory</a> <a href="#fnref7" class="footnote-backref">↩︎</a></p></li><li id="fn8" class="footnote-item"><p><a href="https://ai.google.dev/gemini-api/docs/gemini-3?thinking=high">Gemini 3 Developer Guide</a> <a href="#fnref8" class="footnote-backref">↩︎</a></p></li><li id="fn9" class="footnote-item"><p><a href="https://research.google/blog/introducing-nested-learning-a-new-ml-paradigm-for-continual-learning/">Introducing Nested Learning: A new ML paradigm for continual learning</a> <a href="#fnref9" class="footnote-backref">↩︎</a></p></li></ol></section></body></html>]]>
      </content:encoded>
    </item>
    <item>
      <title>如何根据历史胜负数据估计 Elo 打分？</title>
      <link>https://wangwei1237.github.io/2025/11/15/How-to-estimate-the-winning-rate-based-on-historical-match-results/</link>
      <description>
        <![CDATA[<html><head></head><body><p>最近，Grok4.1 和 Gemini3 模型的发布都提到了人类偏好打分平台——LMArena 大模型竞技场：</p>
<ul>
<li>在 LMArena 的 Text Arena 排行榜上，Grok 4.1 的推理模式（代号：quasarflux）以 1483 的 Elo 分数位居总榜首位，领先最高的非 xAI 模型整整 31 分<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>。</li>
<li>刚一发布，Gemini 3就几乎屠榜所有评测集，以1501 Elo得分位列LMArena大模型竞技场第一<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>。</li>
</ul>
<p>通过对 LMArena 的榜单进行仔细分析，我们发现，不同于我们在 <a href="/2023/08/12/the-interesting-elo-rating-system/">有趣的 Elo 积分系统</a> 中介绍的实时更新的 Elo 分数，LMArena 的 Elo 分数还提供了 95% 置信区间的估计，同时还提供了模型上下限能力的排序。</p>
<p>这种新颖的打分、排序方式引起了我们的兴趣，于是我们决定深入研究 LMArena 的 Elo 评分系统，了解其背后的原理和方法，从而可以对 LMArena 的榜单有更深入的理解。</p>
<p><img src="/2025/11/15/How-to-estimate-the-winning-rate-based-on-historical-match-results/1.png" alt=""></p></body></html>]]>
      </description>
      <author>Wang Wei</author>
      <category domain="https://wangwei1237.github.io/categories/LLM/">LLM</category>
      <category domain="https://wangwei1237.github.io/tags/Elo/">Elo</category>
      <category domain="https://wangwei1237.github.io/tags/Bradley-Terry/">Bradley-Terry</category>
      <category domain="https://wangwei1237.github.io/tags/Pairwise-Comparisons/">Pairwise Comparisons</category>
      <pubDate>Sat, 15 Nov 2025 16:20:21 GMT</pubDate>
      <content:encoded>
        <![CDATA[<html><head></head><body><p>最近，Grok4.1 和 Gemini3 模型的发布都提到了人类偏好打分平台——LMArena 大模型竞技场：</p><ul><li>在 LMArena 的 Text Arena 排行榜上，Grok 4.1 的推理模式（代号：quasarflux）以 1483 的 Elo 分数位居总榜首位，领先最高的非 xAI 模型整整 31 分<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>。</li><li>刚一发布，Gemini 3就几乎屠榜所有评测集，以1501 Elo得分位列LMArena大模型竞技场第一<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>。</li></ul><p>通过对 LMArena 的榜单进行仔细分析，我们发现，不同于我们在 <a href="/2023/08/12/the-interesting-elo-rating-system/">有趣的 Elo 积分系统</a> 中介绍的实时更新的 Elo 分数，LMArena 的 Elo 分数还提供了 95% 置信区间的估计，同时还提供了模型上下限能力的排序。</p><p>这种新颖的打分、排序方式引起了我们的兴趣，于是我们决定深入研究 LMArena 的 Elo 评分系统，了解其背后的原理和方法，从而可以对 LMArena 的榜单有更深入的理解。</p><p><img src="/2025/11/15/How-to-estimate-the-winning-rate-based-on-historical-match-results/1.png" alt=""></p><span id="more"></span><h2 id="成对打分">成对打分</h2><p>在 LMArena 平台，对于同一个 Query，平台会给出两个匿名模型的结果，然后让用户根据自己的偏好对这两个匿名模型的结果进行投票。</p><p><img src="/2025/11/15/How-to-estimate-the-winning-rate-based-on-historical-match-results/2.png" alt=""></p><p>如果不考虑 <em>两个模型差不多</em> 和 <em>两个模型都很差</em> 这两种情况，那么用户的每次投票都可以看作是对两个模型进行的一次成对比较（<em>Pairwise Comparison</em>）打分，要么 A 胜 B，要么 B 胜 A。因此，每一次投票，都可以得到如下的数据：</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">winner,loser</span><br><span class="line">model_A,model_B</span><br><span class="line">model_B,model_C</span><br><span class="line">……</span><br></pre></td></tr></tbody></table></figure><p>在 <em>Chatbot Arena: An Open Platform for Evaluating LLMs by Human Preference</em> <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup> 的第 4 节中提到：如何根据胜负数据对模型的能力进行排序是排序领域中需要深入研究的一个课题。</p><blockquote><p>ur data consists of pairwise comparisons—but how can we use these comparisons to recover a ranking over all M models? This is a well-studied topic in the literature on learning to rank (Liu et al., 2009), and we present our perspective here.</p></blockquote><p>在 LMArena 的 <em>Statistical Extensions of the Bradley-Terry and Elo Models</em> <sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup>这篇博客中，作者提到 LMArena 是通过 Bradley-Terry 模型来估计模型的真实能力。</p><blockquote><p>Based on our previous posts, Chatbot Arena uses the Bradley-Terry model for the purposes of statistical inference on the model strength.</p></blockquote><h2 id="bradley-terry-模型">Bradley-Terry 模型</h2><p>Bradley-Terry<sup class="footnote-ref"><a href="#fn5" id="fnref5">[5]</a></sup> 模型是一种统计模型，用于根据成对比较的结果来估计参与者的相对实力。Bradley-Terry 模型由 Bradley 和 Terry 在 1952 年提出，并且广泛应用于体育竞技、心理学实验和机器学习模型评估等领域。</p><p>Bradley-Terry 模型假设每个参与者（大模型）都有一个潜在的“能力参数” <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>θ</mi></mrow><annotation encoding="application/x-tex">\theta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span></span></span></span>，模型 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>i</mi></mrow><annotation encoding="application/x-tex">i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6595em;"></span><span class="mord mathnormal">i</span></span></span></span> 战胜模型 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>j</mi></mrow><annotation encoding="application/x-tex">j</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.854em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.0572em;">j</span></span></span></span> 的概率为：</p><p class="katex-block has-jax"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>P</mi><mo stretchy="false">(</mo><mi>i</mi><mtext>&nbsp;</mtext><mi>b</mi><mi>e</mi><mi>a</mi><mi>t</mi><mi>s</mi><mtext>&nbsp;</mtext><mi>j</mi><mo stretchy="false">)</mo><mo>=</mo><mfrac><msub><mi>θ</mi><mi>i</mi></msub><mrow><msub><mi>θ</mi><mi>i</mi></msub><mo>+</mo><msub><mi>θ</mi><mi>j</mi></msub></mrow></mfrac></mrow><annotation encoding="application/x-tex">P(i\ beats\ j) = \frac{\theta_i}{\theta_i + \theta_j}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.1389em;">P</span><span class="mopen">(</span><span class="mord mathnormal">i</span><span class="mspace">&nbsp;</span><span class="mord mathnormal">b</span><span class="mord mathnormal">e</span><span class="mord mathnormal">a</span><span class="mord mathnormal">t</span><span class="mord mathnormal">s</span><span class="mspace">&nbsp;</span><span class="mord mathnormal" style="margin-right:0.0572em;">j</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:2.3435em;vertical-align:-0.9721em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3714em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0572em;">j</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.9721em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span></p><p>上述公式有如下的重要特性：</p><ul><li>对称性：如果 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>θ</mi><mi>i</mi></msub><mo>=</mo><msub><mi>θ</mi><mi>j</mi></msub></mrow><annotation encoding="application/x-tex">\theta_i = \theta_j</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.9805em;vertical-align:-0.2861em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0572em;">j</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span></span>，则两个模型各有 50% 的获胜概率</li><li>传递性：如果 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>i</mi></mrow><annotation encoding="application/x-tex">i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6595em;"></span><span class="mord mathnormal">i</span></span></span></span> 强于 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>j</mi></mrow><annotation encoding="application/x-tex">j</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.854em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.0572em;">j</span></span></span></span>，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>j</mi></mrow><annotation encoding="application/x-tex">j</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.854em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.0572em;">j</span></span></span></span> 强于 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.0315em;">k</span></span></span></span>，则 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>i</mi></mrow><annotation encoding="application/x-tex">i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6595em;"></span><span class="mord mathnormal">i</span></span></span></span> 强于 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.0315em;">k</span></span></span></span></li><li>可解释性：能力参数的比值直接对应获胜概率的几率比</li></ul><p>Bradley-Terry 模型最常见的应用就是在给定一组&nbsp;<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>i</mi><mo>&gt;</mo><mi>j</mi></mrow><annotation encoding="application/x-tex">i &gt; j</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6986em;vertical-align:-0.0391em;"></span><span class="mord mathnormal">i</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.854em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.0572em;">j</span></span></span></span> 的比赛结果下，推断潜在的能力参数&nbsp;<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>θ</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">\theta_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>&nbsp;的值。采用最大似然估计法来估计 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>θ</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">\theta_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 的值是最简单的方法——即通过估计的参数来实现模型的输出结果与实际观察到的比赛结果最大程度的相似。</p><p>假设我们已经有了一份 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">n</span></span></span></span> 个选手比赛结果的数据，令&nbsp;<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>w</mi><mrow><mi>i</mi><mi>j</mi></mrow></msub></mrow><annotation encoding="application/x-tex">w_{ij}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7167em;vertical-align:-0.2861em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0269em;">w</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0269em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0572em;">ij</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span></span>&nbsp;表示 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>i</mi></mrow><annotation encoding="application/x-tex">i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6595em;"></span><span class="mord mathnormal">i</span></span></span></span>&nbsp;战胜 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>j</mi></mrow><annotation encoding="application/x-tex">j</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.854em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.0572em;">j</span></span></span></span>&nbsp;的次数。那么在 Bradley-Terry 模型中，这组结果的似然性为：</p><p class="katex-block has-jax"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>L</mi><mo stretchy="false">(</mo><msub><mi>θ</mi><mn>1</mn></msub><mo separator="true">,</mo><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mo separator="true">,</mo><msub><mi>θ</mi><mi>n</mi></msub><mo stretchy="false">)</mo><mo>=</mo><munder><mo>∏</mo><mrow><mi>i</mi><mi>j</mi></mrow></munder><msup><mrow><mo fence="true">[</mo><mi>P</mi><mo stretchy="false">(</mo><mi>i</mi><mo>&gt;</mo><mi>j</mi><mo stretchy="false">)</mo><mo fence="true">]</mo></mrow><msub><mi>w</mi><mrow><mi>i</mi><mi>j</mi></mrow></msub></msup><mo>=</mo><munder><mo>∏</mo><mrow><mi>i</mi><mi>j</mi></mrow></munder><msup><mrow><mo fence="true">[</mo><mfrac><msub><mi>θ</mi><mi>i</mi></msub><mrow><msub><mi>θ</mi><mi>i</mi></msub><mo>+</mo><msub><mi>θ</mi><mi>j</mi></msub></mrow></mfrac><mo fence="true">]</mo></mrow><msub><mi>w</mi><mrow><mi>i</mi><mi>j</mi></mrow></msub></msup></mrow><annotation encoding="application/x-tex">L(\theta_1,...,\theta_n) = \prod_{ij} \left[P(i &gt; j)\right]^{w_{ij}} = \prod_{ij} \left[\frac{\theta_i}{\theta_i + \theta_j}\right]^{w_{ij}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">L</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">...</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:2.4638em;vertical-align:-1.4138em;"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.05em;"><span style="top:-1.8723em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0572em;">ij</span></span></span></span><span style="top:-3.05em;"><span class="pstrut" style="height:3.05em;"></span><span><span class="mop op-symbol large-op">∏</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.4138em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner"><span class="minner"><span class="mopen delimcenter" style="top:0em;">[</span><span class="mord mathnormal" style="margin-right:0.1389em;">P</span><span class="mopen">(</span><span class="mord mathnormal">i</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal" style="margin-right:0.0572em;">j</span><span class="mclose">)</span><span class="mclose delimcenter" style="top:0em;">]</span></span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8043em;"><span style="top:-3.2029em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em;">w</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3281em;"><span style="top:-2.357em;margin-left:-0.0269em;margin-right:0.0714em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0572em;">ij</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2819em;"><span></span></span></span></span></span></span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:2.9181em;vertical-align:-1.4138em;"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.05em;"><span style="top:-1.8723em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0572em;">ij</span></span></span></span><span style="top:-3.05em;"><span class="pstrut" style="height:3.05em;"></span><span><span class="mop op-symbol large-op">∏</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.4138em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner"><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size3">[</span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3714em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0572em;">j</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.9721em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size3">]</span></span></span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:1.5043em;"><span style="top:-3.9029em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em;">w</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3281em;"><span style="top:-2.357em;margin-left:-0.0269em;margin-right:0.0714em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0572em;">ij</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2819em;"><span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></p><p>对上式取对数，得到对数似然函数：</p><p class="katex-block has-jax"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mtable rowspacing="0.25em" columnalign="right left" columnspacing="0em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mi>ln</mi><mo>⁡</mo><mi>L</mi><mo stretchy="false">(</mo><msub><mi>θ</mi><mn>1</mn></msub><mo separator="true">,</mo><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mo separator="true">,</mo><msub><mi>θ</mi><mi>n</mi></msub><mo stretchy="false">)</mo></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><mi>ln</mi><mo>⁡</mo><munder><mo>∏</mo><mrow><mi>i</mi><mi>j</mi><mo separator="true">,</mo><mi>i</mi><mo mathvariant="normal">≠</mo><mi>j</mi></mrow></munder><msup><mrow><mo fence="true">[</mo><mfrac><msub><mi>θ</mi><mi>i</mi></msub><mrow><msub><mi>θ</mi><mi>i</mi></msub><mo>+</mo><msub><mi>θ</mi><mi>j</mi></msub></mrow></mfrac><mo fence="true">]</mo></mrow><msub><mi>w</mi><mrow><mi>i</mi><mi>j</mi></mrow></msub></msup></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><munderover><mo>∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mi>n</mi></munderover><munderover><mo>∑</mo><mrow><mi>j</mi><mo>=</mo><mn>1</mn><mo separator="true">,</mo><mi>j</mi><mo mathvariant="normal">≠</mo><mi>i</mi></mrow><mi>n</mi></munderover><mrow><mi>ln</mi><mo>⁡</mo><msup><mrow><mo fence="true">[</mo><mfrac><msub><mi>θ</mi><mi>i</mi></msub><mrow><msub><mi>θ</mi><mi>i</mi></msub><mo>+</mo><msub><mi>θ</mi><mi>j</mi></msub></mrow></mfrac><mo fence="true">]</mo></mrow><msub><mi>w</mi><mrow><mi>i</mi><mi>j</mi></mrow></msub></msup></mrow></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><munder><mo>∑</mo><mrow><mi>i</mi><mi>j</mi><mo separator="true">,</mo><mi>i</mi><mo mathvariant="normal">≠</mo><mi>j</mi></mrow></munder><mrow><msub><mi>w</mi><mrow><mi>i</mi><mi>j</mi></mrow></msub><mi>ln</mi><mo>⁡</mo><mfrac><msub><mi>θ</mi><mi>i</mi></msub><mrow><msub><mi>θ</mi><mi>i</mi></msub><mo>+</mo><msub><mi>θ</mi><mi>j</mi></msub></mrow></mfrac></mrow></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><munder><mo>∑</mo><mrow><mi>i</mi><mi>j</mi><mo separator="true">,</mo><mi>i</mi><mo mathvariant="normal">≠</mo><mi>j</mi></mrow></munder><mrow><mo fence="true">[</mo><msub><mi>w</mi><mrow><mi>i</mi><mi>j</mi></mrow></msub><mi>ln</mi><mo>⁡</mo><mo stretchy="false">(</mo><msub><mi>θ</mi><mi>i</mi></msub><mo stretchy="false">)</mo><mo>−</mo><msub><mi>w</mi><mrow><mi>i</mi><mi>j</mi></mrow></msub><mi>ln</mi><mo>⁡</mo><mo stretchy="false">(</mo><msub><mi>θ</mi><mi>i</mi></msub><mo>+</mo><msub><mi>θ</mi><mi>j</mi></msub><mo stretchy="false">)</mo><mo fence="true">]</mo></mrow></mrow></mstyle></mtd></mtr></mtable><annotation encoding="application/x-tex">\begin{aligned}\ln L(\theta_1,...,\theta_n) &amp;= \ln \prod_{ij,i \ne j} \left[\frac{\theta_i}{\theta_i + \theta_j}\right]^{w_{ij}} \\&amp;= \sum_{i=1}^{n}\sum_{j=1,j \ne i}^{n} {\ln \left[\frac{\theta_i}{\theta_i + \theta_j}\right]^{w_{ij}}} \\&amp;= \sum_{ij,i \ne j} {w_{ij} \ln \frac{\theta_i}{\theta_i + \theta_j}} \\&amp;= \sum_{ij, i \ne j} \left[ w_{ij} \ln(\theta_i) - w_{ij} \ln(\theta_i + \theta_j)\right]\end{aligned}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:12.23em;vertical-align:-5.865em;"></span><span class="mord"><span class="mtable"><span class="col-align-r"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:6.365em;"><span style="top:-8.5121em;"><span class="pstrut" style="height:3.6514em;"></span><span class="mord"><span class="mop">ln</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">L</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">...</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span><span style="top:-5.1225em;"><span class="pstrut" style="height:3.6514em;"></span><span class="mord"></span></span><span style="top:-2.0128em;"><span class="pstrut" style="height:3.6514em;"></span><span class="mord"></span></span><span style="top:0.7754em;"><span class="pstrut" style="height:3.6514em;"></span><span class="mord"></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:5.865em;"><span></span></span></span></span></span><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:6.365em;"><span style="top:-8.5121em;"><span class="pstrut" style="height:3.6514em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mop">ln</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.05em;"><span style="top:-1.8479em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0572em;">ij</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">i</span><span class="mrel mtight"><span class="mrel mtight"><span class="mord vbox mtight"><span class="thinbox mtight"><span class="rlap mtight"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="inner"><span class="mord mtight"><span class="mrel mtight"></span></span></span><span class="fix"></span></span></span></span></span><span class="mspace nobreak mtight"></span><span class="mrel mtight">=</span></span><span class="mord mathnormal mtight" style="margin-right:0.0572em;">j</span></span></span></span><span style="top:-3.05em;"><span class="pstrut" style="height:3.05em;"></span><span><span class="mop op-symbol large-op">∏</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.4382em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner"><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size3">[</span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3714em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0572em;">j</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.9721em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size3">]</span></span></span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:1.5043em;"><span style="top:-3.9029em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em;">w</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3281em;"><span style="top:-2.357em;margin-left:-0.0269em;margin-right:0.0714em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0572em;">ij</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2819em;"><span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span><span style="top:-5.1225em;"><span class="pstrut" style="height:3.6514em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.6514em;"><span style="top:-1.8723em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.05em;"><span class="pstrut" style="height:3.05em;"></span><span><span class="mop op-symbol large-op">∑</span></span></span><span style="top:-4.3em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">n</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.2777em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.6514em;"><span style="top:-1.8479em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0572em;">j</span><span class="mrel mtight">=</span><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.0572em;">j</span><span class="mrel mtight"><span class="mrel mtight"><span class="mord vbox mtight"><span class="thinbox mtight"><span class="rlap mtight"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="inner"><span class="mord mtight"><span class="mrel mtight"></span></span></span><span class="fix"></span></span></span></span></span><span class="mspace nobreak mtight"></span><span class="mrel mtight">=</span></span><span class="mord mathnormal mtight">i</span></span></span></span><span style="top:-3.05em;"><span class="pstrut" style="height:3.05em;"></span><span><span class="mop op-symbol large-op">∑</span></span></span><span style="top:-4.3em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">n</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.4382em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mop">ln</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner"><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size3">[</span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3714em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0572em;">j</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.9721em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size3">]</span></span></span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:1.5043em;"><span style="top:-3.9029em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0269em;">w</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3281em;"><span style="top:-2.357em;margin-left:-0.0269em;margin-right:0.0714em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0572em;">ij</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2819em;"><span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span><span style="top:-2.0128em;"><span class="pstrut" style="height:3.6514em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.05em;"><span style="top:-1.8479em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0572em;">ij</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">i</span><span class="mrel mtight"><span class="mrel mtight"><span class="mord vbox mtight"><span class="thinbox mtight"><span class="rlap mtight"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="inner"><span class="mord mtight"><span class="mrel mtight"></span></span></span><span class="fix"></span></span></span></span></span><span class="mspace nobreak mtight"></span><span class="mrel mtight">=</span></span><span class="mord mathnormal mtight" style="margin-right:0.0572em;">j</span></span></span></span><span style="top:-3.05em;"><span class="pstrut" style="height:3.05em;"></span><span><span class="mop op-symbol large-op">∑</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.4382em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0269em;">w</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0269em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0572em;">ij</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop">ln</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3714em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0572em;">j</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.9721em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span><span style="top:0.7754em;"><span class="pstrut" style="height:3.6514em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.05em;"><span style="top:-1.8479em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0572em;">ij</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">i</span><span class="mrel mtight"><span class="mrel mtight"><span class="mord vbox mtight"><span class="thinbox mtight"><span class="rlap mtight"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="inner"><span class="mord mtight"><span class="mrel mtight"></span></span></span><span class="fix"></span></span></span></span></span><span class="mspace nobreak mtight"></span><span class="mrel mtight">=</span></span><span class="mord mathnormal mtight" style="margin-right:0.0572em;">j</span></span></span></span><span style="top:-3.05em;"><span class="pstrut" style="height:3.05em;"></span><span><span class="mop op-symbol large-op">∑</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.4382em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;">[</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0269em;">w</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0269em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0572em;">ij</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop">ln</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0269em;">w</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0269em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0572em;">ij</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop">ln</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0572em;">j</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mclose delimcenter" style="top:0em;">]</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:5.865em;"><span></span></span></span></span></span></span></span></span></span></span></span></p><p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ln</mi><mo>⁡</mo><mi>L</mi><mo stretchy="false">(</mo><msub><mi>θ</mi><mn>1</mn></msub><mo separator="true">,</mo><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mo separator="true">,</mo><msub><mi>θ</mi><mi>n</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\ln L(\theta_1,...,\theta_n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mop">ln</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">L</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">...</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span> 只有一个最大值，并且可以通过对 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>θ</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">\theta_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 求导并令结果为零来找到该最大值。</p><h2 id="bradley-terry-模型的例子">Bradley-Terry 模型的例子</h2><p>假设我们有三个模型 A、B 和 C，它们之间的比赛结果如下：</p><table><thead><tr><th>比赛</th><th>胜</th><th>负</th></tr></thead><tbody><tr><td>A 对 B</td><td>8</td><td>4</td></tr><tr><td>A 对 C</td><td>3</td><td>5</td></tr></tbody></table><p class="katex-block has-jax"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>ln</mi><mo>⁡</mo><mi>L</mi><mo stretchy="false">(</mo><msub><mi>θ</mi><mi>A</mi></msub><mo separator="true">,</mo><msub><mi>θ</mi><mi>B</mi></msub><mo separator="true">,</mo><msub><mi>θ</mi><mi>C</mi></msub><mo stretchy="false">)</mo><mo>=</mo><mn>8</mn><mi>ln</mi><mo>⁡</mo><mfrac><msub><mi>θ</mi><mi>A</mi></msub><mrow><msub><mi>θ</mi><mi>A</mi></msub><mo>+</mo><msub><mi>θ</mi><mi>B</mi></msub></mrow></mfrac><mo>+</mo><mn>4</mn><mi>ln</mi><mo>⁡</mo><mfrac><msub><mi>θ</mi><mi>B</mi></msub><mrow><msub><mi>θ</mi><mi>A</mi></msub><mo>+</mo><msub><mi>θ</mi><mi>B</mi></msub></mrow></mfrac><mo>+</mo><mn>3</mn><mi>ln</mi><mo>⁡</mo><mfrac><msub><mi>θ</mi><mi>A</mi></msub><mrow><msub><mi>θ</mi><mi>A</mi></msub><mo>+</mo><msub><mi>θ</mi><mi>C</mi></msub></mrow></mfrac><mo>+</mo><mn>5</mn><mi>ln</mi><mo>⁡</mo><mfrac><msub><mi>θ</mi><mi>C</mi></msub><mrow><msub><mi>θ</mi><mi>A</mi></msub><mo>+</mo><msub><mi>θ</mi><mi>C</mi></msub></mrow></mfrac></mrow><annotation encoding="application/x-tex">\ln L(\theta_A,\theta_B,\theta_C) = 8 \ln \frac{\theta_A}{\theta_A + \theta_B} + 4 \ln \frac{\theta_B}{\theta_A + \theta_B} + 3 \ln \frac{\theta_A}{\theta_A + \theta_C} + 5 \ln \frac{\theta_C}{\theta_A + \theta_C}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mop">ln</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">L</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">A</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0502em;">B</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0715em;">C</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:2.2074em;vertical-align:-0.836em;"></span><span class="mord">8</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop">ln</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3714em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">A</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0502em;">B</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">A</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.836em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:2.2074em;vertical-align:-0.836em;"></span><span class="mord">4</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop">ln</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3714em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">A</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0502em;">B</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0502em;">B</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.836em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:2.2074em;vertical-align:-0.836em;"></span><span class="mord">3</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop">ln</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3714em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">A</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0715em;">C</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">A</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.836em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:2.2074em;vertical-align:-0.836em;"></span><span class="mord">5</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop">ln</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3714em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">A</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0715em;">C</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0715em;">C</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.836em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span></p><p>分别对 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>θ</mi><mi>A</mi></msub></mrow><annotation encoding="application/x-tex">\theta_A</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">A</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>、<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>θ</mi><mi>B</mi></msub></mrow><annotation encoding="application/x-tex">\theta_B</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0502em;">B</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 和 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>θ</mi><mi>C</mi></msub></mrow><annotation encoding="application/x-tex">\theta_C</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0715em;">C</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 求导，并令结果为零，我们可以得到：</p><p class="katex-block has-jax"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>θ</mi><mi>A</mi></msub><mo>=</mo><mn>1</mn><mo separator="true">,</mo><mtext>&nbsp;</mtext><msub><mi>θ</mi><mi>B</mi></msub><mo>=</mo><mfrac><mn>1</mn><mn>2</mn></mfrac><mo separator="true">,</mo><mtext>&nbsp;</mtext><msub><mi>θ</mi><mi>C</mi></msub><mo>=</mo><mfrac><mn>5</mn><mn>3</mn></mfrac></mrow><annotation encoding="application/x-tex">\theta_A = 1, \ \theta_B = \frac{1}{2}, \ \theta_C = \frac{5}{3}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">A</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord">1</span><span class="mpunct">,</span><span class="mspace">&nbsp;</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0502em;">B</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:2.0074em;vertical-align:-0.686em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3214em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">2</span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mpunct">,</span><span class="mspace">&nbsp;</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0715em;">C</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:2.0074em;vertical-align:-0.686em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3214em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">3</span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">5</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span></p><p>即根据观察到的比赛结果，如果以模型 A 的能力参数作为参考（1），那么模型 B 的能力参数为 0.5，模型 C 的能力参数为 1.67。此时，模型 B 战胜模型 C 的概率为：</p><p class="katex-block has-jax"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>P</mi><mo stretchy="false">(</mo><mi>B</mi><mtext>&nbsp;</mtext><mi>b</mi><mi>e</mi><mi>a</mi><mi>t</mi><mi>s</mi><mtext>&nbsp;</mtext><mi>C</mi><mo stretchy="false">)</mo><mo>=</mo><mfrac><msub><mi>θ</mi><mi>B</mi></msub><mrow><msub><mi>θ</mi><mi>B</mi></msub><mo>+</mo><msub><mi>θ</mi><mi>C</mi></msub></mrow></mfrac><mo>=</mo><mfrac><mn>0.5</mn><mrow><mn>0.5</mn><mo>+</mo><mn>1.67</mn></mrow></mfrac><mo>≈</mo><mn>0.23</mn></mrow><annotation encoding="application/x-tex">P(B\ beats\ C) = \frac{\theta_B}{\theta_B + \theta_C} = \frac{0.5}{0.5 + 1.67} \approx 0.23</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.1389em;">P</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0502em;">B</span><span class="mspace">&nbsp;</span><span class="mord mathnormal">b</span><span class="mord mathnormal">e</span><span class="mord mathnormal">a</span><span class="mord mathnormal">t</span><span class="mord mathnormal">s</span><span class="mspace">&nbsp;</span><span class="mord mathnormal" style="margin-right:0.0715em;">C</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:2.2074em;vertical-align:-0.836em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3714em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0502em;">B</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0715em;">C</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0502em;">B</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.836em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:2.0908em;vertical-align:-0.7693em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3214em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">0.5</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord">1.67</span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">0.5</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.7693em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">0.23</span></span></span></span></span></p><p>在实际应用中，我们可以直接使用 R 语言中的 <code>BradleyTerry2</code> 包来进行 Bradley-Terry 模型的拟合和参数估计。我们首先把比赛结果整理成成对比较的格式：</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">winner,loser</span><br><span class="line">A,B</span><br><span class="line">A,B</span><br><span class="line">A,B</span><br><span class="line">A,B</span><br><span class="line">A,B</span><br><span class="line">A,B</span><br><span class="line">A,B</span><br><span class="line">A,B</span><br><span class="line">B,A</span><br><span class="line">B,A</span><br><span class="line">B,A</span><br><span class="line">B,A</span><br><span class="line">A,C</span><br><span class="line">A,C</span><br><span class="line">A,C</span><br><span class="line">C,A</span><br><span class="line">C,A</span><br><span class="line">C,A</span><br><span class="line">C,A</span><br><span class="line">C,A</span><br></pre></td></tr></tbody></table></figure><p>然后我们把如上的数据处理成如下的格式：</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">  player1 player2 win1 win2</span><br><span class="line">1       B       A    4    8</span><br><span class="line">2       C       A    5    3</span><br><span class="line">3       A       B    8    4</span><br><span class="line">4       A       C    3    5</span><br></pre></td></tr></tbody></table></figure><p>然后使用 <code>BradleyTerry2</code> 包进行拟合：</p><figure class="highlight r"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 拟合模型（使用默认参考类别）</span></span><br><span class="line">model <span class="operator">&lt;-</span> BTm<span class="punctuation">(</span>cbind<span class="punctuation">(</span>win1<span class="punctuation">,</span> win2<span class="punctuation">)</span><span class="punctuation">,</span> player1<span class="punctuation">,</span> player2<span class="punctuation">,</span> data <span class="operator">=</span> bt_data<span class="punctuation">)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 显示模型摘要</span></span><br><span class="line">cat<span class="punctuation">(</span><span class="string">"Bradley-Terry模型结果:\n"</span><span class="punctuation">)</span></span><br><span class="line">print<span class="punctuation">(</span>summary<span class="punctuation">(</span>model<span class="punctuation">)</span><span class="punctuation">)</span></span><br><span class="line"></span><br><span class="line">cat<span class="punctuation">(</span><span class="string">"\n"</span><span class="punctuation">,</span> <span class="built_in">rep</span><span class="punctuation">(</span><span class="string">"="</span><span class="punctuation">,</span> <span class="number">60</span><span class="punctuation">)</span><span class="punctuation">,</span> <span class="string">"\n\n"</span><span class="punctuation">,</span> sep <span class="operator">=</span> <span class="string">""</span><span class="punctuation">)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 提取能力参数(对数几率)</span></span><br><span class="line">abilities <span class="operator">&lt;-</span> BTabilities<span class="punctuation">(</span>model<span class="punctuation">)</span></span><br><span class="line">print<span class="punctuation">(</span><span class="string">"选手能力参数(log-odds):"</span><span class="punctuation">)</span></span><br><span class="line">print<span class="punctuation">(</span>abilities<span class="punctuation">)</span></span><br><span class="line"></span><br><span class="line">cat<span class="punctuation">(</span><span class="string">"\n"</span><span class="punctuation">,</span> <span class="built_in">rep</span><span class="punctuation">(</span><span class="string">"="</span><span class="punctuation">,</span> <span class="number">60</span><span class="punctuation">)</span><span class="punctuation">,</span> <span class="string">"\n\n"</span><span class="punctuation">,</span> sep <span class="operator">=</span> <span class="string">""</span><span class="punctuation">)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 计算相对强度(exponential of abilities)</span></span><br><span class="line">relative_strength <span class="operator">&lt;-</span> <span class="built_in">exp</span><span class="punctuation">(</span>coef<span class="punctuation">(</span>model<span class="punctuation">)</span><span class="punctuation">)</span></span><br><span class="line">cat<span class="punctuation">(</span><span class="string">"相对强度(相对于参考选手A):\n"</span><span class="punctuation">)</span></span><br><span class="line">print<span class="punctuation">(</span>relative_strength<span class="punctuation">)</span></span><br><span class="line"></span><br><span class="line">cat<span class="punctuation">(</span><span class="string">"\n"</span><span class="punctuation">,</span> <span class="built_in">rep</span><span class="punctuation">(</span><span class="string">"="</span><span class="punctuation">,</span> <span class="number">60</span><span class="punctuation">)</span><span class="punctuation">,</span> <span class="string">"\n\n"</span><span class="punctuation">,</span> sep<span class="operator">=</span><span class="string">""</span><span class="punctuation">)</span></span><br></pre></td></tr></tbody></table></figure><p>最终可以得到如下的输出结果：</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">Bradley-Terry模型结果:</span><br><span class="line"></span><br><span class="line">Call:</span><br><span class="line">BTm(outcome = cbind(win1, win2), player1 = player1, player2 = player2, </span><br><span class="line">    data = bt_data)</span><br><span class="line"></span><br><span class="line">Coefficients:</span><br><span class="line">    Estimate Std. Error z value Pr(&gt;|z|)</span><br><span class="line">..B  -0.6931     0.4330  -1.601    0.109</span><br><span class="line">..C   0.5108     0.5164   0.989    0.323</span><br><span class="line"></span><br><span class="line">(Dispersion parameter for binomial family taken to be 1)</span><br><span class="line"></span><br><span class="line">    Null deviance: 3.7291e+00  on 4  degrees of freedom</span><br><span class="line">Residual deviance: 1.3323e-15  on 2  degrees of freedom</span><br><span class="line">AIC: 14.803</span><br><span class="line"></span><br><span class="line">Number of Fisher Scoring iterations: 3</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">============================================================</span><br><span class="line"></span><br><span class="line">[1] "选手能力参数(log-odds):"</span><br><span class="line">     ability      s.e.</span><br><span class="line">A  0.0000000 0.0000000</span><br><span class="line">B -0.6931472 0.4330127</span><br><span class="line">C  0.5108256 0.5163978</span><br><span class="line"></span><br><span class="line">============================================================</span><br><span class="line">相对强度(相对于参考选手A):</span><br><span class="line">     ..B      ..C </span><br><span class="line">0.500000 1.666667 </span><br><span class="line"></span><br><span class="line">============================================================</span><br></pre></td></tr></tbody></table></figure><p>根据如上的输出可以看出，<code>BradleyTerry2</code> 包计算出的能力参数与我们手动计算的结果是一致的。</p><p>当然，我们也可以用如下的代码来计算能力参数的置信区间：</p><figure class="highlight r"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 计算95%置信区间</span></span><br><span class="line">conf_intervals <span class="operator">&lt;-</span> confint<span class="punctuation">(</span>model<span class="punctuation">,</span> level <span class="operator">=</span> <span class="number">0.95</span><span class="punctuation">)</span></span><br><span class="line">print<span class="punctuation">(</span><span class="string">"能力参数的95%置信区间(log-odds):"</span><span class="punctuation">)</span></span><br><span class="line">print<span class="punctuation">(</span>conf_intervals<span class="punctuation">)</span></span><br><span class="line"></span><br><span class="line">cat<span class="punctuation">(</span><span class="string">"\n"</span><span class="punctuation">,</span> <span class="built_in">rep</span><span class="punctuation">(</span><span class="string">"="</span><span class="punctuation">,</span> <span class="number">60</span><span class="punctuation">)</span><span class="punctuation">,</span> <span class="string">"\n\n"</span><span class="punctuation">,</span> sep <span class="operator">=</span> <span class="string">""</span><span class="punctuation">)</span></span><br></pre></td></tr></tbody></table></figure><p>最终可以得到如下的输出结果：</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Waiting for profiling to be done...</span><br><span class="line">[1] "能力参数的95%置信区间(log-odds):"</span><br><span class="line">         2.5 %    97.5 %</span><br><span class="line">..B -1.5958865 0.1280845</span><br><span class="line">..C -0.4797566 1.5889808</span><br><span class="line"></span><br><span class="line">============================================================</span><br></pre></td></tr></tbody></table></figure><h2 id="把能力参数转化为-elo-分数">把能力参数转化为 Elo 分数</h2><p>利用如下的公式把 Bradley-Terry 模型的能力参数转化为 Elo 分数：</p><p class="katex-block has-jax"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>E</mi><mi>l</mi><mi>o</mi><mi mathvariant="normal">_</mi><mi>S</mi><mi>c</mi><mi>o</mi><mi>r</mi><mi>e</mi><mi>s</mi><mo>=</mo><mi>E</mi><mi>l</mi><mi>o</mi><mi mathvariant="normal">_</mi><mi>B</mi><mi>a</mi><mi>s</mi><mi>e</mi><mo>+</mo><mn>400</mn><mo>×</mo><mfrac><msub><mi>θ</mi><mi>i</mi></msub><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>10</mn></msub></mfrac></mrow><annotation encoding="application/x-tex">Elo\_Scores = Elo\_Base + 400 \times \frac{\theta_i}{\log_{10}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0044em;vertical-align:-0.31em;"></span><span class="mord mathnormal" style="margin-right:0.0576em;">E</span><span class="mord mathnormal" style="margin-right:0.0197em;">l</span><span class="mord mathnormal">o</span><span class="mord" style="margin-right:0.0278em;">_</span><span class="mord mathnormal" style="margin-right:0.0576em;">S</span><span class="mord mathnormal" style="margin-right:0.0278em;">cor</span><span class="mord mathnormal">es</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.0044em;vertical-align:-0.31em;"></span><span class="mord mathnormal" style="margin-right:0.0576em;">E</span><span class="mord mathnormal" style="margin-right:0.0197em;">l</span><span class="mord mathnormal">o</span><span class="mord" style="margin-right:0.0278em;">_</span><span class="mord mathnormal" style="margin-right:0.0502em;">B</span><span class="mord mathnormal">a</span><span class="mord mathnormal">se</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em;"></span><span class="mord">400</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:2.3016em;vertical-align:-0.9301em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3714em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mop"><span class="mop">lo<span style="margin-right:0.0139em;">g</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.207em;"><span style="top:-2.4559em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">10</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2441em;"><span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.9301em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span></p><p>具体代码如下所示：</p><figure class="highlight r"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># ========== 重新参数化：所有选手都有置信区间 ==========</span></span><br><span class="line">cat<span class="punctuation">(</span><span class="string">"重新参数化模型 - 所有选手都有置信区间:\n\n"</span><span class="punctuation">)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 计算所有选手的对数能力参数（包括A）</span></span><br><span class="line"><span class="comment"># A的系数为0（参考类别）</span></span><br><span class="line">abilities_all <span class="operator">&lt;-</span> <span class="built_in">c</span><span class="punctuation">(</span>A <span class="operator">=</span> <span class="number">0</span><span class="punctuation">,</span> coef<span class="punctuation">(</span>model<span class="punctuation">)</span><span class="punctuation">)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 计算方差-协方差矩阵</span></span><br><span class="line">vcov_matrix <span class="operator">&lt;-</span> vcov<span class="punctuation">(</span>model<span class="punctuation">)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 为A添加行和列（方差为0，协方差为0）</span></span><br><span class="line">n_players <span class="operator">&lt;-</span> <span class="built_in">length</span><span class="punctuation">(</span>abilities_all<span class="punctuation">)</span></span><br><span class="line">vcov_all <span class="operator">&lt;-</span> matrix<span class="punctuation">(</span><span class="number">0</span><span class="punctuation">,</span> n_players<span class="punctuation">,</span> n_players<span class="punctuation">)</span></span><br><span class="line">rownames<span class="punctuation">(</span>vcov_all<span class="punctuation">)</span> <span class="operator">&lt;-</span> colnames<span class="punctuation">(</span>vcov_all<span class="punctuation">)</span> <span class="operator">&lt;-</span> <span class="built_in">names</span><span class="punctuation">(</span>abilities_all<span class="punctuation">)</span></span><br><span class="line">vcov_all<span class="punctuation">[</span><span class="number">2</span><span class="operator">:</span>n_players<span class="punctuation">,</span> <span class="number">2</span><span class="operator">:</span>n_players<span class="punctuation">]</span> <span class="operator">&lt;-</span> vcov_matrix</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方法1: 计算相对于平均水平的能力参数</span></span><br><span class="line">mean_ability <span class="operator">&lt;-</span> mean<span class="punctuation">(</span>abilities_all<span class="punctuation">)</span></span><br><span class="line">abilities_centered <span class="operator">&lt;-</span> abilities_all <span class="operator">-</span> mean_ability</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用Delta方法计算标准误</span></span><br><span class="line"><span class="comment"># 对于centered abilities: SE(ability_i - mean) </span></span><br><span class="line">se_centered <span class="operator">&lt;-</span> <span class="built_in">rep</span><span class="punctuation">(</span><span class="number">0</span><span class="punctuation">,</span> n_players<span class="punctuation">)</span></span><br><span class="line"><span class="built_in">names</span><span class="punctuation">(</span>se_centered<span class="punctuation">)</span> <span class="operator">&lt;-</span> <span class="built_in">names</span><span class="punctuation">(</span>abilities_all<span class="punctuation">)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> <span class="punctuation">(</span>i <span class="keyword">in</span> <span class="number">1</span><span class="operator">:</span>n_players<span class="punctuation">)</span> <span class="punctuation">{</span></span><br><span class="line">  <span class="comment"># 构造线性组合系数: c_i = 1, c_j = -1/n for all j</span></span><br><span class="line">  contrast <span class="operator">&lt;-</span> <span class="built_in">rep</span><span class="punctuation">(</span><span class="operator">-</span><span class="number">1</span> <span class="operator">/</span> n_players<span class="punctuation">,</span> n_players<span class="punctuation">)</span></span><br><span class="line">  contrast<span class="punctuation">[</span>i<span class="punctuation">]</span> <span class="operator">&lt;-</span> contrast<span class="punctuation">[</span>i<span class="punctuation">]</span> <span class="operator">+</span> <span class="number">1</span></span><br><span class="line">  </span><br><span class="line">  <span class="comment"># SE = sqrt(c' * Vcov * c)</span></span><br><span class="line">  se_centered<span class="punctuation">[</span>i<span class="punctuation">]</span> <span class="operator">&lt;-</span> <span class="built_in">sqrt</span><span class="punctuation">(</span>t<span class="punctuation">(</span>contrast<span class="punctuation">)</span> <span class="operator">%*%</span> vcov_all <span class="operator">%*%</span> contrast<span class="punctuation">)</span></span><br><span class="line"><span class="punctuation">}</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 计算95%置信区间</span></span><br><span class="line">z_value <span class="operator">&lt;-</span> qnorm<span class="punctuation">(</span><span class="number">0.975</span><span class="punctuation">)</span>  <span class="comment"># 95% CI</span></span><br><span class="line">ci_lower_centered <span class="operator">&lt;-</span> abilities_centered <span class="operator">-</span> z_value <span class="operator">*</span> se_centered</span><br><span class="line">ci_upper_centered <span class="operator">&lt;-</span> abilities_centered <span class="operator">+</span> z_value <span class="operator">*</span> se_centered</span><br><span class="line"></span><br><span class="line">cat<span class="punctuation">(</span><span class="string">"相对于平均水平的能力参数:\n"</span><span class="punctuation">)</span></span><br><span class="line">result_centered <span class="operator">&lt;-</span> data.frame<span class="punctuation">(</span></span><br><span class="line">  Player <span class="operator">=</span> <span class="built_in">names</span><span class="punctuation">(</span>abilities_centered<span class="punctuation">)</span><span class="punctuation">,</span></span><br><span class="line">  Ability <span class="operator">=</span> <span class="built_in">round</span><span class="punctuation">(</span>abilities_centered<span class="punctuation">,</span> <span class="number">4</span><span class="punctuation">)</span><span class="punctuation">,</span></span><br><span class="line">  SE <span class="operator">=</span> <span class="built_in">round</span><span class="punctuation">(</span>se_centered<span class="punctuation">,</span> <span class="number">4</span><span class="punctuation">)</span><span class="punctuation">,</span></span><br><span class="line">  CI_Lower <span class="operator">=</span> <span class="built_in">round</span><span class="punctuation">(</span>ci_lower_centered<span class="punctuation">,</span> <span class="number">4</span><span class="punctuation">)</span><span class="punctuation">,</span></span><br><span class="line">  CI_Upper <span class="operator">=</span> <span class="built_in">round</span><span class="punctuation">(</span>ci_upper_centered<span class="punctuation">,</span> <span class="number">4</span><span class="punctuation">)</span></span><br><span class="line"><span class="punctuation">)</span></span><br><span class="line">print<span class="punctuation">(</span>result_centered<span class="punctuation">)</span></span><br><span class="line"></span><br><span class="line">cat<span class="punctuation">(</span><span class="string">"\n"</span><span class="punctuation">,</span> <span class="built_in">rep</span><span class="punctuation">(</span><span class="string">"="</span><span class="punctuation">,</span> <span class="number">60</span><span class="punctuation">)</span><span class="punctuation">,</span> <span class="string">"\n\n"</span><span class="punctuation">,</span> sep<span class="operator">=</span><span class="string">""</span><span class="punctuation">)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 转换为Elo评分（所有选手都相对于平均1500分）</span></span><br><span class="line">base_elo <span class="operator">&lt;-</span> 1500</span><br><span class="line">elo_scores_centered <span class="operator">&lt;-</span> base_elo <span class="operator">+</span> <span class="number">400</span> <span class="operator">*</span> abilities_centered <span class="operator">/</span> <span class="built_in">log</span><span class="punctuation">(</span><span class="number">10</span><span class="punctuation">)</span></span><br><span class="line">elo_ci_lower <span class="operator">&lt;-</span> base_elo <span class="operator">+</span> <span class="number">400</span> <span class="operator">*</span> ci_lower_centered <span class="operator">/</span> <span class="built_in">log</span><span class="punctuation">(</span><span class="number">10</span><span class="punctuation">)</span></span><br><span class="line">elo_ci_upper <span class="operator">&lt;-</span> base_elo <span class="operator">+</span> <span class="number">400</span> <span class="operator">*</span> ci_upper_centered <span class="operator">/</span> <span class="built_in">log</span><span class="punctuation">(</span><span class="number">10</span><span class="punctuation">)</span></span><br><span class="line"></span><br><span class="line">results_final <span class="operator">&lt;-</span> data.frame<span class="punctuation">(</span></span><br><span class="line">  Player <span class="operator">=</span> <span class="built_in">names</span><span class="punctuation">(</span>elo_scores_centered<span class="punctuation">)</span><span class="punctuation">,</span></span><br><span class="line">  Elo_Rating <span class="operator">=</span> <span class="built_in">round</span><span class="punctuation">(</span>elo_scores_centered<span class="punctuation">,</span> <span class="number">1</span><span class="punctuation">)</span><span class="punctuation">,</span></span><br><span class="line">  SE <span class="operator">=</span> <span class="built_in">round</span><span class="punctuation">(</span><span class="number">400</span> <span class="operator">*</span> se_centered <span class="operator">/</span> <span class="built_in">log</span><span class="punctuation">(</span><span class="number">10</span><span class="punctuation">)</span><span class="punctuation">,</span> <span class="number">1</span><span class="punctuation">)</span><span class="punctuation">,</span></span><br><span class="line">  CI_Lower <span class="operator">=</span> <span class="built_in">round</span><span class="punctuation">(</span>elo_ci_lower<span class="punctuation">,</span> <span class="number">1</span><span class="punctuation">)</span><span class="punctuation">,</span></span><br><span class="line">  CI_Upper <span class="operator">=</span> <span class="built_in">round</span><span class="punctuation">(</span>elo_ci_upper<span class="punctuation">,</span> <span class="number">1</span><span class="punctuation">)</span><span class="punctuation">,</span></span><br><span class="line">  CI_Width <span class="operator">=</span> <span class="built_in">round</span><span class="punctuation">(</span>elo_ci_upper <span class="operator">-</span> elo_ci_lower<span class="punctuation">,</span> <span class="number">1</span><span class="punctuation">)</span></span><br><span class="line"><span class="punctuation">)</span></span><br><span class="line">results_final <span class="operator">&lt;-</span> results_final<span class="punctuation">[</span>order<span class="punctuation">(</span><span class="operator">-</span>results_final<span class="operator">$</span>Elo_Rating<span class="punctuation">)</span><span class="punctuation">,</span> <span class="punctuation">]</span></span><br><span class="line"></span><br><span class="line">cat<span class="punctuation">(</span><span class="string">"最终结果 - Elo评分及95%置信区间（所有选手）:\n"</span><span class="punctuation">)</span></span><br><span class="line">print<span class="punctuation">(</span>results_final<span class="punctuation">,</span> row.names <span class="operator">=</span> <span class="literal">FALSE</span><span class="punctuation">)</span></span><br><span class="line"></span><br><span class="line">cat<span class="punctuation">(</span><span class="string">"\n注: 所有Elo评分都相对于平均水平1500分\n"</span><span class="punctuation">)</span></span><br><span class="line"></span><br><span class="line">cat<span class="punctuation">(</span><span class="string">"\n"</span><span class="punctuation">,</span> <span class="built_in">rep</span><span class="punctuation">(</span><span class="string">"="</span><span class="punctuation">,</span> <span class="number">60</span><span class="punctuation">)</span><span class="punctuation">,</span> <span class="string">"\n\n"</span><span class="punctuation">,</span> sep <span class="operator">=</span> <span class="string">""</span><span class="punctuation">)</span></span><br></pre></td></tr></tbody></table></figure><p>最终可以得到如下的输出结果：</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">重新参数化模型 - 所有选手都有置信区间:</span><br><span class="line"></span><br><span class="line">相对于平均水平的能力参数:</span><br><span class="line">    Player Ability     SE CI_Lower CI_Upper</span><br><span class="line">A        A  0.0608 0.2246  -0.3795   0.5011</span><br><span class="line">..B    ..B -0.6324 0.3361  -1.2911   0.0264</span><br><span class="line">..C    ..C  0.5716 0.3733  -0.1601   1.3033</span><br><span class="line"></span><br><span class="line">============================================================</span><br><span class="line"></span><br><span class="line">最终结果 - Elo评分及95%置信区间（所有选手）:</span><br><span class="line"> Player Elo_Rating   SE CI_Lower CI_Upper CI_Width</span><br><span class="line">    ..C     1599.3 64.8   1472.2   1726.4    254.2</span><br><span class="line">      A     1510.6 39.0   1434.1   1587.0    153.0</span><br><span class="line">    ..B     1390.1 58.4   1275.7   1504.6    228.9</span><br><span class="line"></span><br><span class="line">注: 所有Elo评分都相对于平均水平1500分</span><br><span class="line"></span><br><span class="line">============================================================</span><br></pre></td></tr></tbody></table></figure><p>所以，我们就根据成对比赛数据，计算出了和 LMArena 类似的 Elo 分数和置信区间。</p><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">R 源代码与输入数据</p><p>完整的 R 代码和输入数据可以点击链接 <a href="https://github.com/wangwei1237/wangwei1237.github.io_src/blob/master/source/_posts/How-to-estimate-the-winning-rate-based-on-historical-match-results/bt.R">bt.R</a>、<a href="https://github.com/wangwei1237/wangwei1237.github.io_src/blob/master/source/_posts/How-to-estimate-the-winning-rate-based-on-historical-match-results/matches_2.csv">matches_2.csv</a>获取。</p></div><h2 id="如何进行排序？">如何进行排序？</h2><p>用户偏好打分，本质上还是使用抽样的方式来估计群体参数，因此采用绝对 Elo 分值对模型能力排序有时候也并不那么客观。有可能 A、B 两个模型的能力实际上差不多，但是刚好在观察到的打分结果中，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>E</mi><mi>l</mi><mi>o</mi><mi mathvariant="normal">_</mi><mi>A</mi><mo>=</mo><mn>1500</mn></mrow><annotation encoding="application/x-tex">Elo\_A = 1500</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0044em;vertical-align:-0.31em;"></span><span class="mord mathnormal" style="margin-right:0.0576em;">E</span><span class="mord mathnormal" style="margin-right:0.0197em;">l</span><span class="mord mathnormal">o</span><span class="mord" style="margin-right:0.0278em;">_</span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1500</span></span></span></span>、<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>E</mi><mi>l</mi><mi>o</mi><mi mathvariant="normal">_</mi><mi>B</mi><mo>=</mo><mn>1498</mn></mrow><annotation encoding="application/x-tex">Elo\_B = 1498</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0044em;vertical-align:-0.31em;"></span><span class="mord mathnormal" style="margin-right:0.0576em;">E</span><span class="mord mathnormal" style="margin-right:0.0197em;">l</span><span class="mord mathnormal">o</span><span class="mord" style="margin-right:0.0278em;">_</span><span class="mord mathnormal" style="margin-right:0.0502em;">B</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1498</span></span></span></span>。难道这就能说明模型 A 的能力要比模型 B 好吗？</p><p>如前所述，对于每一个模型，我们均可以通过 Bradley-Terry 模型计算出该模型的能力参数、以及能力参数对应的 95% 置信区间。因此，基于 Bradley-Terry 模型，我们可以估计出每一个模型的实际能力、实际能力的下限、实际能力的上限。</p><p>为了让排序更为合理，LMArena 根据模型的实际能力、实际能力的下限、实际能力的上限这三个数据提供了 <code>Rank Spread</code> 的排序方式。在 LMArena 的 <em>LMArena’s Ranking Method</em> <sup class="footnote-ref"><a href="#fn6" id="fnref6">[6]</a></sup> 这篇博客中，作者对 <code>Rank Spread</code> 的排名方法进行了详细的描述。</p><p>对于模型 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>M</mi></mrow><annotation encoding="application/x-tex">M</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.109em;">M</span></span></span></span> 而言：</p><ul><li>模型 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>M</mi></mrow><annotation encoding="application/x-tex">M</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.109em;">M</span></span></span></span> 的最佳排名为：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mo>+</mo><mi mathvariant="normal">#</mi><mo stretchy="false">{</mo><mtext>置信区间下限</mtext><mo>&gt;</mo><mtext>模型</mtext><mi>M</mi><mtext>置信区间上限的模型数量</mtext><mo stretchy="false">}</mo></mrow><annotation encoding="application/x-tex">1+\#\{置信区间下限 &gt; 模型 M 置信区间上限的模型数量\}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em;"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">#</span><span class="mopen">{</span><span class="mord cjk_fallback">置信区间下限</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord cjk_fallback">模型</span><span class="mord mathnormal" style="margin-right:0.109em;">M</span><span class="mord cjk_fallback">置信区间上限的模型数量</span><span class="mclose">}</span></span></span></span>。</li><li>模型 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>M</mi></mrow><annotation encoding="application/x-tex">M</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.109em;">M</span></span></span></span> 的最差排名为：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mo>+</mo><mi mathvariant="normal">#</mi><mo stretchy="false">{</mo><mtext>置信区间上限</mtext><mo>&gt;</mo><mtext>模型</mtext><mi>M</mi><mtext>置信区间下限的模型数量</mtext><mo stretchy="false">}</mo></mrow><annotation encoding="application/x-tex">1+\#\{置信区间上限 &gt; 模型 M 置信区间下限的模型数量\}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em;"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">#</span><span class="mopen">{</span><span class="mord cjk_fallback">置信区间上限</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord cjk_fallback">模型</span><span class="mord mathnormal" style="margin-right:0.109em;">M</span><span class="mord cjk_fallback">置信区间下限的模型数量</span><span class="mclose">}</span></span></span></span>。</li></ul><blockquote><p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mo>+</mo></mrow><annotation encoding="application/x-tex">1+</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em;"></span><span class="mord">1</span><span class="mord">+</span></span></span></span> 确保排名不会为零，并且可能的最佳排名是 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn></mrow><annotation encoding="application/x-tex">1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1</span></span></span></span>。</p></blockquote><p><img src="/2025/11/15/How-to-estimate-the-winning-rate-based-on-historical-match-results/Raw-Ranking_v5.png" alt=""></p><p>对于上图中的模型 C 而言：</p><ul><li>模型 C 的 Elo 分数为 1400，按照 Elo 分数排序，模型 C 的排名为第 3 名。</li><li>置信区间下限大于模型 C 置信区间上限（1420）的模型只有模型 A，因此模型 C 的最佳排名为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mo>+</mo><mn>1</mn><mo>=</mo><mn>2</mn></mrow><annotation encoding="application/x-tex">1 + 1 = 2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em;"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">2</span></span></span></span>。</li><li>置信区间上限大于模型 C 置信区间下限（1380）的模型有模型 A、B、D、E 四个模型，因此模型 C 的最差排名为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mo>+</mo><mn>4</mn><mo>=</mo><mn>5</mn></mrow><annotation encoding="application/x-tex">1 + 4 = 5</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em;"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">4</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">5</span></span></span></span>。</li><li>最终，模型 C 的 <code>Rank Spread</code> 为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>2</mn><mo>⟺</mo><mn>5</mn></mrow><annotation encoding="application/x-tex">2 \Longleftrightarrow 5</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6684em;vertical-align:-0.024em;"></span><span class="mord">2</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">⟺</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">5</span></span></span></span>。</li></ul><p>对于模型 B 而言：</p><ul><li>模型 B 的 Elo 分数为 1418，按照 Elo 分数排序，模型 B 的排名为第 2 名。</li><li>置信区间下限大于模型 B 置信区间上限（1418）的模型只有模型 A，因此模型 B 的最佳排名为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mo>+</mo><mn>1</mn><mo>=</mo><mn>2</mn></mrow><annotation encoding="application/x-tex">1 + 1 = 2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em;"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">2</span></span></span></span>。</li><li>置信区间上限大于模型 B 置信区间下限的模型有模型 A、C、D 三个模型，因此模型 B 的最差排名为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mo>+</mo><mn>3</mn><mo>=</mo><mn>4</mn></mrow><annotation encoding="application/x-tex">1 + 3 = 4</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em;"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">3</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">4</span></span></span></span>。</li><li>最终，模型 B 的 <code>Rank Spread</code> 为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>2</mn><mo>⟺</mo><mn>4</mn></mrow><annotation encoding="application/x-tex">2 \Longleftrightarrow 4</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6684em;vertical-align:-0.024em;"></span><span class="mord">2</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">⟺</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">4</span></span></span></span>。</li></ul><p>因此，当两个模型的 <code>Rank Spread</code> 有重叠时，那么他们就属于同级别模型（如上所示的模型 B 和模型 C），我们可以认为他们在能力上是属于统一梯队的。当然就和体育赛事一样，虽然两名选手都是产不多的视频，但实际比赛结果只能有一个冠军。因此，我们可以认为 Elo 分数排序（唯一排序）就代表在当前的数据下，模型的实际能力排序结果。</p><h2 id="后记">后记<sup class="footnote-ref"><a href="#fn7" id="fnref7">[7]</a></sup></h2><style>.admonition {    margin: 1.5625em 0;    padding: .6rem;    overflow: hidden;    font-size: 1.6rem;    page-break-inside: avoid;    border-left: .6rem solid #42b983;    border-radius: .6rem;    box-shadow: 0 0.1rem 0.4rem rgba(0,0,0,.5), 0 0 0.05rem rgba(0,0,0,.1);    background-color: #fafafa;  }    p.admonition-title {    position: relative;    margin: -.6rem -.6rem .8em -.6rem !important;    padding: .4rem .6rem .4rem .4rem;    font-weight: 700;    background-color:rgba(66, 185, 131, .1);  }    /*  .admonition-title::before {    position: absolute;    top: .9rem;    left: 1rem;    width: 12px;    height: 12px;    background-color: #42b983;    border-radius: 50%;    content: ' ';  }  */    .info>.admonition-title, .todo>.admonition-title {    background-color: rgba(0,184,212,.1);  }    .warning>.admonition-title, .attention>.admonition-title, .caution>.admonition-title {    background-color: rgba(255,145,0,.1);  }    .failure>.admonition-title, .missing>.admonition-title, .fail>.admonition-title, .error>.admonition-title {    background-color: rgba(255,82,82,.1);  }    .admonition.info, .admonition.todo {    border-color: #00b8d4;  }    .admonition.warning, .admonition.attention, .admonition.caution {    border-color: #ff9100;  }    .admonition.failure, .admonition.missing, .admonition.fail, .admonition.error {    border-color: #ff5252;  }    .info>.admonition-title::before, .todo>.admonition-title::before {    background-color: #00b8d4;    border-radius: 50%;  }    .warning>.admonition-title::before, .attention>.admonition-title::before, .caution>.admonition-title::before {    background-color: #ff9100;    border-radius: 50%;  }    .failure>.admonition-title::before,.missing>.admonition-title::before,.fail>.admonition-title::before,.error>.admonition-title::before{    background-color: #ff5252;;    border-radius: 50%;  }    .admonition>:last-child {    margin-bottom: 0 !important;  }  </style><div class="admonition note"><p class="admonition-title">Arena-Rank: Open Sourcing the Leaderboard Methodology</p><p>值得高兴的是，2025 年 12 月 18 日，LMArena 团队宣布开源他们的排行榜方法 <a href="https://lmarena.ai/blog/arena-rank/">Arena-Rank</a>，并且提供了 <a href="https://github.com/lmarena/arena-rank?ref=lmarena.ai">Python 版本</a> 的实现。我们可以使用 <a href="https://huggingface.co/datasets/lmarena-ai/arena-expert-5k">lmarena-ai/arena-expert-5k</a> 数据集对 Arena-Rank 进行测试。</p></div><h2 id="参考文献">参考文献</h2><hr class="footnotes-sep"><section class="footnotes"><ol class="footnotes-list"><li id="fn1" class="footnote-item"><p><a href="https://mp.weixin.qq.com/s/6V3M1BFho0Y2L26SrgOT4g">刚刚，马斯克Grok 4.1低调发布！通用能力碾压其他一切模型</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p></li><li id="fn2" class="footnote-item"><p><a href="https://mp.weixin.qq.com/s/1qvy9mH-KXGPQrBv3PGYag">全世界在等的Gemini 3终于来了！强到断崖领先，连马斯克OpenAI都夸好</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p></li><li id="fn3" class="footnote-item"><p><a href="https://arxiv.org/abs/2403.04132">Chatbot Arena: An Open Platform for Evaluating LLMs by Human Preference</a> <a href="#fnref3" class="footnote-backref">↩︎</a></p></li><li id="fn4" class="footnote-item"><p><a href="https://news.lmarena.ai/extended-arena/">Statistical Extensions of the Bradley-Terry and Elo Models</a> <a href="#fnref4" class="footnote-backref">↩︎</a></p></li><li id="fn5" class="footnote-item"><p><a href="https://en.wikipedia.org/wiki/Bradley%E2%80%93Terry_model">Bradley–Terry model</a> <a href="#fnref5" class="footnote-backref">↩︎</a></p></li><li id="fn6" class="footnote-item"><p><a href="https://news.lmarena.ai/ranking-method/">LMArena’s Ranking Method</a> <a href="#fnref6" class="footnote-backref">↩︎</a></p></li><li id="fn7" class="footnote-item"><p><a href="https://lmarena.ai/blog/arena-rank/">Arena-Rank</a> <a href="#fnref7" class="footnote-backref">↩︎</a></p></li></ol></section></body></html>]]>
      </content:encoded>
    </item>
  </channel>
</rss>
