はじめに
「車がタイムマシンになる80年代の映画で…」「あの俳優さん、他にどんな作品に出てたかな?」
このもどかしい「なんだっけ?」という大抵の問題は ChatGPT などは簡単に解決できる。解決できるのだが、その情報がハレーションではないかと疑ってしまう。また 最新トレンドなどの情報については TMDB の情報のほうが、生成AIがWeb検索ツールを利用して取得するよりも最新で正確な情報が取得できるようだ。
そこで、LangChain と TMDB API を使って、高機能なコンテンツ検索エージェントTMDBSearchAgent
の開発に挑戦してみた。
ソースコード
今回の記事のソースコードは下記から参照できます
TMDB APIについて
今回のエージェント開発では、The Movie Database (TMDB) API を利用しました。
TMDBとは?
TMDB (The Movie Database)は、映画・TV番組・人物に関する情報を集めた、コミュニティベースの巨大なデータベースです。ユーザーが情報を追加・編集できるため、データは常に更新され、非常に網羅性が高いのが特徴です。
その膨大なデータを開発者がプログラム経由で利用できるようにしたのが、TMDB APIです。
TMDB APIでできること
このAPIを使うことで、以下のような様々な情報を取得できます。今回のエージェントが持つ機能は、ほぼ全てこのAPIの能力に基づいています。
- 検索: 作品タイトルや人物名での検索
- 詳細情報: あらすじ、公開日、評価、ポスター画像などの取得
- クレジット情報: 監督、キャスト、スタッフの一覧を取得
- トレンド: 日別・週別の人気作品や人物のランキングを取得
- 制作会社: 特定の制作会社が関わった作品の一覧を取得
利用には無料のAPIキー登録が必要ですが、ドキュメントも整備されており、個人開発で利用するには十分すぎるほど高機能なAPIです。
完成したエージェントの姿
完成したTMDBSearchAgent
は、以下のことに対応した
- マルチ検索: 映画、TV番組、人物をキーワード一つで横断検索
- 詳細情報取得: キャストや監督といったクレジット情報を深掘り
- トレンド検索: 「今日の人気映画」や「今週話題の俳優」を即座に回答
- 制作会社検索: 「スタジオジブリの作品一覧」のような検索にも対応
- 補完検索: TMDBにない情報は、Web検索ツール(Tavily)で補完
- 多言語対応: 日本語の曖昧な表現を理解し、適切な言語でAPIを叩き分けます
システムアーキテクチャ
LangChainの ReAct (Reason + Act) という考え方で、エージェントが単にツールを呼び出すだけでなく、「思考」と「行動」を繰り返しながら自律的に答えにたどり着く仕組みを利用する。
- ユーザーの質問を受けると、エージェントはまず思考 (Thought) します。「この質問に答えるには、どの情報が必要で、どのツールを使えばいいだろう?」と
- 次に行動 (Action) として、使うツールと引数を決定します
- ツールを実行し、得られた結果を観察 (Observation) します
- そして、その観察結果をもとに、また思考する…というサイクルを、最終的な答えが見つかるまで繰り返します
- 最終回答 (Final Answer) として、すべての情報を統合してユーザーに返します
このアーキテクチャのおかげで、まるで人間が調査するように、段階的に情報を集めて回答を組み立てるエージェントが実現できます
開発の舞台裏:工夫と試行錯誤のポイント
開発プロセスで特にこだわった点や、つまずいた点を示す
1. 開発環境の整備
まず、モダンで高速なパッケージマネージャーuv
を導入し、開発環境を整えました。pyproject.toml
に必要なライブラリを記述し、uv sync
一発で環境が再現できるようにしています。
# pyproject.toml
[project]
name = "media-search-agent"
version = "0.1.0"
requires-python = ">=3.13"
dependencies = [
"langchain>=0.3.27",
"langchain-community>=0.3.27",
"sudachipy>=0.6.10", # 日本語処理のキーパーツ!
"langdetect>=1.0.9",
# ...
]
特にsudachipy
は、後述する日本語検索の精度を上げるための重要な選択でした。
2. エージェントの手足となる「ツール」
エージェントの能力は、使える「ツール」で決まります。
課題:LLMが意図しない引数を渡してくる
当初、ツールの引数を単純な文字列で渡していたところ、LLMが説明文をそのまま渡してきたり、形式が微妙に違っていたりと、エラーが頻発しました。
解決策:@tool
デコレーターとPydanticによる厳格なスキーマ定義
この問題を解決するのが、Pydanticでした。各ツールが期待する引数をBaseModel
として厳格に定義し、@tool
デコレーターのargs_schema
に指定しました。
# tools.py
class MovieSearchInput(BaseModel):
"""映画検索の入力パラメータ"""
query: str = Field(
description=(
"検索する映画タイトルまたは最小限の関連キーワードのみを指定する。"
"説明文・引用符・装飾・改行は不可。"
"関係ない語句(例: 80年代, 車, 教えて等)は含めない。"
"複数語は半角スペース区切り、中黒や句読点は使わない。"
),
min_length=1,
max_length=64,
examples=[
"バック トゥ ザ フューチャー",
"Back to the Future",
"ターミネーター 2",
],
)
# ...
このアプローチにより、LLMに対して「query
には映画タイトルだけを入れてね」と明確に指示できるだけでなく、バリデーションも自動で行えるようになり、エージェントの安定性が劇的に向上しました。
課題:日本語の検索ヒット率が低い…
「バックトゥザフューチャー」のようにスペースなしで入力されると、TMDB API では検索精度が落ちる、もしくは検索に失敗する問題が発生しました。
解決策:SudachiPy
による形態素解析
そこで、入力が日本語の場合、SudachiPy
で一度形態素解析を行い、適切に単語を分割してから検索クエリを生成するようにしました。
# tools.py
TOKENIZER = dictionary.Dictionary().create()
MODE = tokenizer.Tokenizer.SplitMode.B
def tokenize_text(text):
return [m.surface() for m in TOKENIZER.tokenize(text, MODE)]
# 各ツール内で
if lang_code == "ja-JP":
query = " ".join(tokenize_text(query)) # "バックトゥザフューチャー" -> "バック トゥ ザ フューチャー"
この一手間が、日本語での使い勝手を大きく左右しました。
3. プロンプトエンジニアリングの試行錯誤
開発中、最も時間と労力を費やしたのが、エージェントの思考を司る「プロンプト」の調整でした。最初は単純な指示で動かしていたのですが、すぐに様々な問題に直面しました。特に苦労したのは、以下の2点です。
(1)最適なツールをどう選ばせるか?ー 無駄なAPIコール
最初に直面した問題は、エージェントが非効率な動きをすることでした。例えば、「『バック・トゥ・ザ・フューチャー』の監督は?」と質問すると、映画情報を検索してからクレジット情報を検索するという、回りくどいステップを踏んでいました。
この無駄をなくすため、プロンプトに具体的な行動ルールを明記しました。
CREDITS-FIRST DECISION RULES (NO PRE-SEARCH):
- If the user asks for director/cast/crew/credits of a specific title:
- For movies, call tmdb_movie_credits_search directly with the title string.
- For TV shows, call tmdb_tv_credits_search directly with the title string.
- Do NOT call tmdb_movie_search or tmdb_tv_search beforehand. The credits tools already find the title internally and then fetch credits.
- If the title is ambiguous or unknown, use tmdb_multi_search once with a single keyword to identify the likely title, then immediately call the appropriate credits tool with that exact title.
- Only use tmdb_credits_search_by_id if you already have a numeric TMDB ID.
この「クレジットファーストルール」を導入することで、「クレジットに関する質問が来たら、まず最初にtmdb_movie_credits_search
を使いなさい」と直接的に指示できます。これにより、エージェントは1回のツール呼び出しで済むようになり、応答速度と効率が大きく改善されました。
(2)ReActの思考ループをどう安定させるか? ー 暴走するエージェント
ReActエージェント開発で最も頭を悩ませるのが、エージェントが思考の途中で「壊れてしまう」現象です。ReActはThought:
、Action:
、Action Input:
という厳格な形式に従う必要がありますが、LLMは時々このルールを破り、プロセス全体がエラーで停止してしまうことが頻発しました。
これを解決するため、プロンプトで「お作法」の調整に手間取りました。
STRICT OUTPUT FORMAT RULES:
1. Only use the exact ReAct schema lines (Thought/Action/Action Input/Observation/.../Final Answer).
2. Do NOT write any normal prose before 'Final Answer:'; only schema lines are allowed.
3. Action line must contain only the tool name (e.g., tmdb_trending_people).
4. Action Input line must contain only the input (empty string for no-argument tools).
5. End with 'Final Answer:'.
6. Never include Action Input and Final Answer in the same response.
7. Always inspect Observation before writing Final Answer.
8. Action Input must be a single query (no 'or', no multiple queries).
4. 実際に動かしてみる
「その映画の主演は?」のような文脈に沿った会話ができるように、直近の会話履歴を保持するシンプルなメモリ機能も実装しました。
uv run python -m tmdb_agent.main --chat
出力例1
ChatGPTの出力とほぼ変わりません
🎬 あなた: バック・トゥ・ザ・フューチャーの監督と出演者を教えて。
🤖 AI: 「バック・トゥ・ザ・フューチャー」の監督はロバート・ゼメキスです。
主な出演者は以下の通りです。
- マイケル・J・フォックス(Marty McFly役)
- クリストファー・ロイド(Emmett Brown役)
- クリスピン・グローヴァー(George McFly役)
- リー・トンプソン(Lorraine Baines役)
- クラウディア・ウェルズ(Jennifer Parker役)
- トーマス・F・ウィルソン(Biff Tannen役)
出力例2
ChatGPTの場合は 映画.com などの情報を参考にしているため、どちらが良いかはおいておて、結果がかなり異なりました。
🎬 あなた: トレンド映画を教えて
🤖 AI: 現在のトレンド映画トップ10は以下の通りです。
1. ジュラシック・ワールド/復活の大地
2. Weapons
3. ザ・ピックアップ ~恋の強盗大作戦~
4. ヒックとドラゴン
5. 28年後...
6. サンダーボルツ*
7. トリプルX
8. 哪吒之魔童闹海
9. スーパーマン
10. リロ&スティッチ
出力例3
こちら日本ではまだ最新版が未公開なので「ヒックとドラゴン」では旧作の情報ばかりが検索にでてきますが、TMDBでは情報が取得できました。
🎬 あなた: 4の映画について詳しく教えて
🤖 AI: 「ヒックとドラゴン」(How to Train Your Dragon)は、2025年6月6日に公開された映画で、評価は8.021と高評価です。物語は、バイキングとドラゴンが長年敵対してきたバーク島を舞台にしています。主人公のヒックは、傷ついたドラゴンのトゥースと出会い、敵同士であるはずの二人が徐々に心を通わせていく冒険を描いています。シリーズとしては、2010年の初作から続く人気作で、ドラゴンと人間の共存や成長をテーマにした感動的なストーリーが特徴です。
まとめ
「あの映画なんだっけ?」という問題を解決する、LangChainを使ったエージェント開発を試みたのですが、この分野で最新のChatGPT5以上のより良い結果を返すエージェントの作成はかなり難しそうです。