AI エージェントが突然黙り込むと、開発はそこで止まります。私自身、個人開発の傍らで Dolice Labs の複数サイトの定型作業を Antigravity のエージェントに任せていますが、いちばん厄介なのは「エラーで落ちる」ことではなく「エラーも出さずに、ただ応答が返ってこない」状態でした。落ちてくれれば原因はログに残りますが、沈黙はどこを見ればいいのかが分かりにくいのです。
この記事でお伝えしたいのは、その沈黙を切り分けるために私が普段たどっている順序です。起動・認証・ネットワーク・プロンプト・リソースという 5 段の診断を、原因別の対処コードとあわせて整理します。一度この順番を体に入れておくと、次に黙り込んだときも慌てずに上から潰していけます。
問題の分類と初期診断
エージェントが応答しない問題は、以下の 4 つのカテゴリに分類できます:
- タイムアウト系: エージェントが応答を返すまでに異常に時間がかかる、または完全に応答がない
- エラーログ出力系: エラーメッセージが表示されるが、エージェントが停止している
- 予期しない挙動系: エージェントは動いているが、期待と異なる出力をしている
- 部分的停止系: マルチエージェント環境で一部のエージェントだけ応答しない
経験上、原因の体感分布は「起動・認証まわり」と「リソース枯渇」に偏ります。派手なネットワーク障害よりも、.env の読み込み漏れや、長時間運転で静かに膨らんだヒープのほうが、はるかに多く沈黙の引き金になります。だからこそ、勘で当てにいくより上から順に消すほうが速いのです。
応答しない問題の診断フローチャート
まず、以下の順序で問題を切り分けます:
1. エージェントはプロセスとして起動しているか?
→ はい: Step 2 へ
→ いいえ: 起動スクリプトのエラーログを確認(後述)
2. API キーは正しく設定されているか?
→ はい: Step 3 へ
→ いいえ: API キーを再設定(後述)
3. ネットワーク接続は正常か?
→ はい: Step 4 へ
→ いいえ: ネットワーク設定を確認(後述)
4. プロンプト / コンテキストに問題はないか?
→ はい: Step 5 へ
→ いいえ: プロンプトを修正(後述)
5. タイムアウト時間は十分か?
→ はい: メモリリークやリソース枯渇を疑う
→ いいえ: タイムアウト時間を延長
原因別対処法
原因 1: エージェント起動スクリプトのエラー
エージェントがそもそも起動していない場合:
診断方法:
# エージェントのプロセスが起動しているか確認
ps aux | grep agent
# 出力がない場合は起動していない
# ログファイルを確認
tail -50 logs/agent.log
# エラーメッセージを見て、以下の項目を確認:
# - Node.js バージョン不一致
# - 依存パッケージ未インストール
# - ポート競合対処法:
パッケージのインストール確認:
# 依存パッケージが正しくインストールされているか確認
npm list | grep -E "agent|gemini|langchain"
# 不足していれば再インストール
npm install
# キャッシュの問題であれば、クリア
npm cache clean --force
npm installポート競合の確認と解決:
# ポート 3000 が使用されているか確認(例)
lsof -i :3000
# 使用されている場合、別のポートに変更
# agent.js や .env ファイルで PORT=3001 に変更
# または既存プロセスを強制終了(慎重に)
kill -9 <PID>Node.js バージョンの確認:
node --version
# v18.0.0 以上が推奨
# 複数バージョンがある場合は nvm で切り替え
nvm use 20原因 2: API キーまたは認証エラー
エージェントが API に接続できない場合:
診断方法:
# ログに "401 Unauthorized" や "Invalid API key" が出ていないか確認
tail -100 logs/agent.log | grep -i "auth\|401\|invalid"
# 環境変数が正しく読み込まれているか確認
echo $GEMINI_API_KEY
# 出力されなければ .env ファイルが読み込まれていない対処法:
API キーの再設定:
# .env ファイルが存在するか確認
ls -la .env
# なければ作成
cat > .env << EOF
GEMINI_API_KEY=YOUR_API_KEY_HERE
GEMINI_MODEL=gemini-2-flash
AGENT_TIMEOUT=30000
EOF
# YOUR_API_KEY_HERE を実際のキーに置き換え
# キーは https://console.cloud.google.com/ で取得キーの有効期限確認:
# API キーが無効化されていないか Google Cloud Console で確認
# Console > APIs & Services > Credentials > API Key > Restrictions を確認
# - API の制限: "Gemini API" が選択されているか
# - 制限なし、または IP / ホスト制限が正しく設定されているか環境変数の読み込み確認:
// Node.js でテスト
require('dotenv').config();
console.log('API Key:', process.env.GEMINI_API_KEY ? 'Set' : 'Not set');
// 正しく読み込まれていればコンソールに "Set" と表示ここは地味ですが、私が実際にいちばん多く踏んだ落とし穴です。ターミナルでは読めている環境変数が、バックグラウンドで起動したプロセスや別シェルから起動したジョブには引き継がれていない、というすれ違いが頻発します。沈黙したらまず「そのプロセスから本当にキーが見えているか」を、上の3行のテストで確かめるのが近道です。
原因 3: ネットワークとタイムアウト
エージェントがタイムアウトで応答していない場合:
診断方法:
# ネットワーク接続を確認
ping google.com
# 正常: "64 bytes from..." と出力
# エラー: "ping: cannot resolve" など
# Gemini API へのアクセス確認
curl -H "Authorization: Bearer YOUR_API_KEY" \
https://generativelanguage.googleapis.com/v1beta/models/gemini-2-flash:generateContent
# 成功: JSON レスポンスが返ってくる
# エラー: "401" や "403" が返ってくる対処法:
タイムアウト時間の延長:
// agent.js の設定例
const agent = new Agent({
model: 'gemini-2-flash',
timeout: 60000, // デフォルト 30秒 → 60秒に延長
maxRetries: 3, // リトライ回数を増やす
});バックオフリトライ(指数バックオフ)の実装:
async function callAgentWithRetry(prompt, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await agent.run(prompt);
} catch (error) {
if (i === maxRetries - 1) throw error;
// i 回目のリトライで、2^i 秒待機
const delayMs = Math.pow(2, i) * 1000;
console.log(`Retry ${i + 1} after ${delayMs}ms`);
await new Promise(resolve => setTimeout(resolve, delayMs));
}
}
}ネットワークプロキシ設定(会社ネットワーク環境):
# .env に追加
HTTP_PROXY=http://proxy.company.com:8080
HTTPS_PROXY=http://proxy.company.com:8080
NO_PROXY=localhost,127.0.0.1原因 4: プロンプトまたはコンテキストの問題
エージェントが予期しない動作をしたり、応答が遅い場合:
診断方法:
# 過去の実行ログからプロンプトと出力を確認
tail -200 logs/agent.log | grep -A 5 "Prompt:"
# 入力トークン数が多すぎないか確認(多いほど遅い)
# 一般的に 50,000 トークン以上は遅延の原因に対処法:
プロンプトの最適化:
// ❌ 悪い例: 冗長すぎるプロンプト
const prompt = `
あなたは世界一優秀なプログラマです。
以下のタスクを完璧に実行してください。
タスク: ユーザーからの質問に答える。
詳細: ユーザーはプログラミング初心者です。
...(長々と続く)
`;
// ✅ 良い例: 簡潔で具体的
const prompt = `
Role: Programming instructor for beginners
Task: Answer the user's question about JavaScript
User question: ${userQuestion}
`;コンテキストの削減:
// 過去のコンテキストサイズを制限
const agent = new Agent({
model: 'gemini-2-flash',
maxContextTokens: 8000, // 以前の会話履歴を 8,000 トークン以下に制限
contextWindow: 5, // 直近 5 個のメッセージのみ保持
});システムプロンプトの明確化:
const systemPrompt = `
You are a code review agent. Your job is to:
1. Analyze the provided code
2. Identify potential bugs
3. Suggest improvements
4. Provide fixed code
Keep responses concise. Use JSON format for structured output.
`;
agent.setSystemPrompt(systemPrompt);原因 5: マルチエージェント環境での部分的停止
複数のエージェントが動いている環境で、一部だけ応答しない場合:
診断方法:
# 各エージェントのヘルスチェック
curl http://localhost:3000/health/manager
curl http://localhost:3001/health/worker1
curl http://localhost:3002/health/worker2
# 応答がない(タイムアウト)または 500 エラーが返ってくるエージェントが問題対処法:
エージェント間の通信確認:
// Manager Agent でワーカーの状態を確認
const workerStatus = await manager.checkWorkerHealth();
console.log(workerStatus);
// Output: { worker1: 'healthy', worker2: 'timeout', worker3: 'healthy' }
// タイムアウトしたワーカーを自動リスタート
if (workerStatus.worker2 === 'timeout') {
await manager.restartWorker('worker2');
}デッドロック検出と回避:
// ワーカー間で互いを待つ循環依存を避ける
// ❌ 悪い例: Worker A が Worker B の結果を待ち、Worker B が Worker A の結果を待つ
// ✅ 良い例: Manager が Worker A → Worker B の順序を管理
const result = await manager.executeSequential([
{ task: 'worker-a-task', input: data },
{ task: 'worker-b-task', input: 'result-from-worker-a' }
]);Antigravity 特有の「沈黙」パターン
汎用的な診断に加えて、Antigravity の Background Agent / Sub-agent を使っていると、上の 5 分類のどれにも当てはまりにくい沈黙に出くわすことがあります。私が実運用で繰り返し踏んだのは、エージェントが実行したコマンドが対話的な入力(y/n の確認や認証プロンプト)を待ったまま、ターミナルが見えないところで止まってしまうケースです。プロセスは生きていて、CPU もほぼ使っていないのに、応答だけが永遠に返ってこないので、最初は原因がまったく掴めませんでした。
このパターンは、ヘルスチェックでもエラーログでも捕捉できません。見分け方は単純で、「そのエージェントが直前に実行したコマンドが、人間の入力を前提にしていないか」を疑うことです。git push(認証待ち)、npm init(対話ウィザード)、パッケージマネージャの確認プロンプトなどが典型です。対処は、エージェントに渡すコマンドへ非対話フラグ(--yes、--no-input、CI=true など)を最初から付けておくこと。詳しい再発防止策は「Antigravity エージェントが対話コマンドで止まる問題の対処法」にまとめています。
メモリリークとリソース枯渇
長時間実行していると応答しなくなる場合:
診断方法:
# メモリ使用量を監視
node --inspect agent.js
# ブラウザで chrome://inspect にアクセス
# デバイスの一覧から agent.js を選択
# Memory タブでヒープサイズを確認
# または定期的にメモリ状況をログ
setInterval(() => {
const memUsage = process.memoryUsage();
console.log(`Heap: ${Math.round(memUsage.heapUsed / 1024 / 1024)}MB`);
}, 5000);対処法:
// エージェント実行後、リソースを明示的に解放
async function runAgentTask(input) {
try {
const result = await agent.run(input);
return result;
} finally {
// クリーンアップ
agent.clearCache();
gc(); // ガベージコレクション強制実行(--expose-gc フラグで有効)
}
}
// または定期的にリスタート
setInterval(() => {
if (process.memoryUsage().heapUsed > 500 * 1024 * 1024) {
console.log('Memory threshold reached. Restarting agent...');
process.exit(0); // PM2 や Docker で自動リスタート
}
}, 60000);夜間に何時間も走らせるバッチ的な使い方をしていると、この「徐々に膨らんで最後に黙る」型がいちばん見つけにくいと感じています。私の場合は、しきい値を超えたら自分でプロセスを終了させてプロセスマネージャに任せる、という上の割り切りが結局いちばん安定しました。完璧にリークを潰すより、定期的に綺麗な状態へ戻すほうが、一人で運用するには現実的でした。
つまずきやすい 2 つのケース
Q: 同じプロンプトでも毎回異なる出力が返ってくる
→ Gemini モデルの temperature 設定を確認してください。temperature: 0.0 にすると出力が固定されます。
const agent = new Agent({
model: 'gemini-2-flash',
temperature: 0.0, // 0 = 決定的,1.0 = ランダム(デフォルト 0.7)
});Q: エージェントの応答が部分的(最後まで完成していない)
→ max_output_tokens が小さすぎる可能性があります。増やしてみてください:
const agent = new Agent({
maxOutputTokens: 4096, // デフォルト 1024 → 4096 に増やす
});次に試すこと
エージェントが黙り込んだら、まずは起動 → 認証 → ネットワーク → プロンプト → リソースの順に、上から一つずつ潰してみてください。多くの場合、原因は最初の 3 段のどこかにあります。
私自身は、この順序を短いチェックリストとして AGENTS.md に書き留めておき、エージェントが沈黙したらまずそれを開く、という運用に落ち着きました。原因を当てる速さは、知識量よりも「どの順で疑うか」をあらかじめ決めているかどうかで決まる、というのが個人開発を続けてきて得た実感です。さらに踏み込んだ設計は「マルチエージェント本番環境パターン」や「エージェント メモリパターン」もあわせてご覧ください。