🔶 StateM:面向长程 Agent 的有状态控制
英文标题:🔶 StateM: Stateful Control for Long-Horizon Agents
StateM 将 Agent 工作流组织为可检查的状态、转换和可执行验证检查图,以避免规划、执行、验证、修复与交接全部混入一段长提示词。该项目通过版本化 runbook 和持久化运行时历史,将流程状态从模型上下文中移出,支持上下文刷新、修复循环、动态检查及可恢复的长程运行。论文在 Terminal-Bench 2.1 上报告了系统级执行框架结果:GPT-5.6 Sol xhigh 配合冻结版 StateM profile 的公开提交原始分数为 95.28%,DeepSeek-V4-Flash 配合适配 profile 的标准超时成绩为 88.09%。对开发者而言,StateM 提供了仅依赖 Python 3.11+ 的 CLI 工具,可将 Agent 的关键流程边界、验证门槛和运行记录纳入可版本控制、可审计的工程实践。
211.3 KB
112.1 KB
中文
[2026-08-18]🤗 StateM 在 Hugging Face Daily Papers 排名第 #1。
[2026-08-18]🔥 我们发布了 DeepSeek-V4-Flash 的 StateM runbook 与可复现性发布包,在 Terminal-Bench 2.1 上达到 88.8% 描述性准确率(395/445 次试验;未取整为 88.76%)。所有人都可以试用!
StateM 将 Agent 工作流转化为由状态、转换和可执行检查构成的可检查图。它避免规划、执行、验证、修复和交接全部坍缩为一段长提示词。
长时间运行的 Agent 往往因一些普通原因而失败:原始目标逐渐脱离注意力范围,进度只存在于聊天记录中,验证被推迟,或者新会话无法重建此前发生的过程。StateM 将这类过程状态从模型上下文移至轻量级、版本化的 runbook 中。
prepare -> execute -> verify -> handoff
^ |
+-- repair-+
在每个状态中,Agent 都可以询问:
- 我现在应该做什么?
- 哪些转换是合法的?
- 在我转换前需要什么证据?
- 此前在这次运行中发生了什么?
答案存储在文件和运行时历史中,而不是依赖模型记住一切。
| 方案 | 记住阶段 | 阻止无效转换 | 支持修复循环 | 经受上下文刷新 | Agent 可编辑 |
|---|---|---|---|---|---|
| 仅提示词工作流 | 部分 | 否 | 非正式 | 否 | 是 |
| TODO 列表 | 部分 | 否 | 非正式 | 是 | 是 |
| CI 流水线 | 是 | 是 | 有限 | 是 | 通常否 |
| 通用工作流引擎 | 是 | 是 | 是 | 是 | 很少 |
| StateM | 是 | 是 | 是 | 是 | 是 |
StateM 有意比工作流引擎更小。它是一个具备状态感知能力的 runbook,Agent 可以通过命令行读取、编写、检查和修复它。
- 明确的阶段边界 —— 将模型规划、实现、审查、恢复和交接设为真实状态。
- 可执行的转换门槛 —— 在离开状态前使用清单、命令、谓词、人工审批和 LLM 审查。
- 动态检查 —— 允许 Agent 为当前状态条目注册任务专属检查,而无需修改共享 runbook。
- 持久化运行时历史 —— 持久保存当前节点、转换、hook 结果、证据、时间戳和 spec 身份。
- 上下文生命周期支持 —— 为长时间循环运行生成安全的恢复和压缩提示词。
- 零运行时依赖 —— 核心包仅要求 Python 3.11 或更高版本。
克隆仓库并以可编辑模式安装:
git clone https://github.com/henryqin1997/statem.git
cd statem
python3 -m pip install -e .
检查 CLI:
验证并启动随附的 coding-agent runbook:
statem validate examples/coding-agent.yaml
statem start examples/coding-agent.yaml --run-id demo
statem cur --run-id demo
statem next --run-id demo
仅当当前状态的检查通过时才进行转换:
statem goto plan --run-id demo
statem history --run-id demo
运行时数据默认存放在 .statem/ 中。若需要可跨一次性检出环境保留的、机器本地持久状态,请将其置于仓库外:
export STATEM_STATE_DIR="$HOME/.local/state/statem/my-project"
statem start examples/coding-agent.yaml --run-id demo
name: implementation-loop
initial: plan
nodes:
plan:
prompt: |
Read the task and write a concrete implementation plan.
before_transfer:
type: checklist
items:
- Scope and constraints are recorded
- Verification steps are defined
execute:
prompt: |
Implement the plan and keep the change scoped.
before_transfer:
- type: command
run: "python3 -m pytest -q"
- type: checklist
items:
- Relevant tests pass
- Unrelated files were not changed
handoff:
prompt: |
Summarize the change, verification, and remaining risks.
edges:
- from: plan
to: execute
condition: The plan is ready.
- from: execute
to: plan
condition: Verification found a fixable gap.
- from: execute
to: handoff
condition: The implementation and verification are complete.
将其保存为 runbook.yaml,然后运行:
statem validate runbook.yaml
statem start runbook.yaml --run-id my-run
statem cur --run-id my-run
StateM 将共享工作流定义与每次运行的执行状态分离:
| 层级 | 内容 | 提交到 git? |
|---|---|---|
| 静态 runbook | 节点、边、提示词、hook、门槛 | 是 |
| 运行时状态 | 当前节点、历史、结果、时间戳 | 否 |
| 动态检查 | 任务专属的当前条目验证 | 否 |
| 持久项目笔记 | 计划、决策、进度、产物 | 通常是 |
一次转换是一个事务:
- 解析请求的出站边。
- 运行当前节点的
before_transfer检查。 - 加载并运行当前条目的动态检查。
- 评估该边的
condition。 - 运行当前节点的
out_hook和该边的hook。 - 记录此次转换,创建新的目标条目,并运行目标节点的
in_hook。
如果一个阻塞性检查失败,Agent 将停留在当前状态,失败结果会被记录以供修复。
| 字段 | 用途 |
|---|---|
name |
人类可读的图名称 |
initial |
新运行启动时进入的节点 |
nodes |
已命名的状态定义 |
edges |
状态之间的有向转换 |
| 字段 | 运行时机 | 典型用途 |
|---|---|---|
prompt / pre_request |
节点处于活动状态时 | 状态本地指令 |
in_hook |
进入后 | 加载上下文或初始化证据 |
before_transfer |
离开前 | 对必要验证进行阻断 |
dynamic_before_transfer |
离开前 | 运行任务专属的当前条目检查 |
out_hook |
转换提交前 | 持久保存进度或交接说明 |
| 字段 | 用途 |
|---|---|
from / to |
源节点和目标节点 |
condition |
转换专属的阻塞门槛 |
hook |
退出门槛通过后执行的转换准备工作 |
max_attempts |
此边及源节点条目的可选正数重试上限 |
省略 max_attempts 会保留默认的无限重试行为。配置后,每次实际的 goto 都会消耗一次尝试;被阻塞的检查同样计入,而新的源节点条目会获得新的尝试额度。
| 类型 | 行为 |
|---|---|
message |
显示非阻塞性指导 |
manual |
请求明确确认 |
checklist |
确认一组完成条件 |
command |
运行 shell 命令并使用其退出码 |
predicate |
以声明式方式检查文件 |
llm_review |
将结构化审查委托给外部命令或模型 |
检查可配置 blocking、on_failure、timeout 和 cwd 等字段。应优先使用能检验任务承诺给最终使用者的相同接口的检查。
| 命令 | 用途 |
|---|---|
statem start SPEC |
创建或恢复一次运行 |
statem cur |
显示当前节点及其提示词 |
statem state |
显示完整图 |
statem ls NODE |
检查一个节点 |
statem next |
显示出站转换 |
statem goto TARGET |
尝试一次经过检查的转换 |
statem save |
持久保存状态并运行当前 out_hook |
statem history |
检查此前的转换和结果 |
statem prompt |
生成持久的清空后恢复提示词 |
statem compact-prompt |
生成安全的压缩提示词 |
statem validate SPEC |
验证图结构和引用 |
statem validate SPEC --strict |
同时拒绝未知或位置错误的 runbook 关键字 |
statem dynamic ... |
管理当前条目的动态检查 |
大多数命令接受 --run-id、--state-dir 和 --json,分别用于显式选择运行、隔离状态以及机器可读输出。
静态门槛覆盖编写 runbook 时已知的不变量。动态检查覆盖具体任务期间发现的验证需求——例如,恰好针对刚刚修复的 bug 的回归测试。
statem dynamic path --run-id demo
statem dynamic write checks.json --run-id demo --agent-id implementer
statem dynamic list --run-id demo --json
动态检查限定在当前节点条目的作用域内。StateM 会记录是谁注册了这些检查,并在允许转换提交之前运行它们。
长时间运行应将持久事实保存在项目文件中,并将模型上下文用于当前决策。StateM 通过以下方式支持这种分工:
- 使用
statem history获取持久的转换记录; - 使用
statem prompt在已清空的会话后恢复注意力; - 使用
statem compact-prompt在循环 runbook 内进行安全压缩; - 在其他循环应继续时使用显式恢复节点或会话刷新节点;
- 使用 spec 哈希和运行标识符检测或有意重新绑定已编辑的 runbook。
Runbook 应纳入版本控制,运行时状态则不应如此。使用默认本地状态目录时,请将 .statem/ 添加到 .gitignore。
持续至完成模式(可选)
StateM 无需宿主 hook 也能工作。若要让 Agent 在原本会结束其回合后继续推进,可将可选的 Stop hook 注册为持续至完成模式。当活跃运行仍位于一个具有出站转换的非终止节点时,该 hook 会返回一条继续提示词,要求 Agent 检查 StateM 并从持久状态继续。
- 使用
statem start正常启动运行。 - 对于 Codex,将
codex-stop-autoloop.hooks.json合并至.codex/hooks.json或~/.codex/hooks.json。 - 对于 Claude Code,将
claude-stop-autoloop.settings.json合并至项目或用户设置文件。 - 如果 hook 在此仓库外运行,请将其命令替换为
integrations/hooks/statem_stop_hook.py的绝对路径。当运行不使用默认.statem/目录时,也请设置STATEM_STATE_DIR。
该 hook 不会自行推进 StateM、绕过转换检查、运行 /clear 或运行 /compact。当不存在活跃运行、当前状态为终止状态,或图中没有出站转换时,它允许宿主停止。请参阅完整的设置与行为参考。
StateM 的核心保持宿主无关:任何能够运行 shell 命令的 Agent 都可以查询并推进一个 runbook。
如需了解有关证据回执、面向消费者的检查、自适应验证器计划、新鲜度、恢复及基准完整性的高级指导,请阅读验证指南。
随附论文将 StateM 作为 Terminal-Bench 2.1 上的执行框架进行评估。这些是系统层面的结果,并非关于新基础模型的主张:
| 配置 | 结果 | 运行条件 |
|---|---|---|
| GPT-5.5 xhigh + StateM | 92.1% | 89 个任务,445 次试验;88/89 五次试验覆盖 |
| GPT-5.6 Sol xhigh + 冻结版 StateM profile | 95.28% 原始分数 | 424/445 次公开提交试验;89/89 覆盖 |
| DeepSeek-V4-Flash + 适配版 StateM profile | 88.09% | 标准超时条件下的 392/445 |
| DeepSeek-V4-Flash + 适配版 StateM profile | 88.76% 描述性结果 | 395/445,以披露的延长超时试验替换一个任务 |
95.28% 的数值是裁决前的公开提交原始分数。DeepSeek 的描述性汇总结果与标准超时结果分开报告。实验协议、参考文献、成本和局限性请参见论文。
DeepSeek policy-v9 产物
结果产物涵盖 88 个任务,排除 gpt2-codegolf:记录了 392/440 次原始通过(89.09%)。上表采用论文中的标准 89 任务分母,即 392/445(88.09%)。这些大型产物托管为发布资源,在克隆或安装 StateM 时不会下载。
statem/ 核心状态机和 CLI
examples/ Runbook 和 hook 示例
integrations/ 宿主适配器
plugins/statem/ Codex skill 打包内容
tests/ 单元测试和集成测试
design.md 详细的运行时与 schema 设计
docs/verification-guide.md
高级验证模式
README 媒体由独立的 henryqin1997.github.io 仓库提供服务,因此克隆或安装 StateM 不会下载演示视频。
- 从
examples/coding-agent.yaml开始,并删除工作流不需要的任何状态。 - 当需要完整的运行时、转换、hook 和恢复语义时,阅读
design.md。 - 在关键边界处添加确定性的
before_transfer检查。 - 仅当具体任务揭示出共享 runbook 无法预先知晓的验证需求时,才使用动态检查。
- 将大型输出和持久决策保存在文件中;让活跃模型上下文聚焦于当前状态。
@misc{qin2026statemreaching953raw,
title = {StateM: Reaching 95.3\% Raw Accuracy, or a \$15 Frontier Run,
on Terminal-Bench 2.1 via Harness Scaling},
author = {Ziheng Qin and Yaxin Lu and Zhangyang Atlas Wang and Kai Wang},
year = {2026},
eprint = {2608.15089},
archivePrefix = {arXiv},
primaryClass = {cs.AI},
url = {https:
English / 原文
[2026-08-18]🤗 StateM ranked #1 on Hugging Face Daily Papers.
[2026-08-18]🔥 We released the DeepSeek-V4-Flash StateM runbook and reproducibility release, reaching 88.8% descriptive accuracy on Terminal-Bench 2.1 (395/445 trials; 88.76% unrounded). Everyone can try!
StateM turns an agent workflow into an inspectable graph of states, transitions, and executable checks. It keeps planning, execution, verification, repair, and handoff from collapsing into one long prompt.
Long agent runs often fail for ordinary reasons: the original goal fades from attention, progress lives only in chat history, verification is postponed, or a new session cannot reconstruct what happened. StateM moves that procedural state out of the model context and into a lightweight, versioned runbook.
prepare -> execute -> verify -> handoff
^ |
+-- repair-+
At every state, the agent can ask:
- What should I do now?
- Which transitions are legal?
- What evidence is required before I move?
- What happened earlier in this run?
The answer is stored in files and runtime history rather than relying on the model to remember everything.
| Approach | Remembers phase | Blocks invalid transitions | Supports repair loops | Survives context refresh | Agent-editable |
|---|---|---|---|---|---|
| Prompt-only workflow | Partial | No | Informal | No | Yes |
| TODO list | Partial | No | Informal | Yes | Yes |
| CI pipeline | Yes | Yes | Limited | Yes | Usually no |
| General workflow engine | Yes | Yes | Yes | Yes | Rarely |
| StateM | Yes | Yes | Yes | Yes | Yes |
StateM is deliberately smaller than a workflow engine. It is a state-aware runbook that an agent can read, author, inspect, and repair from the command line.
- Explicit phase boundaries — model planning, implementation, review, recovery, and handoff as real states.
- Executable transition gates — use checklists, commands, predicates, manual approval, and LLM review before leaving a state.
- Dynamic checks — let an agent register task-specific checks for the current state entry without mutating the shared runbook.
- Durable runtime history — persist the current node, transitions, hook results, evidence, timestamps, and spec identity.
- Context lifecycle support — generate safe resume and compaction prompts for long cyclic runs.
- Zero runtime dependencies — the core package requires only Python 3.11 or newer.
Clone the repository and install it in editable mode:
git clone https://github.com/henryqin1997/statem.git
cd statem
python3 -m pip install -e .
Check the CLI:
Validate and start the included coding-agent runbook:
statem validate examples/coding-agent.yaml
statem start examples/coding-agent.yaml --run-id demo
statem cur --run-id demo
statem next --run-id demo
Move only when the current state's checks pass:
statem goto plan --run-id demo
statem history --run-id demo
Runtime data defaults to .statem/. For durable machine-local state that survives disposable checkouts, place it outside the repository:
export STATEM_STATE_DIR="$HOME/.local/state/statem/my-project"
statem start examples/coding-agent.yaml --run-id demo
name: implementation-loop
initial: plan
nodes:
plan:
prompt: |
Read the task and write a concrete implementation plan.
before_transfer:
type: checklist
items:
- Scope and constraints are recorded
- Verification steps are defined
execute:
prompt: |
Implement the plan and keep the change scoped.
before_transfer:
- type: command
run: "python3 -m pytest -q"
- type: checklist
items:
- Relevant tests pass
- Unrelated files were not changed
handoff:
prompt: |
Summarize the change, verification, and remaining risks.
edges:
- from: plan
to: execute
condition: The plan is ready.
- from: execute
to: plan
condition: Verification found a fixable gap.
- from: execute
to: handoff
condition: The implementation and verification are complete.
Save it as runbook.yaml, then run:
statem validate runbook.yaml
statem start runbook.yaml --run-id my-run
statem cur --run-id my-run
StateM separates the shared workflow definition from per-run execution state:
| Layer | Contents | Commit to git? |
|---|---|---|
| Static runbook | Nodes, edges, prompts, hooks, gates | Yes |
| Runtime state | Current node, history, results, timestamps | No |
| Dynamic checks | Task-specific current-entry verification | No |
| Durable project notes | Plans, decisions, progress, artifacts | Usually yes |
A transition is a transaction:
- Resolve the requested outgoing edge.
- Run the current node's
before_transferchecks. - Load and run current-entry dynamic checks.
- Evaluate the edge's
condition. - Run the current node's
out_hookand the edgehook. - Record the transition, create a new target entry, and run the target node's
in_hook.
If a blocking check fails, the agent remains in the current state with the failure recorded for repair.
| Field | Purpose |
|---|---|
name |
Human-readable graph name |
initial |
Node entered when a new run starts |
nodes |
Named state definitions |
edges |
Directed transitions between states |
| Field | When it runs |
| --- | --- |
prompt / pre_request |
While the node is active |
in_hook |
After entering |
before_transfer |
Before leaving |
dynamic_before_transfer |
Before leaving |
out_hook |
Before the transition commits |
| Field | Purpose |
| --- | --- |
from / to |
Source and target nodes |
condition |
Transition-specific blocking gate |
hook |
Prepare-transfer work after exit gates pass |
max_attempts |
Optional positive retry ceiling for this edge and source-node entry |
Leaving out max_attempts preserves the default unbounded retry behavior. When configured, each real goto consumes one attempt; blocked checks count, and a fresh source-node entry receives a fresh budget.
| Type | Behavior |
|---|---|
message |
Display non-blocking guidance |
manual |
Ask for explicit confirmation |
checklist |
Confirm a set of completion conditions |
command |
Run a shell command and use its exit code |
predicate |
Inspect files declaratively |
llm_review |
Delegate a structured review to an external command/model |
Checks can be configured with fields such as blocking, on_failure, timeout, and cwd. Prefer checks that exercise the same interface the task promises to its eventual consumer.
| Command | Purpose |
|---|---|
statem start SPEC |
Create or resume a run |
statem cur |
Show the current node and its prompt |
statem state |
Show the full graph |
statem ls NODE |
Inspect one node |
statem next |
Show outgoing transitions |
statem goto TARGET |
Attempt a checked transition |
statem save |
Persist state and run the current out_hook |
statem history |
Inspect prior transitions and results |
statem prompt |
Generate a durable post-clear resume prompt |
statem compact-prompt |
Generate a safe compaction prompt |
statem validate SPEC |
Validate graph structure and references |
statem validate SPEC --strict |
Also reject unknown or misplaced runbook keywords |
statem dynamic ... |
Manage current-entry dynamic checks |
Most commands accept --run-id, --state-dir, and --json for explicit run selection, isolated state, and machine-readable output.
Static gates cover invariants known when the runbook is authored. Dynamic checks cover verification discovered during the concrete task—for example, a regression test for the exact bug just fixed.
statem dynamic path --run-id demo
statem dynamic write checks.json --run-id demo --agent-id implementer
statem dynamic list --run-id demo --json
Dynamic checks are scoped to the current node entry. StateM records who registered them and runs them before the transition is allowed to commit.
Long runs should keep durable facts in project files and use the model context for the current decision. StateM supports that split with:
statem historyfor the durable transition record;statem promptfor restoring attention after a cleared session;statem compact-promptfor safe compaction inside cyclic runbooks;- explicit recovery or session-refresh nodes when another loop should continue;
- spec hashes and run identifiers to detect or deliberately rebind edited runbooks.
Runbooks belong in version control. Runtime state does not. Add .statem/ to .gitignore when using the default local state directory.
Till-finish mode (optional)
StateM works without a host hook. To keep an agent moving after it would otherwise end its turn, register the optional Stop hook as a till-finish mode. When an active run is still on a non-terminal node with outgoing transitions, the hook returns a continuation prompt that tells the agent to inspect StateM and continue from the durable state.
- Start the run normally with
statem start. - For Codex, merge
codex-stop-autoloop.hooks.jsoninto.codex/hooks.jsonor~/.codex/hooks.json. - For Claude Code, merge
claude-stop-autoloop.settings.jsoninto a project or user settings file. - If the hook runs outside this repository, replace its command with the absolute path to
integrations/hooks/statem_stop_hook.py. SetSTATEM_STATE_DIRtoo when the run does not use the default.statem/directory.
The hook does not advance StateM by itself, bypass transition checks, run /clear, or run /compact. It allows the host to stop when no active run exists, the current state is terminal, or the graph has no outgoing transition. See the complete setup and behavior reference.
StateM's core remains host-agnostic: any agent that can run shell commands can query and advance a runbook.
For advanced guidance on evidence receipts, consumer-facing checks, adaptive verifier plans, freshness, recovery, and benchmark integrity, read the verification guide.
The accompanying paper evaluates StateM as an execution harness on Terminal-Bench 2.1. These are system-level results, not claims about a new base model:
| Configuration | Result | Operating condition |
|---|---|---|
| GPT-5.5 xhigh + StateM | 92.1% | 89 tasks, 445 trials; 88/89 five-trial coverage |
| GPT-5.6 Sol xhigh + frozen StateM profile | 95.28% raw | 424/445 public-submission trials; 89/89 coverage |
| DeepSeek-V4-Flash + adapted StateM profile | 88.09% | 392/445 under standard timeouts |
| DeepSeek-V4-Flash + adapted StateM profile | 88.76% descriptive | 395/445, replacing one task with disclosed extended-timeout trials |
The 95.28% value is the raw pre-adjudication public-submission score. The DeepSeek descriptive aggregate is reported separately from the standard-timeout result. See the paper for experimental protocol, references, costs, and limitations.
DeepSeek policy-v9 artifacts
The policy-v9 artifact release provides:
The result artifact covers 88 tasks and excludes gpt2-codegolf: it records 392/440 raw passes (89.09%). The table above uses the paper's standard 89-task denominator, 392/445 (88.09%). These large artifacts are hosted as release assets and are not downloaded when cloning or installing StateM.
statem/ Core state machine and CLI
examples/ Runbooks and hook examples
integrations/ Host adapters
plugins/statem/ Codex skill packaging
tests/ Unit and integration tests
design.md Detailed runtime and schema design
docs/verification-guide.md
Advanced verification patterns
README media is served from the separate henryqin1997.github.io repository, so cloning or installing StateM does not download the demo video.
- Start with
examples/coding-agent.yamland remove any states your workflow does not need. - Read
design.mdwhen you need the full runtime, transition, hook, and recovery semantics. - Add deterministic
before_transferchecks at consequential boundaries. - Use dynamic checks only when the concrete task reveals a verification need the shared runbook could not know in advance.
- Keep large outputs and durable decisions in files; keep the active model context focused on the current state.
@misc{qin2026statemreaching953raw,
title = {StateM: Reaching 95.3\% Raw Accuracy, or a \$15 Frontier Run,
on Terminal-Bench 2.1 via Harness Scaling},
author = {Ziheng Qin and Yaxin Lu and Zhangyang Atlas Wang and Kai Wang},
year = {2026},
eprint = {2608.15089},
archivePrefix = {arXiv},
primaryClass = {cs.AI},
url = {https://arxiv.org/abs/2608.15089}
}
We thank Zekai Li and Mengxuan Wu for discussions and feedback on this work.
StateM is released under the Apache License 2.0.