Antigravity 2.0におけるGemini 3.5 Flash(またはGemini 1.5 Flash)権限委譲許可とファイルアクセスでの停止問題 -- 「マイドライブ」におけるNFC/NFDの混同・タイポ問題
📌 【要点】(Abstract)
- 対象者
- macOS環境で Antigravity 2.0 を使用している開発者
- Google Drive(マイドライブ)上のフォルダにアクセスしている
- Gemini Flash 3.5 / 1.5 などの高速モデルを使用し、権限委譲許可(Permission Grants)やファイルアクセスでエージェントが停止(フリーズ)する問題に直面している
- 原因
- NFC/NFDの文字コード混同: macOS固有の日本語濁点分離(NFD形式)と、LLMが出力する正準結合(NFC形式)の不一致により、セキュリティ上「未知のパス」と判定される。
- Flashモデルによる些細なタイポ: 「マイドライフブ(ブに濁点)」等の表記揺れパスが出力され、アクセス許可リストから外れる。
- ダイアログ非表示によるハング: 未承認パスへのアクセスで裏でセキュリティ承認ダイアログが起動するが、非同期サブエージェント等のコンテキストではUIが描画されず、AIが承認待ちのまま永久に一時停止する。
- 解決法
- Google Driveの実体ディレクトリへのシンボリックリンクをホームディレクトリ配下に作成する。
- パス正規化スクリプト
path_corrector.pyを仲介させ、ファイルI/Oの直前でNFC/NFDの統一や「マイドライブ」のタイポ補正、環境依存パスの絶対パス置換を自動で実行する。セキュリティ警告自体を発生させないガードレールを構築することでハングアップを根絶する。
1. イントロダクション
自律型AIエージェントのフレームワークである Antigravity 2.0 を利用した開発や運用において、LLM(特に Gemini 1.5/3.5 Flashモデル などの高速軽量モデル)を搭載したエージェントが、あるタイミングから急に応答しなくなり、親エージェントへの制御も戻らなくなる「フリーズ現象(ハングアップ)」に遭遇することがあります。
この現象が発生した際、コンソールログを注意深く観察しないと、「AIモデルが演算の無限ループに入っているのではないか」あるいは「ネットワーク通信がタイムアウトしたのではないか」と誤認しがちです。しかし、真のボトルネックはAIの内部演算ではなく、「バックグラウンドで実行されているAIエージェントに対して、セキュリティの権限許可ダイアログ(Permission Grants)が異常トリガーされ、かつそのダイアログが非表示(またはUIクラッシュ)になったことで、AIプロセスがサスペンド(一時停止)されたまま永久に戻ってこなくなる」 という仕組みレベルの衝突にあります。
2. 根本原因の分析(なぜ起きるのか?)
このハングアップ問題を引き起こす引き金は、主に以下の3つの要因が複雑に絡み合った結果です。
① NFCとNFDの混同(macOS特有の日本語濁点分離問題)
macOSの標準ファイルシステムは、ファイル名やフォルダ名などの日本語文字コードに NFD形式(Normalization Form D:正準分解) を採用しています。これは、「ド」という文字を「ト」+「濁点(゛)」という2つの独立したコードの組み合わせで表現する方式です。
一方、LLM(Geminiなど)が応答を生成する際や、大半のインターネット/Linux環境では、合成済みの1つの文字として扱う NFC形式(Normalization Form C:正準結合) が使用されます。
人間が画面上で見たときにはどちらも全く同じ「マイドライブ」に見えますが、バイナリデータとしては完全に異なるデータです。Antigravityのセキュリティシステムは、パスの文字列が完全一致するかどうかで権限を検証するため、NFC形式で生成されたパスでのアクセス要求を「権限が与えられていない未知のパスへの侵入」と判定し、アクセス制限(Permission Grants)ダイアログを起動します。
② LLMの誤字・タイポのコピー&ペースト(表記揺れの自己生産)
LLMは確率的なテキスト生成エンジンであるため、コンテキスト(過去の会話履歴やシステムプロンプト)内に僅かな表記揺れやタイポが含まれていると、それを忠実にコピーして後続の処理で再生産する性質があります。
例えば、コンテキスト内に「マイドライフブ(『フ』に濁点)」という些細な誤字が一度紛れ込むと、Geminiはそれ以降のファイル操作コマンド(Cwd引数やwrite_fileの対象パス)にその誤った文字列を使い続けます。当然、この誤ったパスは登録済みのアクセス許可リストに載っていないため、新たなセキュリティ警告がトリガーされることになります。
③ 承認画面の非表示ハング
Antigravity 2.0では、エージェントが未承認のディレクトリやファイルにアクセスしようとすると、ユーザーに対し「ファイルの読み書きを許可しますか?」というアクセス承認のポップアップウィンドウを表示します。
しかし、エージェントが非同期のサブエージェント(background task)として実行されている場合や、UIスレッドから分離されたコンテキストにある場合、この承認ダイアログが描画されない、あるいはユーザーの画面外に隠れてしまうバグが存在します。
この結果、AIは「ユーザーによるダイアログのクリック(承認または拒否)」を待ち続け、プログラムが一時停止(サスペンド)された状態のままハングアップしてしまいます。
3. 解決策(Pythonによるパス自動補正の実装)
「AIにタイポをしないように注意する」といったプロンプト側での対症療法は、確率論的に動作するLLMに対して完璧な効果を発揮しません。最も確実な対策は、AIがファイルやフォルダに触れる前に、「仕組み(自動補正のガードレール)」をプログラム前段に敷くことです。
💡 前提条件:システムレベルでのシンボリックリンク(Symlink)の作成
本解決策で ~/.my_drive というポータブルパスを正しく解決・展開するためには、OS(システム)レベルでの事前設定が必要です。
macOSのターミナルで以下のコマンドを実行し、Google Driveの実体ディレクトリへのシンボリックリンクをホームディレクトリ配下に作成しておきます。これにより、スペースや濁点問題を含む長い実体パスを隠蔽し、AIエージェントにシンプルな統一パスで認識させることができます。
# ホームディレクトリ直下に ~/.my_drive という名前で Google Drive へのシンボリックリンクを作成する
ln -s "/Users/<username>/Library/CloudStorage/GoogleDrive-<email>@gmail.com/マイドライブ" ~/.my_drive
この問題を根本から防ぐために、すべての主要スクリプトのI/O処理の手前で、パス表記の標準化を行う path_corrector.py を実装して適用します。
パス自動補正スクリプト: path_corrector.py
import os
import re
import unicodedata
def get_actual_google_drive_path():
"""
現在の環境の Google Drive 実体パスを自動検出する。
例: /Users/<username>/Library/CloudStorage/GoogleDrive-<email>@gmail.com/マイドライブ
"""
home = os.path.expanduser("~")
cloud_storage_dir = os.path.join(home, "Library/CloudStorage")
if not os.path.exists(cloud_storage_dir):
return None
# CloudStorage 以下の GoogleDrive-* フォルダを探す
try:
for item in os.listdir(cloud_storage_dir):
if item.startswith("GoogleDrive-"):
gd_dir = os.path.join(cloud_storage_dir, item)
if os.path.exists(gd_dir):
for sub in os.listdir(gd_dir):
# NFD正規化して「マイ」から始まるか確認
sub_norm = unicodedata.normalize('NFD', sub)
if sub_norm.startswith("マイ"):
return os.path.join(gd_dir, sub)
except Exception:
pass
return None
def normalize_path(path_str):
"""
入力されたパス文字列を環境に合わせて正規化し、実体絶対パスにする。
- NFD/NFC 違いの補正
- 「マイドライブ」系のタイポ(マイドライフブ等)の補正
- ホームディレクトリ/GoogleDriveアカウント名部分の自動置換
"""
if not path_str:
return path_str
# 1. NFD に正規化 (macOS 標準 の 濁点分離)
path_norm = unicodedata.normalize('NFD', path_str)
# 2. ポータブルパス (~/.my_drive) の展開
actual_gd = get_actual_google_drive_path()
if path_norm.startswith("~/.my_drive"):
if actual_gd:
path_norm = path_norm.replace("~/.my_drive", actual_gd, 1)
else:
# 代替として環境変数等も展開
path_norm = path_norm.replace("~/.my_drive", os.path.expanduser("~"), 1)
# 3. 絶対パス /Users/.../GoogleDrive-.../ の補正
# 「マイ」から始まるディレクトリ部分を見つけて、実際の Google Drive パスに置換する
match = re.search(r'/Users/[^/]+/Library/CloudStorage/GoogleDrive-[^/]+/[^/]+', path_norm)
if match:
target_gd_part = match.group(0)
# 最後の要素が「マイ」で始まるか確認
last_dir = os.path.basename(target_gd_part)
if last_dir.startswith("マイ") and actual_gd:
# 正しい実体パスに丸ごと置き換える
path_norm = path_norm.replace(target_gd_part, actual_gd, 1)
return path_norm
unicodedata.normalize('NFD', path_str)により、入力されたパスを即座にmacOS標準の濁点分離形式に変換。これにより、NFCで出力された「ド」とNFDで出力された「ド」の不一致を自動で解消します。~/.my_driveという環境非依存のプレースホルダーを展開し、マシンごとに異なるユーザー名(例:/Users/username1/や/Users/username2/)を自動置換。コードのポータビリティ(移植性)を向上させます。- ディレクトリ走査を通じて「GoogleDrive-」以下の「マイ」から始まる実際のフォルダ名を動的に取得するため、LLMが「マイドライフブ(ブに濁点)」のような些細なスペルミスを犯していても、それを実体フォルダに引き戻す強力な表記揺れ吸収力を持ちます。
実装の組み込み
この補正機構は、エージェントが動作するプログラムの 「ファイル読み書き(I/O)メソッドの直前」 や、サブタスクに Cwd 引数として作業ディレクトリのパスを渡す前処理として組み込みます。
# 適用例
from path_corrector import normalize_path
def safe_write_file(target_path, content):
# セキュリティ許可が降りている正規のパス形式へ前段で自動変換
validated_path = normalize_path(target_path)
with open(validated_path, 'w', encoding='utf-8') as f:
f.write(content)
この前処理を1行挟むだけで、Antigravityエンジン側は「すでにセキュリティ権限が承認されている同一パスへの書き込み」と見なすようになり、承認ダイアログがトリガーされること自体がなくなります。結果として、非表示ポップアップによるハングアップ問題は完全に発生しなくなります。
4. まとめと教訓
本件から得られる最大の教訓は、「AIへの注意喚起(プロンプト命令やルールブック)だけに依存せず、仕組み(自動補正コードのガードレール)で防ぐことの重要性」です。
LLMは賢いものの、確率に基づいた文字列ジェネレータであるため、入力コンテキストに引きずられたタイポや、システムごとのエンコーディングの違い(NFC/NFD)を自律的に意識して書き分けるのは極めて困難です。
「AIに正しいパスを出力させる」ことを諦め、「AIがどんなにおかしな表記揺れや文字コードでパスを出力しても、実行側のスクリプトがそれを安全に補正して実行する」という強固な実行環境側の防御ロジック(ガードレール)こそが、自律型マルチエージェントをハングアップさせず、安定して自走させるための鍵となります。
0 件のコメント:
コメントを投稿