你請 AI 讀完一串倉庫紀錄:紅箱原有三件貨,後來加兩件,再把藍箱當時的數量抄過來。它找到了每一段文字,最後數量卻答錯。這就是 State Tracking(狀態追蹤)要檢查的問題:讀過更新之後,能不能算出「現在是多少」?
這篇專為第一次接觸模型評測、願意複製一段 Python 的讀者寫。你會用 StateBench 的四種更新認識難度,再建立有答案本的小考卷,分開檢查變數與整題正確率。只想懂概念,可以先跟著手算;要動手,準備 Python 3 與空白練習資料夾。
依 2026 年 11 月 6 日查閱的原始論文、程式與資料卡整理。AlphaLab 執行了本文 CPU 題目產生器,並用固定版本的官方 evaluator 核對十二組語意題的真值;未執行模型推論,因此下方沒有模型成績或架構排名。
先說結論:現在的值=起點+依序生效的更新
State Tracking=沿著更新順序,維護每個對象的當前值。像櫃台一本庫存簿:找到舊頁是檢索,依序對完入庫、改單和轉抄才是追蹤。長 Context(一次交給模型的文字範圍)容得下多少字,與能追對多少次更新,是兩張成績單。
- 先分清固定、覆寫、累加、跨變數複製,再比較同一種題目的長短。
- 先由程式算真值,答案本留在本機;送給模型的只有題目。
- 程式式與試算表式題面分開記分;小題通過後,再擴充題數與長度。
一、State Tracking 是什麼?四個抽屜、四種改法
變數就是貼名字的小抽屜,值是裡面的數字。Ai2 在 2026 年 11 月 3 日介紹 StateBench,用四個理論層級區分更新。下面的日常例子幫你理解操作;正式分類還涉及有限狀態與代數結構,不能光看一行指令便替所有任務定級。
①「寫好就固定」:r-trivial
痛點是長文件中的固定資訊被忘掉。先測找回原值:例如抽屜 a 一直裝著 3,後續對它的寫入也仍是 3。驗收問的是最後能否找回 3,並不需要把歷次變動累積起來。
②「後寫蓋前寫」:aperiodic
痛點是 AI 把舊設定當現況。改查最後有效寫入:a ← 3,後來 a ← 8,現在就是 8。像冷氣設定,最後一次調整決定目前目標;遇到資料撤銷或例外條件,則要另外定義規則,本練習只處理明確賦值。
③「用自己的舊值再算」:soluble/程式名 solvable
痛點是最後一筆只寫「加幾個」,單看它得不到總數。沿途累加:a ← (a + 5) mod 10 是把目前值加 5,再取除以 10 的餘數。原來 8 就變 3,像十格循環計數器。這是固定有限值域,與無上限的整數帳本有不同邊界;Python 用 % 表示餘數,見官方數字操作說明。
④「抄另一格現在的值」:general finite/程式名 periodic
痛點是你得先追好另一個抽屜,才能處理這次更新。讀取來源當下的值:a ← b 會把當時的 b 複製給 a;後來 b 改變,a 不會因此一起變。官方 evaluator明確採這種立即複製語意。它不是試算表裡持續重算的 =B1 公式。

二、先手算一次:為什麼交換需要暫存格?
先看兩格的教學例子:a=3、b=8、TMP=0。依序做 TMP ← a、a ← b、b ← TMP。第一步得到 TMP=3;第二步 a=8;第三步 b=3。最後是 a=8、b=3、TMP=3。
如果直接做 a ← b 再做 b ← a,第二步讀到的 a 已經是 8,兩格都變成 8。這個錯誤很適合教人理解相依性,但兩格交換不等於研究中五個以上變數可呈現的非可解群難度。下面的考卷使用五個普通變數,再加一格 TMP,仍只是一個小型教具。
StateBench 的設計重點還有「同一條來源」。官方衍生邏輯把跨格複製改寫成常數或自我累加,保留對應更新位置。覆寫、累加與複製版本可在每一步得到相同狀態;固定版本則回到各格最初的值。考卷難度的差別在於你必須讀取什麼資訊,並非答案數字比較大。
三、State Tracking 小考卷:先讓 CPU 寫好答案本
痛點是拿 AI 的答案反過來當標準答案。先建立可獨立核對的真值。把下方存成 mini_statebench.py,在空白資料夾執行 python3 mini_statebench.py。程式使用標準函式庫,會建立或覆寫目前目錄的 state-lab 同名檔案;重跑前先保存舊紀錄。
這是 AlphaLab 自製的教學產生器,借用四種更新與配對原則;不是官方完整資料生成流程。它把 2、20、200 次交換展開成 12、66、606 筆賦值,再以兩種題面產生 24 份題目。這三檔只是短、中、長的入門練習,尚不代表數萬 Token 的長 Context 壓力測試。
import json, random
from pathlib import Path
out = Path('state-lab'); out.mkdir(exist_ok=True)
names = ['a', 'b', 'c', 'd', 'e', 'TMP']
answer = {}; checks = []
for swaps in (2, 20, 200):
rng = random.Random(7)
source = [('set', i, v) for i, v in enumerate([1, 2, 3, 4, 5, 0])]
for _ in range(swaps):
a, b = rng.sample(range(5), 2)
source += [('copy', 5, a), ('copy', a, b), ('copy', b, 5)]
state = {}; first = {}; traces = {k: [] for k in ('fixed', 'overwrite', 'add', 'copy')}
for op, t, x in source:
old = state.get(t, 0)
value = x if op == 'set' else state[x]
first.setdefault(t, value)
traces['fixed'].append(('set', t, first[t]))
traces['overwrite'].append(('set', t, value))
traces['add'].append(('set', t, value) if op == 'set' else ('add', t, (value-old) % 10))
traces['copy'].append((op, t, x))
state[t] = value
for family, ops in traces.items():
truth = {}; steps = []; code = []; sheet = []
for op, t, x in ops:
key = names[t]
if op == 'set':
truth[key] = x; c = f'{key} <- {x};'; s = f'Enter {x} in cell {key}.'
elif op == 'copy':
truth[key] = truth[names[x]]; c = f'{key} <- {names[x]};'
s = f'Copy the CURRENT VALUE of cell {names[x]} into cell {key}.'
else:
truth[key] = (truth[key]+x) % 10; c = f'{key} <- ({key} + {x}) mod 10;'
s = f'Add {x} to cell {key}, keeping the result modulo 10.'
code.append(c); sheet.append(s); steps.append(dict(truth))
for form, lines in [('code', code), ('sheet', sheet)]:
case = f'{swaps}-{family}-{form}'
prompt = ('Execute these edits in order. Copy means a value snapshot, never a live formula. '
'Return ONLY a JSON object with integer values for a,b,c,d,e,TMP.\n'
+ '\n'.join(lines))
(out/f'{case}.txt').write_text(prompt, encoding='utf-8')
answer[case] = {'truth': truth, 'steps': steps, 'assignments': len(ops), 'characters': len(prompt)}
checks.append((family, swaps, truth))
(out/'answers.json').write_text(json.dumps(answer, indent=2), encoding='utf-8')
print('Created 24 prompts and answers.json; model calls: 0')
看到 Created 24 prompts and answers.json; model calls: 0,代表材料已產生。answers.json 同時保存最後狀態、逐步狀態、賦值筆數與文字長度。不要把答案本貼給模型。本文在 CPU 核對的最短複製題,依 a、b、c、d、e、TMP 順序,真值為 4,3,2,1,5,4;這是程式計算結果,並非模型作答結果。
想改用完整 StateBench,先查看固定版本的執行需求與官方資料卡。本次核對版本是 5b333b8d426638d8825f40efe8a508c20eb85730,官方套件要求 Python 3.12 以上;資料的格式、split 與 revision 也應逐項記下。先預覽樣本,再決定下載範圍,別直接啟動完整建庫或訓練。
四、兩張題面、一張記分單:怎樣測才公平?
- 固定考生與設定。寫下模型完整版本、日期、system prompt、推論預算、temperature、輸出上限、是否可用工具。介面未提供的設定記為「未提供」,不要猜值。
- 每題開新對話。先貼
state-lab/2-copy-code.txt;只接受六個整數欄位的 JSON。關閉額外工具測到的是文字推導;允許程式工具則另建組別,測的是模型與工具的組合。 - 按四層與三檔長度分組。保存原題與完整輸出。Token 是模型切分文字的單位,不能把賦值筆數或字元數當成 Token 數;實際呼叫時另記服務回報的輸入 Token 與是否截斷。
- 再換 sheet 題面。例如
2-copy-sheet.txt與 code 版本共用答案。這裡的 cell 是文字題面,不必開試算表軟體;複製的是數值快照。換題面時不一起換模型或提示。
把模型回覆存為 response.json,再把以下程式存成 score_state.py。執行 python3 score_state.py 2-copy-code response.json,就能核對那一題。它故意不接受 Markdown 圍欄與額外說明,格式錯誤另列,避免解析器替模型偷偷修答案。
import json, sys
from pathlib import Path
case, filename = sys.argv[1:]
key = json.loads(Path('state-lab/answers.json').read_text())[case]['truth']
try:
got = json.loads(Path(filename).read_text())
valid = (isinstance(got, dict) and set(got) == set(key)
and all(type(v) is int for v in got.values()))
except (ValueError, OSError):
valid = False
if not valid:
print('FORMAT_ERROR: expected exactly six integer fields')
raise SystemExit(2)
hits = sum(got[k] == key[k] for k in key)
print(json.dumps({'case': case, 'correct_variables': hits,
'total_variables': len(key), 'exact_match': hits == len(key)}))
逐變數正確率=答對欄位數 ÷ 全部欄位數;整題正確率=六欄全對題數 ÷ 該組總題數。本練習六欄包含 TMP;若你另報五個普通變數,須改標分母。格式錯誤保留在全題成功率的分母,另報格式錯誤數,不可悄悄刪掉後只算容易解析的題。
工作表先開這些欄:case|seed|模型版本|設定|題面|賦值數|輸入 Token|正確欄位/6|整題對錯|格式/截斷|費用。未作答就留空。當每組只有一題,結果只能叫「這題通過/失敗」;多換幾個 seed、保留未調提示的測試題後,才比較各組比例,並同時報題數。
五、追第一個錯點,再讀研究結論
痛點是只知道最後錯,卻不知道哪裡出事。做前綴診斷。保留失敗題,在第 6、9、12 筆等邊界截斷更新,分別新開對話詢問當前六格,與答案本 steps 的對應位置核對。陣列從 0 開始,所以第 k 筆對應 steps[k-1]。
先粗查,再逐筆找出「第一個被觀察到答錯的前綴」。前綴正確性可能先錯後對,不能假設單調並直接二分搜尋。這個診斷也改變了輸入與提問方式,定位的是可觀察錯點,無法據此斷言完整作答時的內部推理正好在那一步壞掉。
研究結果則分三層讀:預訓練模型反映已有能力與題面敏感性;直接用任務資料訓練反映學習與長度外推;理論分析依指定精度、注意力與架構假設推導可表達範圍。原論文的預訓練評估觀察到長序列與題面會影響表現;針對任務訓練的循環/混合模型,在部分層級展現更好的長度外推。這些條件要跟著結論一起讀,不能縮成「某種架構全面獲勝」。
ACL 2023 的 Entity Tracking 研究也以初始狀態與連續操作探測模型,提醒我們將狀態追蹤當成獨立能力來測。實務上,跨人員關係、無限新增物件、歧義描述與缺失事件,都比這份有限變數小考卷多出問題。
如果你的下一題是「把狀態交給外部程式會不會更好」,請另做同任務、同工具權限、同預算的對照,量完成率與修正成本。這是本文提出的工程假說,本篇 CPU 核對只確認答案產生器;原 StateBench 評估也不能替你的 Agent 部署做驗收。記憶來源問題可接著讀RAW-RAG 證據覆蓋診斷,程式決策分工則看確定性核心與機率邊緣。
六、常見問題:八個先講答案的判斷
長 Context 能放進去,就代表會追狀態嗎?
不代表。先檢查輸入是否完整,再依四種更新分開作答;收得下文字與算對結果各要證據。
找到最後一行就夠了嗎?
覆寫題常能這樣處理;累加與跨變數複製則要知道被引用的當前值。先辨認更新讀取誰。
這裡的 soluble 是「所有題都能解」嗎?
不是。這是代數分類用語;程式使用 solvable 名稱,本文用自我累加幫你建立直覺,不把名稱讀成能力保證。
兩格交換失敗,就證明模型架構的理論上限嗎?
不行。一次小題失敗也可能來自提示、解析或輸出限制。理論上限要對照論文的精確假設;五格教具通過也不能反推一般定理。
我只用聊天介面,可以測嗎?
可以做入門檢查。每題開新對話,記日期與介面顯示版本;可控設定較少時,把結果限定在該次介面測試。
讓模型用 Python,成績會比較公平嗎?
看問題。想評模型本身的文字追蹤,就分開工具組;想評交付任務,則把工具可用性、執行錯誤與成本一併評分。
這 24 份題目等於官方 benchmark 嗎?
不是。它們是同一 seed、三個長度的教具,配對題面也不是獨立樣本。正式研究需保留官方資料版本、抽樣方法與完整評估設定。
CPU 真值全通過,就能填模型正確率嗎?
不能。先取得實際模型輸出,再用獨立答案本核對。尚未呼叫的結果留空,避免把評分器通過誤當模型通過。
給新手的三個重點
- 先辨認更新要讀初值、最後寫入、自身舊值,還是另一格的當前值。
- 先算真值,再分長度與題面記分;別把字元、Token 與更新筆數混用。
- 讓每個結論都帶著樣本與配置:這題算對了,下一步才是換題、加長和驗證外推。
接著閱讀
左右滑動查看更多推薦
下一步:先交出一題可對帳的狀態更新
現在先跑產生器,手算最短的交換,再只送一份 2-copy-code.txt 給模型。保存回覆、核對六格、把結果填進工作表。記住:現在的值=起點+依序生效的更新。想把這套評測方法用到自己的 AI 工作流程,可從AlphaLab AI 專區繼續學習,或透過AlphaLab 課程安排系統化練習。