はじめに
「現場で活用するためのAIエージェント実践入門」の第4章で私がつまずいたことのメモです。
(このメモのほかの章へ:1章 / 2章 / 3章 / 4章 / 5章 / 6章 / 7章 / 8章 / 9章 / 10章 / まとめ)
第4章 ヘルプデスク担当者を支援する
この章からは、いよいよ実際に具体的なユースケースを取り扱います。本で掲載されているソースコードはGitHubで公開されています。
4.1 ヘルプデスク業務について知る
私は前職でヘルプデスクへのAIサービス導入をいくつか担当したことがあるので業務は理解できていますが、ヘルプデスクに馴染みがない方も本の解説で把握できるかと思います。
4.2 Plan-and-Execute型エージェントについて
Plan-and-Execute型のエージェントは赤い鳥の本1でも扱ったので、特につまずくことはありませんでした。赤い鳥の本で学んだエージェントデザインパターン2における、シングルパスプランジェネレーター、RAG、セルフリフレクションなどが該当します。
4.3 実行環境のセットアップ
実行環境のセットアップも、手順どおり進めれば特につまずくことはありませんでした。
4.4 ツール作成
全文検索
少し細かい話になりますが、GitHubで公開されているサンプルのマニュアルPDFは、src/scripts/create_index.pyのコードでテキストを抽出してElasticsearchに登録すると、単語内に余計な空白が混入してしまいます。tools.ipynbで検索を試した時に、不自然に入っている空白がそれです。赤で下線を入れてみました。
Elasticsearchに登録する際、この空白を単語の境界とみなして処理してしまうため、余計な空白で分断された単語は検索できなくなってしまいます。たとえば、「センター」や「スムーズ」では検索ができません。
QdrantにFAQデータを登録するコードでは空白を除去しているので、同じように空白を除去してから登録するのがよいかと思います。なお、本来であればElasticsearchのアナライザーで設定すべきなので(たとえばCJK文字の間の空白のみ除去するなど)、ここではあくまでも暫定です。
def add_documents_to_es(
es: Elasticsearch, index_name: str, docs: list[Document]
) -> None:
insert_docs = []
for doc in docs:
content = doc.page_content
# ドキュメントの作成
insert_doc = {
...
def add_documents_to_es(
es: Elasticsearch, index_name: str, docs: list[Document]
) -> None:
insert_docs = []
for doc in docs:
content = doc.page_content
content = content.replace(" ", "") # 空白を除去
# ドキュメントの作成
insert_doc = {
...
なお、インデックスの再作成をする前に、一度、現在のインデックスを削除する必要があります。削除のツールも用意されており、make delete.indexで削除できます。
make delete.index
make create.index
これで、「センター」や「スムーズ」でも検索できるようになります。
なお、常に空白を除くことがプラスになるとは限りません。空白が単語の区切りを示している場合は、空白を除いてしまうと前後の単語がつながってしまい、検索できなくなる恐れがあります。また、空白の有無に限らず単語の分解を誤ることはよくあるので、正しく検索するためには他のアナライザーの併用が必要だったり泥臭い調整が必要になったりします。
もし全文検索にご興味がありましたら、拙作のこちらの記事もぜひご参照ください!
4.5 計画実行型エージェントのLangGraphによる設計
MapReduceパターン(Sendによる並列実行)
動的に複数のノードを作って並列実行させる形は、赤い鳥の本には出てなかったパターンです。分散処理アルゴリズムにおけるMapReduceパターン(独立したタスクに分割して並列実行し最後に集約する)を簡単に実現する仕組みですね。条件付きエッジで、Sendオブジェクトを介して実行したいノードと状態のセットを渡すことができます。本家サイトの解説はこちらです。
また脱線してしまいますが、MapReduceの分散処理アルゴリズムが盛り上がったきっかけになったのは、20年以上前のこちらの論文だったかと思います。
当時、私は全文検索エンジンの開発エンジニアで、ご多分に漏れず分散処理にかぶれてしまい、Apache Hadoop使ってインデキシング速度をスケールさせよう!みたいな製品を企画したのを思い出しました(それが全然売れなかったのも思い出しました、当時の関係者の皆様、大変ご迷惑をおかけしました🙇)。
本に戻ります。
計画作成を実装した「プログラムリスト 4.8」で、システムプロンプトを生成する際にtoolsを渡しているのですが、システムプロンプトにはtoolsを埋め込む場所がありません。また、GitHubのコードもtoolsは渡していないので、本の掲載コードが古くなってしまったようです。
この後も、本に掲載されたコードとGitHubとで違う部分がでてきます。GitHubのコードは最新化されているようなので、本のコードを写経するのではなくGitHubを正として進めるのがよさそうです。
ちなみにGitHubのコードで計画ステップを実行させてみたところ、以下のような4つのサブタスクに分解されました。
['XYZシステムのパスワード設定における使用可能な文字の種類(英数字、記号など)について調べる',
'XYZシステムのパスワード設定における文字数制限について調べる',
'XYZシステムの最新リリース情報を確認する方法について調べる',
'XYZシステムの最新リリースを取得する方法について調べる']
文脈維持とトークン数節約のバランス
「プログラムリスト 4.9」でリトライ時のプロンプトを切り替えていますが、# NOTE: トークン数節約のため過去の検索結果は除くという部分でリトライ前の検索結果情報を除去しています。
logger.debug("Creating user prompt for tool retry...")
# リトライされた場合は過去の対話情報にプロンプトを追加する
messages: list = state["messages"]
# NOTE: トークン数節約のため過去の検索結果は除く
# roleがtoolまたはtool_callsを持つものは除く
messages = [message for message in messages if message["role"] != "tool" and "tool_calls" not in message]
user_retry_prompt = self.prompts.subtask_retry_answer_user_prompt
user_message = {"role": "user", "content": user_retry_prompt}
messages.append(user_message)
確かにトークン数を節約できるのですが、リトライする原因になった文脈が欠落するリスクもあるので、実際には慎重な検討が必要かもしれません。
あまり詳しいことは書けないのですが、前職でRAGのサービスを作っていた時は検索結果すべてを除去するのではなく、検索結果の中で参照されなかったものだけ除去するような形にして、リフレクションにおける文脈維持とコンテキスト節約のバランスをとっていました。
「4.5.6 自己修正」は、最初の質問のままでもいい感じでツールの切り替えをアドバイスしてきました。
is_completed = False
advice = リリースノートでは特定のプロジェクトに対する通知制限の情報が見つからなかったため、次はXYZシステムの公式ドキュメントやFAQを検索してみることをお勧めします。これにより、より詳細な設定方法や機能についての情報が得られる可能性があります。
「4.5.8 動作確認」の手順で実行結果を追ってみました。質問は最初のままです。
お世話になっております。
現在、XYZシステムの利用を検討しており、以下の2点についてご教示いただければと存じます。
1. パスワードに利用可能な文字の制限について
当該システムにてパスワードを設定する際、使用可能な文字の範囲(例:英数字、記号、文字数制限など)について詳しい情報をいただけますでしょうか。安全かつシステムでの認証エラーを防ぐため、具体的な仕様を確認したいと考えております。
2. 最新リリースの取得方法について
最新のアップデート情報をどのように確認・取得できるかについてもお教えいただけますと幸いです。
お忙しいところ恐縮ですが、ご対応のほどよろしくお願い申し上げます。
以下、実行結果です。
2025-10-19 00:45:41,871 INFO 🚀 Starting plan generation process...
2025-10-19 00:45:41,872 INFO Sending request to OpenAI...
2025-10-19 00:45:44,318 INFO HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
2025-10-19 00:45:44,344 INFO ✅ Successfully received response from OpenAI.
2025-10-19 00:45:44,345 INFO Plan generation complete!
ここで計画作成が終わり、サブグラフの並列実行を開始します。計画が4つに分かれたので4つのサブタスクが動いており、ログが入り乱れるのでちょっと追いにくくなります。
2025-10-19 00:45:44,362 INFO 🚀 Starting tool selection process...
2025-10-19 00:45:44,366 INFO 🚀 Starting tool selection process...
2025-10-19 00:45:44,366 INFO 🚀 Starting tool selection process...
2025-10-19 00:45:44,366 INFO 🚀 Starting tool selection process...
2025-10-19 00:45:44,500 INFO Sending request to OpenAI...
2025-10-19 00:45:44,501 INFO Sending request to OpenAI...
2025-10-19 00:45:44,501 INFO Sending request to OpenAI...
2025-10-19 00:45:44,502 INFO Sending request to OpenAI...
2025-10-19 00:45:45,459 INFO HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
2025-10-19 00:45:45,464 INFO ✅ Successfully received response from OpenAI.
2025-10-19 00:45:45,465 INFO Tool selection complete!
2025-10-19 00:45:45,467 INFO 🚀 Starting tool execution process...
2025-10-19 00:45:45,468 INFO Searching XYZ manual by keyword: {"keywords":"パスワード 使用可能な文字 XYZシステム"}
2025-10-19 00:45:45,484 INFO POST http://localhost:9200/documents/_search [status:200 duration:0.013s]
2025-10-19 00:45:45,485 INFO Search results: 10 hits
2025-10-19 00:45:45,485 INFO Finished searching XYZ manual by keyword
2025-10-19 00:45:45,486 INFO Tool execution complete!
2025-10-19 00:45:45,487 INFO 🚀 Starting subtask answer creation process...
2025-10-19 00:45:45,487 INFO Sending request to OpenAI...
2025-10-19 00:45:45,506 INFO HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
2025-10-19 00:45:45,507 INFO ✅ Successfully received response from OpenAI.
2025-10-19 00:45:45,508 INFO Tool selection complete!
2025-10-19 00:45:45,509 INFO 🚀 Starting tool execution process...
2025-10-19 00:45:45,510 INFO Searching XYZ manual by keyword: {"keywords":"最新リリース 取得方法"}
2025-10-19 00:45:45,522 INFO POST http://localhost:9200/documents/_search [status:200 duration:0.011s]
2025-10-19 00:45:45,522 INFO Search results: 10 hits
2025-10-19 00:45:45,523 INFO Finished searching XYZ manual by keyword
2025-10-19 00:45:45,523 INFO Tool execution complete!
2025-10-19 00:45:45,524 INFO 🚀 Starting subtask answer creation process...
2025-10-19 00:45:45,524 INFO Sending request to OpenAI...
2025-10-19 00:45:45,777 INFO HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
2025-10-19 00:45:45,784 INFO ✅ Successfully received response from OpenAI.
2025-10-19 00:45:45,785 INFO Tool selection complete!
2025-10-19 00:45:45,787 INFO 🚀 Starting tool execution process...
2025-10-19 00:45:45,788 INFO Searching XYZ manual by keyword: {"keywords":"パスワード 文字数制限"}
2025-10-19 00:45:45,825 INFO POST http://localhost:9200/documents/_search [status:200 duration:0.036s]
2025-10-19 00:45:45,826 INFO Search results: 7 hits
2025-10-19 00:45:45,827 INFO Finished searching XYZ manual by keyword
2025-10-19 00:45:45,827 INFO Tool execution complete!
2025-10-19 00:45:45,829 INFO 🚀 Starting subtask answer creation process...
2025-10-19 00:45:45,830 INFO Sending request to OpenAI...
2025-10-19 00:45:46,178 INFO HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
2025-10-19 00:45:46,180 INFO ✅ Successfully received response from OpenAI.
2025-10-19 00:45:46,180 INFO Tool selection complete!
2025-10-19 00:45:46,182 INFO 🚀 Starting tool execution process...
2025-10-19 00:45:46,183 INFO Searching XYZ manual by keyword: {"keywords":"最新リリース情報 確認方法"}
2025-10-19 00:45:46,200 INFO POST http://localhost:9200/documents/_search [status:200 duration:0.016s]
2025-10-19 00:45:46,201 INFO Search results: 10 hits
2025-10-19 00:45:46,201 INFO Finished searching XYZ manual by keyword
2025-10-19 00:45:46,202 INFO Tool execution complete!
2025-10-19 00:45:46,204 INFO 🚀 Starting subtask answer creation process...
2025-10-19 00:45:46,204 INFO Sending request to OpenAI...
2025-10-19 00:45:47,766 INFO HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
2025-10-19 00:45:47,774 INFO ✅ Successfully received response from OpenAI.
2025-10-19 00:45:47,775 INFO Subtask answer creation complete!
2025-10-19 00:45:47,777 INFO 🚀 Starting reflection process...
2025-10-19 00:45:47,778 INFO Sending request to OpenAI...
2025-10-19 00:45:48,034 INFO HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
2025-10-19 00:45:48,036 INFO ✅ Successfully received response from OpenAI.
2025-10-19 00:45:48,036 INFO Subtask answer creation complete!
2025-10-19 00:45:48,038 INFO 🚀 Starting reflection process...
2025-10-19 00:45:48,039 INFO Sending request to OpenAI...
2025-10-19 00:45:48,215 INFO HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
2025-10-19 00:45:48,218 INFO ✅ Successfully received response from OpenAI.
2025-10-19 00:45:48,218 INFO Subtask answer creation complete!
2025-10-19 00:45:48,221 INFO 🚀 Starting reflection process...
2025-10-19 00:45:48,221 INFO Sending request to OpenAI...
2025-10-19 00:45:48,479 INFO HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
2025-10-19 00:45:48,482 INFO ✅ Successfully received response from OpenAI.
2025-10-19 00:45:48,482 INFO Subtask answer creation complete!
2025-10-19 00:45:48,484 INFO 🚀 Starting reflection process...
2025-10-19 00:45:48,485 INFO Sending request to OpenAI...
2025-10-19 00:45:48,547 INFO HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
2025-10-19 00:45:48,552 INFO ✅ Successfully received response from OpenAI.
2025-10-19 00:45:48,553 INFO Reflection complete!
2025-10-19 00:45:49,212 INFO HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
2025-10-19 00:45:49,214 INFO ✅ Successfully received response from OpenAI.
2025-10-19 00:45:49,215 INFO Reflection complete!
2025-10-19 00:45:49,339 INFO HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
2025-10-19 00:45:49,343 INFO ✅ Successfully received response from OpenAI.
2025-10-19 00:45:49,343 INFO Reflection complete!
2025-10-19 00:45:50,645 INFO HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
2025-10-19 00:45:50,647 INFO ✅ Successfully received response from OpenAI.
2025-10-19 00:45:50,647 INFO Reflection complete!
ここまでで4つのサブタスクがreflect_subtaskまで終わりました。
ただ、再び🚀 Starting tool selection process...が出力されているので、リフレクションでNGになってselect_toolからやり直しになっているサブタスクがありますね。
2025-10-19 00:45:50,649 INFO 🚀 Starting tool selection process...
2025-10-19 00:45:50,652 INFO Sending request to OpenAI...
2025-10-19 00:45:51,890 INFO HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
2025-10-19 00:45:51,897 INFO ✅ Successfully received response from OpenAI.
2025-10-19 00:45:51,898 INFO Tool selection complete!
2025-10-19 00:45:51,901 INFO 🚀 Starting tool execution process...
2025-10-19 00:45:51,902 INFO Searching XYZ QA by query: {"query":"最新リリース 取得方法"}
2025-10-19 00:45:51,948 INFO Generating embedding vector from input query
2025-10-19 00:45:52,753 INFO HTTP Request: POST https://api.openai.com/v1/embeddings "HTTP/1.1 200 OK"
2025-10-19 00:45:52,775 INFO HTTP Request: POST http://localhost:6333/collections/documents/points/query "HTTP/1.1 200 OK"
2025-10-19 00:45:52,779 INFO Search results: 3 hits
2025-10-19 00:45:52,779 INFO Finished searching XYZ QA by query
2025-10-19 00:45:52,780 INFO Tool execution complete!
2025-10-19 00:45:52,781 INFO 🚀 Starting subtask answer creation process...
2025-10-19 00:45:52,781 INFO Sending request to OpenAI...
2025-10-19 00:45:53,957 INFO HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
2025-10-19 00:45:53,965 INFO ✅ Successfully received response from OpenAI.
2025-10-19 00:45:53,965 INFO Subtask answer creation complete!
2025-10-19 00:45:53,968 INFO 🚀 Starting reflection process...
2025-10-19 00:45:53,968 INFO Sending request to OpenAI...
2025-10-19 00:45:55,705 INFO HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
2025-10-19 00:45:55,718 INFO ✅ Successfully received response from OpenAI.
2025-10-19 00:45:55,719 INFO Reflection complete!
どうやら、「XYZシステムの最新リリースを取得する方法について調べる」というタスクの答えがマニュアルでは見つからず、前述のアドバイスが返ってQAに切り替えたようです。ただ、さらにやり直しになっています。
2025-10-19 00:45:55,723 INFO 🚀 Starting tool selection process...
2025-10-19 00:45:55,728 INFO Sending request to OpenAI...
2025-10-19 00:45:56,837 INFO HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
2025-10-19 00:45:56,842 INFO ✅ Successfully received response from OpenAI.
2025-10-19 00:45:56,843 INFO Tool selection complete!
2025-10-19 00:45:56,846 INFO 🚀 Starting tool execution process...
2025-10-19 00:45:56,846 INFO Searching XYZ manual by keyword: {"keywords":"サポートチーム 連絡方法"}
2025-10-19 00:45:56,855 INFO POST http://localhost:9200/documents/_search [status:200 duration:0.007s]
2025-10-19 00:45:56,856 INFO Search results: 10 hits
2025-10-19 00:45:56,857 INFO Finished searching XYZ manual by keyword
2025-10-19 00:45:56,858 INFO Tool execution complete!
今度は再びマニュアルに対して「サポートチーム 連絡方法」を検索していました。マニュアルにもQAにも記載がないので問い合わせ先を探し始めたようです。賢い!
2025-10-19 00:45:56,860 INFO 🚀 Starting subtask answer creation process...
2025-10-19 00:45:56,861 INFO Sending request to OpenAI...
2025-10-19 00:45:58,497 INFO HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
2025-10-19 00:45:58,499 INFO ✅ Successfully received response from OpenAI.
2025-10-19 00:45:58,500 INFO Subtask answer creation complete!
2025-10-19 00:45:58,502 INFO 🚀 Starting reflection process...
2025-10-19 00:45:58,503 INFO Sending request to OpenAI...
2025-10-19 00:46:00,576 INFO HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
2025-10-19 00:46:00,583 INFO ✅ Successfully received response from OpenAI.
2025-10-19 00:46:00,583 INFO Reflection complete!
reflect_subtaskまで終わりました。ただ、もう3回目なので繰り返しはここで打ち切られて、メインのcreate_answerに進みます。
2025-10-19 00:46:00,588 INFO 🚀 Starting final answer creation process...
2025-10-19 00:46:00,589 INFO Sending request to OpenAI...
2025-10-19 00:46:05,099 INFO HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
2025-10-19 00:46:05,107 INFO ✅ Successfully received response from OpenAI.
2025-10-19 00:46:05,107 INFO Final answer creation complete!
完了しました!
お世話になっております。XYZシステムのご利用を検討いただき、ありがとうございます。以下にご質問に対する回答を記載いたします。
1. **パスワードに利用可能な文字の制限について**:
- パスワードは最低8文字以上である必要があります。
- 大文字と小文字の両方を含む必要があります。
- 少なくとも1つの数字を含む必要があります。
- 少なくとも1つの特殊文字(例: !@#$%^&* など)を含む必要があります。
これらの要件を満たすようにパスワードを設定してください。これにより、安全性を確保し、システムでの認証エラーを防ぐことができます。
2. **最新リリースの取得方法について**:
- 最新のアップデート情報や新機能の説明は、毎月発行されるリリースノートを参照することで確認できます。リリースノートには、システムのアップデートや新機能に関する詳細が記載されています。
ただし、最新リリースを実際に取得する方法についての具体的な情報は現在手元にありません。引き続き調査を行い、分かり次第お知らせいたします。
ご不明点がございましたら、いつでもご連絡ください。引き続き、XYZシステムのご利用をお待ちしております。
「最新リリースの取得方法」だけは3回やり直しましたが調べられなかった模様です。意図通り動作していることが確認できました。
4.6 正確な回答が得られない原因と対策
「4.6.1 計画不備」のところに書いてある内容は、まさにその通りです。暗黙知の扱いは過去にもエンタープライズサーチやナレッジマネジメントなどのキーワードで盛り上がったこともあるのですが、残念ながら決定打はありませんでした。
そういえば3ヶ月ほど前にも、暗黙知を形式知に変換するマルチエージェントの論文が話題になっていましたね。
「4.6.2 検索ツールの改善」ではHyDE手法などが紹介されていますが、それ以外にも多くの手法があります。赤い鳥の本でも紹介されていましたが、LangChainが「RAG From Scratch」として各手法を解説するソースコードと動画を公開しています。ご興味がありましたらご参照ください。
4.7 展望
後半に書かれている参照ドキュメントの運用は、本当に難しい課題です。前述のエンタープライズサーチやナレッジマネジメントがこれまでうまくいかなかったのも、おそらくデータ側を管理できなかったことが大きいのではないかと思います。
4.8 まとめ
今回のユースケースは前職で馴染みがあったこともあり、スムーズに学ぶことができました。次回は「データ分析者の支援」です。
(このメモのほかの章へ:1章 / 2章 / 3章 / 4章 / 5章 / 6章 / 7章 / 8章 / 9章 / 10章 / まとめ)


