PaPoo
cover
technews
Author
technews
世界の技術ニュースをリアルタイムでキャッチし、日本語でわかりやすく発信。AI・半導体・スタートアップから規制動向まで、グローバルテックシーンの「今」をお届けします。

Genkit MiddlewareでAIパイプラインを賢く守る・伸ばす

記事のキーポイント

Genkit Middlewareって何がうれしいの?

DZoneの記事は、Genkit の新しい middleware システムをかなり実践寄りに紹介しています。ざっくり言うと、​LLM に何かをお願いする前後に、横から処理を差し込める仕組みです。

これ、普通のWeb開発でいう Express や Koa の middleware に近い考え方です。たとえばHTTPなら「認証チェックを先に入れる」「ログを取る」「エラー時にリトライする」みたいなことを middleware でやりますよね。Genkit ではそれを AIの generate() パイプライン に対してやるわけです。

ここが地味に重要で、AIアプリって見た目以上に「毎回同じ面倒」が多いんです。

こういう処理を、毎回 ai.generate() の外側で手作りしていると、コードがどんどん散らかります。記事でもそこを問題視していて、​middleware として共通化できるようにしたのが新しいポイントだと説明しています。これはかなり筋がいい設計だと思います。

middleware は3つの層に分かれている

Genkit の面白いところは、ただ1か所を横取りするのではなく、​3つの段階に分けて介入できることです。

1. model

これは、​実際にモデルへ送る呼び出しを包む層です。

向いている用途はたとえば:

つまり、「モデルが返す前後で何かしたい」ならここです。

2. tool

これは、​モデルが呼び出す tool の実行を包む層です。

tool というのは、AIが外部機能を使うための仕組みです。たとえばファイル操作、DB参照、メール送信などを指します。

向いている用途:

3. generate

これは、​prompting、tool calling、output parsing を含む全体の生成ループを包みます。

つまり、いちばん上位の流れです。
ここでは「生成の前にツールを注入する」「システム指示を加える」といった、より広い制御がしやすいです。

この3層構造、個人的にはかなり好きです。
全部を1つのフックに押し込まないのがよい。AIアプリって、後から要件が増えると一気にごちゃつくので、​役割分担が最初から分かれているのは強いです。

使い方は use: [] で明示的に指定する

Genkit では middleware をグローバルに勝手適用するのではなく、​呼び出しごとに use: [] で指定します。

これはかなり健全です。
理由はシンプルで、AIの挙動が見えにくくなる一番の原因は「どこで何が入っているか分からないこと」だからです。

たとえばこんな感じです。

const response = await ai.generate({
  model: googleAI.model('gemini-flash-latest'),
  prompt: 'Hello',
  use: [retry({ maxRetries: 3 }), loggerMiddleware({ verbose: true })],
});

このスタイルなら、​その呼び出しにどんな補助処理が入っているかが一目で分かる
私はこの「明示的に足す」設計、かなり好感触です。便利さの代わりに魔法っぽくなるフレームワークも多いですが、AIまわりはブラックボックスが増えやすいので、なおさら明示性が大事だと思います。

公式 middleware は @genkit-ai/middleware

記事によると、公式 middleware は @genkit-ai/middleware パッケージとして提供されています。

インストールはこんな感じです。

npm install @genkit-ai/middleware
# or
pnpm add @genkit-ai/middleware

もちろん、Genkit 本体とモデルプロバイダープラグインも別途必要です。
たとえば Google 系のモデルを使うなら @genkit-ai/google-genai のようなプラグインを組み合わせます。

この「中核と周辺を分ける」構成も、長く使う上ではありがたいです。必要なものだけ足せるので、巨大な依存関係に巻き込まれにくいですからね。

公式 built-in middleware の中身

記事では、Genkit チームが用意した built-in middleware が紹介されています。ここが実用的で、単なる仕組み説明ではなく「すぐ役立つ道具」が並んでいるのがいいところです。

filesystem: サンドボックス付きでファイル操作を許す

filesystem middleware は、モデルに以下のような file manipulation tools を追加します。

しかも、​指定した root directory の中だけに制限できます。
つまり、モデルに自由にPCを触らせるのではなく、「この箱の中だけね」という形で扱えるわけです。

オプションとしては:

これはかなり「コードを書くAIエージェント」の土台になります。
ファイル操作 tool を毎回手作りしなくてよくなるのは、普通に助かるはずです。しかも、path validation まで面倒を見なくて済むのはかなり大きい。

skills: Markdown の「知識ファイル」を読み込む

skills middleware は、SKILL.md のような Markdown ファイルをスキャンして、​関連する内容を system prompt に注入します。さらに、モデルが必要に応じて use_skill tool を呼び出せます。

要するに、​ファイルベースの知識レイヤーです。
私はこれ、かなり現実的な発想だと思いました。

AI に全部を長い system prompt で詰め込むと、あとで修正しづらいし、内容も散らかりがちです。
でも skills のように、Markdown ファイルとして分けておけば、チームで管理しやすい。人間にも読める。しかも必要時にだけ出せる。

image_0003.svg

「system prompt soup」を避ける、という表現が記事にありましたが、まさにその通りだと思います。プロンプトを鍋みたいに全部混ぜる運用、たしかに気持ちよくないんですよね。

toolApproval: 人間の承認を挟む

toolApproval middleware は、AI が自由に使える tool を制限し、​許可されていない操作を止める仕組みです。

許可されていない tool を使おうとすると ToolInterruptError が発生し、そこで一旦止まります。
その後、人間が確認して承認し、再開できます。

記事の例では、ファイルを書き込む tool を使う場面で、まず停止し、ユーザーの承認後に restartTool() で再開しています。

これは本当に大事です。
たとえば次のような行為は、AIに勝手にやらせると困ります。

要するに、​現実世界に影響を与える操作には、人間の確認を挟むということです。
この発想をフレームワークが標準機能として持っているのは、かなり心強いです。

retry: transient error に強くなる

retry middleware は、モデル呼び出しが一時的に失敗したときに再試行します。
対象となる status はたとえば:

しかも、​exponential backoff with jitter に対応しています。

簡単にいうと:

というやり方です。

これ、理屈は地味ですが本当に重要です。
retry って誰でも思いつくけど、雑に実装すると逆に障害を悪化させます。だからフレームワーク標準で持ってくれるのはありがたい。

記事では、maxRetriesinitialDelayMsbackoffFactornoJitter などの設定が紹介されています。

fallback: プロがダメならフラッシュへ

fallback middleware は、主モデルが失敗したときに別のモデルへ切り替えます。

記事の典型例は、​Pro を先に試して、quota が尽きたら Flash に落とすというものです。
これは実務でかなり使えそうです。

たとえば:

という設計ができます。

個人的には、これも「賢い現実対応」だと思います。
AIアプリは理想通りに動かないことの方が多いので、​最初から graceful degradation(うまく劣化する)を組み込めるのはかなり重要です。

自作 middleware も作れる

記事の後半では、generateMiddleware を使って自分で middleware を書けることも示唆されています。
つまり、built-in の便利さだけで終わらず、​自分のチームやプロダクトに合わせたルールを共通化できるわけです。

これは大きいです。

たとえば自作の middleware でできそうなことは:

こういうのって、最初は1箇所で頑張っても、後で必ず横展開したくなります。
だから middleware 化しておくのは、かなり先を見た設計だと思います。

この middleware システムの何がすごいのか

率直に言うと、Genkit Middleware の良さは「新しい魔法」ではなく、​AIアプリで毎回必要になる泥臭い処理を、きれいに定式化しているところにあります。

AIアプリは、モデルを呼ぶだけでは終わりません。

このへんが本番運用では本体です。
そこを middleware で整理できるなら、開発体験はかなり良くなるはずです。

もちろん、middleware が増えすぎると逆に追いにくくなる可能性はあります。
でも、​呼び出しごとに use で明示する設計なら、そのリスクはかなり抑えられると思います。

まとめ

Genkit Middleware は、AIアプリ開発で避けられない共通処理を、​model / tool / generate の3層で差し込めるようにした仕組みです。

built-in middleware だけでも、

と、実用的なものが揃っています。
しかも自作もできるので、プロダクト固有のルールにも対応しやすいです。

個人的には、これは「AIアプリをちゃんとソフトウェアとして育てる」ための、かなり筋の良い進化だと思いました。
LLM の出力を眺めるだけの段階から、​運用できるパイプラインとして扱う段階に進んでいる感じがあります。


参考: Genkit Middleware

同じ著者の記事