即座に解決するためのクイックフィックス
長文のAI生成において、connection resetほど厄介なものはありません。ストリームが文章の途中で途切れてしまう場合は、まず以下の調整を試してください。
- SDKのタイムアウト値を上げる: Claude 3.5 Sonnetは、複雑なプロンプトの処理に30秒以上かかることがあります。デフォルトの60秒のタイムアウトでは不十分です。少なくとも300秒に設定してください。
- プロキシのバッファリングを無効にする: Nginxを使用している場合、ストリームをバッファに保存しようとしている可能性があります。バッファが十分に速く埋まらないと、タイムアウトが発生します。この機能をオフにしてください。
- VPNを確認する: ZscalerやPalo Altoなどの企業向けファイアウォールは、30秒以上データの送信がないまま開いている「アイドル状態」のTCP接続を強制終了することがよくあります。
- ライブラリを更新する:
pip install -U anthropicまたはnpm install @anthropic-ai/sdk@latestを実行して、最新の接続ハンドリングロジックが適用されていることを確認してください。
実際には何が起きているのか?
「接続リセット」(ECONNRESET)は、電話が突然切れるようなものだと考えてください。サーバーが「ビジー状態」であることを通知する標準的なタイムアウトとは異なり、リセットは適切なハンドシェイクなしにソケットが閉じられたことを意味します。
このエラーは通常、「思考」フェーズ(Time to First Token)で発生します。Claudeが膨大なプロンプトを処理している間、接続は維持されていますが無音の状態が続きます。ロードバランサーやファイアウォールなどの中間ハードウェアがこの沈黙を検知し、接続が切れたと判断して、唐突に回線を遮断してしまうのです。
解決策1:SDKクライアントのタイムアウト調整
PythonおよびNode.jsのSDKのデフォルト設定は保守的です。4,000トークンのレポートを生成する場合、Claudeはチャンク間で一時的に停止することがあり、クライアントが早まって接続を諦めてしまう原因となります。
Python SDKでの実装
from anthropic import Anthropic
# 重いテクニカルライティングのタスクには、10分のタイムアウトを推奨します
client = Anthropic(
api_key="your_api_key",
timeout=600.0,
)
with client.messages.stream(
model="claude-3-5-sonnet-20240620",
max_tokens=4096,
messages=[{"role": "user", "content": "Write a 20-page technical manual for a nuclear reactor."}]
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
Node.js SDKでの実装
import Anthropic from '@anthropic-ai/sdk';
const anthropic = new Anthropic({
apiKey: 'my_api_key',
timeout: 600000, // ミリ秒単位で10分
});
const stream = await anthropic.messages.create({
max_tokens: 4096,
messages: [{ role: 'user', content: 'Explain quantum field theory in excruciating detail' }],
model: 'claude-3-5-sonnet-20240620',
stream: true,
});
for await (const event of stream) {
if (event.type === 'content_block_delta') {
process.stdout.write(event.delta.text);
}
}
解決策2:Nginxとリバースプロキシのチューニング
自社のインフラを経由してClaudeのリクエストをルーティングしている場合、Nginxがボトルネックになっている可能性があります。デフォルトでは、Nginxは接続を閉じる前に60秒間(proxy_read_timeout)待機します。また、レスポンスをバッファリングしますが、これはストリーミングにおいては逆効果です。
Nginxのlocationブロックに以下の設定を適用してください:
location /api/claude/ {
proxy_pass https://api.anthropic.com/;
proxy_http_version 1.1;
proxy_set_header Connection "";
# 重要:バッファリングがオンだとストリーミングが失敗します
proxy_buffering off;
proxy_cache off;
# Claudeが考える時間を確保する
proxy_read_timeout 600s;
proxy_send_timeout 600s;
# パイプを開いたままにする
keepalive_timeout 600s;
}
解決策3:アグレッシブなネットワーク機器への対策
企業のオフィスでは、connection resetはディープパケットインスペクション(DPI)ツールによって引き起こされることがよくあります。これらのツールは、データが継続的に送信されない長時間のHTTPS接続を嫌います。
- VPN外でテストする: モバイルホットスポットに切り替えてみてください。エラーが消える場合、社内ネットワークがストリームを遮断しています。
- MTUを下げる: Dockerや特定のVPNでは、1500バイトを超えるパケットが断片化(フラグメンテーション)されます。これが接続断の原因になることがあります。
sudo ifconfig eth0 mtu 1400を使用して、インターフェースのMTUを1400に設定してみてください。 - HTTP/1.1を強制する: 一部の古いプロキシは、Server-Sent Events(SSE)に対するHTTP/2のマルチプレクシングの扱いに苦労することがあります。環境でプロトコルの強制が可能か確認してください。
解決策4:回復力のある再試行ループの構築
ネットワークの一時的な不具合(瞬断)は避けられません。アプリをクラッシュさせるのではなく、指数バックオフ(exponential backoff)を用いた再試行メカニズムでストリームをラップしましょう。これにより、2秒、4秒、8秒と待機してから諦めるようになります。
import time
from anthropic import Anthropic, APIConnectionError
client = Anthropic()
def get_claude_response(prompt, retries=3):
for i in range(retries):
try:
with client.messages.stream(
model="claude-3-5-sonnet-20240620",
max_tokens=2048,
messages=[{"role": "user", "content": prompt}]
) as stream:
for text in stream.text_stream:
yield text
return
except APIConnectionError as e:
if i < retries - 1:
time.sleep(2 ** i)
# 再試行を継続
continue
raise e
修正を確認する方法
ただ直ったと期待するのではなく、以下の3つのステップでテストしてください。
- 負荷テスト: 「18世紀の建築の進化について3000語で書いてください」といったプロンプトを送信します。リセットされずに完了すれば、タイムアウト設定は適切です。
- 通信を検査する:
tcpdump -i any port 443を使用して、RST(リセット)パケットを探します。Nginxの設定変更後にパケットが表示されなくなれば、設定の問題は解決しています。 - 詳細ログを有効にする: ターミナルで
export ANTHROPIC_LOG=debugを設定します。これにより、接続が切断された正確なミリ秒が判明し、プロキシログと照らし合わせるのに役立ちます。

