機能するエージェントを作るのと、そうでないエージェントを作るのとでは、天と地ほどの差がある。どうすれば、後者のカテゴリに入るエージェントを構築できるのでしょうか?このガイドでは、エージェントを構築するためのベストプラクティスを紹介します。
エージェントを構築するのが初めての方は、まず、以下のページをお読みください。 イントロ エージェントへ そして smolagentガイドツアー.
最高のエージェント・システムは最もシンプルである。 #
ワークフローの中でLLMに何らかの権限を与えることは、エラーのリスクをもたらす。
よくプログラムされたエージェントシステムには、エラーログとリトライメカニズムがある。しかし、LLMのエラーのリスクを最大限に減らすためには、ワークフローを単純化すべきです!
intro_agents]の例をもう一度見てみよう。サーフトリップ会社のユーザーからの問い合わせに答えるボットだ。新しいサーフスポットについて質問されるたびにエージェントに "移動距離API "と "天気API "の2つの異なる呼び出しをさせる代わりに、1つの統一されたツール "return_spot_information "を作ることができる。
これにより、コスト、待ち時間、エラーのリスクを削減することができる!
主なガイドラインはこうだ:LLMコールの回数をできるだけ減らすこと。
これはいくつかの教訓につながる:
- 可能な限り、2つのAPIの例のように、2つのツールを1つにまとめる。
- 可能な限り、ロジックはエージェント的な決定ではなく、決定論的な関数に基づくべきである。
LLMエンジンへの情報フローの改善 #
あなたのLLMエンジンは、ドアの下で交わされるメモだけが外界との唯一のコミュニケーションである部屋に閉じ込められた、~知的な~ロボットのようなものであることを忘れないでください。
プロンプトに明示的にそう書かないと、何が起こったかわからない。
ですから、まずタスクを明確にすることから始めましょう!エージェントはLLMによって動かされるため、タスクの定式化のちょっとした違いで結果が全く違ってくるかもしれません。
そして、ツール使用におけるエージェントへの情報の流れを改善する。
特に守るべきガイドライン
- 各ツールは(単に
プリント
ツールの前方
メソッド)LLMエンジンに役立ちそうなものすべてだ。- 特に、ツールの実行エラーに関する詳細なログは大いに役立つだろう!
例えば、場所と日時に基づいて気象データを検索するツールがある:
まず、これがお粗末なバージョンだ:
コピー
インポート datetime from smolagents import tool def get_weather_report_at_coordinates(coordinates, date_time): # ダミー関数で、[気温(℃)、雨の危険度(0~1)、波の高さ(m)]のリストを返す。 28.0, 0.35, 0.85]を返す。 def get_coordinates_from_location(location): # ダミー座標を返す 3.3, -42.0]を返す。 ツール def get_weather_api(location: str, date_time: str) -> str: """ 天気予報を返します。 引数 location: 天気を知りたい場所の名前。 date_time: レポートの日付と時刻。 """ lon, lat = convert_location_to_coordinates(location) date_time = datetime.strptime(date_time) return str(get_weather_report_at_coordinates((lon, lat), date_time))
なぜ悪いのか?
- に使用すべきフォーマットの精度はない。
日時
- どのようにロケーションを指定すべきかについての詳細はない。
- locationが適切なフォーマットでないとか、date_timeが適切なフォーマットでないとか、そういう明示的な失敗ケースに関連するロギングメカニズムがない。
- 出力形式がわかりにくい
ツールの呼び出しに失敗した場合、メモリに記録されたエラートレースは、LLMがツールをリバースエンジニアリングしてエラーを修正するのに役立つ。しかし、なぜLLMに重い仕事をさせるのだろうか?
このツールのより良い作り方は、次のようなものだっただろう:
コピー
ツール def get_weather_api(location: str, date_time: str) -> str: """ 天気予報を返します。 引数 location: 天気を知りたい場所の名前。Anchor Point, Taghazout, Morocco "のように、地名、都市名、国の順に続く。 date_time: レポートが欲しい日付と時刻。'%m/%d/%y %H:%M:%S'のようなフォーマット。 """ lon, lat = convert_location_to_coordinates(location) 試す: date_time = datetime.strptime(date_time) except Exception as e: raise ValueError("Conversion of `date_time` to datetime format failed, make sure to provide a string in format '%m/%d/%y %H:%M:%S'.完全なトレース:"+ str(e)) temperature_celsius, risk_of_rain, wave_height = get_weather_report_at_coordinates((lon, lat), date_time) return f"{場所}, {日付_時刻}の天気予報:気温は{temperature_celsius}℃、雨のリスクは{risk_of_rain*100:.0f}%、波の高さは{wave_height}mです。"
一般的に、LLMの負担を軽減するために、自問自答するのに良い質問は、「もし私が頭が悪く、このツールを初めて使うなら、このツールを使ってプログラミングし、自分のエラーを修正するのはどれだけ簡単だろうか?
エージェントにより多くの引数を与える #
タスクを記述する単純な文字列以外に、いくつかの追加オブジェクトをエージェントに渡すには additional_args
引数で任意の型のオブジェクトを渡すことができる:
コピー
from smolagents import CodeAgent, HfApiModel model_id = "meta-llama/Llama-3.3-70B-Instruct" agent = CodeAgent(tools=[], model=HfApiModel(model_id=model_id), add_base_tools=True) agent.run( "なぜマイクはニューヨークで多くの人を知らないのか?"、 additional_args={"mp3_sound_file_url":'https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/recording.mp3'}. )
たとえば、次のようにします。 additional_args
引数で、エージェントに活用させたい画像や文字列を渡します。
エージェントのデバッグ方法 #
1.より強力なLLMを使う #
エージェント型ワークフローでは、エラーのいくつかは実際のエラーであり、他のいくつかはLLMエンジンが適切に推論していないせいである。例えば コードエージェント
クルマの写真を作ってくれと頼んだ:
コピー
==================================================================================================== New task ==================================================================================================== かっこいい車の写真を作ってください ──────────────────────────────────────────────────────────────────────────────────────────────────── New step ──────────────────────────────────────────────────────────────────────────────────────────────────── エージェントは以下のコードを実行している:─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── image_generator(prompt="LEDヘッドライト、空気力学に基づいたデザイン、鮮やかな色彩、高解像度のフォトリアリスティックなクールな未来的スポーツカー") ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── コード・スニペットからの最後の出力:─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── /var/folders/6m/9b1tts6d5w960j80wbw9tx3m0000gn/T/tmpx09qfsdd/652f0007-3ee9-44e2-94ac-90dae6bb89a4.png ステップ1: - かかった時間16.35秒 - 入力トークン: 1,383 - 出力トークン:77 ──────────────────────────────────────────────────────────────────────────────────────────────────── New step ──────────────────────────────────────────────────────────────────────────────────────────────────── エージェントは以下のコードを実行している:─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── final_answer("/var/folders/6m/9b1tts6d5w960j80wbw9tx3m0000gn/T/tmpx09qfsdd/652f0007-3ee9-44e2-94ac-90dae6bb89a4.png") ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── プリント出力 コード・スニペットからの最後の出力: /var/folders/6m/9b1tts6d5w960j80wbw9tx3m0000gn/T/tmpx09qfsdd/652f0007-3ee9-44e2-94ac-90dae6bb89a4.png 最終的な答え /var/folders/6m/9b1tts6d5w960j80wbw9tx3m0000gn/T/tmpx09qfsdd/652f0007-3ee9-44e2-94ac-90dae6bb89a4.png
ユーザは、画像が返されるのではなく、パスが返されるのを見る。これはシステムのバグのように見えますが、実際にはエージェントシステムがエラーを引き起こしたわけではありません:LLMの頭脳が画像出力を変数に保存しないというミスを犯しただけです。そのため、画像を保存する際に記録されたパスを利用する以外、画像に再度アクセスすることができず、画像の代わりにパスを返してしまうのです。
エージェントのデバッグの第一歩は、このように「より強力なLLMを使う」ことです。次のような選択肢があります。 クウェン2/5-72B-インストラクター
そうでなければ、あのようなミスは犯さなかっただろう。
2.より多くのガイダンス/より多くの情報を提供する #
また、より効果的に誘導するのであれば、それほど強力でないモデルを使うこともできる。
モデルの立場になって考えてみてください。もしあなたがタスクを解決するモデルだったら、(システムプロンプト+タスクの定式化+ツールの説明から)利用可能な情報で苦労しますか?
補足説明が必要ですか?
デフォルトのシステムプロンプトには多くの調整があり、プロンプトをよく理解している場合を除き、台無しにしたくありません。LLMエンジンをガイドする、より良い方法があります:
- 解決すべきタスクについてなら、タスクにこれらの詳細をすべて追加する。タスクは何百ページにもなる。
- ツールの使い方に関するものであれば、ツールのdescription属性。
3.システムプロンプトを変更する(一般的にはお勧めしません) #
上記の説明で不十分な場合は、システムプロンプトを変更することができます。
その仕組みを見てみよう。例えば、デフォルトのシステム・プロンプトで コードエージェント (以下のバージョンはゼロショットの例を省略して短縮しています)。
コピー
print(agent.system_prompt_template)
これがその内容だ:
コピー
あなたは、コードブロブを使ってあらゆるタスクを解決できるエキスパートアシスタントです。あなたには、できる限り解決するタスクが与えられます。 そのために、あなたにはツールのリストへのアクセスが与えられている。これらのツールは基本的にPythonの関数で、コードで呼び出すことができる。 タスクを解決するためには、「思考:」、「コード:」、「観察:」のサイクルで、一連のステップを進める計画を立てなければならない。 各ステップでは、まず「Thought:」シーケンスで、タスクを解決するための理由と使いたいツールを説明する。 次に「Code:」シーケンスで、簡単なPythonでコードを書く。コード・シーケンスは''シーケンスで終わらなければならない。 各中間ステップでは、'print()' を使って、そのときに必要となる重要な情報を保存することができます。 これらのプリント出力は'Observation:'フィールドに表示され、次のステップの入力として利用できる。 最終的には `final_answer` ツールを使って最終的な答えを返さなければならない。 以下は想定されるツールを使った例である: --- 例 上記の例は、あなたにとって存在しないかもしれない想定外のツールを使っています。あなたが作成したPythonコードスニペットで計算を実行する上に、あなたはこれらのツールにアクセスするだけです: {tool_descriptions}}。 {{managed_agents_descriptions}} です。 タスクを解決するために常に従うべきルールは以下の通りです: 1.常に 'Thought:' シーケンスと 'Code: \n```py' シーケンスの最後に '``' シーケンスを記述してください。 2.2. 変数は自分で定義したものだけを使ってください! 3.ツールには常に正しい引数を使用すること。answer = wiki({'query': "ジェームズ・ボンドが住んでいる場所は?"})」のように引数をdictとして渡さず、「answer = wiki(query="ジェームズ・ボンドが住んでいる場所は?")」のように引数を直接使用してください。 4.4.特に出力形式が予測できない場合は、同じコードブロック内であまり多くの連続したツールコールを連鎖させないように注意してください。例えば、searchの呼び出しは戻り値の形式が予測できないので、その出力に依存する別のツール呼び出しを同じブロック内で行わないでください。 5.5.ツールは必要なときだけ呼び出すようにし、以前にまったく同じパラメータで行ったツール呼び出しをやり直さないようにする。 6.例えば、変数名を「final_answer」にしない。 7.例えば、変数名を「final_answer」にしないこと。ログにこのような変数が残っていると、真の変数から外れてしまうかもしれない。 8.コード内でインポートを使用することはできますが、以下のモジュールのリストからのみ使用できます:{authorized_imports}}。 9.したがって、あるステップで変数を作成したり、モジュールをインポートした場合、これらはすべて持続します。 10.あきらめないで!あなたはタスクを解決するのが仕事であって、解決するための指示を与えるのが仕事ではありません。 さあ、始めましょう!課題を正しく解くと、$1,000,000の報酬がもらえます。
ご覧のように、次のようなプレースホルダーがあります。 "{{tool_descriptions}}"
これらはエージェントの初期化時に使用され、ツールや管理エージェントの自動生成された説明が挿入されます。
そのため、カスタムプロンプトを システムプロンプト
パラメータを使用する場合、新しいシステム・プロンプトには以下のプレースホルダーが含まれていなければならない:
"{{tool_descriptions}}"
をクリックしてツールの説明を挿入します。"{{managed_agents_description}}"
管理エージェントがある場合は、その説明を挿入します。- について
コードエージェント
だけである:"{{authorized_imports}}"
をクリックして輸入許可リストを挿入する。
そうすれば、システム・プロンプトを次のように変更できる:
コピー
from smolagents.prompts import CODE_SYSTEM_PROMPT modified_system_prompt = CODE_SYSTEM_PROMPT + "♪お待たせしました!" # ここでシステムプロンプトを変更する agent = CodeAgent( tools=[]、 model=HfApiModel()、 system_prompt=modified_system_prompt )
これは ツールコーリングエージェント.
4.追加計画 #
我々は、エージェントが通常の行動ステップの合間に定期的に実行できる、補足的な計画ステップのモデルを提供する。このステップでは、ツールの呼び出しはなく、LLMは単に知っている事実のリストを更新し、それらの事実に基づいて次にどのようなステップを取るべきかを考えるように求められる。
コピー
from smolagents import load_tool, CodeAgent, HfApiModel, DuckDuckGoSearchTool from dotenv import load_dotenv load_dotenv() # ハブからのインポートツール image_generation_tool = load_tool("m-ric/text-to-image", trust_remote_code=True) search_tool = DuckDuckGoSearchTool() agent = CodeAgent( tools=[search_tool]、 model=HfApiModel("Qwen/Qwen2.5-72B-Instruct")、 計画期間=3 # ここでプランニングを開始する! ) # 実行してください! result = agent.run( "チーターが全速力でアレクサンドル3世橋の長さを走るのにかかる時間は? )