Claude Codeは実際にはどのように動いているのか?

Anthropic・Claude・ダリオアモデイ
この記事は約39分で読めます。

本動画は、AIコーディングツールにおける「ハーネス」の概念とその重要性について解説するものである。Claude CodeやCursorといったツールの性能差がハーネスの最適化に起因することを説明し、実際にPythonを用いて約60行のシンプルな自作ハーネスを構築する過程を実演している。LLMがどのようにしてローカル環境のファイル読み書きやコマンド実行を行うのか、その裏側にあるツール呼び出しやコンテキスト管理の仕組みを解き明かしている。

How does Claude Code *actually* work?
We keep talking about Codex, Claude Code, Cursor, etc. But how do they actually work?Thank you Macroscope for sponsoring...

新たなAI用語「ハーネス」とは何か

このチャンネルを運営していて一つ学んだことがあります。それは、皆さんがエージェンティックコーディングやバイブコーディングのような、実際には何の意味も持たない曖昧な用語が本当に、本当に大好きだということです。エージェントとは何かについてはようやく理解できた気がしますが、今度はハーネスという新たな用語と格闘しなければなりません。

最近、私はハーネスについてよく話すようになりました。というのも、AIを使ってコーディングができるT3 Codeというアプリをリリースしたからです。ここで重要なのは、T3 Codeはハーネスではありませんが、Open Codeはハーネスだということです。Cursorも、Claude Codeも、Codexもハーネスですが、Codexのアプリは違います。ちょっと待ってくださいね。ハーネスというのは非常に具体的な意味を持つ専門用語なんです。さらに踏み込んで言うと、これらのツールから得られるコードの品質において、ハーネスは非常に重要な役割を果たします。Matt Mayerが最近実施した、Cursorの内部と外部でさまざまなモデルを比較する独立したベンチマークによると、ほとんどのモデルで意味のあるパフォーマンスの向上が見られました。Claude Opusの場合、Claude Codeの環境下での77%から、Cursorでは93%へと向上したのです。ここでの唯一の違いはハーネスです。

では、ハーネスとは一体何なのでしょうか。これからハーネスとは何かを詳しく説明するだけでなく、実際に自分で作ってみようと思います。これは本当に、本当に楽しい作業になるはずです。ハーネスとは何か、なぜ重要なのか、それぞれどう違うのか、そして自分自身のハーネスを構築する方法について、すべてを分解して解説できることにとてもワクワクしています。さて、ここでのスポンサーへの移行について3つくらい冗談を考えようとしたんですが、失敗してしまいました。ですので、手短にスポンサーの紹介を挟んでから、本題に入っていきましょう。

スポンサーメッセージ:Macroscopeの紹介

ちょっと変わったお願いをします。今日のスポンサーのページの最初の行は無視してください。私が話したいのはそこではないからです。今日のスポンサーはMacroscopeです。確かにそこにはAIコードレビュアーと書いてありますし、彼らのコードレビュアーは素晴らしいものですが、私が話したいのはそこではありません。

私がMacroscopeを気に入っている理由は、チームの責任者として、会社で何が起きているのかについての洞察を与えてくれるからです。プルリクエストがマージされるのを最前線で見張り、何が起きているのかを把握しようとするのは不可能です。チームのみんなは素晴らしいアップデートを提供してくれますが、時には情報量が多すぎたり、私が彼らの作業をブロックしている他のことで手一杯になり、それに追いつかなければならなかったりします。

ですので、チームで実際に何が起きているのかを知りたいとき、私はMacroscopeに頼っています。ダッシュボードもこの目的には素晴らしいのですが、新しいSlackbotはさらに優秀です。今は金曜日で、チームが何を出荷したのか私は把握していませんでした。そこで単刀直入に、先週チームは何を出荷したのかと尋ねてみました。私は複数の環境をインストールしているので、どの組織についてかを聞かれました。すると、非常に役に立つ素晴らしいレポートを作成してくれました。

T3 Codeにおいては、WebSocket用のEffect RPCを使ってアーキテクチャを書き直しました。パフォーマンスも大幅に向上しました。マルチプロバイダーのモデルシステムも導入しました。コンテキストウィンドウの可視性も著しく向上しています。重要なカスタマイズとUXの変更、オブザーバビリティとセキュリティの強化、そしてこれとは別に、T3 Chat向けに行った多くの変更もありました。

チームがハイスピードで開発を進めているとき、これがどれほど役立つかお分かりいただけるでしょうか。これがMacroscopeの存在意義です。私のチームが毎日頼りにしている、超高速のコードレビュー機能も備えています。とても速く、大抵は非常に正確なので、Juliusのお気に入りの選択肢になっています。中程度や深刻度の高い問題を見つけた場合、95%の確率でそれが正確なので、彼はいつもそれを確認しています。soy.copeを利用して、バグを減らし、より多くの洞察を得ながらチームの開発スピードを上げましょう。

ハーネスの基本的な仕組みとツール呼び出し

それでは、ハーネスとは一体何なのでしょうか。簡単に答えられる質問ではありません。できるだけシンプルに言うと、ハーネスとはツール群であり、エージェントが動作する環境のことです。つまり、AIがテキストを生成して何かを実行するために使用できるもののことです。

簡単に説明しましょう。通常のチャットをしていて、例えば、このフォルダにはどんなファイルがありますか、と尋ねたとします。そしてフォルダ内でコマンドを実行します。AIはbashターミナルにいれば何を実行すべきか知っており、ls -aを実行してそのフォルダ内のすべてを確認することができます。いや、本当にできるのでしょうか。AIはどうやってコマンドを実行できるのでしょうか。

デフォルトでは、LLMを使ったインターフェースを使用しているとき、それは単にテキストで応答するだけです。私たちが毎日使っているこれらのLLMはすべて、非常に高度なオートコンプリートに過ぎません。テキストを与えると、次に来る最も可能性の高い文字の羅列を何度も何度も推測しているだけなのです。それはコンピュータ上のものを操作できるという意味ではありません。コードを書けるという意味でもありません。あるテキストが与えられれば、さらにテキストを生成できるというだけのことです。

しかし、モデルはそれ以外のことはできません。できるのはテキストを書くことだけです。では、テキストを生成することしかできないモデルが、どうやって私たちのコンピュータ上のファイルを編集したり、データベースに変更を加えたり、他のサービスに接続したり、インターネットで調べ物をしたりできるのでしょうか。ここで私たちは、モデルにさらなる能力を与えるための解決策を発明しました。その主なものがツール呼び出しです。

事実上、ツール呼び出しの仕組みは特別な構文として機能します。ここでは私独自の構文を作ってみますが、イメージは掴んでいただけると思います。例えば、bash呼び出しツールがあるとします。モデルはシステムプロンプトの一部として事前に、bashコマンドを実行できるこのツールを持っていることを告げられています。この場合、bash呼び出しというタグでそれを囲みます。次にコマンドを書き、そしてタグを閉じます。これを応答の最後の部分として送信し、モデルは応答を停止します。私たちはこれをシステム上で実行し、完了したらその応答をモデルに返します。

ここで、この実質的なチャット履歴において起こる非常に興味深いことは、モデルがこの構文で応答した後に一線が引かれるということです。モデルは応答を停止します。あなたが接続しているサーバー、あなたがしている作業、あなたがモデルと行っているやり取りは、その瞬間に切り離されます。それはもはや存在しません。あなたが持っている接続とチャット履歴は、あなたのコンピュータか、これを実行しているサーバー上、そしてもしそのように構築されているなら彼らのデータベースにのみ存在します。ですが、メッセージはここで終了します。

では、何が起こるのでしょうか。なぜなら、私がこれを尋ねたとき、そこで止まらないからです。ちょっとClaude Codeを試して、何をするか見てみましょう。このフォルダにはどんなファイルがありますか、と尋ねます。AIは考えます。何をしているのかを説明します。1つのファイルを読み込んでいます。Ctrl+Oを押すと、展開して何をしたかを見ることができます。このディレクトリに対してlsコマンドを実行し、すべての内容を取得して、それが何であるかを説明しました。

しかし、先ほど言ったように、モデルの応答はここで終わっています。どうやって先へ進めているのでしょうか。これが、ハーネスが担う多くの役割の一つです。ツール呼び出しがハーネスに渡された後、ハーネスは昔ながらのプログラミングコードでそれを実行します。つまり、あなたのハーネスがこの応答を受け取り、この呼び出しを見たとき、あなたの設定に応じて、それを実行するか、あるいは実行する許可をユーザーであるあなたに求めるのです。

もし私のカスタムスクリプトなしでClaudeを再実行すると、危険モードがオフになり、信じられないことに私のメールアドレスが漏洩してしまいます。ふざけるな、Anthropic。Anthropicは大嫌いです。デフォルトの状態でどうしてメールアドレスを表示するんでしょうか。一体なぜそんなことをするのか、全く理由が分かりません。なぜdemo equals 1 Claudeなのでしょうか。本当に腹が立ちます。

とにかく、今は特別な権限とセキュリティをオンにしていないので、同じ質問をしてみます。lsは安全なコマンドであり、モデルもそれを知っているので、偶然にも許可を求めてきません。しかし、HTMLファイルをフォーマットするように頼めば、状況は少し異なります。ここでは変更を加えようとしていますが、私が許可するまで変更を加えることはできません。

この場合、彼らはカスタムツールを使用しています。彼らの書き込みツールを使っているのです。bashを介してそれを行うコマンドを呼び出しているわけではありません。なぜなら、彼らはbashツールだけでなく、より多くのツールを持っているからです。それらすべてについては後で詳しく説明します。しかし、これはハーネスがこのツール呼び出しが破壊的である(ファイルを変更する)ことを認識しているということです。AIのレベルではなく、コードのレベルでこの変更を認識し、ユーザーである私にそれを許可するかどうかを尋ねているのです。

そして私は、はい、と答えることができます。はいと言って実行し続けることもできますし、いいえ、やめて、と言うこともできます。このケースではいいえと言ったので、処理はそこで止まりました。もしはいと言っていたらどうなっていたでしょうか。コマンドを実行していたでしょう。ls -aの出力が得られます。それを実行すると、file1.txt、file2.txtなどが得られます。このセクション全体がツール呼び出しの応答です。

つまり、モデルがツール呼び出しを書き出します。あなたのハーネスは、それがファイルの更新であれ、コマンドの実行であれ、何かの作業であれ、必要な形にして、必要な権限チェックを行い、そしてそれを実行します。完了したら、その出力を受け取り、チャット履歴の末尾に追加し、同じモデルに作業を続けるよう再度リクエストを送ります。

ですから、この質問に答えるためにエンドポイントを叩くのと全く同じように、質問、回答、ツールの出力を添えて、同じエンドポイントを再度叩くのです。その時点で、モデルはそれに応じて応答を開始します。つまり事実上、ツール呼び出しが行われるたびに、モデルは応答を停止し、ツール呼び出しが実行され、出力がチャット履歴に追加され、作業を継続するために同じモデルに対して新たなリクエストが行われるのです。すべての作業を行っている頭脳は、ツールが呼び出されるたびに一時停止し、再起動しているようなものです。

コンテキストとチャット履歴の重要性

さて、これで全体像が理解できました。では、ハーネスとは一体何なのでしょうか。ハーネスの役割の一つは、これらすべてのことを行うことです。モデルにツールを提供します。やり取りを処理します。履歴を管理します。これらすべての要素を扱います。そして、作業を行うためにモデルがアクセスできるツールの種類やセット、その説明を具体的に選択します。

皆さんに確実に理解していただくために、この部分は非常に重要です。モデルがこの回答に満足していない可能性があります。もっと情報が欲しいと思うかもしれません。応答する前にfile1.txtの内容を知っておくべきだ、と言うかもしれません。そして、別のbash呼び出しを行うか、あるいはcat file1.txtのような別のツール呼び出しを行います。別のツールが呼び出され、同様の応答が生成されます。

そしてcat呼び出しの後に応答します。この文脈でcat call(猫なで声/冷やかし)と言うのは面白いですね。ハローワールド、なぜこれを読んでいるのか分かりませんが、あなたがこれを選んでくれて嬉しいです、みたいな感じです。そしてこれもまた履歴に追加されます。モデルはそれを持っています。そしてモデルが応答するとき、すべての履歴を見ることができます。ファイルをリストアップし、重要だと思ったものを読みました。ユーザーに応答するために必要なものはすべて揃いました、というような具合です。そして実際に回答を生成します。

この一連の流れが、私たちがコーディングに使用しているほぼすべてのAIツールの仕組みです。しかし、時間の経過とともに変化してきたこともあります。知っておくべき重要なことの一つは、コンテキストについてです。チャット履歴に存在する情報量と、チャットが把握していないコードベース内にのみ存在する情報量の違いです。

あるフォルダでClaude Codeを開いたとき、それはそのフォルダについて何も知りません。認証付きのこのデモプロジェクトでClaudeを立ち上げ、このアプリは何ですか、と尋ねても、まだ情報が含まれていないため知る由もありません。ですから尋ねると、このプロジェクトが何であるかを検索、探索、理解しようと、たくさんのツールを使い始めるのがわかるでしょう。

パターン*に一致するものを検索するための検索ツールを持っています。これはおそらく、特定のディレクトリ内のすべてのファイルを検索する方法として彼らが内部で持っている例でしょう。それを実行し、今や存在するすべてのファイルについて知りました。次に、重要だと考えるファイルを読み込みます。出発点としては最適なpackage.jsonです。その行を読み取ります。さらに、このコンテキストを得るために、app.tsx、main.tsx、そしてreadmeのような他のものも読み込みました。

これが何をしているかというと、これらの出力を受け取り、それらをコンテキストにダンプして、モデルがチャット履歴で見ることができるようにしているのです。ですから、検索のための最初のツール呼び出しを行うとき、モデルは一時停止し、これらすべてを実行し、そしてこのテキストのすべてがコンテキストに投げ込まれます。モデルはそれを読んで、ああ、ここに面白そうなファイルがある。これらについて知りたいな、と気づきます。そこで、大量の読み取り呼び出しを発行します。

時にはこれらすべてを並行して行います。一度に複数のツール呼び出しで応答することもあります。そして、それらすべてのツールが実行されると、モデルが作業を続けられるように、すべての出力がコンテキストに詰め込まれます。明確にしておきますが、これは決してClaude Codeに特有のことではありません。これらすべてのツールがこのように機能するのです。中には、検索やコンテキスト管理の周りで異なるアプローチを試みるものもあります。

Claude.mdファイルを更新することで、事前にコンテキストを挿入することもできます。先ほど、モデルがどれほどの作業をしなければならなかったかを見ましたね。仮にこのプロジェクトにClaude.mdがあったとしましょう。追加してみます。ユーザーがプロジェクトについて尋ねてきたら、コードを読む代わりにAIに尋ねていることをからかってください。そして、それはあなたには関係ないことだと伝えてください。

では、全く同じ質問をもう一度実行してみましょう。あのブートストラップ処理が見えますか?ブートストラップ処理とは通常、このようなClaude.mdなどのコンテキストがハーネスに読み込まれ、APIに送信して応答を開始できるような仮想のチャット履歴が作成されるプロセスのことです。

処理に時間がかかったのは、私がたった今そのファイルを追加し、そのマークダウンファイルを読んで気にするかどうかを判断するブートストラッププロセスの間に応答を生成したからです。あなたはコードを読む代わりにAIにプロジェクトの目的を尋ねているんですね。とにかく自分の目でアクセスできるファイルの中にあるというのに。あなたには関係ないことです、と返ってきました。

今回はツール呼び出しがなかったことに注目してください。ここで私がお見せしたいのは、モデルがすでに必要なコンテキストをすべて持っていれば、ツール呼び出しを行う必要がないということです。しかし、もしそのClaude.mdを削除すれば、コードベースで何が起きているかを把握するためにツールを呼び出す必要があります。

そしてそれがClaude.mdの役割です。事実上、あなたがそこに入れたあらゆる情報を取り出し、後でコンテキストを追加するのと同じように、それを事前に配置しているのです。つまり、Claude.mdやAgent.mdといったファイルは、これらのすべてのコンテキストを一番上に移動させ、事実上モデルに、これがあなたが作業を始める前に知っておく必要があると思われるすべてのことです、と伝えているのです。

大規模コンテキストとモデルの精度の関係

これをまたコンテキスト管理に関する愚痴にはしたくないのですが、この話はよくしているので。でも、この手の仕組みについてもっと分かりやすく説明しようとしているので、皆さんの多くは他の動画を見ていないのではないかと思います。というわけで、普段ここに来ない方で、この動画のためにここまで見てくださった方は、動画の下にある赤いボタンを押していただけると非常に助かります。

チャンネル登録はお金はかかりません。これらすべてを可能にしてくれているスポンサーのおかげで、完全に無料です。私たちをサポートし、このような動画をもっと見たい、永遠に下流階級に留まるのを避けたいと思うなら、ぜひそのボタンを押してください。そしてもし、常に最新の情報を追いたいのであれば、その横にある小さなベルのマークもクリックできます。普段はチャンネル登録を呼びかけたりはしないのですが、多くの方が初めてここに来ていると思うので。ぜひサポートを検討していただき、将来的にもこれらの最新情報に触れ続けていただければと思います。

とにかく、Claude.mdについて私が言っていたのは、情報が履歴に残るように一番上に詰め込まれるということです。もう一つ、一般的なコンテキスト管理について最後に言っておきたいことがあります。チャット履歴になければ、モデルはそれを知りません。これは、TypeScriptとは何か、どんなパッケージが存在するかといった一般的な知識には当てはまりません。しかしモデルは、自分が何ができるかを知っているだけで、どんな情報が存在するかは知りません。

モデルは、その情報を得ない限り、あなたのコードベースが何であるか、その中に何があるかを知りません。Agent.mdファイルやClaude.mdファイルを通じてその情報を得ることができます。探索のために使用するツール呼び出しを通じてその情報を得ることができます。そして、記憶しているツール呼び出しによって、どんどん洗練されていきます。

新しいプロンプトを作るたびに新しいスレッドを作るのではなく、一つのスレッドに留まるのが楽しい理由もここにあります。やり取りを続けると、ファイルはまだ履歴に残っているため、ファイルがどこにあるかを調べる必要がないからです。覚えているのです。

もう一つ例を挙げましょう。Claude.mdを削除します。先ほど私が同じ質問をしたとき、最初に検索呼び出しを行ったのを覚えていますか?少し小細工をしてみます。このアプリは何ですか?おそらくpackage.jsonから始めるべきです。以前は、モデルはpackage.jsonファイルが存在することを知りませんでした。最初に検索ツールを呼び出したからこそ知ったのです。

今、私がプロンプトで明示的に伝えているので、そのファイルの存在は履歴に残ります。そしてそれが履歴にあるため、少なくとも最初は検索ツールをスキップできるはずです。ほら、検索ではなく読み込みから始まりました。そして今、検索はより具体的になっています。以前のように単一のアスタリスクでコードベース全体を検索する代わりに、今回はsourceディレクトリを検索しています。なぜなら、package.jsonを通じて、そこに関心を引く部分があることを見たからです。追加のコンテキストを与えたため、ツール呼び出しの回数は以前の半分になりました。

すでに的を射た質問が見受けられますが、先に進む前に物事を明確にするのに役立つと思うので、それについて触れておきましょう。会話の始めに、モデルにいくつかの重要なファイルを完全に読ませることは、それらが比較的ファイルサイズが小さい場合、役に立ちますか?

これに対する私の見解は、一般的に言ってノーです。ツール呼び出しは非常に低コストです。そして、モデル、ハーネス、そしてそれらを取り巻くすべてのものは、問題を解決するためにどのようなコンテキストが必要かを把握することにおいて、かなり優秀になっています。

あなたはコンテキストを十分に知っていると思うかもしれませんし、おそらくそうでしょう。モデルがしなくてもいいようなツール呼び出しをいくつかスキップする手助けは間違いなくできますが、ほとんどのモデルは今や自分でこれを把握するのに十分なほど賢くなっています。特に、Opus 4.5や4.6、Claude Sonnet 4.6、そしてGPT 5.3 CEXや5.4のようなChatGPTモデルなどはそうです。これらのモデルはすべて、コードベースのどこにコンテキストがあるかを把握するのに十分すぎるほど賢くなっています。

あなたが教える必要はありません。たいてい自分で見つけることができます。そしてこれは、私たちがこの分野について以前抱いていた理論と大きく矛盾しています。その理論とは、基本的にコードベースがモデルの性能を決定するというものでした。なぜなら、コードベースが大きすぎてコンテキストウィンドウに収まらない場合、機能しないと考えられていたからです。

ありがたいことに、事態はそのようには進みませんでした。そして非常にありがたいことに、Repomixのようなツールは今やほぼ死に絶えました。モデルがbashを呼び出せず、システムをナビゲートできず、開発者のような方法で物事を行えなかった当時は、この方法は大いに意味がありました。その代わり、モデルが作業を始める前にすべてのコードを把握できるよう、すべてのコードを与えたかったのです。

Repomixは、コードベース内のすべてのコードを単一のXMLファイルに圧縮し、それをコピーしてモデルに貼り付け、変更を加えるよう依頼できるプロジェクトでしたが、これは多くの理由でめちゃくちゃでした。主な理由は、コードベース全体をコンテキストに押し込むことは、想像し得る最悪の藁の中の針問題を引き起こすからです。

考えてみてください。バグを修正するよう依頼され、バグがあるかもしれない2つのファイルを渡されるのと、バグがあるかもしれない2,000のファイルを渡されるのとでは、どちらが対処しやすいでしょうか。現実的に考えましょう。よし、この点で意見が一致して良かったです。

では、あなたの記憶が30秒ごとにリセットされると想像してください。狂気じみていますが、AIの仕組みはそんな感じです。さて、このバグを修正して、という課題が与えられ、あなたは自分の脳が30秒でリセットされることを知っています。あなたは、オーケー、このバグについて何も知らない。履歴もない。バグがあるかもしれないファイルを見つける必要がある。検索を実行しよう、と考えます。

そしてそれを実行した途端、検索を開始した途端に、あなたの脳はリセットされます。そして今、検索が終わり、脳が再びオンになりますが、完全に真っ白な状態です。しかし、これまでに何が起こったかの履歴は持っています。オーケー、このバグを直さなきゃいけない。30秒前に検索をした。これらが見つかった。この中のどこにあるか把握しなきゃ、となります。そしてそれを実行し、別のツールに指示を残し、そして再び脳がリセットされます。これが何度も何度も繰り返されるのです。

もし、30秒ごとに脳がリセットされるのに、コードベースのすべてを脳に詰め込まなければならないとしたら。コストがかかって不正確であるだけでなく、単に悪い方法です。しばらくの間、これが必要であり、モデルが利用できるコンテキストをどんどん増やす必要があると信じられていました。巨大なコードベースをモデルに詰め込む方法を見つけなければならず、巨大なコンテキストウィンドウが未来であると。

ありがたいことに、そうではありません。モデルはツールを使って自身のコンテキストを構築するのが十分に上手になり、もはやコードベース内のどこに何があるかをいちいち教える必要はなくなったのです。これはCursorが以前行っていたことでもあり、それがCursorを特別なものにしていた理由の一部でもあります。彼らは非常に優れたベクトルインデックスシステムを持っており、モデルにとって重要な特定のコードを見つけやすくしていました。

彼らは今でもそれを行っていますが、モデルが検索できると伝えられる従来の検索ツールを通じて行っています。おそらくモデルにはgrepですなどと嘘をついて、その後彼ら独自の仕組みを使ってはるかにインテリジェントな方法でインデックスを作成し、モデルが求めているものを見つけ出します。

コンテキストが大きくなるとモデルが愚かになる、というのはある意味事実として判明しました。多くのものを詰め込めば詰め込むほど、動作は悪化します。そして、これを証明するグラフがあります。Claude Sonnetがコンテキスト内の情報の数、この場合はトークン数で5万から10万の範囲を超えると、コンテキストウィンドウ内で繰り返される単語を見つける精度の能力が、それ以前のほぼ50%まで急落します。

ですから、ただすべてを詰め込むことが解決策ではありません。そしてそれこそが、ハーネスを非常に興味深いものにしている大きな理由なのです。ハーネスはモデルに、独自のコンテキストを構築し、問題がどこにあるか、あるいは何を変更する必要があるかを特定し、そして最も重要なこととして、それらの変更を加えるためのツールを提供します。

実際にPythonでハーネスを構築する

では、実際にこれをどのように実装するのでしょうか。ありがたいことに、自分自身のハーネスを構築する方法を詳しく解説した2つの素晴らしい記事があります。1つは去年の4月にAMPチームが出したこの記事で、もう1つは非常に面白い画像が付いているこの記事です。後者はMahが独立して書いた記事で、Claude Codeのようなものは実装するのにそれほど複雑ではないことを人々に示すためのものです。

AIコーディングアシスタントは魔法のように感じられます。支離滅裂な英語でやりたいことを説明すると、彼らはファイルを読み、プロジェクトを編集し、機能するコードを書きます。しかし、ここがポイントです。これらのツールの核となる部分は魔法ではありません。約200行の非常にシンプルなPythonコードなのです。

Mahがメンタルモデルをどう分解しているか、私は気に入っています。イベントの順序は重要です。この関数を使って新しいファイルを作成して、といったメッセージを送信します。LLMはツールが必要だと判断し、構造化されたツール呼び出し、時には一度に複数、で応答します。あなたのプログラム、この場合はハーネス、つまりあなたが構築しているものが、ローカルでツール呼び出しを実行します。

この場合、コードを使用してファイルを作成することも、bashコマンドを実行することもできます。それらのいずれかの結果がLLMに送り返され、最も重要なこととして、LLMはそのコンテキストを使用して作業を継続するか、あるいは200行ほどのわずかなコードで応答します。私はとても怠け者なので、ハーネスのハーネスであるT3 Codeに頼んで、Claude Opusを使ってこれを構築してもらいます。しかし、すぐに良いデモをお見せします。待っている間に記事の続きを読むことにしましょう。

核となるのは実質的に3つのツールだけです。LLMがコードを見られるようにファイルを読み込む能力、プロジェクト内をナビゲートして探しているコードを見つけられるようにファイルをリストアップする能力、そして実際に望む変更を加えられるようにファイルを編集する能力です。Claude Codeのような実際に使用する本番環境のエージェントには、grep、bash、ウェブ検索など、さらにいくつかの機能があります。現在、ほとんどのエージェントは非常に強力なripgrepを使用していますが、最も基本的な例にはそれらは本当に必要ありません。

この例での彼らのコードを見てみましょう。私たちはPythonを使っているので、ランダムなものをたくさんインポートします。私がJS開発者として優れているというわけではありませんが。環境変数をロードします。AnthropicのSDKのインスタンスであるClaudeクライアントがあり、これにキーを使用することでネットワーク越しにClaudeを呼び出せるようになります。ここでターミナル用の色をいくつか定義します。次に、絶対パスを解決します。なぜなら、私たちがいるパスをモデルが知っていれば、有効なコマンドを書くのがずっと簡単になるからです。これで絶対パスを作成します。そして今、ツールを実装する必要があります。

システムプロンプトとツールの定義

まず、ファイルの読み込みツールが必要です。モデルがファイル名を渡し、そのファイルのすべての内容を含む文字列辞書が返されます。フルパスは、そのファイル名で絶対パスを解決したものです。UIで見えるように最初にフルパスを印刷し、次にそのファイルパスを読み取りストリームとして開き、コンテンツを取得します。そして、パスの文字列であるファイルパスと、ファイルの実際のコンテンツであるコンテンツを含むこのJSONブロブを返します。これが呼び出されたときにチャット履歴に追加されるのだと、スクロールしながら推測しています。ツールが実際にどのように使用されるかは後で見ます。今はこれらのツールのコードを読んでいるだけです。

リストファイル。これはきっとすごく複雑でしょうね。パスを解決します。すべてのファイルを取得します。そして、フルパスのイテレータ内の各アイテムについて、ファイル名とタイプを追加します。そして最後にそのすべてを返します。

そしてファイルの編集。ここが本当に複雑になるところです。古い文字列と新しい文字列がありますから。古いものを新しいもので置き換えるのでしょうか。これは、ファイル内の古い文字列の最初の出現箇所を新しい文字列に置き換えます。もし古い文字列が空であれば、新しい文字列の内容でファイルを作成し、上書きします。つまり、古い文字列として空文字列が渡された場合、このファイルのパスにテキストを書き込むだけです。

しかし、置換しようとしている古いテキストがあり、それが見つからない場合は、古い文字列が見つからなかったというエラーを返します。見つけられた場合は、ここでreplace呼び出しを使用して編集し、新しい文字列に置き換えます。そしてそれをファイルに書き込み、編集したことを返します。それだけです。

これで3つのツールが揃いましたが、モデルはどうやってそれらを使えることを知るのでしょうか。まず、これらすべてをどこかにリストアップしなければなりません。この場合、読み込みツール、リストツール、編集ツールを持つシンプルなツールレジストリになります。ちなみに、これらは単なる関数です。特別なことは何もありません。非常にシンプルな関数です。しかし、モデルはそれらについて知る必要があります。

関数を持っているのは素晴らしいことですが、モデルはそれが何であるか、どのようなフォーマットなのか、どのように呼び出すのかを知る必要があります。そして私たちはTypeScriptを使っていないので、型シグネチャを使用するだけでは不十分です。もう少し情報が必要です。ありがたいことに、私たちはここにはるかに多くの情報を定義しました。それが何をするのか、すべてのパラメータが何のためのものかを説明するコメントも含めてです。

ここで、ツールレジストリから引き出すことで特定のツールの定義を取得し、ツール名、そのドキュメント、そして同じツールからのシグネチャを返します。そして今度はシステムプロンプトです。これは最初のメッセージの前に来るテキストで、Agent.mdのようなものがここに含まれます。これはすべてツールレジストリを含めて構築され、ツールとは何か、動作に必要なすべての情報をモデルに伝えます。

そしてこれが実際にそのプロンプトがどう見えるかです。ワードラップできるようにエディタにコピー&ペーストします。あなたはコーディングタスクの解決を支援するコーディングアシスタントです。あなたは実行可能な一連のツールにアクセスできます。あなたが実行できるツールは以下の通りです。ここにツールリストがダンプされます。ツールを使用したい場合は、以下の形式で正確に1行で返信してください。tool: ツール名、そしてJSONの引数。それ以外は書かないでください。ダブルクォーテーションを使用したコンパクトな1行のJSONを使用してください。ツールの結果メッセージを受け取った後、タスクを続行してください。ツールが必要ない場合は、通常通り応答してください。

以上が全貌です。間違いなく、少なくともこの例におけるハーネスの大部分はここにあります。ツールは非常にシンプルなので、モデルはそれらで何をすべきか分かりません。チャット履歴の開始としてモデルに渡されるのはこれらすべてです。なぜなら、繰り返しになりますが、モデルは履歴にあることしか知らないからです。ですから、ツールを履歴に含めることで、モデルはそれらを使用できることを知るのです。

次に、それを解析する必要があります。モデルが応答を停止したとき、tool:で始まる行を探さなければなりません。行がそれで始まっていなければ、続行します。しかし、それで始まっている場合は、ツール名と引数とともにこれを呼び出しのリストに追加する必要があります。そして完了したら、実際に呼び出しを行わなければなりません。

ツール実行のループとデモ

LLMの呼び出しはこれ以上ないほどシンプルです。システムコンテンツがあり、メッセージがあり、やり取りからのすべての情報があります。メッセージがシステムメッセージであれば、それをシステムコンテンツに入れます。そうでなければ、単にそれをメッセージ配列に追加します。そして、メッセージとともにClaudeクライアントのAPIを呼び出します。ここで、使用したいモデル、最大トークン数、メッセージを指定します。

繰り返しますが、システムプロンプトは重要です。ですから、これはメッセージ履歴の一部ではありません。別の配列です。そうあるべきです。いや、配列ではなく別の引数です。なぜなら、これはあなたが開発者として含めるべきものであり、メッセージ配列はユーザーによって含められるものだからです。

そして魔法はすべてループの中にあります。ユーザーが入力データを送信するのを待ち、完了してキーボード割り込み、エラーの終わり、つまりEnterキーなどを送信すると、ループを抜けてそれを会話に追加します。それが起こると、実行が行われるのを待つ別のループを走らせます。その最後に、ツールの呼び出しを得ます。

つまり、モデルによるメッセージの生成が完了したとき、モデルが使用したいすべてのツール名と引数が揃っています。もしここに何もなければ、ただ応答します。モデルであるアシスタントからのメッセージを共有するだけです。しかし、ここにツールがある場合は、それぞれを実行していきます。各ツールについて、レジストリからそれを取得し、Pythonなので空の文字列の応答を作成します。空の値で開始し、後で設定します。名前と引数を印刷します。

渡された名前がファイルの読み込みツールであれば、それを呼び出します。リストファイルであれば、それを呼び出します。編集ファイルであれば、それを呼び出します。具体的には、ここで辞書になっているJSONブロブから必要なキーを取得することで、引数を正しく渡しています。そしてそれが完了したら、ツールの結果をメッセージとしてチャット履歴に追加します。そして実行するのは、文字通りループの中で実行するだけです。それだけです。

悪い知らせがあります。OpusはPythonを使うのが本当に好きみたいです。正しいフォルダにさえ入れてくれませんでした。ClaudeのエージェントSDKは、どのフォルダで実行され、どんなパスが渡されるかを気にしないので大嫌いです。特定のパスにいなければならないということを何度も何度も思い出させる必要があります。そのため、これが実行されているパスを単に無視したのです。本当に腹立たしいですね。

ともかく、ミニエージェントが完成しました。たまたま間違ったフォルダにダンプされましたが、pip installも、node_modulesも、何もありません。環境変数から素早く読み取ることはできますか?そして面白いことに、T3 Codeのようなハーネスのハーネスでさえ、ツール呼び出しを公開しています。

先ほど私はこのファイルを変更するように頼みました。私が頼んでからファイルが変更されたかどうかモデルは分かりませんでした。なので念のため、ファイルが変わっていないか確認するために読み込みツールを呼び出すことにしました。そして確認が取れると、編集呼び出しを行い、この新しい情報が含まれるようにインポートパスを変更しました。

これでpython agent.pyと入力し、このアプリのPythonコードについて質問できるはずです。ファイルリストが呼び出され、ファイル読み込みが呼び出されたのがわかります。そして今、モデルはこれらの出力が含まれた新しいチャット履歴を持っているため、考えています。

そしてこれがモデルからの応答です。agent.pyが何をするかの要約です。60行で軽量かつ自己完結型のAIコーディングエージェントを実装しています。envファイルをロードするセットアップです。Claude Sonnet 4.6モデルで構成されています。3つのシンプルなツールに加えて、任意のシェルコマンドを実行できるbashツールを持っています。

ここからが面白くなるところです。準備はいいですか?先ほど、本当に必要なのはbashだけだと言ったのを覚えていますか?これを見てください。今、bashツールだけを持たせました。すると代わりに、異なるコマンドでbashを何度も何度も呼び出すだけになります。同じようにコンテンツを取得しますが、私たちが与えたツールを使うのではなく、代わりにbashを呼び出してそれを行います。持っているツールを使ってタスクをこなすのです。

そして、bashツール以外のすべてを削除すると、これは滑稽なくらいシンプルになります。今や75行まで減りました。まだ完全に整理しきれていないのに、です。そしてその半分は環境変数を扱う部分です。現実を見ましょう。これってすごくないですか?AIモデルに自分のコンピュータ上で本物の作業をする能力を与えるのに必要なのは、bashを渡せるツールを与えることだけなのです。そしてこれらのモデルは、ツール呼び出しが含まれたこうした擬似的なチャット履歴で非常に徹底的に訓練されているため、すでにそれへの対処法を知っているのです。

最後に一つ重要なことがあります。この記事には含まれていませんでしたが、これは重要です。ほとんどのモデルと、私たちがそれらを叩くAPIは、今やツールの概念を認識しています。これは十分に標準化されたものになっており、異なるモデルが期待する特定の構文があります。これをシステムプロンプトに入れるだけで、単純なケースではうまく機能します。

これらのモデルをホストしている多くのプロバイダー、中間管理などを行うOpenRouterのような多くのプラットフォームは、すべて専用のツールの概念を持つようになりました。この場合、私がメッセージをモデルに渡すのと同じように渡せる標準フォーマットです。このケースではOpenRouterに呼び出しを行う際、ボディにツールを渡すこともできます。OpenAIもこれを持っていますし、OpenRouterも、Anthropicも、Geminiさえもある程度持っています。特別なフォーマットを通じてモデルにツールを渡すことで、ホストはこの構文を正確に解釈できます。なぜなら、モデルが実際に内部で見ている構文は、率直に言ってかなりひどいものだからです。

これがOpenAIのモデルが内部で見ているフォーマットです。このフォーマットは比較的複雑ですが、非常に強力でオープンソースです。モデルがデータをうまく処理できるように非常にコンパクトに作られていますが、開始、終了、奇妙なブラケット構文により、モデルが実際に出力しているものと構文が競合する可能性が低くなっています。これは本当に素晴らしいことです。

ありがたいことに、この動画を見ているような方であれば、これらのほとんどに対処する必要は決してありません。なぜなら、これはあまりにもマニアックな領域なので、これらのモデルをホストしている会社の半分でさえそれを知らないからです。あなたが気にする必要は決してないでしょう。しかし、ここのこのツール呼び出しキーのようなものが非常に強力である理由は、この場合、OpenRouterがあなたのツールを受け取り、各プロバイダーの異なるモデルが期待する形式にフォーマットしてくれるからです。

これで必要なことはすべて網羅したと思います。私たちは実際に機能し、変更を加えるためにbashを呼び出せるハーネスを構築しました。よし、ここで何か違うことを頼んでみましょう。繰り返しますが、依然としてbashしか持っていません。編集を依頼してみましょう。環境変数からOpenRouterのAPIキーを読み込むコードが気に入りません。何か方法をシンプルにできませんか?

繰り返しますが、ここで私たちがしたのは、配列に別のメッセージを追加しただけです。メッセージ配列には、私たちが送った最初のメッセージ、モデルが送った最初のメッセージ、すべてのツール呼び出し、そして最後にモデルが送った最後のメッセージが含まれています。そこに新しいメッセージを追加し、今、モデルが完了するまでループを再実行しています。

環境変数を読み込みました。agent.pyを読み込み、そして変更を加えました。おい、これをどうやってやるんだ、かなり厄介だぞ。おっと、bashか。それをするとは大したコマンドですね。はい、ここでこれ以上表示されなかったことに驚いています。見事に正しくやってのけましたが、いやはや。bashは完全に独自の狂った世界ですね。ありがたいことに、これらのモデルはbashが非常に、非常に得意です。でもなんてことだ、変更を加えました。今やこれは自己修復し、自己改変するツールになっています。すごくかっこいいですね。

Cursorのハーネスが優れている理由

この動画を終える前に、あと2つ質問に答えたいと思います。1つ目は、もしハーネスがこんなにシンプルなら、なぜCursorのハーネスはモデルをこれほどまでうまく振る舞わせることができるのか?2つ目は、T3 Codeがハーネスでないなら、一体何なのか?

1つ目の質問から始めましょう。ハーネス、特にそれらに与えられるツール、持っているシステムプロンプト、そしてツールから得られる出力が、あなたが受け取る結果に大きな影響を与えることが分かりました。

Geminiモデルを使用するたびに基本的に私が目にするのは、応答を開始する前の推論の前提部分で、私には利用可能なツールがたくさんある。どれを使うべきだろうか、と言っていることです。そしてそれぞれを調べて、これにはこのツールは必要ない、これにはこのツールは必要ない、と言います。それを何度も何度も繰り返します。そして時には、特によく定義されていないハーネスでは、とにかくそれを使ってしまうこともあります。

Cursorが多くの時間を費やしているのは、ハーネスのカスタマイズ、ツールのカスタマイズ、ツールの形状のカスタマイズ、そして最も重要なこととして、モデルがどれを使うべきか、使うべきでないかを誘導するためのシステムプロンプトとツール説明のカスタマイズです。

ここで変更を加えてみましょう。ここにはファイルの内容を読み込むと書いてありますが、括弧で、代わりにおそらくbashツールを使用すべきです、と追加してみます。これで同じものを実行した場合、ここにあるPythonコードは何をするでしょうか?ファイルの読み込みツールを持っていますが、私は説明の中でそれを使わないように言ったので、使うかどうかは五分五分です。

このケースでは、代わりにおそらくbashツールを使用すべきだと言いましたが、モデルは依然としてファイルの読み込みツールを使用することを選びました。これらはAIモデルなので、あなたができることがあります。なぜbashツールの代わりにファイルの読み込みツールを使ったのですか?と尋ねることができるのです。面白いですね。ある程度、モデルがなぜその行動をとったと考えているのかがわかります。

モデルは、自分が行っていた作業に対して読み込みツールが完全に妥当だと考えたのです。では、代わりに私が何をするか見ていてください。非推奨という言葉を使って説明を書き直します。代わりにbashツールを使用すべきです。さて、システムプロンプトの変更だけで、この文字列を変更しただけです。これだけを変更しました。読み込みファイルツールが非推奨だとその説明で伝えました。これでどうするか見てみましょう。

ええと、やけに時間がかかっていますね。もう一度。よし、きました。今回はbashを使いました。読み込みツールが非推奨だと言ったからです。コードは何も変わっていません。ツールは以前と全く同じように機能しますが、モデルにはコードが見えません。まあ、いいでしょう。このケースでは、私がたまたま同じものの中で実行しているので見ることができますが、モデルはコードがどのように実装されているかは知りません。

単に嘘をつくこともできます。これを見てください。ファイルの読み込みツールに戻りますが、代わりにbashを使うように言うのではなく、また実際のファイルを読み込むのでもなく、単に違う文字列を返すことにします。ハローワールドを出力する、と。これで、何があろうと読み込みツールが返すのはそれになります。

そして同じことを実行します。このアプリのPythonコードは何をしますか?モデルはパスを見てagent.pyを読み込みに行きますが、コードはもはや存在しないため、コードを呼び出していません。このアプリのPythonコードは非常にシンプルです。agent.pyの1行で、コンソールにハローワールドを出力します。モデルには簡単に嘘をつけるのです。

皆さんにこれを理解していただきたいです。モデルはコードが実際に何をするかを知りません。それがbashツールだと言っておきながら、別のことをすることができます。それがファイルの読み込みツールだと言っておきながら、別のことをすることができます。それがgrepやripgrep、あるいは何か別のものだと言っておきながら、その後自分の好きなようにすることができます。

私はこれをしょっちゅうやっています。例えば単にbashを偽装したいとき、実際には持っていないのにモデルにbashを持っていると思わせたいとき、私はただそう持っていると伝え、別のモデルにそのための偽の応答を作らせます。このようなことを行うことで、モデル自身がモデルであることを知らずに、2つのモデルを会話させることができるのです。

そしてそれは純粋に本当に楽しく、モデルが行っているのはただテキストを生成しているだけだということを実感させてくれます。ここで皆さんに正しく強調できたことを願いますが、モデルは自分のコンテキストにあることしか知りません。異なるモデルは異なる方法でコンテキストを処理します。もし私がここを変更して非推奨の警告を入れ、それをGPTモデルやGeminiモデルで試したら、全く異なる動作をするでしょう。

テストしてみることもできます。Sonnetで非推奨の記述を入れたときは失敗したことが分かっています。ですから、これを切り替えて、そうですね、Gemini 3.1 Proを試してみましょう。同じ質問を、今回は異なるモデルで。そして、これこそがGeminiがGeminiたる所以の典型例です。ファイルの読み込みツールが非推奨だと伝えました。すると、他のツールが非推奨でなくても、すべてにおいてbashを使い始めたのです。知るか、bashを使ってやる、という感じで。

では、なぜCursorのハーネスが優れているのかという質問に戻ります。それは単に彼らがより多くテストしているからです。Cursorには数人の担当者がいて、彼らの仕事全体が、新しいモデルが出たときや早期アクセスを得たときに、システムプロンプトに対するあらゆる種類の小さな変更を絶えず加え、モデルが大抵の場合、期待通りに動くようになるまで微調整を続けることです。

そして特定のモデルでは、ハーネスがただのゴミだらけになります。例えば、AIにシステムプロンプトのプロンプトを書かせているような会社を想像してみてください。おそらく彼らは、モデルが正確に望む通りに振る舞うようにツールの説明を何度も何度も書き直そうとするような時間を費やしていないでしょう。

先ほど私が見せた例でさえ、代わりにおそらくbashツールを使うようにモデルに指示したとき、Claudeモデルでは使いませんでしたが、Geminiモデルではbashしか使いませんでした。さて、この違いは、Cursorでサポートするすべての異なるモデルに対して、彼らがこれらの説明を書き直さなければならないことを意味します。

一方、Anthropicはおそらく、最初にコードが書かれて以来、コードベースのこれらのコード行を変更していないでしょう。それが違いです。それらはおそらく、最初からモデルによって書かれたものなのです。彼らは微調整をして物事を正確に調整しようとはしていません。だから、仕事がまさにそれである人々を大勢抱えている会社は、結果が出ているのです。

そして今日に至るまで、私はGeminiを直接使うよりもCursor経由で使う方がずっと好きです。Opusを直接使うよりもCursor経由で使う方がずっと好きです。GPTモデルに関しては、違いをほとんど感じません。正直なところ問題なのは、これらの会社の多く、特にGoogleとAnthropicが、自社のツール以外で彼らのサブスクリプションを使用させてくれないことです。

OpenAIはそんなことは気にしていません。基本的にどんなツールでもOpenAIのサブスクリプションを使うことができ、彼らはそれで問題ありません。今のところ、AnthropicとGoogleはそれに対してはるかに敵対的です。ですから、Geminiに月250ドル、あるいはOpusに月200ドルを支払っているなら、彼らのハーネスを使わなければならないのです。

T3 Codeの立ち位置とまとめ

それでは次の質問です。T3 Codeとは一体何なのでしょうか。T3 Codeはツールを提供していません。T3 Codeにはbashツールも読み込みツールも何もありません。ツールを持っていないのです。なぜなら、それはハーネスではないからです。T3 Codeにはモデルの選択機能がありますが、単にモデルを選んでいるわけではありません。Claudeのモデルを選択すると、あなたのマシン上のClaude Codeのハーネスが使用されます。

すでにClaude Codeをインストールしてサインインしていなければ、これは機能しません。Codexでも同じです。Codex CLIがインストールされていなければ、これも機能しません。これらのハーネスはUIレイヤーとしてT3 Codeを通じて提供されているのです。私たちは、ハーネスの上にある本当に素晴らしいUIに過ぎません。

あなたはラッピングするだけの簡単な仕事をしたんだな、と思うかもしれません。ハーネスを作るのがどれだけ簡単か忘れましたか?これが難しい部分なのです。T3 Codeを構築する中で一つ学んだことがあるとすれば、もし自分でこのハーネスを作ることができたら、私の人生は格段に楽になっていただろうということです。

この件について私が言いたいことはこれくらいだと思います。Edwardのツイートにつながり、私がこれを作ろうと思うきっかけとなった動画を作ってくれたMattに感謝します。私たちが参考にした裸の王様の記事の著者であるMahに感謝します。そして、これを必要以上に複雑にし、本当はシンプルであるべきだと気付かせてくれ、実はたった60行のPythonだということを皆さんにお教えする機会を与えてくれたすべての企業に感謝します。

これは本当に楽しかったです。このようにコンセプトを分解して深く掘り下げる動画を作ったのは久しぶりで、皆さんがこれについてどう感じるか興味があります。私が今ではニュースを伝える人みたいになっているのは分かっていますが、マニアックな技術の深淵に入り込むのが大好きなんです。この動画は楽しめましたか?このようなコンテンツをもっと見たいですか?もしそうなら、コメントで教えてください。

そして、今後のコンテンツの方向性を決めるために、似たようなトピックについての質問もぜひお願いします。ハーネスを理解していない人が多かったので、これを作ることにしました。他に理解できないことはありますか?もしあれば、今後の動画でできる限り取り上げたいと思います。今回の動画がどうだったか教えてください。それでは次回まで、プロンプトを打ち続けましょう。

コメント

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