Alternatives for the EDIT tool of LLM agents

Salvatore Sanfilippo

LLMエージェントのEDITツールの代替案

追記:もちろんこれは昔からすでにやられていたことだ!薄々そうだろうとは思っていたが、Twitterでみんなが教えてくれた :) でも、ぜひ最後まで読んでほしい。最後に出てくるCRC32を使った折衷案は面白いトレードオフだし、そもそも議論しておく価値のあるテーマだ。

今、DS4プロジェクト用のエージェントを作っている。ローカル推論はトークンが貧しく、最適化がものをいう戦場だ。今みんなが使っているEDITツールが、LLMに古いテキストを一字一句そのまま出力させていることにかなり驚いた。このCAS(check and set)方式、つまり EDIT old="foo" new="bar" のように指定する動作は、衝突する編集が頻繁に起こるため(ユーザーが同時に編集していたり、別のブランチに切り替えていたりする)や、LLMがある行の内容を幻覚(ハルシネーション)してしまうことがあるために必要とされている。

つまり、単純に行番号だけで「22行目を new="foobar" に変えて」と指定するのは非常に脆いということだ。一方で、毎回古いテキストを書き直すのにローカルLLMの貴重なトークンを浪費したくはない。古いテキストには特殊文字や空白が多く、モデルが間違えやすいこともあるからだ。その場合はツールが失敗し、LLMは同じ編集をやり直すことになる。そこで私は(再)設計した。CAS方式は維持しつつ、トークン効率の良いタグベースのEDITツールだ。

READやSEARCHツールは、こんな感じの結果を返す:

10:Q8fA int count = 10;
11:rA3_ if (count > limit) {
12:Kq9z     count = limit;
13:PX0b }

行番号とタグが付いている。タグは4文字で、平均するとLLMの2.5トークンほどだ。行のチェックサムを表している。LLMはこんなふうに編集できる:

{
  "tool": "edit",
  "path": "/tmp/example.c",
  "line": 10,
  "tag": "Q8fA",
  "new": "int count = 11;"
}

複数行の場合は、こんな感じだ:

{
  "tool": "edit",
  "path": "/tmp/example.c",
  "lines": "11:rA3_\n12:Kq9z\n13:PX0b",
  "new": "if (count > limit)\n    return limit;"
}

トークンの節約効果は、エージェントが大量のテキストを削除するときに特に大きいが、一般的なケースでも十分意味がある。ただ、行番号とタグを付ける分、オーバーヘッドもある。トレードオフの余地は色々ある。タグを8文字にして行番号もハッシュに含めるべきかもしれないし、衝突の可能性やトークン化を正確に検証して、どれだけ得なのかを見極める必要もある。ただ、私は line:tag という形式が気に入っている。後でLLMが行番号の情報をいろいろな形で活用できるからだ。たとえば後続のツール呼び出しで範囲を指定するときなどだ。タグについても、他にも活用方法があるかもしれない。「この行はまだ dj4_ のままか?」といった具合に。

面白いのは、DeepSeek v4 Flashがこのツールを非常にうまく使いこなせることだ。どうやらこのモデルにとっては自然な形式らしい。正確な節約量は計測していないが、現場では編集がはるかに速く、しかも信頼性も上がったと感じている。

これに対する代替案は、毎回ファイル全体のCRC32だけを返す方法だ(つまり、部分的に読み込んだ場合でもタグはファイル単位のタグになる)。そうすれば行番号+CRCだけで済むので、編集時は 11,12,13,14 と指定するだけでいい。もちろんトークンはさらに少なくなる。ただ、その代わりに毎回ファイル全体のCRC32を再計算する必要がある。とはいえ、そこそこのサイズのファイルなら十分安い処理で済む。しかしこの方式には限界もある。無関係な箇所に変更があっただけでも編集が失敗してしまうので、大きなトレードオフがある。とはいえファイル全体方式にも利点はあって、10:23 のように範囲指定ができるのは大きなメリットだ。

どちらが優れているかは、ds4-agentを2つの方式で何セッションも使って、十分な実践的な証拠が集まってからでないと判断できない気がしている。当面は、編集モードを切り替えられるコマンドラインオプションを用意するのが、まず取るべき一歩だろう。

原文は Salvatore Sanfilippo により に公開されました。

この記事は「muse-spark-1.2-contributor」を使用して翻訳されました。