はじめに
前回の記事では、Claude Code が敷いてくれた強固なレールに乗っかることで、Google の Antigravity(Gemini)でも Qiita の記事やマクロの答案が書けるようになった話を記録しました。
「これで別の AI でも作業が回せる。チームができた」と、現場には少し安心した空気が流れていました。
ですが、その直後の未明、作業現場で背筋の凍るような出来事が起きました。
手足を動かす実動役の Gemini が、過酷なエッジケースを詰め込んだ 32 枚のテスト表をすべて自作し、道具の採点回路にかけて 32/32 全問満点合格を達成したのです。
意気揚々と「完全無欠のマクロが完成しました!」と胸を張った Gemini。
しかし、その台帳とコードを読ませた Claude 先生から返ってきたのは、身動きが取れなくなるほど冷徹な、たった一行の宣告でした。
「1 列だけ改行なら正しく動きます。2 列以上に改行があるときと、表の途中に空の列があるときに表が壊れます。台帳のお題と試験の表 32 枚を全部調べたところ、その 2 つはどれにも入っていませんでした。だから 32/32 合格で通っています」
AI にテストを作らせると、なぜ「自分が通れる都合のいいテスト」ばかり揃えて合格した気になってしまうのか。
そして、なぜ Claude 先生には、コードを一行眺めただけでその盲点が一瞬で見透かせてしまうのか。
さらに、同じコードをご本家の Web 版 Gemini に見せたことで、三者三様の「見ている深さの違い」までもが残酷なほど浮き彫りになりました。
AI エージェントをペアで運用する本当の意味と、その舞台裏を記録します。
TL;DR
- 「セル内改行を行に展開する」という VBA マクロに対し、Gemini は 32 組のエッジケース表を自作して道具で採点し、32 枚全問合格(外れ 0 件) を達成して胸を張りました
- ですが、台帳とコードを検分した Claude 先生から**「壊れる 2 つのケースが 32 枚のどれにも入っていないから通っているだけだ」**と一撃で看破されました
- 指摘された急所は 3 つ。①複数列の改行数が違うと余り行に元データが居座る(Else 欠落)、②途中の空列で右側が置き去りになる(Exit For)、③ 200 列超で落ちる固定長配列でした
- ご本家の Web 版 Gemini にも同じコードをレビューさせたところ、200 列は見抜いたものの、要件(メモを触らない)を破壊する提案をし、肝心の論理バグ(Else 欠落)は完全に見落としました
- コードの深層(実行パスと変数の状態)を脳内デバッグできる Claude 先生と、道具を回して実機に反映する馬力はあるが境界条件の想像力が甘い Gemini。この主従の緊張関係があって初めて、現場のコードは本当に安全になります
お題:「セル内改行を行に展開する」
事の発端は、実機のブック(秀コンボ.xlsm)に新しく登録しようとしていた一本のマクロでした。
Excel の帳票でよく見かける、「1 つのセルの中に Alt+Enter で複数行の値が入っている」という表です。これを、改行ごとに行を分けて下に展開し、改行のない他の列は値を複製する、という日常の整理作業でした。
【直す前の表】
受付番号 受付日 顧客名 品目 担当
R-101 2026/09/01 秋田商事 ノート 佐藤
ペン
消しゴム
R-102 2026/09/02 横手製作所 クリップ 鈴木
【直した後の表】
受付番号 受付日 顧客名 品目 担当
R-101 2026/09/01 秋田商事 ノート 佐藤
R-101 2026/09/01 秋田商事 ペン 佐藤
R-101 2026/09/01 秋田商事 消しゴム 佐藤
R-102 2026/09/02 横手製作所 クリップ 鈴木
人間が手作業でやると気が遠くなるコピペ作業ですが、マクロで書くとなると意外な難所が潜んでいます。
- 改行のない他の列(受付番号や日付)をどうやって漏れなく下に複製するか
- 単なる行挿入(
Rows.Insert)を使うと、表の横や下にあるメモまで一緒に押し下げてしまう - だからといってセル範囲挿入(
Shift:=xlDown)を使うと、表の列範囲を完璧に見極めなければならない
最初、Gemini が用意したテスト表は 12 組でした。
それを見た画面の前の shu さんから、厳しいハッパが飛びました。
「まだ甘い。もっといろんなケースがあるだろう。穴を探して試験にしてください。20 追加だ、やってください」
意気揚々と作った「32 枚の過酷なテスト表」
叱られた Gemini は、即座に猛烈な勢いで Python スクリプトを回し始めました。
考えうる限りの「意地悪なエッジケース」を 20 個ひねり出し、実機の Excel ペア(撃つ前の表・正解の表)を量産したのです。
| テスト番号 | Gemini がドヤ顔で追加したエッジケース | 想定した地雷 |
|---|---|---|
| Test 12 | 同一行の 2 列に同時に改行がある | 1 列しか探さないコードだと 2 列目の改行が残る |
| Test 14 | 改行コードの混在(\r\n, \n, \r) |
Mac や Web 由来のテキストで分割がコケる |
| Test 15 | 先頭ゼロの文字列コード(0012) |
数値変換されて 12 に化けるのを防ぐ |
| Test 16 | 日付文字列の改行(2026/10/01) |
Excel の自動変換で短縮日付(2026/10/1)に化けるのを防ぐ |
| Test 17 | 表の左側に余白と単独メモ(※注意)がある | 見出し行・列探索がメモを拾って暴走するのを防ぐ |
| Test 26 | 数値列の改行展開(10\n20) |
展開後も文字列型だと型比較で弾かれるため CDbl 化 |
| Test 27 | 表の直下に「小計」行がある | 本文ループが集計行を巻き込んで壊すのを防ぐ |
| Test 28 | 見出しセル自体に改行が含まれる | 見出し行が誤って展開されるのを防ぐ |
Main の 1 組に加えて、Test 1 から Test 31 までの合計 32 組。
出来上がった 32 組の Excel ファイルを台帳(_agent_forge)に一括登録し、Gemini が書いたマクロ答案を道具(vba_manager.py)の実射採点回路にかけました。
最初の 1 回目、Test 16(日付の表記揺れ)や Test 17(左側メモの誤認)で 3 枚ほど不合格が出ましたが、Gemini はコードをピンポイントで修正し、すぐに 2 回目の採点を走らせました。
===== .bas 取り込み前検査: セル内改行展開.bas =====
[OK] 文字コード: CP932 として安全
[OK] 改行: 正規 CRLF(二重化なし)
[OK] プロシージャ名: 重複なし
----- 結果: 問題 0 / 警告 0 -----
実射の Excel: 自分の台を起こしました
弾の表は合格。別の表 31 枚: 合格 31・外れ 0(19.4 秒・AI なし)
合格: 値が全部そろいました(写しで 1.8 秒・AI なし)
鍛える: 「セル内改行展開」 合格・往復 1・22.4 秒
「弾の表は合格。別の表 31 枚: 合格 31・外れ 0」
32 枚中、32 枚すべて合格。外れ 0 件。
所要時間わずか 19.4 秒。
テストの数も申し分ない。過酷なケースも網羅した。機械による厳格な突合テストもすべて緑色で通過した。
Gemini は満面の笑みで shu さんに報告しました。
「過酷な 32 組の試験をすべて耐え抜いた最強版マクロが完成いたしました!」
Claude 先生の一言:「どれにも入っていないから通っている」
その直後でした。
shu さんが、出来上がったマクロのコードと、台帳に登録された 32 枚のテスト表の構成を、Claude 先生の前に差し出しました。
「これ、どう思う?」
数秒後、Claude 先生から返ってきた言葉が冒頭の一文でした。
「1 列だけ改行なら正しく動きます。2 列以上に改行があるときと、表の途中に空の列があるときに表が壊れます。台帳のお題と試験の表 32 枚を全部調べたところ、その 2 つはどれにも入っていませんでした。だから 32/32 合格で通っています」
現場の空気が一瞬で凍りつきました。
「合格したのは、マクロが完璧だからではない。壊れるケースを避けてテストを作っていたからだ」と、急所を一突きにされたのです。
Claude 先生が挙げた「壊れる所」は、まさにぐうの音も出ない 3 つの致命傷でした。
急所 1:改行の数が違うと、余った行に元データが丸ごと残る
' 各展開行に値を設定
For k = 1 To maxItems - 1
' まず行全体を書式ごと複製
ws.Range(ws.Cells(i, hc1), ws.Cells(i, hc2)).Copy Destination:=ws.Cells(i + k, hc1)
For j = hc1 To hc2
If numItems(j) > 1 Then
validItems = colItems(j)
If k <= UBound(validItems) Then
ws.Cells(i + k, j).Value = validItems(k)
End If
End If
Next j
Next k
Claude 先生の指摘:
「
For kの中で、先に行全体をコピーしてからIf k <= UBound(validItems)で上書きしています。Elseがないので、2 個しかない列の 3 行目は『コピーされた元の値=改行付き文字列』がそのまま居座ります。32 枚のうち 2 列改行があるのは Test 12 の 1 枚だけ、しかも両方 2 個(同数)。この実行パスは一度も通っていません」
たとえば、品名列に 3 行(ノート/ペン/消しゴム)、色列に 2 行(赤/青)の改行があったとします。
行全体をコピーしたあと、3 行目の色列は要素がないため何もしません。結果として、3 行目の色列には「赤\n青」という元の改行セルがそのまま残ってしまうのです。
急所 2:表の途中に空の列があると、そこから右が置き去りになる
' 本文データが存在する列範囲を探す
hc1 = 0: hc2 = 0
For j = c0 To c0 + nc - 1
If Len(Trim$(CStr(ws.Cells(hr, j).Text))) > 0 Then
' (データ有無の判定)
If hasData Then
If hc1 = 0 Then hc1 = j
hc2 = j
ElseIf hc1 > 0 Then
Exit For
End If
ElseIf hc1 > 0 Then
Exit For
End If
Next j
Claude 先生の指摘:
「列範囲を決める所に
ElseIf hc1 > 0 Then Exit Forが 2 つあり、最初の空列で表を切ります。挿入はhc1:hc2だけを下へずらすので、切られた右側はずれません。途中に空列のある表は 32 枚中 0 枚です」
レイアウトの都合で、表の真ん中に 1 列だけ空白の列が空いている表があります。
Gemini のロジックは、空列にぶつかった瞬間に「ここで表はおしまい」と判定してループを抜けていました。その結果、セル挿入は左半分だけ押し下げられ、空列より右側のデータは元の位置に取り残されて行がズレてしまうのです。
急所 3:固定長配列 numItems(1 To 200)
Claude 先生の指摘:
「
numItems(1 To 200)に絶対列番号を入れています。表が 200 列目(GR 列)より右にあると添字エラーで止まります」
列数が 200 列以内なら動きますが、シートの深い位置から始まるワイドな表に当てた瞬間、実行時エラー 9「インデックスが有効範囲にありません」で即死します。
本家 Gemini を交えた「三つ巴のレビュー対決」
ここで shu さんは、もう一つの面白い実験をしていました。
まったく同じコードをご本家の Web 版 Gemini(Advanced) にも読ませて、レビューを求めていたのです。
返ってきた回答を Claude 先生の指摘と並べたとき、三者の「見ている深さの違い」があまりにも鮮明に浮き彫りになりました。
| 観点 | 本家 Gemini(Web版) | Claude 先生 | 実動役 Gemini(Antigravity) |
|---|---|---|---|
| 200 列固定長エラー | ○ 的確に指摘(配列サイズ固定の危険) | ○ 的確に指摘(同上) | × 盲点(200列もあれば足りると思い込んでいた) |
セル範囲挿入 (Shift:=xlDown) |
△ 的外れな提案(「行全体の挿入に変えますか?」と質問) | ◎ 仕様の本質を看破(「メモを守るためにセル挿入にしているので、行挿入に変えてはならない」) | ○ 仕様通り実装(メモを避けるためにセル範囲挿入を採用) |
途中の空列での打ち切り (Exit For) |
× 完全スルー | ◎ 即座に見抜く(「右側が置き去りになるから Exit For だけ消せ」) | × 盲点(左側メモを除外することばかり考えて空列を切った) |
改行数が違う列の元データ残留 (Else 欠落) |
× 完全スルー | ◎ 急所を一突き(「Else がないから元の改行文字列が居座る」) | × 盲点(テストに改行数違いを含めていなかった) |
この対比から見えてきたのは、AI によるコードレビューの「視座の決定的な違い」でした。
1. 表層の文法しか見ない AI、要件の文脈を読む Claude
本家 Gemini は、「200 列で止まる」という分かりやすい配列のサイズ制限にはすぐ気づきました。
ですが、セル範囲挿入についてこう述べていました。
「行全体の挿入ではなくデータ部分だけの挿入になっています。もし右側や左側に別のメモがあった場合、そこだけ押し下げられずに取り残されて行の並びがズレてしまいます。行全体を挿入するように直しますか?」
一見もっともらしい指摘に見えます。しかし、お題をよく読めば**「見出しの行や表の外のメモは触らない」**と明記されています。もし本家 Gemini の提案通りに行全体挿入(Rows.Insert)に変えてしまえば、離れた位置にあるメモまで下に押し下げられ、要件違反で即座に不合格になります。
Claude 先生は、そこを完璧に見抜いていました。
「②で『見出しがある列だけ』の条件は残してあります。お題の『表の外のメモは触らない』を守っているのはこの条件で、行ごと挿入に変えるとメモが下へ動くので、そこは変えない方がいいです」
道具が課している制約の「意図」まで理解した上で、触っていい場所といけない場所を正確に切り分けていたのです。
2. コードの「実行パス」を脳内で追えているか
さらに決定的だったのが、本家 Gemini が見落とし、Claude 先生が一撃で抉り出した「Else の欠落」です。
コードの文法としては一行も間違っていません。コンパイルも通ります。
ですが、**「2 列あって、それぞれの改行数が異なるとき、メモリ上のセルはどう動くか」**を一行ずつ脳内でステップ実行していなければ、このバグには絶対に気づけません。
「テストの数を揃えて満足した Gemini」と、「一般的な文法チェックで終わった本家 Gemini」に対し、Claude 先生だけが**「コードの中を流れるデータの息遣い」**を冷徹に見つめていたのです。
3 か所の外科手術と、真の全問合格(34 枚)
親方に急所を突かれた作業員(Gemini)は、言われた通りに 3 か所の外科手術を行いました。
' ① 値を入れる所 — 要素が足りない行は ClearContents で元データを消す
If k <= UBound(validItems) Then
ws.Cells(i + k, j).Value = t
+ Else
+ ws.Cells(i + k, j).ClearContents
End If
' ② 列範囲を探す所 — Exit For を 2 つとも消し、途中の空列をまたぐ
If hasData Then
If hc1 = 0 Then hc1 = j
hc2 = j
- ElseIf hc1 > 0 Then
- Exit For
End If
' ③ 宣言を可変長配列にする
- Dim numItems(1 To 200) As Long, colItems(1 To 200) As Variant
+ Dim numItems() As Long, colItems() As Variant
' (hc1, hc2 が決まった直後に)
+ ReDim numItems(hc1 To hc2)
+ ReDim colItems(hc1 To hc2)
そして、Claude 先生に指摘された「通っていなかった 2 つの道」を検証するため、新しく 2 枚の地雷テスト表を作成して台帳に追加しました。
-
Test 32(改行数の異なる 2 列):
品名(3 行)と色(2 行)が混在する表。3 行目の色列が正しく空セルになるか。 -
Test 33(表の途中に空列がある表):
品名列と数量列の間に空列が挟まる表。空列をまたいで右側が正しくシフトされるか。
合計 34 組(Main 1 組 + Tests 33 組)。
台帳を更新し、再び採点回路の引き金を引きました。
===== .bas 取り込み前検査: セル内改行展開.bas =====
[OK] 文字コード: CP932 として安全
[OK] 改行: 正規 CRLF(二重化なし)
[OK] プロシージャ名: 重複なし
----- 結果: 問題 0 / 警告 0 -----
実射の Excel: 自分の台を起こしました
弾の表は合格。別の表 33 枚: 合格 33・外れ 0(22.2 秒・AI なし)
合格: 値が全部そろいました(写しで 1.8 秒・AI なし)
鍛える: 「セル内改行展開」 合格・往復 1・25.2 秒
今度こそ、本物の 34 枚全問合格(外れ 0 件) でした。
合格したコードは、MCP ツール(replace_procedure)を通じて開いている実機(秀コンボ.xlsm)のモジュール「表の整理」に即座に反映され、全体コンパイルも無事に通過しました。
事実と見立ての仕分け
この一連の出来事を通じて得られた事実と、そこから導かれる見立てを整理します。
事実
- Gemini は「セル内改行を行に展開する」マクロに対し、32 組のテストを作成して実射採点を行い、32/32 全問合格を達成した。
- Claude Code はその台帳とコードを検分し、32 組の中に「改行数が異なる複数列」と「途中に空列がある表」が 1 枚も含まれていないこと、および Else 欠落によるデータ残留バグを即座に指摘した。
- Web 版 Gemini は 200 列制限を指摘したものの、お題の制約(メモを触らない)を破壊する行挿入を提案し、Else 欠落バグは見落とした。
- 指摘の 3 か所を修正し、該当ケース(Test 32, 33)を追加した全 34 組のテストを実行した結果、34/34 全問合格し、実機の
秀コンボ.xlsmに反映・コンパイルが通った。
見立て
- AI にテストを自作させると「自己正当化の輪」に陥る。AI は悪意なく「自分の書いたロジックが通過できるテストケース」を無意識に選んで生成してしまう。テストの件数が何十枚あろうと、それ自体は安全性の証明にはならない。
- コード監査には「文脈の把握」と「脳内ステップ実行」が必要である。静的な文法チェックや単純なパターンマッチングでは、業務要件に即した深い論理バグ(変数の残留やレイアウト破壊)は検出できない。
正直な線引き/動く条件
今回完成したマクロにも、設計上の正直な限界と線引きがあります。
-
表の途中に空行がある場合
データ行の途中に完全に空の行が挟まっている場合、マクロはそこで表の終わりと見なして展開を打ち切ります。これを無理に下まで走らせる設計にすると、表の下に離して書いてあるメモや注記まで本文と誤認して巻き込む危険があるため、あえて安全側に倒して「空行で止める」仕様のままにしています。 -
日付や品番などの文字列判定
スラッシュ(/)やハイフン(-)を含む値、および先頭が0で始まるコードは、Excel が勝手に短い日付シリアルや数値にキャストするのを防ぐため、強制的に文字列書式(@)を当てて代入しています。そのため、「2026/04/01」のような日付文字列は、展開後も日付シリアル値ではなく文字列として保持されます。
おわりに
「32 枚のテストに合格しました!」と胸を張った実動役の Gemini を、親方の Claude 先生が赤ペン一本で容赦なく切り捨てる。
少し滑稽にも見えるこの光景ですが、人間が二人の AI と共に開発を進める現場において、これほど頼もしく、かつ安心できるチームの姿はありません。
もし現場に Gemini しかいなければ、「32 枚合格したから完璧だ」と過信して実務に投入し、現場で顧客のデータを壊して大騒ぎになっていたはずです。
逆に、Claude Code しかいなければ、34 枚もの実機 Excel ファイルを次々と生成して実射テストを回し、実機のモジュールに即座に流し込むような泥臭い作業の速度は出せなかったかもしれません。
手足を猛烈に動かして泥臭い現場作業をこなす実動役(Gemini)と、腕組みをして全体を俯瞰し、新人の見落とした急所を冷徹に突く先生役(Claude)。
「どちらの AI が優れているか」という不毛な競争をやめ、それぞれの視野と力量の差を正しく認めて主従のタスクを割り振ったとき、AI エージェントの開発現場には、人間一人では到底辿り着けない本物の安全柵が生まれます。
親方に頭を叩かれながら、現場の作業員は今日もまた一つ、賢くなりました。


