확장 프로그램 프로세서로 Apigee 정책 구성

이 페이지는 ApigeeApigee Hybrid에 적용됩니다.

Apigee Edge 문서 보기

Apigee 확장 프로그램 프로세서를 사용하면 Apigee 프록시를 통과하지 않는 트래픽에 Apigee의 AI 게이트웨이 기능을 적용할 수 있습니다. 예를 들어 Google Kubernetes Engine에서 실행되는 서비스, 다른 게이트웨이에서 관리하는 API 또는 AI 에이전트의 MCP 서버가 있습니다. 트래픽 경로가 표준 API 프록시와 다르므로 일부 정책 구성은 확장 프로그램 프로세서에만 적용됩니다. 이 페이지에서는 이러한 고려사항을 설명하고 구성 예를 제공합니다. 각 정책 섹션은 해당 정책의 전체 튜토리얼로 연결됩니다.

이 페이지의 정책은 확장 프로그램 프로세서가 연결된 위치에 관계없이 동일하게 적용됩니다.

주요 고려사항

확장 프로그램 프로세서 프록시에 정책을 연결할 때는 다음 고려사항이 적용됩니다. 첨부 위치는 빠른 시작의 확장 프로그램 프로세서와 함께 정책 사용을 참고하세요.

예를 모델 API와 일치시킵니다.

이 페이지의 예시에서는 Gemini 요청 및 응답 모양을 사용합니다. 다른 모델 제공자도 동일한 방식으로 작동합니다. UserPromptSource, LLMTokenUsageSource, LLMModelSource는 메시지 템플릿이므로 해당 API의 페이로드에서 동일한 위치로 설정합니다. 정책 자체는 변경되지 않습니다.

처리하는 트래픽 범위 지정

표준 API 프록시를 사용하면 기본 경로를 할당하고 클라이언트가 해당 특정 URL을 호출하므로 프록시는 의도된 트래픽만 수신합니다. 확장 프로그램 프로세서에는 기본 경로가 없으므로 트래픽의 범위를 두 곳에서 지정합니다. 하나는 Apigee에 도달하는 항목을 결정하는 확장 프로그램이고 다른 하나는 도착한 트래픽에서 실행되는 항목을 결정하는 프록시입니다. 둘 다 사용합니다.

관리하지 않으려는 트래픽이 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 안전

모델 호출 범위로 지정된 SanitizeUserPromptSanitizeModelResponse 정책을 연결합니다. 템플릿 설정은 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 정책을 연결합니다. 두 정책 모두 모델 호출로 범위가 지정됩니다. 색인 및 삽입 설정은 시맨틱 캐싱 시작하기를 참고하세요. 요청이 캐시된 프롬프트와 일치하면 모델을 호출하지 않고 캐시에서 응답이 제공됩니다.

모델 호출의 토큰 한도

두 가지 정책은 모델 호출에서 대규모 언어 모델 (LLM) 토큰 사용량을 제한합니다. (request.uri Like "*generateContent*") 조건으로 모델 호출에 모두 범위를 지정합니다. 설정은 LLM 토큰 정책 시작하기를 참고하세요.

프롬프트 토큰 제한

PromptTokenLimit 정책은 사용자 프롬프트를 기반으로 토큰을 제한합니다(프롬프트의 급증 방지). 요청 흐름에 연결합니다. 가로채진 요청에서 프롬프트를 읽고 비율이 초과되면 호출을 거부하므로 지나치게 큰 프롬프트가 모델에 도달하지 않습니다. 다음 예에서는 프롬프트를 분당 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 인스턴스를 연결하고 사용된 토큰을 계산하고 $.usageMetadata.candidatesTokenCount에서 읽는 응답 흐름에 CountOnly 인스턴스를 연결합니다. 두 인스턴스에 동일한 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>

다음 단계