OpenAI Assistants APIで旅行AIを作る方法|Function Callingと継続スレッドの実装ガイド
このやり方で何ができるか
OpenAIの Assistants API を使うと、単発の質問に答えるだけではなく、ユーザーとの会話を続けたまま、外部のプログラムやデータベースと自動で連携できるAIを作ることができます。
たとえば「10月の京都旅行で3日間のプラン考えて」と聞かれたら、AIが「何人で行く?」と返して、答えが返ってくるのを待ち、その情報をホテル予約サイトと連携して実際の空き状況を確認し、「このホテルが空いてますよ」と提案する。こういう一連の流れが実現できます。
もう一つの大事な点が「会話の記憶」です。通常のChatGPTは「今この瞬間のやり取り」だけを考えて返事をしますが、このやり方なら過去のやり取り全部を覚えたままで、10回目の質問のときも「さっき教えてもらった人数」を参考に答えられるようになります。
準備するもの
・OpenAIのアカウント(ChatGPT有料版でなくOK。APIキーが必要) ・Pythonの基本知識(3年目以上の業務経験があれば十分) ・FastAPIまたはFlask等のWebアプリケーション枠組み(フレームワーク) ・テストの為の簡単なツール(curl コマンドまたはPostman等)
手順(15分程度で概念を理解)
1. Function Calling の概念をつかむ(3分)
通常のAIは「答え」をテキストで返すだけです。しかし Assistants API の Function Calling を使うと、AIが「このデータを持ってきてほしい」「このプログラムを実行してほしい」という「指示」を出せるようになります。
具体的には:
- ユーザー質問:「10月で空いてるホテルある?」
- AI の返答:「ホテル検索という関数を実行してほしい。検索条件は『10月、京都、3人』」
- プログラム(あなたが書いたコード):その指示を受け取り、実際にホテルDB を調べて、結果をAIに返す
- AI の 2番目の返答:「見つかりました。〇〇ホテルが1泊8000円です」
このようなやり取りが可能になるということです。
2. Persistent Threads の仕組みを理解する(3分)
Persistent Threads(永続スレッド)は「会話の記録」です。
通常の API では:
ユーザー1回目:「旅行計画立ててください」
AI:「何人ですか」
(ここで会話終了)
ユーザー2回目に別の質問:「ホテルもさがして」
AI:「何人ですか」← 前の会話を忘れてる
Persistent Threads を使うと:
ユーザー1回目:「旅行計画立ててください」
AI:「何人ですか」
(スレッド ID: thread_abc123 に記録)
ユーザー2回目:「ホテルも探して」(同じ thread_abc123 を使う)
AI:「さっき聞いた3人ですね。ホテルを探します」← 覚えている
つまり Thread ID という識別番号を持ち続けることで、会話の履歴全体が保存され、AIがそれを参考に返答できるということです。
3. 最小限のコード構造を書く(5分)
実装には以下の 3つの部分が必要です。
(1)Assistants の作成(初回のみ)
from openai import OpenAI
client = OpenAI()
# アシスタント(会話相手のAI)を作成
assistant = client.beta.assistants.create(
name="旅行プランナーAI",
instructions="ユーザーの旅行計画をサポートしてください。ホテルや飛行機の検索が必要なら search_hotel 関数を使ってください。",
model="gpt-4"
)
print(f"Assistant ID: {assistant.id}")
実行するとアシスタント ID が得られます。これ以降はこの ID を指定して使います。
(2)関数定義(Function Calling の仕組み)
# アシスタントに「このような関数が使える」と教える
tools = [
{
"type": "function",
"function": {
"name": "search_hotel",
"description": "指定された日付と人数でホテルを検索します",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "検索地域(例:京都)"
},
"date": {
"type": "string",
"description": "チェックイン日(YYYY-MM-DD形式)"
},
"people": {
"type": "integer",
"description": "人数"
}
},
"required": ["location", "date", "people"]
}
}
}
]
# アシスタントを更新
client.beta.assistants.update(
assistant.id,
tools=tools
)
このコードでアシスタントに「search_hotel という関数が使えるよ」と知らせます。
(3)会話ループ(実際の やり取り)
# スレッド作成(初回のみ)
thread = client.beta.threads.create()
thread_id = thread.id
# ユーザーメッセージを追加
message = client.beta.threads.messages.create(
thread_id=thread_id,
role="user",
content="10月に京都で3人の旅行プランを立ててください"
)
# Assistants を実行
run = client.beta.threads.runs.create(
thread_id=thread_id,
assistant_id=assistant.id
)
# 実行完了を待つ(ポーリング)
while run.status != "completed":
run = client.beta.threads.runs.retrieve(
thread_id=thread_id,
run_id=run.id
)
# 関数呼び出しが必要な場合
if run.status == "requires_action":
# AI が「search_hotel を実行してほしい」と言った場合の処理
tool_calls = run.required_action.submit_tool_results.tool_calls
for tool_call in tool_calls:
# 実際のホテル検索処理(例)
if tool_call.function.name == "search_hotel":
result = "〇〇ホテル:1泊8000円" # 実装例
# 結果をスレッドに送り返す
client.beta.threads.runs.submit_tool_results(
thread_id=thread_id,
run_id=run.id,
tool_results=[
{
"tool_call_id": tool_call.id,
"output": result
}
]
)
# AIの返答を取得
messages = client.beta.threads.messages.list(thread_id=thread_id)
print(messages.data[0].content[0].text)
4. 実装時の確認ステップ(4分)
次の 4つを順番に試してください:
Step 1:OpenAI API キーをセット
export OPENAI_API_KEY="sk-proj-xxxxxxxx" # 実際のキーに置き換え
Step 2:最小限のテストコードを実行 アシスタント作成のコードだけ走らせて、ID が得られるか確認。
Step 3:ユーザーメッセージを送る 会話ループのコードで「簡単な質問」(関数呼び出しなし)をテスト。答えが返るか確認。
Step 4:関数呼び出しが発動するメッセージでテスト 「ホテルを探して」など、関数が必要な質問をして、requires_action 状態になるか確認。
つまずきやすいところ
問題 1:「requires_action が返ってこない」
原因:関数の定義がアシスタントに反映されていない可能性があります。
解決方法:assistants.update() で新しいツールを登録した後、必ず新しい run を作り直してください。キャッシュが残っていることがあります。
問題 2:「関数の結果を返したのに次に進まない」
原因:submit_tool_results() のレスポンス後、run.status をすぐに確認する前に一呼吸置く必要があります。
解決方法:submit_tool_results() 後に、小さなスリープ(0.5秒)を入れてから run.retrieve() で状態を確認してください。
import time
client.beta.threads.runs.submit_tool_results(...)
time.sleep(0.5)
run = client.beta.threads.runs.retrieve(...)
問題 3:「複数回の関数呼び出しが連続で必要な場合どうする?」
たとえば「ホテルを探した後、その近くのレストランも探す」みたいなケースです。
解決方法:while ループの中に tool_calls に対するループを入れることで、複数の関数をまとめて処理できます。OpenAI側が「次は requires_action」と判定するまでループが続きます。
慣れてきたら試したいこと
・WebアプリケーションとしてFastAPI で公開する
今までのコードはスクリプトですが、これを Web API にすると、フロントエンド(ブラウザやモバイルアプリ)から呼び出せるようになります。
FastAPI で StreamingResponse を使うと、ホテル検索の結果を「待つ間も少しずつ表示」みたいなリアルタイム表示も可能になります。
・複数の関数を組み合わせる
「ホテル検索」「飛行機検索」「レストラン検索」など、複数の関数をアシスタントに教えることで、「3日間の京都旅行プランを丸ごと立ててください」という複雑な依頼に対応できるようになります。
・AIのシステム指示を工夫する
Assistants の instructions パラメータの書き方を工夫することで、AIの返答スタイルを変えられます。
たとえば:
- 「敬語で丁寧に返答してください」
- 「常に予算の観点からコスト・パフォーマンスの良さを優先してください」
- 「3人用のプランを提案するときは、必ずグループで割った1人当たりの料金も表示してください」
こういった指示を加えることで、より実務的で使いやすいAIになります。
あわせて読みたい
- ClaudeとChatGPTで業務自動化と栄養管理アプリを自分で作る方法
- 本番環境でAIエージェントが暴走するのを防ぐ:料金・間違い・情報漏洩を守る実践ガイド
- RAGパイプライン構築の実装手順:LangChainとベクトルDBで作る質問応答システム