拡張機能プロセッサで Apigee ポリシーを構成する

このページは ApigeeApigee ハイブリッドに適用されます。

Apigee Edge のドキュメントを表示する

Apigee 拡張機能プロセッサを使用すると、Apigee プロキシを通過しないトラフィックに Apigee の AI ゲートウェイ機能を適用できます。たとえば、Google Kubernetes Engine で実行されているサービス、別のゲートウェイで管理されている API、AI エージェントの MCP サーバーなどです。トラフィック パスは標準の API プロキシとは異なるため、一部のポリシー構成は拡張機能プロセッサに固有のものです。このページでは、これらの考慮事項について説明し、構成例を示します。各ポリシー セクションは、そのポリシーの完全なチュートリアルにリンクしています。

このページのポリシーは、拡張機能プロセッサが接続されている場所に関係なく、同じように適用されます。

考慮すべきポイント

拡張機能プロセッサ プロキシにポリシーを適用する場合は、次の考慮事項が適用されます。アタッチする場所については、クイックスタートの拡張機能プロセッサでポリシーを使用するをご覧ください。

例をモデル API に合わせる

このページの例では、Gemini のリクエストとレスポンスの形状を使用しています。他のモデル プロバイダも同様に動作します。UserPromptSourceLLMTokenUsageSourceLLMModelSource はメッセージ テンプレートであるため、それらの API のペイロード内の同等の場所に設定します。ポリシー自体に変更はありません。

処理するトラフィックの範囲を指定する

標準の API プロキシでは、ベースパスを割り当て、クライアントがその特定の URL を呼び出すため、プロキシは意図したトラフィックのみを受信します。拡張機能プロセッサにはベースパスがないため、トラフィックのスコープは 2 か所で設定します。1 つは拡張機能で、Apigee に到達するものを決定します。もう 1 つはプロキシで、到達したトラフィックで実行するものを決定します。両方使用。

まず拡張機能でフィルタして、管理対象外のトラフィックが Apigee に送信されないようにします。

  • トラフィック拡張機能で、拡張機能チェーンに CEL 一致条件を設定します(例: matchCondition.celExpression: 'request.host == "example.com"')。
  • 認可拡張機能で、認可ポリシーhttpRules.to.operations でホストとパスの接頭辞を照合します。

次に、プロキシ内の個々のポリシーのスコープを設定します。単一の拡張機能プロセッサ プロキシは、拡張機能が選択したすべてのものを受け取ります。これは、AI エージェントのモデル呼び出し、セッション呼び出し、状態呼び出し、テレメトリー呼び出しがホストを共有できるため、依然として混合状態になる可能性があります。モデル ペイロードを検査するポリシーは、ペイロードのない呼び出しで失敗し、失敗したポリシーはリクエストをブロックします。各ポリシーを、目的のトラフィックにスコープ設定する条件とともにアタッチします。

<!-- Run only on the model (generateContent) call -->
<Step>
  <Name>My-Policy</Name>
  <Condition>(request.uri Like "*generateContent*")</Condition>
</Step>

<!-- Or scope by backend host -->
<Step>
  <Name>My-Policy</Name>
  <Condition>(request.header.host = "backend.example.com")</Condition>
</Step>

ターゲットなしプロキシを使用する

拡張機能プロセッサ プロキシは、インターセプトされたトラフィックを処理し、ターゲット エンドポイントを持ちません。拡張可能なプロキシとしてデプロイします。拡張機能プロセッサ環境内のすべてのプロキシは、同じプロキシタイプである必要があります。

傍受された通話の本文を読み取る

ペイロードを検査するポリシーは、ワイヤに表示されるメッセージに対して動作します。モデル呼び出しの場合、これはモデルのリクエストとレスポンスです。たとえば、ユーザー プロンプトは $.contents[-1].parts[-1].text にあり、モデルのレスポンスは $.candidates[-1].content.parts[-1].text にあります。

Google サービスを呼び出すポリシーのサービス アカウントを付与する

Google サービス(Model Armor や、セマンティック キャッシュで使用されるエンベディングとインデックス ルックアップなど)を呼び出すポリシーには、デプロイ サービス アカウントが必要です。serviceAccount パラメータを使用してプロキシをデプロイします。

Model Armor による AI の安全性

モデル呼び出しにスコープ設定された SanitizeUserPrompt ポリシーと SanitizeModelResponse ポリシーを適用します。テンプレートの設定については、Model Armor のスタートガイドをご覧ください。

<SanitizeUserPrompt name="SUP-sanitize" continueOnError="false">
  <ModelArmor>
    <TemplateName>projects/PROJECT/locations/LOCATION/templates/TEMPLATE</TemplateName>
  </ModelArmor>
  <UserPromptSource>{jsonPath('$.contents[-1].parts[-1].text',request.content,true)}</UserPromptSource>
</SanitizeUserPrompt>

リクエスト フローで、条件 (request.uri Like "*generateContent*") を使用して SUP-sanitize を関連付けます。プロンプトが Model Armor テンプレートと一致すると、ポリシーはリクエストを拒否するため、プロンプトがモデルに到達することはありません。

セマンティック キャッシュ保存

リクエスト フローに SemanticCacheLookup ポリシーを、レスポンス フローに SemanticCachePopulate ポリシーをアタッチします。どちらもモデル呼び出しにスコープ設定します。インデックスとエンベディングの設定については、セマンティック キャッシュ保存を使ってみるをご覧ください。リクエストがキャッシュに保存されているプロンプトと一致すると、モデルを呼び出すことなく、レスポンスがキャッシュから提供されます。

モデル呼び出しのトークン上限

2 つのポリシーは、モデル呼び出しでの大規模言語モデル(LLM)トークンの使用を制限します。両方を (request.uri Like "*generateContent*") 条件でモデル呼び出しにスコープします。設定については、LLM トークン ポリシーを使ってみるをご覧ください。

プロンプト トークンを制限する

PromptTokenLimit ポリシーは、ユーザー プロンプトに基づいてトークンをスロットリングします。これは、プロンプトのスパイク抑制です。リクエスト フローに添付します。インターセプトされたリクエストからプロンプトを読み取り、レートが超過したときに呼び出しを拒否するため、サイズの大きなプロンプトがモデルに到達することはありません。次の例では、プロンプトを 1 分あたり 1,000 トークンに制限します。

<PromptTokenLimit continueOnError="false" enabled="true" name="PTL-limit-prompt">
  <Rate>1000pm</Rate>
  <UserPromptSource>{jsonPath('$.contents[-1].parts[-1].text',request.content,true)}</UserPromptSource>
</PromptTokenLimit>

レスポンス トークンの消費を制限する

LLMTokenQuota ポリシーは、モデル レスポンスで返されたトークンをカウントし、一定の時間間隔でトークン使用量の割り当てを適用します。リクエスト フローに EnforceOnly インスタンスを接続して、割り当てを超えたときに呼び出しを拒否し、レスポンス フローに CountOnly インスタンスを接続して、使用されたトークンをカウントし、$.usageMetadata.candidatesTokenCount から読み取ります。両方のインスタンスに同じ SharedName を指定して、単一のカウンタを更新します。このポリシーには拡張可能なプロキシが必要です。次のペアは、30 分あたり 15,000 個のトークンを適用します。

<!-- Request flow: reject when the token quota is exceeded -->
<LLMTokenQuota name="LTQ-enforce" type="rollingwindow">
  <SharedName>llm-token-counter</SharedName>
  <EnforceOnly>true</EnforceOnly>
  <Allow count="15000"/>
  <Interval>30</Interval>
  <TimeUnit>minute</TimeUnit>
  <Distributed>true</Distributed>
</LLMTokenQuota>

<!-- Response flow: count the tokens used in the model response -->
<LLMTokenQuota name="LTQ-count" type="rollingwindow">
  <SharedName>llm-token-counter</SharedName>
  <CountOnly>true</CountOnly>
  <Allow count="15000"/>
  <Interval>30</Interval>
  <TimeUnit>minute</TimeUnit>
  <Distributed>true</Distributed>
  <LLMTokenUsageSource>{jsonPath('$.usageMetadata.candidatesTokenCount',response.content,true)}</LLMTokenUsageSource>
</LLMTokenQuota>

トラフィック ガバナンス: 割り当て、認可、スパイク阻止

これらのポリシーは、拡張プロセッサを通過するトラフィックに適用されます。これには、Apigee でホストされていないバックエンド(GKE でホストされている API、AI エージェントが呼び出すツールや MCP サーバーなど)も含まれます。保護するバックエンドに各ポリシーのスコープを設定します。

<Step><Name>Verify-API-Key</Name><Condition>(request.header.host = "backend.example.com")</Condition></Step>
<Step><Name>Quota-Limit</Name><Condition>(request.header.host = "backend.example.com")</Condition></Step>
<Step><Name>Spike-Arrest</Name><Condition>(request.header.host = "backend.example.com")</Condition></Step>
  • 認可: VerifyAPIKey ポリシーまたは OAuthV2 ポリシーを使用します。不正な呼び出しは、バックエンドに到達する前に拒否されます。
  • Quota: Quotaポリシーを使用して、正確な呼び出し制限を適用します。ポリシーを分散同期として構成して、ランタイム全体で単一の共有カウントとして上限が適用されるようにします。
  • スパイク アレスト: SpikeArrest ポリシーを使用して、トラフィックの急増を緩和します。スパイク制御はメッセージ プロセッサごとに適用され、正確なグローバル レートは保証されません。正確な上限が必要な場合は、割り当てポリシーを使用します。

メッセージを変換して変数を抽出する

AssignMessage ポリシーを使用して、メッセージの一部(ヘッダー、クエリ パラメータ、ペイロード)を追加、変更、削除します。また、ExtractVariables ポリシーを使用して、メッセージから値を読み取り、後続のポリシーで使用できる変数に格納します。拡張機能プロセッサを使用すると、両方のポリシーがリクエスト フローで動作し、インターセプトされたリクエストを処理します。また、レスポンス フローで動作し、バックエンド レスポンスを処理します。拡張機能プロセッサ ポリシーと同様に、各アタッチメントのスコープを条件で設定して、目的のトラフィックでのみ実行されるようにします。

次の例では、ExtractVariables を使用してリクエスト本文からフィールドを読み取り、レスポンス フローでレスポンス本文からフィールドを読み取ります。

<!-- Request flow: read a field from the intercepted request -->
<ExtractVariables name="EV-from-request">
  <Source>request</Source>
  <JSONPayload>
    <Variable name="user.prompt">
      <JSONPath>$.contents[-1].parts[-1].text</JSONPath>
    </Variable>
  </JSONPayload>
</ExtractVariables>

<!-- Response flow: read a field from the backend response -->
<ExtractVariables name="EV-from-response">
  <Source>response</Source>
  <JSONPayload>
    <Variable name="model.answer">
      <JSONPath>$.candidates[-1].content.parts[-1].text</JSONPath>
    </Variable>
  </JSONPayload>
</ExtractVariables>

次の例では、AssignMessage を使用して、リクエストがバックエンドに到達する前にリクエストのヘッダーを設定し、レスポンスが呼び出し元に返される前にレスポンスのヘッダーを設定します。

<!-- Request flow: add a header to the intercepted request -->
<AssignMessage name="AM-set-request-header">
  <Set>
    <Headers>
      <Header name="X-Apigee-Processed">true</Header>
    </Headers>
  </Set>
  <AssignTo createNew="false" type="request"/>
</AssignMessage>

<!-- Response flow: add a header to the backend response -->
<AssignMessage name="AM-set-response-header">
  <Set>
    <Headers>
      <Header name="X-Apigee-Cache">miss</Header>
    </Headers>
  </Set>
  <AssignTo createNew="false" type="response"/>
</AssignMessage>

次のステップ