TL;DR: クイックフィックス
スレッドは永続的なものではありません。OpenAIは30日間アクティビティがないスレッドを自動的に削除します。アプリのクラッシュを防ぐには、メッセージ送信ロジックを try-except ブロックで囲みます。404エラーをキャッチした場合は、単に新しいスレッドを作成し、新しいIDをデータベースに保存して、リクエストを再試行します。
import openai
try:
# 既存のスレッドの使用を試みる
client.beta.threads.messages.create(
thread_id=stored_thread_id,
role="user",
content="こんにちは!"
)
except openai.NotFoundError:
# スレッドが消失している場合は、新しいスレッドを開始する
new_thread = client.beta.threads.create()
stored_thread_id = new_thread.id
# ここでデータベースを更新する
client.beta.threads.messages.create(
thread_id=stored_thread_id,
role="user",
content="こんにちは!"
)
なぜスレッドが消えるのか
スレッドは会話を保持するコンテナですが、永久に保持されるわけではありません。PostgreSQLやRedisなどのデータベースに thread_id を保存していても、そのIDがリンク切れになることがあります。これには主に2つの理由があります。
- **30日間の有効期限:** OpenAIは現在、**30日間アクティビティがない**スレッドを削除します。この期間内にユーザーがメッセージを送信したり、実行(Run)をトリガーしたりしない場合、スレッドは完全に消失します。
- **手動によるクリーンアップ:** `client.beta.threads.delete()` メソッドを使用したり、テスト中に OpenAI ダッシュボードから誰かがスレッドを削除した可能性があります。
スレッドが削除されると、そのスレッドを取得したりメッセージを追加しようとしたりするたびに404エラーが発生します。コードはこの「消失」状態を適切に処理できるようにしておく必要があります。
404エラーを処理するスマートな戦略
1. 「レイジーチェック」パターン
1つの方法は、スレッドを使用する前にその存在を確認することです。ただし、これには余分なAPI呼び出しが必要になり、やり取りごとに約 150msから300msのレイテンシ が加わります。複雑なロジックを実行する前にスレッドの状態を確認する必要がある場合にのみ、この方法を使用してください。
def get_active_thread_id(user_id):
thread_id = db.get_thread_for_user(user_id)
if not thread_id:
return create_and_save_new_thread(user_id)
try:
client.beta.threads.retrieve(thread_id)
return thread_id
except openai.NotFoundError:
return create_and_save_new_thread(user_id)
2. 「フェイルファスト&リカバリ」パターン(推奨)
事前に確認するのではなく、まずメッセージを送信してみます。これによりAPIコストを節約し、アプリのレスポンスを軽快に保つことができます。スレッドが存在しない場合、APIはすぐにエラーを返します。このアプローチは、ほとんどのスレッドがまだアクティブであるトラフィックの多いアプリケーションにおいて、より効率的です。
def send_message(user_id, user_input):
thread_id = db.get_thread_for_user(user_id)
try:
return client.beta.threads.messages.create(
thread_id=thread_id,
role="user",
content=user_input
)
except openai.NotFoundError:
# スレッドが消失しているため、新しいものを作成して再試行する。
new_thread = client.beta.threads.create()
db.update_thread_for_user(user_id, new_thread.id)
return client.beta.threads.messages.create(
thread_id=new_thread.id,
role="user",
content=user_input
)
3. 長期的なコンテキストの管理
新しいスレッドを開始するということは、以前のチャット履歴が失われることを意味します。ボットが数ヶ月前の内容を記憶しておく必要がある場合は、OpenAIのスレッドストレージだけに頼らないでください。重要な会話の節目を独自のデータベースに保存しましょう。スレッドの期限が切れた際、過去のやり取りの要約を最初のメッセージとして注入することで、新しいスレッドを「再プライミング(初期化)」できます。
リカバリロジックのテスト
修正が機能するかどうかを確認するために30日間待つ必要はありません。数秒で有効期限切れをシミュレートできます。
- 開発環境からアクティブな `thread_id` を取得します。
- スクリプトまたはターミナルで `client.beta.threads.delete("thread_ID_here")` を実行します。
- アプリのメッセージ送信機能をトリガーします。
- データベースを確認します。古いIDが `thread_` で始まる新しいIDに置き換わっているはずです。
参考文献
- [OpenAI API リファレンス:スレッド](https://platform.openai.com/docs/api-reference/threads)
- [公式ガイド:スレッドの管理](https://platform.openai.com/docs/assistants/how-it-works/managing-threads-and-messages)
- [OpenAI Python SDK 例外ソース](https://github.com/openai/openai-python/blob/main/src/openai/_exceptions.py)

