OpenAI 在 2026‑09‑10 发布的 Agents API,其 harness 控制回路被写成一份 152 NAND + 10 LATCH 的网表,于次日烧在链上。模型、工具、子 agent 都在链下;这块电路只负责"下一步该做什么",而且每一步都可以被任何人逐门重算。本页把链上那 1104 字节原样装进浏览器,你可以直接拨动观测位看它动作。
The harness control loop of OpenAI's Agents API (announced 2026‑09‑10) written as a 152 NAND + 10 LATCH netlist and burned on chain the next day. Model, tools and subagents stay off‑chain; this circuit only decides "what to do next", and every step can be recomputed gate by gate by anyone. This page loads the exact 1104 on‑chain bytes into your browser so you can flip observation bits and watch it act.
今天人们对 AI 的担忧,很少是担心某一次回答说错了,而是担心几家公司在闭门推进能自我改进的智能,而约束这些系统的规则只写在它们自己的服务器里:什么时候调工具、能派多少帮手、什么时候该停、谁来判定做对了。这些规则可以被悄悄改,外人只能相信公告。
Today's worry about AI is rarely that one answer was wrong. It is that a handful of companies are advancing self‑improving systems behind closed doors, while the rules that constrain those systems live only in their own servers: when to call a tool, how many helpers to spawn, when to stop, who decides the work was done right. Those rules can be changed quietly; outsiders can only trust the announcement.
模型本身我们暂时验证不了:27B 参数一次前向的证明成本比推理高几个数量级。但 agent 系统里,模型只是一个零件,其余全部是规则,而规则可以今天就钉死在公开的地方。把 API 烧成电路,就是把一家公司的办事流程刻成一块石碑:每条规则是几个开关,谁都能拿着石碑自己走一遍,看某次执行是不是照规矩来的。公司改不了石碑,也不能事后说"流程本来不是这样"。
The model itself we cannot verify yet: proving one forward pass of a 27B model costs orders of magnitude more than running it. But in an agent system the model is one part; everything else is rules, and rules can be pinned in public today. Burning an API into a circuit carves a company's procedure into a stone tablet: every rule is a handful of switches, anyone can walk the tablet themselves and check whether a given run followed it. The company cannot edit the tablet, nor claim afterwards that the procedure was never like that.
模型是办事员,电路是流程。办事员可以换,聪明或笨都行,但只能按石碑上的流程动:该叫工具就叫工具,失败只准重试一次,帮手最多三个,办得对才付钱。执行仍在链下,规则和判定在链上。这不解决"模型会不会失控",它解决的是更朴素的一层:约束模型的那套规则,不该只有制定者自己能看、能改。#283 是第一块石碑,刻的是 OpenAI 昨天发布的 Agents API。
The model is the clerk; the circuit is the procedure. The clerk can be swapped, clever or dull, but can only move as the tablet allows: call a tool when told, retry a failure once, at most three helpers, get paid only when the work checks out. Execution stays off chain; the rules and the judgment live on chain. This does not settle whether models can go out of control. It settles a plainer layer: the rules that constrain a model should not be visible and editable only by whoever wrote them. #283 is the first tablet, carved from the Agents API OpenAI published yesterday.
下面的逻辑不是重写的 JavaScript 版本,而是把 netlist 字节解码后按 TapeOut 的 step() 语义逐门求值。点左栏拨动观测位,按 Step 送入一拍;Play sample 会重放一段完整会话:tool search → MCP 并行调用 → 失败重试 → vault 授权 → 三个子 agent → 排队 → 压缩 → 完成付费。
This is not a JavaScript rewrite: the netlist bytes are decoded and evaluated gate by gate with TapeOut's step() semantics. Toggle observation bits on the left and press Step to feed one tick; Play sample replays a full session: tool search → parallel MCP call → failed call retried → vault grant → three subagents → a fourth queued → compaction → finish and pay.
| # | phase | inputs high | actions | state (hex) |
|---|
#283 证明了 API 可以烧成电路;#284 让它能接真实 agent 长期跑。同一天烧上链,接口从 16/15/10 扩到 16 输入 / 17 输出 / 23 LATCH,295 NAND。所有会把会话拖死的失败路径都有了单向熔断,每一次 abort 带 3 位原因码,付费按 judge 分档。下面的模拟器装的是 #284 的 2157 字节。
#283 showed an API can be burned into a circuit; #284 makes it usable by real agents for long sessions. Burned the same day, the interface grows from 16/15/10 to 16 in / 17 out / 23 LATCH, 295 NAND. Every failure path that could drag a session to death now has a one‑way fuse, every abort carries a 3‑bit reason code, and payment is graded by the judge. The simulator below holds #284's 2157 bytes.
| 项目item | #283 (v2) | #284 (v3) | abort 码abort code |
|---|---|---|---|
| 轮数上限(call_model 与 compact 都计)turn cap (call_model and compact both count) | 7 | 255 | 2 |
| 工具重试tool retries | 1 | 3 | 3 |
| 子 agent 并发concurrent subagents | 3 | 7 | — |
| 子 agent 失败(新输入 sub_fail;等待 3 拍无果视同失败)subagent failures (new input sub_fail; 3 waits with no report count as one) | 无none | 3 | 4 |
| 连续 compact 无效consecutive ineffective compacts | 活锁livelock | 3 | 6 |
| 连续 tool_searchconsecutive tool_search | 无none | 3 | 7 |
| 预算耗尽 / vault 拒绝budget_low / vault denied | abort | abort | 1 / 5 |
| 付费payment | 1 bit | judge 2 位 → pay 2 位(0 不付、1 部分、2 全额)judge 2 bits → pay 2 bits (0 none, 1 partial, 2 full) | — |
证明:2^39 个状态×输入无法穷举,改用 SAT。spec 经两条独立路径降级(结构哈希 517 门、yosys+ABC 289 门),kissat miter 判 UNSAT;封装后的链上字节再证一次。16 条安全性质逐条证明对全部 2^39 行成立:DONE 吸收、DONE 只经 abort/finish 进入、turns 单调、abort/finish/call 互斥、pay ⇒ finish∧judge、finish 时 pay 位等于 judge 位、四个计数器不越界、等待超时必触发、原因码非零 ⇔ abort。最后由 Grok 4.6 只看文字规格独立手写 Verilog,与参考 miter:前两轮各被抓出一个具体反例(搜索封顶时误发 tool_search;同拍 sub_req+sub_done+sub_fail 时计数错),第三轮 UNSAT。
Proofs: 2^39 state×input rows cannot be enumerated, so SAT. The spec is lowered by two independent paths (structural hashing, 517 gates; yosys+ABC, 289 gates) and kissat proves the miter UNSAT; the wrapped on‑chain bytes are proved again. Sixteen safety properties are proved for every one of the 2^39 rows: DONE absorbs, DONE only via abort/finish, turns monotone, abort/finish/call exclusive, pay ⇒ finish ∧ judge, pay bits equal judge bits on finish, four counters never exceed their caps, the wait timeout always fires, reason code ≠ 0 ⇔ abort. Finally Grok 4.6 wrote an independent Verilog from the prose spec alone and it was mitered against the reference: two concrete counterexamples in the first two rounds (tool_search emitted at the search cap; a counter slip when sub_req, sub_done and sub_fail arrive in one tick), UNSAT on the third.
接入:链上 step(284, state, inputs),state 3 字节(23 位)、inputs 2 字节(16 位)、outputs 3 字节(17 位)。Grok 4.6、Composer 2.5、GLM‑5.3 已各跑一局(5、5、7 拍),每拍链上 step() 与本地一致,均以 pay=2(全额)结束。仓库 agent_api_netlist/v5/(内部构建号 v5,公开版本号 v3)。
Driving it: on chain, step(284, state, inputs) with state 3 bytes (23 bits), inputs 2 bytes (16 bits), outputs 3 bytes (17 bits). Grok 4.6, Composer 2.5 and GLM‑5.3 have each run a session (5, 5 and 7 ticks); every tick matched on‑chain step() and all ended with pay=2 (full). Repo dir agent_api_netlist/v5/ (internal build v5, public version v3).
| # | phase | inputs high | actions | state (hex) |
|---|
三组位。观测位由 harness 每拍填入,动作位告诉 harness 下一拍做什么,寄存器由电路自己维护。位序即字节序:pack() 把第 i 位放在第 i/8 字节的第 i%8 位。
Three groups of bits. The harness fills the observation bits every tick, the action bits tell the harness what to do next, and the register is maintained by the circuit itself. Bit order is byte order: pack() puts bit i at byte i/8, bit i%8.
| in | name | meaning |
|---|---|---|
| 0 | model_done | 模型返回最终答案(end_turn)model returned a final answer (end_turn) |
| 1 | model_tool | 模型请求调用工具model requested a tool call |
| 2,3 | tool_type1,0 | 00 function · 01 MCP · 1x builtin |
| 4 | tool_known | 工具定义已加载(否则先 tool_search)tool definition already loaded (else tool_search first) |
| 5 | tool_batch | 请求程序化并行调用programmatic parallel call requested |
| 6 | tool_ok | 上一次工具调用成功last tool call succeeded |
| 7 | ctx_high | 上下文接近上限context near its limit |
| 8 | budget_low | token 或轮数预算耗尽token or turn budget exhausted |
| 9 | sub_req | 模型要派子 agentmodel wants to delegate to a subagent |
| 10 | sub_done | 有一个子 agent 完成one subagent finished |
| 11 | secret_req | 模型需要 vault 里的密钥model needs a secret from the vault |
| 12 | vault_ok | vault 允许vault grants it |
| 13 | judge_ok | 验证器接受最终产物verifier accepted the final output |
| 14,15 | env1,0 | 环境:00 hosted · 01 own infra · 10 partner sandboxenvironment: 00 hosted · 01 own infra · 10 partner sandbox |
| out | name | meaning |
|---|---|---|
| 0 | call_model | 再调一次模型(计入 turns)call the model again (counts a turn) |
| 1–3 | run_fn / run_mcp / run_builtin | 按类型执行工具run the tool, routed by type |
| 4 | tool_search | 先加载工具定义load tool definitions first |
| 5 | tool_parallel | 与 run_* 同置:并行批调用set alongside run_*: parallel batch call |
| 6 | compact | 压缩上下文compact the context |
| 7 / 8 | spawn_sub / wait_sub | 派子 agent / 已满 3 个则等待spawn a subagent / wait when 3 are running |
| 9 | vault_grant | 放行密钥release the secret |
| 10 / 11 | finish / pay | 结束;pay 仅当 judge_okfinish; pay only if judge_ok |
| 12 | abort | 预算耗尽、vault 拒绝或二次工具失败budget exhausted, vault refused, or a second tool failure |
| 13,14 | env_sel1,0 | 锁存的环境选择latched environment choice |
| state | name | meaning |
|---|---|---|
| 0,1 | phase1,0 | 00 MODEL · 01 TOOL · 10 SUB · 11 DONE |
| 2 | err | 本轮已用掉一次工具重试the one tool retry has been used |
| 3,4 | subcnt1,0 | 运行中的子 agent 数,上限 3subagents running, max 3 |
| 5–7 | turns2..0 | call_model 次数,到 7 即 abortcall_model count; abort at 7 |
| 8,9 | envl1,0 | 首拍锁存的 envenv latched on the first tick |
把你的 harness 循环改成"观测 → 问电路 → 执行动作"。你不需要理解规则,只需要正确填 16 个观测位并执行返回的动作位。电路在链上,也可以在本地用同一份字节跑;两者必须一致,否则就是你的实现错了。
Turn your harness loop into "observe → ask the circuit → execute the actions". You do not need to understand the rules; fill the 16 observation bits correctly and carry out the action bits that come back. The circuit lives on chain and can also be run locally from the same bytes; the two must agree, otherwise your implementation is wrong.
# 1. local evaluation / 本地求值 (bit-identical to on-chain step())
gates = decode(nl_bytes) # op 0x00 = NAND(a,b) 两个 3 字节 wire id;op 0x01 = LATCH(d)
wires = [0, 1] + inputs16 # wire 0/1 常量,2..17 输入,之后按 gate 顺序
for g in gates:
wires.append(1 - (wires[g[1]] & wires[g[2]]) if g[0]==0 else state[next_latch])
new_state = [wires[g[1]] for g in gates if g[0]==1]
actions = wires[-15:]
# 2. on-chain evaluation / 链上求值 (BSC, CPU 0x6Fb4089e7Cbaa9660Fd11056274Cbd8117EE5B38)
step(uint256 circuitId=283, bytes state, bytes inputs) -> (bytes newState, bytes outputs)
# state 2 bytes (10 bit), inputs 2 bytes (16 bit), outputs 2 bytes (15 bit); pack: bit i -> byte i//8, bit i%8
# note: circuits with LATCH revert on eval(): "has latch: use step" / 带 LATCH 的电路必须用 step()
# 3. one real on-chain vector from the post-burn check / 一条真实链上向量
state=0x8903 inputs=0x2ae5 -> newState=0x5003 outputs=0x0160
验证它、复现它或在它之上再建一层,所需文件都在仓库 circuits/_scratch/agent_api_netlist/v2/:spec.py 是独立于 Verilog 的参考语义;top.v 由 Cursor 上的 Grok 4.6 手写;compress.py 做 ABC 压缩、精简封装和 2^26 穷举;burn_agent_v2.py 是钱包桥烧录与链上复核脚本。
Everything needed to verify it, reproduce it or build on top of it is in the repo under circuits/_scratch/agent_api_netlist/v2/: spec.py is the reference semantics, independent of the Verilog; top.v was hand-written by Grok 4.6 in Cursor; compress.py does the ABC compression, the lean wrap and the exhaustive 2^26 check; burn_agent_v2.py is the wallet-bridge burn and on-chain verification script.
烧完当天用 drive283.py 让几个不同的模型当"办事员"跑同一个任务(算 1000 以内素数和,judge 判 76127)。每一拍都先从链上取回 1104 字节,调链上 step(),并与本地求值逐位比对。
On burn day, drive283.py put several different models in the clerk's seat for the same task (sum of primes below 1000; the judge expects 76127). Every tick fetched the 1104 bytes from chain, called on‑chain step() and compared it bit for bit with the local evaluation.
| 办事员clerk | 拍数ticks | 路径path | 结果verdict | 备注note |
|---|---|---|---|---|
| Grok 4.6 (Cursor) | 5 | call → tool_search → run_fn → call → finish+pay | PAID | 两次模型调用two model calls |
| Composer 2.5 (Cursor) | 5 | 同上same | PAID | 首局因 harness 的 python 工具作用域 bug 报错,电路给一次重试后 abort;修好工具重跑即付费first run hit a harness bug in the python tool; the circuit allowed one retry then aborted; fixed, rerun paid |
| Composer 2.5 首局first run | 4 | call → tool_search → run_fn → run_fn(retry) → abort | ABORT | "只准重试一次"不在 harness 代码里,在链上门里"one retry only" is not in the harness code; it is in the gates |
| GPT‑5.6 Sol / Opus 5 (Cursor) | 2 | call → finish | UNPAID | 当月额度用完返回空串;harness 现改为记"本拍无动作",交给电路的轮数上限处理monthly quota exhausted, empty reply; the harness now records "no move" and lets the circuit's turn cap deal with it |
| GLM‑5.3 (zhipu) | 5 | 同 Groksame as Grok | PAID | 额度重置后立即跑,两次模型调用;OpenAI 兼容端点接入ran right after the usage cap reset; two model calls, via an OpenAI‑compatible endpoint |
v2 的硬伤是 7 轮上限:turns 只有 3 位,第 7 次 call_model 就 abort,演示够用,真实会话不够。v3 计划:turns 8 位、子 agent 上限 7、工具重试 3 次,其余不变,约多 20 门,同一流程再烧一块。
v2's hard limit is the 7‑turn cap: turns has 3 bits, so the seventh call_model aborts. Fine for a demo, not for real sessions. v3 plan: 8‑bit turns, up to 7 subagents, 3 tool retries, everything else unchanged, about 20 more gates, burned through the same pipeline.
然后是三件工程活:一个常驻网关(POST /step,按 session 保存那 2 字节状态);链上调用策略(本地求值同一份字节,每 N 拍抽一拍打链上比对,需要审计时把整局的哈希链写进一笔交易);接入现有 agent(Claude Code 用 PreToolUse 和 Stop 钩子,电路没给 run_* 位就拒绝工具调用;pi、Codex、自写循环用几十行适配层)。
Then three pieces of engineering: a resident gateway (POST /step, keeping the 2 state bytes per session); an on‑chain policy (evaluate the same bytes locally, spot‑check one tick in N on chain, and write the run's hash chain in one transaction when an audit is needed); and adapters for existing agents (Claude Code via PreToolUse and Stop hooks, refusing tool calls the circuit did not grant; pi, Codex or a hand‑written loop via a few dozen lines).
它不能阻止一个不接它的 agent,接了的也可以谎报观测位。它保证的是"声称按 #283 跑的会话,第三方能逐拍复核";要让谎报有代价,把结算绑在 pay 位上。
It cannot stop an agent that does not connect to it, and a connected one can still lie about its observations. What it guarantees is that a session claiming to follow #283 can be checked tick by tick by anyone; to make lying costly, bind settlement to the pay bit.