ChatKitで添付ファイルをエージェントに渡して解析させる
昨日のエントリ『ChatKitでファイルアップロード機能を実装する』では,ChatKitのチャットUIにファイルアップロード機能を追加しました。今回は,アップロードされた添付ファイルをエージェントが使うコンテキストとして利用できるように,添付ファイルをLLMへの入力に変換します。
ドキュメントはこちら: Convert attachments to model input
OpenAI Files経由でエージェントに添付ファイルを渡すときの注意
OpenAI FilesとChatKitを併用する時にハマった点です。
Files APIへのファイルアップロード(create)時に,fileパラメータにはデータ本体だけでなく,コンテンツタイプ(MIMEタイプ)も含めておきましょう。コンテンツタイプを含めておかないと,ファイル利用時,つまりエージェントに読ませようとしたタイミングで BadRequestError が発生します。エラーの原因がわかれば,それはそう...なのですが,特に注記もなくデバッグに時間がかかったので紹介しておきます。
# これはOK
response = await self._openai_client.files.create(
file=(filename, data, content_type),
purpose="user_data",
)# これはNG
# ファイルアップロードは成功するけど,Responses APIで利用するときに「サポートしていないフォーマットです」というエラーになる
response = await self._openai_client.files.create(
file=data
purpose="user_data",
)添付ファイルをエージェントに渡す
ThreadItemConverterを継承したクラスを作成して,attachment_to_message_contentというメソッドを実装します。
LLMにファイルを与えるメッセージの作り方は,エージェントで画像やPDFファイルを解析する - Files APIとAgents SDKとResponses APIとほぼ同じです。
class FileAttachmentConverter(ThreadItemConverter):
@override
async def attachment_to_message_content(
self, attachment: Attachment
) -> ResponseInputContentParam:
"""添付ファイルをエージェント(LLM)への入力メッセージに変換する"""
# 基底クラスのThreadItemConverterでは未実装なので,必ずオーバーライドが必要
if isinstance(attachment, ImageAttachment):
# 画像添付ファイルの場合
# 戻り値の型はTypedDictで定義されているので,ソースコードとResponses APIのリファレンスを参照のこと
return ResponseInputImageParam(
type="input_image",
file_id=attachment.id, # OpenAI FilesのファイルID
detail="auto",
)
elif isinstance(attachment, FileAttachment):
# その他のファイルの場合
return ResponseInputFileParam(
type="input_file",
file_id=attachment.id, # OpenAI FilesのファイルID
)
else:
# should not happen
raise ValueError(f"Unsupported attachment type: {type(attachment)}")あとは,作成したクラスをChatKitServerの初期化時に渡します。
class MyChatKitServer(ChatKitServer[dict[str, Any]]):
"""ChatKit server wired up with the virtual cat caretaker."""
def __init__(self, store: MemoryStore, attachment_store: FileAttachmentStore, thread_item_converter: ThreadItemConverter) -> None:
self.store = store
super().__init__(self.store, attachment_store)
self.thread_item_converter = thread_item_converter # カスタムThredItemConverterをセット
...ChatKitで画像ファイルをアップロードして解析できるようになった
フロントエンドとバックエンドを起動。
# フロントエンド
npm run dev
# バックエンド
export OPENAI_API_KEY="sk-..."
python -m uvicorn app.main:app --reload --port 8000cookies1.jpg

cookies2.jpg

2つ画像をUIから上げて,画像について質問します。手抜きをしていてプレビュー機能を実装していないので,画像のサムネイルが表示されていません。

モデルはgpt-5を使っています。1枚目の画像は簡単そうですが,どう見ても15枚なので数え間違いをしている。2枚目も間違っていると思う(7枚か9枚ならわかる)けど,これはなんで8枚と認識したんだろう?
↑これはLLMの性能問題ではなくて,画像の与え方の問題でした。ResponseInputImageParamのdetailパラメータをautoからhighに変更すると,よりトークンを消費して,高い解像度の画像がLLMに渡されます。参考: input detail level
return ResponseInputImageParam(
type="input_image",
file_id=attachment.id, # OpenAI FilesのファイルID
detail="high", # "high"だと画像認識精度が上がる
)
ただしい画像認識ができました。
これは Agents SDK+αのTipsを一人で書いていくアドカレ Advent Calendar 2025の7日目の記事です。