15
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Manus元リードが語る「function callingをやめた理由」— 2年のエージェント開発から得た実践知見

15
Posted at

はじめに

AIエージェント開発において、function callingは「当たり前」の選択肢とされてきた。OpenAIが2023年にAPIとして提供して以来、ほぼすべてのエージェントフレームワークがfunction callingを前提に設計されている。

しかし、話題のAIエージェントManusの元バックエンドリードは、Redditの投稿で「2年のエージェント開発を経て、function callingを完全にやめた」と語った。411 upvotes、123コメントという大きな反響を呼んだこの投稿は、エージェント開発の常識に疑問を投げかけている。

本記事では、function callingの限界、代替アプローチとしてのコード生成、そしてClaude Codeの内部設計から得られる知見を整理する。

function callingの3つの限界

1. スキーマ管理の複雑性

function callingでは、各ツールをJSONスキーマで定義する。ツールが5個程度であれば問題ないが、実務のエージェントではツール数が数十個に膨らむ。すべてのツール定義はコンテキストウィンドウに含まれるため、ツールが増えるほどモデルが使える文脈が減る。

Manusの実績では、平均50回のツール呼び出しが1タスクに必要だった。50種類のツール定義をすべてコンテキストに載せると、それだけで数千トークンを消費する。

2. 表現力の制限

function callingの構造は本質的に「1回の呼び出しで1つのアクション」である。以下のような処理を表現しようとすると、すぐに限界にぶつかる。

  • ループ処理(10個のファイルに同じ変換を適用)
  • 条件分岐(レスポンスコードに応じて異なる処理を実行)
  • エラーリカバリ(失敗時に代替手段を試行)
  • 複数ステップの組み合わせ(検索→フィルタ→加工→保存)

これらはすべて、プログラミング言語であれば数行で書ける処理である。function callingでは、モデルとの往復を何度も繰り返す必要がある。

3. プロバイダ間の非互換性

OpenAI、Anthropic、Google、Mistralはそれぞれ異なるfunction calling仕様を持つ。ツール定義のフォーマット、レスポンスの構造、エラーハンドリングの方式が微妙に異なる。

マルチモデル戦略を採用するエージェント(Manusはまさにこれに該当する)では、この非互換性が開発・保守コストを押し上げる。

代替アプローチ: コード生成への転換

Manusが採用したのは、CodeActと呼ばれるパラダイムである。ICML 2024で発表されたこのアプローチは、エージェントのアクションをPythonコード(またはBashコマンド)の生成・実行で行う。

CodeActの基本的な考え方

従来のfunction calling方式:

{
  "function": "search",
  "arguments": {"query": "Python async patterns"}
}

CodeAct方式:

results = search("Python async patterns")
filtered = [r for r in results if r.relevance > 0.8]
for item in filtered:
    content = fetch_page(item.url)
    save_to_file(f"research/{item.title}.md", content)

後者では、検索→フィルタ→取得→保存を1回のアクションで完了できる。function callingでは4回以上の往復が必要になる処理である。

定量的な優位性

Apple Machine Learning Researchの論文によると、CodeActはfunction callingと比較して最大20%の成功率向上を示した。特に、複数ステップの組み合わせが必要なタスクでの差が顕著である。

CodeActの課題

万能ではない。以下のトレードオフが存在する。

  • コードパースエラー: 2.4%の確率で構文エラーが発生し、連鎖的な失敗を引き起こすことがある
  • モデル依存性: LLMのコーディング能力に直接依存する。能力の低いモデルでは効果が薄い
  • サンドボックスの必要性: 任意のコードを実行するため、セキュリティの隔離が必須

Manusのアーキテクチャ — 実践的な設計パターン

Manusの公式ブログで公開されたアーキテクチャから、実務に応用可能なパターンを抽出する。

レイヤードアクションスペース

Manusはfunction callingを完全に排除したわけではない。正確には、function callingの範囲を極限まで絞り、残りをコード生成に委ねた設計である。

レイヤー 内容 ツール数
Function Calling層 Bash実行、ファイル操作、コード実行などのアトミック操作 20未満
サンドボックス層 実際のタスク処理。VM内でBashやMCPツール経由で実行 無制限

ツール定義の肥大化を防ぐために、機能をファイルシステムに格納し、必要に応じてサンドボックス内で呼び出す設計を採用している。

KVキャッシュの最適化

Manusの実データでは、入出力トークン比が100:1に達する。つまり、出力1トークンに対して100トークンの入力がある。この環境では、KVキャッシュのヒット率がコストに直結する。

トークン種別 コスト
キャッシュトークン $0.30/MTok
非キャッシュトークン $3.00/MTok

10倍のコスト差があるため、システムプロンプトやツール定義の先頭部分を安定させ、キャッシュヒット率を最大化する設計が極めて重要になる。ツールの動的な追加・削除はキャッシュを破壊するため、代わりにデコーディング時のlogitマスキングで特定のツールを無効化する手法を採用している。

ファイルシステムを「究極のコンテキスト」として活用

コンテキストウィンドウの制約を超えるため、Manusはファイルシステムを外部メモリとして活用する。

  • todo.md: タスクの進捗管理。更新し続けることで、モデルの注意(アテンション)を目標に集中させる
  • 中間結果のファイル保存: 大量データをコンテキストに載せず、ファイルに保存してからコードで処理する
  • エラー情報の保持: 失敗したアクションとスタックトレースをコンテキストに残し、同じ失敗を繰り返さない

マルチエージェント構成

エージェント 役割
Planner タスク分解と割り当て
Knowledge Manager 情報の取捨選択とファイルシステムへの保存判断
Executor 割り当てられたタスクの実行(分離コンテキスト)

当初はPlannerを分離していなかったが、todo.mdの更新にアクション全体の約1/3を費やしていたことが判明し、専用エージェントとして切り出した。

Claude Codeの内部設計から学ぶ

Claude Codeのリバースエンジニアリング分析からは、Manusとは異なるアプローチの設計判断が見えてくる。

TAORループ

Claude Codeのオーケストレーションは驚くほどシンプルである。

  1. Think: モデルが次のアクションを決定
  2. Act: ツールを実行
  3. Observe: 出力をキャプチャ
  4. Repeat: 完了シグナルが出るまで繰り返し

このループを実装するコードはわずか約50行。すべての知能はモデル自身とシステムプロンプトに存在する。「オーケストレーターは薄く、モデルに任せる」という設計哲学である。

4つのプリミティブツール

Claude Codeが公開するツールは、100以上の専用統合ではなく、わずか4種類のプリミティブに絞られている。

ツール 機能
Read ファイル読み取り、grepパターン
Write ファイル作成・変更
Execute Bashコマンド実行
Connect MCP経由の外部サービス接続

この設計の背景にある考え方は、「100の専用ツールを定義するより、4つの汎用ツールを組み合わせたほうが、モデルは柔軟に問題を解決できる」というものである。Manusの「アトミック操作を20未満に絞る」設計と思想的に共通している。

RAGを排除した検索設計

Claude CodeはRAG(Retrieval-Augmented Generation)を採用していない。類似度検索、リランカー、チャンキング戦略——これらを使わず、代わりにripgrepjqfindコマンドとLLM駆動の検索ロジックを組み合わせている。

理由は「隠れた失敗モード」の排除である。RAGのパイプラインは、チャンキングの粒度、エンベディングの品質、リランキングの閾値など、多くのパラメータが絡み合い、失敗時の原因特定が困難になる。コマンドラインツールであれば、失敗は明示的で再現可能である。

Function Calling vs Code Generation — 6軸の比較

評価軸 Function Calling Code Generation
表現力 1アクション=1ツール ループ・条件分岐・関数定義が可能
コンテキスト効率 ツール数に比例して消費 プリミティブのみ定義すれば良い
エラーハンドリング フレームワーク依存 コード内でtry-exceptが書ける
プロバイダ互換性 各社仕様が異なる Python/Bashは共通
型安全性 JSONスキーマで保証 パースエラーのリスクあり(2.4%)
学習コスト フレームワーク固有の知識が必要 プログラミングの一般知識で対応可能

どちらが「正解」というわけではない。小規模で固定的なツールセットならfunction callingが適切であり、複雑で動的なタスクにはコード生成が適している。

実際、ManusもClaude Codeもハイブリッド構成を採用している。基本的なインターフェースにはfunction calling的な構造を残しつつ、実際の作業はコード生成で行う。違いは「どこに境界線を引くか」である。

まとめ — エージェント設計の方向性

Manusの元リードの決断から読み取れるのは、「フレームワークの抽象化に頼りすぎない」という教訓である。

function callingは、ツールの数が少なく、タスクが単純な場合には優れた選択肢である。しかし、エージェントが扱うタスクの複雑性が増すにつれて、以下の方向への移行が見られる。

  1. ツール定義は最小限に: Manusは20未満、Claude Codeは4つのプリミティブ
  2. コード生成でアクションを表現: 条件分岐・ループ・エラーハンドリングはコードのほうが自然
  3. ファイルシステムを外部メモリとして活用: コンテキストウィンドウの制約を回避
  4. オーケストレーターは薄く: Claude Codeのループはわずか50行。知能はモデルに任せる
  5. KVキャッシュを意識した設計: プロダクション環境では10倍のコスト差がある

LLMの能力が向上するにつれて、「ハードコードされたロジック」は減り、「モデルに委ねる範囲」が広がる。Manusの公式ブログでも「ハーネス(制御構造)は時間とともに縮小すべき」と述べられている。

function callingを使うか使わないかという二項対立ではなく、「どこまでをフレームワークで制御し、どこからをモデルに委ねるか」——この境界線の設計こそが、エージェント開発の核心である。

参考

15
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?