AIにおけるハーネス:ディープダイブ — IBM テジャス・クマール

AIエージェント
この記事は約19分で読めます。

IBMのAIデベロッパーアドボケイトであるTejas Kumar氏が、AIエージェントの信頼性を劇的に向上させる「AIハーネス(Harness)」について解説する動画。ブラックボックスであるLLMに依存せず、決定論的な環境制御やガードレール、検証ステップを組み込むことで、安価な旧型モデルでも確実にタスクを遂行させる手法をライブデモを交えて実証している。

Harnesses in AI: A Deep Dive — Tejas Kumar, IBM
The agent hit a login page, panicked, reported success anyway, and the upvote never happened. Tejas Kumar's diagnosis: n...

AIハーネスが必要とされる理由

みなさん、こんにちは。全員の顔が上がりましたね、こんにちは。どうも。
ランチはどうでしたか。美味しかったですか。
そうでもなかったみたいですね。イギリス料理みたいだったのかな。とにかく、こんにちは。私はテジャスです。今日の午後、最初のスピーカーを務めさせていただきます。テジャスという名前は、コンテイジャス(伝染性)と同じような発音です。安心してください、私は伝染しません。私のAIに対する情熱がみなさんに伝染するといいなと思っています。私はこれまでのキャリアの中で、さまざまな場所で何らかの形でAIに関わる幸運に恵まれてきました。最高の仲間から学ぶことができて、本当に最高の喜びでした。現在はIBMでAIデベロッパーアドボケイトをしています。信じられないかもしれませんが、私たちはIBMでAIを使ったさまざまな取り組みを行っています。フロンティアモデルのトレーニングをしたり、ハーネスを構築したりしています。本当に働いていて楽しいラボです。

しかし、今日ここで話したいのはそのことではありません。今日は「AIハーネス」についてお話しするために来ました。AIハーネスです。先に進む前に、ちょっと挙手をお願いできますでしょうか。AIハーネスについて、自分は十分に理解していると自信を持っている方はどれくらいいますか。今日このステージの上でプレゼンできるというくらいのレベルの方です。周りを見回してみてください。誰もいませんよね。本当に見回してみてください。だからこそ、私たちはこのトークを行っているのです。これが私の願いです。このトークの最後に同じ質問をしたとき、みなさんに、なるほど、ようやく理解できた、と言ってもらいたいのです。それがこのトークの目的のすべてです。私には知識を共有すること以外に、ここから得るものは文字通り何もありません。

また、この言葉は今やあらゆる場所で見かけます。今日だけでも5万2000回くらい耳にしたかもしれません。そして、この言葉は人によって異なる意味を持っています。機械学習の世界では、機械学習モデルのための豪華なテストスイートのようなものを意味しますが、AIエンジニアリングの世界では異なる意味を持ちます。そこで今日は、これについて詳しく理解していきましょう。ディープダイブではありますが、時間は18分しかありません。では、先へ進みましょう。

まず、なぜハーネスを使うのかという理由から始めたいと思います。その理由は、私たちが計算資源や推論、トークンを提供してくれる企業にいわば家賃を払っているからです。みなさんの中には、AnthropicやGoogleなど、フロンティアモデルを持つ企業で働いている方もいるかもしれません。そうした方々は、トークン大富豪とでも呼ぶべき存在でしょうか。私はそうではありません。Watsonのモデルを使えば多少はそうかもしれませんが、私たちの大部分はトークン大富豪ではありません。私たちは家賃を払っています。文字通りClaude Proに毎月20ドルを支払っているのです。そうすると、制限されたコンテキストウィンドウが与えられ、完全に自由なコントロールは得られません。そして、私たちが借りているモデルはブラックボックスです。彼らはいつでも、実際にやっているとは言いませんが、もしOpusが何らかの理由で利用できない場合、画面にOpusと表示されていてもSonnetを裏で提供することだってできるのです。私たちは決して気付くことはできません。このように、私たちがコントロールできない変数が多すぎるのです。

では、なぜハーネスが必要なのでしょうか。ハーネスの目的は「信頼性」だからです。この白い線の前に立ってはいけなかったのかな。カメラに映らなくなってしまいますね。まあ、とにかく。目的は信頼性です。私たちが構築するエージェントが、決められた仕事を確実にこなすようにすること。これに尽きます。ブラックボックスのモデルがどうであろうと、私たちが借りているものが何であろうと関係ありません。

ハーネスとは何か

なぜハーネスが必要なのかを理解したところで、そもそもハーネスとは何なのかを第一原理から考えてみましょう。私たちがよく知っていて理解できる、現実世界のハーネスまで遡ってみましょう。山登りをしたことがある人や、その様子を見たことがある人なら分かると思いますが、これがハーネス(安全帯)です。登山家は文字通り、自分自身を何に固定するでしょうか。山です。山は安定しており、そこから滑落するのを防いでくれます。安定したものに自分を固定することで、遠くに流されてしまうのを防ぐのです。これが設計におけるハーネスの本来の役割です。犬を飼っている方はいますか。犬を散歩させるとき、ハーネスをつけますよね。なぜなら、犬が勝手に走り回ってトークンであなたを破産させないようにするためです。それがハーネスです。

しかし、ハーネスについて考えるとき、実際には2つのタイプが存在するという問題があります。1つは機械学習の世界のもので、先ほど触れたように、テストスイートやテストランナーのようなものです。モデルにいくつかの入力を与え、出力の品質を確認します。しかし、ここは「ML Engineer Europe」の会場ではありません。今日私たちが話すのは、AIエンジニアリングにおいて一般的な「エージェントハーネス」についてです。

では、エージェントハーネスとは何でしょうか。ここが最も重要なポイントです。エージェントハーネスとは、モデルの周囲にあって、モデルを現実にしっかりと定着させるためのすべての仕組みのことです。文字通り、モデルを安定した環境に結びつけるためのものです。たとえば、Claude Codeはエージェントハーネスの一種と見なすことができます。中には、いや、それはコーディングエージェントだろ、と言う人もいるでしょう。確かにそれはコーディングエージェントです。しかし、それはハーネスで制御されたコーディングエージェントなのです。

エージェントハーネスには、多かれ少なかれ共通したお決まりの構成要素があります。
第1に「ツールレジストリ」があります。Claude CodeやCursor、Codecksなどは、ファイルシステムから読み込み、書き込み、BASHコマンドを実行するためのツールを持っています。これがツールレジストリです。
第2に「モデル」があります。モデルを選択できるものもあれば、選択できないものもあります。
第3に「コンテキストを管理するためのプリミティブ」があります。現在ハーネスで制御されているエージェントランタイムのほぼすべてが、独自のコンテキストを圧縮する機能を持っています。それがハーネスの仕事だからです。
第4に「ガードレール」もハーネスの一部です。たとえば、最大ステップ数の制限がそれにあたります。5回以上のツール呼び出しは行うな、といった指示がガードレールです。これに達した場合、実行を強制終了します。
第5に「エージェントループ」もエージェントハーネスの一部です。これは面白い点です。このトークを準備するにあたって何人かの人と話したのですが、彼らは、待ってくれ、ハーネスって単なるエージェントループのことじゃないのか、と言いました。いいえ、違います。エージェントループの周囲にある仕組みのことです。実際、それはエージェントループの外側を囲むさらに大きなループ、つまりN-Mループである場合もあります。これについては後ほどコードで少し確認します。
最後に「検証ステップ」があります。これはたとえばコーディングエージェントにおいて、作業が完了した後に、リントを実行し、テストを実行し、何も壊れていないことを確認しよう、というステップです。

今回はコーディングエージェントを例に挙げましたが、あらゆるものに対してハーネスを作ることができます。そしてそれは素晴らしいものです。なぜなら、あなたがコントロールできる安定した環境に、ブラックボックスのモデルをしっかりと固定することができるからです。

ハーネスの構築:ライブデモ

デモをお見せしたいと思います。これから一緒に、必要最低限の最もシンプルなハーネスを作っていきましょう。これを簡易的なAIハーネスと呼ぶことにします。第一原理からこれがどのように機能するのかを理解していきましょう。今回構築するのは、ある明確な任務を持つコンピューター操作エージェントです。その任務とは、Hacker Newsに行き、最初の投稿をアップボート(高評価)することです。これはブラウザを操作するエージェントです。あえて非常に性能の低いモデルを使用します。2023年頃のモデルであるGPT-3.5 Turboを使います。しかし、これにハーネスを装着させることで、実際に仕事を遂行させ、同時にお金を節約します。

喋りすぎましたね。さっそくデモに入りましょう。私のプロジェクトへようこそ。これが私のプロジェクトです。これがエントリーポイントになります。見えますでしょうか。文字が小さすぎますか。もっと大きくしましょう。これで見やすくなりましたね。実はこの部屋は明るすぎるので、ライトモードにしましょう。私の好みではありませんが、時にはこちらの方が見やすいこともあります。

モデルがあり、古いモデルを試そうとしています。そしてこれがプロンプトであり、タスクです。文字通りこれが私のプロンプトです。「ストーリーをアップボートする」。このデモのためにその内容を記述しています。このデモではプロンプトは一切変更しません。なぜなら私たちの多くは、エージェントが思った通りに動かないとき、もっと強力なプロンプトを書かなければならないと考えがちだからです。システムプロンプトを変更しなければならない、と。しかし、それは常に正しいとは限りません。ここではプロンプトには一切触れません。ハーネスを構築するだけで、結果が変わるのです。

いくつかの内容をコンソールにログ出力し、ブラウザセッションを開始します。ブラウザセッションとは何かというと、文字通り単なるPlaywrightです。Playwright MCPのようなものではなく、純粋なPlaywrightです。これは私が作成したクラスで、Chromiumを起動してコンテキストを取得し、ページを作成してナビゲートするオープンメソッドを持っています。文字通りPlaywrightの関数を呼び出しているだけです。これは伝統的なエンジニアリングですね。セッションを作成してセッションを開き、ブラウザウィンドウとコンテキストを用意します。次にツールを作成し、そのブラウザセッションをツールに渡します。そしてコンテキストを作成し、今回のタスク、つまりプロンプトをコンテキストに渡します。

ツールを作成する関数は、文字通りの内容です。ここにはいくつかの型定義があり、ツール作成関数はブラウザセッションを受け取ってツールを返します。これらのツールは私が発明したものではなく、OpenAIのSDKのものです。名前、説明、パラメータ、そしてランタイムで実際にツールを呼び出す実行方法が定義されています。画面を操作するためのツールを私が作成しました。とても簡単です。これが私のツールです。

次にコンテキストの作成です。コンテキストエンジニアリングという大げさなものかと思うかもしれませんが、全く違います。これが私のコンテキストです。ここには何もありません。文字通り最も基本的なシステムプロンプトと、ユーザーのタスクがあるだけです。非常にベーシックなものです。

そして、エージェントをループで実行する実行ループがあります。ここで何が行われているかも確認してみましょう。Whileループになっています。これはエージェントループであり、エージェントからの応答を取得します。応答がストップを意味している場合、つまりLLMが「完了した」と言った場合は、値を返します。それ以外の応答を受け取った場合は、何もせず、これらのイベントをトレースに追加するだけです。履歴を履歴の大きなリストにプッシュしていきます。意味が分かりますでしょうか。ここでやっているのはこれだけです。完了するまでイベントを収集し続けるだけの単純なループです。

これは非常に基本的なものです。では、どのように動くか見てみましょう。ここに来て、実行してみます。大丈夫ですか、そちらの方。お水は必要ですか。エージェントを実行します。Chromiumが開きます。Hacker Newsに行きました。ここまでは順調です。アップボートをクリックします。おっと。ログイン画面が表示され、パニックを起こしてクラッシュしてしまいました。しかし、見てください。モデルは嘘をついています。これが見えますか。これは問題です。

では、解決策は何でしょうか。もっと強いプロンプトを与えることでしょうか。いいえ。システムプロンプトを変更して、システムプロンプトにあらかじめこれらのログイン情報を常に含めてログインするように指示することでしょうか。いいえ。では、これをどのように解決すればよいのでしょうか。ログを確認すると、モデルは単にアップボートボタンをクリックしただけで、それを成功と見なしていることが分かります。検証を行っていないのです。これこそがハーネスの仕事です。

ガードレールとコンテキストの追加

ここから、少しずつ段階的にハーネスを構築していきます。ここでコードを直接書くようなライブコーディングはしません。私たちはもうコードをいちいち手で書いたりはしませんよね。差分を確認していく時代です。手でコードを書いている人はいますか。いないようですね。あ、もしかしたらここにいる中にはいるかもしれませんが。冗談です。

これが最初に行う変更です。これは先ほどのインデックスファイルで、お見せした実行ループがあります。しかし、ここに1つの要素を追加します。それがデフォルトのガードレールです。いくつかのガードレールを作成します。ガードレールはどのようなものでしょうか。エディタで確認してみましょう。ここにはいくつかの型がありますが、これが私たちのガードレールです。2つの条件があります。最大イテレーション数、つまり6ステップ以上の処理を行ったら強制終了するというもの。そして最大メッセージ数、つまりこれ以上のメッセージが蓄積されたらコンテキストを圧縮するというものです。これらは単なるガードレールです。これらを結合するための小さなユーティリティもあり、ここで自由に組み合わせることができます。

変更点に戻りましょう。これがガードレールです。エージェントループに戻ると、この差分の中で実際にガードレールが使用されているのが分かります。ガードレール関数を組み込んでおり、ここで何をしているかというと、蓄積されたメッセージ数を確認し、多すぎる場合はコンテキストをトリミングしています。最後にお見せしたかったのは、ガードレールで何を行ったかに関するメタデータを追加して、コンテキストサイズをプッシュしている点です。

私たちのコンテキスト圧縮機能は非常にシンプルで素朴なものです。実際どのような動きをするのか、見やすいように構文ハイライト付きで開いてみます。やっていることとしては、システムプロンプトとユーザープロンプト、そして最新の2つのメッセージを常に維持するという処理です。ガードレールがトリガーされると、中央にあるシステムプロンプトとユーザープロンプトより後ろのものをすべて削除し、最後の2つのメッセージだけを残します。これは非常に素朴なやり方です。もっと良い方法はありますが、これは最初のステップですので、ここから始めていきます。

ハーネスへのロジックの分離と検証の導入

これでハーネスの原型ができつつありますが、まだハーネスという名前のファイルにはなっていません。いわばハーネスが誕生する一歩手前の状態です。そこで、これを正式にハーネスと呼ぶことにしましょう。別の差分をお見せします。インデックスファイルからはほとんどすべてのコードが削除されています。そして、それらはハーネスという名前のファイルに移動されました。現在のエントリーポイントであるインデックスファイルを見てみましょう。中身はすべて消えています。プロンプトは残っていますが、文字通り19行ほどのコードになり、ハーネスを実行する関数があるだけです。これまでのすべてのロジックをここから取り除き、ハーネス実行関数の中に隠しました。予想通り、ハーネス実行関数は、先ほどインデックスファイルで行っていたこととまったく同じことを行います。コンソール出力を行うプリント関数が追加されたくらいで、新しいことは何もありません。ここまではクリアでしょうか。単にコードを移動しただけです。

ハーネスという仕組みが用意できたので、実際にこれを使ってみましょう。自分としてログインする問題を解決する前に、まずはモデルが嘘をつく問題を解決しましょう。モデルはアップボートしたと言いましたが、実際にはしていません。それを正確に把握したいのです。そこで、いくつかのガードレールを追加し、モデルに真実を語らせるようにします。失敗したなら、失敗したと正直に伝えさせるのです。

それはどのように実現できるでしょうか。こちらの差分を見てみましょう。多くの変更が加えられています。ハーネス実行関数に3つ目の引数を追加しました。それが検証ステップと最大試行回数です。最大試行回数はガードレールに適用されます。もしこのタスクに3回以上トライしてもダメなら、諦めるという設定です。

ハーネスのファイルに行くと、手動のコードが多く追加されているのが分かります。これはプロンプトの変更ではなく、私のロジックです。メインのロジックとして、ハーネス実行関数は移動したコードをそのままラップするのではなく、それを別の関数に移しました。その関数をハーネス試行実行関数と呼ぶことにします。ブランチを切り替えますね。

ハーネス試行実行関数を見てみましょう。コードを折りたたんで整理します。この関数の中身は、先ほどインデックスファイルにあったものと同じです。メインのハーネス実行関数は最大3回までしか実行されないループになっているため、中身をこの試行用の関数に移動したのです。意味は分かりますでしょうか。安全性のために、ハーネスのレベルで最大ステップ数を強制しているのです。

そして、ハーネス試行実行関数がそれを呼び出します。ここには、私が書いたアップボート成功検証関数というものがあります。これは決定論的なコードです。それをお見せしたかったのです。これは何をしているのでしょうか。エージェントループの中でトレースを行っていたのを覚えているでしょうか。履歴イベントを追加していましたよね。それを振り返って、ブラウザがアップボートボタンをクリックしたか、そしてそれが真に成功したかを確認し、そうであれば真(True)を返します。

しかし、ここには大きな例外処理があります。ログイン失敗のケースです。もしハーネス自動ログインという名前のツールがあり、メッセージが失敗から始まっている場合は、処理を途中で抜けて、これは失敗したと判断します。嘘を排除するのです。同様に、回復不能なログインリダイレクトのケースもあります。エージェントループがプッシュしてきたツールの履歴を確認します。もしハーネス自動ログインが実行されておらず、現在のページがログインURLである場合は、再び失敗とみなします。このように、もしこれが起きたら、あるいはあれが起きたら、すぐに失敗として処理を早期に終了させるのです。クリアでしょうか。これがハーネスの役割です。

では、このハーネスを使って実行してみましょう。エージェントを実行します。Hacker Newsにアクセスし、同じサイクルを繰り返します。ここに来ました。今回も失敗しましたが、見てください、モデルは嘘をつくのをやめました。なぜなら、私たちのハーネスがツールの履歴をチェックし、実際に何が起きたのかを確認しているからです。これこそがハーネスが果たすべき役割です。素晴らしいですね。問題を解決するための第一歩は、問題が存在することを認めることですから、これでもう戦いの半分には勝利したようなものです。テスト駆動開発のような感覚ですね。正しく失敗できるようになったので、次は成功させることができます。

決定論的な自動ログインの実装

最後の差分をお見せして、このトークを締めくくりたいと思います。4つ目の変更として、新しい関数を追加しました。ログインハンドラーと呼ばれるものです。見やすいように構文ハイライトを適用します。ログインハンドラー作成関数がしていることはこれだけです。エージェントのループが実行されるたび、トレースにプッシュされる直前に実行されます。これがチェックするのはブラウザセッションの現在のURLです。もしログインページにいなければ、そのまま何もせずに処理を返します。この計算コストはまったく高くありません。しかし、もしログインページにいる場合は、一時的な認証情報を入力します。これは環境変数にしてもいいですし、安全に管理できます。そしてエージェントからではなく、ハーネスからプログラムによって決定論的かつ安全にボタンを送信します。なぜなら、このファイルは私が許可したあらゆる秘密情報にアクセスできるからです。

この関数はどのように呼び出されるのでしょうか。これはエージェントループの中で呼び出されます。エージェントループに戻ってみると、トレースをプッシュしていた場所がありますね。その直前です。ログインハンドラーが存在する場合、エージェントループの中でトレースをプッシュする直前にログインハンドラーを呼び出します。ログインハンドラーは何をするかというと、ログインページにいなければ何もしません。ログインページにいる場合は、素早く認証情報を注入してフォームを送信し、元の場所に引き戻します。また、ここで見られるように、キューにメッセージをプッシュします。「ハーネスです。ログインを代行しました。もう大丈夫です」というメッセージです。分かりますでしょうか。このように、ハーネスは文字通り、エージェントを何か安定的で決定論的なものに固定しているのです。そのために存在しています。

では、これを実行して何が起きるか見てみましょう。エージェントを実行します。Hacker Newsが開きます。そしてログイン画面に達したとき、ハーネスのステップが働き、ログインを実行し、最初の投稿をアップボートして終了しました。

素晴らしい。Nilixの投稿を正常にアップボートしました。6回のイテレーションの後に成功しました。リンクをクリックしてHacker Newsにアクセスし、実際に確認してみましょう。確かにアップボートされています。アンボート(投票取り消し)ができる状態になっているということは、正しくアップボートされたということです。エージェントがコンピューターを操作し、私がこのステージ上で作成したハーネスを使って、私としてログインしたのです。それがこの仕組みの目的です。ここまではクリアでしょうか。理解できましたか。ハーネスの役割が分かったというように、みなさんが頷いているのが見えます。これは私にとって最高の光栄です。素晴らしい。

AIハーネスの未来

話をまとめましょう。私のここでの仕事は終わったと思います。これが実務においてどのような意味を持つのでしょうか。なぜ私がこれほどまでにハーネスにこだわるのかというと、それらが世界を動かしているからです。モデルは非決定論的なものであり、みなさんはより少ないリソースでより多くのことを行いたいと考えています。安価なモデルを使いたい。Qwenのようなモデルや、さらに小さなモデルを使いたい。無料のオープンソースモデルを使い、優れたハーネスを組み合わせることで、非常に大きな成果を上げることができます。

だからこそ、IBMでは企業向けにデプロイできるオープンソースプロジェクトを作成しました。これにより、非常に大きな大企業が、データ制限の厳しいプライベートな領域で、Teamsの通話記録やPDF、請求書など、あらゆる種類のデータに対してRAG操作を行うことができるようになります。私たちはそれを「Open RAG」と呼んで構築しました。RAGが今でもトレンディかどうかは分かりませんが、Open RAGには非常に強力なハーネスが備わっており、社内の高度にサイロ化されたデータに対して質問を行う際に、エンタープライズレベルのセキュリティを提供します。そこにハーネスエンジニアリングの本領があるのです。

では、まとめましょう。多くの内容をカバーしました。ディープダイブだったでしょうか。18分ほどの短い時間でしたが、十分に深い内容だったと思います。私たちはかなり遠くまで来ました。私がプロンプトには一度も触れなかったという事実に、ぜひ注目してください。システムプロンプトは一切変更していません。私たちはただハーネスを構築しただけで、結果が劇的に変わったのです。もちろん、秘密情報やトークンを追加することも可能です。最終的に多くのことを成し遂げました。ハーネスとは何か、それがもたらす価値、そしてそれをどのように活用できるかを理解していただけたなら幸いです。

この先には何があるでしょうか。私には他の人と同じように未来を見通す水晶玉があるわけではありません。しかし、2025年がエージェントの年であったことは間違いありません。そして、2026年はハーネスの年です。この言葉がここで何回使われたかからも、それは確かだと思います。

そして、2027年が「オンザフライで動的に生成されるハーネス」の年になったら、非常に面白いことになるのではないかと期待しています。そんな世界が来たら素晴らしいと思いませんか。エージェントに向かって、航空券を買ってくれ、などと指示を出します。エージェントは作業を開始する前に、自らハーネスを作成するのです。これはプランモードに似ています。プランモードを使っている方はいますか。それをさらに強力にしたものです。エージェントが実際のハーネスを自ら作成します。自己意識を持ち、ここでハルシネーションを起こすかもしれない、といったことを自ら察知してハーネスを作成し、仕事をこなし、ガードレールなども含めてすべてを整えた状態であなたに結果を返すのです。それはとても素晴らしいことです。動的にその場で生成されるハーネス。これこそが、AGI(汎用人工知能)に向けた次の論理的なステップであると私は考えていますし、ぜひ見てみたいものです。これが私の一風変わったアイデアに過ぎないのかどうかは分かりませんが、方向性としては間違っていないと思います。

ということで、そろそろ時間がなくなってきました。最後の30秒を使って、みなさんに心からの感謝を伝えたいと思います。スライドはGitHubにありますし、私もGitHubにいます。もっとたくさんお話ししたいですね。ありがとうございました。

コメント

タイトルとURLをコピーしました