miniに任せた初日、3回失敗した 原因はAIの外にあった

公開: 2026-10-17
開発日記OpenClawローカルLLMbashColima

10月17日、Mac miniのローカルモデルに、Issueを最後まで解かせる入口 run-issue.sh を作りました。 Issueの検査、作業場所(worktree)の用意、依頼文の組み立て、実行、PR、型検査、レビュー、マージ、後片付けを、1コマンドにまとめたものです。これで、ちょうぼっち(accounting-app)のIssueを1件ずつ任せ始めました。

初日に、3回止まりました。3回とも、モデルが悪い出力を返したことが原因ではありませんでした。 そう言い切れたのは、人が同じ手順を先に実行して作っておいた「参照」と、miniの出力を突き合わせていたからです。参照とは、Issueの手順どおりに進めたときの正解の変更です。

3回の失敗は、別の層で起きた

3回が止まった場所と、原因のある層を1枚にまとめます。

失敗止まった場所原因のある層miniの変更は失敗1失敗2失敗3依頼文の組み立て型検査の関門codexのレビューホストのシェル仮想マシンのメモリIssueの設計欠けた依頼文で動いた参照と1文字も違わない手順どおりに実装した依頼文が途中で切れた強制終了(コード137)設計の誤りを指摘されたAIを動かす直前マージの前マージの前bash 3.2の仕様2つが重なり4GBを超えた人が書いた手順の誤り気づいて取り消した変更は正しかった手順のほうが誤り
原因の層(赤い枠)は、3回とも、モデルの出力の外にあった。

層が違えば、直す場所も違います。順に書きます。

失敗1 依頼文が途中で切れたまま、実行された

最初に任せたのは、CSVの引用符で囲んだ値の中の改行を、1つの値として読めない不具合です。その最初の実行で、依頼文を作る途中に、次のエラーが出ました。

WT_NAME(文字化けした1文字): unbound variable

$WT_NAME は、作業場所の名前を入れる変数です。依頼文の中に 合図のworktree名は $WT_NAME、コミットメッセージは… と書いていました。合図とは、PRを出してよいと固定スクリプトに知らせる、印のファイルのことです。 変数の直後に日本語が続くと、macOS標準のbash 3.2は、日本語の最初の1バイトを変数名の一部として読みます。 名前が WT_NAME ではなくなり、未設定の変数として空になります。

雨の城門の前で、巻物の筒を差し出す飛脚と、右半分が破れた巻物を広げて見る侍

厄介だったのは、set -u(未設定の変数をエラーにする)を付けていたのに、止まらなかったことです。 依頼文は cat <<EOF で組み立てていて、この形だと、エラーが出ても、スクリプトは次の行へ進みます。 依頼文が途中で欠けたまま、エージェントが実行されました。作業場所、in-progress のラベル、見張りのプロセスも残りました。気づいて取り消し、すべて片付けました。

このMacのbash 3.2.57で、同じ形を再現しました。

測り方と全結果は、WIKIのbash 3.2の変数と日本語に表にしました。

同じ書き方は、ほかにも2か所ありました。待ち受けスクリプトのログと、型検査のメッセージです。どちらも、失敗したときにしか通らない経路にあり、通常の実行では一度も表に出ていませんでした。

実は、この落とし穴は、10月13日までの不具合(前の記事の5つの症状)でも踏んでいました。WIKIのOpenClawの切り分けに、$MAX、 が set -u で落ちた、と書いています。そのとき直したのは、落ちた1か所だけでした。同じ書き方は、ほかのスクリプトに残っていて、今回は、止まらない形(ヒアドキュメントの中)で再発しました。

入れた対策は4つです。

  1. 3か所とも、${変数} と波括弧で囲んだ
  2. ホスト側の全スクリプト(33ファイル)に、変数の直後に日本語が続く書き方が無いかを調べるテストを入れた。書くとテストが落ちる
  3. 依頼文を、作業場所を作る前に作って、中身を確かめるようにした。本文の印、合図の指示、コミットメッセージ、Issue本文、長さを調べ、欠けていたら実行しない(テスト32件)
  4. エージェントの実行より前に失敗したら、作った作業場所・ブランチ・ラベルを元に戻す。見張りは、どの終わり方でも止める

失敗2 型検査が、メモリ不足で強制終了した

2回目は、同じIssueを、直した入口でやり直したときです。 miniのモデルは、122秒で変更を作り、PRまで進みました。その変更は、人が作った参照と、1文字も違いませんでした。 そのあと、マージ前の型検査の関門が、終了コード137(強制終了)で止まりました。

霧の谷にかかる1枚板の橋の上で、向かい合って立つ荷を背負った2人

変更が正しいと分かっているので、疑うのは変更ではありません。原因はメモリでした。 miniの仮想マシン(Colima)のメモリは4GBです。エージェントが終わった直後は、受付役(ゲートウェイ)が約2.1GB(キャッシュを含む)を持っています。そこへ、型検査のコンテナが約2.2GBを使うので、4GBを超えました。数十秒たつとゲートウェイは約1.7GBに縮み、同じ型検査を手で流すと、12秒で通りました。

VMのメモリ 4GBエージェント終了の直後ゲートウェイ 約2.1GB型検査 約2.2GB合計 約4.3GB。4GBを超えて、強制終了(137)数十秒後ゲートウェイ 約1.7GB型検査 約2.2GB合計 約3.9GB。4GBに収まり、12秒で通った
同じ型検査が、間隔をあけると通った。図は、この2つの合計だけで、VMのほかの使用分は入れていない。

間隔の問題で、変更のどこかが悪かったのではありません。直したのは、関門の側です。

失敗3 Issueの設計が、間違っていた

3回目は、仕訳テンプレートの画面で、科目を選んだときの既定の区分が他の画面と食い違う、という不具合でした。 miniは、Issueの手順どおりに実装しました。型検査は、メモリ不足で2回やり直したあと通りました。 そこで、codexのレビューが、設計の誤りを指摘しました。科目マスタの画面で付けた既定の区分を、使わなくなっていたのです。

指摘は正しく、誤りは、私が書いたIssueの手順にありました。 「他の画面と同じ」にそろえる側に寄せすぎて、マスタの既定を捨てる書き方にしていました。miniは、書かれたとおりに実装しただけです。

指摘を受けて、ローカルモデルが修正を始めましたが、8分以上動いても終わらず、人が取り消しました。設計に関わる指摘は、ローカルモデルの修正では直らないというのは、これまでの2件と同じです。

崖の縁まで道が描かれた地図を手に考え込む浪人と、本物の崖を杖で指す僧

そこで、直す場所を変えました。

  1. PRは閉じ、作業場所とブランチを片付ける
  2. Issueを直す。マスタの既定を、IDからコードに直して渡す設計にする
  3. 人が、直した手順を通しで実行して参照を作り、テスト18件・静的解析のエラー0・型検査0を確かめる
  4. そのIssueで、miniにやり直させる

2回目は、miniが187秒で実装し、型検査(メモリ不足で2回やり直し)とレビュー(修正なし)を通ってマージされました。変更は、手順から作った参照と、4ファイルとも同一でした。

切り分けの手順

3回に共通していたのは、次の順で見たことです。

  1. miniの変更を、参照と比べる。 同じなら、出力は悪くない。失敗は、出力の外にある
  2. 同じ失敗を、手で再現する。 型検査を手で流すと、12秒で通った。再現しないなら、間隔や重なりを疑う
  3. 指摘が出たら、指摘が正しいかを、Issueの手順に照らして読む。 正しいなら、誤りは手順にある
  4. 直す場所を決める。 仕組み(スクリプト)、環境(メモリ)、Issue(手順)のどれか

切り分けの一般的な手順は、WIKIのOpenClawの切り分けにあります。今回は、その「層」に、Issueの設計を足したことになります。

まだ確かめていないこと

学んだこと

  1. 失敗したら、モデルより先に、渡したものと動かす環境を疑う。 依頼文、Issue、メモリ。参照と比べれば、どこから疑うかが分かる
  2. 失敗したときにしか通らない経路は、実行するまで壊れているか分からない。そして、1か所直したら、同じ型の書き方を全体から探す。 先に踏んだときに直したのは落ちた1か所だけで、同じ書き方が3か所に残っていた。今は、静的に調べるテストが探す
  3. やり直しは、原因が分かっている失敗にだけ付ける。 137は待てば通ることを確かめてあるので、やり直す。型エラーは、やり直しても通らない
  4. Issueの手順は、人が先に通してから渡す。 レビューの指摘が出たら、まずIssueの誤りを疑う

関連する記事です。