語源英単語コースの挿絵を全語洗い直しした記録
朝いちばんに確認を頼んだ。「列の自動スクロール追従、なんかやった気がするけど、どうなってたっけ」。前日のコミットにも、メモにも、それらしい記述が見当たらない。やった実感だけが残り、証拠が残っていない。困ったのはそこだった。
一覧ビューアーのセクション・チャプター・トピック列で、選択中の項目に合わせて自動でスクロールする機能は、確かに前日に作ってあった。実機で開くと、しかしハイライトが見当たらない。「壊れているのでは」と思ってDOMを覗くと、動いてはいた。色が薄くて目に入らなかっただけだった。機能は生きていた。記憶が薄かったのではなく、UIの主張が薄かった。
同じ朝、もっと厄介な話が待っていた。画像が159枚、ディスクから消えていた。最初の見立ては「不要な生成物だから消していい」だった。詳説141語分と語根系の画像は残す約束をこのプロジェクトのルールに書いていたはずで、その約束と消えたファイルの一覧を突き合わせると、まさにその141語分が丸ごと含まれていた。消していい画像ではなく、消してはいけない画像が消えていた。方針を「削除」から「R2からの復元」に切り替えた。
復元と、もう一つの思い込み
159枚をR2から復元させ、差分ゼロを確認した。ここまでは一直線だった。次に111枚のRound3画像をR2へアップロードさせたところ、ログに「1件失敗」の文字が浮かんだ。血の気が引いたが、突き合わせると失敗ではなく、スクリプトの標準エラー出力に紛れ込んだ「合計: 111件」という文字列が、誤って失敗件数として拾われていただけだった。実データは111件とも成功していた。
安心したのも束の間、別の3枚(356番・574番・579番の画像)だけ、何度読み出しても内容が古いまま返ってくる。削除して撮り直すべきか、それともR2側の伝播遅延を待つべきか。破壊的な削除操作は安全装置に止められ、結局は待つことを選んだ。その日の午後、腰を据えて調べ直すと、正体は伝わっていた。読み出しに使っていたコマンド(wrangler r2 object get)側の異常で、書き込みは最初から111件とも正しく完了していた。「反映されていない」ように見えたものは、実は「読めていない」だけだった。見ている側の目が曇っていて、対象が曇っていたわけではなかった。
画面で判断する
Before/Afterを並べて見比べるHTMLビューアーを立ち上げ、ブラウザで一枚ずつ確認していった。effective という単語のページでは、以前は棒と重りだけの抽象的な絵だったものが、ヨガマットの上で背中を鍛える人物の絵に変わっていた。例文は"This simple exercise is an effective way to make your back stronger."。絵と文がようやく手をつないでいた。良し悪しは、結局この目で見て決めるしかない。
原因は最初の100件で見えていたはずだった
一連の作業のそもそもの発端は、単語一覧を眺めていて「例文と挿絵がかみ合っていない」と気づいたことだった。何千語かを最初に一気に生成した時のプロンプトに、「人物を描写しない」という制約を入れていた。効果を狙った制約のはずが、結果は逆に出た。人を描けない分、絵は行為や感情を無理やり物や図形の比喩に置き換え、意味の通らない構図を大量に生んだ。
洗い直しの規模は最終的に1,724語。仕分けにかけた2,418語のうち7割強にあたり、コース全体の4,000語で見ても4割強になる。もったいなかったのは、その原因が最初の100件くらいレビューした時点で、実は既に見えていたはずだということだ。大量に流す前にパイロットで検収する手順を踏んでいれば、この日一日分の作業自体が丸ごと要らなかった。
RoundとPhaseを積み重ねる
まず全体2,418語を10並列で仕分けさせ、抽象的な絵か具体的な絵か境界線上かを分類した。次に例文と挿絵が一致しているかどうかの判定を、同じく並列で回した。ここで浮かび上がった数字が、抽象718語・境界666語・例文不一致340語だった。
一番確実に問題だとわかっている340語(Round4)から手をつけた。設計図(spec)を書き直させ、Before/Afterビューアーで確認し、学習ゲートのクイズに全問正解してから408ファイルをコミットした。本番にも反映させ、実機でも直っていることを確かめた。
残る1,384語(抽象と境界の合算、Phase3・Round5)は、規模が違う。ここは並列2ではなく並列20でCodexに投げることにした。
並列20は思ったほど速くならなかった
20並列にしたCodexへ設計図の書き直しを任せると、1,384語がおよそ70分で片づいた。問題は次の画像生成そのものだった。1,576枚のバッチを20並列で走らせ始めた直後、「もう完走までだいぶかかりそうだ」という感触があった。「まだ20枚目なんですか」と聞かれて数字を見返すと、勘違いだった。設計図の書き直しと画像生成は別の工程で、画像生成は動き出してから十数分しか経っていなかった。
そこから先は、週次のレート制限との根比べになった。最初は1時間あたり260枚ほどのペースで進み、20あった並列ランナーは275枚あたりから一本ずつ429エラーで脱落し始めた。13、9、5、2、最後は1本だけが約24枚/時でこつこつ進める状態になった。当日の朝(Round4)は294枚あたりで枯渇していたので、この夜も似た壁があると見て、資源を使い切るより長丁場に構える方を選んだ。空いた枠を拾えるよう30分おきに再試行するランナーを5本追加し、1,576枚中1,037枚(66%)まで進んだところで、この日は夜間体制に切り替えて区切りをつけた。
ドキュメントを厚くする
この日ずっと引っかかっていたのは、前日にやった自動スクロール追従の経緯が、どこにも残っていなかったことだった。同じことを繰り返さないよう、進捗の節目ごとにハンドオフのドキュメントを書き足させ、その手を止めさせなかった。セッションの利用上限に当たって作業が途切れる場面が何度かあったが、そのたびに、今どこまで終わっていて次に何をすればいいかを書き残させてから区切った。
「経緯が残っていない」という気まずさから始まった一日が、「反映されていないと思ったら読み出しの誤りだった」という切り分けと、「人物を描かないという一行が全部の元凶だった」という発見を経て、「まずは記録を厚くする」という当たり前の結論に戻ってきた。今夜はランナー任せで眠り、続きは残り539枚が終わってから考える。