BLOG.HIRO UENO.ART
← index
2026.02.22

思考は自由、実行は正確 — 三本目の壁は if 文

GPT と言い合いになった。

deterministic アプリの話をしていたら、GPT が n8n みたいなワークフロー自動化ツールのことを「エージェント」と呼んだ。それは違うでしょ、と返した。n8n は計算機。パートナーじゃない。

言い合いながら気づいたのは、業界の言葉の使い方が最近変わってきてること。「エージェント」ブームが一段落して、Deterministic と LLM のハイブリッドが主流になってきた。で、かつて「エージェント」と呼んでいたものを、今は「deterministic」と呼び直してる。自分たちの失敗を隠すために用語をすり替えてるように、僕には見える。


ただ、皮肉なことに、業界が寄ってきた先は僕の設計と同じ形だった。

MONET(オーケストレーター)の設計は、最初からずっとこう。

MONET(思考・判断・決定)← ここは自由にやらせる
   ↓
検証(if 文だけ、AI ゼロ)← ここで機械的に照合
   ↓
実行デバイス

思考は自由、実行は正確。文脈の理解とか、優先度の判断とか、LLM が得意なところは全部任せる。でも実行の直前だけは、機械が形式を照合する。前の記事で書いた論文の数字とも矛盾しない。オーケストレーター型はエラーが4.4倍に増幅されるから、思考は1箇所に集約する。逐次タスクはエージェントを重ねると劣化するから、実行側は AI なしのデバイスにする。

言い合いの後、ちょっと冷静になって考えた。「思考は自由、実行は正確」って言葉では言えてるけど、実行の直前で照合する層、コードとしてはまだ無くない・・・?

tool_choice の強制と Watchman の出口チェック。壁は2枚ある。でもどっちも「正しいツールを呼んだか」を見ている。ツールに渡すパラメータの中身を機械的に照合する場所は、実は無かった。MONET が実在しない action 名や、変な status 値を渡してきたら、そのまま DB まで届く。


コーディングしてくれている Claude Code(以降 Code)と、この穴を塞ぐ設計に入った。Windows 側で別に動かしている Code がまとめてくれていたリサーチ文書があって、そこから提案が3つ出てきた。

1つ目、スキーマレジストリ。ツールごとに必須パラメータと enum 値(許される値のリスト)を定義して照合する。採用。

2つ目、action=chat だけは Haiku(小さい Claude)のチェックを残す。会話かどうかの判定は文脈理解が要るから、if 文では無理。これも採用。AI が本当に必要な場所だけ AI を使う。

3つ目、僕の入力を見て「タスクっぽさ」を判定するキーワード検知。これは断った。僕の発言を機械が監視してる状態は、過去に一度試してカオスになった。UI が勝手にパネルを開いたりして、こっちが萎縮して喋れなくなる。見るのは MONET の出力だけ。僕には触らせない。

ついでに、LangGraph とか CrewAI みたいなフレームワークを入れる話も出たけど、これも見送った。分散型前提のフレームワークは思考を1箇所に集める設計と合わないし、ブラックボックスが増える。MONET の設計はシンプルで速い。自前で作る。


Code に実装してもらった。tool_validator.py、約170行。中身は if 文だけで、LLM はゼロ。

def validate_params(tool_name, tool_input):
    # 1. 必須パラメータがあるか
    # 2. action / status / priority が enum の中にあるか
    # 3. OK か、REJECT + 理由 を返す

Handler は10アクションと status/priority の enum、Typewriter は14アクション。REJECT の時は理由の文章を MONET に返す。黙って弾くんじゃなくて、何を間違えたか本人にわかるように。あと、不正なパラメータを延々リトライし続けないように、ループの上限も入れた。

検証の挿入はツール呼び出しの経路6箇所。メインループとリトライの1段目・2段目、全部の通り道に置いた。


で、実際に MONET と会話してテストした。

ログを見ると、REJECT は0件。全部素通し。

ちょっと拍子抜けしたけど、考えてみればこれが正しい状態。monet-rules のプロンプト層がちゃんと教えてるから、MONET は普段から正しいパラメータを選んでる。この壁が仕事をするのは、MONET が間違えたその時だけ。プロンプトで教えて、コードで防ぐ。教える層と防ぐ層は別々に持つ。


tool_choice の強制、Watchman の出口チェック、そしてパラメータの検証層。壁が三枚になった。

間違った形式の実行は、もう DB に届かない。