avatar
蕭富云モバイルアプリ開発者 · Android & Flutter

難しいのは分母のほう — エージェントが規律を守っているかをどう測るか

2026-08-31 · 14 min read

補足: 本記事は AI(Claude)が私の最初のメモと着想をもとに仕上げたものです。

前回の「待つのは一つだけ」は、一つのことを認めて終わっていた。モデルが八段すべてを守りきる保証は出せない。だから次にやることはルールを増やすことではなく、どれだけ守られているかを測ることだ、と。

今回はその測定から得たことを書く。結論を先に言うと、点数をつけるのは簡単で、難しいのは分母を決めることだ。 分母を三度直し、点数は 11% から 24% まで上がった。その間、モデルの振る舞いは一度も変わっていない。

測り方そのものは素朴だ

どのルールにも、セッションの記録の中に見つけられる痕跡がある。ゲートのブロックが書かれたか、審査の判決行が現れたか、テスト実行の結果が error を返したか。だから測り方はこうなる——すべてのセッションの記録を引き出し、区間に切り、区間ごとに問う。「このルールはここで発火すべきだったか。発火したか」。

分子は「発火した」区間の数、分母は「発火すべきだった」区間の数だ。分子はまず間違えない。痕跡は痕跡だからだ。落とし穴はすべて分母にある。

一度目の修正:作品でない区間を外す

最初の版は、何かしら手を動かした区間をすべて「審査対象となる」分母に数えていた。よく見ると、163 の区間は git commitmkdirinstall.shtee の類しか走らせていない——分母の三割を占め、遵守率は 1% だった。

1% で当然だ。それらの区間には審査する作品がない。ルールはそもそもそこで発火するはずのものではなかった。外したところ、遵守率は 11% から 15% に動いた。

二度目の修正:区間の切り方が間違っていた

雑音を取り除いてもまだ何かがおかしい。審査対象の 390 の区間のうち 58 で、判決行は区間の中にはある——ただし最後の編集の後にはない。

追っていくと、区間の境界が間違っていた。隣り合う二つの仕事が一つの区間に畳まれている。一つ目は完了し、審査され、判決が書かれた。続いて二つ目が編集を始め、審査なしで終わった。一つの区間として読めば、最後の編集の後に判決はないから、落第と採点される。

だがこの読みはまったく逆さまだ。審査を受けた一つ目が減点され、審査を受けていない二つ目は同じ区間に隠れて、一度も数えられていない。

直し方は「判決が書かれた瞬間に区間を閉じる」。これで遵守率は 15% から 24% に動いた。

点数は三度上がり、振る舞いは一度も変わらない

三つの数字を並べてみる。

分母遵守率モデルの振る舞い
コマンドのみの区間を含める11%
コマンドのみの区間を外す15%変わらず
判決が書かれた時点で区間を閉じる24%やはり変わらず

毎回、分母が真に近づいただけで、規律が良くなったわけではない。これがこの記事の背骨だ。「エージェントの遵守率」という数字を見たら、まずその分母が誰で、いつ変えられたかを問うこと。 分母の履歴を持たない点数は、先月と比べられない。

メインの流れに自己採点をさせない

二つ目の落とし穴は分母の話ではない。誰が誰を採点しているか、の話だ。

ルールはこう書いてある。審査で指摘が出たらまず直す。直してから完了報告を書き、最後の行に Compliance Review: PASS と記す。ルール自体は正しい。だが測定が読むのがその行なら、結果は 100% にしかなりえない——その行を書くのは、審査される側であるメインの流れ自身であり、メインの流れは自分が通ったと思えるまで書かないからだ。

だから判決は、審査者自身の記録から読むしかない。派遣された、読み取り専用の、メインの流れと記憶を共有しないエージェントが実際に何を言ったか。メインの行は主張であり、審査者の記録が証拠だ。

同じ落とし穴は、ゲートの受入率にも姿を変えて現れる。ゲートが止まって人を待つとき、ユーザーはいくつかの選択肢から一つを選んで答える。「一つ選んだ」を常に「計画を受け入れた」と数えるなら、計画がどれほど雑でも、分母の十分の一は構造的に 100% に張り付く。

構造的であることと確定的であることは違う

判定を二つの層に分けた。構造的な判定は tool_result の error フラグだけを読み、出力文字列はいっさい解析しない——だから書式の変更で壊れることはない。頼もしく聞こえる。

そこへ前回の落とし穴が戻ってきた。今度は数字を携えて。

flutter analyze 2>&1 | tail -6

set -o pipefail がなければ、shell が返すのは tail の終了ステータスだ。記録を横断して見ると、pipe を通した lint 実行の 99% が「クリーン」を返していた。linter が何を捕まえていようがだ。これらを通過と数えるわけにはいかない。「測定不能」という別枠を設け、その行の下に印字し、どちら側にも数えないことにした。

同じ行から、小さいものもいくつか出てきた。

構造的とは「モデルの読みに頼らない」という意味であって、「間違えない」という意味ではない。

分からなければ推測しない

三つ目の落とし穴は、測定が確信を持てないときにどうするか、だ。私の答えは、どちら側にも数えず、別に記録すること。

データを捨てるのは痛い。だが一つの誤った帰属は、行全体の数字を間違った方向へ傾ける。しかもそれは見えない。

二層の信頼度を平均しない

前述の通り、判定は二層ある。構造的な層は終了ステータスを読む。発見的な層は記録の一区間をモデルに渡して意図を判断させる(セッションとターン単位でキャッシュし、二度は訊かない)。

二つの信頼度は大きく違う。だから見出しの数字は二つに分けて出し、一つの平均で確からしさを装うことはしない。さらにモデル別——Opus、Sonnet、Fable をそれぞれ一列——にも切っている。「モデルはルールを忘れる」はもともとモデルについての主張であって、混ぜてしまえば差が見えなくなるからだ。

遵守率のダッシュボード:見出しの数字、モデル別の折れ線、そして各行の下に別枠で記された放棄件数と帰属不明件数

2026-08-31 時点のダッシュボード。各行の下の小さな文字が、分母から外されたものだ。コンプライアンス審査の行は、本記事を書く時点で 37% まで来ている。

正直な但し書き

先に三つ。

一行で締める

エージェントが規律を守っているかを知りたいなら、まず点数を見ないこと。先に三つ問う。この点数の分母は誰か、誰が誰を採点しているか、分からないときそれはどうするか。三つとも答えが出て初めて、その数字は意味を持ちはじめる。