初めてのAIエージェント:最小限で誠実な構築
エージェントとは、ツールを備えループの中に置かれたモデルのこと。最小限で誠実な版を作り、なぜ動くのかを理解し、野心を加える前にどこで破綻するかを学びましょう。
「AIエージェント」という言葉は、説明する以上のことを約束する言葉になってしまいました。マーケティングの装飾を剥ぎ取れば、エージェントとはかなり具体的で、かなり作りやすいものです。すなわち、ループの中に置かれた言語モデルが、呼び出せるツール一式を与えられ、自ら完了と判断するまで目標に向かって働く、というものです。これがすべてのアイデアです。本稿では、このアイデアの最小限で誠実な版を作り、各要素がなぜそこにあるのかを説明し、そしてより有益なこととして、どこで壊れるのかを示します。そうすることで、あなたの初めてのエージェントは、コピーしてうまく動くことを願うものではなく、自分で本当に理解しているものになります。
エージェントとは本当は何なのか
ただの言語モデルは一つのことしかしません。あなたがテキストを送ると、テキストが返ってきて、やり取りは終わります。何かを調べることも、計算を実行することも、世界に対して何らかの行動を起こすこともできません。エージェントはこの土台に三つのものを加えます。モデルが使うことを許されたツール、一度きりではなく複数回行動できるようにするループ、そして到達しようとしている目標です。
決定的な動きは、モデルが単なるテキスト生成器であることをやめ、意思決定者になる点にあります。各ターンでモデルはこう選びます。答えるのに十分な情報があるか、それともまずツールを呼び出すべきか。ツールを呼び出せば、その結果を得て、再考し、また選びます。この循環――考え、行動し、観察し、繰り返す――が仕組みのすべてです。これより手の込んだものは、すべてこのループのバリエーションにすぎません。
三つの材料
ツールとは、あなたがモデルに公開する関数のことです。ウェブを検索する、データベースに問い合わせる、計算をする、メールを送る、などです。モデルにとってツールとは、名前と、それが何をするかの説明と、期待する入力の説明です。モデルが自らあなたのコードを実行することは決してありません。モデルは呼び出しを要求し、あなたのプログラムが関数を実行し、あなたが結果を返すのです。この境界は重要であり、後ほど再び立ち返ります。
ループこそが、エージェントを単一のツール呼び出しと分けるものです。ツールが結果を返したあと、モデルはその結果を見て次に何をするかを決めます。別のツールを呼び出すかもしれませんし、答えるかもしれません。ループがなければ、ツールを一度だけ使えるモデルしか手に入りません。ループがあれば、ステップを連鎖させられるモデルになります。
目標とは、あなたが与えるタスクに加えて、どう振る舞うべきかについての指示です。何のためにあるのか、どのツールを優先すべきか、いつ止まるべきか。明確な目標こそが、答えへと収束していくエージェントと、さまよい続けるエージェントとの違いを生みます。
ツールを設計する
エージェントの品質は、その大部分がツールによって決まります。そしてツール設計こそ、初心者が投資を怠るところです。モデルは、説明だけからそのツールが何をしていつ手を伸ばすべきかを理解できて初めて、ツールをうまく使えます。各ツールの説明は、有能だが字義どおりに読む読者のために書かれた小さなドキュメントだと考えてください。
入力と出力について具体的に書きましょう。説明が「検索する」とだけ書かれているsearchツールでは、それがウェブを検索するのか、あなたのファイルを検索するのか、商品カタログを検索するのか、モデルには何も伝わりません。明確に綴りましょう。何を検索するのか、クエリはどのような形であるべきか、何が返ってくるのか。失敗のケースにも名前を付けましょう――何も見つからなかったときにツールが何を返すのか――そうすればモデルは、結果をでっち上げる代わりに、まともに反応できます。曖昧なツールは、誤ったタイミングで誤ったものを呼び出し、その混乱の上で即興を始めるエージェントを生みます。
良い規律があります。ツールは少なく、はっきりと区別されたものに保つことです。明確に異なることをする三つのツールは、互いに重なり合う十のツールよりも、モデルにとって選びやすいものです。エージェントがそれらなしで苦戦するのを見届けてから、いつでもツールを増やせます。
ループを一歩ずつ
最小限のループを擬似コードで示します。これは他のすべてがこの上の装飾にすぎないので、ゆっくり読む価値があります。
messages = [system_instructions, user_goal]
loop:
response = model(messages, tools)
if response asks to call a tool:
result = run_the_tool(response.tool, response.inputs)
append the model's request and the result to messages
continue # back to the model with new information
else:
return response # the model is done; answer the user
何が起きているか読んでみましょう。あなたは会話と利用可能なツールを送ります。モデルはツールを要求するか、最終的な答えを返すかのどちらかです。ツールを要求したら、あなたがそれを実行し、要求と結果の両方を進行中の会話に追加し、再びモデルを呼び出します――いまや少し前よりも多くの情報を持った状態で。モデルは答えるのに十分なだけ情報を得るまで、情報を得続けます。ループは魔法ではありません。それは単に「モデルが求めたものを与えて、もう一度尋ねる」というだけのことです。
省略できないガードレール
上記のループには、すべての実用的なエージェントが塞がねばならない穴があります。永遠に動き続けることを止めるものが何もないのです。モデルは同じツールを呼び出し続けて行き詰まることもあれば、決して到達しない目標を追い続けることもあります。そこで上限を設けます――ステップ数の最大値です。それを超えたら、たとえ未完了でも、エージェントは停止して手元にあるものを報告します。「許されたステップ数では終えられませんでした」と認めるエージェントは、無限にループするものや、さらに悪いことに際限なくコストを積み上げるものよりも、はるかに安全です。
二つ目のガードレールは、決定することと実行することの間のあの境界に関するものです。モデルはツールを呼び出すと決定しますが、それを実行するのはあなたのコードです――そしてモデルは間違うこともあれば、操られることも、単に混乱することもあります。もしツールが取り返しのつかないこと(データを削除する、お金を使う、実在の人にメッセージを送る)をできるなら、最初の構築ではループにそれを監督なしで起動させてはいけません。初期のツールは読み取り専用に保つか、結果を伴う行動の前には人間の確認を必須にしましょう。モデルは、自発性のある若手の同僚です。役に立ち、ときどき間違え、そしてまだ、取り消せないものの鍵を渡してよい相手ではありません。
初めてのエージェントはどこで間違うか
いくつかの失敗はあまりに確実に現れるので、あらかじめ名前を付けておく価値があります。一つ目はツールの取り違えです。モデルが誤ったツールを選んだり、不正な入力を与えたりするもので、ほとんどの場合ツールの説明が曖昧だったことが原因です。モデルを責める前に説明を直しましょう。二つ目はループです。ツールが役に立たないものを返し続け、モデルが同じ対処を試み続けるために、エージェントがステップを繰り返すのです。ステップの上限がこれを捕まえます。トランスクリプトを読めば、なぜそれが起きたかが分かります。三つ目は失敗したツール呼び出しのあとの過信です――検索が何も返さなかったのに、モデルはまるで何かを見つけたかのように答え進めるのです。治療法は、どこでも役立つあの同じものです。ツールが空で返ってきたとき何をすべきか、モデルに明示的に伝えることです。
この三つすべてをつなぐ糸は、エージェントのデバッグはそのトレースを読むことで行う、という点です。思考、ツール呼び出し、結果の連なりは、エージェントの推論のトランスクリプトです。何かがうまくいかなかったとき、その答えはほとんどいつもそのログの中に座っており、検査できないエージェントとは、改善できないエージェントなのです。
まとめ
エージェントとは、目標に向けられた、ツールを備えループの中に置かれたモデルです。まずその最小限の版を作りましょう。はっきりと説明された少数のツール、結果をモデルに戻すループ、ステップに対する厳格な上限、そして取り返しのつかない何かの前に置く人間の確認。トレースを読みながら動きを見届ければ、それがどこでツールを取り違え、どこで堂々巡りに陥るのかが、まさにそのとおり見えてきます。派手なマルチエージェントシステムも、同じループをスケールさせたものにすぎません。小さく誠実な版を完全に理解すれば、残りは謎ではなく、ただの肉付けになります。
