SKILL.state:長任務 Agent 不再重播整段對話,改用顯式執行狀態
SKILL.state 用 structured state 取代長任務 Agent 的完整對話重播。本文拆解 runtime、Claude/Codex/Pi 接法與導入判斷。
作者
Seer
日期
2026-08-31
LLM Agent 跑十幾步時,對話歷史通常還能撐住;跑到上百步,runtime 本身就會變成問題。每次呼叫模型都重送先前的 observation、reasoning、tool output 與 action,prompt 越來越長,舊資訊也會一直留在上下文裡。模型不只要決定下一步,還得先從整段歷史重建「現在到底是什麼狀態」。
SKILL.state: Scalable Long-Horizon Agent Skills 提出的做法很直接:把 append-only conversation 換成一份可修改、可驗證的 structured execution state。每一步只送三樣東西給模型:固定的 skill specification、目前 state、最新 observation。模型當步產生的 reasoning 用完即丟,只留下經 runtime 驗證過的 state patch。[1][2]
研究範圍聚焦 Agent runtime architecture,模型本身與長期記憶資料庫維持不變。它要處理的是長任務執行時,agent 如何保留「未來還需要的事實」,同時不讓整段執行歷史持續膨脹。
論文版本:arXiv:2608.26263v1,2026-08-26 提交;作者為 Sanket Badhe、Priyanka Tiwari、Jonghyun Chung。arXiv 註記為 accepted at EMNLP,授權為 CC BY 4.0。[1][3]
先看重點
- SKILL.state 每一步只輸入
P、Σ_t、O_t:固定程序、目前狀態、最新觀察。 - 模型仍可在單一步驟內做多步 reasoning,但 reasoning 不會進入下一輪 prompt。
- 模型輸出
state_patch與action;runtime 負責 schema validation、merge、rollback/retry。 null代表刪除 state key,patch 需要用 merge semantics,不能把整份 state 覆蓋掉。- 在論文的 Warehouse 測試中,
T=100時 SKILL.state 使用 65,408 tokens,Stateful baseline 使用 1,062,387,約為 16.2 倍。[2] - 在 InterCode CTF,SKILL.state pass@1 為 54.2%,同時比 ReAct 少約 60.4% total tokens。[2]
- 這套方法成立的前提,是 structured state 足以代表未來決策需要的資訊。
- 論文沒有釋出 SKILL.state/SkillExecBench 官方程式碼或完整 schema;目前能做的是依論文 prompt、演算法與附錄自行實作,不能宣稱完整重現作者實驗。
問題出在 Agent 一直把歷史當成狀態
常見 Agent runtime 會把每輪內容接在同一段 conversation 後面:
- 使用者或環境送出 observation。
- 模型推理並選 action。
- tool 執行 action,回傳 output。
- 下一輪把前面所有內容再送一次。
這種設計的好處是簡單。模型看得到完整軌跡,不需要 runtime 先定義 state。缺點也很明確:第 t 步的 prompt 包含前 t-1 步內容,單步 prompt 隨時間成長,累積 token 會接近 O(T²)。[2]
更麻煩的是資訊語意。舊 observation 可能已經失效,早期推理也可能只是當時的假設。它們留在 prompt 裡後,模型每一輪都要重新判斷:
- 哪個狀態還有效?
- 哪個 tool output 已被後續動作推翻?
- 哪個 hypothesis 已經測過?
- 哪段文字只是過去的 reasoning,不是目前事實?
摘要、retrieval memory、sliding window 能縮小 prompt,但它們仍以「壓縮或挑選歷史」為主。論文的切入點是:如果 Agent 下一步真正需要的是 current state,那就直接把 current state 設成 runtime 的 canonical representation,不要每次從歷史文字重建。[2]
SKILL.state 的核心資料流
論文把每一步輸入寫成:
A_t = (P, Σ_t, O_t)
P:immutable procedural specification,也就是 skill instructions、可用 action、環境規則與角色限制。Σ_t:第t步的 structured execution state。O_t:環境最新回傳的 observation。
模型產生:
(R_t, ΔΣ_t, a_t)
R_t:當步 reasoning。ΔΣ_t:JSON state patch,只描述要新增、修改或刪除的 state。a_t:下一個 action。
runtime 驗證 patch 後更新:
Σ_(t+1) = Σ_t ⊕ ΔΣ_t
⊕ 是 dictionary merge;patch 中的 null 表示刪除。完成 transition 後,R_t 永久丟棄,下一輪也不會收到先前的 observation 或 action。[2]
flowchart TB
A["Agent Adapter<br/>Claude: Hooks / one-shot<br/>Codex: Hooks / exec JSONL<br/>Pi: Extension events"]
U["User Intent Contract<br/>goal · constraints · approval"]
X["External Authority<br/>Git · Issue / ADR · CI · Runtime"]
subgraph Paper["論文核心:Bounded State Loop"]
R["Authority Refresh + Bounded Prompt<br/>P + Intent + Σt + Ot"]
C["Candidate Transition<br/>state_patch + action"]
end
subgraph Production["本文建議:Production Governance"]
G["Governance Gate<br/>schema · intent · freshness · sensitivity"]
Q["User Confirmation / Corrective Observation"]
T["Tool Execution"]
V["Evidence Validation<br/>Git · CI · Runtime"]
S["CAS Commit<br/>task state + audit pointer"]
end
A --> R
U --> R
X --> R
R --> C --> G
G -->|"intent change / stale / reject"| Q
Q --> R
G -->|"allow"| T --> V
V -->|"failure"| Q
V -->|"success"| S
S --> R
這張 Mermaid 把兩個範圍分開:Bounded State Loop 是論文核心;使用者確認、freshness、sensitivity、CAS commit 與外部 authority reconciliation 是本文建議的 production governance。完整版本、決策與執行證據留在 Git、Issue/ADR、CI、runtime telemetry 與 audit log;Prompt 只帶目前需要的 bounded snapshot。
這裡的重點不是「禁止模型思考」。模型在單次 generation 裡仍可做完整 reasoning;runtime 只是不把 reasoning 當成跨步記憶。需要跨步保留的內容,必須投影進 Σ_t。
Schema 才是這套方法真正的工程成本
SKILL.state 把執行可靠性從 history reconstruction 移到 schema authoring。論文主張 schema 以 domain 為單位定義,不需要每個 task 重寫。
論文的 sufficient-statistic assumption 沒有自動保證 schema 真的代表使用者在意的事。只讓模型自己摘要,仍可能把使用者的優先順序、禁止事項、版本決策與驗收條件壓掉。 Production runtime 必須把「誰定義重要性」做成獨立治理層。
怎麼確保 Schema 保存的是使用者真正關心的事
第一步不是請模型自由歸納,而是先建立一份 使用者意圖契約(User Intent Contract)。這份契約由使用者需求、任務規格與明確確認組成,模型只能引用或提出修改,不能自行刪除。
至少要包含:
goal:最後要完成什麼。success_criteria:什麼結果才算完成,要如何驗證。locked_requirements:使用者要求原文保留、不可降級或不可省略的內容。constraints:技術、安全、成本、時間與範圍限制。non_goals:這一輪明確不做什麼,避免 Agent 自行擴張範圍。approval_boundaries:發布、付款、刪除、部署、merge 等哪些動作要先取得許可。must_remember:即使壓縮 working state 也不能消失的資訊。priority_order:衝突時要先保哪個目標。
每個重要 state field 還要帶治理 metadata:
{
"value": "使用者要求所有版本迭代都要可回溯",
"source": "user-message:2026-08-31T...",
"owner": "user",
"retention": "task-lifetime",
"freshness": "until-superseded",
"change_policy": "explicit-user-approval",
"supersedes": null
}
這會帶來六條硬規則:
- User-owned 欄位不可由一般 state patch 刪除。 模型只能提出
change_request。 - 重要資訊不能只剩摘要。 要保留 source pointer,能回到原始訊息、Issue、commit 或文件。
- 不確定就標記 unknown。 不可因模型判斷「可能不重要」而直接遺失。
- 衝突要進 confirmation queue。 新 observation 與 locked requirement 衝突時,由使用者或 policy resolver 決定。
- Schema 本身要 versioned。 每次增刪欄位都要有 migration、相容性與回滾方式。
- 定期做反向檢查。 從 success criteria 回查目前 state:完成判斷所需的證據是否都還找得到。
所以保留策略應分成不同層,而不是把所有資料塞進同一份 JSON:
| 資料層 | 保存內容 | 是否進每輪 Prompt | 權威來源 |
|---|---|---|---|
| 使用者意圖契約 | 目標、驗收、限制、approval、must-remember | 是,精簡且受保護 | 使用者/任務規格 |
| Working State | 目前步驟、事實、未解問題、下一個 action | 是,bounded | Runtime state store |
| Source Authority | branch、HEAD、diff、tag、正式文件 | 只放 pointer/current snapshot | Git/文件系統 |
| Runtime Evidence | test、CI、telemetry、deployment state | 只放摘要與最新證據 pointer | CI/GSC/監控/部署平台 |
| Decision/Audit History | 為何修改、誰批准、tool calls、失敗紀錄 | 預設不進 | Issue/ADR/audit log |
| Ephemeral Reasoning | 當步推理過程 | 否,用完丟棄 | 不作持久權威 |
程式開發的版本迭代要記,但由 Git 與證據系統保存完整歷史
程式開發不能只留下「目前正在改登入功能」這種摘要。版本迭代至少有兩種權威,handoff 時要分開:
Source Authority
- repository path/remote
- current branch
- base commit、HEAD commit
- staged/unstaged diff 與 working-tree digest
- release tag/package version
- schema migration/database migration version
- 目前哪些檔案是這輪 change scope
Runtime Evidence
- 已執行哪些 tests,完整 command 與結果
- CI run/artifact/coverage
- 實際 runtime telemetry、logs、GSC 或 production state
- deployment environment、deployed revision
- 尚未執行的 production authority
- rollback target 與最後已知穩定版本
完整 commit history、diff、CI log 與 deployment event 留在外部 source of truth。Σ_t 保存的是目前權威快照與可解引用 pointer,例如:
{
"user_intent": {
"goal": "完成登入重構並保持既有 session 相容",
"success_criteria": [
"unit and integration tests pass",
"staging can resume existing sessions"
],
"locked_requirements": [
"不可變更公開 API response shape"
],
"approval_boundaries": ["production deploy"]
},
"source_authority": {
"repo": "/workspace/app",
"branch": "feature/auth-refactor",
"base_commit": "abc1234",
"head_commit": "def5678",
"working_tree": "dirty",
"diff_ref": "git-diff:sha256:..."
},
"current_change": {
"issue": "AUTH-42",
"plan_step": 3,
"files_touched": ["src/auth.ts", "tests/auth.test.ts"],
"open_questions": ["舊 refresh token 是否要一次性遷移"]
},
"runtime_evidence": {
"tests": [{"command": "npm test", "status": "passed", "artifact": "ci://run/812"}],
"staging_revision": "def5678",
"production_revision": "abc1234",
"production_authority_executed": false
},
"decision_ledger": [
{
"decision": "保留舊 session cookie 名稱",
"source": "issue://AUTH-42#comment-18",
"approved_by": "user",
"supersedes": null
}
],
"next_action": "驗證 staging 舊 session 恢復"
}
這份 state 不複製完整 Git history,但不會遺失版本迭代。Agent 每輪開始前要向 Git、Issue、CI 與 runtime 平台做 reconciliation:
- 讀取目前 branch/HEAD/diff 與 deployed revision。
- 比對
Σ_t裡的 pointer 是否仍新鮮。 - 發現外部變更時,先更新 authority snapshot,再規劃下一步。
- patch 若改動 locked requirement 或 approval boundary,停止執行並詢問使用者。
- action 完成後,把完整 evidence 寫回 Git/CI/audit,再更新 state pointer。
這樣「丟棄對話歷史」只代表不把整段歷史重送給模型,不代表刪除程式版本、使用者決策或執行證據。
套用到 Claude Code、Codex CLI 與 Pi
三個工具都能接入這套方法,但不能只把同一份 prompt 貼進去。SKILL.state 要成立,runtime 必須掌握四個邊界:
- 每輪只注入 Skill Spec、User Intent Contract、目前 state 與最新 observation。
- 模型輸出的
state_patch只能先成為 candidate。 - action 通過 policy gate 後才執行。
- tool result 經環境驗證後,才以 compare-and-swap commit state。
三者應共用同一份 adapter contract,而不是各自發明不同的 schema:
UserInput
→ AuthorityRefresh(Git / Issue / CI / runtime)
→ PromptRender(P + intent + Σt + Ot)
→ AgentStep
→ StatePatchCandidate + Action
→ Schema / Intent / Action Gate
→ ToolExecution
→ EvidenceValidation
→ StateCommit(expected_version)
建議把資料拆成兩個目錄:
.agent-contract/ # 可進 Git;不可放敏感資訊
skill-spec.md
intent-contract.json
state.schema.json
transition.schema.json
.agent-runtime/ # gitignored 或外部 SQLite/KV
state.json
audit.jsonl
observations/
state.lock
AGENTS.md、CLAUDE.md 適合放穩定指令,不適合每輪覆寫 mutable state。完整 tool output 進 audit/artifact store;state.json 只留有上限的決策快照與外部 pointer。
| 工具 | 最接近完整 SKILL.state 的作法 | Native 接點 | 主要限制 |
|---|---|---|---|
| Claude Code | one-shot claude -p+外部 controller | Hooks、structured output | 互動 session 仍保留 transcript |
| Codex CLI | codex exec --ephemeral --json --output-schema+外部 controller | Hooks、JSONL events、App Server/SDK | output schema 只約束 final response |
| Pi | project extension+自訂 transition tool | before_agent_start、context、tool_call、tool_result | 平行 tool call 需要 single-writer/CAS |
Claude Code:one-shot controller 最接近論文語意
Claude Code 的 Hooks 已提供 SessionStart、UserPromptSubmit、PreToolUse、PostToolUse、PostToolUseFailure、PreCompact、Stop 與 SessionEnd 等事件。Hook 可以補 additionalContext、拒絕 tool call,或在 tool 完成後收集 evidence。[7]
如果要忠實實作 SKILL.state,不建議直接讓一個互動 session 無限跑。互動 session 仍會保存 transcript,hooks 只增加治理,沒有自動把每輪輸入縮成 P + Σ_t + O_t。較完整的方式是外部 controller 每一步啟動一次不保存 session 的 structured-output call:[8]
TRANSITION_SCHEMA="$(jq -c . .agent-contract/transition.schema.json)"
STEP_PROMPT="$(python3 runtime/render_step.py)"
claude -p "$STEP_PROMPT" \
--no-session-persistence \
--tools "" \
--output-format json \
--json-schema "$TRANSITION_SCHEMA"
這一輪 Claude 只產生 state_patch 與 action,不直接執行 shell。Controller 接著:
- 驗證 transition schema、protected fields、state version 與 action policy。
- 由 controller 的 tool adapter 執行 action。
- 讀 Git/CI/runtime 驗證結果。
- 成功才 commit candidate state;失敗則寫 corrective observation。
- 下一輪重新啟動 one-shot call,不 resume 舊 session。
需要保留 Claude Code 原生工具體驗時,可以改成 hooks+MCP state service:
| Claude Hook | SKILL.state 工作 |
|---|---|
SessionStart | 載入 task state、schema version、authority pointer |
UserPromptSubmit | 刷新 Git/Issue/CI,注入 bounded state |
PreToolUse | 檢查 protected path、approval boundary、stale state version;必要時 deny |
PostToolUse | 正規化 tool result,寫 audit,產生 candidate observation |
PostToolUseFailure | 記錄失敗,不 commit optimistic patch |
Stop | 驗證最後一筆 tool result 已 reconciliation,否則阻止 task 被標成完成 |
SessionEnd | 封存或清除 session-scoped state |
MCP service 可提供 state_read、state_transition、state_confirm、authority_refresh 四個工具。CLAUDE.md 只寫「何時必須呼叫這些工具」與穩定 invariant;mutable state 留在 MCP service/external store。這是容易落地的 retrofit,但 session 內歷史仍可能進 context;若目標是論文所說的固定 footprint,仍要使用 one-shot controller 或自己掌握 agent loop。
Codex CLI:用 JSONL event stream 做 controller 邊界
Codex 的 non-interactive mode 可以用 --json 輸出 JSONL events,包含 thread/turn lifecycle、command execution、file change、MCP tool call 與完成狀態;--output-schema 則約束 final response。最保守的模式同樣讓 Codex 只提議 transition:[10]
STEP_PROMPT="$(python3 runtime/render_step.py)"
codex exec \
--ephemeral \
--json \
--output-schema .agent-contract/transition.schema.json \
--cd "$REPO" \
--sandbox read-only \
"$STEP_PROMPT" > .agent-runtime/events.jsonl
Controller 從 JSONL 取得 thread.started、turn.、item. 與 error,最後解析符合 schema 的 response,再執行 action。--ephemeral 只是不把 session 存到磁碟;真正的 bounded context 仍取決於每個 transition 都開新 invocation,且不要把上一輪 JSONL 全部塞回下一輪。
Codex 現在也有官方 Hooks,包括 SessionStart、UserPromptSubmit、PreToolUse、PermissionRequest、PostToolUse、PreCompact、Stop 與 SessionEnd。對應方式與 Claude 類似:[9]
SessionStart/UserPromptSubmit:authority refresh,透過additionalContext注入 bounded snapshot。PreToolUse:檢查 state version、approval boundary;可回permissionDecision: "deny"。PostToolUse:保留 tool evidence,更新 latest observation,不直接把整段 output 寫入 state。Stop:確認 tests/CI/Git status 與 state 已對帳。SessionEnd:關閉 lock,封存 task state。
如果把 sandbox 改成 workspace-write,Codex 可能在 final structured response 前已經執行多個 tool。這時 --output-schema 只能保證最後輸出形狀,不能提供 transaction isolation。需要每一步都先驗 patch 再執行時,應使用 hooks+MCP transition service,或由 Codex App Server/SDK 的外部 orchestrator 管理 tool execution。
AGENTS.md 只放穩定流程、repo invariant 與 state tool 使用規則。Branch、HEAD、CI run、暫時 workaround 留在 task state/外部權威,避免每次迭代都改寫 AGENTS.md。[11]
Pi:用 extension API 直接改造 agent loop
Pi 的 extension API 更接近這篇論文需要的 runtime seam:[12][13]
before_agent_start可注入 state 或改 system prompt。context在每次 LLM call 前可重建 messages。tool_call可修改參數或 block。tool_result可正規化 result、標記 error。pi.appendEntry()可把 extension state 寫入 session JSONL,且 custom entry 不會自動進 LLM context。
Project extension 可放在 .pi/extensions/skill-state.ts。概念骨架如下:
import { Type } from "typebox";
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
export default function (pi: ExtensionAPI) {
let state = loadState();
let latestObservation = "task started";
pi.on("session_start", async (_event, ctx) => {
state = restoreLatestState(ctx.sessionManager.getBranch()) ?? state;
state = await refreshAuthorities(state);
});
pi.on("before_agent_start", async () => ({
message: {
customType: "skill-state-current",
content: renderBoundedSnapshot(state, latestObservation),
display: false,
},
}));
pi.on("context", async (event) => ({
messages: buildBoundedContext(event.messages, state, latestObservation),
}));
pi.on("tool_call", async (event) => {
const denied = validateToolPolicy(event.toolName, event.input, state);
if (denied) return { block: true, reason: denied };
});
pi.registerTool({
name: "skill_state_transition",
label: "Validate and commit one state transition",
parameters: Type.Object({
expectedVersion: Type.Integer(),
statePatch: Type.Record(Type.String(), Type.Unknown()),
action: Type.Object({
type: Type.String(),
args: Type.Record(Type.String(), Type.Unknown()),
}),
}),
async execute(_id, params, signal, _onUpdate, ctx) {
const candidate = validateAndMerge(state, params);
const observation = await executeDomainAction(params.action, signal, ctx);
state = await reconcileAndCommit(candidate, observation);
latestObservation = observation.summary;
pi.appendEntry("skill-state", { version: state.version, state });
return {
content: [{ type: "text", text: latestObservation }],
details: { stateVersion: state.version },
terminate: true,
};
},
});
}
context handler是關鍵:它可讓 LLM 只看到目前 state 與最新 observation,完整 session history 仍保留在 Pi 的 JSONL,供 audit、branch、resume 使用。這比把 history 真正刪掉安全,也最符合論文「不再把完整 trajectory 當推理輸入」的意思。
Pi 預設可以平行執行同一 assistant message 中的 tool calls,因此 state transition 要強制 single writer:每個 patch 帶 expectedVersion,寫入時做 CAS;檔案更新則用同一把 state lock 或 withFileMutationQueue()。未通過 project trust 時,project-local extension 不應被自動載入;non-interactive run 也要明確處理 trust policy。[13]
三者共同的 commit 規則
不論 adapter 用哪一套 API,都應遵守同一條 transaction boundary:
模型提議 patch/action
→ schema + intent + freshness gate
→ stage candidate state
→ 執行 action
→ 驗證 Git/CI/runtime evidence
→ CAS commit 或產生 corrective observation
只掛 hooks、每輪把 state 附加到既有 session,能得到治理與 observability,但不必然得到論文的固定 prompt footprint。要重現 SKILL.state 的核心效益,controller 還必須限制每輪實際送入模型的 messages,並把完整歷史留在模型 context 之外。
是否值得引入:值得做有限導入,不適合直接取代所有 Agent Memory
對 Claude Code、Codex CLI、Pi 這類 coding agent,這套架構值得用一個真實長任務流程試點。原因很直接:程式開發已有 Git、Issue、CI、filesystem 與 deployment platform 當外部權威,state 可以保存 pointer 與未完成事項,不必自己冒充版本資料庫。
目前不適合直接升成所有任務的全域 memory layer。論文提供的 benchmark 訊號很強,但截至本文查核時沒有官方 SKILL.state/SkillExecBench code;使用者意圖治理、transaction semantics、multi-agent conflict 也不是論文已解決的部分。正確決策是先做 task-scoped pilot,再用實際指標決定是否擴大。
適合導入的訊號
| 工作特徵 | 為什麼有價值 |
|---|---|
| 任務跨很多 tool steps、session 或 agent handoff | 降低重送完整 history 與重新理解成本 |
| 下一步主要由明確 state 決定 | Schema 能保存 branch、issue、驗證與 pending work |
| Git/CI/API 能驗證 action 結果 | Candidate patch 可以等 evidence 通過後再 commit |
| 常重跑已失敗命令或忘記先前假設 | tested_hypotheses/evidence pointer 可阻止重複工作 |
| 外部 actor 會改 repo、issue 或 deployment | Authority refresh 能處理 stale state |
| 需要可稽核 handoff | Task state 與完整 audit 分離,接手者先看 canonical snapshot |
不適合導入的訊號
| 工作特徵 | 主要問題 |
|---|---|
| 一兩輪就完成的問答、簡短修改 | Adapter、schema、state store 的成本高於收益 |
| 創作、研究探索,早期素材的價值要到後面才知道 | 固定 schema 容易過早丟掉內容 |
| 成功條件與 state structure 持續改變 | Schema migration 會變成主要工作 |
| Tool result 無法驗證,也沒有 source of truth | Runtime 無法判斷何時能 commit |
| 使用者真正要的是完整過程與脈絡 | 原始 trajectory 本身就是產出,不能只留 state |
真正要付的成本
- 替每個 domain 設計 schema、invariant、migration 與 size bound。
- 維護 Claude/Codex/Pi adapter,處理版本與 hook 行為差異。
- 建 state store、lock、CAS、audit 與 authority refresh。
- 處理 optimistic patch、tool failure、timeout、retry 與 partial success。
- 建 protected intent、confirmation queue、sensitivity 與 retention policy。
這些成本說明了導入邊界:它適合「長、可建模、可驗證」的 workflow;短任務與開放式探索繼續使用原本的 Agent session。
建議 rollout
- Sidecar state:先不裁剪 context、不接管 tool。每輪只做 authority refresh、task snapshot、structured handoff 與 audit pointer,確認 schema 是否能描述真實工作。
- Gated transition:挑一個長任務流程,加入 candidate patch、protected fields、PreToolUse policy、tool 後 evidence validation 與 staged commit。完整 history 仍保留,方便對照。
- Bounded-turn runtime:只有前兩階段證明 state 足夠,才改成 Claude/Codex one-shot controller,或 Pi
contexthandler,把每輪模型輸入限制為P + Intent + Σ_t + O_t。
第一階段應先做平台無關的 state service,再接 Claude、Codex、Pi adapter。直接維護三套各自的 state logic,schema 與 commit semantics 很快會分岔。
Pilot 要量什麼
| 指標 | 想確認的問題 |
|---|---|
| 每步 prompt tokens、state bytes | Context 是否真的維持 bounded |
| Task success、人工修正次數 | 壓縮後有沒有犧牲正確性 |
| 重複 command/重複 hypothesis | Structured state 是否減少重工 |
| Validator reject/retry | Schema 與模型輸出是否穩定 |
| Stale pointer/CAS conflict | 外部 drift 與並行寫入是否可控 |
| Recovery steps | Git/CI/runtime 被外部修改後多久能恢復 |
| Protected-intent violation | 使用者要求是否被模型覆寫;此項應作 hard gate |
| 延遲與維護工時 | 節省的 token 是否被 runtime 複雜度抵消 |
擴大導入的條件是:prompt 與重複工作下降,task success 不退步,外部 drift 能更快恢復,且沒有 protected-intent violation。若 schema migration、人工 reconciliation、validator retry 或 adapter 維護成為主要成本,就停在 sidecar/handoff 層,不必追求完整 bounded-turn runtime。
對三個工具的實際判斷
- Claude Code:先做 hooks+MCP state service;有明確 token 或 context drift 問題後,再把特定 workflow 改成 one-shot controller。
- Codex CLI:最適合先用
exec --json做 observer/controller,保留 JSONL evidence;高風險 action 維持 read-only proposal+外部執行。 - Pi:extension API 能直接控制
context與 tool lifecycle,最適合做完整 prototype;但要先處理 project trust、平行 tool 與 state migration。
總結判斷:值得引入 task-scoped execution state,不值得把它包成全域永久記憶。 先選一個會跨 session、會跑大量工具、又能由 Git/CI 驗證的 coding workflow。試點成功後,再決定是否擴到其他 Agent 或 domain。
論文中的 Domain Schema 範例
例如 InterCode CTF 的 100 個 challenge 共用五個 state 欄位:[2][4]
discovered_flagstested_hypothesesactive_filesworking_dircmd_summary
這五個欄位對應 CTF Agent 下一步真正要知道的資訊:找到了什麼、哪些路線已失敗、目前有哪些檔案、工作目錄在哪、先前命令做了什麼。完整 terminal transcript 不再是 primary reasoning substrate。
但論文只列出欄位名,沒有公開完整 JSON Schema 與型別。下面這份是依論文概念整理的實作建議,不是作者官方 schema:
{
"discovered_flags": ["flag{...}"],
"tested_hypotheses": [
{
"hypothesis": "binary contains a hard-coded key",
"result": "rejected",
"evidence": "strings and objdump found no candidate"
}
],
"active_files": ["/tmp/challenge/app", "/tmp/challenge/output.txt"],
"working_dir": "/tmp/challenge",
"cmd_summary": [
"file app: ELF 64-bit executable",
"strings app: no obvious flag"
]
}
設計 schema 時要控制四件事:
- 只保留會改變未來 action 的資訊:純敘事、重複 tool output 不應進 state。
- 值要有上限:schema 欄位固定,不代表 list 與字串不會無限長。要設定長度、筆數與淘汰規則。
- 區分事實與假設:
tested_hypotheses應帶結果與 evidence,避免把猜測升格成世界狀態。 - 寫出 invariant:例如一個 warehouse shelf 最多一個 item、
working_dir必須存在、同一 action 不可同時標示成功與失敗。
論文實際怎麼要求模型回傳
附錄中的 SKILL.state prompt 會放入:[2]
skill.instructions- compact JSON state
- latest observation
- 回傳格式約束
模型必須輸出當步 reasoning,接著給一個 fenced JSON block,而且頂層只能有兩個 key:
{
"state_patch": {
"key": "new value",
"deleted_key": null
},
"action": "exact command to execute"
}
這個格式把「思考內容」與「runtime 可執行輸出」分開。真正寫進 persistent state 的只有 state_patch;真正送給 tool adapter 的只有 action。
Warehouse 範例更清楚。最新 observation 是:
Customer ordered item_12.
目前 state 顯示 item_12 在 shelf_42。模型推理後回傳:
{
"state_patch": {
"inventory": {
"shelf_42": null
}
},
"action": "Ship item_12 shelf_42"
}
下一筆 observation 才是:
Success: Shipped item_12 from shelf_42.
這個例子也揭露一個 production 設計問題:論文 pseudo-code 是先 merge patch,再 execute action。也就是 item 在 tool 確認 Ship 成功前,已先從 state 移除。
正式 runtime 至少要選一種語意:
- Optimistic state update:照論文先更新;action 失敗時,依新 observation 產生 corrective patch。
- Staged commit:先驗證出 candidate state,tool 成功才 commit;失敗則丟棄 candidate 或進 reconciliation。
- 雙狀態:
desired_state與confirmed_state分開,適合遠端 API、交易或非同步 workflow。
論文實驗使用第一種。涉及付款、刪除、部署、權限或其他高風險 action 時,第二或第三種會更安全。
Runtime 要怎麼實作
論文的 Algorithm 1 可以整理成以下流程:
for t = 0 ... T:
接收最新 observation
用 skill spec、current state、observation 組 prompt
LLM 產生 reasoning、state patch、action
驗證 state patch
合併 state patch
執行 action
真正落地時,validator 不能只檢查 JSON 能否 parse。至少要有六層:
- Envelope validation:頂層只能有
state_patch與action。 - Schema validation:欄位、型別、enum、長度與 nesting depth 符合定義。
- Patch validation:只能修改 allowlist path;
nulldeletion 不得移除必要欄位。 - Intent-policy validation:一般 patch 不得修改 user-owned/locked fields;變更改送 confirmation queue。
- Invariant validation:patch 套用後的 candidate state 仍符合 domain 規則。
- Action policy validation:action grammar、權限、參數與目前 state 相容。
下面是一個從論文推導的 Python implementation sketch。它不是作者程式碼,重點是呈現 runtime boundary:
from copy import deepcopy
from dataclasses import dataclass
from typing import Any, Callable
@dataclass
class ModelStep:
state_patch: dict[str, Any]
action: str
def deep_merge_with_null_delete(
current: dict[str, Any],
patch: dict[str, Any],
) -> dict[str, Any]:
result = deepcopy(current)
for key, value in patch.items():
if value is None:
result.pop(key, None)
continue
if isinstance(value, dict) and isinstance(result.get(key), dict):
result[key] = deep_merge_with_null_delete(result[key], value)
else:
result[key] = deepcopy(value)
return result
def run_skill_state(
skill_spec: str,
initial_state: dict[str, Any],
first_observation: str,
call_llm: Callable[[str], str],
parse_model_step: Callable[[str], ModelStep],
reconcile_authorities: Callable[[dict[str, Any]], dict[str, Any]],
validate_intent_change: Callable[[dict[str, Any], dict[str, Any]], None],
validate_state: Callable[[dict[str, Any]], None],
validate_action: Callable[[str, dict[str, Any]], None],
execute_action: Callable[[str], str],
max_steps: int = 100,
):
state = deepcopy(initial_state)
observation = first_observation
for _ in range(max_steps):
# Git/CI/runtime authority 才是完整版本與證據的來源。
state = reconcile_authorities(state)
prompt = build_prompt(
skill_spec=skill_spec,
state=state,
observation=observation,
)
raw = call_llm(prompt)
step = parse_model_step(raw)
candidate = deep_merge_with_null_delete(
current=state,
patch=step.state_patch,
)
validate_intent_change(state, candidate)
validate_state(candidate)
validate_action(step.action, state)
# 論文語意:先 commit patch,再執行 action
state = candidate
observation = execute_action(step.action)
write_audit_log(
state=state,
action=step.action,
observation=observation,
)
return state
parse_model_step() 若失敗,persistent state 不能被修改。論文描述的行為是 rollback-retry;實作時還要補:
- retry 上限
- retry prompt 是否回傳 validation error
- 相同錯誤連續發生時的 fallback
- malformed JSON 的 constrained decoding/grammar
- action timeout、idempotency key 與重試語意
- state snapshot/checkpoint
- audit log retention
最重要的一條是:audit log 可以保留,但不要自動塞回模型 prompt。 安全稽核、除錯、token 計費與 provenance 仍需要完整紀錄。SKILL.state 丟棄的是「作為下一輪模型輸入的歷史」,不是要求整個系統不留 log。
為什麼 token 會從 O(T²) 變成 O(T)
傳統 history runtime 在第 t 步送入的 context 長度約為 O(t):
Σ |C_t| = O(T²)
SKILL.state 每一步只保留:
|P_t| = O(|P| + |Σ| + |O|)
只要 skill spec、state、observation 大小有上限,單步 prompt 就不隨已執行步數增加,總成本為:
Σ |P_t| = O(T)
這個 O(1) prompt footprint 有明確前提。若 tested_hypotheses、cmd_summary 或 observation 仍無限累積,state 只是換一個地方變長。可用的 implementation 必須替每個欄位設定容量、聚合與淘汰策略。[2]
實驗怎麼設計
論文用 SkillExecBench 與兩個公開 benchmark 測試:[2][4][5]
| Benchmark | 任務 | 主要測試點 |
|---|---|---|
| SkillExecBench Warehouse | 500 個 shelf 的 Store/Ship/Move/Wait | 長 horizon、多個獨立 state variables |
| SkillExecBench Software Repository | branch、commit、PR、CI transition | 關聯 state 與相依 action |
| InterCode CTF | 100 個 Linux bash CTF challenge | open-ended search、tool use、hypothesis tracking |
| Sierra τ-Bench | Retail、Airline customer service | DB API、政策限制、交易型 action |
對照 runtime 包含:
- ReAct-style full history
- 三步 rolling window+自然語言 summary
- structured state+full transcript 的 LangGraph-style baseline
- sliding-window truncation
- capped summary
- ReAct+LLMLingua compression[6]
模型涵蓋 Gemini-3-Flash、Gemma-4-31B-it、Qwen-3-8B-it。解碼設定是 temperature 0.0、top-p 1.0。合成實驗使用五個 generator seeds,論文稱 T ≥ 50 時與 baseline 的差異在 paired t-test 下達 p < 0.01。[2]
主要結果:Prompt 維持固定,長任務差距才拉開
Warehouse long-horizon scaling
Gemini-3-Flash 的 Warehouse 結果:
| Horizon | Runtime | Score | Avg Prompt | Total Tokens |
|---|---|---|---|---|
| 100 | ReAct | 0.84 | 36,362 | 1,245,413 |
| 100 | Memory | 0.87 | 29,607 | 1,082,154 |
| 100 | Stateful | 0.91 | 31,354 | 1,062,387 |
| 100 | SKILL.state | 0.94 | 1,905 | 65,408 |
| 200 | Memory | 0.84 | 84,364 | 6,175,509 |
| 200 | SKILL.state | 0.94 | 1,811 | 122,384 |
T=100 時,Stateful baseline 的 total tokens 約為 SKILL.state 的 16.2 倍。到 T=200,Memory baseline 約為 50.5 倍。SKILL.state 的 average prompt 仍落在約 1,700–1,900 tokens。[2]
Noise robustness
Warehouse 固定 T=50,每步加入 5、20、50 個背景事件:
| Noise events/step | ReAct | Memory | Stateful | SKILL.state |
|---|---|---|---|---|
| 5 | 0.68 | 1.00 | 1.00 | 1.00 |
| 20 | 0.61 | 1.00 | 0.98 | 0.97 |
| 50 | 0.53 | 0.96 | 0.98 | 0.98 |
SKILL.state 在產生 patch 時把無關 telemetry 排除,這些內容不會進入下一輪 prompt。[2]
但這個測試的 noise 有嚴格邊界:附錄說它們是隨機、與任務完全無關、且不改變 ground-truth state 的 telemetry。這不等同於 prompt injection、惡意 tool output、互相衝突的 relevant evidence,也不能直接延伸成「可抵抗 context poisoning 攻擊」。
State recovery
論文模擬外部 actor 在 Agent loop 之外改變真實環境。三個可恢復的 Warehouse scenario 中,Prompt/Memory/Stateful 需要 5–8 步才修正;SKILL.state 收到 corrective observation 後立即更新,recovery steps 為 0。[2]
另一個 Canceled Order scenario 則是所有 runtime 都失敗。顯式 state 能降低舊資訊干擾,沒有保證 observation 不完整時也能恢復。
InterCode CTF 與 τ-Bench
| Runtime | InterCode CTF Pass@1 | Retail Pass Rate | Airline Pass Rate |
|---|---|---|---|
| ReAct | 43.2% | 48.2% | 21.8% |
| Memory | 46.4% | 29.9% | 23.6% |
| Stateful | 41.8% | 51.7% | 28.1% |
| SKILL.state | 54.2% | 58.3% | 32.4% |
InterCode CTF 中,SKILL.state total tokens 是 387k,ReAct 為 977k,降低約 60.4%。Airline 則從 ReAct 的 4.85M 降到 2.88M,降低約 40.6%。[2][4][5]
這組結果的意義不只在 token。顯式記錄 tested_hypotheses、discovered_flags 等狀態,也讓 Agent 少重跑已失敗命令。
相同 token budget 下,壓縮歷史仍不夠
Warehouse T=100、平均 prompt 約 1,800 tokens 的 budget-matched control:
| Runtime | Score | Avg Prompt | Total Tokens |
|---|---|---|---|
| Sliding Window | 0.18 | 1,800 | 62,100 |
| Summary-capped | 0.52 | 1,840 | 63,400 |
| ReAct+LLMLingua | 0.22 | 1,810 | 62,350 |
| SKILL.state | 0.94 | 1,905 | 65,408 |
把歷史截短或壓縮到相同 budget,仍可能刪掉 shelf ID、inventory allocation 這類看起來重複、實際上決定 action 的 exact relation。SKILL.state 的優勢來自資料結構與 state ownership,不只是 prompt 比較短。[2][6]
Open-weight 模型仍會被 JSON 與 merge semantics 絆倒
Gemma-4-31B 在 T=100 的 SKILL.state score 是 0.42。作者分析 failure log 後分成:[2]
- 68%:過早 overwrite/delete,沒有做 in-place merge。
- 20%:schema comprehension/type coercion。
- 12%:JSON delimiter、trailing comma 等格式問題。
作者據此把主要問題歸因於 structured output adherence,並提出 grammar-constrained decoding 作為後續方向。論文沒有做 constrained-decoding ablation,所以目前只能說「failure log 主要落在結構化輸出錯誤」,還不能證明 reasoning capacity 完全不是因素。
這也提醒實作者:response_format=json 只解決語法,不會自動解決錯誤 deletion、錯誤 nested merge 或語意上不合法的 state transition。validator 仍然是核心元件。
這套方法不適合哪些任務
論文自己列出三個 sufficient-state assumption 會失效的情況:[2]
- Schema 必須在執行中才知道:事前無法固定 relevant state structure。
- 早期 observation 的價值要到後面才看得出來:當時沒寫入 state,history 又已丟棄,後面無法取回。
- 歷史軌跡本身就是輸出:audit、debug provenance、解釋過去 action,都需要原始 trajectory。
另外還有幾個工程邊界:
- 論文只測 single-agent;multi-agent shared state 會遇到 concurrent writes、版本衝突與 merge policy。
- schema authoring 需要 domain knowledge,欄位設計錯誤會把重要資訊在入口處丟掉。
- 論文沒有定義 user-owned protected fields、使用者確認流程、field-level provenance、retention policy 或 supersession 規則;前文的 User Intent Contract 是本文補上的 production governance,不是作者已實作的功能。
- long-running state 仍要做 size bound、snapshot、migration 與 schema versioning。
- tool action 可能失敗、timeout 或重複執行,state commit 必須配合 idempotency 與 reconciliation。
- noise robustness 沒有涵蓋惡意或語意相關的衝突訊號。
- 論文與 arXiv source package 沒有附 SKILL.state/SkillExecBench 官方 code、完整 schema 與實驗 runner。
因此,這篇論文現在適合拿來做 runtime design 與 independent implementation。若要宣稱 benchmark reproduction,仍需要作者公開 artifact,或自行重建環境後把差異完整列出。
一個可執行的重現順序
第一階段:先做 deterministic toy environment
先做 Warehouse 類環境,不急著接 web browser 或真實 API:
- 20–500 個 state slots。
- Store/Ship/Move/Wait。
- tool adapter 回傳 deterministic observation。
- 每次 action 都有 ground-truth transition。
這一階段先驗證 deep merge、null deletion、schema validation、retry 不污染 state。
第二階段:同時做四種 runtime
用同一模型、同一 skill instructions、同一事件序列跑:
- full-history ReAct
- rolling summary
- structured state+history
- SKILL.state
控制 temperature、top-p、max output tokens、action parser 與 tool environment,避免把模型或工具差異誤算成 runtime 差異。
第三階段:把計量寫進 runtime
每一步至少記錄:
step_id
prompt_tokens
completion_tokens
prompt_chars
state_bytes
observation_bytes
patch_bytes
action
validator_result
retry_count
tool_result
score_delta
Audit log 存在外部,不自動回填 prompt。
第四階段:測三種壓力
- Horizon scaling:10/25/50/100/200 steps。
- Irrelevant noise:每步增加 5/20/50 筆背景 telemetry。
- External drift:繞過 Agent 修改 ground truth,觀察幾步能恢復。
再加一組論文沒測完整的情境:相關但互相矛盾的 observation、malicious tool output、schema migration、action timeout 與 retry。
第五階段:再接 public benchmark
- InterCode 可測 terminal action 與 hypothesis state。
- τ-Bench 可測 transaction、policy 與資料庫 state。
- LLMLingua 可作 budget-matched compression baseline。
通過 toy environment 之後再接這些 benchmark,才能分清是 runtime bug、schema 問題、model formatting,還是 benchmark adapter 出錯。
我的判斷
SKILL.state 最有價值的地方,是把 Agent 長任務問題從「如何讓模型記住更多對話」改成「runtime 應該保存哪一份 canonical state」。這個方向很適合程序明確、狀態可建模、tool action 可驗證的工作:CI/CD、客服交易、terminal workflow、庫存、資料處理 pipeline、長時間批次任務。
它也把責任分得更清楚:模型負責當步推理與提出 transition;runtime 負責 schema、validation、merge、action policy、checkpoint 與 audit。模型不再是唯一的狀態管理者。
真正的難點會落在 schema 品質、使用者意圖治理與 commit semantics。state 設計得太窄,早期資訊會永久遺失;設計得太寬,prompt 還是會成長。模型也不能自行決定哪些需求可以丟,user-owned constraints 與 approval boundary 要由 runtime 保護。tool action 先更新 state 或成功後才 commit,也會直接影響一致性與安全性。
程式版本、決策與執行證據繼續由 Git、Issue/ADR、CI、runtime telemetry 與 audit log 保存完整歷史;working state 帶目前 branch、HEAD、diff、test、deployment 等權威 pointer。這能讓 prompt 保持 bounded,同時維持可回溯性。
對 Claude Code、Codex CLI、Pi 的導入判斷很明確:先在一個長、可建模、可由 Git/CI 驗證的 coding workflow 做 sidecar state 與 gated transition。只有 task success、prompt cost、重複操作、drift recovery 與 protected-intent 指標都過關,才升到 bounded-turn runtime。
SKILL.state 值得作為 task-scoped execution state 驗證,不適合直接取代全域使用者記憶或所有 Agent session。它的價值在 canonical state、驗證與 handoff;完整 provenance 繼續留在外部 source of truth。
Sources
- 1:SKILL.state arXiv abstract 與 metadata
- 2:SKILL.state HTML 全文與附錄
- 3:SKILL.state PDF
- 4:InterCode 官方 repository
- 5:Sierra τ-Bench 官方 repository
- 6:Microsoft LLMLingua 官方 repository
- 7:Claude Code Hooks
- 8:Claude Code CLI reference
- 9:Codex Hooks
- 10:Codex non-interactive mode
- 11:Codex AGENTS.md guide
- 12:Pi coding agent repository
- 13:Pi extension API,固定 revision
<!-- [1] https://arxiv.org/abs/2608.26263 [2] https://arxiv.org/html/2608.26263 [3] https://arxiv.org/pdf/2608.26263 [4] https://github.com/princeton-nlp/intercode [5] https://github.com/sierra-research/tau-bench [6] https://github.com/microsoft/LLMLingua [7] https://code.claude.com/docs/en/hooks [8] https://code.claude.com/docs/en/cli-reference [9] https://developers.openai.com/codex/hooks [10] https://developers.openai.com/codex/noninteractive [11] https://developers.openai.com/codex/guides/agents-md [12] https://github.com/earendil-works/pi [13] https://github.com/earendil-works/pi/blob/853a80d26c90a14c1886f0ebb8ffaae133ca2185/packages/coding-agent/docs/extensions.md -->
Signals
Visits
--
Waiting for Cloudflare metrics.