Apigee-Richtlinien mit dem Erweiterungsprozessor konfigurieren

Diese Seite gilt für Apigee und Apigee Hybrid.

Apigee Edge-Dokumentation aufrufen

Mit dem Apigee Extension Processor können Sie AI Gateway-Funktionen von Apigee auf Traffic anwenden, der nicht über einen Apigee-Proxy geleitet wird. Beispiele sind ein Dienst, der in Google Kubernetes Engine ausgeführt wird, eine API, die von einem anderen Gateway verwaltet wird, oder ein MCP-Server für einen KI-Agenten. Da sich der Trafficpfad von einem Standard-API-Proxy unterscheidet, ist die Richtlinienkonfiguration teilweise spezifisch für den Erweiterungsprozessor. Auf dieser Seite werden diese Überlegungen beschrieben und Beispielkonfigurationen bereitgestellt. Jeder Richtlinienabschnitt enthält einen Link zum vollständigen Tutorial für die jeweilige Richtlinie.

Die Richtlinien auf dieser Seite gelten unabhängig davon, wo der Erweiterungsprozessor angehängt ist:

Wichtige Überlegungen

Die folgenden Überlegungen gelten, wenn Sie eine Richtlinie an einen Proxy für die Erweiterungsverarbeitung anhängen. Informationen dazu, wo Sie sie anhängen, finden Sie in der Kurzanleitung unter Richtlinien mit dem Erweiterungsprozessor verwenden.

Beispiele an Ihre Modell-API anpassen

In den Beispielen auf dieser Seite wird die Form von Gemini-Anfragen und ‑Antworten verwendet. Bei anderen Modellanbietern ist es genauso: UserPromptSource, LLMTokenUsageSource und LLMModelSource sind Nachrichtenvorlagen. Sie legen sie also an der entsprechenden Stelle in der Nutzlast der jeweiligen API fest. Die Richtlinien selbst bleiben unverändert.

Verarbeiteten Traffic eingrenzen

Bei einem Standard-API-Proxy weisen Sie einen Basispfad zu und Clients rufen diese bestimmte URL auf. Der Proxy empfängt also nur den für ihn bestimmten Traffic. Der Erweiterungsprozessor hat keinen Basispfad. Sie müssen den Traffic daher an zwei Stellen eingrenzen: in der Erweiterung, die entscheidet, was überhaupt Apigee erreicht, und im Proxy, der entscheidet, was mit dem eingehenden Traffic passiert. Beide verwenden.

Filtern Sie zuerst in der Erweiterung, damit Traffic, den Sie nicht steuern möchten, nie an Apigee gesendet wird:

  • Legen Sie für eine Traffic-Erweiterung eine CEL-Abgleichsbedingung für die Erweiterungskette fest, z. B. matchCondition.celExpression: 'request.host == "example.com"'.
  • Bei einer Autorisierungserweiterung müssen Sie Hosts und Pfadpräfixe unter httpRules.to.operations in der Autorisierungsrichtlinie abgleichen.

Weisen Sie dann dem Proxy einzelne Richtlinien zu. Ein einzelner Erweiterungsprozessor-Proxy empfängt alles, was die Erweiterung auswählt. Das kann weiterhin eine Mischung sein: Modellaufrufe, Sitzungs- und Statusaufrufe sowie Telemetrieaufrufe eines KI-Agents können sich einen Host teilen. Eine Richtlinie, die die Nutzlast eines Modells prüft, schlägt bei einem Aufruf fehl, der keine Nutzlast enthält. Eine fehlgeschlagene Richtlinie blockiert die Anfrage. Hängen Sie jede Richtlinie mit einer Bedingung an, die sie auf den gewünschten Traffic beschränkt:

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

Proxy ohne Ziel verwenden

Erweiterungsprozessor-Proxys verarbeiten abgefangenen Traffic und haben keinen Zielendpunkt. Stellen Sie sie als erweiterbare Proxys bereit. Alle Proxys in einer Erweiterungsprozessorumgebung müssen denselben Proxypfad haben.

Inhalt des abgefangenen Anrufs lesen

Richtlinien, die die Nutzlast prüfen, werden auf die Nachricht angewendet, wie sie über die Leitung übertragen wird. Bei einem Modellaufruf ist dies die Anfrage und Antwort des Modells. Der Nutzer-Prompt befindet sich beispielsweise unter $.contents[-1].parts[-1].text und die Modellantwort unter $.candidates[-1].content.parts[-1].text.

Dienstkonto für Richtlinien gewähren, die Google-Dienste aufrufen

Für Richtlinien, die einen Google-Dienst aufrufen, z. B. Model Armor oder die Einbettungen und Indexsuchvorgänge, die vom semantischen Caching verwendet werden, ist ein Dienstkonto für die Bereitstellung erforderlich. Stellen Sie den Proxy mit dem Parameter serviceAccount bereit.

KI-Sicherheit mit Model Armor

Hängen Sie die Richtlinien SanitizeUserPrompt und SanitizeModelResponse an den Modellaufruf an. Informationen zur Vorlageneinrichtung finden Sie unter Erste Schritte mit 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>

Hängen Sie SUP-sanitize im Anfrageablauf mit der Bedingung (request.uri Like "*generateContent*") an. Wenn der Prompt mit der Model Armor-Vorlage übereinstimmt, wird die Anfrage von der Richtlinie abgelehnt. Der Prompt erreicht das Modell also nie.

Semantisches Caching

Hängen Sie die Richtlinie SemanticCacheLookup an den Anfrageablauf und die Richtlinie SemanticCachePopulate an den Antwortablauf an. Beide sind auf den Modellaufruf beschränkt. Informationen zum Einrichten von Index und Einbettungen finden Sie unter Erste Schritte mit semantischem Caching. Wenn eine Anfrage mit einem im Cache gespeicherten Prompt übereinstimmt, wird die Antwort aus dem Cache bereitgestellt, ohne das Modell aufzurufen.

Tokenlimits für Modellaufrufe

Zwei Richtlinien begrenzen die Verwendung von Large Language Model (LLM)-Tokens beim Modellaufruf. Beschränken Sie beide auf den Modellaufruf mit der Bedingung (request.uri Like "*generateContent*"). Eine Anleitung zur Einrichtung finden Sie unter Erste Schritte mit LLM-Tokenrichtlinien.

Prompt-Tokens begrenzen

Die PromptTokenLimit-Richtlinie drosselt Tokens basierend auf dem Nutzer-Prompt. Sie ist also eine SpikeArrest-Richtlinie für Prompts. Hängen Sie sie an den Anfragestrom an. Sie liest den Prompt aus der abgefangenen Anfrage und lehnt den Aufruf ab, wenn die Rate überschritten wird. Ein zu großer Prompt erreicht das Modell also nie. Im folgenden Beispiel werden Prompts auf 1.000 Tokens pro Minute begrenzt:

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

Verbrauch von Antwort-Tokens begrenzen

Die LLMTokenQuota-Richtlinie erzwingt ein Kontingent für den Tokenverbrauch über ein Zeitintervall hinweg und zählt die in der Modellantwort zurückgegebenen Tokens. Hängen Sie eine EnforceOnly-Instanz im Anfragefluss an, um Aufrufe abzulehnen, sobald das Kontingent überschritten wurde, und eine CountOnly-Instanz im Antwortfluss, um die verwendeten Tokens zu zählen, die aus $.usageMetadata.candidatesTokenCount gelesen werden. Weisen Sie beiden Instanzen denselben SharedName zu, damit ein einzelner Zähler aktualisiert wird. Für diese Richtlinie ist ein erweiterbarer Proxy erforderlich. Das folgende Paar erzwingt 15.000 Tokens pro 30 Minuten:

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

Traffic-Governance: Kontingent, Autorisierung und Schutz vor Traffic-Spitzen

Diese Richtlinien werden für Traffic über den Erweiterungsprozessor zu jedem Backend erzwungen, einschließlich Backends, die nicht auf Apigee gehostet werden, z. B. eine in GKE gehostete API oder ein Tool oder MCP-Server, den ein KI-Agent aufruft. Weisen Sie jede Richtlinie dem Backend zu, das Sie schützen möchten:

<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>
  • Autorisierung: Verwenden Sie die VerifyAPIKey- oder die OAuthV2-Richtlinie. Nicht autorisierte Anrufe werden abgelehnt, bevor sie das Backend erreichen.
  • Quota: Verwenden Sie die Quota, um ein genaues Aufruflimit zu erzwingen. Konfigurieren Sie die Richtlinie als verteilt und synchron, damit das Limit als einzelner gemeinsamer Zähler für die gesamte Laufzeit erzwungen wird.
  • Spike Arrest: Verwenden Sie die SpikeArrest-Richtlinie, um Traffic-Bursts auszugleichen. Die SpikeArrest-Richtlinie wird pro Nachrichtenprozessor erzwungen und garantiert keine genaue globale Rate. Wenn Sie ein genaues Limit benötigen, verwenden Sie die Kontingentrichtlinie.

Nachrichten transformieren und Variablen extrahieren

Mit der AssignMessage können Sie Teile einer Nachricht (Header, Abfrageparameter oder die Nutzlast) hinzufügen, ändern oder entfernen. Mit der ExtractVariables können Sie Werte aus einer Nachricht in Variablen lesen, die später von anderen Richtlinien verwendet werden können. Mit dem Erweiterungsprozessor funktionieren beide Richtlinien im Anfrageablauf, wobei sie auf die abgefangene Anfrage angewendet werden, und im Antwortablauf, wobei sie auf die Backend-Antwort angewendet werden. Wie bei jeder Richtlinie für Erweiterungsprozessoren sollten Sie jede Anlage mit einer Bedingung versehen, damit sie nur für den vorgesehenen Traffic ausgeführt wird.

Im folgenden Beispiel wird „ExtractVariables“ verwendet, um ein Feld aus dem Anfragetext zu lesen, und im Antwortfluss, um ein Feld aus dem Antworttext zu lesen:

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

Im folgenden Beispiel wird mit AssignMessage ein Header für die Anfrage festgelegt, bevor sie das Back-End erreicht, und für die Antwort, bevor sie an den Aufrufer zurückgegeben wird:

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

Nächste Schritte