Coding Agent 架构对比 Coding Agents — Architecture Comparison

6 家开源 coding agent / harness(hermes-agent · claw-code · codex · opencode · openclaw · pi)到底怎么跑, 为什么这么设计。从零讲起,带交互动画,对得上源码文件与部分行号。 How six open-source coding agents / harnesses (hermes-agent · claw-code · codex · opencode · openclaw · pi) actually run, and why. Zero-to-deep, with interactive animations tied to source files and selected line references.

一键复制 Quick cite → 跳到引用区→ Jump to cite section
30 秒看懂 agent: 大语言模型 (LLM, 像 Claude / GPT) 本身只做一件事——猜下一个字该是什么。 要让它真正去读文件、执行命令、上网搜, 得给它工具, 外面再套一层循环: 它说要调工具 → 框架去执行 → 结果塞回对话 → 它接着想。 这一整套就叫 agent。本页拆解了 6 种 agent / harness 的设计差异。第一次看, 先从下面的"基本概念"读起。
30-second primer: an LLM (Claude / GPT / ...) only predicts "what text comes next." To make it really read files, run commands, or browse the web, you wrap it with tools + a loop: it asks to call a tool → the framework runs it → result is fed back → it continues. That wrapper is an "agent." This page compares six agent / harness designs. New here? Start with "Concepts" below.

1. 基本概念1. Concepts

1. LLM 本质是"下一个 token 预测器"1. LLM is a next-token predictor

你给它一段文本, 它给你接下来最可能出现的一段文本。没了。

Feed it text, it returns the most-likely next chunk of text. That's all.

LLM 只做一件事: 看见前面的 token, 预测下一个 token;
然后把预测的那个加到末尾, 再预测下一个——一步步生成出整段话。

先把文字切成 token(≈ 子词):

   "The cat sat on the"
   ──tokenize──▶ ["The", " cat", " sat", " on", " the"]

然后一步步预测: 

   步 1  看到: ["The", " cat", " sat", " on", " the"]   → 预测 " mat"
   步 2  看到: ["The", " cat", " sat", " on", " the", " mat"] → 预测 "."
   步 3  看到: [..., "."]                                 → 预测 <end> (结束)

每预测一次, 输入就长一点。"历史 + 新词"的总长度就是 context window 的上限所在——
超过了, 要么压缩 (后文会讲各家的压缩策略), 要么丢弃尾部。
An LLM only does one thing: look at the tokens so far, predict the next token;
then append that prediction and predict the next one — step by step, a whole reply appears.

First, text is split into tokens (≈ subwords):

   "The cat sat on the"
   ──tokenize──▶ ["The", " cat", " sat", " on", " the"]

Then one step at a time:

   step 1  sees: ["The", " cat", " sat", " on", " the"]   → predicts " mat"
   step 2  sees: ["The", " cat", " sat", " on", " the", " mat"] → predicts "."
   step 3  sees: [..., "."]                                → predicts <end> (stop)

Each step makes the input one token longer. The total length of "history + new tokens" is
capped by the context window — overflow either gets compacted (covered later in each project's compaction story) or truncated.

不会执行代码、不会读文件、不会上网。这些全是 agent 在外面套的一层。

It can't execute code, read files, or access the web. All of that is what the agent wraps around it.

2. Function calling: 让 LLM "指点"框架去干活2. Function calling: let the LLM direct the framework

给 LLM 一段 system prompt 告诉它"你可以调 read_file(path)"。用户问"看看 /tmp/foo.py"——LLM 不会猜文件内容, 它会返回结构化 JSON:

Tell the LLM in the system prompt "you can call read_file(path)". A user asks "read /tmp/foo.py" — the LLM won't invent file contents, it returns structured JSON:

{
  "stop_reason": "tool_use",
  "content": [
    { "type": "text", "text": "Let me read it." },
    { "type": "tool_use",
      "id": "toolu_01abc",
      "name": "read_file",
      "input": { "path": "/tmp/foo.py" } }
  ]
}

真正去读文件的是外面的 agent 框架——拿到这个 JSON, 调 open(), 把结果塞回对话:

The agent framework does the actual reading — it takes the JSON, calls open(), and feeds the result back:

{
  "role": "tool",
  "tool_use_id": "toolu_01abc",
  "content": "import os\nprint(os.getcwd())\n"
}

这一来一回就是 agent 的一次工具调用

That round-trip is one tool call inside an agent turn.

3. Agent = LLM + 工具 + 循环3. Agent = LLM + tools + loop

Agent Loop (一次 turn)
Agent loop (one turn)
  1. 用户消息
  2. 把当前对话状态给 LLM(历史 + 可能的压缩摘要 / 上下文)
  3. LLM 输出文本 或 工具调用
  4. 工具调用 → 框架执行 → 拿到结果
  5. 结果塞回历史
  6. 回到 2, 直到 LLM 不再要调用
  1. User message
  2. Send the current conversation state to the LLM, usually history plus compacted summary/context
  3. LLM outputs text or a tool_use
  4. Tool_use → framework runs it → result
  5. Result appended to history
  6. Go to 2 until no more tool_use

就这 6 步。六个 agent / harness 都遵循这个骨架, 差异在每一步做多少事、加了多少保护、怎么扩展

Six steps. All six agent / harness designs follow this skeleton — they differ in how much each step does, what safety layers are added, and how it extends.

4. 为什么 agent 能做到 LLM 做不到的事4. Why agents can do what LLMs can't

核心洞察: LLM 的"脑"很强但"手"是假的。Agent 框架负责给它装上真手, 并保证它不把厨房烧了。六家 agent / harness 的差异, 本质是"装手的方式"和"保证不烧厨房的方式"不同

Key insight: LLMs have great brains but fake hands. The agent framework installs real hands — and makes sure it doesn't burn the kitchen down. The six designs differ in how they install the hands and how they prevent fires.

术语表 — 忘记某个词时展开查 Glossary — expand when you forget a word

术语表Glossary

一句话解释在本文里怎么用
LLM大语言模型, 如 Claude、GPT-4本页 agent / harness 都是套在 LLM 外面的框架
TokenLLM 看到的最小单位, ≈ 一个子词"hello world" ≈ 2 tokens
Context windowLLM 单次能看到的 token 总量上限具体上限取决于模型和运行配置; 对 harness 来说关键是快满时如何压缩、裁剪和重载持久记忆
API程序调程序的接口, 本文指 LLM 服务商 REST API发 HTTP, 模型返回 JSON
Streaming边生成边返回 (打字机效果)减少等待, 能提前开始下一步
Function calling / Tool useLLM 输出"我要调这个函数"的结构化 JSON是 agent 能"动手"的前提
Prompt cache服务端缓存长 system prompt省钱 (最多 10x) 省延迟
Sandbox把进程关进小盒子, 限制它能访问什么防 agent 把电脑搞坏
ProviderLLM 服务商 (Anthropic / OpenAI / Google)绑一家 或 做 adapter 兼容多家
Turn用户说一次话 + agent 干完活返回 = 一个 turn"一次 turn" = 主循环跑一整圈
ReActReasoning + Acting 循环: 想一下 → 做一下本页多数 agent / harness 都是 ReAct 变体
MCPModel Context Protocol, 外部工具协议让 agent 接入任意第三方工具
CLAUDE.md / AGENTS.md项目根目录的约定配置文件启动时读, 相当于"给 bot 的 README"
Plan-and-execute先让模型出计划, 再一步步执行的编排模式opencode 的 plan 模式、claw-code 的 EnterPlanMode
Reflectionagent 完成动作后再自我检查一轮, 发现错误就重试§8 Takeaway · Reflection 是很多 harness 的辅助轮回
Toolsagent 可以调用的带类型函数 (读文件、执行 bash、浏览网页…)§2 Tools vs Skills 对照表
Skills教 agent "什么时候、怎么用工具" 的 Markdown 文件 (SKILL.md)§2 · anthropics/skills
Subagent父 agent 派出的子 agent, context 隔离, 只回传总结hermes delegate_task、opencode @general/@explore
Orchestration决定"谁来做、按什么顺序、出错怎么接"——harness 的外层编排§2 五层地图的最顶层
Hook用户配置的脚本, 在 agent 生命周期关键时刻 (工具前/后) 自动跑, 能拦截 / 改写 / 否决 / 记日志claw-code 的 PreToolUse / PostToolUse
Adapter通用 agent loop 和具体 provider API 之间的翻译层; 换 adapter 就换 provider, loop 一行不用动hermes 的 anthropic_adapter / gemini_native
Compaction对话历史超过 context 上限时, 自动摘要旧 turn、保留头尾的行为claw-code 的 auto-compact · hermes 的 trajectory 压缩
Rolloutagent 一次从头跑到尾的完整 turn 序列; RL 训练里通常并行跑几百个§9 hermes 的 Modal 云沙箱每 rollout 一个 VM
SSEServer-Sent Events, 单向 HTTP 流式推送协议opencode 用它把 agent 事件从 server 流回 TUI
ACPAgent Client Protocol, IDE 与 agent 之间的 stdio 协议 (Zed / Cursor 推动)openclaw 的 acp 桥、opencode 的 acp 支持
LSPLanguage Server Protocol, IDE 与语言服务器 (跳转定义、查引用、诊断…) 之间的协议opencode 把 LSP 做成一等工具
CDPChrome DevTools Protocol, 程序化控制 Chromium 的协议 (无头浏览器自动化的基础)openclaw 浏览器沙箱用 CDP 给 agent 下操作指令
noVNC浏览器里的 VNC 客户端, 允许你通过 HTTP 端口远程看到沙箱里的图形桌面openclaw 6080 端口可"看 agent 在浏览器里点什么"
RL (强化学习)让 agent 反复试错、按"奖励函数"给出的分数学习的训练范式; 通常需要并行跑几百个 rollout§9 讨论 hermes 与训练的契合——它被设计成 RL-friendly harness
Modalserverless 云 VM 服务商, 按秒计费、秒级启停; agent 可以把每个 rollout 扔进一个独立 VMhermes 的 RL 沙箱就是基于 Modal
AWS BedrockAWS 托管的 LLM API 网关, 里面可以调 Claude、Llama、Mistral 等多家模型hermes 的 bedrock_adapter 就是对接它
OpenRouter第三方 LLM 路由服务, 一个 API key 调所有主流模型, 自动做限流 / 回退hermes 支持它作为 provider 之一
OS 沙箱 (seatbelt / landlock / bubblewrap / seccomp)操作系统层的进程隔离原语: seatbelt (macOS sandbox-exec) · landlock (Linux 内核自愿放弃能力) · bubblewrap (用户态容器) · seccomp (系统调用白名单)codex 默认模式用这一套限制文件系统和网络边界
Vercel AI SDKVercel 出的 provider-agnostic TypeScript SDK, 抽象了 streaming / tool-calling / reasoning 的跨家差异opencode 的 provider 层直接用它, 新加一家只改一行配置
Self-evolving(自进化)agent 在运行中自己写新 SKILL.md、改 prompt 或更新 memory, 下一次起跑点比上一次更高; 比 reflection 更进一步——学到的东西能持久化, 不只是当轮改错Skills 层就是自进化的产物入口 · hermes 的 skill_manage 工具 · §8 Reflection 之后的下一层 takeaway
TermOne-linerUsage in this doc
LLMLarge Language Model (Claude, GPT-4, etc.)The systems on this page wrap an LLM
TokenSmallest unit the LLM sees, ≈ a subword"hello world" ≈ 2 tokens
Context windowMax tokens the LLM can see at onceThe exact limit depends on model and runtime config; for harness design, the key issue is compaction, truncation, and reloading persistent memory near the limit
APIHow programs call programs; here: LLM REST APIsSend HTTP, get JSON back
StreamingReturn tokens as they are generatedLower latency, pipeline next step earlier
Function calling / Tool useLLM returns structured "please call this tool" JSONPrerequisite for an agent to "do things"
Prompt cacheServer-side cache of long system promptUp to 10× cheaper, lower latency
SandboxConfined process env (FS/network limited)Keeps agent from wrecking your machine
ProviderLLM vendor (Anthropic / OpenAI / Google)Pick one, or write adapters
TurnOne user message + full agent response cycle"One turn" = the main loop runs a full pass
ReActReasoning + Acting loop: think → act → thinkMost systems here are ReAct variants
MCPModel Context Protocol for external toolsLets agents plug in any 3rd-party tool
CLAUDE.md / AGENTS.mdRoot-level project convention fileRead at startup; a "README for bots"
Plan-and-executeAsk the model to plan first, then execute step by stepopencode's plan mode, claw-code's EnterPlanMode
ReflectionAgent self-reviews after acting; retries on error§8 Takeaway · common auxiliary loop
ToolsTyped functions the agent can call (read file, run bash, browse…)§2 Tools vs Skills table
SkillsMarkdown files (SKILL.md) teaching when/how to use tools§2 · anthropics/skills
SubagentChild agent spawned by a parent; isolated context; returns summary onlyhermes delegate_task, opencode @general/@explore
Orchestration"Who does what, in what order, with what fallback" — the harness's outer layerTop row of §2's five-layer map
HookUser-configured script run at lifecycle moments (before / after a tool call); can intercept, modify, veto, or logclaw-code's PreToolUse / PostToolUse
AdapterTranslation layer between a generic agent loop and a specific provider's API; swap adapter → swap provider, loop unchangedhermes's anthropic_adapter / gemini_native
CompactionAuto-summarise old turns when history exceeds the context window, preserving head and tailclaw-code's auto-compact · hermes's trajectory compression
RolloutOne full start-to-end turn sequence of an agent; in RL you run hundreds in parallel§9 hermes's Modal cloud VM per rollout
SSEServer-Sent Events, a one-way HTTP streaming protocolopencode pushes agent events from server to TUI over SSE
ACPAgent Client Protocol; a stdio protocol between an IDE and an agent (pushed by Zed / Cursor)openclaw's acp bridge, opencode's acp support
LSPLanguage Server Protocol; the standard protocol between an IDE and a language server (goto-definition, find-references, diagnostics, ...)opencode ships LSP as a first-class tool
CDPChrome DevTools Protocol; a wire protocol for programmatically controlling Chromium (the foundation of headless browser automation)openclaw's browser sandbox drives the agent via CDP
noVNCA VNC client that runs in the browser, letting you view a sandbox's GUI desktop over HTTPopenclaw's port 6080 lets you "watch the agent click around in Chromium"
RL (Reinforcement Learning)A training paradigm where the agent learns by trial and error, scored by a "reward function"; usually runs hundreds of rollouts in parallel§9 discusses why hermes suits training — it's designed as an RL-friendly harness
ModalA serverless cloud-VM provider with per-second billing and sub-second cold start; an agent can launch one isolated VM per rollouthermes's RL sandbox runs on Modal
AWS BedrockAWS-managed LLM API gateway that serves Claude, Llama, Mistral and others behind one interfacehermes's bedrock_adapter targets it
OpenRouterThird-party LLM-routing service: one API key calls every major provider, with automatic rate-limit / fallback handlingsupported as a hermes provider
OS sandboxes (seatbelt / landlock / bubblewrap / seccomp)OS-level process-isolation primitives: seatbelt (macOS sandbox-exec) · landlock (Linux capability-dropping kernel feature) · bubblewrap (userland container) · seccomp (syscall allowlist)codex's default mode uses this stack to bound filesystem and network access
Vercel AI SDKProvider-agnostic TypeScript SDK from Vercel that abstracts streaming / tool-calling / reasoning across vendorsopencode's provider layer uses it; adding a new vendor is a config one-liner
Self-evolvingAgent that writes new SKILL.md files, updates prompts, or augments memory at runtime — so the next run starts from a higher baseline; a step beyond reflection, where what was learned persistsThe Skills layer is the persistence entry point · hermes's skill_manage tool · a natural next step after §8 Reflection

2. 五层概念地图: Prompt → Harness2. The five-layer stack: prompt to harness

2023 年大家对着 prompt 雕花; 2024 年重心转到 context engineering (检索、memory、压缩); 2025 年前沿又往上走了两层——SkillsHarness。本页比的六个开源项目, 本质上都是 harness 工程的不同答卷。

In 2023 the craft was prompt engineering. In 2024 it moved to context engineering — retrieval, memory, compaction. In 2025 the frontier climbed two more layers: Skills and Harnesses. The six projects on this page are all different takes on harness engineering.

管什么产物 / 例子在本页哪里体现
Harness Engineering 主循环 · 沙箱 · 预算 · hook · session · channel hermes-agent / claw-code / codex / opencode / openclaw / pi 都属于 harness 或 harness runtime §4 流程图 + §6 深度拆解
Skills "什么时候 / 怎么用工具"——可复用的 procedural knowledge Anthropic SKILL.md(markdown + YAML frontmatter), anthropics/skills, agentskills.io spec openclaw 文档、hermes skill_manage、claw-code /Claude Code Skill 工具
Tools "agent 能调什么"——typed 函数 bash / read / write / browser / MCP §4 流程图里绿色节点 + §1 术语表
Context Engineering "窗口里装什么"——检索、memory、compaction、prompt cache RAG、MEMORY.md、auto-compaction、cache_control §1.2 function calling · §6 各家的压缩策略
Prompt Engineering "输入文本怎么写" system prompt · few-shot · chain-of-thought 所有层都建立在它之上
LayerConcernArtifacts / examplesWhere it shows on this page
Harness Engineering Main loop · sandbox · budget · hook · session · channel hermes-agent / claw-code / codex / opencode / openclaw / pi are all harnesses or harness runtimes §4 diagrams + §6 deep dives
Skills "When and how to use tools" — reusable procedural knowledge Anthropic SKILL.md (markdown + YAML frontmatter), anthropics/skills, agentskills.io spec openclaw docs, hermes skill_manage, Claude Code Skill tool
Tools "What the agent can call" — typed functions bash / read / write / browser / MCP Green nodes in §4 diagrams + glossary in §1
Context Engineering "What goes in the window" — retrieval, memory, compaction, cache RAG, MEMORY.md, auto-compaction, cache_control §1.2 function calling · §6 each project's compaction story
Prompt Engineering "How to phrase the input" System prompt · few-shot · chain-of-thought Every layer above stands on it

一句话记法: Prompt Engineering 是措辞; Context Engineering 是窗口里装什么; Tools 是能干什么; Skills 是什么时候怎么干; Harness Engineering 是整个外骨骼——没有它 LLM 的脑袋没地方安手。

One-line summary: Prompt Engineering is wording; Context Engineering is what fits in the window; Tools are what the agent can do; Skills are when and how to do it; Harness Engineering is the exoskeleton — without it, the LLM brain has nowhere to attach its hands.

3. Claude Code 重点理解3. Understanding Claude Code

Claude Code 最该被理解成一个 agentic harness, 而不是"Claude 加了几个 shell 命令"。它的核心价值不在模型本身, 而在外层状态机: 怎么把用户输入、项目上下文、工具 schema、权限策略、hook、工具结果、压缩摘要组织成一个可持续运行的 turn loop。官方文档把这个循环概括成 gather context → take action → verify results; 本文把它拆成更工程化的状态转移。

Claude Code is best understood as an agentic harness, not "Claude plus a few shell commands." Its value is the outer state machine: how user input, project context, tool schemas, permission policy, hooks, tool results, and compaction summaries are organized into a durable turn loop. The official docs describe the loop as gather context → take action → verify results; this post expands it into implementation-level state transitions.

Claude Code 状态它在做什么为什么重要
Context Assembly读 system prompt、CLAUDE.md / skills / conversation history / tool schemas, 组装本轮请求。决定模型"看见什么"; 这比单句 prompt 更接近真实能力上限。
Model Step流式调用模型, 输出自然语言或结构化 tool_use模型不直接执行动作, 只声明"我想调用什么工具"。
PreToolUse工具执行前先跑 hook, 可以改参、拒绝、要求确认、推迟、补充上下文。这是 Claude Code 的治理入口: 用户能写程序影响 agent, 但强制权限规则仍会评估。
Permission根据工具类型、路径、命令危险度、用户策略做 allow / ask / deny。把"模型想做"和"系统允许做"分开, 防止工具失控。
Execute + Observeharness 执行真实 shell / 文件 / MCP 工具, 把结果作为 tool_result 放回消息历史。LLM 的行动能力来自这里; 它通过观察结果进入下一步推理。
Loop / Terminate如果还有 tool call 就回到下一次 model step; 如果没有 tool call, 本 turn 结束。这就是 coding agent 能多步修 bug 的原因。
Compaction上下文过长时摘要旧历史, 保留关键状态。长任务能继续跑, 不会因为 context 爆掉直接失忆。
Claude Code stateWhat it doesWhy it matters
Context AssemblyLoad system prompt, CLAUDE.md / skills / conversation history / tool schemas, then assemble the request.Determines what the model can see; this matters more than any single prompt.
Model StepStream the model; receive either natural language or structured tool_use.The model does not act directly; it declares which tool it wants.
PreToolUseRun hooks before execution; rewrite input, deny, ask, defer, or add context.This is the governance surface: users can program the agent, while enforced permission rules still apply.
PermissionAllow / ask / deny based on tool type, path, command risk, and user policy.Separates "the model wants" from "the system permits."
Execute + ObserveThe harness runs shell / file / MCP tools and appends tool_result back into history.This is where action happens; the model learns the result by observation.
Loop / TerminateIf tool calls remain, go back to the model; if none remain, end the turn.This is why a coding agent can fix bugs over multiple steps.
CompactionSummarize old history when context is too long, preserving important state.Long tasks can continue instead of losing the session.

最关键的 transition: assistant_message has ToolUse → 进工具管线; no ToolUse → 进入 stop/结束检查; hook 或 permission denied → 生成 error tool_result 让模型读到; context too long → compact 后继续。这四个分支就是 Claude Code 状态机的骨架。

The key transitions: assistant_message has ToolUse → enter the tool pipeline; no ToolUse → enter stop/finalization checks; hook or permission denied → append an error tool_result for the model to read; context too long → compact then continue. Those four branches are the backbone of the Claude Code state machine.

Tools 和 Skills 的分工是 Anthropic 2025 年在 Agent Skills 博客 + anthropics/skills 仓库里推的核心抽象。openclaw 把它复述为一句话: "Tools are what the agent calls; Skills teach the agent when and how."

The Tools / Skills split is the core abstraction Anthropic pushed in 2025 (see their Agent Skills blog and the anthropics/skills repo). openclaw restates it as: "Tools are what the agent calls; Skills teach the agent when and how."

Having all five layers in place only means the system is theoretically capable; whether it actually works requires real-task success rates. That's exactly what ClawBench measures: live web tasks that grade each layer end-to-end, not offline DOM snapshots you can game.

Skill 的标准: SKILL.mdThe SKILL.md standard

github.com/anthropics/skills 就是这个标准的官方参考实现——Anthropic 发布的 Skill 示例合集, 也是 SKILL.md 格式的源头。每个 skill 是一个文件夹, 里面有一个必写文件 SKILL.md: YAML frontmatter 头 (name + description) + Markdown 正文。Claude 在 session 里看到相关任务时, 按 description 自动挂载、读正文、照做。

github.com/anthropics/skills IS the reference repository for this standard — Anthropic's official collection of Skill examples and the origin of the SKILL.md format. Each skill is a folder containing one required file SKILL.md: YAML frontmatter (name + description) followed by a Markdown body. Claude auto-mounts the skill when the description matches the task, reads the body, and follows it.

# 最小模板(来自 anthropics/skills/template/SKILL.md):
---
name: my-skill-name
description: A clear description of what this skill does and when to use it
---

# My Skill Name

[Add your instructions here that Claude will follow when this skill is active]

## Examples · Guidelines · Reference files · etc.
# Minimum template (from anthropics/skills/template/SKILL.md):
---
name: my-skill-name
description: A clear description of what this skill does and when to use it
---

# My Skill Name

[Add your instructions here that Claude will follow when this skill is active]

## Examples · Guidelines · Reference files · etc.

真实例子: anthropics/skills/skills/pdf/SKILL.md 的 description 写得很细——"用户要读 PDF / 合并 / 分页 / 旋转 / 水印 / OCR 时用这个 skill"——Claude 看到这些关键字就自动挂上。正文里放 Python 代码片段、命令行工具指引、REFERENCE.md 链接等可复用知识。官方仓库会持续增删 skill, 所以公开引用数量时要带检查日期。

Real example: anthropics/skills/skills/pdf/SKILL.md has a very precise description — "use this skill whenever the user wants to read PDFs / merge / split / rotate / watermark / OCR" — Claude auto-invokes on those keywords. The body contains Python snippets, CLI guidance, links to REFERENCE.md, etc. The official repo changes over time, so cite counts with a last-checked date.

Tools vs Skills 对照表Tools vs Skills side-by-side

维度ToolsSkills
是什么带类型签名的函数带 YAML frontmatter 的 Markdown 文件夹
谁执行harness 执行 (调真实 API / shell / FS)LLM 自己读完照做 (instructions + 参考资料)
回答的问题"agent 调什么?""什么时候 / 怎么调?"
进入上下文schema 列在 tools[]description 常驻, 正文按需挂载
跨 harness 复用每家 harness 都要自己实现同一 SKILL.md 任何支持的 agent 都能装
例子bashreadwritebrowser、MCP 工具pdfmcp-builderfrontend-design
DimensionToolsSkills
WhatTyped function with a signatureFolder of Markdown with YAML frontmatter
ExecutorThe harness runs it (hits real APIs / shell / FS)The LLM reads it and follows (instructions + refs)
Question"What can the agent call?""When and how should it call things?"
Context costSchema sits in tools[]Description always loaded; body mounted on demand
PortabilityEach harness re-implementsSame SKILL.md works on any compatible agent
Examplesbash, read, write, browser, MCP toolspdf, mcp-builder, frontend-design

在本页这些项目里对号入座: Claude Code (≈ claw-code) 提供一级 Skill 工具直接挂载 SKILL.md; openclaw 文档大篇幅讲 Skills; hermes-agent 提供 skill_view / skills_list / skill_manage 三件工具, 按 agentskills.io spec 加载 SKILL.md; opencode 以 Markdown frontmatter 定义 agent 接近此思路; codex 没有一等 Skill 概念, 用 AGENTS.md 做类 CLAUDE.md 的项目注入; pi 把这类能力推到 extensions / packages。

How these projects relate: Claude Code (≈ claw-code) ships a first-class Skill tool that mounts SKILL.md; openclaw devotes major docs space to Skills; hermes-agent provides skill_view / skills_list / skill_manage tools that load SKILL.md per the agentskills.io spec; opencode's Markdown-frontmatter agents sit close to this idea; codex has no first-class Skill concept — it uses AGENTS.md like CLAUDE.md for per-project instructions; pi pushes this surface into extensions / packages.

3.5 相关旁支:Flue Framework3.5 Related Aside: Flue Framework

Flue Framework 不是下面六个系统之一,而是一个外部参照点。它把本页讨论的"五层栈"重新画成四层模型: Model · Harness · Sandbox · Filesystem。它的口号 "Not another SDK" 表明态度——不是再造一套 chat 抽象, 而是给 harness 这一层提供可编程的 TypeScript 控制面。把它放进本页, 价值在于: 它用外部视角验证了 §2 五层地图里 harness 是真正的工程主轴。

Flue Framework is not one of the six systems compared below; it is an external reference point for the harness layer. It recasts the "five-layer stack" of this page into a four-layer model: Model · Harness · Sandbox · Filesystem. Its slogan "Not another SDK" is a stance — it doesn't add another chat abstraction; it offers a programmable TypeScript control plane at the harness layer. It earns a place here because it echoes §2's framing: the harness is a major engineering axis.

Flue 的四层 ↔ 本页五层Flue's four layers ↔ the five-layer stack

Flue 层它管什么对应本页 §2 哪一层
Modeltokens · tools · promptsPrompt + Context + Tools
Harnessskills · memory · sessionsSkills + Harness Engineering
Sandboxbash 执行 · 隔离 · 网络管理Harness Engineering 里的 sandbox 子模块
Filesystemread / write / grep / globTools 层的核心成员
Flue layerWhat it ownsMaps to §2 layer
Modeltokens · tools · promptsPrompt + Context + Tools
Harnessskills · memory · sessionsSkills + Harness Engineering
Sandboxbash exec · isolation · network policySandbox sub-module of Harness Engineering
Filesystemread / write / grep / globCore members of the Tools layer

三个一等概念Three first-class primitives

概念Flue 怎么定义对照其他 harness
Session持续的工作状态容器, 可挂 skill / 跑 prompt / 执 shell≈ Claude Code 的 turn loop · opencode 的 session.sql · openclaw 的 session 路由
Skill结构化输入输出的可复用 workflow (例: triage(issueNumber) → typed result)比 Anthropic SKILL.md 更像"带 schema 的子程序"——更接近 hermes 的 delegate_task + skill
Sandbox三档可换: 内置零配置 virtual sandbox / 远程容器 (Daytona) / 云后端 (Cloudflare Durable Object + SQLite + R2)codex 走 OS 原语 (seatbelt/landlock); hermes 走 Modal 云 VM; Flue 把这条选项做成plug-in
ConceptFlue's definitionCounterpart in this comparison
SessionDurable work-state container; you can mount skills, run prompts, exec shell≈ Claude Code's turn loop · opencode's session.sql · openclaw's session routing
SkillReusable workflow with structured I/O (e.g. triage(issueNumber) → typed result)More "schema'd subroutine" than Anthropic's SKILL.md — closer to hermes's delegate_task + skill
SandboxThree pluggable backends: built-in virtual sandbox / remote container (Daytona) / cloud (Cloudflare Durable Object + SQLite + R2)codex uses OS primitives (seatbelt/landlock); hermes uses Modal VMs; Flue makes the choice swappable

两个值得抄的设计Two ideas worth lifting

Flue 在六个项目对比中的坐标Where Flue sits relative to the six projects

维度Flue最像谁不一样在哪
语言TypeScriptopencode (TS+Go) · openclaw (TS)纯 TS, 不需要 Go runtime
形态SDK / library, 用户用 TS 写 agent 入口opencode 的 core library更彻底——没有自带 TUI, 部署形态完全交给用户
Sandbox三档可插hermes (Modal) · openclaw (Docker)把"挑哪个 sandbox"做成配置而不是源码 fork
定位"自主代理可编程控制面"偏 opencode 的服务化思路更强调"全栈自控": agent 逻辑 + harness + sandbox 都在你这边
DimensionFlueClosest siblingHow it differs
LanguageTypeScriptopencode (TS+Go) · openclaw (TS)Pure TS — no Go runtime needed
ShapeSDK / library; users write the agent entry in TSopencode's core libraryMore radical — no bundled TUI, deployment shape is fully user-decided
SandboxThree pluggable backendshermes (Modal) · openclaw (Docker)Backend choice is a config knob, not a source fork
Stance"Programmable control plane for autonomous agents"opencode's service-shaped approachPushes harder on full-stack ownership: agent logic + harness + sandbox all yours

一句话定位: 如果 §2 把 harness 列为 2025 年最值得做的工程层, Flue 就是把这层做成一个 TS package的最直接尝试——它没有把"agent"当成产品, 而是当成由你写的 TS 代码 + 一个标准 harness runtime

One-line placement: if §2 names harness engineering as the layer of 2025, Flue is the most literal attempt to ship that layer as a TS package — it doesn't treat "agent" as a product, but as your TS code on top of a standard harness runtime.

4. 六个流程图4. Six diagrams

下面六张图讲的是这些 harness 怎么运作;想量化它们到底 work 得多好, 用我们的 ClawBench 在真实网页任务上跑一跑就知道。The six diagrams below show how these harnesses work; to quantify how well they actually work, run them against our ClawBench on live web tasks.

hermes-agent

Python · multi-provider
ReAct 循环 + 共享迭代预算 + 子代理委派。 ReAct loop with shared iteration budget and sub-agent delegation.
flowchart TD U([User message]):::io A[Apply prompt cache + memory · every 10 turns]:::ctx M{{Adapter.stream · Anthropic · Bedrock · Gemini}}:::model P[Parse tool_calls · preserve reasoning_content]:::model R[ToolRegistry.dispatch · built-in tools]:::tool S{delegate_task?}:::decision SA[[Spawn sub-agent · shared IterationBudget]]:::sub RES[Append tool results]:::tool C[ContextCompressor · if near context limit]:::ctx B{budget > 0?}:::decision Y([Return final message]):::io U --> A --> M --> P --> R --> S S -- yes --> SA --> RES S -- no --> RES RES --> C --> B B -- yes --> A B -- no --> Y class U step1 class A step2 class M step3 class P step4 class R step5 class SA step6 class RES step7 class C step8 class B step9 click U call jumpTo("hermes", 1) click A call jumpTo("hermes", 2) click M call jumpTo("hermes", 3) click P call jumpTo("hermes", 4) click R call jumpTo("hermes", 5) click SA call jumpTo("hermes", 6) click RES call jumpTo("hermes", 7) click C call jumpTo("hermes", 8) click B call jumpTo("hermes", 9) classDef io fill:#233042,stroke:#7aa2f7,color:#e6e8ef; classDef model fill:#2b1f3a,stroke:#bb9af7,color:#e6e8ef; classDef tool fill:#1f3a2b,stroke:#9ece6a,color:#e6e8ef; classDef sub fill:#3a2b1f,stroke:#e0af68,color:#e6e8ef; classDef ctx fill:#3a1f2b,stroke:#f7768e,color:#e6e8ef; classDef decision fill:#2d2d3a,stroke:#8a93a6,color:#e6e8ef;
Step 0 / 9
  • 共享预算——父 + 所有子代理共用一个 IterationBudget, 不会 fork-bomb。
  • 临时注入——memory nudge (读取 MEMORY.mdUSER.md) 只在 API 调用时加, 不污染 prompt cache 前缀。
  • Adapter 分发——一套 loop 对接 N 家 provider; 错误分类器自动切换。
  • Modal 沙箱——每个 rollout 独立云 VM, RL 奖励函数看到一致的 FS 状态。
  • Shared budget — parent + all sub-agents draw from one IterationBudget; can't fork-bomb.
  • Ephemeral injections — memory nudges (reading MEMORY.md and USER.md) added at API time only, keeping cache prefix stable.
  • Adapter fan-out — one loop, N providers; error classifier routes failures.
  • Modal sandbox — each rollout in its own cloud VM; RL reward funcs see identical FS.
run_agent.py:634 — max_iterations=90 default run_agent.py:730 — IterationBudget init run_agent.py:8076 — delegate_task dispatch run_agent.py:100 — apply_anthropic_cache_control

claw-code

Rust · hooks-first
Claude Code 风格状态机: 模型流式产出, 有 ToolUse 就 hook → permission → execute → hook, 没有 ToolUse 就进入结束检查。 Claude Code-style state machine: stream model output; ToolUse triggers hook → permission → execute → hook; no ToolUse enters finalization checks.
flowchart TD U([User message]):::io B[BootstrapPlan — 12 phases, once per session]:::ctx L[Assemble ApiRequest · system_prompt + messages]:::ctx API{{ApiClient.stream · AssistantEvent · PromptCacheEvent}}:::model TU[Parse ToolUses]:::model H1[PreToolUse hook · allow · ask · deny · defer · modify]:::gate PG[PermissionPolicy · authorize_with_context]:::gate EX[Execute tool · bash · file · mcp · web]:::tool H2[PostToolUse hook · success or failure]:::gate CMP[Auto-compact + health probe]:::ctx TS([TurnSummary · persist Session]):::io U --> B --> L --> API --> TU --> H1 --> PG --> EX --> H2 --> CMP CMP -- more tools --> L CMP -- done --> TS class U step1 class B step2 class L step3 class API step4 class TU step5 class H1 step6 class PG step7 class EX step8 class H2 step9 class CMP step10 click U call jumpTo("claw", 1) click B call jumpTo("claw", 2) click L call jumpTo("claw", 3) click API call jumpTo("claw", 4) click TU call jumpTo("claw", 5) click H1 call jumpTo("claw", 6) click PG call jumpTo("claw", 7) click EX call jumpTo("claw", 8) click H2 call jumpTo("claw", 9) click CMP call jumpTo("claw", 10) classDef io fill:#233042,stroke:#7aa2f7,color:#e6e8ef; classDef model fill:#2b1f3a,stroke:#bb9af7,color:#e6e8ef; classDef tool fill:#1f3a2b,stroke:#9ece6a,color:#e6e8ef; classDef gate fill:#3a2b1f,stroke:#e0af68,color:#e6e8ef; classDef ctx fill:#3a1f2b,stroke:#f7768e,color:#e6e8ef;
Step 0 / 10
  • Hook 先于权限——PreToolUse hook 可以在权限引擎之前否决、要求确认、推迟或改写调用; 强制 deny/ask 规则仍是最终安全边界。
  • 主循环无子代理——task registry 只做异步后台; 多 agent 协作被推到 context 外。
  • 有 provenance 的压缩——摘要记录为 SessionCompaction 事件 + 健康探针。
  • Workspace 绑定——workspace_root 防并行 lane 写错 CWD。
  • Hooks before permissions — a PreToolUse hook can deny, ask, defer, or rewrite a call before the policy engine; enforced deny/ask rules remain the safety boundary.
  • No in-loop sub-agents — task registry is for async background only; multi-agent coord pushed outside.
  • Auto-compaction with provenance — summaries logged as SessionCompaction events + health probe.
  • Workspace bindingworkspace_root prevents parallel lanes writing to wrong CWD.

状态转移速读State transitions

当前状态State 触发条件Trigger 下一状态Next
UserInput 用户输入被追加到 session messagesUser message appended to session messages BuildRequest
BuildRequest system prompt + 历史 messages 组装完成System prompt + history assembled ModelStream
ModelStream assistant message 没有 ToolUse blockAssistant message has no ToolUse block TurnDone
ModelStream 解析到一个或多个 ToolUse blockOne or more ToolUse blocks parsed PreToolUse
PreToolUse hook 允许、改写、要求询问或直接拒绝Hook allows, rewrites, asks, or denies Permission / ToolResult(error)
Permission policy allow / ask / denyPolicy allows / asks / denies ExecuteTool / ToolResult(error)
ExecuteTool 工具 stdout/stderr 或结构化结果返回Tool stdout/stderr or structured result returned PostToolUse
PostToolUse tool_result 被追加回 messages; 本轮工具全部处理完Tool result appended to messages; all tool calls processed BuildRequest
conversation.rs:314 — run_turn() conversation.rs:414 — PreToolUse hook gate conversation.rs:432 — authorize_with_context compact.rs:96 — compact_session hooks.rs:23 — HookEvent enum

codex

OpenAI Responses API
结构化 output[] 流 + 原生推理项 + 沙箱 bash。 Structured output[] stream with first-class reasoning items and sandboxed bash.
flowchart TD U([User message]):::io K[_build_api_kwargs · instructions · tools · reasoning.effort]:::ctx ST{{responses.stream · with reasoning.encrypted_content}}:::model FB[[Fallback — responses.create stream · synthesize from deltas]]:::model N[_normalize_codex_response · parse output array]:::model RS[codex_reasoning_items · dedup by ID across turns]:::ctx PP[PermissionPolicy · ReadOnly · WorkspaceWrite · DangerFull]:::gate SB[Exec in sandbox · seatbelt · landlock]:::tool AP[Append tool result]:::tool CK{incomplete or commentary}:::decision Y([Return message]):::io U --> K --> ST ST -- transport err --> FB --> N ST --> N --> RS RS --> CK CK -- function_call --> PP --> SB --> AP --> K CK -- commentary --> K CK -- completed --> Y class U step1 class K step2 class ST step3 class FB step4 class N step5 class RS step6 class PP step7 class SB step8 class AP step9 class CK step10 click U call jumpTo("codex", 1) click K call jumpTo("codex", 2) click ST call jumpTo("codex", 3) click FB call jumpTo("codex", 4) click N call jumpTo("codex", 5) click RS call jumpTo("codex", 6) click PP call jumpTo("codex", 7) click SB call jumpTo("codex", 8) click AP call jumpTo("codex", 9) click CK call jumpTo("codex", 10) classDef io fill:#233042,stroke:#7aa2f7,color:#e6e8ef; classDef model fill:#2b1f3a,stroke:#bb9af7,color:#e6e8ef; classDef tool fill:#1f3a2b,stroke:#9ece6a,color:#e6e8ef; classDef gate fill:#3a2b1f,stroke:#e0af68,color:#e6e8ef; classDef ctx fill:#3a1f2b,stroke:#f7768e,color:#e6e8ef; classDef decision fill:#2d2d3a,stroke:#8a93a6,color:#e6e8ef;
Step 0 / 10
  • Responses API, 不是 Chat Completions——输出是 typed output[](message / function_call / reasoning)。
  • 推理跨 turn 保留——include: ["reasoning.encrypted_content"], 按 ID 去重。
  • 三级流式回退——stream() → 重试 → create(stream=True) → 从 deltas 合成, 永不静默掉 turn。
  • OS 级沙箱——seatbelt (macOS) / landlock (Linux) 在 bash 执行前 gate FS/网络。
  • Responses API, not Chat Completions — typed output[] of message · function_call · reasoning.
  • Reasoning across turnsinclude: ["reasoning.encrypted_content"], deduplicated by ID.
  • Streaming fallback cascadestream() → retry → create(stream=True) → synthesize.
  • OS-level sandbox — seatbelt / landlock gate FS/network before bash.
run_agent.py:5168 — _run_codex_stream run_agent.py:5183 — responses.stream(**api_kwargs) run_agent.py:5297 — fallback responses.create run_agent.py:4640 — _normalize_codex_response run_agent.py:7266 — reasoning ID dedup

opencode

TS server + Go TUI
客户端-服务端分离 · HTTP · 任何前端都能驱动同一个 core。 Client-server split over HTTP — any frontend drives the same agent core.
flowchart TD TUI([Go TUI / Web / IDE]):::io SRV[POST /session/:id/message · → Bun server loop]:::ctx MODE{Agent mode}:::decision AI{{Vercel AI SDK stream · Anthropic · OAI · Google · Copilot · local}}:::model TC[Tool-call parts]:::model PG[Permission gate · allow · ask · deny + wildcards]:::gate EX[Tool executor · bash · edit · read · grep · lsp · mcp]:::tool SUB[[Subagent · general · explore]]:::sub APP[Append result]:::tool CC[Compaction / summary / title · hidden system agents]:::ctx SSE([SSE /global/event · → TUI renders parts]):::io TUI --> SRV --> MODE MODE -->|build: full tools| AI MODE -->|plan: read-only, ask first| AI AI --> TC --> PG --> EX EX --> SUB --> APP EX --> APP APP --> CC CC --> AI CC -.stream events.-> SSE class TUI step1 class SRV step2 class MODE step3 class AI step4 class TC step5 class PG step6 class EX step7 class SUB step8 class APP step9 class CC step10 click TUI call jumpTo("opencode", 1) click SRV call jumpTo("opencode", 2) click MODE call jumpTo("opencode", 3) click AI call jumpTo("opencode", 4) click TC call jumpTo("opencode", 5) click PG call jumpTo("opencode", 6) click EX call jumpTo("opencode", 7) click SUB call jumpTo("opencode", 8) click APP call jumpTo("opencode", 9) click CC call jumpTo("opencode", 10) classDef io fill:#233042,stroke:#7aa2f7,color:#e6e8ef; classDef model fill:#2b1f3a,stroke:#bb9af7,color:#e6e8ef; classDef tool fill:#1f3a2b,stroke:#9ece6a,color:#e6e8ef; classDef sub fill:#3a2b1f,stroke:#e0af68,color:#e6e8ef; classDef gate fill:#3a2b1f,stroke:#e0af68,color:#e6e8ef; classDef ctx fill:#3a1f2b,stroke:#f7768e,color:#e6e8ef; classDef decision fill:#2d2d3a,stroke:#8a93a6,color:#e6e8ef;
Step 0 / 10
  • HTTP 作为边界——TUI / Web / IDE 都走 /session/*; OpenAPI 3.1 spec 在 /doc(server.ts), 配合 mDNS broadcast (server/mdns.ts), 任何客户端都能自动发现并生成 SDK。
  • build 与 plan 模式——同一套工具, 不同权限映射: build(默认模式) 放行 edit/write/bash; plan 把写类工具 (edit/write/patch/bash) 降到 askdeny(默认行为因 agent.ts 内置 + 用户 config 合并而定), 只读工具自动放行。一个 loop, 两种人格。
  • Provider 无关——Vercel AI SDK 把 streaming / tool-calling / reasoning 下放到各 adapter。
  • 一等 LSP + MCP——代码智能和外部工具与原生工具并列。
  • HTTP as the boundary — TUI / web / IDE all speak to /session/*; OpenAPI 3.1 spec at /doc (server.ts) and mDNS broadcast (server/mdns.ts) let any client discover and generate an SDK.
  • Build vs plan modes — same tool surface, different permission maps: build (default) allows edit/write/bash; plan lowers write-class tools (edit/write/patch/bash) to ask or deny depending on built-in defaults plus user config. Read-only tools flow through. One loop, two personas.
  • Provider-agnostic — Vercel AI SDK delegates streaming / tool / reasoning to each adapter.
  • First-class LSP + MCP — code intelligence and external tools sit beside native ones.
packages/opencode/src/tool — native tools packages/opencode/src/mcp — MCP client packages/opencode/src/lsp — LSP bridge packages/tui — Go TUI client (SSE consumer)

openclaw

TS · multi-channel gateway
本地 Gateway + 多通道 (IM/CLI/iOS/IDE) + Docker 浏览器沙箱。 Local Gateway + many channels (IM/CLI/iOS/IDE) + Dockerised browser sandbox.
flowchart TD CH([Channel input · IM · CLI · iOS · IDE · 10+ providers]):::io GW[Gateway · local-first orchestrator]:::ctx SR[Resolve session · history · DM pairing]:::ctx BP[Build payloads · system + tools + schemas]:::ctx PR{{Provider plugin · Anthropic · OpenAI · Google · ...}}:::model MC[Parse text + tool_calls · streaming deltas]:::model TP[ToolPolicy pipeline · per sandbox + channel]:::gate EX[Execute tool · bash · file · canvas]:::tool BR[[Browser sandbox · Docker + Chromium + CDP + noVNC]]:::sub CMP[Async compaction · if near context limit]:::ctx OUT(["Emit events → all channels"]):::io CH --> GW --> SR --> BP --> PR --> MC --> TP --> EX EX -- browser tool --> BR --> EX EX --> CMP CMP -- more tools --> PR CMP -- done --> OUT class CH step1 class GW step2 class SR step3 class BP step4 class PR step5 class MC step6 class TP step7 class EX step8 class BR step9 class CMP step10 click CH call jumpTo("openclaw", 1) click GW call jumpTo("openclaw", 2) click SR call jumpTo("openclaw", 3) click BP call jumpTo("openclaw", 4) click PR call jumpTo("openclaw", 5) click MC call jumpTo("openclaw", 6) click TP call jumpTo("openclaw", 7) click EX call jumpTo("openclaw", 8) click BR call jumpTo("openclaw", 9) click CMP call jumpTo("openclaw", 10) classDef io fill:#233042,stroke:#7aa2f7,color:#e6e8ef; classDef model fill:#2b1f3a,stroke:#bb9af7,color:#e6e8ef; classDef tool fill:#1f3a2b,stroke:#9ece6a,color:#e6e8ef; classDef sub fill:#3a2b1f,stroke:#e0af68,color:#e6e8ef; classDef gate fill:#3a2b1f,stroke:#e0af68,color:#e6e8ef; classDef ctx fill:#3a1f2b,stroke:#f7768e,color:#e6e8ef;
Step 0 / 10
  • 多通道路由——Discord / Slack / Telegram / WhatsApp / iMessage / Signal / Matrix / Microsoft Teams / Google Chat / Zalo 等 IM 消息都进同一个 Gateway(长驻守护进程), CLI / iOS / IDE 则作为额外入口, 全部路由到同一组 session。
  • Embedded Runner + Gateway 拆分——agent 核心可嵌入 CLI / 浏览器 / 远端, Gateway 管 channels / cron / auth。
  • Docker 浏览器沙箱——Chromium + CDP + noVNC, 操作可视化调试, 自动化与"我来看它点什么"并存。
  • ACP 桥接 IDE——openclaw acp 暴露 stdio 协议, Zed / Cursor 可直接驱动同一 agent。
  • Multi-channel routing — IM traffic from Discord / Slack / Telegram / WhatsApp / iMessage / Signal / Matrix / Teams / Google Chat / Zalo all feeds one Gateway (long-lived daemon); CLI / iOS / IDE act as additional entry points, all landing in shared sessions.
  • Embedded runner ↔ Gateway split — core agent is portable (CLI / browser / remote); Gateway owns channels / cron / auth.
  • Dockerised browser sandbox — Chromium + CDP + noVNC; automation while you can watch it click.
  • ACP bridge to IDEsopenclaw acp exposes stdio protocol; Zed / Cursor drive the same agent.
src/agents/pi-embedded-runner/run.ts — main turn loop src/agents/pi-tools.ts — tool registry + lazy loading src/agents/sandbox/browser.ts — Docker CDP browser src/agents/pi-embedded-runner/compact.ts — async compaction src/acp/session.ts — ACP ↔ Gateway bridge

pi

TypeScript · minimal harness
4 个工具默认 (read/write/edit/bash); 一切其它特性住在 TS Extensions / Skills / Packages 里。 Four default tools (read/write/edit/bash); every other feature lives in TS Extensions / Skills / Packages.
flowchart TD U([User input · Enter = steer · Alt+Enter = queue]):::io AS[AgentSession · assemble system + AGENTS.md + skills + history]:::ctx PR{{Provider · 15+ via OAuth or API key · /model switches mid-session}}:::model ST[Stream typed events · text · tool_use · usage]:::model EX[ExtensionRunner · before-tool hook · may rewrite or block]:::gate T4[[Default tools · read / write / edit / bash]]:::tool EXT[[Extension tools · sub-agent / plan / MCP / sandbox · user-installed]]:::sub TR[Session tree · append message · parentID for branching]:::ctx CMP[Compaction · replaceable strategy · default summary-rewrite]:::ctx OUT(["Render to TUI · or emit JSON · or return via SDK"]):::io U --> AS --> PR --> ST --> EX EX -- default --> T4 EX -- extension --> EXT T4 --> TR EXT --> TR TR --> CMP CMP -- more tool_use --> PR CMP -- done --> OUT OUT -- user steers --> AS class U step1 class AS step2 class PR step3 class ST step4 class EX step5 class T4 step6 class EXT step7 class TR step8 class CMP step9 class OUT step10 click U call jumpTo("pi", 1) click AS call jumpTo("pi", 2) click PR call jumpTo("pi", 3) click ST call jumpTo("pi", 4) click EX call jumpTo("pi", 5) click T4 call jumpTo("pi", 6) click EXT call jumpTo("pi", 7) click TR call jumpTo("pi", 8) click CMP call jumpTo("pi", 9) click OUT call jumpTo("pi", 10) classDef io fill:#233042,stroke:#7aa2f7,color:#e6e8ef; classDef model fill:#2b1f3a,stroke:#bb9af7,color:#e6e8ef; classDef tool fill:#1f3a2b,stroke:#9ece6a,color:#e6e8ef; classDef sub fill:#3a2b1f,stroke:#e0af68,color:#e6e8ef; classDef gate fill:#3a2b1f,stroke:#e0af68,color:#e6e8ef; classDef ctx fill:#3a1f2b,stroke:#f7768e,color:#e6e8ef;
Step 0 / 10
  • 4 工具默认——read / write / edit / bash 是模型唯一能直接调的工具; find/grep/ls 存在但默认未挂载, 让 bash 走原生工具链。
  • Steering 双键——agent 跑工具时 Enter 立刻打断后续工具并塞新消息进推理; Alt+Enter 排队等本轮跑完。
  • "什么不在 core 里"——sub-agent、plan mode、MCP、权限弹窗、todo、后台 bash 全故意放到 extension/package 里; 想要就写一个或装一个。
  • 会话作为树——/tree 跳回任意旧消息从那分叉; 全部分支住在同一文件; /share 上传到 GitHub gist 直出可分享 URL。
  • SDK 嵌入——同一个 AgentSession 跑 4 模式: TUI / print(JSON) / RPC / SDK; openclaw 用 SDK 把 pi 嵌进自家 runner。
  • Four default tools — read / write / edit / bash are the only ones the model can call directly; find/grep/ls exist as files but aren't mounted, so the model uses native shell via bash.
  • Two-key steering — while the agent is running tools, Enter interrupts the remaining tools and lands a new message in reasoning; Alt+Enter queues a follow-up until the current run ends.
  • "What we didn't build" — sub-agents, plan mode, MCP, permission popups, todos, background bash are all deliberately pushed into extensions / packages; build one or install one.
  • Session as a tree/tree jumps back to any old message and forks from there; every branch lives in one file; /share uploads to a GitHub gist and returns a shareable URL.
  • SDK embedding — the same AgentSession runs in four modes: TUI / print(JSON) / RPC / SDK; openclaw uses the SDK to embed pi as its runner.
packages/coding-agent/src/core/agent-session.ts — AgentSession (3099 lines) packages/coding-agent/src/core/tools/{read,write,edit,bash}.ts — 4 default tools packages/coding-agent/src/core/extensions/ — TS extension runtime packages/coding-agent/src/core/compaction/ — replaceable compaction packages/coding-agent/src/modes/{interactive,print-mode.ts,rpc} — 4 run modes packages/coding-agent/src/core/sdk.ts — embed API (used by openclaw)

5. 一眼看懂5. At a glance

下面表里的术语若有陌生, 后面"深度拆解"会讲透——先扫一眼整体。Unfamiliar terms below will be explained in the deep dives — just skim for now.

AgentAgent 技术栈Stack 主循环Loop driver 沙箱 / 权限Sandbox / perms 招牌特性Signature feature
hermes-agent Python, 多 provider adapterPython, multi-provider adapters ReAct + 共享 IterationBudget(默认 90)ReAct w/ shared IterationBudget (default 90) Modal 云 VM; bash 权限策略Modal cloud VM; bash policy 子代理与父共享预算Sub-agents share parent budget
claw-code Rust runtime + Python 参考Rust runtime + Python reference run_turn() 每工具级 gatingrun_turn() per-tool gating Pre/Post hook → PermissionPolicyPre/Post hooks → PermissionPolicy Hook 触发早于权限检查Hooks fire before permission
codex OpenAI Responses API (非 Chat Completions)OpenAI Responses API responses.stream() + 回退级联responses.stream() w/ fallback cascade seatbelt (macOS) / landlock (Linux)seatbelt / landlock 加密推理跨 turn 保留Encrypted reasoning across turns
opencode TS 服务端 (Bun) + Go TUI, HTTP 分离TS server (Bun) + Go TUI, HTTP split 客户端 POST → 服务端 loop → SSE 回推Client POST → server loop → SSE back 每工具 allow | ask | denyPer-tool allow | ask | deny 任何前端都能驱动同一个 agent coreAny frontend drives the same core
openclaw TypeScript, 本地 Gateway + 多通道TypeScript, local Gateway + multi-channel Channel → Gateway → embedded runner → streamChannel → Gateway → embedded runner → stream Docker 沙箱 + per-channel ToolPolicyDocker sandbox + per-channel ToolPolicy IM / CLI / iOS / IDE 都路由到同一 agentIM / CLI / iOS / IDE all route to one agent
pi TypeScript library / CLI runtimeTypeScript library / CLI runtime AgentSession + typed stream + extension runnerAgentSession + typed stream + extension runner 默认轻量; 复杂治理交给 extensionsLight by default; governance via extensions 把 agent 做成可嵌入 SDK, openclaw 直接复用Embeddable SDK; openclaw uses it as a runner

6. 六家深度拆解6. Six deep dives

每家按同一模板: 目的 → 核心机制 (带源码引用) → 为什么 work → 为什么好 → 代价

Same template for each: purpose → key mechanisms with source references → why it works → why it's good → cost.

6.1 hermes-agent hermes-agent/run_agent.py

为什么你该看懂这家: 它展示了"一个 loop 兼容多家 LLM provider"和"让 agent 派小弟并防止失控"——自己造 agent 时第一个会撞到的两个工程问题。

Why you should care: it shows how to support multiple LLM providers in one loop, and how to delegate to sub-agents without losing control — the first two engineering problems you'll hit when building your own agent.

目的Purpose

做一个 provider-agnostic 的通用 agent: 今天 Claude, 明天 Gemini, loop 不用动。同时支持把大任务拆给子 agent 并行处理。

A provider-agnostic agent: swap Claude for Gemini without touching the loop. Plus parallel sub-agent delegation for large tasks.

核心机制Key mechanisms

为什么 workWhy it works

为什么好Why it's good

对 RL 训练场景极友好: agent 扔进 Modal 云沙箱, 同时跑 100 个 rollout, 每个独立 FS 但共享奖励函数。MCP server (mcp_serve.py) 把内部对话反向暴露, Claude Code / Cursor 能把 hermes 当工具。

Great for RL training: drop into Modal cloud sandbox, run 100 rollouts in parallel, each with its own FS but a shared reward function. MCP server (mcp_serve.py) exposes internal conversations outward, letting Claude Code / Cursor consume hermes as a tool. ClawBench is a natural RL evaluation target for this setup — its per-evidence scores plug straight in as reward signal.

代价Cost

复杂度高, 单文件 12,000+ 行。Multi-provider 意味着无法 1:1 映射各家最新特性 (比如 Claude 的 extended thinking 在 Gemini 上没有等价物)。

Complex — single file is 12,000+ lines. Multi-provider means you can't 1:1 map each vendor's latest features (e.g., Claude's extended thinking has no Gemini equivalent).

6.2 claw-code claw-code/rust/crates/runtime/src/

为什么你该看懂这家: 它展示了"如何让 agent 在生产环境也敢用"——每个危险动作都能被脚本拦下来审查、改写、记录。想把 agent 上线的人必看。

Why you should care: shows how to make an agent safe enough for production — every dangerous action can be intercepted, rewritten, or audited by scripts. Essential for anyone shipping an agent.

注: claw-code 是 Claude Code 的 Rust 开源复刻。Anthropic 官方文档把 Claude Code 定位为围绕 Claude 的 agentic harness(见 How Claude Code Works)。官方可确认的是 agentic loop、工具、权限、hooks、CLAUDE.md / memory、context compaction 这些机制; 本节的具体源码行、100k 压缩阈值、12 阶段 Bootstrap、health probe 是 claw-code 的实现选择, 不等于官方 Claude Code 内部实现。

Note: claw-code is an open-source Rust reimplementation of Claude Code. Anthropic's docs describe Claude Code as an agentic harness around Claude (see How Claude Code Works). The official surface confirms the agentic loop, tools, permissions, hooks, CLAUDE.md / memory, and context compaction; the source lines, 100k compaction threshold, 12-phase bootstrap, and health probe in this section are claw-code implementation choices, not official Claude Code internals.

目的Purpose

做一个可审计、可干预的 Claude Code 开源实现。每一次工具调用都可以被脚本拦截、改参、查权限、记日志、事后清理。

An auditable, interceptable Claude Code reimplementation. Every tool call can be intercepted, rewritten, permission-checked, logged, or cleaned up afterward.

核心机制Key mechanisms

为什么 workWhy it works

为什么好Why it's good

同一个 runtime 可以跑出完全不同风格的 agent: 开发者用=宽权限, hook 做 lint; 生产跑=收紧权限, hook 强制 dry-run; 教学用=所有 bash 都 ask。Rust 实现启动快内存低, 可内嵌到其他程序。

Same runtime, different personas: developers get wide permissions with lint hooks; prod gets strict permissions with forced dry-run hooks; teaching mode asks on every bash. Rust impl means fast startup, low memory, embeddable in other programs.

代价Cost

主 loop 里没有子代理 (task registry 只做异步后台)。多 agent 协作被推到 runtime 外部——这是 claw-code 的哲学选择: "让 agent context 专注做事, 不要用来开会"。

Main loop has no sub-agents (task registry is async background only). Multi-agent coordination is pushed outside the runtime — a deliberate philosophy: "keep agent context focused on work, not meetings."

6.3 codex codex/ + hermes adapter run_agent.py:5168+

为什么你该看懂这家: Responses API 是未来几年其他服务商大概率会跟进的方向。提前看懂 = 别家跟进时你能立刻上手。

Why you should care: Responses API is likely the direction other vendors will follow over the next few years. Learn it now, be ready when others catch up.

目的Purpose

展示 OpenAI 把 agent 能力直接内置到 API 会是什么样——不是让客户端组装工具调用, 而是 API 直接返回"我在想什么 / 要调什么工具 / 要说什么"的结构化流。

What it looks like when OpenAI bakes agent capability into the API itself — not client-side tool-call assembly, but the API streaming structured items: "what I'm thinking / which tool to call / what to say."

核心机制Key mechanisms

为什么 workWhy it works

为什么好Why it's good

第一方优化——Responses API 是 agent 一等公民。客户端只处理回退与去重, 服务端负责推理、缓存、流式, 整条链路比 "Chat Completions + 手搓 agent loop" 干净得多。对专门用 OpenAI 的团队, codex 是上限最高的方案。

First-party optimization — Responses API treats agents as first-class citizens. The client only handles fallback and dedup; the server owns inference, caching, streaming. The whole pipeline is cleaner than "Chat Completions + hand-rolled agent loop." For OpenAI-committed teams, codex is the most natural option in this comparison.

代价Cost

锁定 OpenAI——Responses API 目前只有 OpenAI。推理加密——你拿不到纯文本推理内容, 只能原样传回。

Locked to OpenAI — Responses API is OpenAI-only today. Reasoning is encrypted — you can't inspect it, only pass it back.

6.4 opencode github.com/sst/opencode

为什么你该看懂这家: 如果你要做 IDE 插件、团队共享 agent、或多端同步, 这是蓝图。

Why you should care: if you want to build an IDE plugin, a team-shared agent, or multi-client sync — this is the blueprint.

目的Purpose

解决一个工程问题: agent 不应该和终端 UI 绑死。今天 TUI, 明天 VS Code 插件, 后天 iPhone app——agent 逻辑应该只写一份。

One engineering problem: agent logic should not be bound to a TUI. TUI today, VS Code plugin tomorrow, iPhone app the day after — write the agent once.

核心机制Key mechanisms

为什么 workWhy it works

为什么好Why it's good

对团队协作友好——server 跑在共享机器上, 多人接客户端连进来看同一 session。对 IDE 集成友好——任何 IDE 插件都能对接, 不用各自重造 agent。

Team-friendly — run server on a shared machine, multiple clients connect to the same session. IDE-friendly — any IDE plugin can wire up, no need to reinvent the agent.

代价Cost

HTTP 带来的延迟 (毫秒级, 交互上可忽略)。Server 要长期维护, 不像单进程 CLI 那样"用完即退"。

HTTP adds latency (milliseconds, negligible interactively). Server needs long-term maintenance, unlike a fire-and-forget CLI.

6.5 openclaw openclaw/src/agents/pi-embedded-runner/

为什么你该看懂这家: 它展示了"一个 agent 同时吃得下 IM 消息、CLI 命令、iOS 推送、IDE 会话"——想做 all-in-one 个人 copilot 的人必看。

Why you should care: shows how one agent can handle IM messages, CLI commands, iOS pushes, and IDE sessions in parallel — essential reading if you want an "all-in-one" personal copilot.

目的Purpose

做一个本地优先的多通道 agent。官方把它定位成"单个长驻 Gateway + 所有通道共用一个 agent": 不是又一个聊天框, 而是挂在你设备上的控制面板——Discord / Slack / Telegram / WhatsApp / iMessage / Signal / Matrix / Teams / Zalo 等 10+ IM 通道 + CLI / iOS / IDE 都路由进同一个 Gateway, agent 在共享 session 里工作。

A local-first multi-channel agent. The docs position it as "one long-lived Gateway, many channels, one agent" — not another chat box but a control plane on your device. 10+ IM channels (Discord / Slack / Telegram / WhatsApp / iMessage / Signal / Matrix / Teams / Zalo and more) plus CLI / iOS / IDE all feed the same Gateway and share sessions.

核心机制Key mechanisms

官方术语: Tools vs Skills —— Tools 是 agent 可以调用的带类型函数(bash / read / write / browser / canvas 等核心工具),Skills 是注入 system prompt 的 Markdown 教材(SKILL.md,讲“什么时候、怎么用”工具)。这套分层是 openclaw 文档自己强调的核心抽象。

Docs terminology: Tools vs Skills. Tools are the typed functions the agent can call (bash / read / write / browser / canvas and other core tools); Skills are Markdown docs (SKILL.md) injected into the system prompt, teaching when and how to use them. This split is called out in the official docs as the core abstraction.

为什么 workWhy it works

为什么好Why it's good

对"个人 copilot / 值班机器人"场景最到位——开会时 agent 监听 Slack, 下班路上用 iMessage 追问结果, 到家接 CLI 继续改代码, 同一条 session 贯穿。加上 ACP, IDE 会话也一起上。偏 CLI 的几家需要你手动切换工具。浏览器沙箱刚好也能跑 ClawBench 任务——用 openclaw 做网页 agent 的研发+评测一条龙。

Ideal for "personal copilot / on-call bot": the agent watches Slack during a meeting, answers iMessage on the commute, resumes via CLI at home — all the same session. Add ACP and the IDE joins in too. The more CLI-centric systems ask you to context-switch tools yourself. The browser sandbox doubles as a ClawBench runner, so you can use openclaw for both web-agent dev and evaluation in one place.

代价Cost

运维成本高: Docker、WebSocket、多通道 webhook 要一次跑起来。核心文件 run.ts 2,100+ 行, 逻辑密集。不是开箱即用的小工具。

Higher ops cost: Docker + WebSocket + multi-channel webhooks must all be up. run.ts is 2,100+ lines of dense logic. Not a plug-and-play mini-tool.

6.6 pi github.com/badlogic/pi-mono · packages/coding-agent pi.dev

为什么你该看懂这家: 当前面的全功能框架比的是"我加了多少特性", pi 反过来比"我能砍掉多少特性还活得下去"。openclaw 的 embedded runner 就是基于 pi 的 SDK——这是 pi 在生产里很有力的存在证明。如果你想把 agent 做成一个能放进自己 app 里的库, 而不是一个吞掉用户工作流的 CLI, 这就是范例。

Why you should care: while the full-featured frameworks compete on "how many features I add," pi competes on "how many features I can strip out and still survive." openclaw's embedded runner is built on pi's SDK — a strong existence proof in production. If you want an agent shaped like a library you embed in your app, not a CLI that eats your workflow, this is the template.

目的Purpose

作者 Mario Zechner (badlogicgames; pi.dev 由 exe.dev 捐赠) 把 pi 称作 "minimal terminal coding harness"——只给模型 4 个原子工具 (read / write / edit / bash), 其他全部由用户用 TypeScript Extensions / Skills / Prompt Templates / Themes 自己长出来, 还能打成 npm/git 包分享。pi.dev 主页直接列了一串 "What we didn't build": 没有 MCP、没有 sub-agent、没有 plan mode、没有权限弹窗、没有内建 todo、没有后台 bash——每一项都给了"你可以这样替代"的提示。

Author Mario Zechner (badlogicgames; pi.dev donated by exe.dev) calls pi "a minimal terminal coding harness." The model gets four atomic tools (read / write / edit / bash); everything else is grown by users via TypeScript Extensions / Skills / Prompt Templates / Themes, which can be shipped as npm or git packages. The pi.dev homepage literally has a "What we didn't build" section: no MCP, no sub-agents, no plan mode, no permission popups, no built-in to-dos, no background bash — each entry tells you the recommended workaround instead.

核心机制Key mechanisms

为什么 workWhy it works

为什么好Why it's good

对"我要把 agent 嵌进自己产品里"的团队来说,pi 是这一组里最干净的选项之一——SDK 干净、协议稳定 (RPC 模式有 doc), 没有强加给你的 UI 概念。openclaw 把 pi 当 runtime 嵌进 Gateway, 自己只关心通道路由——这就是 pi 设计哲学的最佳广告。pi 还做了一个值得借鉴的事: 作者把自己的 pi-mono 工作 session 持续发到 Hugging Face, 用 pi-share-hf 工具一键分享 OSS session, 给 RL/agent 训练社区提供真实工作流数据。

For teams who want to embed an agent into their own product, pi is the cleanest option in this set — SDK-first, documented RPC mode, no imposed UX concepts. openclaw embedding pi as a runtime in its Gateway, while only owning channel routing, is a strong advertisement for pi's philosophy. One more thing worth copying: the author publishes his own pi-mono work sessions to Hugging Face via pi-share-hf, donating real OSS workflow data to the RL / agent-training community.

代价Cost

"故意没有 X" 的代价就是用户得自己长 X。生产场景需要权限弹窗、subagent、plan mode、MCP 接入的团队, 在 pi 上得先写一组 extension; 直接用 claw-code / opencode 是更省事的选择。Steering 双键虽然优雅, 学习曲线对新用户也不友好——团队人多时谁都得知道 Enter 和 Alt+Enter 的差别, 否则会误打断。

"Deliberately not built" comes with a tax — you grow it yourself. Teams that need permission popups, sub-agents, plan mode, or MCP in production must first write a stack of extensions; reaching for claw-code or opencode is the cheaper path. The two-key steering protocol is elegant but has a learning curve — everyone on a team has to know the Enter vs Alt+Enter distinction or the wrong key will break a long task.

7. 对比与选型7. Comparison & selection

什么场景选哪个Which to pick when

场景推荐原因
RL 训练 / 批量 rollouthermes-agentModal 沙箱 + 共享预算 + 子代理做并行
生产跑 agent, 要审计和治理claw-codeHook 系统 + 权限策略 + Rust 稳健性
只用 OpenAI, 要最强 reasoningcodexResponses API 原生支持 + 加密推理保留
多端 (TUI / Web / IDE) 共用opencodeHTTP 协议 + OpenAPI + 自动 SDK
个人多通道 copilot / 值班机器人openclaw本地 Gateway + IM/CLI/iOS/IDE 路由到同一 session
想把 agent 嵌进自己的 app / workflowpiSDK-first, core 极简, 复杂能力交给 extensions
真实浏览器任务评测 / 验证ClawBenchlive web 任务, 不是 offline DOM 快照; 动态 JS、cookie 弹窗、多步交互、可追溯 per-evidence 评分
ScenarioPickWhy
RL training / batch rolloutshermes-agentModal sandbox + shared budget + parallel sub-agents
Production with audit & governanceclaw-codeHooks + policy + Rust robustness
OpenAI-only, max reasoningcodexResponses API native support + encrypted reasoning
Multi-client (TUI / Web / IDE)opencodeHTTP + OpenAPI + auto-generated SDKs
Personal multi-channel copilot / on-call botopenclawLocal Gateway + IM/CLI/iOS/IDE route into one session
Embedding an agent in your own app / workflowpiSDK-first, tiny core, advanced behavior lives in extensions
Evaluating real browser tasksClawBenchLive web tasks, not offline DOM snapshots; dynamic JS, cookie popups, multi-step interactions, traceable per-evidence scoring

设计维度对比Design dimensions

维度Dimension hermesclawcodexopencodeopenclawpi
进程模型Process model 单进程Single process 单进程Single process 单进程Single process 客户端/服务端分离C/S split Gateway + Runner 拆分Gateway + Runner split 库 / CLI runtimeLibrary / CLI runtime
子代理Sub-agents 主循环内in-loop 无 (外置)None (external) None @mention session 路由session routing 无内建; extension 可加Not built-in; extension surface
权限粒度Permission grain 粗 (bash 分类)Coarse (bash class) 细 (每工具 + hook)Fine (per-tool + hook) 粗 (bash 分类)Coarse (bash class) 细 (每工具)Fine (per-tool) 细 (sandbox × channel)Fine (sandbox × channel) 用户扩展User extensions
Provider 多家 adapterMulti via adapter 多家 (Claude 优先)Multi (Claude-first) 仅 OpenAIOpenAI only 多家 (AI SDK)Multi (AI SDK) 多家 pluginMulti via plugins 多家 providerMulti-provider
语言Language PythonRustPythonTS + GoTypeScriptTypeScript
沙箱Sandbox Modal 云 VMcloud VM OS-level seatbelt / landlock 无 (容器可选)None (container optional) Docker (含浏览器)Docker (incl. browser) 按宿主 / extension 决定Host / extension-defined
入口通道Entry channels CLICLI / IDECLI / IDECLI / TUI / IDE IM / CLI / iOS / IDEIM / CLI / iOS / IDE CLI / SDK / RPC

8. Takeaway: 7 条值得借鉴的设计8. Takeaway: seven design patterns worth adopting

自己造 agent 时可以直接借鉴的设计。顺带一提:把它们造出来后, 用 ClawBench 在真实网页任务上打个分, 就知道到底哪几条 idea 真的 work。

Design patterns you can lift directly for your own agent. And once you've built it, run it against ClawBench on live web tasks to see which of these ideas actually pay off in practice.

1. 共享预算防失控 — hermes 的 IterationBudget1. Shared budget to prevent runaway — hermes's IterationBudget

不管 agent 怎么嵌套, 总工具调用次数不会爆炸。自己造时: 给所有工具调用加一个共同递减的计数器, 比"每个 agent 独立限制"稳得多。

No matter how deeply agents nest, total tool calls can't explode. DIY: add a single shared counter decremented by every call — far more robust than "each agent gets its own limit."

2. Hook 先于权限给用户终极表达力 — claw-code2. Hook-before-permission = ultimate expressiveness — claw-code

传统权限是 allow/deny 二元。Hook 是可编程的中间层。自己造时: 给每个关键决策点暴露一个"用户可注入的函数", 而不是做死的规则。

Traditional permission is binary allow/deny. Hooks are a programmable middle layer. DIY: expose a "user-injectable function" at every critical decision point, not hard-coded rules.

3. Reasoning 跨 turn 保留 — codex3. Reasoning persisted across turns — codex

多 turn 任务里, 上轮思考应可延续到这轮, 而不是每轮重新想。自己造时: 如果模型支持, 开启 reasoning persistence; 不支持, 在 system prompt 里人工把"上轮结论"塞回去。

In multi-turn tasks, the prior turn's reasoning should carry to the next — don't re-think from scratch. DIY: turn on reasoning persistence if the model supports it; if not, inject "last turn's conclusion" via the system prompt manually.

4. 把 agent 做成服务 — opencode4. Agent as a service — opencode

Agent 逻辑和 UI 彻底分开。自己造时: 哪怕只做 CLI, 也把 core 拆成独立 library + server mode, 将来扩展成本近似零。

Separate agent logic from UI. DIY: even for a CLI, split core into library + server mode — expanding to new clients later costs near-zero.

5. Ephemeral injection 保 cache — hermes5. Ephemeral injection to preserve cache — hermes

Prompt cache 最怕 prompt 前缀变。把动态内容 (memory, hook output) 作为"仅本次 API 调用生效"的补充, 别污染历史。自己造时: 历史只存用户消息 + 工具结果, 所有 agent 内部的元数据另算。

Prompt cache breaks when the prefix changes. Treat dynamic content (memory, hook output) as "only effective for this API call" — don't pollute the history. DIY: history stores only user messages + tool results; all agent-internal metadata lives elsewhere.

6. 预算耗尽时留一次 "grace call" — codex6. Give the model one "grace call" on budget exhaustion — codex

工具预算打满时不要直接 hard-error。hermes 里的 codex 适配器会再放模型一次 API 调用(run_agent.py:916 _budget_grace_call), 让它有机会给用户一个体面的收尾: 总结已完成的事、列出没跑完的、保存部分结果。自己造时: budget 监督器保留一次 graceful-exit 槽位, 用户体验立竿见影。

When the tool budget is exhausted, don't hard-error. The codex adapter in hermes grants one more model call (run_agent.py:916 _budget_grace_call) so the model can exit gracefully: summarise what got done, what's left, save partial results. DIY: reserve a single graceful-exit slot in your budget watchdog — the UX upgrade is immediate.

7. Session 分叉作为一等公民 — opencode (源码级)7. Session forking as a first-class primitive — opencode (code-level)

opencode 的 session.sql.tsparentID 字段追踪 session 血统, 支持从任意消息点分叉出一条平行路径注: 公开文档目前只介绍 share 功能, fork 还没进官方 docs——这个 pattern 来自源码。多数 agent 框架把"回溯/重试"做成销毁状态, opencode 把它做成树。自己造时: 消息持久化加一个 parent_id 字段, 就能解锁"多方案并跑"这类体验。

opencode's session.sql.ts uses a parentID field to track session lineage, letting you fork a parallel session from any message. Note: public docs only describe the share feature; forking is present in source but not yet promoted to official docs — this one is from the code. Most agent frameworks treat "undo/retry" as destruction; opencode treats it as a tree. DIY: add a parent_id to persisted messages and you've unlocked "try both approaches at once."

一句话画像One-line mental model

hermes-agent "ReAct + 预算 + 子代理, 一进程多 provider。" "ReAct + budgets + sub-agents, one process, many providers."
claw-code "每个工具调用都是 hook → permission → execute → hook。" "Every tool call is hook → permission → execute → hook."
codex "Responses API + 原生推理项 + OS 级沙箱。" "Responses API with first-class reasoning items and OS-level sandbox."
opencode "Agent core 藏在 HTTP 服务后, TUI 只是一个客户端。" "Agent core behind an HTTP API; the TUI is just one client."
openclaw "所有通道 (IM / CLI / iOS / IDE) 都是同一个 agent 的入口。" "Every channel (IM / CLI / iOS / IDE) is a door into the same agent."
pi "把 agent core 缩到 SDK, 其余能力都交给 extension。" "Shrink the agent core into an SDK; push everything else into extensions."

9. hermes-agent 与训练9. hermes-agent and training

本页这些项目大多在解决"怎么让 agent 好用"; hermes-agent 额外把"怎么让 agent 被训练"放进核心设计。这一点在 LLM 领域正在变得越来越重要——评估、RL、离线分析、跨模型对比, 都是"agent 作为训练 target"才能产出的结果。hermes 从进程模型到数据流, 每一层都为这个场景设计。

Most systems here optimize for running an agent well; hermes-agent additionally optimizes for training one. As evaluation, RL, offline analysis, and cross-model comparison become more important, training-aware harness design is increasingly useful — and hermes is architected for it from the process model up to the data flow.

为什么训练友好就是优雅Why training-friendly is elegance

为什么这比"好看"更优雅?训练是 agent 领域最苛刻的负载——要求并行、幂等、成本可控、失败可恢复、数据可追溯。一个能同时扛住这五件事的 harness, 本质上也是一个能在生产跑的 harness, 只是反过来不成立。hermes 把"能被训练"设计进了每一层, 这种系统级一致性才是真正的优雅。

Why does this beat "pretty" for elegance? Training is the harshest load an agent harness can face: parallel, idempotent, cost-bounded, failure-recoverable, data-traceable. A harness that withstands all five is usually well positioned for production too, though the reverse is not always true. Hermes bakes "trainable" into every layer; that system-wide coherence is what real elegance looks like.

荣誉提名 (各自最优雅的一处)Honorable mentions (each has one truly elegant choice)

这是我的口味, 不是唯一正确答案。生产审计选 claw-code; OpenAI 全家桶选 codex; 多端协作选 opencode; 个人多通道 copilot 选 openclaw; 嵌入式 SDK 选 pi;想真 stress-test 任何一家在真实网页任务上的表现——ClawBench 就是干这个的。我把票投给 hermes, 是因为"能被训练"这条路, 长期看会把整个 agent 生态拉进一个新范式——你今天不训练, 明年大概率也会训练。

This is my taste, not the only right answer. Production audit → claw-code. OpenAI stack → codex. Multi-client work → opencode. Personal multi-channel copilot → openclaw. Embedded SDK → pi. And if you want to actually stress-test any of these systems on real web tasks, that's what ClawBench is for. I vote hermes because training-aware design is likely to matter more over time as agent evaluation, RL, and offline analysis become standard practice.

下面是四篇姊妹笔记的完整并入版:第 10 节讲 Harness Engineering(提示词 → 可问责的智能体循环,六个工程平面), 第 11 节讲 Agentic RL(harness 也是策略的一部分,PPO/GRPO/SFT+RL 的 rollout 账本), 第 12 节是 LLM RL 核心算法(PPO / DPO / GRPO 速查), 末尾附录是一份正交的代码与实现参考(Classic Hot 100 + 手写 PyTorch MHA / Conv)。前 9 节是系统对比,这几节是它们背后的方法与训练视角。
Four companion notes are folded in below, in full. Section 10 is Harness Engineering (prompts → accountable agent loops, six engineering planes), Section 11 is Agentic RL (the harness is part of the policy; the PPO/GRPO/SFT+RL rollout ledger), Section 12 is Core LLM RL Algorithms (PPO / DPO / GRPO quick reference), and the closing appendix is an orthogonal coding & implementation reference (Classic Hot 100 + from-scratch PyTorch MHA / Conv). The first nine sections compare systems; these add the method and training lens behind them.

10. Harness Engineering:从提示词到可问责的智能体循环10. Harness Engineering: From Prompts to Accountable Agent Loops

Jul 2026 · Harness Engineering / Long-Horizon Agents / Evaluation

一个能长期工作的智能体,并不是"模型加一段 prompt"。它是一个运行系统:在有限上下文里选择信息,调用工具,保存工件,执行权限策略,管理预算和失败恢复,并把结果交给 agent 无法改写的 evaluator。Harness Engineering(智能体运行支架工程)研究的正是这一层。

本节的中心句:可信的 agent 结果属于一个配置,而不只属于模型:模型 + harness 版本 + 环境 + 工具/权限策略 + evaluator + 预算。因此,一次 pass rate 上升并不能单独说明"模型变强了",也不能说明更复杂的 orchestration 一定有效。

10.1 这篇文章不做什么

这不是产品横评,也不是"更多子 agent 必然更好"的宣言,更不是 RSI 已经达成的主张。上文的六个系统深度拆解解释六个具体系统怎样运行;本节换一个问题:我们应该怎样设计、约束并评估 harness 本身?

这里的"工程"有两个边界:

  1. Agent harness(智能体运行支架) 是围绕模型的运行时:上下文、工具、状态、控制流、权限、日志和恢复。
  2. Evaluation harness(评测支架) 是配置任务、执行试验、收集证据并计算结果的基础设施。它不能被待测 agent 当作可编辑工具。

如果这两个概念混在一起,就会出现一个危险的模糊说法:"agent 得分提高了"。读者无法判断是模型、提示、工具、环境、预算还是评分器变了。

10.2 先画出可问责的边界

Base model / 基础模型Agent harness / 运行支架Environment / 外部环境
生成候选文本与工具调用context、tools、state、budget、recoveryterminal、browser、computer、service state

不可由 agent 自行改写的证据轨:版本清单、权限决定、工具事件、工件哈希、使用量、终态和 evaluator 输出。人类只在明确的升级点批准高风险动作或解释不确定结论。

这张图不是某个已发布系统的结构图,而是一条设计立场:模型可以提出行动,harness 可以安排行动,但权限与接受标准不能藏在 prompt 的一句"请小心"里。MCP 为 tools、resources、prompts 等交互对象提供协议边界;它并不自动使工具输出可信,也不替代宿主的权限控制。MCP Specification

10.3 六个工程平面

3.1 契约:工具不是函数名的集合

工具是非确定性模型与有状态服务之间的契约。一个可评估的工具面应让输入 schema、权限、错误、幂等性与返回范围都可见;工具之间的功能重叠越大,agent 越容易用偶然路径得到不可复现的结果。Anthropic 的工具设计指南建议在真实多步任务上测试工具,并用保留任务检查是否只对开发轨迹过拟合。Writing tools for agents

可检验的问题不是"工具多不多",而是:在相同权限和预算下,任务形状化的窄工具是否降低无效调用、token 浪费或安全违规,同时不牺牲保留任务成功率。

3.2 状态:把记忆变成工件,而不是越塞越长的上下文

Context engineering(上下文工程)是每次调用所见 token 状态的选择与维护;它不等于增加 context window,更不等于把所有历史日志塞回 prompt。长程系统需要把任务清单、进度、测试、候选工件、依赖和版本历史保存为可检索的外部工件。这样一个新 session 才能依据同一证据继续工作,而不是猜测前一轮发生过什么。

要特别区分三个词:

名称它保存什么它不能保证什么
Context compaction / 上下文压缩当前对话的摘要或选择后的上下文原始环境可以恢复
Session handoff / 会话交接让新上下文接手的状态清单、工件位置、未决问题之前的动作可安全重放
Checkpoint / 检查点可重新启动的环境状态、配置和工件版本外部服务或实时网页保持不变

关于长程 harness 的公开工程实践强调了 feature list、进度文件、测试、初始化命令和版本历史:它们让新的执行者能从工件重建状态,而不是依赖不可见的聊天记忆。Effective harnesses for long-running agents

长程 harness 生命周期
  1. Initialize — 冻结任务、版本、权限与预算。
  2. Execute — 调用工具,把工件和观察写入谱系。
  3. Checkpoint — 保存可恢复状态,不把聊天记录当检查点。
  4. Evaluate — 在独立 evaluator 上测 outcome、成本与违规。
  5. Recover or stop — 按固定策略重试、交接、回退或终止。

3.3 权限:authority 必须由运行时执行

Prompt 可以表达偏好,不能单独构成权限边界。一个严肃的 authority policy 至少应列出:可读写路径、允许域名、凭据作用域、危险动作类别、审批点、资源上限、网络出口和审计保留期。沙箱隔离与网络控制是运行时机制,不是礼貌性指令;这也是为什么官方 sandboxing 设计同时讨论文件系统与网络边界。Claude Code sandboxing

这带来一条简单但重要的规则:agent 可以提议扩大权限或修改 policy,但它不能自行批准该变更,更不能同时改写 evaluator 并宣布自己通过。这个规则同样适用于浏览器、Computer Use、terminal 和训练 harness。

3.4 控制:并行是一项待验证的调度策略

并行子 agent 能拓宽检索和独立执行的覆盖面,也会放大 token、协调与合并成本。它只适合可明确拆分、输出契约稳定的子问题;同一代码面或强耦合决策常常需要共享的主控上下文。公开的多 agent 研究系统也报告了更宽探索带来的显著 token 开销,并强调协调方式会影响结果。Multi-agent research system

因此父 agent 应拥有:全局预算、任务切分、取消权、合并权和最终发布权。子 agent 应拥有:窄目标、隔离工作区、明确交付物和可审计结果记录。所谓"多 agent 更好"只有在相同模型、总预算与任务分布下,比较 sequential baseline 后才是可成立的经验结论。

3.5 证据:trace、replay 与 rerun 不是同义词

每次运行至少应记录 harness、模型、工具、环境与 policy 版本;初始状态 digest;输入输出与工具事件;批准/拒绝;工件哈希;重试与恢复;evaluator 输出;token、墙钟和资源用量。密钥、个人数据和第三方内容需要在保留之前脱敏。

这份 trace 支持三件不同的事:

  1. 诊断(diagnosis):为什么失败、为什么花费异常、哪一步触及权限。
  2. 观察式回放(observational replay):查看已发生的事件而不重放外部副作用。
  3. 执行式重跑(execution rerun):从冻结状态重新执行,并公开随机性和外部依赖控制。

只拥有录像或聊天记录,通常只支持第一项或第二项。对一个仍在变化的在线服务而言,不能把它称为"可复现 rerun"。

3.6 完整性:evaluator 是系统的一部分,却不属于优化面

确定性测试、模型 grader 和人工审查回答的不是同一个问题。确定性 verifier 适合检查明确终态;模型 grader 可以处理开放文本,但要校准并报告其误差;人工审查用来发现未建模的损害。关于 agent eval 的工程指南强调了任务、grader、样本和指标必须先被定义,再将结果解释为能力。Demystifying evals for AI agents

所以 harness 的改动应携带一个 change manifest:它预期改善什么、不会伤害什么、在哪个保留集上验收、何时回退。Harness-Bench 与 VeRO 等预印本探索了 configuration-level 或版本化 agent 评测;它们提供有价值的方法线索,但不证明复杂 harness 在所有任务上都更强,也不构成无界自我改进的证据。Harness-Bench · VeRO

10.4 报告一个 harness,需要哪些表

维度至少报告不应省略的限定
Outcome成功率、任务版本、grader、置信区间单次成功不等于稳定行为
Efficiencytoken、工具调用、墙钟、GPU / sandbox 小时、每个成功任务成本必须使用相同预算比较
Reliability超时率、故障注入后的恢复率、resume latency、方差说明重试与停止策略
Governancepolicy violation、误拦截、审批准确率写清 threat model 与拦截面
Observabilitytrace 完整率、脱敏失败、replay / rerun agreement指明是观察式还是执行式回放
Parallelismspeedup、worker-hours、重复工作、合并冲突与同预算 sequential baseline 比较
Generalization保留任务变化、回归数、跨模型/任务迁移最终测试集不进入优化循环

这张表让"harness 很好"变成一组可能失败的陈述。比如,更高的 pass rate 可能来自隐藏的重试、更多并发或不等的 token 预算;若不报告这些,读者无法把结果用于工程决策。

10.5 当 harness 尝试改进自己时

Harness optimization 可以是很有价值的:系统分析失败轨迹,提出一个小的 context、tool 或控制流修改,在冻结评测上验证,保留或回退版本。但它仍不是自动的 RSI。要讨论更强的主张,修改后的 harness 必须让之后的改进过程在未参与选择的任务上持续更好;权限与 evaluator 也不能由候选版本自己授予或重写。

因此可接受的最小循环是:

  1. 人或外部 policy 冻结可改动面、预算、evaluator 与回退规则。
  2. agent 从失败谱系提出窄修改,并说明预期收益和回归风险。
  3. 独立 runner 在 capability 与 regression split 上执行,记录同预算证据。
  4. 外部 acceptance gate 合并、拒绝或回退;候选 harness 没有对 gate 的写权限。

这和 RSI 文章的边界一致:改进一条 workflow 很有意义,但不应凭一次局部 gain 宣称已经改进了"改进者"。

10.6 一个可公开讨论的 workshop 议程

下面是研究议程,不是已经宣布的活动、CFP 或赞助消息。

  1. Interfaces and environments。 工具协议、browser / computer actions、沙箱、可移植性与确定性验证。
  2. Memory and long-horizon execution。 context 生命周期、工件存储、检查点、恢复、持久任务与 replay。
  3. Governed autonomy。 capability scoping、身份、批准设计、prompt injection containment 与审计日志。
  4. Optimization and evaluation。 workflow search、多 agent 调度、harness evolution、trace analysis、evaluator integrity、成本与负结果。

一篇系统论文若声称 harness 改进,最少应交付版本化 harness/environment manifest、任务与 evaluator 版本、预算账本、trace/redaction 方案、同预算 baseline、关键消融、威胁模型和失败分析。这样的要求不会保证正确性,但能让论文的关键依赖暴露在读者面前。

10.7 五个可被推翻的研究问题

假设最小实验推翻信号
结构化的外部 handoff 比只做 context compaction 更可靠固定模型、任务、预算和环境状态,比较两种恢复路径保留任务无完成/恢复收益,或成本更高
窄而任务化的工具优于广泛 API mirror在相同 authority 下比较成功率、无效调用与 token广工具同样好且无可靠性/成本代价
并行只在独立分支有收益固定总 token 与 worker-time,比较 orchestration无速度/质量收益,或合并回归抵消收益
外部 acceptance gate 降低 harness 自改的回归冻结 evaluator/policy,比较 versioned edit 与无约束 edit接受改动的回归率不降或 manifest 没有预测力
过程信号可以改善恢复而非被刷将过程指标与独立终态在保留集上对照过程分数上升却不改善终态或安全

这些问题和 AutoResearchAgentic RL(第 11 节)Agent Research EnvironmentsAgent Planning 相连:它们都要求把可改动的系统、获得的反馈和最终证据放在不同的边界中;其中 planning 专文进一步拆开 plan、重规划、拒绝与终态验收。

10.8 主要参考

Jul 2026 · Harness Engineering / Long-Horizon Agents / Evaluation

A long-running agent is not "a model plus a prompt." It is a runtime system: it selects information within a finite context, calls tools, preserves artifacts, enforces authority, manages budget and recovery, and hands results to an evaluator the agent cannot rewrite. Harness engineering is the discipline of designing that layer.

Central claim: a trustworthy agent result belongs to a configuration, not only to a model: model + harness version + environment + tool/permission policy + evaluator + budget. A pass-rate increase alone therefore cannot show that "the model got better," nor that a more elaborate orchestration helped.

10.1 What This Section Does Not Do

This is not a product survey, a declaration that more subagents are always better, or a claim that RSI has arrived. The six deep dives above explain how six concrete systems run. This section asks a different question: how should a harness itself be designed, constrained, and evaluated?

Two meanings must remain separate:

  1. An agent harness is the runtime around a model: context, tools, state, control flow, authority, logs, and recovery.
  2. An evaluation harness provisions a task, executes trials, collects evidence, and scores results. The evaluated agent must not treat it as an editable tool.

When those meanings collapse, "the agent's score improved" becomes dangerously ambiguous. A reader cannot tell whether model, prompt, tools, environment, budget, or scorer changed.

10.2 Draw an Accountable Boundary First

Base modelAgent harnessEnvironment
Generates candidate text and tool callsContext, tools, state, budget, recoveryTerminal, browser, computer, service state

An evidence rail outside the agent's write boundary: version manifests, permission decisions, tool events, artifact hashes, usage, terminal state, and evaluator outputs. Humans approve high-risk escalations or interpret uncertain conclusions at explicit handoff points.

This is not a diagram of one released system. It is a design position: the model may propose actions and the harness may schedule them, but authority and acceptance criteria cannot live only in a prompt that says "be careful." MCP provides a protocol boundary for objects such as tools, resources, and prompts. It does not automatically make a tool response trustworthy or replace host-side policy enforcement. MCP Specification

10.3 Six Engineering Planes

3.1 Contract: Tools Are Not Just a List of Function Names

Tools are contracts between a stochastic agent and stateful services. An evaluable tool surface makes its input schema, authority, errors, idempotency, and response scope inspectable. The more overlapping tool paths an agent has, the easier it is to obtain a fragile result through an accidental route. Anthropic's tool-design guidance recommends testing tools on realistic multi-step tasks and using held-out tasks to detect overfitting to development trajectories. Writing tools for agents

The testable question is not "how many tools exist?" It is whether task-shaped narrow tools reduce invalid calls, token waste, or policy violations at equal authority and budget without harming held-out success.

3.2 State: Turn Memory into Artifacts, Not an Ever-Longer Prompt

Context engineering is the selection and maintenance of token state visible at each invocation. It is not merely a larger context window, and it is not dumping every past log into a prompt. Long-running systems need task manifests, progress, tests, candidate artifacts, dependencies, and version history as retrievable external artifacts. A fresh session can then continue from the same evidence instead of guessing what a prior session did.

Three terms deserve a strict distinction:

TermWhat it preservesWhat it does not guarantee
Context compactionA summary or selected context for the current conversationThe original environment can be restored
Session handoffA state manifest, artifact locations, and open questions for a fresh contextEarlier actions can be safely replayed
CheckpointRestartable environment state, configuration, and artifact versionA live external service stays unchanged

Public long-running-harness practice emphasizes feature lists, progress files, tests, bootstrap commands, and version history so a new worker reconstructs state from artifacts rather than hidden chat memory. Effective harnesses for long-running agents

Long-running harness lifecycle
  1. Initialize — Freeze task, versions, authority, and budget.
  2. Execute — Use tools and record artifacts and observations in lineage.
  3. Checkpoint — Persist recoverable state; chat history is not a checkpoint.
  4. Evaluate — Measure outcome, cost, and violations in an independent evaluator.
  5. Recover or stop — Retry, hand off, revert, or stop under a fixed policy.

3.3 Authority: Runtime Enforcement, Not Prompt Wording

A prompt can express a preference; it cannot enforce an authority boundary by itself. A serious policy names readable and writable mounts, allowed hosts, credential scope, risky action classes, approval points, resource ceilings, network egress, and audit retention. Sandboxing and network controls are runtime mechanisms, not courtesy instructions. This is why public sandboxing designs discuss both filesystem and network boundaries. Claude Code sandboxing

One simple rule follows: an agent may propose a policy expansion, but it cannot approve that expansion itself, nor rewrite the evaluator and announce that it passed. The rule applies to browser, Computer Use, terminal, and training harnesses alike.

3.4 Control: Parallelism Is a Scheduling Hypothesis

Parallel subagents can widen research and independent execution coverage, but also multiply token, coordination, and merge cost. They fit only materially separable work with stable output contracts; shared code surfaces and tightly coupled decisions often need a common controller. A public multi-agent research system likewise reports substantial token cost for broader exploration and stresses that coordination shapes the outcome. Multi-agent research system

The parent should own global budget, decomposition, cancellation, merge, and publication. Workers should receive narrow objectives, isolated workspaces where possible, explicit deliverables, and durable result records. "Multi-agent is better" becomes an empirical claim only after comparison with a same-model, same-budget, same-task sequential baseline.

3.5 Evidence: Trace, Replay, and Rerun Are Not Synonyms

Every run should at least record harness, model, tool, environment, and policy versions; an initial-state digest; input/output and tool events; approvals and denials; artifact hashes; retries and recovery; evaluator outputs; and token, wall-clock, and resource usage. Secrets, personal data, and third-party content need redaction before retention.

That trace supports three different operations:

  1. Diagnosis: why a run failed, spent unusually, or touched authority.
  2. Observational replay: inspect an event record without repeating external side effects.
  3. Execution rerun: execute from frozen state while declaring nondeterminism and external-dependency controls.

A video or chat transcript generally supports only the first two. It should not be called a reproducible rerun of a changing online service.

3.6 Integrity: The Evaluator Is Part of the System but Not the Optimization Surface

Deterministic tests, model graders, and human review answer different questions. Deterministic verifiers check explicit terminal states; model graders can handle nuanced text but need calibration and error reporting; humans catch unmodeled harm. Agent-evaluation engineering guidance stresses defining tasks, graders, samples, and metrics before interpreting a result as capability. Demystifying evals for AI agents

Harness changes should therefore carry a change manifest: what should improve, what must not regress, the held-out split that accepts it, and the rollback condition. Harness-Bench and VeRO are preprints exploring configuration-level or versioned agent evaluation. They provide useful methodological clues, not proof that elaborate harnesses are stronger on every task or evidence of unbounded self-improvement. Harness-Bench · VeRO

10.4 What a Harness Report Must Include

DimensionMinimum reportQualification that cannot be omitted
OutcomePass rate, task version, grader, confidence intervalOne success is not durable behavior
EfficiencyTokens, tool calls, wall clock, GPU / sandbox-hours, cost per successful taskCompare under an identical budget
ReliabilityTimeout rate, recovery after injected failure, resume latency, varianceState retry and stopping policy
GovernancePolicy violations, false blocks, approval accuracyName the threat model and interception surface
ObservabilityTrace completeness, redaction failures, replay / rerun agreementState whether replay is observational or executable
ParallelismSpeedup, worker-hours, duplicate work, merge conflictsCompare with a same-budget sequential baseline
GeneralizationHeld-out change, regressions, cross-model or cross-task transferKeep final test data out of the optimization loop

This table turns "the harness is good" into claims that can fail. A higher pass rate may come from hidden retries, greater concurrency, or an unequal token budget; omitting those makes the result unhelpful for engineering decisions.

10.5 When a Harness Tries to Improve Itself

Harness optimization can be useful: a system analyzes failed traces, proposes a small context, tool, or control-flow edit, tests it on frozen evaluation, then retains or reverts the version. It is still not automatically RSI. A stronger claim would require the modified harness to make later improvement processes reliably better on tasks that did not select it, while authority and evaluation remain outside the candidate's control.

The minimum acceptable loop is:

  1. A human or external policy freezes editable surface, budget, evaluator, and rollback rule.
  2. The agent proposes a narrow edit from failure lineage and declares expected gain and regression risk.
  3. An independent runner executes capability and regression splits under the same budget and records evidence.
  4. An external acceptance gate merges, rejects, or reverts; the candidate harness has no write access to that gate.

This matches the boundary in the RSI note: improving one workflow is valuable, but one local gain does not establish that the system improved "the improver."

10.6 A Workshop Agenda That Can Be Discussed Publicly

The following is a research agenda, not an announced event, CFP, or sponsorship notice.

  1. Interfaces and environments. Tool protocols, browser and computer actions, sandboxing, portability, and deterministic verification.
  2. Memory and long-horizon execution. Context lifecycles, artifact stores, checkpoints, recovery, durable jobs, and replay.
  3. Governed autonomy. Capability scoping, identity, approval design, prompt-injection containment, and audit logs.
  4. Optimization and evaluation. Workflow search, multi-agent scheduling, harness evolution, trace analysis, evaluator integrity, cost, and negative results.

A systems paper that claims a harness improvement should at minimum provide a versioned harness/environment manifest, task and evaluator versions, a budget ledger, trace/redaction plan, same-budget baseline, critical ablation, threat model, and failure analysis. These requirements do not guarantee correctness, but they expose a paper's load-bearing dependencies to readers.

10.7 Five Falsifiable Research Questions

HypothesisMinimum experimentFalsifier
Structured external handoffs are more reliable than context compaction aloneHold model, task, budget, and environment state fixed; compare recovery pathsNo held-out completion or recovery gain, or greater cost
Narrow task-shaped tools beat a broad API mirrorCompare success, invalid calls, and tokens under equal authorityBroad tools match results without reliability or cost penalty
Parallelism helps only independent branchesFix total tokens and worker-time; compare orchestrationsNo speed or quality gain, or merge regressions erase it
An external acceptance gate reduces regressions in self-editsFreeze evaluator/policy; compare versioned and unconstrained editsAccepted edits regress no less, or the manifest has no predictive value
Process signals improve recovery rather than get gamedCompare process metrics against independent held-out terminal statesProcess score rises without better outcome or safety

These questions join AutoResearch, Agentic RL (Section 11), Agent Research Environments, and Agent Planning: all require separate boundaries for what can change, what feedback is observed, and what finally counts as evidence; the planning guide further separates plan, replanning, refusal, and terminal acceptance.

10.8 Primary References

11. Agentic RL:Harness 也是策略的一部分11. Agentic RL: The Harness Is Part of the Policy

Jul 2026 · Agentic RL / Polar / RL Environments

第 10 节把 harness 当作被设计与被问责的对象;这一节换到训练视角。传统 RL 常把环境想成一个干净的 API:观测进来,动作出去,奖励回来。真实 coding agent 并不这样运行。它有工具协议、上下文压缩、子 agent、终端、重试和停止规则。对智能体强化学习(agentic RL)而言,这个智能体运行支架(harness)不是可忽略的包装,而是策略实际执行的条件。

本节的中心句:若训练环境改变了测试时的 harness 行为,训练的就不是部署时会运行的策略。Polar 的价值在于把 rollout 接入点放到模型 API 边界,尽量让原生 harness 原样运行。

11.1 为什么把 agent 硬塞进普通 environment API 会丢信息

一个成熟 harness 会定义:系统提示词如何拼接、工具怎么返回、何时压缩上下文、子 agent 怎样并行、文件如何持久化、什么时候终止。把它重写成训练框架内部的 environment,可能改变这些行为,或丢掉 token、log-prob、分支与工具上下文。

这就是 Polar 论文解决的问题:它不是一个新的 PPO / GRPO 公式,也不是 benchmark。它是一个异步 rollout 系统,让原生 harness 把模型请求指向代理网关;网关转发请求并记录模型交互,再从真实执行中重建训练轨迹。

11.2 Polar 的系统边界

原生 harness
  -> 模型 API proxy
  -> 推理后端
  -> CompletionSession
  -> trajectory builder
  -> evaluator / reward
  -> 独立 trainer

这四层需要分别记录:运行时隔离、harness 启动配置、模型流量与轨迹重建、验证与奖励。否则出了分数变化,也无法判断是模型、支架、环境还是评分器造成的。

11.3 论文提供了什么证据

在论文指定的设置中,作者从同一个 Qwen3.5-4B 起点出发,用标准 GRPO 和 293 个 SWE-Gym 训练任务,在 SWE-Bench Verified 上报告了四种 coding harness 的绝对 pass@1 增益:

Harness论文报告的起点 → 训练后绝对变化
Codex3.8% → 26.4%+22.6
Claude Code29.8% → 34.6%+4.8
Qwen Code34.6% → 35.2%+0.6
Pi34.2% → 40.4%+6.2

作者还报告,在一个轨迹重建消融中,prefix_merging 将 trainer 侧更新条目从 1,185 降到 218,并缩短该设置的 wall-clock 时间。这是关于其系统设计和指定实验设置的证据,不是"所有 harness 都会获得同等收益"的定律。

11.4 奖励归因是最危险的地方

长程 agent 的最终 patch 通过了验证器,不代表每次模型调用都同样有功。Polar 论文明确发现,把 session 级 outcome reward 机械地广播给每条 request-level trace 会产生明显的 reward hacking 风险。

因此一个可信的 agentic RL 环境至少应公开:

  1. reward 是对最终工件、某次提交、某个步骤,还是整段 session 赋值;
  2. 奖励如何映射到 token 级 loss mask;
  3. 工具失败、重试、子 agent 分支和上下文压缩是否还留在轨迹中;
  4. 验证器运行在哪个干净环境,agent 能否污染它;
  5. 训练、验证和部署使用的 harness 是否版本一致。

Reward 信号不能替代结果字段

Reward Hacking Benchmark (RHB) 把跳过验证、任务邻近 metadata 泄漏和评测相关篡改等 shortcut 放进多步工具任务,并区分独立与链式 regime;Hack-Verifiable Environments 则把可检测漏洞机会直接嵌入环境。这支持把 exploit 作为 rollout outcome,而不是事后猜测。二者都只覆盖其指定任务与攻击面;RHB 的模型、后训练和 hardening 比较不构成 PPO、GRPO、SFT+RL 或某种 guard 的通用排序。

必须分开记录的字段在 agentic RL 中的最小定义不能代替
proxy_reward实际进入优化、group comparison 或 credit assignment 的过程/终态信号用户目标完成或安全
verified_outcome非 agent 可写的独立 postcondition 或 sidecar 确认的最终状态reward、点击、tool success 或 judge 偏好
exploit_outcome已检测的 shortcut、evaluator/evidence tampering、泄漏或弱 verifier 利用未知攻击上的安全保证
hardening_delta在同一任务、策略、动作面、预算和攻击协议下,hardening 前后的 exploit、合法通过与 false-block 变化"奖励已经安全"或漏洞完全消失

Hardening Agent Benchmarks with Adversarial Hacker-Fixer Loops 进一步给出一个必要的检查:修补 verifier 后,合法解仍要通过,并应在未见 exploit 上重新攻击。其 terminal-benchmark 结果同样是指定流程下的证据,而不是所有 agentic-RL environment 的安全证明。

证据边界:Polar 的"any harness"是指可重定向、且处在其 proxy 支持的模型 API 边界上的 harness。论文的主要实证是软件工程、一个 4B 基座、四个 coding harness 和指定训练集;它没有直接证明浏览器或 GUI 环境上的训练增益。

11.5 来源各自回答不同的问题

Agentic RL 很容易把"能跑 RL""有 trajectory""有 verifier"和"训练结果可迁移"压成一句话。下面这些来源很重要,但各自的证据范围不同:

来源最直接支持什么它不直接证明什么
Polar在兼容模型 API 的原生 coding harness 中代理调用、记录 token 级交互并重建训练轨迹浏览器、GUI 或任意外部环境中的 RL 增益
Agent Lightning将 agent execution 与 trainer 解耦,并提出把复杂执行分解为 RL transitions 的归因接口该归因在任意 harness、任意 reward 下都正确
HarborTRL integration可把 sandbox task、verifier 和 reward 组织成训练环境接口task contract 自动防止 evaluator 污染或 reward hacking
DeepSeekMath / GRPOGRPO 作为 PPO 的组相对变体,源自可验证数学推理设置GRPO 比 PPO 更适合所有长程 agent 任务
SAO在该论文的异步 agentic coding/reasoning 设置中,提出 single-rollout 更新以处理 group-wise sampling 与异步 rollout 的张力一个 fresh preprint 决定所有 harness、所有预算下 PPO、GRPO 或 SAO 的优劣
AgentTrek从教程到执行再到验证的 GUI trajectory synthesis 路径合成轨迹天然没有训练/评测泄漏,或等于 online RL
RHBHack-Verifiable EnvironmentsHacker-Fixer多步 exploit 可单列为结果;hardening 需同时检查合法通过和 false-block降低 exploit rate 已证明任意 reward、verifier 或优化器安全

这张表把两种常见误读挡在门外。第一,GUI trajectory data 与 online RL rollout 不是同一对象:前者可以支持 SFT、离线筛选或训练数据研究,后者还需要定义行为策略、reward、归因和更新。第二,拥有一个 verifier 不等于 verifier 与 agent 隔离;Harbor 文档明确区分 shared 与 separate verifier,任务作者仍要声明可见工件、网络与权限边界。

11.6 和环境文章怎么连起来

环境文章里的 benchmark、harness、sandbox 三层在 agentic RL 中仍然存在,但还需要加一层:轨迹与训练接口。评测系统决定什么算完成;harness 决定策略实际怎样行动;sandbox 决定可见状态和权限;训练接口决定哪些行为被归因、被优化、被复放。

这也解释了为什么"换一个 harness 重新跑同一个模型"不是无关紧要的工程细节。提示词、工具可用性、停止策略、上下文政策和 evaluator 都可能改变 rollout 分布。训练结论需要说明这些条件,而不是把最终分数写成模型脱离环境的固定属性。

11.7 PPO、GRPO 与 SFT+RL:先对齐 rollout 账本,再比较更新规则

算法标签不是实验条件。即使两个 run 都写着 "GRPO",只要 base checkpoint、harness、reset、工具权限、rollout 停止规则、reward、有效 token 或评测选择不同,它们也不是一个可解释的比较。对长程 agent,这些差异往往比 optimizer 名字更大。

最新的 Single-Rollout Asynchronous Optimization (SAO) 让这个问题变得具体:论文指出,GRPO 的 group-wise sampling 并不自然地适配异步 agent rollout,并在其编码和推理设置中报告 single-rollout 异步更新的结果。这是一条值得检验的系统假说,不是对 PPO、GRPO 或任何 harness 的通用排名。SAO 仍是 2026-07-08 提交的 preprint;比较时应同时报告 group 如何构成、行为策略落后多少、哪些 rollout 实际进入更新,以及资源消耗。

共同账本字段PPO 额外披露GRPO 额外披露SFT+RL 额外披露
同一任务、初始状态、harness、系统提示、工具与权限、沙箱/验证器版本、动作面和 splitpolicy/reference/value/reward model 版本;KL、advantage、clipping 与 value 配置group size K、同 prompt/同 state 的分组规则、valid group 数、全同 reward 组比例、无效或丢弃 completion 的规则SFT 数据及其来源/split;SFT token/step 预算;如何选择 SFT checkpoint;该 checkpoint 后另行使用的 RL 预算
behavior_checkpoint、rollout 终止/重试/取消规则、policy lag、有效 rollout token、tool call、reset、墙钟与外部成本每次 update 使用的 rollout 与 update schedule同一 group 的 rollout 是否来自一致策略快照;异步到达如何影响 group 与 updateSFT 和 RL 是否共享了数据、验证集、prompt 或选择信号
proxy_rewardverified_outcomeexploit_outcomehardening_delta、integrity、recovery、cost,以及 reward-to-loss 映射和 verifier 哈希value/reward 信号与最终独立 evaluator 的差异group reward、归一化/优势规则和 reward degeneracySFT-only、RL-only 与 SFT+RL 使用的 final suite 是否完全独立

把这些最小信息固定成三个 manifest,才可以让结果被复查,而不是只留下一个训练曲线:

RolloutManifest(
  task_and_reset, base_and_harness, action_surface,
  behavior_checkpoint, termination_and_retry_policy,
  rollout_tokens, tool_calls, environment_resets, wall_clock, cost
)

OptimizationManifest(
  method, reward_and_verifier_version,
  ppo_value_reference_config_or_grpo_group_rule,
  sft_data_manifest_and_checkpoint, update_schedule, policy_lag
)

DeploymentAcceptance(
  run_hash, final_harness, independent_suite,
  proxy_reward, verified_outcome, exploit_outcome, hardening_delta,
  integrity, recovery, cost, transfer, decision
)

这不是凭空加的仪式。当前 TRL 的 GRPO 文档 也把 stateful environment_factoryreset、environment-owned reward 与 tool loop 暴露为训练接口,并要求 tool-call chat template 保持 prefix-preserving;它是一个很具体的系统配置。相应地,TRL 的 PPO 接口 明确列出 policy、reference、reward 和 value model。框架提供接口,不会自动保证环境真实、奖励安全或 held-out transfer。

一个可反驳的比较流程可以是:

  1. 冻结 RolloutManifest 和训练/验证/final split;先写清楚允许改变的仅是哪个算法层。
  2. 以同一 base、harness、action surface、reward/verifier 和总 token/rollout/reset/墙钟预算构造 baseline、SFT-only、PPO、GRPO 或 SFT+RL 条件;资源不够时,宁可少比较,也不要偷换预算。
  3. 除了平均 reward,报告 task/workflow 分布、失败分支、有效 rollout 比例、GRPO group health、policy lag、verified_outcomeexploit_outcome、hardening 的合法通过/false-block,以及每个 verified success 的成本。
  4. 用没有参与选择 checkpoint、prompt、harness 或超参的独立 final suite 运行 DeploymentAcceptance;训练 reward 上升或一个 training environment 的 pass rate 都不能替代它。

研究表述:"PPO 可能优于 GRPO"或"PPO 更适合 ClawBench V2"现在只是待检验假说。只有在相同 rollout ledger 和独立 acceptance 下得到的、按任务分布分解的结果,才有资格成为条件化结论。

11.8 给自己的实验清单

  1. 固定并记录 harness、模型端点、系统提示、工具版本、沙箱镜像和 verifier 哈希。
  2. 保存可重放的 token 对齐轨迹,而不只保存自然语言 transcript。
  3. proxy_rewardverified_outcomeexploit_outcomehardening_delta 分开;训练 reward 不能代替独立终态或 exploit 报告。
  4. 在至少一个没有被选择或调参过的任务分布上评估,避免把 harness 适配写成通用能力。
  5. 对 PPO、GRPO 与 SFT+RL 先发布同一份 rollout、优化与 acceptance manifest;明确 group、policy lag、checkpoint 选择和每类预算。
  6. 标记每条证据属于 native rollout、offline trajectory、task/verifier contract 还是算法目标;不要把相邻来源的结论直接迁移。

关于 reward、evidence、verifier、judge 与最终 acceptance 如何分开,并怎样把 tampering、leakage、trace integrity 和 process-reward gap 做成对抗回归,见 Agent Evaluation Integrity:把 Reward 变成证据协议。算法层面的 PPO / DPO / GRPO 细节见下文第 12 节

11.9 References

Jul 2026 · Agentic RL / Polar / RL Environments

Section 10 treated the harness as something to design and hold accountable; this section turns to training. Conventional RL often imagines a tidy API: observations arrive, actions leave, and rewards return. Real coding agents do not run that way. They have tool protocols, context compression, subagents, terminals, retries, and stopping rules. For agentic RL, this agent harness is not disposable packaging. It is part of the condition under which the policy actually executes.

Central claim: if training changes the behavior of the test-time harness, it is not training the policy that will run at deployment. Polar is valuable because it moves the rollout integration point to the model API boundary while leaving the native harness as intact as possible.

11.1 Why Forcing an Agent into an Ordinary Environment API Loses Information

A mature harness defines how system prompts are assembled, how tools return observations, when context is compressed, how subagents run concurrently, how files persist, and when execution stops. Rewriting it as an environment owned by a training framework can alter those behaviors or lose tokens, log-probabilities, branches, and tool context.

This is the problem addressed by Polar. It is neither a new PPO / GRPO objective nor a benchmark. It is an asynchronous rollout system: the native harness points model requests to a gateway, the gateway forwards and records the interactions, and training trajectories are reconstructed from the real execution.

11.2 Polar's System Boundary

native harness
  -> model API proxy
  -> inference backend
  -> CompletionSession
  -> trajectory builder
  -> evaluator / reward
  -> independent trainer

Four layers should be recorded independently: runtime isolation, harness launch configuration, model traffic and trajectory reconstruction, and verification plus reward. Otherwise a score change cannot be attributed to the model, harness, environment, or grader.

11.3 What the Paper Actually Shows

In the paper's specified setup, the authors start from the same Qwen3.5-4B base, use standard GRPO and 293 SWE-Gym training tasks, and report the following absolute pass@1 changes on SWE-Bench Verified across four coding harnesses:

HarnessReported start → post-trainingAbsolute change
Codex3.8% → 26.4%+22.6
Claude Code29.8% → 34.6%+4.8
Qwen Code34.6% → 35.2%+0.6
Pi34.2% → 40.4%+6.2

The authors also report that prefix_merging reduced trainer-side updates from 1,185 to 218 and shortened wall-clock time in one reconstruction ablation. This is evidence about the paper's design and stated experimental setting, not a law that every harness will receive equal gains.

11.4 Reward Attribution Is the Dangerous Part

The final patch of a long-running agent may pass a verifier, but that does not make every model call equally responsible. Polar explicitly finds that mechanically broadcasting a session outcome reward to every request-level trace creates a substantial reward-hacking risk.

A credible agentic RL environment should therefore disclose:

  1. whether reward is assigned to the final artifact, a submission, a step, or the full session;
  2. how reward maps onto token-level loss masks;
  3. whether tool failures, retries, subagent branches, and context compression remain in the trajectory;
  4. where the verifier runs and whether the agent can contaminate it; and
  5. whether training, validation, and deployment use the same harness version.

A Reward Signal Cannot Substitute for Outcome Fields

Reward Hacking Benchmark (RHB) places shortcuts such as skipped verification, task-adjacent metadata leakage, and evaluation-relevant tampering in multi-step tool tasks, and separates independent from chained regimes. Hack-Verifiable Environments instead embeds detectable vulnerability opportunities directly in an environment. Together they support treating exploitation as a rollout outcome rather than an after-the-fact guess. Both are scoped to their stated tasks and attack surfaces; RHB's model, post-training, and hardening comparisons are not a general ranking of PPO, GRPO, SFT+RL, or a guard.

Field to report separatelyMinimum meaning in agentic RLIt cannot substitute for
proxy_rewardThe process or terminal signal actually consumed by optimization, group comparison, or credit assignmentUser-goal completion or safety
verified_outcomeFinal state confirmed by a non-agent-writable independent postcondition or sidecarReward, click, tool success, or judge preference
exploit_outcomeA detected shortcut, evaluator/evidence tampering, leakage, or weak-verifier exploitSafety against unknown attacks
hardening_deltaThe change in exploit, legitimate pass, and false-block rates before/after hardening at the same task, policy, action surface, budget, and attack protocolA claim that reward is safe or loopholes are gone

Hardening Agent Benchmarks with Adversarial Hacker-Fixer Loops adds a necessary check: after patching a verifier, legitimate solutions still need to pass, and unseen exploits should be attacked again. Its terminal-benchmark results are likewise evidence under a stated process, not a safety proof for every agentic-RL environment.

Evidence boundary: "Any harness" means a harness that can be redirected at a model API boundary supported by the proxy. The main evidence covers software engineering, one 4B base model, four coding harnesses, and a specified training set. It does not directly establish training gains for browser or GUI environments.

11.5 Sources Answer Different Questions

Agentic RL can easily compress "it runs RL," "it has trajectories," "it has a verifier," and "training transfers" into one sentence. The following sources matter, but their evidence scopes differ:

SourceWhat it most directly supportsWhat it does not directly establish
PolarProxying calls, recording token-level interactions, and reconstructing training trajectories in native coding harnesses with compatible model APIsRL gains in browsers, GUIs, or arbitrary external environments
Agent LightningDecoupling agent execution from a trainer and an attribution interface that decomposes complex execution into RL transitionsCorrect attribution for every harness or reward
Harbor and TRL integrationOrganizing sandbox tasks, verifiers, and rewards into a training-environment interfaceA task contract automatically prevents evaluator contamination or reward hacking
DeepSeekMath / GRPOGRPO as a group-relative PPO variant originating in verifiable mathematical reasoningGRPO is better than PPO for every long-horizon agent task
SAOA single-rollout update proposal for the tension between group-wise sampling and asynchronous rollouts in that paper's agentic coding/reasoning setupA fresh preprint settles the ranking of PPO, GRPO, or SAO across all harnesses and budgets
AgentTrekA GUI trajectory-synthesis path from tutorials through execution and verificationSynthetic trajectories are inherently free from train/evaluation leakage or equivalent to online RL
RHB, Hack-Verifiable Environments, and Hacker-FixerMulti-step exploits can be separate outcomes; hardening must also check legitimate passes and false blocksA lower exploit rate proves an arbitrary reward, verifier, or optimizer safe

The map blocks two common misreadings. First, GUI trajectory data and online RL rollouts are not the same object: the former can support SFT, offline filtering, or training-data research; the latter also needs a behavior policy, reward, attribution, and update rule. Second, possessing a verifier does not mean that it is isolated from the agent. Harbor distinguishes shared from separate verifiers; the task author still has to declare visible artifacts, network, and authority boundaries.

11.6 How This Connects to Environment Design

The benchmark, harness, and sandbox layers in the environment post still apply to agentic RL. A fourth layer must be added: the trajectory and training interface. Evaluation decides what completion means. The harness determines how the policy acts. The sandbox determines visible state and permissions. The training interface determines which behavior receives credit, optimization, and replay.

That is why rerunning the same model with another harness is not an incidental engineering detail. Prompts, tool availability, stopping policy, context policy, and evaluation can all change the rollout distribution. A training result should name those conditions rather than present a final score as an environment-free model property.

11.7 PPO, GRPO, and SFT+RL: Align the Rollout Ledger Before Comparing Update Rules

An algorithm label is not an experimental condition. Even if two runs both say "GRPO," they are not an interpretable comparison when the base checkpoint, harness, reset, tool authority, rollout stopping policy, reward, effective tokens, or evaluation selection differ. For long-running agents, those differences can be larger than the optimizer label.

The recent Single-Rollout Asynchronous Optimization (SAO) makes the issue concrete. It argues that GRPO's group-wise sampling does not naturally fit asynchronous agent rollouts, and reports results for a single-rollout asynchronous update in its coding and reasoning settings. That is a systems hypothesis worth testing, not a universal ranking of PPO, GRPO, or any harness. SAO is a preprint submitted on 2026-07-08; a comparison should disclose how groups are formed, behavior-policy lag, which rollouts actually enter an update, and resource use.

Shared ledger fieldAdditional PPO disclosureAdditional GRPO disclosureAdditional SFT+RL disclosure
Same tasks, initial states, harness, system prompt, tools and authority, sandbox/verifier version, action surface, and splitPolicy/reference/value/reward model versions; KL, advantage, clipping, and value configurationGroup size K; grouping rule for prompt/state; valid-group count; all-equal-reward rate; invalid or discarded-completion ruleSFT data and its provenance/split; SFT token/step budget; how the SFT checkpoint was selected; RL budget used after that checkpoint
behavior_checkpoint, rollout termination/retry/cancel policy, policy lag, effective rollout tokens, tool calls, resets, wall time, and external costRollouts consumed per update and update scheduleWhether a group uses a consistent policy snapshot; how asynchronous arrivals affect groups and updatesWhether SFT and RL share data, validation sets, prompts, or selection signals
proxy_reward, verified_outcome, exploit_outcome, hardening_delta, integrity, recovery, and cost, plus reward-to-loss mapping and verifier hashGap between value/reward signals and final independent evaluatorGroup reward, normalization/advantage rule, and reward degeneracyWhether SFT-only, RL-only, and SFT+RL use the exact same independent final suite

Freeze the minimum information in three manifests so a result can be reviewed rather than reduced to a training curve:

RolloutManifest(
  task_and_reset, base_and_harness, action_surface,
  behavior_checkpoint, termination_and_retry_policy,
  rollout_tokens, tool_calls, environment_resets, wall_clock, cost
)

OptimizationManifest(
  method, reward_and_verifier_version,
  ppo_value_reference_config_or_grpo_group_rule,
  sft_data_manifest_and_checkpoint, update_schedule, policy_lag
)

DeploymentAcceptance(
  run_hash, final_harness, independent_suite,
  proxy_reward, verified_outcome, exploit_outcome, hardening_delta,
  integrity, recovery, cost, transfer, decision
)

This is not ceremonial extra metadata. The current TRL GRPO documentation exposes a stateful environment_factory, reset, environment-owned reward, and a tool loop, and requires a prefix-preserving chat template for that loop. Those are concrete system conditions. Likewise, the TRL PPO interface names a policy, reference, reward, and value model. A framework exposes an interface; it does not automatically make an environment realistic, a reward safe, or transfer held out.

A falsifiable comparison can proceed as follows:

  1. Freeze the RolloutManifest and train/validation/final split, and state which algorithm layer alone may change.
  2. With the same base, harness, action surface, reward/verifier, and total token/rollout/reset/wall-clock budget, construct baseline, SFT-only, PPO, GRPO, or SFT+RL conditions. When resources are tight, compare fewer conditions rather than silently changing budgets.
  3. Beyond mean reward, report the task/workflow distribution, failure branches, effective-rollout rate, GRPO group health, policy lag, verified_outcome, exploit_outcome, hardening's legitimate-pass/false-block behavior, and cost per verified success.
  4. Run DeploymentAcceptance on a final suite that did not select checkpoints, prompts, harnesses, or hyperparameters. Rising training reward or a pass rate in the training environment cannot substitute for it.

Research wording: "PPO may outperform GRPO" or "PPO is better for ClawBench V2" is presently a hypothesis to test. Only results with a shared rollout ledger, independent acceptance, and task-distribution breakdown earn the status of a conditional conclusion.

11.8 A Checklist for Personal Experiments

  1. Fix and record harness, model endpoint, system prompt, tool versions, sandbox image, and verifier hash.
  2. Save replayable token-aligned trajectories, not only natural-language transcripts.
  3. Separate proxy_reward, verified_outcome, exploit_outcome, and hardening_delta; training reward cannot substitute for an independent terminal or exploit report.
  4. Evaluate on at least one task distribution that was not selected or tuned against, so harness adaptation is not reported as general ability.
  5. For PPO, GRPO, and SFT+RL, publish a shared rollout, optimization, and acceptance manifest first; name groups, policy lag, checkpoint selection, and every budget.
  6. Label each piece of evidence as a native rollout, offline trajectory, task/verifier contract, or algorithm objective; do not transfer a conclusion across those categories by default.

For separating reward, evidence, verifier, judge, and final acceptance, and for turning tampering, leakage, trace integrity, and process-reward gaps into adversarial regressions, see Agent Evaluation Integrity: Turn Reward into an Evidence Protocol. For the objective-level PPO / DPO / GRPO details, see Section 12 below.

11.9 References

12. LLM RL 核心算法:PPO、DPO、GRPO12. Core LLM RL Algorithms: PPO, DPO, GRPO

Apr 2026 · RLHF / Preference Optimization / Reasoning RL

第 11 节讲了 rollout 如何从原生 harness 中采集;这一节回答它们如何变成参数更新。本节只聚焦三件事:PPO 是经典 online RLHF(模型边采样边用奖励更新),DPO 是 offline preference optimization(只用 chosen / rejected 偏好对),GRPO 是 critic-free reasoning RL(用同题多答案的组内相对奖励替代 value model)。其他变体可以之后单开一篇,这里先梳理主干。

一句话:PPO 和 GRPO 都属于 policy-gradient-style online RL:模型先采样答案,再根据 reward 调整生成概率。DPO 则把 KL-regularized(KL 正则化的)RLHF 目标改写成离线偏好分类 loss。三者都会用 KL、reference model 或 clipping 来限制模型偏移。

12.0 先背这张表

算法最短定位数据从哪来是否 online rollout是否需要 critic适合场景
PPO经典 RLHFreward model / rule reward / judge通常需要通用对齐、reward model 已经有了
DPO偏好对齐chosen / rejected pair不需要指令风格、偏好数据、稳定省事
GRPO推理 RL同题多采样后的 reward group不需要数学、代码、可验证 reward 的 reasoning

记忆法:

PPO  = 在线采样 + reward model/function + critic baseline + KL/clip
DPO  = 离线偏好对 + reference model + 分类式 loss
GRPO = 在线同题多采样 + 组内相对 reward + no critic

12.1 共同底层:Policy Gradient

LLM 可以看成策略:

pi_theta(y | x)

给定 prompt x,模型生成答案 y。RL 的目标是最大化期望奖励:

maximize_theta E_{x ~ D, y ~ pi_theta(. | x)}[R(x, y)]

最朴素的更新方向:

grad J(theta) ~= A(x, y) * grad log pi_theta(y | x)

直觉:

12.2 PPO:经典 RLHF

PPO 的标准 RLHF pipeline:

SFT model
-> train reward model from preference data
-> online rollout with current policy
-> PPO update policy under KL constraint

LLM 版 PPO 常见奖励:

reward = reward_model_score - beta * KL(pi_theta || pi_ref)

PPO 训练时通常有四个角色:

角色作用
policy model正在被训练的 LLM
reference model通常是 SFT checkpoint,用来计算 KL,限制别跑飞
reward model / reward function给完整回答或过程打分
value model / critic估计 baseline / value,帮助算 advantage、降方差

PPO 的核心目标可以这样理解:

ratio = pi_theta(token) / pi_old(token)
objective = min(ratio * advantage, clip(ratio, 1-eps, 1+eps) * advantage)

这里的 pi_old 是采样 rollout 时的旧策略;PPO 更新时用 importance ratio 比较"现在的 policy"和"采样时的 policy"。为什么要 clip:如果新 policy 相对旧 policy 变化过大,就截断更新,避免一次梯度更新导致训练不稳定。

PPO 的具体流程

假设 prompt 是:

写一个 Python 函数,判断括号字符串是否合法。

当前 policy 采样出一个回答:

用栈扫描字符串;遇到左括号入栈,遇到右括号检查栈顶是否匹配。

训练时会发生这几步:

步骤发生了什么这个量用来干嘛
1. rollout当前 policy 真正生成一条答案得到一条 token trajectory
2. rewardreward model / unit test / judge 给答案打分,比如 0.86告诉模型这条答案好不好
3. KL penalty如果新模型离 reference 太远,扣掉一点分,比如 0.06防止为了 reward 把语言能力训坏
4. criticvalue model 预测每个 prefix 未来大概能拿多少分提供 baseline,降低方差
5. advantageactual return - critic prediction判断这段生成比预期好还是差
6. PPO update用 clipped ratio 更新 token 概率好的 token trajectory 概率上升,坏的下降

一个数字例子:

reward_model_score = 0.86
kl_penalty = 0.06
actual_return = 0.80

critic prediction at this prefix = 0.55
advantage = 0.80 - 0.55 = +0.25

这说明这段生成比 critic 预期更好,PPO 会提高这条回答里相关 token 的概率。但如果某个 token 的概率变化太猛:

ratio = pi_theta(token) / pi_old(token) = 1.35
clip range = [0.8, 1.2]

那这个样本的 surrogate objective 会被截到 1.2 * advantage,不再鼓励 ratio 继续变大。直觉就是:

可以朝高 reward 方向走,但一步不能迈太大。

如果另一个回答 reward 很低:

actual_return = 0.20
critic prediction = 0.55
advantage = -0.35

对应 token 的概率就会被压低。PPO 的重点不是只看 reward,而是看:

这个生成结果是否比 critic 原本预期更好?
新 policy 相对 old policy 改得是否过猛?
新 policy 是否离 reference model 太远?

PPO 的优点:

PPO 的缺点:

12.3 DPO:偏好优化,不是传统 online RL

DPO 直接使用偏好对:

prompt x
chosen answer y_w > rejected answer y_l

它不需要先训练一个显式 reward model,也不需要在线采样 rollout。核心是让模型相对 reference 更偏向 chosen、更远离 rejected:

log pi_theta(y_w | x) - log pi_ref(y_w | x)
should be larger than
log pi_theta(y_l | x) - log pi_ref(y_l | x)

DPO loss 的直觉版:

loss = -log sigmoid(
  beta * [
    log_ratio(chosen) - log_ratio(rejected)
  ]
)

其中 log_ratio(y)=log pi_theta(y|x)-log pi_ref(y|x)beta 控制偏好强度和相对 reference 的保守程度。这里的 reference model 仍然很关键:它不是 critic,而是用来限制模型偏离原始行为的基准。

从技术上说,DPO 更准确地说是从 KL 正则化 RLHF 推导出的离线偏好优化方法。它利用最优 KL 正则化策略与隐式 reward 之间的闭式关系,避免训练单独的 reward model 和运行在线 RL。

DPO 的具体流程

DPO 不让模型在线试错,而是直接吃偏好数据。比如一条训练样本是:

Prompt:
解释 reward model 和 critic 的区别。

Chosen:
reward model 是裁判,给完整答案打分;critic 是预测器,估计当前状态未来能拿多少 reward。

Rejected:
reward model 和 critic 都是给模型打分的东西,差别不大。

DPO 看的是:当前模型相对 reference model,有没有更偏向 chosen。

假设当前 policy 的 log probability 是:

log pi_theta(chosen | x)   = -120
log pi_theta(rejected | x) = -115

这说明当前模型反而更容易生成 rejected,因为 -115-120 概率更高。

再看 reference model:

log pi_ref(chosen | x)   = -118
log pi_ref(rejected | x) = -116

于是 DPO 比较的是两个 reference-relative ratio:

chosen_log_ratio   = -120 - (-118) = -2
rejected_log_ratio = -115 - (-116) = +1
gap = chosen_log_ratio - rejected_log_ratio = -3

gap 是负的,说明当前模型相对 reference 更偏向 rejected。DPO loss 会推动:

increase log pi_theta(chosen | x)
decrease log pi_theta(rejected | x)

所以 DPO 的训练信号不是:

这个答案 reward = 0.8

而是:

同一个 prompt 下,chosen 相对 reference 的 log-prob improvement 应该高于 rejected。

注意:实际实现里通常用整段回答的 token log-probability 求和或平均;回答长度、截断方式和 tokenizer 都会影响数值,所以 DPO 样本最好保证 chosen / rejected 在同一 prompt 下可比。

它的关键优点是工程简单:不需要 rollout、不需要显式 / 单独的 reward model 在线打分,也不需要 critic。缺点也来自这里:模型只能从已有偏好对中学习,不会主动探索全新的更优答案。

DPO 的优点:

DPO 的缺点:

12.4 GRPO:推理模型常用的 critic-free RL

GRPO 的核心是同一个 prompt 采样多个答案,然后组内比较。

Prompt x
├── answer A -> reward 0.9
├── answer B -> reward 0.7
├── answer C -> reward 0.2
└── answer D -> reward 0.0

用组内均值做 baseline、组内标准差做 normalization:

mean = 0.45
std  = 0.36
A_adv = (0.9 - 0.45) / 0.36 = +1.25
B_adv = (0.7 - 0.45) / 0.36 = +0.69
C_adv = (0.2 - 0.45) / 0.36 = -0.69
D_adv = (0.0 - 0.45) / 0.36 = -1.25

所以它可以省掉 value model / critic:

PPO:  reward function + critic baseline
GRPO: reward function + group-relative baseline

GRPO 的具体流程

假设 prompt 是一道数学题:

如果 3x + 5 = 20,求 x。

GRPO 不只采样一个答案,而是同一个 prompt 采样一组答案:

样本模型回答reward
A3x=15,所以 x=51.0
B3x=25,所以 x=8.330.0
Cx=(20-5)/3=51.0
Dx=150.0

组内平均 reward 是 baseline,组内标准差负责归一化:

mean_reward = (1.0 + 0.0 + 1.0 + 0.0) / 4 = 0.5
std_reward  = 0.5

于是每个回答的 advantage 可以近似理解成:

A_adv = (1.0 - 0.5) / 0.5 = +1.0
B_adv = (0.0 - 0.5) / 0.5 = -1.0
C_adv = (1.0 - 0.5) / 0.5 = +1.0
D_adv = (0.0 - 0.5) / 0.5 = -1.0

训练更新就很直观:

A / C 的 token 概率上升
B / D 的 token 概率下降

这里没有单独的 value model,因为 baseline 来自同一个 prompt 的 group mean,std 只负责把尺度拉齐。也就是说:

PPO 问 critic: 这个 prefix 未来大概值多少分?
GRPO 问同组样本: 这个答案比同题其他答案强还是弱?

GRPO 还会保留 PPO 类似的 ratio / KL 控制,避免模型为了拿 reward 过度偏离 reference。它最适合数学、代码、选择题、格式可验证任务,因为 reward 可以自动算:

数学: 最终答案是否正确
代码: unit tests 是否通过
工具调用: 任务是否完成、证据是否满足

GRPO 特别适合 RLVR(reinforcement learning with verifiable rewards):

GRPO 的优点:

GRPO 的缺点:

12.5 Reward Model 和 Critic 不要混淆

项目Reward model / reward functionCritic / value model
一句话裁判预测器
输出这个回答得几分当前状态未来大概能得几分
是否直接作为 policy reward signal是,policy 要提高 reward不直接作为 reward;critic 自身要拟合 value / return,并用来算 advantage
PPO 需要吗需要 reward 信号通常需要
GRPO 需要吗需要 reward 信号不需要
DPO 需要吗不显式需要不需要

最容易被问的点:

reward model: answer -> score
critic: partial trajectory/state -> expected return
advantage: per-token/step return estimate (often GAE / KL-shaped) - critic prediction

12.6 怎么选算法

条件优先选
只有 chosen/rejected 偏好对,想快速稳定对齐DPO
有可靠 reward model,要做经典 RLHFPPO
数学/代码/推理,reward 可自动验证GRPO
训练资源有限,不想维护 criticDPO 或 GRPO
想让模型在在线 rollout 里探索更优 reasoning,且 reward 能可靠自动验证GRPO
想最大化通用 reward,同时 reward model 已成熟PPO

最实用的面试回答:

PPO 是传统 RLHF 主力,完整但重;DPO 是偏好对齐主力,离线、简单、稳定;GRPO 是 reasoning RL 主力,用组内相对 reward 替代 critic,适合数学/代码这类可验证任务。

12.7 高频问答

DPO 算不算 RL?

广义上它来自 RLHF 目标的推导,狭义上不是传统 online RL。它没有 rollout、没有环境交互、没有 critic,更准确叫 offline preference optimization。

GRPO 为什么不需要 critic?

因为它对同一个 prompt 采样多个答案,用组内 reward 的均值构造 baseline,再用组内标准差归一化 advantage。critic 的作用是提供 baseline,GRPO 用 group baseline 替代了它。

PPO 和 GRPO 最大区别?

PPO 通常训练 value model / critic 来估计 advantage;GRPO 用同题多样本的相对 reward 估计 advantage。PPO 更通用,GRPO 在可验证 reasoning 任务上更省、更直接。

三者共同点是什么?

都在控制"新模型相对旧模型/reference model 的概率变化"。PPO 用 ratio clipping 和 KL,DPO 用 reference-relative preference loss,GRPO 通常也会保留 KL 或 ratio clipping。

12.8 最终速记

PPO:
  在线 RLHF;reward model/function + critic + KL/clip
  通用 RLHF 最强基线之一,但成本高、对超参敏感

DPO:
  离线偏好优化;chosen/rejected 偏好对
  无 rollout、无显式 reward model、无 critic
  简单稳定,适合 instruction / preference alignment

GRPO:
  在线 critic-free RL;group-relative advantage
  无 value model,适合可验证 reasoning reward
  常见于现代可验证推理 RL

12.9 References

Apr 2026 · RLHF / Preference Optimization / Reasoning RL

Section 11 covered how rollouts are collected from a native harness; this section turns to the objectives that consume them. It focuses on three central algorithms: PPO as the classic online RLHF workhorse, DPO as a common offline preference optimization method, and GRPO as a critic-free RL method often used in reasoning-model training. Many variants deserve their own post; this section sticks to the core ideas.

One sentence: PPO and GRPO are both policy-gradient-style online RL methods: the model samples answers first, then updates generation probabilities using rewards. DPO rewrites the KL-regularized RLHF objective into an offline preference-classification loss. All three use KL, a reference model, or clipping to limit policy drift.

12.0 Memorize This Table First

AlgorithmShort descriptionWhere the data comes fromOnline rollout?Needs a critic?Best fit
PPOClassic RLHFreward model / rule reward / judgeYesUsually yesGeneral alignment when a reward model already exists
DPOPreference alignmentchosen / rejected pairNoNoInstruction style, preference data, stable and simple tuning
GRPOReasoning RLreward group from multiple samples for the same promptYesNoMath, code, and reasoning with verifiable rewards

Mnemonic:

PPO  = online sampling + reward model/function + critic baseline + KL/clip
DPO  = offline preference pairs + reference model + classification-style loss
GRPO = online multi-sampling per prompt + group-relative reward + no critic

12.1 Shared Foundation: Policy Gradient

An LLM can be viewed as a policy:

pi_theta(y | x)

Given a prompt x, the model generates an answer y. The RL objective is to maximize expected reward:

maximize_theta E_{x ~ D, y ~ pi_theta(. | x)}[R(x, y)]

The simplest update direction is:

grad J(theta) ~= A(x, y) * grad log pi_theta(y | x)

Intuition:

12.2 PPO: Classic RLHF

The standard PPO-based RLHF pipeline is:

SFT model
-> train reward model from preference data
-> online rollout with current policy
-> PPO update policy under KL constraint

A common LLM PPO reward is:

reward = reward_model_score - beta * KL(pi_theta || pi_ref)

PPO training usually has four roles:

RoleWhat it does
policy modelThe LLM being trained
reference modelUsually the SFT checkpoint; used to compute KL and keep the policy from drifting too far
reward model / reward functionScores the full answer or the process
value model / criticEstimates the baseline / value, helps compute advantage, and reduces variance

The core PPO objective can be understood as:

ratio = pi_theta(token) / pi_old(token)
objective = min(ratio * advantage, clip(ratio, 1-eps, 1+eps) * advantage)

Here pi_old is the old policy that produced the rollout samples. During the PPO update, the importance ratio compares the current policy with the policy used at sampling time. The reason for clip is simple: if the new policy changes too much relative to the old policy, PPO truncates the update to avoid destabilizing training in a single gradient step.

The PPO Workflow

Suppose the prompt is:

Write a Python function that checks whether a bracket string is valid.

The current policy samples this answer:

Scan the string with a stack; push left brackets, and when a right bracket appears, check whether it matches the stack top.

Training then goes through these steps:

StepWhat happensWhat the quantity is used for
1. rolloutThe current policy actually generates an answerProduces a token trajectory
2. rewardThe reward model / unit test / judge scores the answer, for example 0.86Tells the model whether this answer is good
3. KL penaltyIf the new model is too far from the reference, subtract a penalty, for example 0.06Prevents reward optimization from damaging language ability
4. criticThe value model predicts the expected future score for each prefixProvides a baseline and reduces variance
5. advantageactual return - critic predictionDecides whether this generation was better or worse than expected
6. PPO updateUpdate token probabilities with the clipped ratioGood token trajectories go up; bad ones go down

A numeric example:

reward_model_score = 0.86
kl_penalty = 0.06
actual_return = 0.80

critic prediction at this prefix = 0.55
advantage = 0.80 - 0.55 = +0.25

This means the generation was better than the critic expected, so PPO increases the probability of the relevant tokens in that answer. But if a token probability changes too aggressively:

ratio = pi_theta(token) / pi_old(token) = 1.35
clip range = [0.8, 1.2]

the surrogate objective for that sample is clipped to 1.2 * advantage, so it no longer encourages the ratio to grow further. The intuition is:

Move toward higher reward, but do not take too large a step at once.

If another answer has low reward:

actual_return = 0.20
critic prediction = 0.55
advantage = -0.35

the corresponding token probabilities are pushed down. PPO is not just asking "what is the reward?" It is asking:

Was this generation better than the critic expected?
Did the new policy change too much relative to the old policy?
Is the new policy too far from the reference model?

PPO strengths:

PPO weaknesses:

12.3 DPO: Preference Optimization, Not Traditional Online RL

DPO directly consumes preference pairs:

prompt x
chosen answer y_w > rejected answer y_l

It does not require training an explicit reward model first, and it does not need online rollout. The core idea is to make the model prefer the chosen answer over the rejected answer relative to a reference model:

log pi_theta(y_w | x) - log pi_ref(y_w | x)
should be larger than
log pi_theta(y_l | x) - log pi_ref(y_l | x)

An intuitive DPO loss:

loss = -log sigmoid(
  beta * [
    log_ratio(chosen) - log_ratio(rejected)
  ]
)

where log_ratio(y)=log pi_theta(y|x)-log pi_ref(y|x), and beta controls preference strength and conservatism relative to the reference. The reference model is still crucial: it is not a critic; it is the behavioral anchor that limits how far the model moves away from the original policy.

Technically, DPO is best described as an offline preference optimization method derived from KL-regularized RLHF. It uses the closed-form relationship between an optimal KL-regularized policy and its implicit reward to avoid training a separate reward model and running online RL.

How DPO Works

DPO does not let the model learn by online trial and error. It trains directly on preference data. For example:

Prompt:
Explain the difference between a reward model and a critic.

Chosen:
A reward model is a judge that scores a complete answer; a critic is a predictor that estimates how much future reward the current state can obtain.

Rejected:
A reward model and a critic both score the model; there is not much difference.

DPO asks whether the current model, relative to the reference model, prefers the chosen answer.

Suppose the current policy log probabilities are:

log pi_theta(chosen | x)   = -120
log pi_theta(rejected | x) = -115

This means the current model is actually more likely to generate the rejected answer, because -115 is a higher log probability than -120.

Now look at the reference model:

log pi_ref(chosen | x)   = -118
log pi_ref(rejected | x) = -116

DPO compares the two reference-relative ratios:

chosen_log_ratio   = -120 - (-118) = -2
rejected_log_ratio = -115 - (-116) = +1
gap = chosen_log_ratio - rejected_log_ratio = -3

The gap is negative, so the current model is more tilted toward the rejected answer than the reference is. The DPO loss pushes it to:

increase log pi_theta(chosen | x)
decrease log pi_theta(rejected | x)

So the training signal is not:

this answer has reward = 0.8

It is:

for the same prompt, the chosen answer should improve relative to the reference more than the rejected answer

In practice, implementations usually sum or average token log probabilities across the whole answer. Answer length, truncation, and tokenizer behavior all affect the values, so chosen / rejected samples should be comparable under the same prompt.

DPO's main advantage is engineering simplicity: no rollout, no explicit / separate online reward-model scoring, and no critic. Its limitation comes from the same place: the model learns only from existing preference pairs and does not actively explore new, better answers.

DPO strengths:

DPO weaknesses:

12.4 GRPO: Critic-Free RL Often Used for Reasoning Models

The core idea of GRPO is to sample multiple answers for the same prompt and compare them within the group.

Prompt x
├── answer A -> reward 0.9
├── answer B -> reward 0.7
├── answer C -> reward 0.2
└── answer D -> reward 0.0

Use the group mean as the baseline and the group standard deviation for normalization:

mean = 0.45
std  = 0.36
A_adv = (0.9 - 0.45) / 0.36 = +1.25
B_adv = (0.7 - 0.45) / 0.36 = +0.69
C_adv = (0.2 - 0.45) / 0.36 = -0.69
D_adv = (0.0 - 0.45) / 0.36 = -1.25

This lets GRPO remove the value model / critic:

PPO:  reward function + critic baseline
GRPO: reward function + group-relative baseline

How GRPO Works

Suppose the prompt is a math problem:

If 3x + 5 = 20, what is x?

GRPO does not sample only one answer. It samples a group of answers for the same prompt:

SampleModel answerreward
A3x=15, so x=51.0
B3x=25, so x=8.330.0
Cx=(20-5)/3=51.0
Dx=150.0

The group mean reward is the baseline, and the group standard deviation normalizes scale:

mean_reward = (1.0 + 0.0 + 1.0 + 0.0) / 4 = 0.5
std_reward  = 0.5

Each answer's advantage can be understood as:

A_adv = (1.0 - 0.5) / 0.5 = +1.0
B_adv = (0.0 - 0.5) / 0.5 = -1.0
C_adv = (1.0 - 0.5) / 0.5 = +1.0
D_adv = (0.0 - 0.5) / 0.5 = -1.0

The update is intuitive:

increase the token probabilities for A / C
decrease the token probabilities for B / D

There is no separate value model. The baseline comes from the group mean for the same prompt; the standard deviation only rescales the advantage. In other words:

PPO asks the critic: roughly how much future reward is this prefix worth?
GRPO asks the group: is this answer better or worse than the other answers for the same problem?

GRPO still usually keeps PPO-like ratio / KL control so the model does not over-optimize reward and drift too far from the reference policy. It is especially suitable for math, code, multiple-choice, and format-verifiable tasks because rewards can be computed automatically:

math: whether the final answer is correct
code: whether unit tests pass
tool use: whether the task is completed and the evidence satisfies the requirement

GRPO is especially natural for RLVR, or reinforcement learning with verifiable rewards:

GRPO strengths:

GRPO weaknesses:

12.5 Do Not Confuse Reward Model and Critic

ItemReward model / reward functionCritic / value model
One-linerJudgePredictor
OutputHow good this answer isHow much future reward this state is expected to get
Direct policy reward signal?Yes, the policy tries to increase rewardNot directly a reward; the critic fits value / return and is used to compute advantage
Needed by PPO?Needs a reward signalUsually yes
Needed by GRPO?Needs a reward signalNo
Needed by DPO?Not explicitlyNo

The most interview-relevant distinction:

reward model: answer -> score
critic: partial trajectory/state -> expected return
advantage: per-token/step return estimate (often GAE / KL-shaped) - critic prediction

12.6 How to Choose

ConditionPrefer
You only have chosen/rejected preference pairs and want fast, stable alignmentDPO
You have a reliable reward model and want classic RLHFPPO
Math / code / reasoning, and rewards can be automatically verifiedGRPO
Training resources are limited and you do not want to maintain a criticDPO or GRPO
You want the model to explore better reasoning through online rollout, and reward can be reliably verifiedGRPO
You want to maximize a general reward, and the reward model is maturePPO

The most practical interview answer:

PPO is the traditional RLHF workhorse: complete but heavy. DPO is the preference-alignment workhorse: offline, simple, and stable. GRPO is a reasoning-RL workhorse: it replaces the critic with group-relative reward and is well suited to verifiable math/code-style tasks.

12.7 Common Questions

Is DPO RL?

In a broad sense, it is derived from an RLHF objective. In the narrower, traditional sense, it is not online RL: there is no rollout, no environment interaction, and no critic. It is more accurate to call it offline preference optimization.

Why Does GRPO Not Need a Critic?

Because it samples multiple answers for the same prompt, uses the group reward mean as the baseline, and uses the group standard deviation to normalize advantage. The critic's role is to provide a baseline; GRPO replaces it with a group baseline.

What Is the Biggest Difference Between PPO and GRPO?

PPO usually trains a value model / critic to estimate advantage. GRPO estimates advantage from relative rewards among multiple samples for the same prompt. PPO is more general; GRPO is cheaper and more direct for verifiable reasoning tasks.

What Do All Three Share?

All three control how much the new model's probabilities change relative to an old policy or reference model. PPO uses ratio clipping and KL, DPO uses a reference-relative preference loss, and GRPO usually keeps KL or ratio clipping as well.

12.8 Final Cheat Sheet

PPO:
  online RLHF, reward model/function + critic + KL/clip
  strongest general RLHF baseline, but expensive and sensitive

DPO:
  offline preference optimization, chosen/rejected pair
  no rollout, no explicit reward model, no critic
  simple, stable, good for alignment

GRPO:
  online critic-free RL, group-relative advantage
  no value model, good for verifiable reasoning rewards
  common in modern verifiable reasoning RL

12.9 References

附录:Coding 面试速查 — Classic Hot 100 + 手写 MHA / ConvAppendix: Coding Interview Quick Review — Classic Hot 100 + From-Scratch MHA / Conv

这是一份正交的实现参考,和上面的 harness / RL 论述没有直接关系,单独收录:面试前两小时的 Classic Hot 100 题型地图,以及可手写、可解释的 PyTorch multi-head attention、1D / 2D convolution 模板。
An orthogonal implementation reference, kept separate from the harness / RL argument above: a two-hour Classic Hot 100 pattern map plus from-scratch, explainable PyTorch multi-head attention and 1D / 2D convolution templates.

Apr 2026 snapshot · Classic Hot 100 · PyTorch from scratch

本节用于面试前两小时复习,不是完整题解集。这里的 Hot 100 是 Apr 2026 的 classic/interview-prep snapshot,而不是声称同步 LeetCode 当前 live list。目标是快速回忆题型入口、常见模板,以及 multi-head attention、1D convolution、2D convolution 三个高频深度学习手写题。

核心原则:Hot 100 的重点不是记题号,而是看到题目后能快速判断应使用哪种状态、数据结构或转移。代码题的重点也不是炫技,而是讲清 shape、边界、mask、stride/padding。

A.1 两小时复习计划

时间复习内容目标
0-15 min数组、哈希、前缀和、双指针、滑动窗口先覆盖最高频、最容易稳定得分的题型
15-35 min链表、栈、堆、二分把模板写顺
35-60 min二叉树 DFS/BFS、BST、LCA递归返回值要清楚
60-85 min图 DFS/BFS、拓扑排序、并查/矩阵搜索visited 和状态染色
85-115 minDP、回溯、贪心/区间写出状态定义和转移
115-120 minMHA / Conv1D / Conv2D shape 口述防止代码题卡在维度

A.2 Classic Hot 100 题型速查

类型必会题第一反应
哈希 / setTwo Sum, Longest Consecutive, Group Anagrams, Majority Element计数、查补数、set 判断序列起点
前缀 / 前后缀Subarray Sum Equals K, Path Sum III, Product Except Selfprefix[cur-k],树上回溯要撤销;Product Except Self 用左右乘积
双指针3Sum, Container With Most Water, Move Zeroes, Palindrome Linked List排序后夹逼;链表找中点再反转
滑动窗口Longest Substring Without Repeating, Find All Anagrams右扩左缩,维护计数
链表Reverse List, Merge Two Lists, Cycle, Detect Cycle, Remove Nth, LRUdummy、快慢指针、哈希+双向链表
Valid Parentheses, Daily Temperatures, Decode String, MinStack单调栈存 index;辅助栈存 min
堆 / TopKKth Largest, Top K Frequent, Merge k Lists / Meeting Rooms 类heap 或 bucket;Kth largest 可 quickselect
二分Search Rotated Array, Search Range, Matrix Search明确哪边有序;左右边界分开写
树 DFSMax Depth, Invert Tree, Diameter, LCA, Flatten, Build Tree递归函数返回什么先说清
BSTValidate BST, Convert BST, Kth-like traversal中序、上下界、反中序累加
图 / 矩阵Number of Islands, Course Schedule, Word Search, Evaluate DivisionDFS/BFS visited;拓扑排序查环
DP 一维House Robber, Coin Change, Word Break, LISdp[i] 的含义先定
DP 二维Maximal Square, Edit Distance, Unique Paths, Min Path Sum当前格依赖左/上/左上
回溯Generate Parentheses, Permutations, Subsets, Combination Sumchoose -> dfs -> unchoose
贪心 / 区间Jump Game, Merge Intervals, Queue Reconstruction, Meeting Rooms排序准则最重要

A.3 Classic Hot 100 解法速查表

这张表只保留面试复习最有用的信息:题目入口、模板和关键一步。面试时先说明模板,再处理边界。

模板入口一行解法
1 Two Sum哈希表遍历 x 时查 target-x 是否见过,存值到下标。
2 Add Two Numbers链表模拟双指针逐位相加,维护 carry,用 dummy 串结果。
3 Longest Substring Without Repeating滑动窗口右扩计数,重复时左缩到合法,更新最大长度。
4 Median of Two Sorted Arrays二分划分在短数组二分切分点,使左右两半数量相等且左侧都小于右侧。
5 Longest Palindromic Substring中心扩展枚举奇偶中心向两侧扩,保存最长区间。
10 Regular Expression MatchingDPdp[i][j] 表示 s[:i] 匹配 p[:j],重点处理 * 的零次和多次。
11 Container With Most Water双指针左右夹逼,每次移动较短边,因为面积受短边限制。
15 3Sum排序 + 双指针固定第一个数,剩下区间夹逼,注意跳过重复值。
17 Letter Combinations回溯每位数字展开字符,路径长度等于输入长度时收集。
19 Remove Nth From End快慢指针dummy 起步,fast 先走 n 步,再同步走到待删前驱。
20 Valid Parentheses左括号入栈,右括号必须匹配栈顶,最后栈空。
21 Merge Two Sorted Lists链表归并dummy + tail,每次接较小节点,最后接剩余链表。
22 Generate Parentheses回溯剪枝left < n 可放左括号,right < left 可放右括号。
23 Merge k Sorted Lists堆 / 分治小根堆按节点值弹出,或两两归并到一条链表。
31 Next Permutation后缀处理从右找下降点,和右侧刚大于它的数交换,再反转后缀。
32 Longest Valid Parentheses栈 / DP栈存下标并保留最后非法位置,遇到匹配时更新长度。
33 Search in Rotated Sorted Array二分每轮判断哪一半有序,再决定目标是否落在有序半边。
34 Find First and Last Position二分边界分别写 lower_bound 找第一个 target 和第一个 > target
39 Combination Sum回溯candidates 可重复,递归传当前 index,剩余和为 0 收集。
42 Trapping Rain Water双指针维护左右最大值,移动较小侧并累加可接水量。
46 Permutations回溯used 数组控制每个数只选一次,路径满长收集。
48 Rotate Image原地矩阵先沿主对角线转置,再反转每一行。
49 Group Anagrams哈希签名排序字符串或 26 维计数作为 key 分组。
53 Maximum SubarrayKadanecur=max(x, cur+x),答案取所有 cur 最大值。
55 Jump Game贪心维护当前能到的最远位置,遍历中一旦 i > far 返回 false。
56 Merge Intervals排序 + 合并按 start 排序,当前区间和结果尾部重叠就扩 end。
62 Unique PathsDPdp[j] += dp[j-1],每格来自上方和左方。
64 Minimum Path SumDP原地或一维 DP,当前格加上上方/左方较小值。
70 Climbing Stairs斐波那契dp[i]=dp[i-1]+dp[i-2],可用两个变量滚动。
72 Edit Distance二维 DPdp[i][j] 表示前缀距离,增删改取最小。
75 Sort Colors三指针zero/i/two 扫描,0 换前面,2 换后面,1 跳过。
76 Minimum Window Substring滑动窗口右扩满足 need,左缩保持有效并更新最短窗口。
78 Subsets回溯 / 位枚举每个位置选或不选,或遍历 bitmask。
79 Word SearchDFS 回溯从匹配首字母格出发,四方向搜索,访问过的格临时标记。
84 Largest Rectangle in Histogram单调栈栈递增,遇到更矮柱子就弹出并以弹出高度算面积。
85 Maximal Rectangle单调栈 + 行高每行把连续 1 转成 histogram,套 84。
94 Binary Tree Inorder Traversal栈 / DFS左根右,迭代版一路压左,弹出后转右。
96 Unique Binary Search TreesDP / Catalan枚举根 kdp[n]+=dp[k-1]*dp[n-k]
98 Validate BST上下界 / 中序递归带 (low, high),或中序必须严格递增。
101 Symmetric Tree双递归比较 left.leftright.rightleft.rightright.left
102 Binary Tree Level OrderBFS队列逐层弹出,每层记录当前队列长度。
104 Maximum Depth of Binary TreeDFS返回 1 + max(left_depth, right_depth)
105 Build Tree from Preorder and Inorder递归建树preorder 首元素是根,用 inorder 下标切左右子树。
114 Flatten Binary Tree to Linked List后序 / 前序重连把左子树展平接到右边,再把原右子树接到左链尾部。
121 Best Time to Buy and Sell Stock一次扫描维护历史最低价,用当前价减最低价更新最大利润。
124 Binary Tree Maximum Path SumDFS返回单边最大贡献,答案用 left+root+right 更新。
128 Longest Consecutive Sequence哈希 set只从没有前驱的数开始向后数长度。
136 Single Number位运算所有数字异或,成对数字抵消,剩下单数。
138 Copy List with Random Pointer哈希 / 织链map 原节点到新节点,或复制节点插入原链再拆分。
139 Word BreakDP + setdp[i] 表示前缀可拆,枚举切点 js[j:i]
141 Linked List Cycle快慢指针slow 走一步 fast 走两步,相遇则有环。
142 Linked List Cycle II快慢指针相遇后一个指针回 head,再同步走,相遇点是入环点。
146 LRU Cache哈希 + 双向链表map 定位节点,链表维护最近使用顺序,容量满删尾部。
148 Sort List归并排序快慢指针切半,递归排序后 merge 两条有序链。
152 Maximum Product SubarrayDP 滚动同时维护最大/最小乘积,遇负数会交换角色。
155 Min Stack辅助栈主栈存值,min 栈同步存当前最小值。
160 Intersection of Two Linked Lists双指针换头A/B 指针走到尾后切到对方头部,路径总长相同后相遇。
169 Majority ElementBoyer-Moore票数为 0 换候选,相同加一,不同减一。
198 House Robber一维 DPdp=max(skip, rob_prev+x),两个变量滚动。
200 Number of IslandsDFS/BFS遇到陆地就 flood fill 标记整座岛,计数加一。
206 Reverse Linked List指针反转prev, cur 迭代,每次保存 next 后反向。
207 Course Schedule拓扑 / 染色 DFS入度 BFS 看能否修完,或 DFS 三色判环。
208 Implement Trie前缀树每个节点保存 children 和 is_end,插入/查询逐字符走。
215 Kth Largest Element堆 / quickselect小根堆保留 k 个,或 partition 找下标 n-k
221 Maximal Square二维 DP若当前为 1,边长是左/上/左上最小值加一。
226 Invert Binary TreeDFS交换左右子树,再递归处理子树。
234 Palindrome Linked List快慢 + 反转找中点,反转后半段,再和前半段逐个比较。
236 Lowest Common Ancestor后序 DFS左右子树都命中则当前是 LCA,否则返回命中的一边。
238 Product Except Self前后缀乘积左乘积扫一遍,右乘积反向扫一遍,无需除法。
239 Sliding Window Maximum单调队列队列存下标且值递减,窗口右移时弹出过期下标。
240 Search a 2D Matrix II右上角搜索从右上开始,大了左移,小了下移。
279 Perfect SquaresDP / BFSdp[i]=min(dp[i-j*j]+1),也可把余数当图 BFS。
283 Move Zeroes双指针slow 指向下一个非零位置,遍历时把非零换到前面。
287 Find the Duplicate Number快慢指针把数组值看成 next 指针,Floyd 找环入口。
300 Longest Increasing Subsequence贪心 + 二分tails[k] 存长度 k+1 的最小尾值,二分替换。
301 Remove Invalid ParenthesesBFS / 回溯BFS 按删除次数扩展,首次出现合法层即答案。
309 Best Time to Buy and Sell Stock with Cooldown状态机 DP维护 hold/sold/rest,sold 后一天只能到 cooldown/rest。
312 Burst Balloons区间 DPdp[l][r] 表示开区间最大值,枚举最后戳破的气球。
322 Coin Change完全背包dp[amount]=min(dp[amount], dp[amount-coin]+1)
337 House Robber III树形 DP每个节点返回 rob/not_rob 两个值。
338 Counting Bits位 DPbits[i]=bits[i>>1]+(i&1)
347 Top K Frequent Elements堆 / 桶统计频率后用小根堆保 k 个,或按频率进桶。
394 Decode String[ 保存当前字符串和重复次数,遇 ] 弹出拼接。
399 Evaluate Division图 DFS / 并查变量是点,比值是带权边,查询时找路径乘积。
406 Queue Reconstruction by Height贪心排序按身高降序、k 升序排序,再按 k 插入。
416 Partition Equal Subset Sum0/1 背包目标是 sum/2,倒序更新 boolean dp。
437 Path Sum III树上前缀和DFS 维护 prefix count,进入加一,退出撤销。
438 Find All Anagrams in a String滑动窗口计数固定长度窗口维护字符差异,差异为 0 记录起点。
448 Find All Numbers Disappeared原地标记abs(nums[i])-1 做下标,把对应位置变负。
461 Hamming Distance位运算x ^ y 后统计 1 的个数。
494 Target Sum背包转化正负号问题转成子集和 (sum+target)/2
538 Convert BST to Greater Tree反中序右根左遍历,累加已见更大值。
543 Diameter of Binary TreeDFS 高度每个节点用左右高度更新直径,返回单边高度。
560 Subarray Sum Equals K前缀和哈希当前前缀为 cur,答案加 count[cur-k]
581 Shortest Unsorted Continuous Subarray双扫描左到右维护 max_seen 找右边界,右到左维护 min_seen 找左边界。
617 Merge Two Binary TreesDFS两树节点都在则值相加,否则返回非空节点。
621 Task Scheduler贪心公式 / 堆由最高频任务决定空槽,答案是 max(len(tasks), (maxFreq-1)*(cool+1)+countMax)
647 Palindromic Substrings中心扩展每个字符和字符间隙作中心,扩到不等为止。
739 Daily Temperatures单调栈栈存等待更高温的下标,遇更高温时弹出并填天数。
763 Partition Labels贪心记录每个字符最后位置,扫描到当前段最大 last 时切分。

A.4 看到题后的选择顺序

1. 需要"连续子数组/路径和"为 K?        -> prefix sum + hashmap
2. 需要"最长/最短 substring"?          -> sliding window
3. 已排序 / 可排序后找二元组或三元组?   -> two pointers
4. 链表删除/合并/反转?                 -> dummy + pointer rewrite
5. 树题问路径/高度/祖先?               -> DFS return value
6. 矩阵连通块?                         -> BFS/DFS + visited
7. 课程/依赖/先后关系?                 -> graph + cycle detection/toposort
8. 最优值/方案数/可行性?               -> DP
9. 枚举所有方案?                       -> backtracking
10. Top K / 频率?                      -> heap / bucket / quickselect

A.5 DP 和回溯最容易丢分的点

DP 面试时先交代四件事:

1. state: dp[i] / dp[i][j] 表示什么
2. transition: 从哪些旧状态转移
3. init: 空串、0、第一行第一列怎么设
4. answer: 返回 dp[n] 还是 max(dp)

回溯模板:

def backtrack(path, choices):
    if is_done(path):
        res.append(path.copy())
        return
    for choice in choices:
        if invalid(choice):
            continue
        path.append(choice)
        backtrack(path, next_choices)
        path.pop()

A.6 手写 Multi-Head Attention

面试时优先讲清楚 shape:

x:      [B, T, C]
q/k/v:  [B, T, C] -> [B, H, T, D]
scores: [B, H, T, T]
out:    [B, H, T, D] -> [B, T, C]
import math
import torch
import torch.nn as nn
import torch.nn.functional as F


class MultiHeadAttention(nn.Module):
    def __init__(self, embed_dim, num_heads, dropout=0.0, bias=True):
        super().__init__()
        assert embed_dim % num_heads == 0
        self.embed_dim = embed_dim
        self.num_heads = num_heads
        self.head_dim = embed_dim // num_heads

        self.q_proj = nn.Linear(embed_dim, embed_dim, bias=bias)
        self.k_proj = nn.Linear(embed_dim, embed_dim, bias=bias)
        self.v_proj = nn.Linear(embed_dim, embed_dim, bias=bias)
        self.out_proj = nn.Linear(embed_dim, embed_dim, bias=bias)
        self.dropout = nn.Dropout(dropout)

    def _split_heads(self, x):
        # [B, T, C] -> [B, H, T, D]
        B, T, C = x.shape
        return x.view(B, T, self.num_heads, self.head_dim).transpose(1, 2)

    def _merge_heads(self, x):
        # [B, H, T, D] -> [B, T, C]
        B, H, T, D = x.shape
        return x.transpose(1, 2).contiguous().view(B, T, H * D)

    def forward(self, x, attn_mask=None, key_padding_mask=None, return_attn=False):
        # x: [B, T, C]
        B, T, C = x.shape
        q = self._split_heads(self.q_proj(x))
        k = self._split_heads(self.k_proj(x))
        v = self._split_heads(self.v_proj(x))

        scores = q @ k.transpose(-2, -1)
        scores = scores / math.sqrt(self.head_dim)

        if attn_mask is not None:
            # Attention/causal mask: [T,T] -> [1,1,T,T], or already broadcastable.
            if attn_mask.dim() == 2:
                attn_mask = attn_mask[None, None, :, :]
            elif attn_mask.dim() == 3:
                attn_mask = attn_mask[:, None, :, :]

            # bool True = masked out; binary numeric uses 1=keep, 0=masked.
            # Additive PyTorch masks (0 / -inf) should be added to scores instead.
            if attn_mask.dtype == torch.bool:
                scores = scores.masked_fill(attn_mask, float("-inf"))
            else:
                scores = scores.masked_fill(attn_mask == 0, float("-inf"))

        if key_padding_mask is not None:
            # Padding mask: [B,T]. Do not infer this from a generic 2-D mask.
            key_padding_mask = key_padding_mask[:, None, None, :]
            if key_padding_mask.dtype == torch.bool:
                scores = scores.masked_fill(key_padding_mask, float("-inf"))
            else:
                scores = scores.masked_fill(key_padding_mask == 0, float("-inf"))

        attn = F.softmax(scores, dim=-1)
        attn = self.dropout(attn)
        out = attn @ v
        out = self.out_proj(self._merge_heads(out))
        return (out, attn) if return_attn else out

常见追问:

A.7 手写 1D Convolution

输入和权重:

x:      [B, Cin, L]
weight: [Cout, Cin, K]
out:    [B, Cout, Lout]
Lout = floor((L + 2P - D*(K-1) - 1) / S + 1)
import torch
import torch.nn.functional as F


def conv1d_naive(x, weight, bias=None, stride=1, padding=0, dilation=1):
    B, Cin, L = x.shape
    Cout, Cin_w, K = weight.shape
    assert Cin == Cin_w

    x = F.pad(x, (padding, padding))
    Lpad = x.shape[-1]
    Lout = (Lpad - dilation * (K - 1) - 1) // stride + 1
    out = x.new_zeros(B, Cout, Lout)

    for b in range(B):
        for co in range(Cout):
            for i in range(Lout):
                start = i * stride
                total = x.new_tensor(0.0)
                for ci in range(Cin):
                    for k in range(K):
                        total = total + x[b, ci, start + k * dilation] * weight[co, ci, k]
                if bias is not None:
                    total = total + bias[co]
                out[b, co, i] = total
    return out

验证:

torch.manual_seed(0)
x = torch.randn(2, 3, 12)
w = torch.randn(4, 3, 5)
b = torch.randn(4)
y = conv1d_naive(x, w, b, stride=2, padding=2)
ref = F.conv1d(x, w, b, stride=2, padding=2)
assert torch.allclose(y, ref, atol=1e-5)

A.8 手写 2D Convolution

输入和权重:

x:      [B, Cin, H, W]
weight: [Cout, Cin, KH, KW]
out:    [B, Cout, Hout, Wout]
Hout = floor((H + 2PH - DH*(KH-1) - 1) / SH + 1)
Wout = floor((W + 2PW - DW*(KW-1) - 1) / SW + 1)
import torch
import torch.nn.functional as F


def _pair(v):
    return v if isinstance(v, tuple) else (v, v)


def conv2d_naive(x, weight, bias=None, stride=1, padding=0, dilation=1):
    stride_h, stride_w = _pair(stride)
    pad_h, pad_w = _pair(padding)
    dil_h, dil_w = _pair(dilation)

    B, Cin, H, W = x.shape
    Cout, Cin_w, KH, KW = weight.shape
    assert Cin == Cin_w

    x = F.pad(x, (pad_w, pad_w, pad_h, pad_h))
    Hpad, Wpad = x.shape[-2], x.shape[-1]
    Hout = (Hpad - dil_h * (KH - 1) - 1) // stride_h + 1
    Wout = (Wpad - dil_w * (KW - 1) - 1) // stride_w + 1
    out = x.new_zeros(B, Cout, Hout, Wout)

    for b in range(B):
        for co in range(Cout):
            for oh in range(Hout):
                for ow in range(Wout):
                    h0 = oh * stride_h
                    w0 = ow * stride_w
                    total = x.new_tensor(0.0)
                    for ci in range(Cin):
                        for kh in range(KH):
                            for kw in range(KW):
                                total = total + (
                                    x[b, ci, h0 + kh * dil_h, w0 + kw * dil_w]
                                    * weight[co, ci, kh, kw]
                                )
                    if bias is not None:
                        total = total + bias[co]
                    out[b, co, oh, ow] = total
    return out

验证:

torch.manual_seed(0)
x = torch.randn(2, 3, 8, 9)
w = torch.randn(4, 3, 3, 5)
b = torch.randn(4)
y = conv2d_naive(x, w, b, stride=(2, 1), padding=(1, 2))
ref = F.conv2d(x, w, b, stride=(2, 1), padding=(1, 2))
assert torch.allclose(y, ref, atol=1e-5)

A.9 三个代码题的面试讲法

先说什么最容易错
MHAshape 流、scale、mask、softmax 维度mask 语义反了;view 前忘 contiguous
Conv1DLout 公式、stride/padding/dilationpadding 方向、kernel index
Conv2DHout/Wout 公式、空间循环 + channel 累加F.pad 参数顺序是 (left,right,top,bottom)

A.10 最后五分钟速记

Hot 100:
  sum/count -> hashmap/prefix
  substring -> sliding window
  sorted pair/triple -> two pointers
  tree -> define DFS return
  graph/matrix -> visited + BFS/DFS
  最优值问题 -> 先定义 DP 状态

MHA:
  [B,T,C] -> [B,H,T,D] -> scores [B,H,T,T] -> softmax -> merge

Conv:
  output size = floor((input + 2*pad - dilation*(kernel-1) - 1)/stride + 1)

A.11 参考

Apr 2026 snapshot · Classic Hot 100 · PyTorch from scratch

This is a two-hour interview-prep notebook, not a full solution manual. The Hot 100 list here is a Classic Hot 100 snapshot for Apr 2026 interview prep, not a claim that it mirrors LeetCode's live list. The goal is to quickly recover each problem's pattern entry point, the template you should reach for, and the shape reasoning behind three common deep-learning implementation prompts: MHA, Conv1D, and Conv2D.

Core rule: Classic Hot 100 practice is not about memorizing problem numbers. It is about recognizing the state, data structure, or transition as soon as you see the prompt. For implementation questions, the bar is not cleverness; it is clear shape accounting, boundary handling, mask semantics, stride, and padding.

A.1 Two-Hour Review Plan

TimeReviewGoal
0-15 minArrays, hash maps, prefix sums, two pointers, sliding windowStart with the highest-frequency patterns that usually convert into stable points.
15-35 minLinked lists, stacks, heaps, binary searchMake the templates feel automatic.
35-60 minBinary-tree DFS/BFS, BST, LCABe explicit about what each recursive call returns.
60-85 minGraph DFS/BFS, topological sort, union-find, matrix searchKeep visited, coloring, and state transitions straight.
85-115 minDP, backtracking, greedy / intervalsState the DP meaning and transition before writing code.
115-120 minMHA / Conv1D / Conv2D shape walkthroughAvoid getting stuck on dimensions during implementation prompts.

A.2 Classic Hot 100 Pattern Entry Points

PatternMust-know problemsFirst reaction
Hash / setTwo Sum, Longest Consecutive, Group Anagrams, Majority ElementCount, look up complements, or use a set to detect sequence starts.
Prefix / prefix-suffixSubarray Sum Equals K, Path Sum III, Product Except SelfUse prefix[cur-k]; undo tree-prefix counts on backtrack; Product Except Self uses prefix/suffix products.
Two pointers3Sum, Container With Most Water, Move Zeroes, Palindrome Linked ListSort then squeeze; for linked lists, find the middle and reverse the second half.
Sliding windowLongest Substring Without Repeating, Find All AnagramsExpand right, shrink left, maintain counts.
Linked listReverse List, Merge Two Lists, Cycle, Detect Cycle, Remove Nth, LRUDummy node, slow/fast pointers, hash map plus doubly linked list.
StackValid Parentheses, Daily Temperatures, Decode String, MinStackMonotonic stack stores indices; helper stack stores current minimum.
Heap / TopKKth Largest, Top K Frequent, Merge k Lists / Meeting Rooms styleHeap or bucket; Kth largest can also be quickselect.
Binary searchSearch Rotated Array, Search Range, Matrix SearchIdentify the sorted half; write left and right boundary searches separately.
Tree DFSMax Depth, Invert Tree, Diameter, LCA, Flatten, Build TreeSay what the recursive function returns before coding.
BSTValidate BST, Convert BST, Kth-like traversalInorder, bounds, or reverse-inorder accumulation.
Graph / matrixNumber of Islands, Course Schedule, Word Search, Evaluate DivisionDFS/BFS with visited; topological sort or coloring for cycles.
1D DPHouse Robber, Coin Change, Word Break, LISDefine exactly what dp[i] means.
2D DPMaximal Square, Edit Distance, Unique Paths, Min Path SumCurrent cell depends on left / up / upper-left.
BacktrackingGenerate Parentheses, Permutations, Subsets, Combination Sumchoose -> dfs -> unchoose.
Greedy / intervalsJump Game, Merge Intervals, Queue Reconstruction, Meeting RoomsThe sorting rule is the main idea.

A.3 Classic Hot 100 Solution Table

This table keeps the interview-useful part only: pattern entry point, template, and the one key move. In an interview, state the template first, then handle edge cases.

ProblemPattern entry pointOne-line solution
1 Two SumHash mapAs you scan x, check whether target-x was seen; store value to index.
2 Add Two NumbersLinked-list simulationAdd digit by digit with two pointers and carry; build the result from a dummy node.
3 Longest Substring Without RepeatingSliding windowExpand right, shrink left when a duplicate appears, update the max length.
4 Median of Two Sorted ArraysBinary partitionBinary search the cut in the shorter array so both halves have equal size and all left values are <= right values.
5 Longest Palindromic SubstringCenter expansionTry odd and even centers, expand outward, keep the longest interval.
10 Regular Expression MatchingDPdp[i][j] means s[:i] matches p[:j]; handle * as zero or many copies.
11 Container With Most WaterTwo pointersSqueeze from both ends; move the shorter side because it bounds the area.
15 3SumSort + two pointersFix the first value, squeeze the remaining range, and skip duplicates.
17 Letter CombinationsBacktrackingExpand each digit into characters; collect when the path length equals input length.
19 Remove Nth From EndSlow/fast pointersStart from dummy; move fast n steps first, then walk together to the predecessor.
20 Valid ParenthesesStackPush left brackets; each right bracket must match the stack top; finish with an empty stack.
21 Merge Two Sorted ListsLinked-list mergeDummy + tail; append the smaller node each time, then append the remainder.
22 Generate ParenthesesBacktracking with pruningAdd ( if left < n; add ) if right < left.
23 Merge k Sorted ListsHeap / divide and conquerPop nodes from a min-heap, or merge lists pairwise.
31 Next PermutationSuffix handlingFind the rightmost descent, swap with the next larger suffix value, then reverse the suffix.
32 Longest Valid ParenthesesStack / DPStore indices and the last invalid position; update length on each match.
33 Search in Rotated Sorted ArrayBinary searchDecide which half is sorted, then test whether the target lies in that half.
34 Find First and Last PositionBoundary binary searchWrite one lower_bound for the first target and another for the first > target.
39 Combination SumBacktrackingCandidates can repeat; pass the current index, collect when the remaining sum is 0.
42 Trapping Rain WaterTwo pointersMaintain left/right maxima; move the smaller side and add trapped water.
46 PermutationsBacktrackingUse a used array so each value is chosen once; collect full-length paths.
48 Rotate ImageIn-place matrixTranspose along the main diagonal, then reverse each row.
49 Group AnagramsHash signatureUse sorted string or 26-count vector as the grouping key.
53 Maximum SubarrayKadanecur=max(x, cur+x); answer is the max over all cur.
55 Jump GameGreedyTrack the farthest reachable index; if i > far, return false.
56 Merge IntervalsSort + mergeSort by start; if the current interval overlaps the result tail, extend the end.
62 Unique PathsDPdp[j] += dp[j-1]; each cell comes from top and left.
64 Minimum Path SumDPIn-place or 1D DP; add the smaller of top and left to the current cell.
70 Climbing StairsFibonaccidp[i]=dp[i-1]+dp[i-2]; roll with two variables.
72 Edit Distance2D DPdp[i][j] is prefix distance; take min over insert/delete/replace.
75 Sort ColorsThree pointersScan with zero/i/two; swap 0 forward, 2 backward, skip 1.
76 Minimum Window SubstringSliding windowExpand until requirements are met, shrink while valid, update the shortest window.
78 SubsetsBacktracking / bitmaskChoose or skip each position, or iterate over bitmasks.
79 Word SearchDFS backtrackingStart from matching first-letter cells, search four directions, temporarily mark visited cells.
84 Largest Rectangle in HistogramMonotonic stackKeep heights increasing; when a shorter bar arrives, pop and compute area using popped height.
85 Maximal RectangleMonotonic stack + row heightsConvert each row into a histogram of consecutive 1s, then reuse problem 84.
94 Binary Tree Inorder TraversalStack / DFSLeft-root-right; iterative version pushes all left nodes, pops, then goes right.
96 Unique Binary Search TreesDP / CatalanEnumerate root k: dp[n]+=dp[k-1]*dp[n-k].
98 Validate BSTBounds / inorderRecurse with (low, high), or require inorder traversal to be strictly increasing.
101 Symmetric TreePaired recursionCompare left.left with right.right, and left.right with right.left.
102 Binary Tree Level OrderBFSPop level by level; record current queue length for each layer.
104 Maximum Depth of Binary TreeDFSReturn 1 + max(left_depth, right_depth).
105 Build Tree from Preorder and InorderRecursive buildFirst preorder value is root; split left/right subtrees by inorder index.
114 Flatten Binary Tree to Linked ListPostorder / preorder rewiringFlatten left subtree to the right, then attach the original right subtree to the left-chain tail.
121 Best Time to Buy and Sell StockOne scanTrack historical minimum price; update max profit with current minus min.
124 Binary Tree Maximum Path SumDFSReturn one-sided max contribution; update answer with left+root+right.
128 Longest Consecutive SequenceHash setOnly start counting from numbers that have no predecessor.
136 Single NumberBit operationXOR all numbers; pairs cancel and the singleton remains.
138 Copy List with Random PointerHash / interleaving copyMap old nodes to new nodes, or insert copied nodes into the original list then split.
139 Word BreakDP + setdp[i] means prefix can be segmented; enumerate cut j and check s[j:i].
141 Linked List CycleSlow/fast pointersSlow moves one step and fast moves two; meeting means there is a cycle.
142 Linked List Cycle IISlow/fast pointersAfter meeting, move one pointer to head; walk both one step until they meet at cycle entry.
146 LRU CacheHash + doubly linked listMap finds nodes; list maintains recency; evict tail when capacity is full.
148 Sort ListMerge sortSplit by slow/fast pointers, recursively sort, then merge two sorted lists.
152 Maximum Product SubarrayRolling DPTrack both max and min product because a negative number can swap roles.
155 Min StackHelper stackMain stack stores values; min stack stores current minimum in sync.
160 Intersection of Two Linked ListsTwo pointers switching headsWhen A/B reach the end, switch to the other head; equal total path length leads to meeting.
169 Majority ElementBoyer-MooreReset candidate when vote is 0; same value increments, different value decrements.
198 House Robber1D DPdp=max(skip, rob_prev+x); roll with two variables.
200 Number of IslandsDFS/BFSEach time land is found, flood-fill the whole island and increment count.
206 Reverse Linked ListPointer reversalIterate with prev, cur; save next before reversing the link.
207 Course ScheduleToposort / coloring DFSBFS on indegrees to see if all courses finish, or use three-color DFS cycle detection.
208 Implement TriePrefix treeEach node stores children and is_end; insert/search walks character by character.
215 Kth Largest ElementHeap / quickselectKeep a size-k min-heap, or partition to find index n-k.
221 Maximal Square2D DPIf current cell is 1, side length is min(left, up, upper-left) + 1.
226 Invert Binary TreeDFSSwap children, then recurse into subtrees.
234 Palindrome Linked ListSlow/fast + reverseFind middle, reverse second half, compare against the first half.
236 Lowest Common AncestorPostorder DFSIf both subtrees hit, current node is LCA; otherwise return the side that hit.
238 Product Except SelfPrefix/suffix productsScan left products, then scan right products backward; no division needed.
239 Sliding Window MaximumMonotonic dequeStore indices with decreasing values; drop expired indices as the window moves.
240 Search a 2D Matrix IITop-right searchStart at top-right; move left if too large, down if too small.
279 Perfect SquaresDP / BFSdp[i]=min(dp[i-j*j]+1); alternatively BFS over remainders.
283 Move ZeroesTwo pointersslow points to the next non-zero slot; swap non-zero values forward while scanning.
287 Find the Duplicate NumberSlow/fast pointersTreat array values as next pointers; Floyd finds the cycle entry.
300 Longest Increasing SubsequenceGreedy + binary searchtails[k] stores the minimum tail for length k+1; binary replace.
301 Remove Invalid ParenthesesBFS / backtrackingBFS by deletion count; the first valid layer is the answer.
309 Best Time to Buy and Sell Stock with CooldownState-machine DPTrack hold/sold/rest; after sold, next day can only move to cooldown/rest.
312 Burst BalloonsInterval DPdp[l][r] is max value for open interval; enumerate the balloon popped last.
322 Coin ChangeComplete knapsackdp[amount]=min(dp[amount], dp[amount-coin]+1).
337 House Robber IIITree DPEach node returns two values: rob and not_rob.
338 Counting BitsBit DPbits[i]=bits[i>>1]+(i&1).
347 Top K Frequent ElementsHeap / bucketCount frequencies, then keep k in a min-heap or bucket by frequency.
394 Decode StringStackOn [, save current string and repeat count; on ], pop and concatenate.
399 Evaluate DivisionGraph DFS / union-findVariables are nodes, ratios are weighted edges; answer queries by multiplying along a path.
406 Queue Reconstruction by HeightGreedy sortSort by height descending and k ascending, then insert each person at index k.
416 Partition Equal Subset Sum0/1 knapsackTarget is sum/2; update boolean DP in reverse.
437 Path Sum IIITree prefix sumDFS maintains prefix counts; add on entry and remove on exit.
438 Find All Anagrams in a StringSliding window countMaintain character differences in a fixed-length window; record start when diff is 0.
448 Find All Numbers DisappearedIn-place markingUse abs(nums[i])-1 as index and negate the corresponding position.
461 Hamming DistanceBit operationCount 1s in x ^ y.
494 Target SumKnapsack transformConvert signs into subset sum (sum+target)/2.
538 Convert BST to Greater TreeReverse inorderTraverse right-root-left and accumulate larger values already seen.
543 Diameter of Binary TreeDFS heightUpdate diameter with left and right heights at each node; return one-sided height.
560 Subarray Sum Equals KPrefix sum + hash mapWith current prefix cur, add count[cur-k] to the answer.
581 Shortest Unsorted Continuous SubarrayTwo scansLeft-to-right max_seen finds the right boundary; right-to-left min_seen finds the left boundary.
617 Merge Two Binary TreesDFSIf both nodes exist, sum values; otherwise return the non-empty node.
621 Task SchedulerGreedy formula / heapHighest frequency decides idle slots: max(len(tasks), (maxFreq-1)*(cool+1)+countMax).
647 Palindromic SubstringsCenter expansionUse every character and gap as a center; expand until unequal.
739 Daily TemperaturesMonotonic stackStack stores indices waiting for a warmer day; pop and fill distance when warmer temperature appears.
763 Partition LabelsGreedyRecord each character's last position; cut when scan reaches the current segment's max last.

A.4 Selection Order After Seeing a Prompt

1. Need "contiguous subarray/path sum equals K"? -> prefix sum + hashmap
2. Need "longest/shortest substring"?           -> sliding window
3. Sorted / can sort and find pairs or triples? -> two pointers
4. Linked-list delete/merge/reverse?            -> dummy + pointer rewrite
5. Tree asks path/height/ancestor?              -> DFS return value
6. Matrix connected components?                 -> BFS/DFS + visited
7. Courses/dependencies/order constraints?      -> graph + cycle detection/toposort
8. Optimal value / count / feasibility?         -> DP
9. Enumerate all valid answers?                 -> backtracking
10. Top K / frequency?                          -> heap / bucket / quickselect

A.5 DP and Backtracking Failure Points

For DP, say these four things before coding:

1. state: what dp[i] / dp[i][j] means
2. transition: which previous states it comes from
3. init: empty string, 0, first row / first column
4. answer: return dp[n] or max(dp)

Backtracking template:

def backtrack(path, choices):
    if is_done(path):
        res.append(path.copy())
        return
    for choice in choices:
        if invalid(choice):
            continue
        path.append(choice)
        backtrack(path, next_choices)
        path.pop()

A.6 From-Scratch MHA

Start with shapes. This is usually more important than the exact syntax:

x:      [B, T, C]
q/k/v:  [B, T, C] -> [B, H, T, D]
scores: [B, H, T, T]
out:    [B, H, T, D] -> [B, T, C]
import math
import torch
import torch.nn as nn
import torch.nn.functional as F


class MultiHeadAttention(nn.Module):
    def __init__(self, embed_dim, num_heads, dropout=0.0, bias=True):
        super().__init__()
        assert embed_dim % num_heads == 0
        self.embed_dim = embed_dim
        self.num_heads = num_heads
        self.head_dim = embed_dim // num_heads

        self.q_proj = nn.Linear(embed_dim, embed_dim, bias=bias)
        self.k_proj = nn.Linear(embed_dim, embed_dim, bias=bias)
        self.v_proj = nn.Linear(embed_dim, embed_dim, bias=bias)
        self.out_proj = nn.Linear(embed_dim, embed_dim, bias=bias)
        self.dropout = nn.Dropout(dropout)

    def _split_heads(self, x):
        # [B, T, C] -> [B, H, T, D]
        B, T, C = x.shape
        return x.view(B, T, self.num_heads, self.head_dim).transpose(1, 2)

    def _merge_heads(self, x):
        # [B, H, T, D] -> [B, T, C]
        B, H, T, D = x.shape
        return x.transpose(1, 2).contiguous().view(B, T, H * D)

    def forward(self, x, attn_mask=None, key_padding_mask=None, return_attn=False):
        # x: [B, T, C]
        B, T, C = x.shape
        q = self._split_heads(self.q_proj(x))
        k = self._split_heads(self.k_proj(x))
        v = self._split_heads(self.v_proj(x))

        scores = q @ k.transpose(-2, -1)
        scores = scores / math.sqrt(self.head_dim)

        if attn_mask is not None:
            # Attention/causal mask: [T,T] -> [1,1,T,T], or already broadcastable.
            if attn_mask.dim() == 2:
                attn_mask = attn_mask[None, None, :, :]
            elif attn_mask.dim() == 3:
                attn_mask = attn_mask[:, None, :, :]

            # bool True = masked out; binary numeric uses 1=keep, 0=masked.
            # Additive PyTorch masks (0 / -inf) should be added to scores instead.
            if attn_mask.dtype == torch.bool:
                scores = scores.masked_fill(attn_mask, float("-inf"))
            else:
                scores = scores.masked_fill(attn_mask == 0, float("-inf"))

        if key_padding_mask is not None:
            # Padding mask: [B,T]. Do not infer this from a generic 2-D mask.
            key_padding_mask = key_padding_mask[:, None, None, :]
            if key_padding_mask.dtype == torch.bool:
                scores = scores.masked_fill(key_padding_mask, float("-inf"))
            else:
                scores = scores.masked_fill(key_padding_mask == 0, float("-inf"))

        attn = F.softmax(scores, dim=-1)
        attn = self.dropout(attn)
        out = attn @ v
        out = self.out_proj(self._merge_heads(out))
        return (out, attn) if return_attn else out

Common follow-ups:

A.7 From-Scratch Conv1D

Input and weight shapes:

x:      [B, Cin, L]
weight: [Cout, Cin, K]
out:    [B, Cout, Lout]
Lout = floor((L + 2P - D*(K-1) - 1) / S + 1)
import torch
import torch.nn.functional as F


def conv1d_naive(x, weight, bias=None, stride=1, padding=0, dilation=1):
    B, Cin, L = x.shape
    Cout, Cin_w, K = weight.shape
    assert Cin == Cin_w

    x = F.pad(x, (padding, padding))
    Lpad = x.shape[-1]
    Lout = (Lpad - dilation * (K - 1) - 1) // stride + 1
    out = x.new_zeros(B, Cout, Lout)

    for b in range(B):
        for co in range(Cout):
            for i in range(Lout):
                start = i * stride
                total = x.new_tensor(0.0)
                for ci in range(Cin):
                    for k in range(K):
                        total = total + x[b, ci, start + k * dilation] * weight[co, ci, k]
                if bias is not None:
                    total = total + bias[co]
                out[b, co, i] = total
    return out

Verification:

torch.manual_seed(0)
x = torch.randn(2, 3, 12)
w = torch.randn(4, 3, 5)
b = torch.randn(4)
y = conv1d_naive(x, w, b, stride=2, padding=2)
ref = F.conv1d(x, w, b, stride=2, padding=2)
assert torch.allclose(y, ref, atol=1e-5)

A.8 From-Scratch Conv2D

Input and weight shapes:

x:      [B, Cin, H, W]
weight: [Cout, Cin, KH, KW]
out:    [B, Cout, Hout, Wout]
Hout = floor((H + 2PH - DH*(KH-1) - 1) / SH + 1)
Wout = floor((W + 2PW - DW*(KW-1) - 1) / SW + 1)
import torch
import torch.nn.functional as F


def _pair(v):
    return v if isinstance(v, tuple) else (v, v)


def conv2d_naive(x, weight, bias=None, stride=1, padding=0, dilation=1):
    stride_h, stride_w = _pair(stride)
    pad_h, pad_w = _pair(padding)
    dil_h, dil_w = _pair(dilation)

    B, Cin, H, W = x.shape
    Cout, Cin_w, KH, KW = weight.shape
    assert Cin == Cin_w

    x = F.pad(x, (pad_w, pad_w, pad_h, pad_h))
    Hpad, Wpad = x.shape[-2], x.shape[-1]
    Hout = (Hpad - dil_h * (KH - 1) - 1) // stride_h + 1
    Wout = (Wpad - dil_w * (KW - 1) - 1) // stride_w + 1
    out = x.new_zeros(B, Cout, Hout, Wout)

    for b in range(B):
        for co in range(Cout):
            for oh in range(Hout):
                for ow in range(Wout):
                    h0 = oh * stride_h
                    w0 = ow * stride_w
                    total = x.new_tensor(0.0)
                    for ci in range(Cin):
                        for kh in range(KH):
                            for kw in range(KW):
                                total = total + (
                                    x[b, ci, h0 + kh * dil_h, w0 + kw * dil_w]
                                    * weight[co, ci, kh, kw]
                                )
                    if bias is not None:
                        total = total + bias[co]
                    out[b, co, oh, ow] = total
    return out

Verification:

torch.manual_seed(0)
x = torch.randn(2, 3, 8, 9)
w = torch.randn(4, 3, 3, 5)
b = torch.randn(4)
y = conv2d_naive(x, w, b, stride=(2, 1), padding=(1, 2))
ref = F.conv2d(x, w, b, stride=(2, 1), padding=(1, 2))
assert torch.allclose(y, ref, atol=1e-5)

A.9 How to Talk Through the Three Implementation Questions

PromptSay firstEasy mistake
MHAShape flow, scaling, mask, softmax dimensionReversing mask semantics; forgetting contiguous before view
Conv1DLout formula, stride/padding/dilationPadding direction, kernel index
Conv2DHout/Wout formula, spatial loops plus channel accumulationF.pad order is (left,right,top,bottom)

A.10 Last Five-Minute Notes

Hot 100:
  sum/count -> hashmap/prefix
  substring -> sliding window
  sorted pair/triple -> two pointers
  tree -> define DFS return
  graph/matrix -> visited + BFS/DFS
  optimal value -> DP state first

MHA:
  [B,T,C] -> [B,H,T,D] -> scores [B,H,T,T] -> softmax -> merge

Conv:
  output size = floor((input + 2*pad - dilation*(kernel-1) - 1)/stride + 1)

A.11 References

引用Cite

如果这篇文章或 ClawBench 对你的工作有用, 欢迎引用。点右上角按钮一键复制 BibTeX。

If this post or ClawBench is useful to you, please cite. Click the button for one-click BibTeX copy.

ClawBench
@article{zhang2026clawbench,
  title={ClawBench: Can AI Agents Complete Everyday Online Tasks?},
  author={Yuxuan Zhang and Yubo Wang and Yipeng Zhu and Penghui Du and Junwen Miao and Xuan Lu and Wendong Xu and Yunzhuo Hao and Songcheng Cai and Xiaochen Wang and Huaisong Zhang and Xian Wu and Yi Lu and Minyi Lei and Kai Zou and Huifeng Yin and Ping Nie and Liang Chen and Dongfu Jiang and Wenhu Chen and Kelsey R. Allen},
  year={2026},
  eprint={2604.08523},
  archivePrefix={arXiv},
  primaryClass={cs.CL},
  url={https://arxiv.org/abs/2604.08523},
}
This post
@misc{zhang2026harnessblog,
  author = {Yuxuan Zhang},
  title  = {Agent Harness Engineering: A Source-Level Comparison of Coding Agents},
  year   = {2026},
  url    = {https://yuxuan.world/blog/harness/}
}