llm --tool CodeInterpreter 'いまのPythonとSQLiteのバージョンを教えて' と打つと、Pythonが動いてバージョンが返ってくる。ただし、そのPythonはあなたのマシンでは動いていない。OpenAIのサーバー上でコードが実行され、結果だけが手元に届く。
8月4日に出た LLM 0.32 は、この「ツールがどこで動くのか」という設計を正面から扱ったリリースだ。作者のSimon Willison自身が「プロジェクト開始以来もっとも大きな更新」と書いている。地味なCLIのバージョンアップに見えて、エージェントの実行モデルに関わる変化が入っている。
そもそもLLM(このCLI)とは何か
混乱しやすいのだが、ここで言う llm は「大規模言語モデル」の略称ではなく、Simon Willisonが作っている同名のコマンドラインツール兼Pythonライブラリのことだ。OpenAI、Anthropic、ローカルモデルなど数百のモデルを、同じ llm 'プロンプト' という文法で叩ける薄いラッパーで、応答をSQLiteに全部記録してくれるのが特徴になっている。データ処理系のエンジニアの間で定番になっているツール、という位置づけで捉えるとわかりやすい。
これまでの llm でも「ツール」は使えた。ただしそれは、モデルが「この関数を呼びたい」と要求したら、llm があなたのマシン上でそのPython関数を実行し、結果をモデルに返す、という仕組みだった。モデルは考える、道具を振るうのは手元、という分担である。
クライアント側ツールとサーバー側ツールの違い 🔧
0.32で加わったのが「サーバー側ツール(server-side tools)」だ。名前のとおり、道具を振るう場所がモデルプロバイダ側に移る。
冒頭の CodeInterpreter がまさにそれで、コードを実行する砂場(サンドボックス)はOpenAIのインフラ側にある。手元のPython環境も、ネットワークの口も一切使わない。同様に WebSearch を渡せば、モデルは検索を自前のサーバーで済ませて要約だけ返してくる。
呼び出しは既存のツールと同じ -T(--tool の短縮形)フラグで、名前を渡すだけだ。
# OpenAIのサーバー側でコードを実行
llm -T CodeInterpreter '1から100までの素数を数えて'
# サンドボックスにメモリ上限を渡す
llm -T 'CodeInterpreter(memory_limit="4g")' '大きめのCSVを集計して'
プロバイダごとに使える道具が違うのが実務上の注意点で、そこは表で押さえておきたい。
| プロバイダ | サーバー側ツール |
|---|---|
| OpenAI |
WebSearch, CodeInterpreter
|
| Anthropic(llm-anthropicプラグイン) |
WebSearch, WebFetch, CodeExecution, AnthropicMCP
|
面白いのはAnthropic側の AnthropicMCP で、外部のMCPサーバーをモデルの実行環境から直接叩かせられる。作者は自分のブログのデータベースを例に出している。
llm -m claude-sonnet-5 \
-T 'AnthropicMCP("https://datasette.simonwillison.net/-/mcp")' \
'how many rows in the blog_blogmark table?'
どのモデルがどのサーバー側ツールに対応しているかは llm tools -m モデル名 で確認できる。
この分担の移動には利点と注意が両面ある。利点は、砂場や検索基盤の運用をプロバイダに丸投げできること。ローカルにDockerを立てたりネットワークの権限を管理したりせずに、コード実行やWeb検索がエージェントに生える。一方で、コードの実行がプロバイダ側のブラックボックスに入るので、何がどう動いたかを手元で完全に監査するのは難しくなる。どこまで信頼して任せるか、というトレードオフを自分で切る必要がある。私見だが、外部データに触るWeb検索はサーバー側で握らせ、社内固有のロジックは従来どおりクライアント側のツールで持つ、という使い分けが現実的だと思う。
エージェントのループがログを膨張させる問題 🗄️
もう一つ、地味だが効いてくるのがログの作り直しだ。
サーバー側ツールを使うと、モデルは「考える→検索する→また考える」とツール呼び出しをまたいで推論を続ける。これはOpenAIの Responses API を既定で使うようになったことと対になっていて、推論(reasoning)とツール呼び出しが1つのチェーンの中で交互に走る。
その代償がログである。エージェント的なやり取りは、毎ターンごとにそれまでの全メッセージ列をAPIに送り直す。素直に記録すると、同じ会話履歴のJSONが何度も重複して保存されてしまう。作者はこう書いている。
If we're going to support the pattern where the message sequence is appended to on every request, ideally we can avoid logging all of that duplicate JSON for every turn.
0.32はこれをGitに着想を得た方式で解いた。メッセージをハッシュで内容アドレス化(content-addressable)し、同一内容のメッセージは一度しか保存しない。SQLiteのスキーマもスレッド・ターン・メッセージという単位に組み直されている。Gitが同じ中身のファイルを1つのオブジェクトとして共有するのと同じ発想で、長時間動くエージェントのログが会話の伸びに比例して膨れ上がるのを止める、という狙いだ。利用には sqlite-utils の4.0以上が必要になる。
すぐ効く小さな改善:推論トレースの分離
派手さはないが日常で効くのが、推論トレースの出力先だ。推論モデルの「考えている途中」は標準エラー出力(stderr)に流れ、最終的な答えだけが標準出力(stdout)に出る。つまり llm ... | jq のようにパイプで後段に渡す中身は答えだけに保たれ、思考の途中経過が混ざらない。パイプラインを組む人間には地味にありがたい設計で、思考を見たくないときは -R(--hide-reasoning)で止められる。
Pythonライブラリ側も構造化メッセージに作り替えられ、model.prompt(messages=[llm.user("Hello")]) のように型付きのメッセージを渡せる。response.stream_events() で推論・テキスト・ツール呼び出しが混ざったストリームを扱え、ツール実装が llm.PauseChain を投げれば、承認を挟むために実行チェーンを一時停止して後から再開することもできる。人間の承認をワークフローに差し込みたいときの土台になる。
なお既定モデルは新しい gpt-5.6-luna に変わり、gpt-5.6-sol と gpt-5.6-terra も追加された。
何が持ち帰れるか
llm を普段使っていなくても、このリリースが示す方向は押さえておく価値がある。エージェントの道具は「手元で動かすもの」から「プロバイダが持つもの」へと二層化しつつあり、そのぶんログや監査の設計が新しい課題になる。0.32はその両方に手を入れた実装例として読める。まず試すなら、pip install -U llm で上げて、llm -T WebSearch '最近のニュースを3つ' を叩き、そのやり取りが llm logs にどう積まれるかを覗いてみるのが早い。道具がどこで動いているかを意識する、いい入り口になる。
出典は以下のとおり。