エージェント

**エージェント**は、タスクが完了するまで次に呼び出すツールを選択しながらループで実行するモデルです。このチャプターの残りの部分では、これが意味すること、その価値、そして静かにコストをかける場所について説明します。
エージェントループ
ツール使用では、モデルが1つの関数を呼び出し、結果を返していました。しかし、複数のステップが必要なタスクがあり、事前にステップを知ることができません。「最も安いフライトを見つけて予約する」は、検索、比較、カレンダーの確認、予約を意味する可能性があり、モデルはこのシーケンスを進行中に解決する必要があります。
そのため、単一のツール呼び出しを処理する代わりに、タスクが完了するまで実行されるループに全体を配置します。この反復サイクルは**エージェントループ**であり、タスクが完了するまで繰り返される1つのターンです:
- 会話と利用可能なツールをモデルに送信します。
- モデルはツールを呼び出すか、最終的な回答を提供します。
- ツールを呼び出した場合は、実行して、結果を会話に追加し、ステップ1に戻ります。
- 最終的な回答を提供した場合は、停止します。
各ターンで、モデルはこれまでに発生したすべてのこと(以前のツール結果を含む)を見て、次の手を決定します。これがツール使用との全体的な違いです。モデルが満足するまでループを続けます。
本当の変化は誰がステアリングしているかです。これまでのすべてのチャプターでは、コードが制御フローを決定していました。モデルをいつ呼び出すか、ツールをいつ実行するか、次は何が来るかを選択していました。エージェントはその決定をモデルに渡します。ループは「次は何が起こるべきか?」と繰り返し尋ね、モデルの予測は毎回その回答です。制御と引き換えに柔軟性を得て、その取引が到達可能性とリスクの両方の源です。
その下では、新しいことは何も起きていません。これは成長するコンテキストに対する同じ予測であり、ツール使用の往復を繰り返しているだけです。モデルは計画を立てて実行するわけではありません。各ターンで現在見ることができるものから次の単一の最良のステップを予測し、ループが再び実行されます。
それが機能するとき、それは目標に向かって推論しているように見えます。そうでないとき、エージェントがループ内でループするか、行き止まりを彷徨う可能性があります。なぜなら、システムのどの部分も全体的な計画を保持していないからです。
最小限のエージェント
ここにコード内のループがあります。ツール使用からツール定義と実装を再利用し、モデルがツールの要求を停止するまで実行されるループで囲みます。
import json
def run_agent(user_text, tools, tool_impls, max_steps=6):
messages = [{"role": "user", "content": user_text}]
step = 0
while step < max_steps:
response = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
message = response.choices[0].message
messages.append(message)
calls = message.tool_calls
if not calls:
return message.content # ツール不要:これが最終的な回答です
# すべての要求されたツールを実行し、結果をフィードバックします
for call in calls:
args = json.loads(call.function.arguments)
result = tool_impls[call.function.name](args)
messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)})
step += 1
return "Stopped: the agent reached its step limit." # セーフティネット上から下に読みます:モデルを呼び出し、そのメッセージを会話に押し、ツールを要求した場合は、それらすべてを実行して結果を追加し、再度ループします。モデルがツール呼び出しの代わりにテキストを返すとき、そのテキストが最終的な回答で、それを返します。
max_steps値は**ステップ制限**を設定します。これは、ループが逃げてしまうのを防ぐセーフティネットであり、次のセクションはこれについて説明しています。ここの形状は1つのプロバイダーのSDKと一致します;フィールド名はプロバイダー全体で異なりますが、ループはどこでも同じです。
停止とガードレール
モデルが停止するタイミングを決定できるループは、停止に失敗する可能性があります。ツールを永遠に呼び出したり、自分自身を繰り返したり、行き止まりを追いかけたりする可能性があります。すべてのステップはトークンで請求されるモデル呼び出しであるため、逃げてしまうエージェントは素早くお金と時間を消費します。
だから**ガードレール**、ループの周りに配置される制限は、ポーランドステップを追加するのではなく、設計の一部です:
- ステップ制限。 ループ数を制限し、
max_stepsがしているように制限します。ヒットした場合は停止して返します。 - ツール入力を検証します。 モデルは引数を選択するため、実際の結果を持つ任意のものを実行する前に確認してください。
- ツールができることを制限します。 エージェントに必要な最小電力を与えます。削除するツールより安全な読み取りツール。
- コストを監視します。 タスクごとに複数のモデル呼び出しはすぐに乗算するため、トークンを追跡し、予算を設定します。
これらを結ぶスレッド:モデルはパスを即興しているため、それが行わない制限を配置します。エージェントが少なくとも1回は間違ったことをするものと仮定し、その生存可能です。
ワークフローがエージェントを打つとき
エージェントは興味深いですが、それは1つが必要がないときにリーチするための誘惑を作ります。明確なルール:ステップを既に知っている場合は、エージェントを使用しないでください。それらを単純なコード、**ワークフロー**として書き出します。各ステップは固定順序で実行されます。
ワークフローは、より信頼性が高く、安く、デバッグがはるかに簡単です。モデルがパスを見つけることを願うのではなく、パスを制御するため。オープンエンドなタスクでステップは発見された内容に依存する場合にエージェントを保存します。
構造化出力呼び出しまたはRAGルックアップが手で配線されているように見える多くの機能。ステップを事前に書き下ろせない場合にのみ、エージェントに到達します。
実際には
上記のrun_agent関数は、全体です。質問、ツールセット、実装、ループするまで完了。小さなタスクはループが実行される場所を示しています:
answer = run_agent(
"東京の天気はどうですか。傘を持って行く必要がありますか?",
tools, # ツール使用チャプターからの get_weather を含める
tool_impls, # {"get_weather": ...}
)
# エージェントはget_weatherを呼び出し、「曇り」を読み、1つの最終的なターンで回答します:
# 「東京は18度で曇りなので、傘を持ち運ぶことは賢明です。」1つのツール、1つのループ、1つの回答。同じ構造は複数のツールとステップにスケール、形状を変えずにします。これほど有能なものを構築できたら、次の質問は、実際に機能するかどうかです。これは評価についてです。
run_agentループは全体パターンです:質問、ツール、実装を与えて、モデルがツール要求を停止するか、ステップ制限にヒットするまでループします。1つのツールと1つのループは形状を変えずに多数にスケールします。次の質問は実際に機能するかどうか、評価です。 
