2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

やってくれたなClaude ── 致命的な誤りをGeminiに修正してもらった夜の話

2
Posted at

はじめに

夜、自宅のパソコンでExcelのテストを回していたときのことです。

自作の開発環境である「秀コンボ」には、画面上のボタンを押すだけでAIを呼び出し、表の加工や数式の埋め込み、条件付き書式の設定などを自然言語で実行させる機能があります。
裏ではClaude CLIが動いており、ExcelのUserFormから渡された指示を解釈して結果を返す仕組みです。

ほかのテスト用シートは、どれも軽快に動いていました。
指示を入力して実行ボタンを押せば、数秒でセルが書き換わり、意図通りの結果が返ってきます。

ところが、「テスト用3」と名付けたシートを開き、ある指示を実行したとき、ピタリと動きが止まりました。

D列に、在庫数が発注点を下回っていたら『要発注』と出す式を入れて
要発注の行の背景を赤くする条件付き書式を付けて

ボタンを押しても、返事が返ってきません。
画面は処理中のまま沈黙し、いつまで待っても結果が表示されませんでした。

直前に別のマクロのキーワード調整を行っていたため、最初は「Geminiがどこかの配線を触って壊したのではないか」と思いました。
あるいは、「またClaudeの利用枠に当たって、API側で弾かれたのではないか」とも考えました。

ですが、実機のログを物理的に開いて調べてみたところ、判明したのは意外な事実でした。

Claudeは、弾かれてなどいませんでした。
指示を受け取ってからわずか1.5秒で、完璧な答えをファイルに書き出して待機していたのです。

では、なぜExcelは沈黙し続けたのか。
原因を突き止めたとき、思わず苦笑いが漏れました。

かつてClaude自身が書いたVBAコードの脆い隙を、Claude自身の最新アップデートが背後から刺していたのです。
その顛末と、今夜机を並べていたGeminiに綺麗に直してもらった一部始終を記録に残しておきます。


TL;DR

  • 条件付き書式で突然のフリーズ: ほかのシートは快調に動くのに、「テスト用3」シートで条件付き書式を頼んだ瞬間、Excelコンボが返事待ちのまま止まりました。
  • Claudeは1.5秒で答えていた: 利用制限や長考を疑いましたが、通信ログ(out.jsonl)を開くと、Claude CLIは指示からわずか1.5秒で完璧な回答を出力済みでした。
  • 原因はJSONキーの順序変更: Claude CLIのバージョンアップにより、出力されるJSONの先頭に処理時間(duration_api_ms)などの計測データが入るようになりました。
  • 先頭16文字決め打ちの罠: VBA側の受信コードは、以前Claude自身が書いたものでした。そこには Left$(ln, 16) = "{""type"":""result""" という、キーの順序が絶対に変わらないことを前提にした判定が書かれていました。
  • Geminiによる修正: GeminiがログとVBAコードを突き合わせて原因を特定し、InStr による安全な走査とエラー即時検知を実装。関所テスト67本を全件通過させました。
  • GitHubの公開リポジトリも更新: 最新のClaude CLIを使う利用者の手元でも同様に止まる問題であるため、公開リポジトリ(shu1551/shu-excel-manager)のテスト760本を通し、最新版をプッシュしました。

1. 沈黙したテスト用シート

発端は、在庫管理表を模した「テスト用3」シートでの確認作業でした。

品名、在庫数、発注点という列が並んでおり、ここに以下の指示を与えて実行ボタンを押しました。

D列に、在庫数が発注点を下回っていたら『要発注』と出す式を入れて
要発注の行の背景を赤くする条件付き書式を付けて

通常の動きであれば、UserFormに送信中の表示が出たあと、2〜3秒ほどでD列に =IF(B6<C6,"要発注","") が入り、該当する行の背景が薄い赤色に変わるはずでした。

ところが今回は、ボタンを押した瞬間から画面の更新が止まりました。
十数秒待っても、1分待っても何も起きません。
エラーメッセージが出るわけでもなく、じっと何かを待ち続けるループに入っていました。

この直前、マクロの整理棚のキーワード調整を行っていたため、最初に疑ったのはマクロ棚の誤爆でした。
秀コンボには、AIを呼ぶ前によくある定型処理を片付ける「棚マクロ」の先撃ち機能があります。
指示文の中の言葉に棚が反応し、AIに渡すべき指示を途中で奪っているのではないか、と考えたのです。

実際、小さな誤爆は見つかりました。
指示文にあった「赤くする」という言葉に、デザイン美化棚の「ノーブルボルドー(赤系美化)」マクロの登録単語が反応していました。

そこで、単一の文字で反応するのをやめさせ、条件付き書式のような高度な指示は棚を通さずAIに直行させるようガードを入れました。

ですが、そこを直しても状況は変わりませんでした。
確実にClaudeへ指示が渡っているログが出ているのに、そこから先が一行も進まないのです。

「Claudeの利用枠の上限に引っかかったのだろうか」

そうも思いました。
しかし、普段なら利用枠に当たった場合は「使用量の上限です」というメッセージが返ってくるはずです。
無言のまま止まるのは、どうにも腑に落ちませんでした。


2. ログを開くと、1.5秒で回答が終わっていた

推測で考えていても仕方がありません。
私はGeminiに指示を出し、裏で動いている通信ログを物理的に開いて中身を点検させました。

秀コンボのClaude CLI連携は、常駐しているClaudeプロセスに対して標準入力でJSONを送り、Claudeが標準出力に追記していくファイル(out.jsonl)をVBA側からタイマーで監視して読み取る仕組みです。

もしプロセスが落ちているならプロセス一覧にいませんし、エラーが出ているなら標準エラー出力(err.log)に記録が残るはずです。

Geminiが調べた結果は、予想外のものでした。

プロセス: 正常稼働中
エラーログ: 空(エラーなし)
出力ファイル(out.jsonl):
  {"duration_api_ms":1524,"type":"result","result":"###完了### D列に式を入れ..."}

Claudeは、止まってなどいませんでした。
指示を受け取ってからわずか1.5秒後には、完璧な数式と条件付き書式の手順を生成し、ファイル末尾に綺麗に出力して、とっくに処理を終えていたのです。

答えは目の前にある。
それなのに、なぜVBAはそれを無視して待ち続けていたのか。

理由は、VBA側で結果の到着を判定していた、たった1行のコードにありました。

これか


3. 「先頭16文字」という時限爆弾

ExcelコンボのUserFormには、out.jsonl から新しい結果行を拾い出す関数があります。
その中に、次のような判定が書かれていました。

' 以前のコード(Claudeが書いたもの)
For i = 0 To UBound(L)
    ln = Trim$(Replace(L(i), vbCr, ""))
    If Left$(ln, 16) = "{""type"":""result""" Then
        CLI新結果行 = ln
        Exit Function
    End If
Next i

このコードを見て、合点がいきました。

行頭の16文字だけを切り出して、一致するかどうかを見ているのです。

以前のバージョンのClaude CLIでは、結果行を出力するとき、確かにキーの先頭が type から始まっていました。

{"type":"result","result":"###完了### ..."}

この形であれば、左から16文字を切り出すと、ぴったり {"type":"result" になります。
高速化のために重いパーサーを通さず、文字列の左端だけを見て「届いた」と判断するショートカットを仕込んでいたわけです。
そして、このコードを過去に書いたのは、他ならぬClaude自身でした。

ところが最近、AnthropicがClaude CLIをアップデートしました。
出力されるJSONに、APIの通信時間を表す duration_api_ms などの計測フィールドが追加されたのです。

その結果、新バージョンの出力はこう変わりました。

{"duration_api_ms":1524,"type":"result","result":"###完了### ..."}

ここから先頭16文字を切り出すと、{"duration_api_m になります。
一致するはずがありません。

VBAのループは、目の前に届いているClaudeからの回答行を眺めながら、
「先頭が {"type":"result" で始まっていないから、まだ結果ではない」
と素通りし続けていたのです。
永遠に来ない「行頭から始まる結果行」を待ち焦がれ、タイムアウトまで黙り込んでいたのがフリーズの正体でした。

郵便受けに例えるなら、手紙を受け取る側が「横幅ぴったり16ミリの封筒しか受け取らない」と頑なに決めていたようなものです。
配達人が少し親切になって、配達時間のスタンプを封筒の端に押して届けるようになった途端、「スタンプのせいで端の模様が違う、これは手紙ではない」と居留守を使い続けていたわけです。

しかも、その狭い郵便受けを作った張本人と、スタンプを押すようになった張本人が、同じClaudeだったというオチでした。

入らん


4. 他のシートが動いていた理由

では、なぜ他のテストシート(テスト用1やテスト用2)は問題なく動いていたのでしょうか。

もしCLIの出力形式変更が原因なら、すべてのシートで止まるはずです。
なぜテスト用3だけが止まったのか。

理由は、秀コンボの「棚マクロ(先撃ち)」にありました。

秀コンボでは、全角半角の統一や空行削除、住所や会社名の表記ゆれ修正といった定型処理は、AIを呼ばずに手元のVBAマクロが一撃(0.1〜0.4秒)で片付けます。
テスト用1やテスト用2で行っていた操作は、すべてこの棚マクロの範囲内でした。
つまり、最初からClaude CLIを呼んでいなかったのです。

手元のマクロだけで素早く完了していたため、CLI連携の受信ロジックが壊れていることに気づきませんでした。

一方、テスト用3のお題は「条件付き書式」でした。
条件付き書式は固定のマクロ棚には入っていないため、ここで初めて裏のClaude CLIが呼び出されました。
そして、呼び出された瞬間に、あの「先頭16文字判定」の罠を踏み抜いたのです。

棚マクロが優秀すぎてAIを呼ぶ機会が減っていたことが、皮肉にも足元の不具合の発見を遅らせていました。


5. Geminiによる修正と検証

原因が分かれば、直すのは難しくありません。

Geminiに指示を出し、受信判定を安全な作りに改修させました。
先頭決め打ちをやめ、行内のどこに "type":"result" があっても確実に拾える形に改めました。

' 修正後のコード(Geminiが直したもの)
For i = 0 To UBound(L)
    ln = Trim$(Replace(L(i), vbCr, ""))
    If (InStr(ln, """type"":""result""") > 0 Or InStr(ln, """type"": ""result""") > 0) And Right$(ln, 1) = "}" Then
        n = n + 1
        If n > mCli結果数 Then
            mCli結果数 = n
            CLI新結果行 = ln
            Exit Function
        End If
    End If
Next i

あわせて、もしClaude側でエラーが発生した場合には、待機ループに入らず即座に画面にエラーを表示して停止する安全策も追加しました。

修正後、全体コンパイルを通し、過去のテストコード67本を走らせて 67/67 全件PASS を確認しました。
アドイン(.xlam)へ更新登録を行い、再びテスト用3で実行ボタンを押してみました。

◆ Claude 17:27:30 (往復3.13秒)
###完了### D列に式を入れ、要発注の行に背景色を付けました。

【やったこと】
・D6:D17 に =IF(B6<C6,"要発注","") を入れました。在庫数が発注点より小さい行だけ「要発注」と出ます。
・A6:D17 に条件付き書式(=$D6="要発注")を付け、該当する行全体の背景を赤系にしました。

シートのD列に数式が入り、発注点を下回った行が綺麗な薄い赤色でハイライトされました。
往復にかかった時間は 3.13秒。
先ほどまでの沈黙が嘘のように、あっさりと動きました。

通った


6. GitHub公開リポジトリの更新

手元で直ったのを確認したあと、公開リポジトリの更新を行いました。

秀コンボのVBAとPythonツール群は、GitHub(shu1551/shu-excel-manager)でオープンソースとして公開しています。
同梱されている 秀コンボ.xlsm にも今回のClaude CLI連携が含まれているため、最新のClaude CLIを使っている利用者の手元でも同じ問題が起きるはずです。

公開用のビルドスクリプトを実行し、禁止語の残存がないことを確認した上で、公開リポジトリ側の 760本の pytest を走らせました。

写した: 748 本 → D:\エクセル\秀エクセルマネージャー
検査: 残り 0 件
pytest[公開の写し]:
  760 passed, 45 skipped in 42.90s

テストは全件合格。
リモート側の最新コミットとマージを行い、GitHubの main ブランチへプッシュを完了しました。


事実と見立ての仕分け

今回のトラブルについて、客観的な事実と技術的な見立てを整理しておきます。

客観的事実

  1. JSONの仕様: RFC 8259において、JSONオブジェクトのキーの順序は保証されていない。キーの並び順に依存する処理は、仕様上安全ではない。
  2. Claude CLIの出力変化: アップデートにより、result 行の先頭側に実行時間メトリクス(duration_api_ms 等)が出力されるようになった。
  3. VBA側の判定: 行頭16文字を Left$ で比較していたため、順序変更によって不一致となり、結果行を検知できなくなっていた。
  4. 棚マクロの先撃ち: 定型処理はAIを呼ばずに手元のマクロで完結するため、AI連携部分の不具合が他のシートでは表面化しなかった。

技術的見立て

  1. なぜ先頭決め打ちにしたのか: かつてコードを書いたClaudeは、VBAに標準のJSONパーサーがない中で、外部ライブラリを使わずに高速に検知するための簡便な手段として先頭比較を選んだのだと考えられます。短期的には動いても、外部の形式変更に弱い作りでした。
  2. AI同士の補完関係: 単一のAIだけでコードを書かせていると、そのAIの思い込みや安易なショートカットに気づきにくい面があります。今回のように、別のAIがログを調べて原因を突き止め、安全なコードに書き直すというアプローチは、実用的な開発において非常に有効だと感じます。

正直な線引き/動く条件

  • 動作環境: Windows 11、Microsoft 365(デスクトップ版Excel)、Claude CLIが導入されている環境を対象としています。
  • 今回の修正範囲: Claude CLIの出力JSONにおいて、キーの順序がどのように変わっても結果行を確実に拾えるようにしました。また、CLI側でエラーが発生した際も、タイムアウトを待たずに即座に停止します。
  • 残る限界: 今回の改修は結果の受信判定を安全にしたものであり、CLIプロセス自体が無応答になった場合や、ネットワークの完全切断についてはOS側のタイマー監視に委ねられます。

おわりに

人間が書いたコードであれ、AIが書いたコードであれ、ちょっとした前提の食い違いでシステムが止まることは珍しくありません。
JSONのキー順序という基本的な仕様の落とし穴に、最新のAIツール自身が足をすくわれていたのは、なんとも興味深い出来事でした。

ですが、Claudeが残した穴を、Geminiがログから特定して直すという一連の流れを眺めていると、AIを使った開発のあり方もずいぶん変わってきたと感じます。

完璧なコードを一発で書くAIを求めるよりも、動かないときに原因を冷静に突き止め、直してくれるパートナーがいることの方が、実務ではずっと心強い。

手元のExcelも、GitHubのリポジトリも、これで最新の環境に対応できました。
また一つ、システムが頑丈になった夜でした。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?