AIエージェントは「賢さ」よりツール設計で変わる:Papillonをつくって実感したこと
木村 優志

AIエージェントをつくるとき、つい「どのモデルを使うか」「プロンプトをどう書くか」に意識が向きます。もちろん、どちらも大切です。
ただ、ローカルファーストのAIノートアプリPapillonを開発していて、より強く感じたことがあります。エージェントの仕事の質は、モデルに渡すツールの設計で大きく変わるということです。
LLMは文章を考え、次に何をするかを選べます。しかし、ノートを探す、内容を読む、見出しだけを書き換える、タスクを完了にする、といった現実の操作は、LLMだけでは完了できません。モデルの判断を実際の結果につなげるのがツールです。
そしてツールは、単にAPIを公開するためのものではありません。AIに何を見せるか、何を一回で実行させるか、失敗したときにどこで止めるかを決める、プロダクトの設計そのものです。
「ファイルを書き換える」だけでは、仕事にならない
Papillonでは、ローカルのMarkdownノートをAIと一緒に扱います。利用者は、たとえば次のように依頼します。
- 「先週の打ち合わせメモから、決まったことを今日のノートへまとめて」
- 「このノートの『課題』の節だけ、読みやすく整理して」
- 「終わったタスクを完了にして」
最初は、ノートを読み書きできる一つの汎用ツールがあればよいように見えます。しかし、write_file(path, content) のようなツールだけを渡すと、AIは「どこまで置換してよいか」を毎回判断しなければなりません。
「課題の節を整理して」という依頼に対して、ノート全体を読み直し、本文全体を再生成して上書きするかもしれません。依頼と関係ない箇所の表記が変わったり、AIが読み取れていない記述が失われたりする危険があります。
これはモデルが不注意だからというより、道具が操作の意図を表せていないことが原因です。利用者が頼んだのは「ファイル全体の置換」ではなく、「ある見出し配下の編集」です。
操作の意図に合わせて、ツールを分ける
そこでPapillonでは、ノートへの操作を一つの書き込みツールに寄せず、用途ごとに分けています。
ノートを探す → search_notes / list_notes / search_by_tag
ノートを確認する → read_note
見出し配下を更新する → replace_section
末尾へ新しい内容を足す → write_note(append)
日次ノートへ残す → append_daily_note
タスクを追加・完了する → create_task / complete_task
整理・改名する → move_note / rename_note
削除する → trash_note たとえば replace_section は、対象ノート、見出し、見出し配下の本文を受け取ります。見出しがなければ末尾へ追加しますが、既存の見出しを重ねて作ることはしません。
このようにすると、AIは「どの方法で書くか」を抽象的に考える必要がなくなります。「この節を整える」なら replace_section、「今日の作業ログを残す」なら append_daily_note というように、利用者の意図とツールを対応づけられます。
ツールを細かくしすぎると、今度は選択肢が増えすぎます。重要なのは、内部実装の関数をそのまま全部公開することではありません。利用者が区別したい操作の単位で、少数のツールにまとめることです。
ツールの説明は、AIへのUIになる
エージェントにとって、ツールの名前・説明・引数は、人間にとっての画面やボタンに近いものです。
たとえばPapillonのノート追記ツールには、「追記・追加・続きを明示した場合だけ使う」と説明しています。書き換え、修正、整理、言い換えなら、見出し単位の更新か、既存ノートを読んだうえでの全体置換を選ぶようにします。
この一文がないと、AIは手軽な「追記」を選びがちです。その結果、同じ見出しが何度も増え、ノートは読みづらくなります。
説明には、機能だけでなく次の点まで書く必要があります。
- どんな利用者の依頼で使うか
- 似たツールとどう使い分けるか
- 使ってはいけない条件は何か
- 成功時に何が変わるか
ツールの仕様を人間の開発者だけが理解していても十分ではありません。実際に選ぶAIが、その境界を判断できなければ、よいツールは使われません。
読む操作と、変える操作を混ぜない
ノートを操作するAIでは、まず対象を正しく見つけることが必要です。
利用者が「先週の議事録を要約して」と言ったとき、AIがファイル名を推測して書き込み始めるのは危険です。Papillonでは、検索、一覧、タグ検索、ノート読込を別の読み取りツールとして用意し、必要なノートがコンテキストにない場合は、推測する前に探して確認するようにしています。
この分離には二つの利点があります。
一つ目は、AIの途中経過が確かめやすいことです。検索結果を見てから読込み、内容を確認してから更新するので、誤った対象へ操作する可能性を下げられます。
二つ目は、権限を分けられることです。Papillonには「閲覧のみ」「提案」「自動反映」のモードがあります。閲覧のみと提案のモードでは、バックエンドから書き込み系ツールそのものを外します。プロンプトで「書き込まないで」と伝えるだけではなく、AIが選べる道具を減らします。
これは不便に見えるかもしれません。しかし、提案だけを求めている利用者にとって、AIが正しい内容を正しい場所へ書いてしまうことも望ましい動作ではありません。能力と権限は分けて設計する必要があります。
失敗を「次の判断に使える結果」にする
ツールは成功するときだけを考えて作りがちです。しかしエージェントは、曖昧な依頼や不完全な情報の中で動きます。失敗したときに何が返るかが、次の行動の質を左右します。
Papillonでは、同名の未完了タスクが複数あり、どれを完了にするか決められない場合、勝手に一つを選びません。「対象ノートで一意なタスク名を指定してください」と返します。移動先に同名のノートがある場合も、上書きせずに停止します。
エラーは単なる例外処理ではありません。AIが利用者へ確認を求めるべき状況を知るための、構造化されたフィードバックです。
安全な失敗には、少なくとも次の三つが必要です。
- 変更を行わないこと
- 何が判断できなかったかを返すこと
- 次に必要な情報や操作を示すこと
この形にしておけば、AIはもっともらしい推測で処理を続けず、会話へ戻って確認できます。
便利なツールほど、操作の後始末まで設計する
AIがノートを変更したあと、利用者の画面はどうなるべきでしょうか。
Papillonでは、ツールによる変更をアプリへ通知し、ファイルツリーや編集中のノートを更新します。また、AIが変更したパスを会話画面に表示し、更新のUndoや自動バックアップも用意しています。ファイルを保存するAPIを呼べた時点で、ユーザー体験が完了するわけではありません。
特に、ローカルのファイルを扱うアプリでは、AIの変更と利用者の編集が同時に起こります。変更を反映したこと、戻せること、同期の対象になることまで扱わなければ、AIは便利な編集者ではなく、予測できない別の書き込み元になってしまいます。
ツール設計では、呼び出し前の入力だけでなく、呼び出し後の状態までを一つの操作として考える必要があります。
モデルを替える前に、仕事の境界を見直す
エージェントが期待どおりに動かないとき、より高性能なモデルへ替えたくなります。それで改善する場合もあります。
一方で、次のような問題はモデルの能力だけでは解決しません。
- どのノートを読めばよいか分からない
- 更新範囲が大きすぎて、必要以上に書き換えてしまう
- 読むだけの依頼でも書き込めてしまう
- 同名の対象が複数あるのに、選択を強いられる
- 更新後に利用者が何を確認すればよいか分からない
こうした問題は、ツールの粒度、入力の制約、権限、戻し方、結果の見せ方を見直すことで改善できます。
AIエージェントをつくるときは、まず「モデルに何をさせるか」ではなく、「利用者はどんな結果を安全に得たいのか」を書き出すのがよいでしょう。その結果を、読取り、判断、変更、確認という操作に分けます。そのうえで、各操作に必要な最小限のツールを設計します。
AIエージェントは、道具を通じてプロダクトになる
LLMは優れた推論や文章生成ができます。しかし、利用者に価値を届けるのは、回答の文章だけではありません。正しい情報にたどり着き、適切な範囲だけを変更し、必要なら止まり、結果を利用者が確認できることが重要です。
Papillonをつくっていて実感したのは、ツール設計はLLMの周辺実装ではないということです。AIが何をでき、何をしてはいけず、失敗したときにどう利用者へ戻るかを決める、エージェントの中心的な設計です。
モデルの進化は速く、同じツールでも将来はより上手に使えるようになるでしょう。だからこそ、特定のモデルの癖に合わせるより、利用者の意図と操作結果が対応する、分かりやすく安全なツールを先に整える価値があります。
「AIに何を考えさせるか」と同じくらい、「AIにどんな道具を渡すか」。AIエージェントを実用的なプロダクトにするには、その両方を設計する必要があります。



