statewright:用状态机给 AI Agent 装护栏
Agents are suggestions, states are laws.
代理提出的是建议,状态制定的是法律。
statewright 的核心很硬:用有限状态机控制 AI agent 在每个阶段能用哪些工具、能跑哪些命令。工作流定义一次,可在 Claude Code、Codex、Cursor、opencode、Pi 上强制执行。完整文档:docs.statewright.ai。
这是典型的 状态机护栏(state machine guardrails):不靠更大模型和更长 prompt,而是把问题空间做小。
给模型 40+ 工具和一个开放式问题,它经常出不了门。常见「药方」是更大模型、更长提示——有时有效。可观测性只能事后告诉你哪里炸了,不能预防。
这在 Coding Agent 场景尤其刺眼:一次 bugfix 里混杂读代码、改文件、跑测试、甚至碰数据库。若全程开放同一工具面,模型容易陷入「再读一遍同一文件」或在错误阶段执行破坏性命令。
状态机在每个步骤约束工具空间和解空间,让模型在聚焦上下文里推理:
- planning:只读工具
- implementing:解锁编辑,shell 受限(即便允许 Bash,重定向写入与破坏性操作仍可被拦)
- testing:只允许指定测试命令
调用当前阶段不该有的工具 → 被拒,并提示现在可用什么、如何转移状态。
注意:这不是「在系统提示里写请先规划」——那仍是建议。这里是协议层或 hook 层的硬拦截,属于 工具调用 的执行面约束。
效果:前沿模型 token 更省;本地 13B+ 模型开始能完成原先做砸的任务。
在本地模型与 5-task SWE-bench 子集上,两个约 13.8GB / 19.9GB 的模型在 statewright 约束下可从 2/10 提到 10/10(同任务同硬件)。低于约 13GB 时,模型能发工具调用,但留不住足够文件内容做准编辑——那是能力地板,不是 statewright 独有。
结构性收益还包括:
- 打断「反复读同一文件 5 次却从不 edit」的死循环
- 工具集足够小,模型真在推理,而不是乱挥
详情见 Research brief。注意:子集实验,不是完整 2294-instance SWE-bench。对外引用时务必写清样本规模,避免把局部增益说成基准榜通杀。
对日常工程的启示更朴素:很多「agent 蠢」其实是工具面过大 + 阶段目标模糊。先把任务切成 planning / implementing / testing,再谈要不要上更大模型。
1/plugin marketplace add statewright/statewright2/plugin install statewright3/reload-plugins
浏览器打开 statewright.ai 注册并生成 key → 粘贴到 agent。然后:
1start the bugfix workflow — fix the failing tests in calc.py
或:/statewright start bugfix。
典型轨迹:planning → implementing → testing → completed,全程按状态解锁工具。若测试失败,状态机可回到 implementing,而不是在 completed 硬装成功——这正是「环与重试」比纯 DAG 更贴近 agent 工作的原因。
- 核心:Rust 引擎评估状态机定义(states、transitions、guards、tool restrictions)——确定性,环路里没有 LLM。
- 插件层:经 MCP / hooks 接入 coding agent;激活 workflow 后,按状态自动强制工具限制。
- 模型看到的可能是 5 个工具而不是 30 个,并得到当前阶段清晰指令。
主要护栏
| Guardrail | 作用 |
| --- | --- |
| Per-state tool enforcement | 不在 allowed_tools 的工具对 agent 不可见 / 不可用 |
| Bash discernment | 非写状态拦 >>、rm 等与脚本解释器 |
| Edit guards | max_edit_lines、每状态可改文件数上限 |
| Command allow-lists | 前缀匹配 allowed_commands |
| Conditional transitions | 守卫谓词(eq、gt、exists…) |
| Approval gates | requires_approval 高风险转移前等人 |
| Environment scoping | 按状态 blocked_env / env_overrides |
| Session isolation | 按 session 隔离状态 |
这些护栏可以叠加:例如 implementing 允许 Edit,但仍限制单次 diff 行数与可改文件数;testing 允许 Bash,但命令前缀必须是测试 runner。目标不是把 agent 关进死胡同,而是把错误动作的成本前移到协议层。
1{2"id": "bugfix",3"initial": "planning",4"states": {5"planning": {6"allowed_tools": ["Read", "Grep", "Glob"],7"max_iterations": 8,8"on": { "READY": "implementing" }9},10"implementing": {11"allowed_tools": ["Read", "Edit", "Write"],12"max_edit_lines": 20,13"max_files_per_state": 3,14"on": { "DONE": "testing" }15},16"testing": {17"allowed_tools": ["Read", "Bash"],18"allowed_commands": ["pytest", "cargo test", "npm test"],19"on": {20"PASS": { "target": "completed", "guard": "tests_passed" },21"FAIL_TEST": "implementing"22}23},24"completed": { "type": "final" }25},26"guards": {27"tests_passed": { "field": "test_result", "op": "eq", "value": "pass" }28}29}
状态机不是 DAG——可以环与重试,这才是 agent 工作的真实形状。也可指向 JSON schema 让 agent 生成 workflow,再在 visual editor 微调。
| Agent | 集成 | 强制程度 | | --- | --- | --- | | Claude Code | Hooks + MCP | Hard(协议层) | | Codex | Hooks | Hard(alpha) | | opencode | TypeScript plugin | Hard(alpha) | | Pi | Skills extension | Hard(alpha) | | Cursor | MCP + rules | Advisory(alpha) |
Hard = 工具调用在协议层拦截;Advisory = 注入规则但不硬拦。Cursor 架构下 MCP alone 无法完全 gate 工具调用。
个人开发者有 Free 档;Pro / Team / Enterprise 在 workflow 数、转移次数、run history 上分级(以官网为准)。引擎 crates/engine 为 Apache 2.0,可嵌入;全栈单人/单团队自托管见 FSL 条款。另有 patent pledge。
- 需要 agent 支持 MCP 或 hooks
- workflow 多半手写(虽可 agent 生成)
- 过严会卡住,escape hatch 是
statewright_deactivate - 研究数字来自小子集,解读要克制
Agent Harness 管「怎么跑」;statewright 把其中 权限与阶段 做成可复用、可跨 harness 的硬约束。它不替代记忆或上下文工程,但补了「模型想做的 ≠ 允许做的」这一刀。
和 Skills 的分工也清晰:Skills 告诉模型 怎么做某类任务;状态机告诉运行时 现在允许做什么。两者叠用时,planning 状态配只读 + 加载调查类 skill,implementing 再放开 edit,比「一锅端系统提示」干净得多。
- 先从 bugfix / review 这类短闭环 workflow 开始,别一上来建模整条发布流水线。
- 每个状态写清 allowed_tools 与 allowed_commands,用真实失败补守卫,而不是先堆复杂并行状态。
- 保留 deactivate 逃生口,并在团队规范里说明何时允许绕过。
- Cursor 等 advisory 集成上,不要假设「写了 rules 就等于硬拦」——关键路径仍要沙箱与 git 边界。
- 评测时对照「同模型无约束」,否则你无法判断收益来自状态机还是 prompt 运气。