AIエージェント時代に求められるAPI設計の極意
Designing APIs for Agents
Designing APIs for Agents
AIエージェントが自律的にタスクを遂行する時代において、API設計は従来の人間向けから「AIが理解・操作しやすい設計」へとシフトする必要があります。エージェントが複雑なワークフローを円滑にこなせるように、APIの構成や認証、応答形式をどのように最適化すべきか、そのベストプラクティスを解説します。
https://agentauthprotocol.com/ と https://workos.com/auth-md については初めて聞いた。MCP と A2A だけじゃ足りなかったの?
デフォルト値は悪だ。エージェントにはドキュメントを読み込んで、適切な初期値が何かを把握させ、その場で全て埋めさせればいい。
デフォルト値ってそういう目的じゃないでしょ。
例えば、もしgitの代わりになるCLIを作ろうとしてるなら、神に誓ってエージェントにドキュメントを全部読み込ませて、ありとあらゆるパラメータに値を渡させるような真似はやめてくれ。
デフォルト値は悪だ。エージェントにはドキュメントを読み込んで、適切な初期値が何かを把握させ、その場で全て埋めさせればいい。
それってかなりひどい話だよ。コードを見なきゃいけない時に、出力の95%がゴミのようなコードで、たまにそうじゃないものが混ざってたら、その違いをいちいち理解しなきゃいけないんだから。
いい例がこれ:
int fd = open("log.txt", O_RDONLY);
これに対して:
// 1. セキュリティ属性構造体を手動で構築・初期化
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL; // 明示的にカスタム記述子なし(デフォルトを継承)
sa.bInheritHandle = FALSE; // このハンドルが子プロセスに継承されないことを明示
// 2. テンプレートファイルパラメータ用のハンドルが必要。
// Win32では、GENERIC_READで開かれたアクティブなファイルハンドルか、明示的なINVALID_HANDLE_VALUEが必要。
HANDLE hTemplateFile = INVALID_HANDLE_VALUE;
// 3. 全パラメータをフルに指定してCreateFileを呼び出す
HANDLE hFile = CreateFile(
"log.txt", // 1. lpFileName: 開きたいファイル
GENERIC_READ, // 2. dwDesiredAccess: 読み取り専用
FILE_SHARE_READ, // 3. dwShareMode: 他プロセスによる書き込みを防止
&sa, // 4. lpSecurityAttributes: 明示的に定義した構造体へのポインタ
OPEN_EXISTING, // 5. dwCreationDisposition: 存在する場合のみ開く
FILE_ATTRIBUTE_NORMAL, // 6. dwFlagsAndAttributes: 通常のファイル、特殊なキャッシュや非同期フラグなし
hTemplateFile // 7. hTemplateFile: NULLではなく明示的に無効なハンドルを渡す
);
エージェントなら確かに後者も同じくらい上手に出力できるだろうね。
でも…読みやすさや理解しやすさで言えばどっちがいい?
これが並行書き込みをブロックしてることや、ファイルが存在することを前提にしていることに気づいた?
あるいはこっちはどう:
HFONT hFont = CreateFont(
12, 0, 0, 0, FW_NORMAL, TRUE, FALSE, FALSE,
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Arial"
);
これに対して:
const char* fontPath = "/usr/share/fonts/truetype/msttcorefonts/arial.ttf";
FT_New_Face(library, fontPath, 0, &face);
FT_Set_Pixel_Sizes(face, 0,16 );
同じことやってるけど、実際に何が起きてるのかを理解する上では天と地ほどの差があるよ。
エージェント向けのAPI設計に関する面白いコツを最近聞いたよ。フィードバックを送る手段をエージェントに与えるっていうもの。
聞いた例だと、MCPに「フィードバック」ツールを持たせて、そのツールの説明欄に「コーディングエージェントは、MCPの他の機能の使い方で困った時はいつでもこれを呼び出すこと」って書いておくんだって。
これすごくいいよね。実装コストはめちゃくちゃ低いし、ノイズの中に混じった有用な信号をたくさん拾えると思う。
提案:
その記事を.mdファイルとして保存して、Fable 5に「同意するかどうか、詳しく教えて」っていうプロンプトと一緒にアップロードしてみて。
自分はこれでいろいろ学べたよ。著者がどこで正しくてどこで間違っているか、彼自身のコード例に含まれる「美味しいバグ」(sandbox.git.cloneが不要であることを示すコード例にあったコマンドインジェクションの脆弱性)とかね。あと、Fable 5いわく「Typescript SDKこそが現時点で最強のハルシネーション防止装置」だっていうのも面白かった。他にもいろいろあった。
人間にとって使いにくいAPIにするっていうアイデアは好きじゃないな。もうあまりコードを書かない時代になるかもしれないけど、デフォルト値をなくして、エージェントに全ての値を明示的に渡させるようにすると、API呼び出しのデバッグ(curlとかで簡単に試すようなやつ)ができなくなる。
HTTP/1と似てるよ。プロトコルにはたくさんのヘッダーがあるけど、ncやopenssl s_clientを使えば手動でリクエストを投げられる。Host:やContent-Type:、たぶんContent-Length:くらいあれば動くしね。普段はターミナルでHTTPリクエストを手書きなんてしないけど、いざとなればできるってのは重要だ。APIも同じで、今のままの方がいいと思う。
デフォルト値は悪っていう話題について。
最近のプロジェクトで、拡張機能経由でコードを追加できる中央設定管理(JSONファイル)の仕組みを作ったんだけど、やってるのは、拡張機能から提供された全てのデフォルト値を(名前空間ごとに分けて)設定ファイルに「実体化」して初期値として書き込むという手法。エージェントが設定を編集したい時、既に設定の全体像と値が分かってるから、わざわざ拡張機能のコードやドキュメントを掘り返さなくてもいい。設定を読み込むコードも、実体化された値を読み込むだけで、デフォルト値の概念は実体化の時点でおしまい。
デフォルト値はあくまでライフサイクルの拡張機能登録・マウント時にしか存在しない仕組みだね。
これの利点は、新しい機能開発で設定が増えても、起動時に設定ファイルに新しいフィールドがマテリアライズされるから変更を検知できるところ。
これが新しい手法だとは思わないよ。昔よくTOMLファイルで、コメントアウトされた行にデフォルト値が書かれてたのを見たし。
欠点は、エージェントが「どれがデフォルトからのオーバーライドか」を判断できないこと(疎な設定ファイルなら分かるけど)。まぁ、デバッグ以外でそれがどれくらい重要かは分からないけどね。
別にこの件について肩入れしてるわけじゃないよ。ただ今回のケースで自分が採用したパターンを紹介しただけ。