Configurer des règles Apigee avec le processeur d'extension

Cette page s'applique à Apigee et à Apigee hybrid.

Consultez la documentation d' Apigee Edge.

Le processeur d'extension Apigee vous permet d'appliquer les fonctionnalités de la passerelle d'IA d'Apigee au trafic qui ne transite pas par un proxy Apigee. Par exemple, un service exécuté sur Google Kubernetes Engine, une API gérée par une autre passerelle ou un serveur MCP pour un agent d'IA. Étant donné que le chemin du trafic diffère de celui d'un proxy d'API standard, certaines configurations de règles sont spécifiques au processeur d'extension. Cette page décrit ces considérations et fournit des exemples de configurations. Chaque section de règles renvoie au tutoriel complet correspondant.

Les règles de cette page s'appliquent de la même manière, quel que soit l'endroit où le processeur d'extension est associé :

Remarques importantes

Les considérations suivantes s'appliquent lorsque vous associez une règle à un proxy de processeur d'extension. Pour savoir où les joindre, consultez Utiliser des règles avec le processeur d'extension dans le guide de démarrage rapide.

Faites correspondre les exemples à l'API de votre modèle

Les exemples de cette page utilisent la forme des requêtes et des réponses Gemini. Les autres fournisseurs de modèles fonctionnent de la même manière : UserPromptSource, LLMTokenUsageSource et LLMModelSource sont des modèles de message. Vous devez donc les définir sur l'emplacement équivalent dans la charge utile de cette API. Les règles elles-mêmes n'ont pas changé.

Définir le champ d'application du trafic que vous traitez

Avec un proxy d'API standard, vous attribuez un chemin de base et les clients appellent cette URL spécifique. Le proxy ne reçoit donc que le trafic qui lui est destiné. Le processeur d'extension n'a pas de chemin de base. Vous devez donc définir le champ d'application de son trafic à deux endroits : au niveau de l'extension, qui décide de ce qui atteint Apigee, et au niveau du proxy, qui décide de ce qui s'exécute sur le trafic qui arrive. Utilisez les deux.

Filtrez d'abord au niveau de l'extension afin que le trafic que vous ne souhaitez pas régir ne soit jamais envoyé à Apigee :

  • Sur une extension de trafic, définissez une condition de correspondance CEL sur la chaîne d'extension, par exemple matchCondition.celExpression: 'request.host == "example.com"'.
  • Dans une extension d'autorisation, faites correspondre les hôtes et les préfixes de chemin d'accès sous httpRules.to.operations dans les règles d'autorisation.

Définissez ensuite le champ d'application des règles individuelles dans le proxy. Un seul proxy de processeur d'extension reçoit tout ce que l'extension sélectionne, et cela peut toujours être un mélange : les appels de modèle d'agent d'IA, les appels de session et d'état, et les appels de télémétrie peuvent partager un hôte. Une règle qui inspecte une charge utile de modèle échoue lors d'un appel qui n'en comporte pas, et une règle ayant échoué bloque la requête. Associez chaque règle à une condition qui la limite au trafic prévu :

<!-- 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>

Utiliser un proxy sans cible

Les proxys de processeur d'extension traitent le trafic intercepté et n'ont pas de point de terminaison cible. Déployez-les en tant que proxys extensibles. Tous les proxys d'un environnement de processeur d'extension doivent être du même type.

Lire le contenu de l'appel intercepté

Les règles qui inspectent la charge utile s'appliquent au message tel qu'il apparaît sur le réseau. Pour un appel de modèle, il s'agit de la requête et de la réponse du modèle. Par exemple, la requête utilisateur se trouve à l'adresse $.contents[-1].parts[-1].text et la réponse du modèle à l'adresse $.candidates[-1].content.parts[-1].text.

Accorder un compte de service pour les stratégies qui appellent des services Google

Les règles qui appellent un service Google (par exemple, Model Armor ou les recherches d'index et d'embeddings utilisées par la mise en cache sémantique) nécessitent un compte de service de déploiement. Déployez le proxy avec le paramètre serviceAccount.

Sécurité de l'IA avec Model Armor

Associez les règles SanitizeUserPrompt et SanitizeModelResponse à l'appel du modèle. Pour configurer des modèles, consultez Premiers pas avec 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>

Ajoutez SUP-sanitize au flux de requête avec la condition (request.uri Like "*generateContent*"). Lorsque le prompt correspond au modèle Model Armor, la règle rejette la requête, de sorte que le prompt n'atteint jamais le modèle.

Mise en cache sémantique

Associez la règle SemanticCacheLookup au flux de requête et la règle SemanticCachePopulate au flux de réponse, toutes deux limitées à l'appel de modèle. Pour configurer l'index et les embeddings, consultez Premiers pas avec la mise en cache sémantique. Lorsqu'une requête correspond à une requête mise en cache, la réponse est fournie à partir du cache sans appeler le modèle.

Limites de jetons pour les appels de modèle

Deux règles limitent l'utilisation des jetons des grands modèles de langage (LLM) lors de l'appel de modèle. Définissez le champ d'application des deux sur l'appel de modèle avec la condition (request.uri Like "*generateContent*"). Pour la configuration, consultez Premiers pas avec les règles relatives aux jetons LLM.

Limiter les jetons de prompt

La règle PromptTokenLimit limite les jetons en fonction du prompt de l'utilisateur. Il s'agit d'une protection contre les pics pour les prompts. Joignez-le au flux de requête. Il lit l'invite à partir de la requête interceptée et rejette l'appel lorsque le taux est dépassé. Ainsi, une invite surdimensionnée n'atteint jamais le modèle. L'exemple suivant limite les requêtes à 1 000 jetons par minute :

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

Limiter la consommation de jetons de réponse

La règle LLMTokenQuota applique un quota de consommation de jetons sur un intervalle de temps, en comptabilisant les jetons renvoyés dans la réponse du modèle. Associez une instance EnforceOnly dans le flux de requête pour rejeter les appels une fois le quota dépassé, et une instance CountOnly dans le flux de réponse pour comptabiliser les jetons utilisés, lus à partir de $.usageMetadata.candidatesTokenCount. Attribuez aux deux instances le même SharedName afin qu'elles mettent à jour un seul compteur. Cette règle nécessite un proxy extensible. La paire suivante applique une limite de 15 000 jetons par période de 30 minutes :

<!-- 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>

Gouvernance du trafic : quota, autorisation et protection contre les pics

Ces règles s'appliquent au trafic transitant par le processeur d'extension vers n'importe quel backend, y compris les backends qui ne sont pas hébergés sur Apigee, tels qu'une API hébergée sur GKE ou un outil ou un serveur MCP qu'un agent d'IA appelle. Définissez le champ d'application de chaque règle sur le backend que vous souhaitez protéger :

<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>
  • Autorisation : utilisez la règle VerifyAPIKey ou OAuthV2. Les appels non autorisés sont rejetés avant d'atteindre le backend.
  • Quota : utilisez la règle Quota pour appliquer une limite d'appels précise. Configurez la règle comme distribuée et synchrone afin que la limite soit appliquée en tant que nombre partagé unique dans l'environnement d'exécution.
  • SpikeArrest : utilisez la règle SpikeArrest pour atténuer les pics de trafic. La protection contre les pics est appliquée par processeur de messages et ne garantit pas un débit global exact. Si vous avez besoin d'une limite précise, utilisez la règle de quota.

Transformer des messages et extraire des variables

Utilisez la règle AssignMessage pour ajouter, modifier ou supprimer des parties d'un message (en-têtes, paramètres de requête ou charge utile) et la règle ExtractVariables pour lire les valeurs d'un message dans des variables que les règles ultérieures pourront utiliser. Avec le processeur d'extension, les deux règles fonctionnent dans le flux de requête, en agissant sur la requête interceptée, et dans le flux de réponse, en agissant sur la réponse du backend. Comme pour toute règle de processeur d'extension, définissez le champ d'application de chaque pièce jointe avec une condition afin qu'elle ne s'exécute que sur le trafic prévu.

L'exemple suivant utilise ExtractVariables pour lire un champ du corps de la requête et, dans le flux de réponse, pour lire un champ du corps de la réponse :

<!-- 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>

L'exemple suivant utilise AssignMessage pour définir un en-tête sur la requête avant qu'elle n'atteigne le backend, et sur la réponse avant qu'elle ne revienne à l'appelant :

<!-- 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>

Étapes suivantes