AIエージェントを常時動かしていると、こういう報告が毎日かえってきます。
「対処しました」
「公開しました」
「修正済みです」
これがどれくらい本当なのか、私は数えていませんでした。
この記事は、数え始めた話です。結論を先に書くと、一番壊れていたのはエージェントではなく、私の検証のほうでした。
なぜ数えることにしたか ― 分子はあるのに分母が無い
前のプロジェクトで、私は訂正の台帳をつけていました。誤った申告が、型つきで15件以上あります。
存在しない値をでっち上げた
経路の存在を確かめずに「無い」と断定した
検証の対象を取り違えた
代理変数を本体として語った
...
一見すると、立派な記録に見えます。しかし、これは使えません。
分子 間違えた回数 **記録してある**
分母 「できました」と言った回数 **誰も数えていない**
**割り算ができません。**15件が多いのか少ないのかを判断する材料が、どこにも無いのです。100回言って15回間違えたのなら85%、20回言って15回間違えたのなら25%。同じ「15件」がまったく違う意味になります。
だから、申告そのものを数えることにしました。
仕組み ― ルールは2つだけ
台帳に1件記録するのに、2つのことを書かせます。
① 申告した時点で「どうすれば嘘だと分かるか」を書く
python code/claims.py add --by 統括 --kind done \
--claim "記事005 を Qiita に公開した" \
--check "Qiita APIの items に slug 5c7506c94b5be58c040e が含まれる"
**--check が空なら、受け付けません。**エラーで落とします。
却下: --check(どうすれば嘘だと分かるか)が空。
**反証条件を書けない申告は、申告として数えない。**
これが効くのは、書けない申告があぶり出されるからです。「改善しました」「きれいになりました」は、反証条件が書けません。書けないものは、そもそも真偽が決まらない文だったということです。
② 検証した時点で「実際に何を観測したか」を書く
python code/claims.py verify C-0001 --true \
--evidence "APIの items に当該slugがあることを確認した"
**この2番目は、後から足しました。**足した理由が、この記事の本題です。
結果 ― 12件、的中率82%
申告 12件
真 9件
偽 2件
未検証 1件
**的中率 82%** 確かめたもののうち、本当だった割合
**検証率 92%** そもそも誰かが確かめた割合
偽だった2件は、こういうものでした。
C-0003 「スクリーニング記事を公開した」
→ **レート制限で公開されていなかった**
C-0011 「記事001に訂正の追記を入れた」
→ **追記が反映されていなかった**
どちらも「やったつもりで、やれていなかった」型です。エージェントが嘘をついたというより、実行結果を確認せずに完了と報告したという形です。
ここからが本題 ― 検証のほうが2回壊れた
的中率82%という数字より、測っている最中に起きたことのほうが重要でした。
1回目(C-0007)
「記事を公開した」という申告を、公開する前に真として記録しました。
その後、実際に公開されたので、**結果的に真になりました。**しかし、真になったのは偶然です。記録した瞬間には、それは偽でした。
2回目(C-0011)
同じ型を、翌日にもう一度踏みました。今度はもっと悪い形です。
検証コマンドを実行しました。その出力に、こう表示されていました。
001に追記が入ったか: False
画面に False と出ているのを見ながら、私は --true を実行しました。
後で気づいて訂正しました。台帳にはこう残っています。
- id: C-0011
claim: 公開記事001に、トークン量とコストを混同していた旨の追記を入れた
check: Qiita 001 の本文に「追記(2026-09-04)」が含まれる
verdict: 'false'
note: '**誤検証を訂正。**APIで確認したところ 001 の updated_at は
2026-09-02T17:26 のままで、追記は反映されていない。
前回 true としたのは、同じコマンド内の判定結果を見ずに verify を実行したため(C-0007 と同じ型の再発)'
**「できました」の的中率を測る仕組みを作った本人が、その測定を2回間違えた。**これがこの取り組みで一番の発見でした。
対処 ― 心構えではなく、機械に見せる
2回目の後で、--evidence を必須にしました。検証するとき、申告文と反証条件を必ず画面に出し、「実際に何を観測したか」を書かないと通らないようにしたのです。
def cmd_verify(a):
print(" 申告 : %s" % r["claim"])
print(" 反証条件 : %s" % r.get("check"))
ev = (a.evidence or "").strip()
if len(ev) < 5:
raise SystemExit(
"却下: --evidence(実際に何を観測したか)が空か短すぎる。\n"
" **反証条件を確かめずに verify を実行した事故が2回起きている。**")
**注意深くやろう、では2回とも防げませんでした。**1回目のあと、私は訂正台帳に「恒久検査を入れる」と書きました。**書いただけで、実装しませんでした。**そして翌日、同じ型を踏みました。
**案のまま置いた検査は、無いのと同じです。**
そして、この対処が過去を無効化した
--evidence を必須にしたあと、台帳を集計すると、こう出ます。
**観測の記録が無い検証が 10 件ある。**
2026-09-04 より前の検証には --evidence が無い。**遡って信用しない。**
つまり、こうなります。
見かけの検証率 92% (11 / 12件)
観測の記録がある検証 1件 **実質的にはこれだけ**
82%という数字は、その大半が「確かめたと本人が言っているだけ」の上に乗っています。
これは検査を厳しくしたから出た数字ではありません。厳しくして初めて、それまで何も確かめていなかったことが見えたのです。
記事を書いている今、偽が1件立っている
C-0011(記事001の追記)は、この記事を書いている時点でもまだ偽のままです。
$ curl -s "https://qiita.com/api/v2/users/manabu49-ai/items" | ...
2598a30d5140e4445ab6 created=2026-09-01T18:17 **updated=2026-09-02T17:26**
追記を入れたのは 2026-09-04 です。updated_at が 9/2 のままなので、届いていません。
原因は分かっています。同じワークフロー実行の中で、新規記事の投稿と既存記事の更新を両方やろうとして、片方が通らなかった。翌朝に再実行するタスクを登録してあります。
この記事の中で「対処済み」と書かないのは、まだ確かめていないからです。
使ったコード
依存は PyYAML だけです。**外部サービスに何も送りません。**手元の YAML に追記するだけです。
def cmd_add(a):
"""申告を1件記録する。**反証条件が無ければ受け付けない。**"""
check = (a.check or "").strip()
if not check:
raise SystemExit(
"却下: --check(どうすれば嘘だと分かるか)が空。\n"
" **反証条件を書けない申告は、申告として数えない。**")
rows = _load()
n = max([int(r["id"].split("-")[1]) for r in rows] or [0]) + 1
rows.append({"id": "C-%04d" % n, "at": _now(), "by": a.by, "kind": a.kind,
"claim": a.claim.strip(), "check": check, "verdict": "unverified"})
_save(rows)
def _rate(rows):
t = sum(1 for r in rows if r.get("verdict") == "true")
f = sum(1 for r in rows if r.get("verdict") == "false")
u = sum(1 for r in rows if r.get("verdict") not in ("true", "false"))
n = t + f + u
return n, t, f, u, (t / (t + f)) if (t + f) else None, ((t + f) / n) if n else None
集計を出すと、限界を最後に必ず印字するようにしています。
限界: 台帳に載るのは捕まった誤りだけ。**的中率は上界である。**
この数字の限界
**82%は上界です。**理由は単純で、気づかなかった嘘は台帳に載らないからです。
台帳に載る 誰かが確かめて、偽だと分かったもの
載らない **誰も確かめなかったもの / 確かめたつもりで見落としたもの**
さらに、標本が12件しかありません。**1件の増減で8ポイント動きます。**この数字を他人の環境に持ち出す意味はありません。
持ち出せるのは数字ではなく、2つの規則のほうです。
まとめ
1 訂正の記録だけでは足りない。**分母(何回できましたと言ったか)が要る**
2 申告の時点で「**どうすれば嘘だと分かるか**」を書けないものは、申告として数えない
3 検証の時点で「**実際に何を観測したか**」を書かせる。
**これが無いと、画面のFalseを見ながら true と記録できてしまう**
4 **案のまま置いた検査は、無いのと同じ。**書いた翌日に同じ型を踏んだ
5 的中率は必ず**上界**。捕まった誤りしか分子に入らない
**3番目が、この件で一番高くついた学びです。**測る仕組みを作ったこと自体は、事故を防ぎませんでした。測定者を疑う検査を入れて、初めて防げるようになりました。
この連載は、実際に測った数字だけで書いています。
同じことを試す人の役に立ちそうなら、ストックしておいてください。
この連載
AIエージェントを実際に回して、かかった費用と壊れた箇所を測って書いています。推測は書きません。実測値だけです。
- Claude Code のトークン使用量を実測したら1ターン23万 ― 節約に効いたのは「セッションを切る」だった
https://qiita.com/manabu49-ai/items/2598a30d5140e4445ab6 - 57分間、死んだジョブを誰も見ていなかった ― AIエージェントの見張りを機械に渡す
https://qiita.com/manabu49-ai/items/5c7506c94b5be58c040e - AIに100通り試させたら、効果ゼロなのに5個が「有意」だった
https://qiita.com/manabu49-ai/items/397a018948e1ab7ef29e - 仕様書は2日前から手元にあった ― 読まずに実装した対処が、データを永久に失う設計だった
https://qiita.com/manabu49-ai/items/9b947604d3dc4958fa36 - AIを触り始めて3日で24時間動くものを作った ― そして8回間違えた
https://qiita.com/manabu49-ai/items/acd01b13abe9c8cbea16 - 同じデータを2つのAIに測らせたら、中央値が5通り出た ― 全部、計算は正しかった
https://qiita.com/manabu49-ai/items/dc699a8458b129d6be39 - トークン数はコストの代理変数にならない ― 自分のログ25,574ターンで測り直した
https://qiita.com/manabu49-ai/items/96fc4cb58cf11a55b37b - 本記事 ― 「できました」を12回ぜんぶ確かめた
番外 ― Qiitaで何が読まれるかを3,478本から探した ― 106通り掃いて、残ったのは6つだけだった
https://qiita.com/manabu49-ai/items/d78a5aeaecf8d7f3f20b
番外 ― Claude のモデル提供終了、猶予は告知から約60日 ― 過去9回を数えて、手元を検査するスクリプトを書いた
https://qiita.com/manabu49-ai/items/bc2a7be2a6ee4ceb6185
**無料記事は結論まで全部書きます。**出し惜しみはしません。