FileSearchTool とファイルアップロードを Streamlit に組み込む
OpenAI Agents SDK の Hosted tools にある FileSearchTool を使うと、Vector Store に載せたドキュメントをセマンティック検索し、エージェントがその内容をもとに回答できます。
この記事では、txt ファイルのアップロード → Vector Store への紐付け → FileSearchTool による検索 → 履歴表示 までを、Streamlit アプリ main.py を例に一通り説明します。
完成イメージ
ユーザーが 自分の Apple 株の保有状況 を書いた txtを chat_input に添付し、「私は Apple 株を何株持っていますか?」のように ファイルの中身に関する質問 をします。
| コンポーネント | 役割 |
|---|---|
FileSearchTool |
Vector Store 内のファイルを検索(Hosted) |
upload_text_file |
txt → Files API → Vector Store |
Runner.run_streamed |
エージェント実行 + ストリーミング |
SQLiteSession |
会話と tool 呼び出し履歴の永続化 |
render_history |
保存済みメッセージをチャット UI に描画 |
Hosted tools とは
Agents SDK のツールのうち、OpenAI サーバー側で動く組み込みツール が Hosted tools です。
| 種類 | 実行場所 | 例 |
|---|---|---|
| Hosted tools | OpenAI サーバー |
WebSearchTool, FileSearchTool, CodeInterpreterTool
|
| Function tools | 自分の Python コード |
@function_tool で包んだ関数 |
FileSearchTool() を Agent の tools=[...] に渡すだけで、ファイル検索のインフラを自分で用意する必要はありません。検索ロジックは OpenAI 側が実行します。
Agent の定義 — FileSearchTool を載せる
Vector Store ID は定数として持ち、FileSearchTool に渡します。
from agents import Agent, FileSearchTool, WebSearchTool
VECTOR_STORE_ID = "vs_xxxxxxxx" # ダッシュボードで作成した ID
agent = Agent(
name="Assistant",
instructions="""
あなたはChatGPTのようなAIアシスタントです。
あなたは以下のツールにアクセスできます:
- WebSearchTool: 学習データに含まれない質問が来たときに使用してください。現在や将来の出来事についての質問、または答えが分からないと思ったときは、まずWebで検索してみてください。
- FileSearchTool: ユーザーが自分自身に関連する質問をした時に、使用できるツール
""",
tools=[
WebSearchTool(),
FileSearchTool(
vector_store_ids=[VECTOR_STORE_ID],
max_num_results=3,
),
],
)
ポイント
-
vector_store_ids— 検索対象の Vector Store(複数指定可) -
max_num_results— 返す検索結果の上限 -
instructions— いつ FileSearchTool を使うか を書くと、モデルが tool を選びやすい
Web 検索とファイル検索を両方載せる場合、instructions で使い分けを明示しておくのがおすすめです。
| ツール | 検索対象 | 向いている質問 |
|---|---|---|
WebSearchTool |
インターネット | 天気、ニュース、最新情報 |
FileSearchTool |
Vector Store 内のファイル | アップロードしたメモ・個人データ |
ファイルのアップロード — Files API + Vector Store
FileSearchTool が検索できるようにするには、ファイルを Vector Store に載せる 必要があります。
-
client.files.create— ファイル本体を OpenAI にアップロード -
client.vector_stores.files.create— その file ID を Vector Store に紐付け
from openai import OpenAI
client = OpenAI()
def upload_text_file(file) -> None:
with st.chat_message("assistant"):
with st.status("Analyzing file...") as status:
uploaded_file = client.files.create(
file=(file.name, file.getvalue()),
purpose="user_data",
)
status.update(label="Attaching file...")
client.vector_stores.files.create(
vector_store_id=VECTOR_STORE_ID,
file_id=uploaded_file.id,
)
status.update(label="File uploaded", state="complete")
注意: Store 作成 API とファイル追加 API は別
| やりたいこと | API |
|---|---|
| Vector Store を新規作成 | client.vector_stores.create(...) |
| 既存 Store にファイルを追加 | client.vector_stores.files.create(vector_store_id=..., file_id=...) |
ダッシュボードで Store を作ってある場合は、vector_stores.files.create だけ使えば十分です。vector_stores.create に vector_store_id を渡そうとして失敗する、というミスがよくあります。
エージェント実行 — ストリーミングと status 表示
Runner.run_streamed でエージェントを動かし、raw_response_event から進行状況と回答テキストを受け取ります。
from agents import Runner
async def run_agent(message: str) -> str:
with st.chat_message("assistant"):
status_container = st.status("⏳", expanded=False)
text_placeholder = st.empty()
response = ""
stream = Runner.run_streamed(agent, message, session=session)
async for event in stream.stream_events():
if event.type == "raw_response_event":
update_status(status_container, event.data.type)
if event.data.type == "response.output_text.delta":
response += event.data.delta
text_placeholder.write(response.replace("$", "\\$"))
return response
ファイル検索中は event.data.type に次のような値が流れます。
def update_status(status_container, event: str) -> None:
status_messages = {
"response.file_search_call.in_progress": ("🔍️ Starting file search", "running"),
"response.file_search_call.searching": ("🔍️ File search in progress", "running"),
"response.file_search_call.completed": ("✅ File Search Completed", "complete"),
"response.completed": ("✅ Response Completed", "complete"),
}
if event in status_messages:
label, state = status_messages[event]
status_container.update(label=label, state=state)
event.data.type |
UI |
|---|---|
response.file_search_call.in_progress |
ファイル検索開始 |
response.file_search_call.searching |
検索中 |
response.file_search_call.completed |
検索完了 |
response.output_text.delta |
回答テキストのストリーミング |
Hosted tool の進行表示は、イベント型を辞書にマップする のがシンプルです。function_tool 名をパースするより、API が返す event.data.type をそのまま使えます。
Web 検索も載せている場合は、response.web_search_call.* を同じ辞書に足すだけです。
実行
web-search toolとfile-search toolを同時に起動させてみる
まとめ
-
FileSearchTool(vector_store_ids=[...])— Vector Store 上のドキュメントを Agent が検索できる -
アップロードは 2 段階 —
files.create→vector_stores.files.create(Store 作成 API と混同しない)
FileSearchTool を載せると、LLM アプリが ユーザーが渡したドキュメント に基づいて答えられるようになります。Web 検索(WebSearchTool)と組み合わせれば、最新情報 と 個人のファイル の両方に触れるチャットエージェントに触れます。
