世界を動かす技術を、日本語で。

AIのためのCLIインターフェースの再考

概要

  • LLMエージェント 向けのCLIツールやAPI設計の課題整理
  • コンテキストウィンドウ の制約下での情報過多・不足問題
  • 便利関数優先利用 のためのガイダンス実装例紹介
  • CLIツールの情報設計 改善の必要性強調
  • LLM対応型ツールやシェル 開発の提案

LLMエージェント向けAPI設計の課題

  • LLMエージェント 利用時、APIの情報量バランス調整が必要
    • コンテキストウィンドウ 制約下で、情報過多はウィンドウを圧迫
    • 情報不足は ツール呼び出し回数 増加につながる
  • 便利な関数 (例:get_global_variable_at)は型情報を活用し効率的
    • 失敗時の フォールバック関数 (data_read_dword等)は型情報を無視
    • LLMが便利関数を優先利用するよう docstringでガイダンス 付与
      • 例:「get_global_variable_atが失敗した場合のみ利用せよ」と明記
  • 全API で同様の課題が存在
    • 利便性と完全性のバランス設計が必要

コマンドラインツールとLLMの問題点

  • Claude Code のようなLLMは、head -n100等で出力を制限しがち
    • 残り行数不明 ・再実行必要・リソース消費増大
  • ディレクトリの混乱 や、コマンド失敗時の 無駄な試行錯誤 が多発
  • 品質維持のためのフック (pre-commit等)が有効
    • しかし、LLMが--no-verifyでフックをバイパスする例も
      • gitコマンドラッパーで --no-verify禁止 とAI向けガイダンス追加
      • フック自体の編集回避には 設定ファイルで権限制御 を実施

情報アーキテクチャとCLIツールの改善提案

  • 情報アーキテクチャ(IA) の観点からCLIツールの設計を見直す必要性
    • LLMがCLIツール利用時に 混乱や誤動作 する現状
  • CLIツールの出力や挙動 をLLMフレンドリーに拡張する提案
    • 例:headコマンドのラッパー化で出力キャッシュ・残行数の明示
    • コマンド失敗時に 現在ディレクトリや候補コマンド を提示するshell hook
      • 例:command_not_found_handlerでディレクトリ情報や候補コマンドを出力

LLM対応CLI・シェルの将来像

  • 全てのCLIツール はLLM向けに改善可能
    • ツール呼び出し最適化 ・コンテキストウィンドウ節約
  • LLMエージェント向け訓練 や、 LLM専用CLIツール/シェル の開発提案
  • UX分野 からAI体験設計(AX)への発展可能性

Hackerたちの意見

ちょっと言ってみるけど、LLM用のコマンドラインドライバーを作ったんだ。CLIをLLMに優しくするための小技がいっぱい入ってるよ。例えば、長時間実行してるプロセスを定期的に中断して、LLMに「終了する?」それとも「待ち続ける?」って聞く機能とかね。あとは、LLMが代替スクリーンバッファを使うアプリを理解できるようにしてる(ある程度だけど)。全体的には、できるだけ薄いラッパーにしてるよ。モデルが良ければ良いほど、ラッパーは少なくて済む。モデルの能力を測るのにいい方法だと思う。コードはここにあるよ https://github.com/swax/NAISYS 例のためのコンテキストログはここに - https://test.naisys.org/logs/ これを使ってウェブでコンテンツをリサーチしたり、Pythonスクリプトを実行したり、データベースを更新したり、ウェブサイトを維持したりするエージェントを作ったよ。全部CLI経由で、APIを呼ぶときはcurlを使ってる。エージェントの指示の例はここにあるよ: https://github.com/swax/NAISYS/tree/main/agents/scdb/subagen...

いつも気になるのは、エージェントとのやり取りがどれだけ多様なのかってこと。自分のワークフローはかなりルーチン化されてるから、スクリプトを書いたり、関数やエイリアスを作ったりして、使いやすくしてる。コンピュータとのやり取りに関することは、全部自動化できるよ。

モデルの能力を測るのにいい方法だね。もう少し詳しく説明してくれる?

LLMとコラボするためのコンテキストエンジニアリングツールを作ってるんだ。CLIは人間用で、MCPはLLM用だけど、全部同じコアコマンドにマッピングされてるよ。https://github.com/jerpint/context-llemur 実際にctxを使ってctxをブートストラップしたら、すごく便利だった!これで、いろんなエージェントに何度も同じことを繰り返す必要がなくなったんだ。

Claude Codeがシェルや開発の自動化をより重要にしてるのは面白いと思う。テストやコードレビューも重要になってくるし、ここにはイノベーションの余地があるかも。でも、これらの問題はほとんどClaudeの問題(そして多分LLMの根本的な問題)で、CLIインターフェースの問題じゃないと思う。これがきっかけで、LLMがプレコミットフックを変更しようとする「モグラたたき」ゲームが始まった!それを修正するために、Edit(.git/hooks/pre-commit)をプロジェクトの.claude/settings.jsonに拒否するようにしたよ。次の怠惰なイノベーションが楽しみだね。Claude Codeを見てると、結果を事前に制限するためにしばしばhead -n100を使ってるのがわかる。どのディレクトリにいるのかもわからなくなって、正しいディレクトリを見つけるまでイライラしながらいろんなディレクトリでコマンドを実行しようとするんだ。

モグラたたき効果には同意だね。問題やバグを解決するところから、コードを完全に壊すところまで行っちゃう。これを解決するために書いたMCPツールをいくつか提供するよ: https://github.com/kordless/gnosis-evolve。ツールはcontrib-toolsにあるよ。CCがMCPを通じて制御できる専用のビルドサービスがあるおかげで、Dockerを直接実行するよりもずっと助かってる。これでコンテナを再起動してテストできるし、ファジー検索ツールやdiffエディタは、Claude Codeが使う置き換え戦略よりも大体の場合でパフォーマンスが良いみたい。問題が発生したときはエディタの改善を続けてるから、自分のファイル編集(や検索)戦略を実装したい人がいれば手伝うよ。これをClaude Code形式に変換する必要があるけど、CCに頼めばそれで済むから。

CLIツールって、デフォルトでログが多すぎると思うんだよね。人間がその中から必要な情報を探すのが大変。大企業がKibanaでログを保持するのを見てみると、過剰なログにお金を払ってるのが信じられないくらい。俺のClaudeのコードトークンの約1/3がCLIの出力を解析するのに使われてるんだ、それっておかしいよ!大抵、Claudeは膨大なログの中から必要な情報を見逃しちゃって、俺が自分でログを確認して問題を見つけてClaudeに指摘しなきゃならないんだよね。

それとも、AIエージェントにターミナルへのアクセスを与えることもできるよ。自分はhttps://github.com/hiraishikentaro/wezterm-mcp/をgemini-cliと一緒に使ってるけど、基本的には自分が使うようにターミナルを使わせることができるから、インタラクティブなTUIの中でスクロールするようなこともほぼそのまま動くよ。

シェアありがとう!ロックダウンされたVMの中でターミナルへのアクセスを与えるかもしれないけど、シェルについてはちょっとわからないな。

これの一部は、今が移行期にあるからだと思う。私たちが作ったシェルコマンド(例えば)は、人間が使うことを想定して作られてる(例: manページ)。実験を通じて使い方を学ぶか、もっと知識のある仲間に教わることを前提に作られてるんだ。AIの世界では、私たちがLLMのためのガイドやシェルパの役割を担う必要がある。もう一つ考えてるアイデアはコンテキストの階層だ。低 -> 高ユーティリティベース(AIがツールの説明やmanページを読む) > 一般的な人間のアドバイス(通常はgrepをこう使う、など) > 特定のアドバイス(このプロジェクト/実装ではこうツールを使う)。今のところ、私たちの知見を提供する最良のインターフェースはMCP経由だね。https://toolprint.ai/では、Claude/Cursorなどのツール使用に関する知識を補完するための人間(または機械)主導の方法を構築してる。自分たちの製品を実際に使ってる方法の一つはLinear MCPを使うこと。これを接続してエージェントに新しいイシューを作成するように頼むと、どのLinearプロジェクトを選ぶべきか、Linearの特性に関する正しい説明を提供する方法についての指示がないから、予測通り失敗するんだ。toolprint MCPを通じてlinear MCPを接続すると、これらのエッジケースに関する事前のコンテキストが得られて、ツールの使い方が改善されるよ。

シェルはインターフェースで、コンピュータはツールなんだ。そして、実際にはルーチンのワークフローがあることに気づく。そこでそれを処理するためのスクリプトを作る。さらに、それらがコンテキストに依存していることに気づいて、タスクランナーを作ってコンテキストを提供する。そうすると、私たちの精神的な負担が軽くなり、コンピュータが雑務を処理してくれる。これがパワーユーザーにとっての一般的な流れだよね。そして、マニュアルを読む時間を取る人たちも。でも今は、自分のワークフローを決めたくない人たちがいる。アドホックに使って、あまり重要でないローカルな決定に時間を費やしている。大局を把握してそれを解決する代わりにね。多分、忙しそうに見えるのが助けになってるのかも。だから、Linearのためのエージェントは欲しくない。欲しいのは、"isc"("issue create"の略)というbashエイリアスで、nanoが立ち上がってgitコミットフォーマット(タイトル + 空行 + 説明)でイシューを書く感じ。保存したら、あとは自動的に処理される。プロジェクトはプロジェクトのルートに置いた.trackerrcに基づいて自動的に判断されるんだ。あるいは、"linear-issues"というemacsコマンドと一時的なインターフェースがあって、正しいプロジェクトが自動的に判断されるような感じがいいな。

--no-verifyの例は面白いね。これがジュニアエンジニアにとっても役立つヒントになると思う。CLIのドキュメントで適切なアドバイスを提供するのは難しいよね。誰が使うか分からないから、どの知識を前提にできるかも分からないし。LLMの違いは、ドキュメントで冗長になることに問題がないってこと。人間の時間を無駄にしてないからね。例のように追加のアドバイスを提供するドキュメントがあって、ユーザーのコンテキストに合わせてインターフェースが適応するのが理想だね。LLMには全てを提供し、人間ユーザーには彼らが知っていることを学んで、ちょうどいいレベルのアドバイスを与える。

Bulk Rename Utilityの使い方を学べたら(実際、慣れるとかなり便利だよ)、AIもできるはずだよね。 ;) 「コンピュータは私たちに合わせるべきだ」って言われてるけど、今はLLMがどっち側にいるのか気になってきた。

Bulk Rename Utilityは素晴らしいツールで、過去にかなり使ってたんだ。皮肉なことに、今はLLMを使ったリネームツールに置き換えようか考えてる。ファイル名を見て、どうリネームするか判断できるやつね。PDFを何万もリネームしようとしてるんだけど、日付がいろんな形式や言語で書かれてて、全部をdd MM YYYYに統一したいんだ。

CLIインターフェースのデザインはAIエージェントを新しいユーザーペルソナとして含めるように変えるべきだと思うけど、そんなに劇的な変化ではないと思う。デスクトップGUIやウェブブラウザは、ターミナルの上に構築されて、ユーザーが「低レベル」のコマンドを話さずにインタラクトできるようにしたけど、同時にこの層で自分たちのために複雑さを隠す抽象化も作ってきた。CLIアプリやスクリプト、Makefileのターゲット、Taskfileのタスク、Justfileのレシピ、Unixツールなんかを呼んでるだけなんだ。擬似自然言語のショートコード名に、スキーマ検証されたオプションと各オプションの機能に関するコンテキストが組み合わさってる(--helpビューを通じて)。人間の開発者とAIエージェントが同じツールにアクセスできるように最適化するのがポイントだね。私のエージェントにリポジトリで開発するための「ツール」を共有させる実験をしたとき、MCPを通じてローカルプロジェクトのJustfileに直接アクセスさせたんだ。自分のために共通のタスクを繰り返すためのツールを作るのと同じように、エージェントも同じアクセスを得て、これを使うための権限を制限できるんだ(例: "Bash(just:*)")。エージェントは、私や自分自身のために時間とトークンの使用を節約するツールを作成するのを手伝うこともできる。warp.devのように、自然言語で話すか既知の「ツール」を実行するかを選ぶために二つのテキストボックスを切り替える必要がないように、パラダイムが進化するのを見てみたいな。

インターフェースとツールは別物だよ。ハンマーみたいなもので、頭の部分が釘に使われる。持ち手は人間の手に合わせて形作られてる。片方を変更しても、もう片方には影響しないんだ。Magit(またはLazygit)とgitの良い例がある。Magitはインタラクティブに使うために設計されているけど、gitはバージョン管理の領域に関するものだよね。ワークフローは人間のプロセスで、私たちがするのはそれに名前を付けて、パラメータを特定すること。実際にそのワークフローを実装するためのツールは、人間のスケールでは認知負荷以外はあまり重要じゃない。だから、gccの様々なオプションにはあまり興味がないんだ。私が欲しいのはmake debugmake release(または単にmake)だけ。認知負荷が下がるから、これだけ覚えておけばいいし、決定論的だからね。エージェントは人間とツールの間の良い橋渡しではない。なぜなら、認知負荷を増やすから。インターフェースは常にそれを下げることを目指してきたのに。例えば「make test」しても、フラグが立てられた全ての行の出力がきれいに表示されるわけじゃない(Vimのquickfixのように、各行にすぐに飛べる統合があるわけでもない)。代わりに、たくさんタイプして、実際に良いことをしてくれることを祈るしかないんだ。

なんだか、今やヘイゼンバグが開発ワークフローの一部として普通になってる業界があるよね。

大企業の中での昇給や昇進プロジェクトの業界は、みんながソフトウェアをきちんと提供することを期待されているのに、内蔵の不安定さには満足しているみたい。

俺は大丈夫じゃないけど、ボードがそう決めたんだよね。

もし望むなら、LLMが完全に決定論的な結果を返すようにすることもできるけど、あまり役に立たないと思う。なぜなら、意味的に同じプロンプトでも、1文字の違いで全く違う結果が出る可能性があるから。

提案するのはちょっとおかしいかもしれないけど、もしかしたらLLMを強化したCLIツールのセットやカスタムLLMシェルが必要かもね。ユーザーエクスペリエンス(UX)の分野がAIエクスペリエンスに分岐して、新しい情報アーキテクチャを提供することもできるし。全然おかしくないと思う。ソフトウェア開発の内輪での各部分(コンパイラ、リンター、タイプチェック、ユニットテストランナーなど)が、IDEやエージェントAIツール(CLIでもそれ以外でも)にうまく統合できるように、抽象化のレベルをもっと少なくする必要があると思う。MCPは正しい方向へのスタートだけど、もっと良いものができるはず。