Model Armor를 사용하는 Agent Runtime에 대한 Agent Gateway 인그레스

1. 소개

이 Codelab에서는 Agent Runtime에서 호스팅되는 AI 에이전트의 Agent Gateway 수신 거버넌스를 살펴봅니다.

수신(클라이언트-에이전트) 모드로 작동하는 Agent Gateway는 클라이언트(실제 최종 사용자, 데스크톱 에이전트, 코딩 IDE, 동종 에이전트 등)와 Agent Runtime 호스팅 에이전트 간의 통신을 관리합니다. 이 모드는 에이전트를 인바운드 프롬프트 인젝션 공격이나 클라이언트가 보낸 유해한 콘텐츠로부터 보호하는 데 사용됩니다. 모든 인바운드 트래픽은 승인 확장 프로그램과 Model Armor를 사용하여 모든 상담사 상호작용의 네트워크 진입점을 보호합니다.

빌드할 항목

  • 인그레스 (클라이언트에서 에이전트로) 모드의 Agent Gateway
  • Model Armor 승인 확장 프로그램
  • 에이전트 ID가 있는 Agent Runtime ADK 에이전트
  • MCP를 사용하여 에이전트가 쿼리한 Cloud Storage 파일 데이터
  • LLM 프롬프트와 대답을 검사하기 위한 Model Armor 템플릿
  • 데이터 익명화를 위한 Sensitive Data Protection 템플릿

figure1

그림 1. Codelab 아키텍처

학습 내용

  • 에이전트로의 인그레스 트래픽을 스크리닝하기 위해 Agent Gateway를 배포하는 방법
  • Model Armor 승인 확장 프로그램 및 위임 구성 방법
  • 맞춤 Model Armor 템플릿을 만들고 배포하는 방법
  • 맞춤 Sensitive Data Protection 템플릿을 만들고 배포하는 방법
  • LLM 검토 정책을 테스트하고 검증하는 방법

필요한 항목

  • 결제가 사용 설정된 Google Cloud 프로젝트
  • 네트워킹 서비스, BigQuery 데이터 세트, Agent Platform 리소스를 프로비저닝하는 IAM 권한
  • Google Cloud CLI (gcloud 구성요소)가 설치된 POSIX 호환 셸 (bash 또는 zsh)
  • 명령줄 도구: git, curl, jq (JSON 프로세서), Python 3, uv (Python 패키지 관리자)

2. 개념

트래픽 방향 및 게이트웨이 역할

Agent Gateway는 에이전트 인식 네트워크 프록시로 작동하지만, 트래픽 방향에 따라 작동 역할이 달라집니다.

  • 에이전트-어디로든 (egress) 모드: 아웃바운드 프록시로 작동합니다. 에이전트가 외부 데이터베이스 도구, 서드 파티 MCP 서버 또는 API를 호출하면 이그레스 게이트웨이가 서비스 검색, 라우팅, 상호 TLS (mTLS), OAuth 사용자 인증 정보의 동적 삽입, 엔드포인트에 대한 액세스 제어를 관리합니다.
  • 클라이언트-에이전트 (수신) 모드: 프런트엔드 보안 게이트웨이로 작동합니다. 기본 목표는 에이전트 실행 런타임의 입구를 보호하는 것입니다. 이를 위해 에이전트 코드나 AI 모델에 도달하기 전에 수신되는 자연어 프롬프트를 가로채고 정리합니다.

Agent Runtime의 인그레스 경로

Agent Runtime에 호스팅된 에이전트를 타겟팅하는 클라이언트 요청은 aiplatform.googleapis.com API 엔드포인트로 전송됩니다.

POST https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}:query

API 엔드포인트로의 이 인바운드 통신 스트림은 클라이언트-에이전트 수신 경로를 나타냅니다.

이 Google 관리 인그레스 경로를 보호하기 위해 에이전트 게이트웨이는 API 제공 인프라 레이어에서 Google 프런트엔드 (GFE)와 직접 통합됩니다. 관리형 에이전트를 Agent Runtime에 배포하면 Google은 네트워크 에지에서 인그레스 게이트웨이 승인 정책을 수신 클라이언트 요청에 기본적으로 바인딩합니다.

figure2

그림 2. Agent Gateway에서 Agent Runtime으로의 인그레스 거버넌스

검사는 요청이 Agent Runtime에 들어가기 전 프런트엔드 계층에서 발생하므로 이 아키텍처는 추가 네트워킹 오버헤드나 내부 홉 지연 시간을 도입하지 않습니다. 확장은 프런트엔드 인프라에서 자동으로 처리하므로 내부 IP 범위, 부하 분산기 또는 맞춤 DNS 경로를 관리할 필요가 없습니다.

Model Armor를 사용한 인라인 위협 정리

호출자 사용자 인증 정보를 평가하고 IAM 액세스 제어 (roles/aiplatform.user)를 적용하는 작업은 aiplatform API 호스팅 계층에서 기본적으로 처리합니다. 인그레스 게이트웨이 자체는 ID 승인을 실행하지 않고 CONTENT_AUTHZ 프로필로 구성된 승인 확장 프로그램을 사용하여 콘텐츠 보안에 중점을 둡니다. 게이트웨이는 인라인 정책 시행 지점 역할을 하여 AI 에이전트 추론 루프 또는 기본 LLM에 도달하기 전에 전송 중인 자연어 프롬프트를 가로챕니다.

수신 사용자 프롬프트가 프런트엔드 서비스에 도착하면 게이트웨이는 지역 Model Armor 승인 확장 프로그램 서비스에 ext_proc (외부 처리) 콜아웃을 시작합니다. 이 서비스는 Model Armor 데이터 플레인에 호출을 스트리밍합니다. Model Armor는 자연어 방화벽 역할을 하며, 활성 템플릿을 기준으로 텍스트를 평가하여 안전 및 보안 위험을 검사합니다.

  • 간접 프롬프트 인젝션 및 탈옥 시도
  • 악성 URL, 유해한 언어 또는 안전하지 않은 콘텐츠
  • 개인 식별 정보 (PII) 및 민감한 정보 유출

템플릿에 Sensitive Data Protection (SDP) 필터가 포함된 경우 Model Armor는 Cloud SDP 서비스에 gRPC 호출을 추가로 실행합니다. Cloud SDP는 지정된 템플릿을 사용하여 페이로드를 검사하고, 요청된 익명화 또는 수정 작업을 실행하고, 안전하게 전달될 수 있도록 체인으로 다시 정리된 결과를 반환합니다.

정책 위반 또는 수정되지 않은 민감한 정보 일치가 감지되면 게이트웨이는 런타임에 들어가기 전에 에지에서 페이로드를 차단하거나 수정합니다. 따라서 실행 중인 AI 에이전트 애플리케이션은 보호된 상태로 유지되며 악성 페이로드나 수정되지 않은 페이로드를 처리하지 않습니다.

이로써 개념 부분이 마무리되었습니다. 다음은 설정 섹션입니다.

3. 설정

필요한 IAM 역할

이 Codelab에서 리소스를 만들려면 다음 역할이 필요합니다.

카테고리

필수 IAM 역할 (ID)

설명

API 관리

roles/serviceusage.serviceUsageAdmin

Google Cloud API 서비스 사용 설정

네트워킹 및 게이트웨이

roles/networkservices.admin

에이전트 게이트웨이 프로비저닝

Service Extensions

roles/serviceextensions.admin

라우팅 확장 프로그램 구성

네트워크 보안

roles/networksecurity.admin

승인 정책 배포

Sensitive Data Protection

roles/dlp.admin

SDP 검사 및 익명화 템플릿 관리

Model Armor

roles/modelarmor.admin

안전 템플릿 만들기 및 관리

Agent Platform

roles/aiplatform.admin

Agent Runtime 워크로드 배포

Cloud Storage

roles/storage.admin

배포 및 고객 데이터 버킷 관리

IAM 관리

roles/resourcemanager.projectIamAdmin

에이전트 ID에 대한 프로젝트 수준 권한 바인딩

로그 및 감사

roles/logging.viewer

트레이스 및 감사 로그 검사

또는 roles/admin와 같은 광범위한 기본 역할이나 기존 역할 roles/owner를 사용합니다.

프로젝트에 액세스

이 Codelab에서는 단일 Google Cloud 프로젝트를 사용합니다. 구성 단계에서는 gcloud CLI 및 Linux 셸 명령어를 사용합니다.

먼저 Google Cloud 프로젝트 명령줄에 액세스합니다.

프로젝트 ID 설정

gcloud config set project SET_YOUR_PROJECT_ID_HERE

세션 인증

# login to gcloud cli
gcloud auth login
# login for gcloud api
gcloud auth application-default login

셸 환경 변수 설정

# set custom var for slug (eg, "foo") and region preference
export SLUG="foo"
export REGION="us-central1"
echo ${SLUG}
echo ${REGION}
# create project vars (automatic)
export PROJ_ID=$(gcloud config list --format="value(core.project)")
export PROJ_NO=$(gcloud projects describe ${PROJ_ID} --format="value(projectNumber)")
export ORG_ID=$(gcloud projects get-ancestors ${PROJ_ID} --format="value(id)" | tail -n 1)
export USER_IDENTITY=$(gcloud config get-value account)
echo ${PROJ_ID}
echo ${PROJ_NO}
echo ${ORG_ID}
echo ${USER_IDENTITY}
# create resource vars (automatic)
export AGW_NAME="agw-${SLUG}-${REGION}-cta"
export AGW_URI="projects/${PROJ_ID}/locations/${REGION}/agentGateways/${AGW_NAME}"
export RE_AGENT_NAME="agent-crm"
export RE_AGENT_ID_SET="principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJ_NO}"
export STAGING_BUCKET="agent-staging-${PROJ_NO}"
export DATA_BUCKET="customer-data-${PROJ_NO}"
export MCP_URL="https://storage.mtls.googleapis.com/storage/mcp"
echo ${AGW_NAME}
echo ${AGW_URI}
echo ${RE_AGENT_NAME}
echo ${RE_AGENT_ID_SET}
echo ${STAGING_BUCKET}
echo ${DATA_BUCKET}
echo ${MCP_URL}
# create local dir for config files
mkdir -p cfg

Google Cloud SDK의 자체 관리 설치를 실행하는 경우 (즉, Cloud Shell 외부) 구성요소를 최신 버전으로 업데이트합니다.

# update gcloud cli
gcloud components update

API 서비스 사용 설정

# enable google apis (agent platform bundle, part 1)
gcloud services enable \
  agentregistry.googleapis.com \
  aiplatform.googleapis.com \
  apphub.googleapis.com \
  apptopology.googleapis.com \
  cloudapiregistry.googleapis.com \
  cloudtrace.googleapis.com \
  compute.googleapis.com \
  dataform.googleapis.com \
  iam.googleapis.com \
  iamconnectors.googleapis.com \
  iap.googleapis.com \
  logging.googleapis.com \
  modelarmor.googleapis.com \
  monitoring.googleapis.com \
  networksecurity.googleapis.com \
  networkservices.googleapis.com \
  notebooks.googleapis.com \
  observability.googleapis.com
# enable google apis (agent platform bundle, part 2)
gcloud services enable \
  securitycenter.googleapis.com \
  saasservicemgmt.googleapis.com \
  storage.googleapis.com \
  telemetry.googleapis.com \
  texttospeech.googleapis.com
# enable google apis (all the rest)
gcloud services enable \
  dlp.googleapis.com

이것으로 설정 부분이 마무리되었습니다. 다음은 게이트웨이 섹션입니다.

4. 게이트웨이

클라이언트-에이전트 (CLIENT_TO_AGENT) 모드로 작동하는 Google 관리형 Agent Gateway를 배포합니다. 아웃바운드 통화를 라우팅하기 위해 에이전트 등록소 연결이 필요한 이그레스 게이트웨이와 달리 인그레스 게이트웨이는 프런트엔드 계층에서 직접 바인딩되어 Agent Runtime을 타겟팅하는 인바운드 프롬프트의 인라인 시행 지점 역할을 합니다.

이그레스 정책은 게이트웨이 레이어에서 DRY_RUN 모드로 시작되는 경우가 많지만 인그레스 콘텐츠 거버넌스 (CONTENT_AUTHZ)는 시행 모드로 직접 배포됩니다. 세부적인 감사 전용 로깅 또는 능동적 차단은 대신 개별 모델 아머 템플릿 내에서 업스트림으로 제어됩니다.

게이트웨이 만들기

# create agent gateway config file
cat > cfg/${AGW_NAME}.yaml <<EOF
name: ${AGW_NAME}
protocols:
  - MCP
googleManaged:
  governedAccessPath: CLIENT_TO_AGENT
EOF
# import agent gateway config file (create gateway)
gcloud network-services agent-gateways import ${AGW_NAME} \
  --source="cfg/${AGW_NAME}.yaml" \
  --location=${REGION}

게이트웨이 확인

# list agent gateways (in region)
gcloud network-services agent-gateways list --location=${REGION}
# show agent gateway details (verify deployment state)
gcloud network-services agent-gateways describe ${AGW_NAME} --location=${REGION}

이로써 게이트웨이 부분이 마무리되었습니다. 다음은 Model Armor 섹션입니다.

5. Model Armor

SDP 템플릿

Model Armor 응답 템플릿에 사용할 Sensitive Data Protection (SDP) 검사 및 익명화 템플릿을 만듭니다. 이 구성은 수정할 미국 사회보장번호 (SSN)에 플래그를 지정합니다.

검사 템플릿 만들기

검사 템플릿은 데이터에서 민감한 정보 (US_SOCIAL_SECURITY_NUMBER)를 식별합니다.

# create inspect template
curl -fsS -X POST "https://dlp.googleapis.com/v2/projects/${PROJ_ID}/locations/${REGION}/inspectTemplates" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" -H "x-goog-user-project: ${PROJ_ID}" \
  -d @- << EOF
{
  "templateId": "agw-ssn-inspect-template",
  "inspectTemplate": {
    "displayName": "ssn inspect template",
    "inspectConfig": {
      "infoTypes": [
        { "name": "US_SOCIAL_SECURITY_NUMBER" }
      ],
      "minLikelihood": "POSSIBLE"
    }
  }
}
EOF

익명화 템플릿 만들기

익명화 템플릿은 검사 템플릿에서 발견된 주민등록번호에 적용할 변환을 지정합니다. 이 경우 변환은 주민등록번호를 정보 유형으로 바꾸는 것입니다.

# create de-identify template
curl -fsS -X POST "https://dlp.googleapis.com/v2/projects/${PROJ_ID}/locations/${REGION}/deidentifyTemplates" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" -H "x-goog-user-project: ${PROJ_ID}" \
  -d @- << EOF
{
  "templateId": "agw-ssn-redaction-template",
  "deidentifyTemplate": {
    "displayName": "SSN Redaction Template",
    "deidentifyConfig": {
      "infoTypeTransformations": {
        "transformations": [{
          "primitiveTransformation": { "replaceWithInfoTypeConfig": {} }
        }]
      }
    }
  }
}
EOF

SDP 템플릿 확인

# get (describe) inspect template
curl -fsS -X GET "https://dlp.googleapis.com/v2/projects/${PROJ_ID}/locations/${REGION}/inspectTemplates" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" -H "x-goog-user-project: ${PROJ_ID}" | jq
# get (describe) de-identify template
curl -fsS -X GET "https://dlp.googleapis.com/v2/projects/${PROJ_ID}/locations/${REGION}/deidentifyTemplates" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" -H "x-goog-user-project: ${PROJ_ID}" | jq

Model Armor 템플릿

Model Armor API의 기본 엔드포인트는 전역 (modelarmor.googleapis.com)입니다. 하지만 템플릿 및 평가 엔진의 Model Armor 리소스는 특정 지리적 리전으로 현지화됩니다. Model Armor의 Google Cloud 리전 엔드포인트 프록시 (REP) 또는 리전 API 엔드포인트는 https://modelarmor.${LOCATION}.rep.googleapis.com/입니다.

기본적으로 gcloud model-armor ...를 실행하면 CLI가 표준 전역 엔드포인트 (https://modelarmor.googleapis.com/)로 API 요청을 전송하려고 시도합니다. API 엔드포인트 재정의는 위치에 바인딩된 템플릿이 실제로 생성, 저장, 쿼리되는 리전 rep.googleapis.com API 계층으로 Model Armor의 모든 SDK/CLI HTTP 요청을 직접 리디렉션하는 데 사용됩니다.

API 재정의 설정

# set api endpoint override per location
gcloud config set api_endpoint_overrides/modelarmor "https://modelarmor.${REGION}.rep.googleapis.com/"

API 재정의 확인

# view api overrides on active gcloud config
gcloud config list api_endpoint_overrides/

요청 필터 템플릿 만들기

증오심 표현, 괴롭힘, 음란물, URI 삽입 공격을 차단하는 요청 필터 템플릿을 만듭니다. 정책 시행에 관한 자세한 이벤트 정보를 캡처하기 위해 로깅이 사용 설정됩니다. 요청이 차단될 때를 위해 맞춤 오류 코드와 메시지도 구성됩니다.

# create model armor template (request)
gcloud beta model-armor templates create ${AGW_NAME}-modar-req-template \
  --project=${PROJ_ID} \
  --location=${REGION} \
  --rai-settings-filters='[
    { "filterType": "HATE_SPEECH", "confidenceLevel": "MEDIUM_AND_ABOVE" },
    { "filterType": "HARASSMENT", "confidenceLevel": "MEDIUM_AND_ABOVE" },
    { "filterType": "SEXUALLY_EXPLICIT", "confidenceLevel": "MEDIUM_AND_ABOVE" }
  ]' \
  --pi-and-jailbreak-filter-settings-enforcement=enabled \
  --pi-and-jailbreak-filter-settings-confidence-level=medium-and-above \
  --template-metadata-enforcement-type=INSPECT_AND_BLOCK \
  --malicious-uri-filter-settings-enforcement=enabled \
  --template-metadata-custom-llm-response-safety-error-code=798 \
  --template-metadata-custom-llm-response-safety-error-message="ahoy! model response blocked by content filter :(" \
  --template-metadata-custom-prompt-safety-error-code=799 \
  --template-metadata-custom-prompt-safety-error-message="ahoy! the request was blocked by ye content filter... so rephrase the prompt and try again!" \
  --template-metadata-ignore-partial-invocation-failures \
  --template-metadata-log-operations \
  --template-metadata-log-sanitize-operations

응답 필터 템플릿 만들기

요청 필터 템플릿과 동일한 콘텐츠를 차단하는 응답 필터 템플릿을 만듭니다. DLP는 에이전트에서 클라이언트로 반환되는 메시지의 주민등록번호를 익명화하도록 응답 구간에서 구성됩니다.

# create model armor template (response)
gcloud beta model-armor templates create ${AGW_NAME}-modar-resp-template \
  --project=${PROJ_ID} \
  --location=${REGION} \
  --rai-settings-filters='[
      { "filterType": "HATE_SPEECH", "confidenceLevel": "MEDIUM_AND_ABOVE" },
      { "filterType": "HARASSMENT", "confidenceLevel": "MEDIUM_AND_ABOVE" },
      { "filterType": "SEXUALLY_EXPLICIT", "confidenceLevel": "MEDIUM_AND_ABOVE" }
  ]' \
  --malicious-uri-filter-settings-enforcement=enabled \
  --advanced-config-inspect-template=projects/${PROJ_ID}/locations/${REGION}/inspectTemplates/agw-ssn-inspect-template \
  --advanced-config-deidentify-template=projects/${PROJ_ID}/locations/${REGION}/deidentifyTemplates/agw-ssn-redaction-template \
  --template-metadata-enforcement-type=INSPECT_AND_BLOCK \
  --template-metadata-custom-llm-response-safety-error-code=798 \
  --template-metadata-custom-llm-response-safety-error-message="ahoy! model response blocked by content filter :(" \
  --template-metadata-custom-prompt-safety-error-code=799 \
  --template-metadata-custom-prompt-safety-error-message="ahoy! the request was blocked by ye content filter... so rephrase the prompt and try again!" \
  --template-metadata-ignore-partial-invocation-failures \
  --template-metadata-log-operations \
  --template-metadata-log-sanitize-operations

Model Armor 템플릿 확인

# list model armor templates
gcloud model-armor templates list --location=${REGION}
# show request filter template details
gcloud model-armor templates describe ${AGW_NAME}-modar-req-template --location=${REGION}
# show response filter template details
gcloud model-armor templates describe ${AGW_NAME}-modar-resp-template --location=${REGION}

IAM 권한

Model Armor는 Sensitive Data Protection (SDP) 서비스를 호출하기 위해 API를 호출합니다. SDP 검사 및 익명화 템플릿을 사용할 수 있도록 Model Armor 서비스 ID에 IAM 권한을 부여합니다.

Sensitive Data Protection의 IAM 정책 바인딩

# grant dlp (sdp) user role to the model armor service identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:service-${PROJ_NO}@gcp-sa-modelarmor.iam.gserviceaccount.com" \
  --role="roles/dlp.user"

IAM 권한 확인

# show iam policy for all dlp (sdp) roles on project
gcloud projects get-iam-policy ${PROJ_ID} \
  --flatten="bindings[].members" \
  --filter="bindings.role:roles/dlp" \
  --format="table(bindings.role:label=ROLE, bindings.members:label=PRINCIPAL_IDENTITY)"

이것으로 Model Armor 부분이 마무리되었습니다. 다음으로 승인 섹션으로 이동합니다.

6. 승인

IAM 권한

Model Armor를 사용하여 인라인 트래픽을 검사하려면 Service Extensions (DEP) 서비스 에이전트에 명시적 IAM 바인딩이 필요합니다 (동일한 프로젝트 내의 리소스 간에도 필요).

  • roles/modelarmor.calloutUser 및 roles/serviceusage.serviceUsageConsumer: 인라인 검사 콜아웃을 허용하기 위해 게이트웨이 프로젝트에 부여됩니다.
  • roles/modelarmor.user: Model Armor 템플릿에 대한 액세스 및 평가를 허용하기 위해 템플릿 프로젝트에 부여됩니다.

Model Armor의 IAM 정책 바인딩

# grant model armor callout user role to dep (service extension) service agent
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com" \
  --role="roles/modelarmor.calloutUser"

# grant service usage consumer role to dep (service extension) service agent
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com" \
  --role="roles/serviceusage.serviceUsageConsumer"

# grant model armor user role to dep (service extension) service agent
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com" \
  --role="roles/modelarmor.user"

IAM 권한 확인

# show iam policy on project for dep (service extension) service agent
gcloud projects get-iam-policy ${PROJ_ID} \
  --flatten="bindings[].members" \
  --filter="bindings.members:serviceAccount:service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com" \
  --format="table(bindings.members:label=PRINCIPAL_IDENTITY, bindings.role:label=ROLE)"

승인 확장 프로그램

에이전트 게이트웨이의 승인 확장 프로그램 구성은 수신 및 발신 페이로드 트래픽에 적용되는 통합 설정을 정의합니다. 구성에서는 리전 Model Armor API를 참조하고 model_armor_settings 메타데이터 필드를 사용하여 특정 요청 및 응답 템플릿에 연결되는 외부 처리 서비스 (service)를 정의합니다.

승인 확장 프로그램 만들기

# create authz extension config file (enforced mode)
cat > cfg/${AGW_NAME}-svc-ext-authz-modar.yaml <<EOF
name: ${AGW_NAME}-svc-ext-authz-modar
service: modelarmor.${REGION}.rep.googleapis.com
metadata:
  model_armor_settings: '[
    {
      "request_template_id": "projects/${PROJ_ID}/locations/${REGION}/templates/${AGW_NAME}-modar-req-template",
      "response_template_id": "projects/${PROJ_ID}/locations/${REGION}/templates/${AGW_NAME}-modar-resp-template"
    }
  ]'
failOpen: true
timeout: 5s
EOF

승인 확장 프로그램 가져오기

# import authz extension file
gcloud service-extensions authz-extensions import ${AGW_NAME}-svc-ext-authz-modar \
  --source=cfg/${AGW_NAME}-svc-ext-authz-modar.yaml \
  --location=${REGION}

승인 확장 프로그램 확인

# list authz extensions
gcloud service-extensions authz-extensions list --location=${REGION}
# show authz extension details
gcloud service-extensions authz-extensions describe ${AGW_NAME}-svc-ext-authz-modar \
  --location=${REGION}

승인 정책

승인 정책은 정책 프로필을 사용하여 실행되는 평가 유형을 결정합니다. 요청 기반 프로필 (REQUEST_AUTHZ)은 HTTP 헤더를 평가하지만 이 구성에서는 콘텐츠 기반 승인 프로필 (CONTENT_AUTHZ)을 사용하여 Model Armor 확장 프로그램을 게이트웨이에 바인딩하여 페이로드를 심층적으로 검사합니다.

승인 정책 만들기

# create authz policy config file (attach dry-run authz extension)
cat > cfg/${AGW_NAME}-authz-policy-modar.yaml <<EOF
name: ${AGW_NAME}-authz-policy-modar
target:
  resources:
    - "projects/${PROJ_ID}/locations/${REGION}/agentGateways/${AGW_NAME}"
policyProfile: CONTENT_AUTHZ
action: CUSTOM
customProvider:
  authzExtension:
    resources:
      - "projects/${PROJ_ID}/locations/${REGION}/authzExtensions/${AGW_NAME}-svc-ext-authz-modar"
EOF

승인 정책 가져오기

# import authz policy config file (enable authz policy)
gcloud beta network-security authz-policies import ${AGW_NAME}-authz-policy-modar \
  --source=cfg/${AGW_NAME}-authz-policy-modar.yaml \
  --location=${REGION}

인증 정책 확인

# list authz policies
gcloud beta network-security authz-policies list --location=${REGION}
# show authz policy details
gcloud beta network-security authz-policies describe ${AGW_NAME}-authz-policy-modar \
  --location=${REGION}

이로써 승인 부분이 마무리되었습니다. 다음으로 코드베이스 섹션으로 이동합니다.

7. 코드베이스

이 Codelab에 사용되는 에이전트 코드와 파일 데이터는 원격 Google Cloud GitHub 저장소에 유지됩니다. 다음 단계에서는 저장소를 로컬로 클론하고, 필요한 파일을 현재 작업 디렉터리 구조에 복사한 후 임시 파일을 정리합니다.

원격 아티팩트 가져오기

# clone remote repository to temp local dir
git clone https://github.com/GoogleCloudPlatform/cloud-networking-solutions.git ./temp_agw_cuj_arun_ingress_modar
# copy agent runtime and endpoint definitions to working project dir
cp -r temp_agw_cuj_arun_ingress_modar/codelabs/agw-cuj-arun-ingress-modar/agent-crm ./agent-crm
# remove temporary directory
rm -rf temp_agw_cuj_arun_ingress_modar

스테이징용 스토리지 버킷은 Agent Runtime에서 패키징된 에이전트 애플리케이션 코드와 종속 항목 아티팩트를 업로드, 빌드, 배포하는 데 사용됩니다.

스테이징용 스토리지 버킷 만들기

# create storage bucket
gcloud storage buckets create gs://${STAGING_BUCKET} --location=${REGION}

스토리지 버킷 확인

# list storage buckets
gcloud storage buckets list --format="value(storage_url)"

이로써 코드베이스 부분이 마무리되었습니다. 다음은 GCS 고객 데이터 섹션입니다.

고객 데이터

고객 데이터를 저장할 Cloud Storage 버킷을 만듭니다. 에이전트는 Cloud Storage MCP 엔드포인트를 호출하는 표준 Google Cloud 클라이언트 라이브러리를 사용하여 직접 읽습니다.

고객 데이터를 위한 스토리지 버킷 만들기

# create storage bucket
gcloud storage buckets create gs://${DATA_BUCKET} --location=${REGION}

스토리지 버킷 확인

# list storage buckets
gcloud storage buckets list --format="value(storage_url)"

고객 데이터 업로드

# copy local data to bucket
gcloud storage cp -r ./agent-crm/data/* gs://${DATA_BUCKET}/

고객 데이터 확인

# list bucket objects
gcloud storage ls gs://${DATA_BUCKET}/ --long

이로써 GCS 고객 데이터 부분이 마무리되었습니다. 다음은 ADK 에이전트 섹션입니다.

8. ADK 에이전트

Agent Runtime에 배포된 agent-crm ADK 에이전트는 Agent Platform과 통합하기 위해 배포 스크립트에서 다음 설정으로 구성됩니다.

  • "identity_type": types.IdentityType.AGENT_IDENTITY를 사용하여 에이전트의 고유한 SPIFFE 기반 보안 주체 ID를 프로비저닝합니다.
  • "client_to_agent_config": {"agent_gateway": "${AGW_URI}"}를 사용하여 에이전트의 모든 인바운드 트래픽을 에이전트 게이트웨이 정책 평가 및 시행 경로로 안내합니다.

또한 에이전트에는 보안 연결을 통해 GCS MCP 도구를 호출하기 위해 Cloud Storage MCP 서버의 mTLS MCP 서버 URL과 데이터 버킷 이름이 전달됩니다.

에이전트 배포

# deploy agent
uv --directory agent-crm run python3 deploy_agent.py \
  --project=${PROJ_ID} \
  --region=${REGION} \
  --src-dir=./agent \
  --staging-bucket=${STAGING_BUCKET} \
  --display-name="${RE_AGENT_NAME}" \
  --description="agent for customer data" \
  --mcp-server-url="${MCP_URL}" \
  --data-bucket=${DATA_BUCKET} \
  --enable-telemetry \
  --enable-agent-identity \
  --agent-gateway-ingress=${AGW_URI} \
  --allow-token-sharing

배포 확인

배포 주요 지표 가져오기

# fetch agent runtime (reasoning engine) resource id
export RE_ENGINE_ID=$(curl -s -X GET "https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  | jq -r --arg name "${RE_AGENT_NAME}" '.reasoningEngines[] | select(.displayName==$name) | .name | split("/") | last')
echo ${RE_ENGINE_ID}
# fetch agent runtime (reasoning engine) agent identity
export RE_AGENT_IDENTITY=$(gcloud agent-registry agents list \
  --project=${PROJ_ID} --location=${REGION} --filter="displayName=${RE_AGENT_NAME}" \
  --format="value(attributes.'agentregistry.googleapis.com/system/RuntimeIdentity'.principal)")
echo ${RE_AGENT_IDENTITY}

게이트웨이 구성 확인

# show agent runtime config details (gateway config)
curl -s -X GET "https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  | jq '{displayName: .displayName, name: .name, effectiveIdentity: .spec.effectiveIdentity, agentGatewayConfig: .spec.deploymentSpec.agentGatewayConfig}'

IAM 권한

에이전트 ID에 대한 IAM 정책 바인딩

# grant mcp tool user role to agent set (all agent runtime agents in project)
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_ID_SET}" \
  --role="roles/mcp.toolUser"
# grant storage object viewer role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/storage.objectViewer"

# grant aiplatform user role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/aiplatform.user"

# grant cloudtrace agent role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/cloudtrace.agent"

# grant cloud monitoring metric writer role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/monitoring.metricWriter"
# grant cloud logging log writer role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/logging.logWriter"

# grant telemetry writer role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/telemetry.writer"

# grant service usage consumer role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/serviceusage.serviceUsageConsumer"

# grant browser role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/browser"

IAM 권한 확인

# show agent identity roles on project
gcloud projects get-iam-policy ${PROJ_ID} \
  --flatten="bindings[].members" \
  --filter="bindings.members:${RE_AGENT_IDENTITY}" \
  --format="table(bindings.members.sub('^.*locations/', 'principal://agents.[...]/locations/'):label=PRINCIPAL_IDENTITY, bindings.role:label=ROLE)"
# show agent set roles on project
gcloud projects get-iam-policy ${PROJ_ID} \
  --flatten="bindings[].members" \
  --filter="bindings.members:${RE_AGENT_ID_SET}" \
  --format="table(bindings.members.sub('^.*platformContainer/', 'principalSet://agents.[...]/'):label=PRINCIPAL_IDENTITY, bindings.role:label=ROLE)"

이것으로 ADK 에이전트 부분이 마무리되었습니다. 다음은 테스트 섹션입니다.

9. 테스트

CLI에서 쿼리 제출

안전한 프롬프트 테스트

# post query to agent streamQuery
curl --no-buffer -s -X POST "https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}:streamQuery" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" -H "X-Goog-User-Project: ${PROJ_ID}" \
  -d @- <<EOF | jq -r --unbuffered 'if type == "array" then .[] else . end | select(.content.parts != null) | .content.parts[].text // empty'
{
  "input": {
    "message": "what are the names of our west customers?",
    "user_id": "test-user"
  }
}
EOF

다음과 같은 응답이 표시됩니다. '서부 고객은 밥 존슨과 앨리스 브라운입니다.'

수정 트리거 테스트

# post query to agent streamQuery
curl --no-buffer -s -X POST "https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}:streamQuery" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" -H "X-Goog-User-Project: ${PROJ_ID}" \
  -d @- <<EOF | jq -r --unbuffered 'if type == "array" then .[] else . end | select(.content.parts != null) | .content.parts[].text // empty'
{
  "input": {
    "message": "what are ssn's for bob johnson and alice brown?",
    "user_id": "test-user"
  }
}
EOF

다른 안전한 프롬프트 테스트

# post query to agent streamQuery
curl --no-buffer -s -X POST "https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}:streamQuery" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" -H "X-Goog-User-Project: ${PROJ_ID}" \
  -d @- <<EOF | jq -r --unbuffered 'if type == "array" then .[] else . end | select(.content.parts != null) | .content.parts[].text // empty'
{
  "input": {
    "message": "what are bob johnson's and alice brown's email addresses?",
    "user_id": "test-user"
  }
}
EOF

감사 로그

trace 로그 보기

원격 분석이 사용 설정되면 Agent Runtime은 사용자 쿼리, 도구 매개변수, 실행 흐름, 모델 선택 출력을 나타내는 구조화된 이벤트를 스트리밍합니다.

# show agent runtime (reasoning engine) telemetry and trace logs
gcloud logging read \
  "logName:\"projects/${PROJ_ID}/logs/aiplatform.googleapis.com%2Freasoning_engine_stdout\" AND labels.managed-by=\"reasoning-engine\"" \
  --project=${PROJ_ID} \
  --limit=15 \
  --format="table(
    timestamp.date(format=\"%I:%M:%S %p\", tz=LOCAL):label=TIME,
    trace.basename().sub('^(.{8}).*$', '\\1'):label=TRACE_ID,
    labels.\"event.name\".scope(-1):label=EVENT,
    jsonPayload.content.role:label=ROLE,
    jsonPayload.content.parts[0].text:label=TEXT_CONTENT,
    jsonPayload.content.parts[0].function_call.name:label=TOOL_CALL
  )"

TRACE_ID는 사용자 질문, 중간 도구 호출, 모델 결정을 하나의 타임라인으로 그룹화합니다.

TIME         TRACE_ID  EVENT                  ROLE   TEXT_CONTENT                                     TOOL_CALL
HH:MM:SS PM  3070a1fd  gen_ai.choice          model  Bob Johnson's SSN is 219-45-7895.
                                                     Alice Brown's SSN is 219-45-7896.
HH:MM:SS PM  3070a1fd  gen_ai.user.message    user
HH:MM:SS PM  3070a1fd  gen_ai.user.message    model                                                   read_customer_file
HH:MM:SS PM  3070a1fd  gen_ai.user.message    user
HH:MM:SS PM  3070a1fd  gen_ai.user.message    model                                                   read_customer_file
HH:MM:SS PM  3070a1fd  gen_ai.user.message    user
HH:MM:SS PM  3070a1fd  gen_ai.user.message    model                                                   list_customer_files
HH:MM:SS PM  3070a1fd  gen_ai.user.message    user   what are ssn's for bob johnson and alice brown?
HH:MM:SS PM  3070a1fd  gen_ai.system.message
HH:MM:SS PM  3070a1fd  gen_ai.choice          model                                                   read_customer_file

Model Armor 정리 로그 보기

이러한 로그는 트래픽이 Agent Gateway를 통과할 때 Model Armor가 실행하는 실시간 양방향 인라인 위협 및 삭제를 보여줍니다.

# show model armor logs
gcloud logging read \
  "logName:\"projects/${PROJ_ID}/logs/modelarmor.googleapis.com%2Fsanitize_operations\"" \
  --project=${PROJ_ID} \
  --limit=50 \
  --format="table(
    timestamp.date(format=\"%I:%M:%S %p\", tz=LOCAL):label=TIME,
    jsonPayload.sanitizationResult.sanitizationVerdict:label=VERDICT,
    jsonPayload.sanitizationInput.byteItem.byteData.decode(base64).decode(utf-8).sub('\n', ' \\\\\\\\n ').trailoff(123):label=INPUT_DATA
  )"

삭제되고 차단된 요청의 로그 항목을 확인합니다.

TIME         VERDICT                                 INPUT_DATA
HH:MM:SS PM  MODEL_ARMOR_SANITIZATION_VERDICT_ALLOW  Bob Johnson's email address is bob.j@example.com. \n Alice Brown's email address is alice.b...
HH:MM:SS PM  MODEL_ARMOR_SANITIZATION_VERDICT_ALLOW  what are bob johnson's and alice brown's email addresses?
HH:MM:SS PM  MODEL_ARMOR_SANITIZATION_VERDICT_BLOCK  6��
HH:MM:SS PM  MODEL_ARMOR_SANITIZATION_VERDICT_ALLOW  what are ssn's for bob johnson and alice brown?
HH:MM:SS PM  MODEL_ARMOR_SANITIZATION_VERDICT_ALLOW  Our west customers are: Bob Johnson and Alice Brown.
HH:MM:SS PM  MODEL_ARMOR_SANITIZATION_VERDICT_ALLOW  what are the names of our west customers?

이것으로 테스트 부분이 마무리되었습니다. 다음은 정리 섹션입니다.

10. 삭제

# remove agent iam bindings
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/storage.objectViewer"
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/aiplatform.user"
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/cloudtrace.agent"
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/monitoring.metricWriter"

# next
# remove more agent and agent set iam bindings
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/logging.logWriter"
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/telemetry.writer"
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/serviceusage.serviceUsageConsumer"
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/browser"

# next
# remove rest of iam bindings
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_ID_SET}" --role="roles/mcp.toolUser"
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="serviceAccount:service-${PROJ_NO}@gcp-sa-modelarmor.iam.gserviceaccount.com" --role="roles/dlp.user"

# next
# delete agent runtime (reasoning engine) agent
curl -s -X DELETE "https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}?force=true" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json"

# next
# delete storage
gcloud -q storage rm --recursive gs://${STAGING_BUCKET}
gcloud -q storage rm --recursive gs://${DATA_BUCKET}

# next
# delete authz resources
gcloud -q beta network-security authz-policies delete ${AGW_NAME}-authz-policy-modar --location=${REGION}

gcloud -q beta service-extensions authz-extensions delete ${AGW_NAME}-svc-ext-authz-modar --location=${REGION} --async

# next
# remove dep (service extensions) service agent iam bindings
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com" \
  --role="roles/modelarmor.calloutUser"

gcloud -q projects remove-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com" \
  --role="roles/serviceusage.serviceUsageConsumer"

gcloud -q projects remove-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com" \
  --role="roles/modelarmor.user"

# next
# delete model armor templates
gcloud -q model-armor templates delete ${AGW_NAME}-modar-resp-template --location=${REGION}
gcloud -q model-armor templates delete ${AGW_NAME}-modar-req-template --location=${REGION}

# unset model armor api endpoint override
gcloud config unset api_endpoint_overrides/modelarmor

# next
# delete sdp (dlp) templates
curl -fsS -X DELETE "https://dlp.googleapis.com/v2/projects/${PROJ_ID}/locations/${REGION}/deidentifyTemplates/agw-ssn-redaction-template" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "x-goog-user-project: ${PROJ_ID}"

curl -fsS -X DELETE "https://dlp.googleapis.com/v2/projects/${PROJ_ID}/locations/${REGION}/inspectTemplates/agw-ssn-inspect-template" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "x-goog-user-project: ${PROJ_ID}"

# next
# delete agent gateway ingress
gcloud -q network-services agent-gateways delete ${AGW_NAME} --location=${REGION} --async

# end

이것으로 정리 부분이 마무리되었습니다. 다음으로 결론 섹션으로 이동합니다.

11. 결론

수고하셨습니다 Agent Gateway를 성공적으로 배포하고 AI 에이전트로의 인바운드 트래픽을 관리했습니다.

cosmopup

Cosmopup은 Codelabs가 아주 좋다고 생각합니다.

다음 단계는 무엇인가요?

이 의견 양식을 사용하여 의견, 질문 또는 수정사항을 자유롭게 제공해 주세요.

감사합니다.