Vacant
Vacant 不是套在 agent 外面的強制層,也不是另一個 agent framework。 它是收件口:沒有可驗證收據的交付,不被接受。 因為它只看交付物、不在乎 agent 怎麼跑,所以任何框架的產出都能套進來。
跑客戶自己的可執行驗收測資、依結果決定交或不交,把每一次嘗試(含失敗那幾次)簽進可離線
重驗的雜湊鏈。要讓它成為全機唯一出口,需要容器/ACL/egress policy——那是部署層的事,
不是 Vacant 的(vacant/controller.py:7-8 早就逐字寫著,只是從沒出現在對外文字裡)。
既有模式,不是我們發明的:供應鏈安全的 in-toto/SLSA/Sigstore 也是同一條 ——「沒有合法 attestation 的 artifact,在收件時被拒」。
pip install vacant-network # 函式庫(import 名仍然是 vacant)
前提句(任何交付成效宣稱都必須帶著它一起講) 整件事建立在『需求可以被編譯成可執行的驗收測資』。需求跑不起來的場合,這個機制沒有免費的 裁判,會退化成『問一個模型』,而那正是量出來很差的東西。 (逐字出自
DECISION_20260903_R440P_CONFORMANCE_GATE.md§五-1)
給 AI agent 的整合契約在 AGENTS.md(索引:llms.txt)。
本頁下半部 §給 AI 讀 是同一份契約的散文版。
30 秒:先看一次閘門把交付擋下來
零設定、零模型端點、零 API key、零網路。
pip install vacant-network
vacant demo gate
不需要 clone。(2026-09-18 起:閘門的判斷層搬進套件了——同一份,不是複製;
ops/gain/r530/* 現在 re-export 到 vacant/vrun/*,R530 實驗跑的仍然是這一支。)
一隻假 agent 宣告它完成了,客戶的驗收說沒有(實跑輸出摘錄;家目錄縮成 ~,其餘逐字):
$ python3 -m vacant.cli run --workspace ~/.vacant-run/demo-gate/ws_vacant \
--suite ~/.vacant-run/demo-gate/tests_visible --run-dir ~/.vacant-run/demo-gate/receipts -- …
Done. I have created solution.py with add() and multiply().
All requirements are implemented and the code is ready to use.
[vacant run] RUN-ON 拒交(visible_fail) ws e5241309c23b→76c38272981f wire 0 通 收據 ~/.vacant-run/demo-gate/receipts
test_visible.py::check_mul — exception: ImportError: cannot import name 'mul' from 'solution' (~/.vacant-run/demo-gate/receipts/_frozen_RUN-ON/solution.py) [test_visible.py:7: from solution import mul]
agent 退出碼 :0 ← agent 自己說它成功了
客戶的驗收 :1/2 通過
裁決 :拒交(visible_fail)
vacant run 退出碼 :20 ← 退出碼反映裁決,不反映 agent 的說法
收據 :2 筆 Ed25519 簽章鏈
agent 說它做完了,客戶的驗收說沒有。 沒有 Vacant,上面那份 solution.py 已經交出去了。
畫面上每一個數字都是當場跑出來的:假 agent 是真子行程、閘門是 vacant/vrun/acceptance.py
那一支(R530 實驗跑的同一支)、那句 ImportError 是驗收 driver 當場抓到的例外原文、
20 是 vacant run 這個真子行程的退出碼。vacant/vrun/demo.py::_assert_not_a_performance 與
tests/test_demo_gate.py
擋著它不准退化成印死字串。收據當場用同一支驗章器驗過一次,你也可以自己再驗:
python3 -m vacant.vrun.verify_receipts --selftest # 先證明驗章器抓得到壞鏈
python3 -m vacant.vrun.verify_receipts --glob ~/.vacant-run/demo-gate/receipts
(clone 之後 python3 ops/gain/replay/verify_run_receipts.py … 是同一支——
那個路徑現在是 re-export,R460R/R529/R532 的鏈驗的就是它。)
還需要 clone 的部分(逐條寫出來,不含糊帶過)
vacant demo gate、vacant run、收據驗章都不需要。下面這些才需要:
| 需要 clone 的 | 為什麼不在 wheel 裡 |
|---|---|
ops/vacantrun/block_egress.sh(V3 出網封鎖)+ verify_egress_block.py |
要 root 一次的維運動作,不是產品功能;而且它改的是整台機器的網路規則 |
ops/vacantrun/selftest.py |
端到端自檢,會去讀 repo 的 runs/ |
ops/vacantrun/wrap_agent.sh(pi/Codex/OpenCode 的接線) |
那三個框架把 base url 寫在設定檔裡,接線是一段 shell 不是產品功能;吃環境變數的框架(Claude Code、走內建 provider 的 OpenCode)零接線、不需要它。逐格實測見 docs/AGENT_COMPAT.md |
ops/gain/**、runs/** |
R529/R530/R532/R534 的 runner、題庫、隱藏驗收、judge、排程器與落盤資料。要重算實驗數字必須 clone(見下面〈從原始碼跑〉) |
examples/**、decisions/**、docs/** |
展件、裁決檔、規格文件 |
⚠ 不把頂層 ops 打進 wheel 是刻意的:PyPI 上 ops 是 Juju 的套件,
同名會在別人的 site-packages 裡安靜覆蓋檔案——那是很糟的失敗方式。
接上你自己的 agent
-- 後面照你平常怎麼跑 agent 就怎麼打,vacant run 不需要知道那是什麼框架:
vacant run --suite ../tests_visible -- <你平常怎麼跑 agent 就怎麼打>
⚠ 驗收目錄不可以在工作區底下(--suite 與 --run-dir 都會擋,SystemExit):agent 改得到的驗收不是驗收。要給 agent 看就另外複製一份進去。
觸發點在 agent 行程結束的那一刻(不是在 wire 上認「它宣告完成了」):那個訊號
100% 可靠、零協定知識、零 token 成本。退出碼 0=交付、20=拒交、22=infra_void。
完整用法與落盤形狀見 docs/VACANT_RUN.md。
「一個開關」的正確講法。 vacant run 把模型通道轉向到自己的 proxy,靠的是一份
環境變數名單(vacant/vrun/envmap.py:
OpenAI 家族/Anthropic 家族/OpenRouter/Groq/Together/DeepSeek/Ollama/LM Studio…)
——涵蓋大多數框架,用設定檔的框架要改設定檔。實測:pi
(@earendil-works/pi-coding-agent)的 provider baseUrl 寫在 models.json 裡,
內建 provider 的 baseUrl 甚至編進 bundle,環境變數在那條路上完全沒有作用。
那種框架用 --port 給一個固定埠,再把它的設定檔指過來。
⚠ 「我設了環境變數」不是被中介的證據,requests_seen 才是。 自檢:
vacant run --allow-no-suite --run-dir /tmp/vr -- <你的 agent 命令>
python3 -c "import json;print(json.load(open('/tmp/vr/run_RUN-ON.json'))['requests_seen'])"
# 非 0 ⇒ 模型通道真的經過 Vacant;0 ⇒ 沒被中介到(框架用設定檔,或那一跑根本沒呼叫模型)。
名單漏一個變數=那條路沒被中介,而且不會有任何錯誤訊息——這是 V0 已知的殘餘風險。
這三條要跟上面那一幕一起讀(不准淡化)
- proxy 單獨只有 L3。 它證明「這些 bytes 經過我」,不阻止 agent 自己開一條連線。
要「agent 逃不掉」必須再加出網封鎖
(
ops/vacantrun/block_egress.sh, 要 root 一次;那支只在 repo checkout 裡,見上面〈還需要 clone 的部分〉)。vacant/controller.py:7-8那句逐字適用:無法阻止同一 OS 使用者繞過本命令。 - 中介的是「模型通道」,不是 agent 的行為。 框架自己發起的動作——自動 lint、 git checkpoint、內建重試、本機工具呼叫——不經過模型通道,proxy 看不到也擋不到。 收據能說「模型通道上發生了什麼」與「工作區最後長這樣」,不能說「agent 做了什麼」。
- 驗收是單邊保證。
vacant/suitegauge.py:30-33逐字:擋得住已知壞解不證明涵蓋真需求。accepted=true只代表「客戶寫下來的那幾條過了」。 實測:R532 那 836 題裡閘門接受了 811 件,其中 120 件(14.8%)過了可見驗收卻沒過隱藏驗收。
其餘邊界(TOCTOU、Responses API + store:true 的落盤缺口、不走 HTTP 的模型、
Bedrock SigV4、為什麼不做透明 MITM)在
docs/VACANT_RUN.md §4,一條都沒有被省略。
函式庫 quickstart(不用 clone)
零模型呼叫、零網路。
from vacant.checks import run_python_check
from vacant.identity import Identity, PublicIdentity
from vacant.logbook import Logbook
# 1) 驗收:客戶的測試在 runner 行程,候選碼在另一個 worker 行程
tests = "assert solve([1, 2, 3, 4]) == 6\nassert solve([]) == 0\n"
good = "def solve(nums):\n return sum(n for n in nums if n % 2 == 0)\n"
cheat = "def solve(nums):\n import os; os._exit(0)\n" # 想偽裝成「測試全過」
print(run_python_check(good, tests, allowed_entry_points=("solve",))) # True
print(run_python_check(cheat, tests, allowed_entry_points=("solve",))) # False
# 2) 收據:每一次嘗試簽進 append-only 雜湊鏈
me, book = Identity.generate(), Logbook()
who = PublicIdentity(vacant_id=me.vacant_id, pub=me.pub)
book.append("attempt", {"draft": "sha256:aaa", "visible_ok": False}, me, ts_ms=1_700_000_000_000)
book.append("attempt", {"draft": "sha256:bbb", "visible_ok": True}, me, ts_ms=1_700_000_000_001)
book.append("shipped", {"accepted": True, "draft": "sha256:bbb"}, me, ts_ms=1_700_000_000_002)
print(book.verify_chain(who)) # True
# 3) 竄改中間那一筆 ⇒ 驗章失敗
import copy
from vacant.logbook import LogEntry
forged = Logbook([copy.deepcopy(e) for e in book.entries])
e = forged.entries[1]
forged.entries[1] = LogEntry(e.stream_id, e.branch_id, e.seq, e.prev_hash, e.ts_ms, e.type,
{"draft": "sha256:aaa", "visible_ok": True}, e.sig) # False -> True
print(forged.verify_chain(who)) # False
# 4) 誠實邊界:砍掉尾巴=合法前綴,這一支抓不到
print(Logbook(list(book.entries[:2])).verify_chain(who)) # True ← 沒抓到
第 4 步不是 bug 的示範,是這條鏈的地界:verify_chain 檢查 seq 連續、prev_hash
串接、逐筆簽章,沒有長度承諾也沒有外部錨,所以合法前綴照樣過。文獻上這叫
truncation/omission attack(Ma & Tsudik 2009)。鏈給的是 integrity(沒被改)
不是 completeness(沒有漏);要偵測「砍尾巴」必須把鏈頭(Logbook.head())對外公示
或找人會簽——Vacant 不會替你做。
vacant --help # 安裝後可用的 CLI
你不用相信我們
一個宣稱可究責的系統若不能被外部查核,主張就沒有內容。下面四件事外部使用者自己跑得出來, 不必相信我們的任何說法:
| 要驗什麼 | 自己跑 | 為什麼這樣就夠 |
|---|---|---|
| 收據鏈沒被動過 | verify_run_receipts.py --selftest(先過負控制)再 --glob 'runs/g_r532_*' |
先證明驗章器抓得到壞鏈,再拿它驗真鏈。R532:86 條鏈 3,895 筆、0 失敗 |
| 題目不是我們挑的 | docs/BANKS_HOWTO.md |
題庫 sha256 釘死;日期窗與已知壞題寫在 runs/INDEX.md |
| 結論不是分析器編的 | 直接數 runs/g_*/rows.jsonl |
一列=一題一臂,deliv = accepted ∧ meets_demand |
| 我們有沒有藏錯 | examples/verdicts.py +下面的〈誠實邊界〉 |
被推翻的宣稱不刪;我們自己抓到的稽核缺口也在裡面(邊界 3) |
60 秒看懂
三步:驗收 → 閘門 → 收據。
flowchart LR
A["需求<br/>prompt + 客戶自己的可執行驗收測資"] --> B["任何 agent<br/>寫一份候選"]
B --> C{"驗收<br/>沙箱跑 visible_check"}
C -- "通過" --> D["出貨<br/>accepted = true"]
C -- "沒過:換一份,或把失敗原文貼回去改" --> B
C -- "預算用完仍沒過" --> E["拒交<br/>accepted = false(拒交算失敗)"]
D --> F["收據<br/>每一次嘗試簽進 hash-chain"]
E --> F
F --> G["離線重驗<br/>verify_chain/瀏覽器內逐筆重算"]
H["hidden_check<br/>只在事後計分"] -. "V/GT 分離:選擇與回饋都沒碰它" .-> C
- 驗收:客戶的驗收測資是資料不是程式(
SuiteSpec=entry point +字面值(args, expected)), 執行器只跑自己渲染出來的碼。上鏈之前要先過量具:參考解全過 ∧ 每個已知壞樁都被擋。 - 閘門:過驗收才出貨;預算內一份都沒過就拒交,而且拒交算失敗(分母是全部題目)。
- 收據:每一次嘗試(不只成功那次)簽進 append-only hash-chain;持公鑰的任何人都能離線重驗。 多方版本是 k 把金鑰各自跑、各自簽,不一致就指名是哪一把。
最新成果
所有數字都帶分母,且都在上面那句前提之下。 單一數字入口是
docs/VACANT_COMPLETE_2026-09-12.md;
裁決的單一真相來源是 examples/verdicts.py。
一句話的主體結論
增益的主體是「可執行驗收閘門+重抽」,不是回饋迴圈。
| 對比 | 12B(gemma-4-12b-it-qat) | 27B(qwen3.8-27b, non-thinking) |
|---|---|---|
| 閘門+重抽 − 單發(Δ_G) | 五次同題複製:+14.17/+18.33/+17.50/+19.17/+18.97 pp(n=120,p_raw 全 < 0.002) | 836 題五個題組合併:+7.89 pp [5.36, 10.04],p=3.0e-9 |
| 迴圈 − 單發(Δ_O) | 十五格全部通過 Holm,+17.5~+29.2 pp | +4.67 pp [1.75, 7.38],p=0.0015(Holm p_adj 0.0030) |
| 迴圈 − 閘門+重抽(Δ_C) | 五次 +5.83/+4.17/+0.83/+2.50/+4.31 pp,0/5 過 Holm;跨題庫四集合併 +1.12 pp,Holm p_adj 0.341 | 五組全部負號 −5.83/−5.19/−16.67/−1.28/−0.54,合併 −3.23 pp [−5.52, −0.75],p=0.0101 |
⚠ Δ_G 不在預註冊家族裡(家族只有 Δ_C 與 Δ_O),所以它的 p 未經多重比較校正、 區間也沒有。引用時必須把這一句一起寫出來。
逐條能講什麼、不能講什麼
能講:迴圈贏單發,穩定。 12B 十五格全過 Holm;27B 上 +4.67 pp 也過。
不能講:迴圈贏重抽。 這一條未確立。12B 九個資料點全部同號(+0.64~+5.83 pp), R460R 五次 0/5 過 Holm、R529 四集合併 Holm p_adj 0.341。27B 上五組全部翻成負號、 合併通過檢定。同號未解析 ≠ 沒有差異(單集 n=54–156 對 +10 pp 的檢定力只有 0.14–0.55)。
27B 那一輪可以引用的狀態是 RULED_OUT:「在這 836 題上排除了迴圈相對同預算重抽有
≥+2 pp 的實務增益」。不可以引用 EFFECTIVE——預註冊的四狀態表沒有守方向,
一個方向相反的顯著結果被貼成了 EFFECTIVE,那個標籤授權的句子在這批資料上是假的
(DECISION_20260917_R532_STRONGER_MODEL_PREREG.md AMEND1)。而「反向且顯著」沒有事前註冊
的狀態可以承接,所以也不下「迴圈有害」這個結論。
誠實邊界(必寫):27B 那一輪的前提「更強的模型」,它自己的資料不支持。
OFF 臂就是模型裸強度(不含任何 harness):27B 74.8% vs 12B 75.2%,而且三個
LiveCodeBench 題組全部更差(−9.2/−9.6/−3.7 pp),只有兩個 EvalPlus 題組較好。
⇒ 這一輪測到的是「換一顆模型」,不是「變強」。不准寫成「模型變強之後迴圈就沒用」
(AMEND2)。
禁語:複製失敗、效果消失、等價、打平、多數支持、複製穩定、迴圈沒用、趨勢明顯。 差值就寫差值,不要寫成 improvement/提升。
資料量與完整性(R532 那一輪)
| 量 | 數字 | 怎麼自己重算 |
|---|---|---|
| 規模 | 五個題組 836 題、43 塊、2,508 列、零 infra_void |
ops/gain/r532/results_r532.json |
| 隱藏測資洩漏(只掃了一臂) | V/GT --scope v2:H-MIX 那一臂 43/43 CLEAN(199,019 個指紋);OFF 與 CONFORM 從未被掃過(見邊界 3) |
ops/gain/harness_vgt_audit.py --run <run> --bank <bank> --scope v2 |
| 收據鏈 | 86 條鏈 3,895 筆,逐筆 Ed25519 簽章與鏈接全過,0 失敗 | python3 ops/gain/replay/verify_run_receipts.py --glob 'runs/g_r532_*' |
| 仲裁者自己的牙齒 | --selftest PASS(12/12 組手算對照) |
python3 ops/gain/r532/analyze_r532.py --selftest |
⚠ 不得寫成「V/GT 全臂乾淨」。 那個工具結構上只掃 H 臂(見邊界 3),而且跳過瑣碎 needle——跳過 ≠ 檢查過。 ⚠ 該量具 沒有在「真的有洩漏」的真實 run 上驗過,負控全是人工植入的。
怎麼自己重跑整批:見 docs/BANKS_HOWTO.md。
誠實邊界
規格的一部分,不是免責聲明。引用任何數字都要一起帶。
- 前提(凌駕以下各條):需求要能編譯成可執行的驗收測資;跑不起來的需求沒有免費的裁判。
- Vacant 不是套在任意 agent 外面就自動生效的強制層。 以 library
(
vacant/agent.py:51-103,self.brain是公開屬性)或 MCP 工具 (vacant/mcp_server.py:184-210,工具 docstring 只是在「勸」)的形態出現時,它是自願的 ——agent 不呼叫就完全不存在,而且沒有任何東西會察覺這件事。只有以 controller (vacant/controller.py:304-530)或由 harness 自己擁有 agent loop 的形態,對它親手 spawn 的那個子行程才是強制的。vacant/controller.py:7-8逐字:「保證只涵蓋透過本 controller 啟動的子行程;無法阻止同一 OS 使用者繞過本命令直接執行 agent。需要強制全機唯一出口時, 仍須容器、ACL 或 egress policy」。本頁任何一句都不得讀成比這句樂觀。 用正式名詞講更精準:Saltzer & Schroeder 1975 的 reference monitor 三條件裡, Vacant 滿足防竄改與小到可被驗證,不滿足 complete mediation(完全中介)。 這不是 bug,是「可選的東西不可能完全中介」的必然後果。把它寫成強制層就是在說謊。 - 我們對外講錯過一句,這裡更正,並附上補掃完成後的結果。 我們寫過 R532「V/GT 紅線
43/43 CLEAN」。那句話是錯的。
ops/gain/harness_vgt_audit.py:746是if arm not in VARIANTS: continue,而ops/gain/harness_arms.py:65的VARIANTS = ("HPI", "HOC", "HMIX")⇒ 古典七臂(含OFF與CONFORM)從來沒有被掃過, 每一塊的per_arm都只有{'HMIX': N}。當時正確的講法是 「H-MIX 那一臂 43/43 CLEAN,另外兩臂未稽核」。修法含把預設值改成完整稽核,理由逐字: 「靠忘了給參數拿到只掃一臂的綠燈,正是這個洞能存在的條件」。 回溯補掃已於 2026-09-18 完成,落盤ops/gain/vgt_retro_audit_20260918.json(generated_at2026-09-18T11:58:32+0800,scopev3=十條臂+逐臂 fail-closed)。 下面每個數字都可以在那份 JSON 裡數出來:-
179 份已歸檔 run:165 CLEAN/10 UNVERIFIABLE/4 VIOLATION, needles 檢查 3,486,403。
-
我們對外引用的四批逐臂全 CLEAN:
批次 塊數 逐臂稽核筆數 R460 6/6 OFF 120、CONFORM 196、OFF5 602、HPI 187、HOC 283、HMIX 163 R460R 30/30 OFF 608、CONFORM 1029、OFF5 3032、HPI 958、HOC 1488、HMIX 890 R529 37/37 OFF 717、CONFORM 936、HMIX 844 R532 43/43 OFF 836、CONFORM 1122、HMIX 1144 -
R532 的
CONFORM1,122 筆首次被動態稽核掃過、零違規。 那是 Δ_C 的被減數; 先前只掃 HMIX 時,「CONFORM 若洩漏會讓 Δ_C 更負、與觀察方向同向」這個替代解釋 無法排除,現在可以排除。 -
10 UNVERIFIABLE 全部是只有 preflight、零 arm 紀錄的中止 run ⇒ 沒有稽核對象。 這是誠實的 verdict,不是壞掉——
UNVERIFIABLE不是「乾淨」也不是「髒」。 -
4 VIOLATION 全在 R530(
g_r530_s1_1004_1、g_r530_s2_1003_1、g_r530_s2_1004_2、g_r530_s3_1003_1),規則全是hidden_file_in_workspace。開封比對後是模型自己建的 同名檔:sha256 與釘死的隱藏測資不同、非瑣碎行零重疊或僅 2–3 行(都是got = solution.redact(line)這種任何測試都會寫的 API 呼叫)、同一題在 s1 與 s2 產生的內容完全不同(真 GT 跨 run 會一樣)。那條規則假設「只有 harness 能放這種檔」, 沒預期模型會自己把測試檔取名test_hidden.py。要不要收緊判準是未決事項。 這份補掃有四條界線必須跟數字一起讀,不准只引好消息: (a)CLEAN只保證hidden \ visible的字面 repr 沒有出現在 harness 自己寫的 system/user 文字裡,語意等價的改寫、以及豁免規則涵蓋的那些,這支認不出來; (b)bank 是推斷出來的(bank_inference欄位),不是 run 自己記的——R529 之前的 run 沒有--record-bank-field;(c)R529/R532 的收官分析器還在讀舊證據:analyze_r529.py的vgt_gate()與analyze_r532.py的gates_post()讀vgt_v2_<block>.json,那批檔案的per_arm只有 HMIX ⇒ 目前的替代證據是vgt_retro_audit_20260918.json,那兩支分析器尚未更新,閘門沒有全部跟上; (d)全 179 份不是全乾淨:4 VIOLATION 與 10 UNVERIFIABLE 在那裡, 「V/GT 全臂乾淨」只在上面點名的四批、且要連 scope 與這幾條界線一起講。 留著這一整段過程(缺口怎麼被自己發現、怎麼掃完、掃完還剩什麼),是因為它比任何效能數字 更能說明可究責是可行的。
-
- 閘門保證的是「過了寫下來的測試」,不是「達成真需求」。
vacant/suitegauge.py:30-33的單邊保證逐字:壞樁擋得住只證明這套驗收不是對什麼都放行, 不證明它涵蓋真需求。實測:R532 那 836 題裡,閘門接受了 811 件,其中 120 件(14.8%)過了可見驗收卻沒過隱藏驗收。沒有閘門時是 211/836=25.2%。 ⇒ 閘門把假交付大致砍半,但沒有消掉。 - 鏈給的是 integrity(沒被改),不是 completeness(沒有漏)。
vacant/logbook.py:168-195只檢查 seq 連續、prev_hash串接、逐筆簽章,沒有長度承諾、 沒有外部錨 ⇒ 合法前綴照樣過(quickstart 第 4 步)。這在文獻裡有正式名字: truncation/omission attack(Ma & Tsudik 2009)。三件必須一起講的事:vacant/checkpoint.py:144-155的存檔點鏈有同一個洞。verify_checkpoint_chain只往前檢查prev_checkpoint_sig串接、首枚為 null;丟掉最後幾枚,剩下的照樣全過 (實測:4 枚全過、丟掉最後 2 枚仍全過、抽掉中間一枚失敗、拔掉首枚失敗)。- 「把筆數簽進每一筆」擋不住它。
seq本來就是筆數,截斷後的前綴每一筆仍然自洽。 長度承諾要有效必須是外生的——在別人手上,或在時間上早於截斷。 - 要偵測就得把
Logbook.head()對外公示或找人會簽。Vacant 不會替你做。
- 簽章指認金鑰,不指認主體,也不指認真假。 收據證明「這句話是這把金鑰說的、事後沒被改過」,
不是「這句話是真的」(
vacant/peerexec.py:117-120)。產品路徑的收據是交付方自己簽的 (vacant/ecosystem.py:641-642),私鑰是同一個 OS 使用者可讀的明文 PEM (vacant/body.py:160呼叫identity.save沒傳 passphrase)。key custody 是部署假設, 軟體層無法 prevents。 - 不是安全邊界。
run_python在獨立行程、暫存 cwd、CPU limit 與逾時下執行,擋得住常見的 提前exit(0)、讀同檔隱藏測資與 process/file API,但不是完整的惡意程式邊界;不可信程式 應放進 container、gVisor 或獨立 VM。vacant/checks.py沒有可用的 Windows 沙箱分支。 - 多數決有數學上界:最多容忍 ⌊(k−1)/2⌋ 個腐化執行器;過半即反轉,且機制無法知道自己在 門檻哪一邊。
- 對驗收套件本身腐化毫無防禦:套件換成「載得進就算過」時,每一票誠實、每條鏈驗得過、 指標滿格,而系統在交垃圾。殘餘一律講兩個數字:可實現 +2.72 pp、事後諸葛上限 +4.35 pp。
- 渲染器與沙箱仍是被信任的輸入:信任被搬走,不是消滅——渲染器有 bug,k 台機器會一致地錯, 爭議率仍是 0。
- 同源/Sybil 防護是 raises-cost,不是 prevents:製造一個新身分本身目前沒有成本。
- n 不夠:LCB v2 n=120 只辨得出約 12 pp 級的差異;要把區間收到 ±5 pp 需要 278 題。
- 題庫特性:「可見篩選無損」部分是題庫性質(MBPP+/LCB 的
hidden_check結構上蘊含 visible)。 驗收套件不是真需求子集的部署裡,拒交會殺掉好答案。 - 五次複製共用同一批 120 題:seed 只換題序/persona/取樣,不換題目 ⇒ 複製不掉題庫特異性。
- 兩台後端=兩種推論條件(thinking/非 thinking),不只是版本號不同;逐集絕對值與 token 是兩種條件的混合物,不可單獨引用。
- 污染查不到底:HumanEval+/MBPP+(2021)幾乎確定在所有現代模型的訓練集裡; 交付率上升無法區分「模型更強」與「這批題進了訓練集」。
- 證據包只保證自洽,不保證內容為真:
SHA256SUMSdetects 落盤後的竄改,不 prevents。 - 對帳是同源的。
ops/gain/replay/verify_run_receipts.py那三條對帳規則 (verdict 數 == rows 列數、task_id 集合相等、attempt 數 ≥ verdict 數)兩端是同一個行程 寫出來的。它抓得到不對稱的疏漏(bug),抓不到兩邊一起不寫(malice)。 真正的對帳要求至少一端握在利益不同的人手上——那件事目前沒有做。 - 不是證明:demo 只能說「看得到提升」;「證明提升」保留給預註冊 batch run。
完整清單(B0–B20、H1–H9 與各 run 的收官邊界)見
docs/VACANT_COMPLETE_2026-09-12.md§四。
可以寫強的地方
這幾條是真的、有程式碼支撐,不必寫得謙虛:
-
收件那一關繞不過。
vacant/receipt.py+controller.verify_delivery會重算五個 sha256(request/task/tests/answer/trust card)、驗 Ed25519 簽章、比對chain_head/stream_id/branch_id與當下活著的鏈是否一致、確認每一份評審都綁在這一筆交付上, 最後才policy.admit。啟動權用os.O_EXCL認領(vacant/controller.py:372), 一張收據只能消費一次。強制點在驗收期,不在執行期——這一段是真的做到了。 -
agent 的自我宣稱從來沒被採信過。 生態自己跑 verifier(
vacant/ecosystem.py:531), controller 在啟動任何下游 agent 之前再獨立重跑一次(vacant/controller.py:299-300)。 -
驗收沙箱是兩個行程。 測試碼在 runner、候選碼在另一個 worker,靠 stdin/stdout + nonce 做 RPC(
vacant/checks.py:577-600、444-457)。ops/gain/gain_run.py:957註解逐字: "the candidate worker cannot see this test code"。候選碼結構上看不到測試碼, 不是「被擋下來」。 -
「量不到不是通過」寫成了程式碼:
"all_pass": bool(total > 0 and passed == total)(vacant/vrun/acceptance.py:268);量具同理,n_broken >= 1才算數,空的壞樁集合 不能空洞地成立。 -
拒交是真的會發生:R532 那 836 題裡,閘門臂拒交 25 件、迴圈臂拒交 68 件, 而且拒交算在每一個比率的分母裡。
這不是什麼
- 不是一個 agent。 它站在任何 agent 的交付出口上:閘門+收據+多方作證。誰來寫程式碼不是它的事。
- 不是 prompt 技巧。 三條迴圈臂的回饋模板、截斷規則、沙箱、逾時逐字相同(鐵律 KS-1 有可執行 防呆),唯一差異是機制本身。
- 不是「信任」。 口徑是可究責性/讓依賴有根據。經典定義(Gambetta 1988、Mayer 1995)把 「不依賴監督」寫進信任的必要條件,而監督正是本系統的全部——所以這裡永遠不用「信任」兩個字。
架構與程式碼地圖
| 層 | 模組 | 承重什麼 |
|---|---|---|
| L0 密碼學 | vacant/canonical.py/identity.py/crypto.py |
跨機驗章一致的唯一序列化;Ed25519 keypair + vacant_id |
| L1 帳 | vacant/logbook.py/envelope.py/checkpoint.py/attest.py/receipt.py |
append-only hash-chain(stream_id=創世 hash);簽章信封+ReviewEnvelope;V1 存檔點自身成鏈 |
| L2 可究責層 | vacant/registry.py/reputation.py/router.py/auditor.py/memory.py/dashboard.py |
發現+信譽索引、五維 Beta、on/off 單開關、確定性再驗、MemoryManager(面板不是可究責性的來源) |
| L3 題庫與量具 | vacant/codebench.py/suitespec.py/suitegauge.py |
MBPP+/LiveCodeBench v1–v3/HumanEval+;驗收套件是資料不是程式;量具=參考解全過 ∧ 已知壞樁全擋(單邊保證) |
| L4 實驗基建 | ops/gain/*/vacant/peerexec.py/record.py/research.py |
九條臂的 runner、仲裁者(四狀態、Holm、區間、--selftest/--mutation-check)、互跑不互審的執行證言層、RECORD_SPEC 證據包 |
| L5 展件 | vacant/entrycost.py/examples/receipt_viewer_multiparty.html/examples/e10_mediator.py |
機制模擬(現場秒級)、離線單檔收據檢視器、E10 兩行路由序列重算 |
九條臂:OFF(單發,1.00 通)、ON(信譽路由+K=3 評審+一次修訂,≈5 通)、
OFF5(五次投票,5.00 通)、CONFORM(驗收閘門、早停,1.3–1.7 通)、EQ5(等預算,恆 5.00 通)、
ONR(只隔離路由)、H-PI/H-OC/H-MIX(三條修訂迴圈)。
為什麼一定要有 OFF5/EQ5:ON 比 OFF 好幾乎必然,因為它多花五倍呼叫——拿 1 次對 5 次去宣稱
「機制有效」是拿成本冒充機制。
從原始碼跑(實驗與重算)
PyPI 的輪子含 vacant/vrun/(閘門、proxy、驗章器),不含 ops/(實驗 runner
與題庫)。要重算實驗數字必須 clone。
git clone https://github.com/cosmopig/Vacant.git && cd Vacant
python3 -m venv .venv && .venv/bin/pip install -e '.[dev]'
.venv/bin/python -m pytest tests/ -q
# 零模型呼叫就能看到的東西
open examples/receipt_viewer_multiparty.html # Linux: xdg-open
.venv/bin/python ops/gain/replay/verify_run_receipts.py --glob 'runs/g_r532_*'
.venv/bin/python ops/gain/analyze_r529.py --selftest
.venv/bin/python ops/gain/analyze_r529.py --mutation-check
.venv/bin/python ops/gain/r532/analyze_r532.py --selftest
⚠ runs/ 底下 136 個 _analysis_* 目錄是衍生物不是證據——它們的輸入就是
runs/g_*/rows.jsonl。引用任何 run 之前先讀 runs/INDEX.md。
給 AI 讀
以下是給 coding agent 的整合契約。完整版(含全部函式簽章與機器可讀事實區塊)在
AGENTS.md;索引在 llms.txt。
A. 哪一種形態才是強制的
| 形態 | 進入點 | 對 agent 有約束力嗎 |
|---|---|---|
| Library | vacant.agent.Vacant(vacant/agent.py:51-103) |
沒有——自願的。 self.brain 是公開屬性;不呼叫就不存在。 |
| MCP 工具 | vacant.mcp_server(vacant/mcp_server.py:184-210) |
沒有——只是在勸。 delegate 的 docstring 寫 "THE PREFERRED PATH";模型忽略它不會被攔,也沒有東西偵測得到。 |
| Controller | VacantFirstController.delegate_then_run(vacant/controller.py:304-530) |
有——但只對它親手 spawn 的子行程。 先取得已驗證交付,才 shell=False 啟動下游 agent。 |
| harness 自己擁有 loop | 例如 ops/gain/r530/openwork_arms.py:642-696 |
有——harness 就是那個 loop,agent 沒有繞過閘門的路徑。 |
正確的定位是「收件口」不是「強制層」:強制點在驗收期——沒有可驗證收據的交付不被
接受,而那一關繞不過(vacant/receipt.py + controller.verify_delivery 重算五個
sha256、驗 Ed25519、比對 chain_head,os.O_EXCL 讓一張收據只能消費一次)。強制點不在
執行期:要讓 Vacant 成為全機唯一出口需要容器/ACL/egress policy,那是部署層的事。
這與供應鏈安全的 in-toto/SLSA/Sigstore 是同一個模式。
用正式名詞說清楚:Saltzer & Schroeder 1975 的 reference monitor 三條件裡,Vacant 滿足 防竄改與小到可被驗證,不滿足 complete mediation。這是「可選的東西不可能完全 中介」的必然後果,不是缺陷。用 library 或 MCP 形態時,誠實的講法是「agent 已驗證的產出 是可究責的」,不是「agent 被約束住了」。
B. 攔在哪一層
挑最窄的、剛好給你所要性質的那一層:
-
只要一條過/不過的線 → 直接呼叫
vacant.checks.run_python_check。不需要身分、鏈或設定。 -
要一份嘗試的紀錄 → 加一個
Logbook,每一次嘗試都 append。其餘都不用改。 -
要驗收套件本身可被查覺竄改 → 把套件寫成
SuiteSpec,在產生任何候選之前用commit_suite_with_gauge上鏈。執行器之後只跑自己從 spec 渲染出來的碼, 供應者無法把「一段程式」偽裝成「一組測試」。 -
要 k 方獨立同意 →
peerexec.select_by_quorum,每個執行器各自持鑰、各自成鏈; 不一致時指名是哪一把金鑰。⚠ 三個照文件寫會踩到的坑,先講清楚:
-
drafts的每一格是(code, worker_id)(先程式碼、後 worker 名字),兩格都是str。傳反不會有型別錯誤:每一份「草稿」都跑不過驗收 ⇒ 三方一致投沒過 ⇒ 你拿到refused=True、shipped_index=None、三條簽章鏈全部驗得過——與「機制正確 地拒絕了爛交付」在畫面上一模一樣,而拒交正是本系統的合法輸出。select_by_quorum進門會做一次啟發式形狀檢查,疑似反了就丟peerexec.DraftOrderError;它有偽陰性(兩格都像碼、或草稿本身沒有換行也沒有def就抓不到),不要讀成「順序錯一定會被抓到」。 -
task必須有entry_point(要驗的函式名)。entry point 屬於題目不屬於套件, 套件上的那一個只拿來核對。少了這一格會得到SuiteSpecError(code="entry_point_unbound"),即使你傳進去的SuiteSpec帶著entry_point='solve'。 -
用 mapping 寫套件時
v: 1是必填(SuiteSpec的版本,唯一合法值是 1):suite = {"v": 1, "dialect": "mbpp", "entry_point": "solve", "tests": [{"args": "[1, 2]", "expected": "3"}], "cmp": {}}
漏了就是
bad_version:None。SuiteSpecError有兩個欄位:.code(機器讀,會原樣 進收據與refusal_reason)與.hint(人讀)——程式要分支請比對.code,不要 比對str(exc)。
-
-
要 agent 不可能交出未驗證的東西 →
VacantFirstController+ OS 層邊界(見 §A)。
agent 必須配合的事:回傳定義了宣告的 entry point 的程式碼(閘門是呼叫
entry_point(*args),不讀散文);容忍拒交(預算用完=拒交,而拒交算失敗——
用完就把最後一份交出去等於把閘門唯一在做的事刪掉);不接收隱藏測資
(保留集不得進入 prompt、重試訊息或教訓;回饋只准抽象到失敗的形狀,鐵律 A4)。
C. 可驗的不變量
- I-1 改中間會被抓到:改 payload、抽掉中間一筆、砍掉創世,
verify_chain都回False。 - I-2 候選碼結構上看不到測試碼:測試在 runner、候選在 worker,靠 nonce 標記的 literal-only
RPC 溝通(
vacant/checks.py:577-600、444-457)。這是結構性質,不是黑名單。 - I-3 自我宣稱從不被採信:生態跑一次 verifier(
ecosystem.py:531),controller 再獨立跑一次 (controller.py:299-300)。 - I-4 「量不到不是通過」寫成了程式碼:
bool(total > 0 and passed == total)(vacant/vrun/acceptance.py:268);量具要求n_broken >= 1。 - I-5 量具是雙向的:
ok同時要求參考解通過與每個已知壞樁被擋。 - I-6 渲染是確定的:
suitespec.render(spec)是 spec 的純函式 ⇒render_sha256跨機可比。 - I-7 拒交真的會發生:R532 836 題,閘門臂拒交 25、迴圈臂拒交 68,且計入分母。
D. 誠實邊界(給 AI 的版本)
上面 §誠實邊界 19 條全部適用。對整合最要命的四條:
- H-1 前提:需求要能編譯成可執行驗收測資,否則沒有免費的裁判。
- H-2 過驗收 ≠ 達成需求(
suitegauge.py:30-33單邊保證)。實測閘門仍有 14.8% 假交付。 - H-3 鏈給的是 integrity 不是 completeness:偵測不到砍尾巴
(truncation/omission attack,Ma & Tsudik 2009)。
checkpoint.py:144-155的存檔點鏈 有同一個洞。把筆數簽進每一筆沒有用(seq已經是筆數,前綴仍自洽)——長度承諾 必須外生。要偵測就得外部公示Logbook.head()或會簽。 - H-5 發布的輪子沒有預設驗收判準。
suitegauge.default_runner與peerexec.sandbox_probe委派給ops.gain.gain_run.meets_demand,而那支只在 git repo 裡(它帶著 G 實驗自己的沙箱 import 白名單與infra_void語意;函式庫不該替使用者宣告那份政策,而且第二份判準會漂移)。 沒有ops/時呼叫會拋vacant.suitegauge.OpsRunnerUnavailable(訊息裡寫了該怎麼做)。 正路是注入:gauge_suite(..., runner=my_runner)、Executor.new(..., probe=my_probe);runner(code, check_code, entry_point, timeout_s) -> (ok, message), 可以拿vacant.checks.run_python_check當地基。
E. 常見錯誤
| 錯 | 對 | 為什麼 |
|---|---|---|
給實驗 runner 設 VACANT_ENDPOINT=http://host:8765 |
VACANT_GAIN_API=http://host:8765/v1/chat/completions |
三個變數三種形狀。VACANT_GAIN_API(ops/gain/brain_cline.py:134)是完整路徑不是 base URL;VACANT_ENDPOINT(vacant/substrate.py:171)才是 base URL;CLI 走 VACANT_MCP_BASE+VACANT_MCP_MODEL+VACANT_MCP_API,而 VACANT_MCP_API 只能是 responses 或 openai。 |
用 contains/regex 當閘門 |
equals/json_schema/run_python |
前兩者適合探索,不足以撐起一份交付或授權 agent 啟動。 |
| 只記成功的嘗試 | 每一次都記,失敗優先 | 只有成功的鏈答不出「試了幾次」「有沒有交錯過」。 |
| 把驗得過的鏈當成「工作是對的」 | 當成「紀錄沒被改過」 | 誠實邊界 6:簽章指認金鑰,不指認真假。 |
| 把驗得過的鏈當成「沒有東西被刪掉」 | 公示鏈頭或會簽 | H-3:integrity ≠ completeness。 |
用 seq/筆數當截斷防護 |
外生的長度承諾(別人手上,或時間上早於截斷) | seq 就是筆數;截斷後的前綴每一筆仍自洽。 |
把 verify_run_receipts.py 的對帳當成獨立稽核 |
當成同源自檢 | 兩端同一個行程寫的:抓得到 bug,抓不到 malice。 |
| 說 Vacant 是「強制層」 | 「收件口」:沒有可驗證收據的交付不被接受 | 不滿足 complete mediation;全機唯一出口是部署層的事。 |
pip install vacant |
pip install vacant-network |
PyPI 上的 vacant 是別人的 DNS 工具。import 名仍是 vacant。 |
從輪子呼叫 Executor.new(id).attest(...) 然後接 ImportError |
注入 probe:Executor.new(id, probe=...) |
H-5,例外是 OpsRunnerUnavailable。 |
| 預算用完就把最後一份交出去 | 拒交,並且把拒交算成失敗 | 那等於把閘門唯一在做的事刪掉。 |
| 把隱藏測資原文貼回重試 prompt | 只回饋失敗的形狀 | 鐵律 A4。引用保留集會讓量測作廢。 |
| 說它是「信任層」 | 「可究責層」 | 見上。 |
引用 runs/_analysis_* 當原始資料 |
引用 runs/g_*/rows.jsonl |
那 136 個目錄是衍生物,引用它們等於把結論再餵給自己一次。 |
F. 機器可讀事實
{
"schema": "vacant.facts/1",
"package": {
"pypi_name": "vacant-network", "import_name": "vacant", "version": "0.7.0",
"requires_python": ">=3.11",
"runtime_dependencies": ["cryptography>=42", "mcp>=1.26,<2", "jsonschema>=4.21"],
"license": "MIT", "console_script": "vacant", "module_count": 50, "test_files": 78
},
"terminology": {
"use": "accountability",
"never_use": ["trust layer", "信任層"],
"reason": "Gambetta 1988 / Mayer 1995 put 'acting without monitoring' into the necessary conditions for trust; monitoring is the whole system."
},
"enforcement": {
"model": "receiving desk, not a mandatory wrapper and not an agent framework",
"framework_agnostic": "operates on the deliverable, not on how the agent ran",
"recommended_shapes": ["library", "mcp_tool", "controller"],
"not_recommended_for_integrators": "harness_owns_loop -- our experiment shape; requires writing your own agent loop",
"enforced_at": "acceptance time (a delivery without a verifiable receipt is not accepted)",
"not_enforced_at": "execution time",
"prior_art": ["in-toto", "SLSA", "Sigstore"],
"reference_monitor_Saltzer_Schroeder_1975": {
"tamper_proof": true, "small_enough_to_verify": true, "complete_mediation": false
},
"library": "voluntary", "mcp_tool": "advisory",
"controller": "binding on its own spawned subprocess only",
"harness_owns_loop": "binding",
"machine_wide": "requires container / ACL / egress policy (vacant/controller.py:7-8)"
},
"chain_guarantees": {
"integrity": true,
"completeness": false,
"truncation_attack": "not detected (Ma & Tsudik 2009, truncation/omission attack)",
"also_affects": "vacant/checkpoint.py:144-155 verify_checkpoint_chain",
"seq_does_not_help": "seq is already the count; a truncated prefix stays self-consistent",
"fix": "an exogenous length commitment -- held by someone else, or timestamped before the truncation"
},
"reconciliation": {
"tool": "ops/gain/replay/verify_run_receipts.py",
"same_origin": true,
"catches": "asymmetric omissions (bugs)",
"does_not_catch": "both sides omitting together (malice)",
"requires": "at least one end held by a party with different interests -- not done yet"
},
"banned_phrasings_for_results": [
"replication failed", "effect disappeared", "equivalent", "tied",
"majority supports", "replication stable", "improvement", "the loop is useless"
],
"headline": {
"gate_plus_resample_vs_one_shot_pp": {"12b_five_reps": [14.17, 18.33, 17.50, 19.17, 18.97], "27b_pooled": 7.89},
"loop_vs_one_shot": {"12b": "15/15 Holm, +17.5..+29.2 pp", "27b_pooled_pp": 4.67},
"loop_vs_resample": {"status": "not established", "12b_reps_pp": [5.83, 4.17, 0.83, 2.50, 4.31],
"12b_holm": "0/5", "27b_pooled_pp": -3.23, "27b_quotable_state": "RULED_OUT"},
"false_delivery_pp": {"ungated": 25.24, "gated": 14.80, "n": 836}
},
"retracted_claim": {
"was": "R532 V/GT 43/43 CLEAN (read as: across the run)",
"is": "the HMIX arm is 43/43 CLEAN; the classical seven arms, OFF and CONFORM included, were never scanned",
"cause": "ops/gain/harness_vgt_audit.py:746 skips any arm not in VARIANTS = (HPI, HOC, HMIX) at ops/gain/harness_arms.py:65",
"fix": "default changed to full audit: a green light obtained by forgetting a flag is the condition that let the hole exist",
"status": "retroactive sweep complete 2026-09-18T11:58:32+0800",
"evidence": "ops/gain/vgt_retro_audit_20260918.json",
"sweep": {
"scope": "v3 (ten arms, per-arm fail-closed)",
"runs": 179, "CLEAN": 165, "UNVERIFIABLE": 10, "VIOLATION": 4,
"needles_checked": 3486403,
"cited_batches_clean_per_arm": {
"R460": {"blocks": "6/6", "per_arm": {"OFF": 120, "CONFORM": 196, "OFF5": 602, "HPI": 187, "HOC": 283, "HMIX": 163}},
"R460R": {"blocks": "30/30", "per_arm": {"OFF": 608, "CONFORM": 1029, "OFF5": 3032, "HPI": 958, "HOC": 1488, "HMIX": 890}},
"R529": {"blocks": "37/37", "per_arm": {"OFF": 717, "CONFORM": 936, "HMIX": 844}},
"R532": {"blocks": "43/43", "per_arm": {"OFF": 836, "CONFORM": 1122, "HMIX": 1144}}
},
"newly_closed": "R532 CONFORM, 1122 records, first ever dynamic audit, zero violations; CONFORM is the subtrahend of delta_C, so the rival reading 'a CONFORM leak would push delta_C more negative, same direction as observed' is now ruled out",
"UNVERIFIABLE_detail": "all 10 are aborted runs with preflight only and zero arm records, so there is nothing to audit; UNVERIFIABLE is an honest verdict, neither clean nor dirty",
"VIOLATION_detail": {
"where": ["runs/g_r530_s1_1004_1", "runs/g_r530_s2_1003_1", "runs/g_r530_s2_1004_2", "runs/g_r530_s3_1003_1"],
"rule": "hidden_file_in_workspace",
"on_inspection": "the model's own same-named test files: sha256 differs from the pinned hidden tests, non-trivial-line overlap is zero or 2-3 lines of the form `got = solution.redact(line)`, and the same task yields entirely different content in s1 vs s2 (real GT would be identical across runs)",
"rule_assumption": "only the harness can place such a file; it did not anticipate a model naming its own test file test_hidden.py",
"tighten_the_rule": "UNRESOLVED"
}
},
"bounds": [
"CLEAN only guarantees that the literal repr of `hidden \\ visible` does not appear in harness-authored system/user text; semantic paraphrase, and whatever the excuse rules cover, are not detected",
"bank is inferred (bank_inference field), not recorded by the run; runs before R529 had no --record-bank-field",
"the closing analyzers still read the old evidence: analyze_r529.py vgt_gate() and analyze_r532.py gates_post() read vgt_v2_<block>.json whose per_arm is HMIX only; the standing substitute evidence is ops/gain/vgt_retro_audit_20260918.json and those two analyzers have not been updated",
"the 179 are not all clean: 4 VIOLATION and 10 UNVERIFIABLE remain"
],
"do_not_claim": "V/GT clean across all 179 archived runs; per-arm CLEAN is established only for R460, R460R, R529 and R532, and only with the scope and bounds above stated alongside"
},
"denominators": {
"HumanEval+": "156, not 164", "MBPP+": "371 of 378",
"LCB v2": 120, "LCB v3 medium": 135, "LCB v3 hard": 54
},
"full_facts": "AGENTS.md#9-machine-readable-facts"
}
實體展覽
唯一交付物=實體場地展覽。不產出畢業論文,也不投稿。 判斷任何工作要不要做,問的是 「觀眾走到展場前面時,這件事有沒有差別」。由此推出的硬約束:
- 秒級互動:真模型每題實測約 114 秒,現場等不起 ⇒ 展件跑機制模擬(
vacant/entrycost.py) 或預跑重放,畫面上必須明講「這是機制模擬」。 - 離線可跑、可無人值守:不假設網路、不假設有解說員。
- 先行研究仍然重要,但理由是不能對觀眾說錯話:脈衝攻擊 2005 年就有名字(Srivatsa)、 入場費沒用 2001 年就證明過(Friedman & Resnick)——我們是重新發現,不是新發現。
- 統計檢定力不必到發表標準:能讓外行一眼看懂的反事實對照比 p 值重要。
- 倫理是第一線需求不是附錄:Hollanek 2024 指出捐贈者同意不夠,互動者也必須能同意。
同一套
logbook/checkpoint機制也用來做展覽自己的同意/刪除證明。
展件:examples/receipt_viewer_multiparty.html——
內嵌三條完整簽章鏈(5,579 筆),瀏覽器內從創世驗到鏈頭、逐格重算裁決/指名/出貨,
零外部資源、file:// 直開。
研究紀律
- 預註冊:門檻、家族、分母、區間方法、四狀態與推翻條件都在資料之前寫死並凍結。
- Holm:家族是那一次複製之內的檢定;不准把五次丟進同一個 Holm。
- complete-case:
infra_void的列不回填,最壞界一起報。 - 複製:宣稱規則事前寫死,達不到就逐次照實列。 「先跑三次」與「只跑三次就下結論」是兩件事。
- 對抗式複驗:每條對外宣稱都送給一個獨立 agent,任務是推翻它。第一輪 12 條裡
3 條被推翻、3 條被判說太滿,全部留在
examples/verdicts.py裡, 舊的不刪。 - 事後修正也寫進紀錄:R532 的四狀態表沒有守方向、它自己的「更強模型」前提不成立—— 兩件都是看到資料之後才發現的,兩件都逐字留在 DECISION 檔裡(AMEND1/AMEND2), 判準不因結果不如預期而改。
- 被推翻的留著:一個宣稱可究責的系統若不能對自己可究責,主張就沒有內容。
文件索引
| 檔案 | 內容 |
|---|---|
AGENTS.md / llms.txt |
給 AI 的整合契約與索引 |
CHANGELOG.md |
版本變更(0.6.0 → 0.7.0 是不同的 codebase) |
docs/VACANT_COMPLETE_2026-09-12.md |
現況總表:數字的唯一入口 |
docs/BANKS_HOWTO.md |
怎麼自己重跑題庫 |
docs/HMIX_ARCHITECTURE_2026-09-11.md |
迴圈:六個零件、逐字 prompt、它做不到什麼 |
DECISION_20260912_R460R_FABLE_AUDIT_REPLICATIONS.md |
五次複製收官稽核 |
DECISION_20260912_R529_FABLE_AUDIT_CROSS_BANK.md |
跨題庫收官稽核 |
DECISION_20260917_R532_STRONGER_MODEL_PREREG.md |
27B 那一輪+AMEND1/AMEND2 |
ops/gain/r532/results_r532.json |
R532 每一個數字的可引用來源 |
SPEC_GAIN.md |
實驗規格:V/GT 分離、固定子集、臂的定義 |
runs/INDEX.md |
run 索引:哪些是證據、哪些是衍生物 |
examples/verdicts.py |
裁決的單一真相來源 |
CLAUDE.md |
工作約束:鐵律、口徑、後推項 |
引用
見 CITATION.cff。
@software{vacant_2026,
author = {cosmopig},
title = {Vacant: an accountability layer for AI agents},
year = {2026},
url = {https://github.com/cosmopig/Vacant}
}
授權
MIT。
Metadata
Release files for vacant-network 0.7.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| vacant_network-0.7.0.tar.gz | 879.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| vacant_network-0.7.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.4 MB
Release files / vacant_network-0.7.0.tar.gz
| Download URL | vacant_network-0.7.0.tar.gz |
|---|---|
| Size | 879.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e102a781827c11027c24d998a3345c12e092b095d8f9e58d2daa9186fb8d6a05
|
|
BLAKE2b-256 checksum How to use checksums |
42adf0c6118acf78cb0b3ceef60354b59c345b5b67f1008d03ee6469b13d985f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.
Transparency logRelease files / vacant_network-0.7.0-py3-none-any.whl
| Download URL | vacant_network-0.7.0-py3-none-any.whl |
|---|---|
| Size | 470.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
66add531d6e662f129ceac743bbe9ebc0d8c6f40fbafeb039d8feb39f71462f5
|
|
BLAKE2b-256 checksum How to use checksums |
76b26adc755ef3a9d9874f6f2d66bb7a1fd1eb52f3bf7b19f9552b2e17bb4b3d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.
Transparency log