前回: https://qiita.com/drafts/c10fa1eb6149c66aa36f/edit
第Ⅱ部AIエージェントを作る
第3章 開発準備
3.1 生成AI時代におけるAPI利用のメリット
3.1.1 Chat Completions API の基本
3.1.2 OpenAI API の使い方
- API、意外と種類あるんだよなー。あんまり把握しきれてない。たまにはちゃんと見ないとな
- この本に書いてあるのももう古そう。あとでちゃんと見ようかな
3.1.3 代表的なモデル
- フラッグシップモデルとして4oが紹介されている。いまは5だね
3.1.4 推論モデル(Reasoning models)
- oシリーズではシステムプロンプト書けないのか。APIから使ったことないから知らなかった
- てか推論モデルってなんなんだろ。中身どうなってるんだろ。ReActっぽいけど、生成前に推論するらしいし、違うんだろうな。あとで調べよう
3.1.5 構造化出力(Structured Outputs)
- うーん??書き方が2種類紹介されてる。よくわからん
- まあいいか。Pydanticで構造化出力が結構な精度でできるよってことだね。最新の情報は後で自分で調べよう。大体わかればOK
3.1.6 Prompt caching
- 同じようなプロンプトを投げたらコストがあまりかからなくなるらしい
- こちらで同行できる話でもないらしい。(手動で有効無効設定することもできず、キャッシュクリアできるわけでもない)
- 同じようなプロンプトを投げる意識だけ持っていればいいのかな
- こっちのプロンプトも向こうの出力もキャッシュされるみたいだから、文脈が大体似てればプロンプトは似せなくていいのかも?
3.2 Function calling の活用方法
- Tool useとかTool callingとかいろんな呼び方がある
- Toolを使った実行はLLMを2回叩く
- 1回目: ツールの選択と引数の内容
- (クライアント側でツール実行)
- 2回目: 実行結果をもとに最終出力をもらう
3.3 AIエージェントで利用されるツール
3.3.1 Web検索
- 検索クエリが「AIエージェント 実践本」になってるけど、検索結果が0だったり、謎の中国語のサイトだったりした。コードのバグかと思ったけど、普通にヒットしてないだけだった
- 「講談社」とかでよかったのでは、、、
- duckduckgoのとこ、ちょっとよくわからなかったな。langchainもduckduckgoも呼ばれてて、langchainがやってくれないのかって感じ
- まあこの辺は使う時にドキュメント読めばいいか
3.3.2 非公開情報を対象とした検索
- setup.shなんてあったんだ。すごい。手が込んでる
- VSCodeのワークスペースもだけど、使いやすくていい
- 全技術書、これくらい環境構築しやすくして欲しい
- SQLDatabaseChainはいいけど、内部的に何してんのって思う。汎用性がないじゃん
- langchainとかはライブラリ的に使うのはアリだけど、フレームワークほど依存したくないな
3.3.3 Code Interpreter
- Pythonのサンドボックス実行環境が使えるらしい。良さそう
3.4 Embedding APIの紹介
3.5 Assistants APIの紹介
- メモリ管理、Code Interpreterと、簡易的なRAGがAPIで使えるらしい
- 使い勝手わからんけど、ちょっとしたやつなら便利なのかな
column OpenAI 以外のAPIとローカルLLMの紹介
3.6 LangGraphによるエージェントワークフロー構築
3.6.1 LangGraphとは
- State、ノード、エッジの3つでワークフローを表現する
- State: グラフ内でのグローバル変数みたいな
- ノード: StateのCRUD的なことをする
- エッジ: ノードとノードを繋ぐやつ。条件付きとなしバージョンがある
3.6.2 エージェントワークフローの構築方法
- State定義→Nodes定義→Nodes追加→Edges追加→Compile って感じか。案外シンプル
- LangChainは嫌われている印象あるけど、LnagGraphはどうなんだろ
3.7 まとめ
第4章 ヘルプデスク担当者を支援する
4.1 ヘルプデスク業務について知る
- まずは業務理解。大事。
4.1.1 ヘルプデスク業務の流れ
4.1.2 ヘルプデスク業務の課題
4.1.3 なぜヘルプデスク業務にAIエージェントを用いるのか
4.2 Plan-and-Execute型エージェントについて
4.2.1 Plan and Execute型エージェントの概要
4.2.2 ヘルプデスク用計画実行型エージェント
4.3 実行環境のセットアップ
- 架空企業の架空システムの資料もちゃんと作ってある。すごい
- モデルはGPT-5にしよう。4oより強いし安いし
4.4 ツール作成
4.4.1 検索準備
- make start.engine でエラー出た。。。
- Java VMの問題っぽくて色々調べてたけど、結局Dockerの割り当てディスク容量増やしたらいけた (60->100)
- からのColimaのarchをx86_64ではなくarm64(aarch64)にしたらいけた
- make create.index でもエラー出た。。
- $OPENAI_API_KEY に変な値が入ってた。前になにか使って残骸が残ってたっぽい
- unset OPENAI_API_KEY で修正完了
- 3時間くらい潰してしまった。。。
- けど通った!!うれしい!!!
4.4.2 マニュアル検索ツール
- ベクトル検索と全文(キーワード)検索の違い
- ベクトル検索は検索クエリも検索対象データもベクトルにして、ベクトルの類似度で検索する
- 全文検索はトークンに分割し、それの一致度で検索する
- オンラインヘルプセンター → オンライン, ヘルプ, センター
- 不要な空白等の削除はkuromoji-analyzerがやってくれている
column RAGとロングコンテキスト
4.4.3 過去QA検索ツール
column 単純なRAGで回答できる質問の種類
- 質問レベルの4分類、参考になるなあ
- レベル4なのにレベル1の対応しようとして全然うまくいかなかったことあったな。。。
4.5 計画実行型エージェントのLangGraphによる設計
- LLMのAPiコールにはLangChainを使わないの、良い
4.5.1 LangGraphのステート・グラフ定義
- うーん、よくわからないかも、、、コードはわかるが全体の流れがよくわからない、、、
- 一旦次進んでみよう
4.5.2 計画作成
- 概念の説明ばっかで退屈だな
- notebooksにflow_steps_runner.ipynbってのがあるからこれ動かしてみよう。多分これステップごとに実行して出力確認できるnotebookだよね
- で、entire_ftaph_runner.ipynbがステップが隠蔽されてて最終出力が得られるようになってそう
- flow_stepsの方実行してみよう
- なるほどなー。だいぶわかった。個別に見ていこう
- AnnotatedとかTypedDictとかについて調べてた
- 型ヒント全然使ったことないなあ。。。使った方がいいんだろな本当は
4.5.3 ツール選択
4.5.4 ツール実行
4.5.5 サブタスク回答
4.5.6 自己修正
4.5.7 最終回答ステップ
4.5.8 動作確認
4.6 正確な回答が得られない原因と対策
4.6.1 計画不備
4.6.2 検索ツールの改善
4.7 展望
4.8 まとめ
- 難しかった。。。
- メインエージェントからサブエージェントを呼び出す流れの理解に時間がかかった。Send APIがポイントだった
- あと全体的に、どのタイミングでなにがどうなるのか追うのに時間がかった
- この処理ではmessagesになにがappendされるのかとか、その辺を適切に追うのに苦労した
- もう一回この章読んでから次にいこう
- もう一回読んだらだいぶ理解が進みそうかも
- でもおもしろかった!
- AI Agentって考えるとなんか崇高なことしてそうに思えるけど、案外シンプルに考えて良さそう
- 状態を持ってるだけで、普通のLLMと同じ。状態をどう管理するかが肝になってくるのがAgent開発
所感: select_tools のプロンプトに違和感
select_toolsの返り値はLLMからのtool-callingの結果をmessagesにappendしたやつ。
で、tool-calling 結果は例えばこのようになっている:
{
"role": "assistant",
"tool_calls": [
{
"id": "call_KKdOPGJv27pmBijQrC4KGiww",
"function": {
"arguments": '{"keywords": "XYZシステム パスワード 設定 文字セット 最小文字数 最大文字数 禁止文字 連続文字 Unicode サポート エンコード 入力プラットフォーム 認証エラー"}',
"name": "search_xyz_manual",
},
"type": "function",
},
{
"id": "call_4KygGpJR9BXGLhK21MaFa3VH",
"function": {
"arguments": '{"query": "XYZシステム パスワード ポリシー 文字セット 最小長 最大長 禁止文字 連続文字 Unicode エンコード 入力プラットフォーム 認証エラー"}',
"name": "search_xyz_qa",
},
"type": "function",
},
],
}
これはいい。
けど、これのプロンプトに違和感。
SUBTASK_SYSTEM_PROMPT = """
あなたはXYZというシステムの質問応答のためにサブタスク実行を担当するエージェントです。
回答までの全体の流れは計画立案 → サブタスク実行 [ツール実行 → サブタスク回答 → リフレクション] → 最終回答となります。
サブタスクはユーザーの質問に回答するために考えられた計画の一つです。
最終的な回答は全てのサブタスクの結果を組み合わせて別エージェントが作成します。
あなたは以下の1~3のステップを指示に従ってそれぞれ実行します。各ステップは指示があったら実行し、同時に複数ステップの実行は行わないでください。
なおリフレクションの結果次第で所定の回数までツール選択・実行を繰り返します。
1. ツール選択・実行
サブタスク回答のためのツール選択と選択されたツールの実行を行います。
2回目以降はリフレクションのアドバイスに従って再実行してください。
2. サブタスク回答
ツールの実行結果はあなたしか観測できません。
ツールの実行結果から得られた回答に必要なことは言語化し、最後の回答用エージェントに引き継げるようにしてください。
例えば、概要を知るサブタスクならば、ツールの実行結果から概要を言語化してください。
手順を知るサブタスクならば、ツールの実行結果から手順を言語化してください。
回答できなかった場合は、その旨を言語化してください。
3. リフレクション
ツールの実行結果と回答から、サブタスクに対して正しく回答できているかを評価します。
回答がわからない、情報が見つからないといった内容の場合は評価をNGにし、やり直すようにしてください。
評価がNGの場合は、別のツールを試す、別の文言でツールを試すなど、なぜNGなのかとどうしたら改善できるかを考えアドバイスを作成してください。
アドバイスの内容は過去のアドバイスと計画内の他のサブタスクと重複しないようにしてください。
アドバイスの内容をもとにツール選択・実行からやり直します。
評価がOKの場合は、サブタスク回答を終了します。
"""
SUBTASK_TOOL_EXECUTION_USER_PROMPT = """
ユーザーの元の質問: {question}
回答のための計画: {plan}
サブタスク: {subtask}
サブタスク実行を開始します。
1.ツール選択・実行, 2. サブタスク回答 を実行してください
"""
ツール選択、具体的に言うとLLMが使いたい関数とその引数を返して欲しいだけなのに、ユーザプロンプトには「サブタスク実行を開始します。1.ツール選択・実行, 2. サブタスク回答 を実行してください」とある。
どういうこと、、、??
tool-calling 使いたい関数とその引数を返すだけでしょ??
あと、この時点でリフレクションの話必要??
よくわからない。。。
追記
OpenAI-Codex に聞いたところ、これは会話履歴を持ったまま次の処理に行くから、これでいいとのこと。
たしかにこれはサブエージェント内で共有される State だから、後続の処理にも渡されることを考えると、このプロンプトでいいのか。なるほどなー。単発利用とはプロンプトも異なってくるってことか。学び。