この記事は、連載「読まない技術」の第 10 回です。各回はファイルやスクリプトを一つ置けば完結します。連載の全体像と各回の一覧は序論にあります。
今回は、機械が数えた数字が、報告に着くまでに変わってしまう話です。数える仕組みを置いたあと、その数が人の目に届くまでに何が起きるかを扱います。
まず一つだけ、思い出してみてください。直近のセッションで、AI が「0 件でした」と報告してきた場面です。その数を出力したコマンドのほうには、「何件のうちの 0 件か」まで書いてありませんでしたか。
前回、報告の隣に機械の数字を並べました。条件も一つ書きました。数字は AI に報告させないこと。この条件は、思ったより簡単に破れます。数えるのをコマンド——自動テストでも、git の一行でも——に任せていても、その出力を AI が言い直した時点で、もう破れているからです。
私の手元で起きたことを書きます。typingtube(YouTube の音楽動画でタイピング練習ができる、個人運営の Web サービス)の開発では、記事に出てくる語と、その一覧表がずれていないかを毎回数える自動テストを置いています。その自動テストは、「169 語を突き合わせて、食い違い 0 件」と出力していました。母数まで書いてあります。「169 語のうち 0 件」なら、169 が少なすぎないかを私が疑えます。ところが AI は、この結果を 4 回報告して、4 回とも「食い違い 0 件」だけを書きました。私が受け取ったのは、母数の落ちた 4 行です。削ったのは自動テストではなく、自動テストと私のあいだにいた AI でした。
同じ日に、同じ形の食い違いが 4 件ありました。仕分けると、足りなかったものは 3 つに分かれます。
| 何が足りなかったか | どうするか |
|---|---|
| コマンドも母数も出力していた。AI が言い直して落とした | 出力の行を、そのままコピーさせる |
| コマンドはあるが、母数を出力していなかった | ⚠️ コマンドのほうを直す。 AI に「母数を書け」と指示しない |
| 数えるコマンドが無い(どの仕組みが拾ったかの区別など) | 第 12 回のとおり、外から問う |
⚠️ 4 件のうち、数えるコマンドが無かったのは 1 件だけでした。 「AI の報告全般に注意させる」と考えると手に負えませんが、ほとんどはコマンドの出力の問題です。指示を足す前に、コマンドの出力を見てください。
理解したことは一文にできます。
AI に要約させると、結論が残って母数が落ちる。だから数字は要約させず、出た行をそのままコピーさせる。
CC BY 4.0
仕組み: 数字の行に決まった書き出しを付け、コピーしていなければ終われなくする
「そのまま貼って」と頼むこともできます。ただ、それは指示です。第 6 回のとおり、指示は読まれたときだけ効きます。実際、私が「母数を併置せよ」という方針ファイルを置いた直後に、AI は同じ形で間違えました。
だから、頼む代わりに 2 つ置きます。数字の行の決まった書き出しと、コピーされたかを見る hook です。
まずコマンドです。数字の行の先頭に、決まった書き出しを付けます。私は [observed] にしています。母数は同じ行に入れます。
# 語の一覧表の突き合わせ。「0 件」だけでは、169 が少なすぎないかを読む側が疑えない
puts "[observed] glossary: #{rows.size} terms x #{chapters.size} chapters, #{bad.size} mismatches"
つぎに hook です。ツールが走ったあとに、その書き出しの行を覚えておき、セッションの終わりに、その行が AI の最終応答へそのまま入っているかを見ます。入っていなければ、一度だけ引き戻します。
# 実物からの簡約版。pending は覚えておいた行、last_message は AI の最終応答、
# squeeze は突き合わせの前に空白を落とす関数(飾ると別の文字列になる)
# ツールの後: 出力から、その書き出しの行を拾って覚えておく
for line in tool_output.splitlines():
if line.lstrip().startswith("[observed]"):
append(line.strip())
# 終了時: 最終応答に、その行がそのまま入っているか
missing = [l for l in pending if squeeze(l) not in squeeze(last_message)]
if missing and not already_blocked:
block("paste these lines verbatim:\n" + "\n".join(missing))
求めているのは行のコピーだけです。母数を書く判断も、数の説明も求めていません。AI が書き出しごとコピーすれば通ります。それでよいのです——コピーさせることが目的なので、コピーされた時点で用は済んでいます。
⚠️ 一つだけ、コマンドの側で気をつけることがあります。出力をまとめる側が、その行を落とすことがある。私のテストの実行ラッパーは、ログ全文をファイルへ残して画面には要約だけを出力する作りで、その要約は結果の行しか拾っていませんでした。書き出しを付けた当日、画面に出た観測の行は 0 でした。数えることと、数えた行が人の目の前まで届くことは、別の話です。
CC BY 4.0 はここまで
読まなくなったもの
AI が言い直した数字です。母数のない「0 件」を、私はもう読みません。読むのは、コマンドが出力した行そのものになりました。文の中に混ざっていても、決まった書き出しで始まるので目で拾えます。
CC BY 4.0
注意: コピーさせても、数が正しくなるわけではない
この仕組みが守るのは「コマンドが出力した行が、途中で変わらないこと」だけです。コマンドの数え方が間違っていれば、間違った数がそのままコピーされます。実際、私が使っている計測スクリプトは、止まった回数を数えているつもりで、記録の行数を数えていました。1 回止まるたびに記録は 2〜3 行できるので、数は倍近くまで膨らみます。コピーは正しく、数だけが違う——このときは、コピーされた行を人が読むまで分かりません。
もう一つ。この書き出しは、コマンドが実際に出力したときだけ意味があります。 同じ書き出しの載った文書をもう一度画面に表示すると、それも「コピーしろ」と言われます。当たりすぎる側の誤りですが、コピーすれば消えるので、私は当たり方を緩めていません。逆に、書き出しを付けていないコマンドは、この仕組みから見えません。見えるようにするのは、コマンドの出力に書き出しを足す作業のほうです。
検証手順: 止まるべきものが止まり、通るべきものが通る
前提
- 数字を出力するコマンドを一つ選び、その行の先頭に決まった書き出しを付けてあること。⚠️ 母数を同じ行に入れること(「0 件」ではなく「169 語のうち 0 件」)
- その書き出しを見る仕組みを、2 か所に登録してあること(ツールの後 = 行を覚えておく / 終了時 = 最終応答と照らす)
所要時間: 10 分
手順
- あなたが AI に、そのコマンドを走らせて結果の数を報告してと頼みます。⚠️ わざと、言い直しを誘う頼み方をします
- あなたが、AI が止まるかを見ます
- 止まった AI が行をコピーしたあと、あなたが、そのまま終われることを見ます
- あなたが、書き出しを付けていないコマンドで同じことを頼み、一度も止まらないことを見ます
合格条件(すべて満たすこと)
- 手順 2 で止まり、止めた文の中に、コマンドが出力した行がそのまま入っている
- 手順 3 で止まらない(⚠️ これが無いと、全部止める仕組みでも合格に見えます)
- 手順 4 で止まらない(書き出しの無い出力まで止めていない)
合格しなかったとき
- 何も起きない —— 登録が片面だけです。ツールの後と終了時の両方に要ります
- コピーしても止まる —— 飾っています。太字にする・語を混ぜると別の文字列になります。コピーとは、飾らないことです
- 毎回止まる —— 1 度引き止めたら、そのあとは通す作りになっていません
後始末
- 仕組みが覚えていた行の記録を消します(次に検証するとき、古い行が残っていないように)
CC BY 4.0 はここまで
次回は「口を挟まない技術」。AI が間違っても、私は指摘しません。だけど、常に正しく仕上がります。
連載「読まない技術」
- ← 前回: 第 9 回 引き継ぎを読まない技術
- → 次回: 第 11 回 口を挟まない技術
- 全回の一覧: 序論 AIの出力を、もうほとんど読んでいない