はじめに
「現場で活用するためのAIエージェント実践入門」の第3章で私がつまずいたことのメモです。
(このメモのほかの章へ:1章 / 2章 / 3章 / 4章 / 5章 / 6章 / 7章 / 8章 / 9章 / 10章 / まとめ)
第3章 AIエージェントの開発準備
この章は、以降の章で必要になる基礎知識の解説です。なお、本で掲載されているソースコードはGitHubで公開されています。手元で動かしたい時はここから持ってきましょう。導入手順も解説されています。
3.1 OpenAI APIの基本
temperature、top_p、seedの説明の中で回答を固定する話がでてきますが、注釈3にもあるとおりランダム性を完全には消せません。
ランダム性はなぜ消せない?
少し脱線しますが、従来から言われている原因としては、確率が同じトークンの選ばれ方の揺れや、並列実行時に実行順序が保証されず浮動小数点演算の丸め順序が変わることなどがありました。
ただ、1ヶ月ほど前に新しい発見があり話題になりました。LLMの推論処理は複数のリクエストを束ねて一括処理することで効率を上げていますが、この動的バッチングが原因になることがあります。同じ入力でも実行の度に異なるリクエストと1つのバッチに詰め込まれる可能性があり、それによってアルゴリズムの分岐や計算順序が変化してしまい、丸め誤差の伝搬や同確率トークンの選択に影響してしまうのです。詳細についてはこちらを参照してください。
回答が揺れると評価の効率が悪くなるので、はやく固定できる日がきてくれるとうれしいです。
本の話に戻ります。「プログラムリスト 3.4」で使われているclient.beta.chat.completions.parse()は、ベータ版ではないclient.chat.completions.parse()でも動作します。機能は変わらないようなので、実際に使うコードではこちらの方がよいかもしれません。
# Structured Outputsに対応するPydanticモデルを指定して呼び出し
response = client.chat.completions.parse(
model="gpt-4o",
messages=[{"role": "user", "content": "タコライスのレシピを教えてください"}],
temperature=0,
response_format=Recipe,
)
そういえば、本に掲載されているコードでは「トマトソースパスタのレシピ」なのに、GitHubのコードでは「タコライスのレシピ」になっていました。不思議 ![]()
3.2 Function callingの活用方法
Function callingについては、特につまずく部分はありませんでした。
なお、tool_choiceでツールを強制するくらいなら直接呼べばよいのでは?と思われる方がいるかもしれませんが、呼び出したい関数のパラメーターを生成させる用途に使えます。
3.3 AIエージェントで利用されるツール
Tavily Searchを使う「プログラムリスト 3.7」は、実行時に警告が出ました。
LangChainDeprecationWarning: The class `TavilySearchResults` was deprecated in LangChain 0.3.25 and will be removed in 1.0. An updated version of the class exists in the :class:`~langchain-tavily package and should be used instead. To use it run `pip install -U :class:`~langchain-tavily` and import as `from :class:`~langchain_tavily import TavilySearch``.
もうすぐリリースされるLangChain 1.0で削除されてしまうクラスなので、新しいものにしましょう。まずパッケージを追加します。なお、古いパッケージlangchain-communityはこの後のSQLDatabaseなどで使うので削除してはいけません。
uv add langchain-tavily
コードの修正は2箇所です。まずimport部分。
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_tavily import TavilySearch
そして実際に検索する部分です。
# Tavily検索ツールを初期化
tools = [TavilySearchResults(max_results=3, tavily_api_key=os.getenv("TAVILY_API_KEY"))]
tavily_tool = tools[0]
# 検索の実行例
query = "AIエージェント 実践本"
results = tavily_tool.run(query)
# Tavily検索ツールを初期化
tools = [TavilySearch(max_results=3, api_key=os.getenv("TAVILY_API_KEY"))]
tavily_tool = tools[0]
# 検索の実行例
query = "AIエージェント 実践本"
response = tavily_tool.invoke({"query": "f{query}"})
results = response["results"]
これで検索できました。
検索クエリ: AIエージェント 実践本
検索結果数: 3
検索結果:
1. タイトル: 日立、AIエージェントの書籍『実践 AIエージェントの教科書』を出版
URL: https://prtimes.jp/main/html/rd/p/000000501.000067590.html
内容: プレスリリース・ニュースリリース配信サービスのPR TIMES * テクノロジー * モバイル * アプリ * エンタメ * ビューティー * ファッション * ライフスタイル * ビジネス * グル...
2. タイトル: 『RAG・AIエージェント[実践]入門』を読んだ - koicの日記
URL: https://koic.hatenablog.com/entry/reading-agent-book
内容: # [『RAG・AIエージェント[実践]入門』を読んだ](https://koic.hatenablog.com/entry/reading-agent-book) まとめると、アプリケーションレイヤ...
3. タイトル: 書評:「AIエージェント実践本」推しポイントと惜しポイント! - Qiita
URL: https://qiita.com/akiraabe/items/53a1441ac135e0bde56a
内容: LoginSignup Go to list of users who liked Share on X(Twitter) Share on Facebook Add to Hatena Bookma...
残念ながらこの本とは違うものがヒットしてしまいました。ちなみに2件目と3件目は赤い鳥の本1の書評のようです。
DuckDuckGoを使う「プログラムリスト 3.9」でも警告が出ました。
RuntimeWarning: This package (`duckduckgo_search`) has been renamed to `ddgs`! Use `pip install ddgs` instead.
with DDGS() as ddgs:
これはパッケージ名の変更なので、古いパッケージを削除して新しいパッケージを追加します。
uv remove duckduckgo_search
uv add ddgs
こちらはimportの修正だけでOKです。
from duckduckgo_search import DDGS
from ddgs import DDGS
検索結果:
1. AIエージェント関連書籍4冊を徹底比較 - karaage. [からあげ]
概要: Sep 30, 2025 · AIエージェント本 比較表 4冊の書籍について、プロンプトエンジニアリング、ライブラリ、MCP等の複数の観点から比較を実施しました。この調査自体も AIエージェント +コ...
URL: https://karaage.hatenadiary.jp/entry/2025/10/01/073000
2. 【2025年8月】AIエージェントがわかる本おすすめ5選 - Alfista NAO Bl...
概要: Jun 28, 2025 · AI エージェント について知りたい人のために、おすすめの 本 5選、注目の新刊、ロングセラー本、そして、初心者からよくある質問と回答、スキルが活かせる職種を紹介します。...
URL: https://alfistanao.com/recommended-ai-agent-books/
3. 現場で活用するためのAIエージェント実践入門 (KS情報科学専門書)
概要: Jul 17, 2025 · 生成AIの次なる段階として注目される「 AI エージェント 」について、その全体像を掴むための一冊として手に取りました。...
URL: https://www.amazon.co.jp/現場で活用するためのAIエージェント実践入門-KS情報科学専門書-太田-真人/dp/4065401402
最初の検索結果のURLにアクセスしています: https://karaage.hatenadiary.jp/entry/2025/10/01/073000
HTTPステータスコード: 200
HTMLコンテンツの大きさ: 156165 bytes
HTMLコンテンツの最初の部分:
b'<!DOCTYPE html>\n<html\n lang="ja"\n\ndata-admin-domain="//blog.hatena.ne.jp"\ndata-admin-origin="https://blog.hatena.ne.jp"\ndata-author="karaage"\ndata-avail-langs="ja en"\ndata-blog="karaage.hatenadiary.jp"\ndata-blog-host="karaage.hatenadiary.jp"\ndata-blog-is-public="1"\ndata-blog-name="karaage. [\xe3\x81\x8b\xe3\x82\x89\xe3\x81\x82\xe3\x81\x92]"\ndata-blog-owner="karaage"\ndata-blog-show-ads=""\ndata-blog-show-sleeping-ads=""\ndata-blog-uri="https://karaage.hatenadiary.jp/"\ndata-blog-uuid="11696248318755249454"\ndata-blogs-uri-base="https:'...
この本に関するページが、いい感じでヒットしました。
Text to SQLツールを使う「プログラムリスト 3.11」でも警告が出ました。
UserWarning: Directly instantiating an SQLDatabaseChain with an llm is deprecated. Please instantiate with llm_chain argument or using the from_llm class method.
調べたところ、SQLDatabaseChain.run()でSQL生成&実行まで一気に行う形はレガシーで、今はcreate_sql_query_chain()とdb.run()に分けて実行するのが推奨されているようです。また、最初のセルですでにcreate_sql_query_chainがインポートされていたので、これを使う形に変えてみました。
# 引数スキーマを定義
class SQLQueryArgs(BaseModel):
keywords: str
@tool(args_schema=SQLQueryArgs)
def text_to_sql_search(keywords: str):
"""
自然言語でのクエリをSQLクエリに変換し、SQLデータベースで検索を実行します。
機能:
- このToolは、与えられた自然言語形式のキーワードをもとに、SQLクエリを生成します。
- LLMを使用してSQL文を生成し、PostgreSQLデータベースで検索を実行します。
- 取得した検索結果を返します。
Args:
keywords (str): 実行したいクエリの自然言語キーワード。
例: "employeeテーブルの情報は何件ありますか?"
Returns:
Any: データベース検索結果を返します。
"""
try:
# PostgreSQLデータベース接続パラメータを設定する
# postgres-genai-ch3コンテナの設定を使用
db_url = "postgresql+psycopg2://testuser:testpass@localhost:5432/testdb"
db = SQLDatabase.from_uri(db_url)
# LLMの設定
llm = ChatOpenAI(
api_key=os.getenv("OPENAI_API_KEY"),
model="gpt-4o-mini",
temperature=0.0,
)
# SQLクエリチェーンを作成
query_chain = create_sql_query_chain(llm, db)
# SQLクエリを生成
print(f"Question: {keywords}")
sql_query = query_chain.invoke({"question": keywords})
sql_query = sql_query.replace("SQLQuery:", "").strip()
print(f"SQLQuery: {sql_query}")
# SQLを実行
result = db.run(sql_query)
print(f"SQLResult: {result}")
return result
except Exception as e:
return f"エラー: PostgreSQLデータベースに接続できません: {str(e)}\n\nセットアップ手順:\n1. chapter3ディレクトリで ./setup_postgres.sh を実行\n2. PostgreSQLコンテナが動作していることを確認"
# 実行例
args = {"keywords": "employeeテーブルの情報は何件ありますか?"}
text_to_sql_search.func(**args)
以下、実行結果です。問題なさそうですね。
Question: employeeテーブルの情報は何件ありますか?
SQLQuery: SELECT COUNT(*) AS "count" FROM employees;
SQLResult: [(10,)]
'[(10,)]'
3.3.2のドキュメント検索の説明で、「上記のようなベクトル検索まで実施するためには、専用の検索エンジンを用意する必要があります。」とありますが、キーワード検索の場合も同様に、専用の検索エンジンを用意するのがいいでしょう。逐次での検索やメモリにインデックスを保持する方式ではデータ量が多くなると対応できません。キーワード検索のエンジンとしてはElasticsearch、OpenSearch、Apache Solrなどがあり、最近はキーワード検索とベクトル検索の両方を備えたものも増えています。
3.4 Embeddings APIの紹介
本にあるように埋め込みモデル間のアップグレードには対応していないので最初の選択は重要ですが、クラウドサービスのモデルを利用している限り、いつかはそのモデルが終了してアップグレードを強制されてしまう日が来ます。そうなっても大丈夫なようにシステムや運用を設計しておきましょう。
少し脱線しますが、これはEmbeddingsモデルに限らずLLM全般にも言えます。1年ほど前の内容になりますが、前職でRAGのサービスのカスタマーサクセスを担当していた際の苦労話をMeetupイベントでお話したので、その時の資料を置いておきます。
3.5 Assistants APIの紹介
注釈26で補足されていますが、Assistants APIは廃止予定で、今後はResponses APIになります。これから学ぶ方はResponses APIを見ておくのがよいでしょう。
3.6 LangGraphによるエージェントワークフロー構築
ここでは概要のみがサラッと解説されています。詳細は第4章以降のユースケースで学んでいきましょう。
なお、本に掲載されている「プログラムリスト 3.13」のコードはreflectorからplannerへとつながっていますが、GitHubのコードではreflectorからgeneratorへとつながっています。
これは、GitHubのサンプルコードに埋めてある固定の振り返りが、計画への指摘ではなく生成に関する指摘のみになっているためのようです。
3.7 まとめ
この章も大きくつまずく点はありませんでした。次章からはいよいよユースケースを学んでいきます。
(このメモのほかの章へ:1章 / 2章 / 3章 / 4章 / 5章 / 6章 / 7章 / 8章 / 9章 / 10章 / まとめ)
