Cổng vào Agent Gateway đến Agent Runtime có Model Armor

1. Giới thiệu

Lớp học lập trình này khám phá hoạt động quản trị lưu lượng truy cập Cổng tác nhân cho các tác nhân AI được lưu trữ trên Thời gian chạy tác nhân.

Agent Gateway hoạt động ở chế độ truyền dữ liệu vào (từ ứng dụng đến nhân viên hỗ trợ) hỗ trợ việc quản lý hoạt động giao tiếp giữa các ứng dụng (người dùng cuối, nhân viên hỗ trợ trên máy tính, IDE mã hoá, nhân viên hỗ trợ ngang hàng, v.v.) và các nhân viên hỗ trợ được lưu trữ Agent Runtime. Chế độ này được dùng để bảo vệ các tác nhân khỏi các cuộc tấn công chèn câu lệnh từ bên ngoài hoặc nội dung gây hại do ứng dụng gửi đến. Tất cả lưu lượng truy cập đến đều được xử lý bằng các tiện ích uỷ quyền và Model Armor để bảo mật điểm truy cập mạng cho mọi hoạt động tương tác của nhân viên hỗ trợ.

Sản phẩm bạn sẽ tạo ra

  • Cổng tác nhân ở chế độ đầu vào (client-to-agent)
  • Tiện ích uỷ quyền Model Armor
  • Tác nhân ADK Thời gian chạy của tác nhân có danh tính của tác nhân
  • Dữ liệu tệp Cloud Storage được tác nhân truy vấn bằng MCP
  • Mẫu Model Armor để sàng lọc câu lệnh và câu trả lời của LLM
  • Mẫu Bảo vệ dữ liệu nhạy cảm để xoá thông tin nhận dạng khỏi dữ liệu

figure1

Hình 1. Cấu trúc lớp học lập trình

Kiến thức bạn sẽ học được

  • Cách triển khai Agent Gateway để sàng lọc lưu lượng truy cập đến một tác nhân
  • Cách định cấu hình tiện ích uỷ quyền và uỷ quyền Model Armor
  • Cách tạo và triển khai mẫu Model Armor tuỳ chỉnh
  • Cách tạo và triển khai mẫu Bảo vệ dữ liệu nhạy cảm tuỳ chỉnh
  • Cách kiểm thử và xác thực chính sách sàng lọc LLM

Bạn cần có

  • Một dự án trên Google Cloud đã bật tính năng thanh toán
  • Quyền IAM để cung cấp các dịch vụ mạng, tập dữ liệu BigQuery và tài nguyên Nền tảng tác nhân
  • Một trình bao tương thích với POSIX (bash hoặc zsh) đã cài đặt Google Cloud CLI (thành phần gcloud)
  • Công cụ dòng lệnh: git, curl, jq (trình xử lý JSON), Python 3 và uv (trình quản lý gói Python)

2. Khái niệm

Hướng lưu thông và vai trò của cổng

Agent Gateway hoạt động như một proxy mạng có nhận biết tác nhân, nhưng vai trò hoạt động của nó sẽ thay đổi tuỳ thuộc vào hướng lưu lượng truy cập:

  • Chế độ từ tác nhân đến mọi nơi (đầu ra): hoạt động như một proxy đầu ra. Khi một tác nhân gọi các công cụ cơ sở dữ liệu bên ngoài, máy chủ MCP bên thứ ba hoặc API, cổng ra sẽ quản lý việc khám phá dịch vụ, định tuyến, TLS hai chiều (mTLS), việc chèn động thông tin đăng nhập OAuth và quyền kiểm soát quyền truy cập vào các điểm cuối.
  • Chế độ từ máy khách đến tác nhân (đầu vào): hoạt động như một cổng bảo mật giao diện người dùng. Mục tiêu chính của lớp này là bảo vệ lối vào thời gian chạy thực thi tác nhân bằng cách chặn và làm sạch các câu lệnh ngôn ngữ tự nhiên đến trước khi chúng đến được mã tác nhân hoặc mô hình AI.

Đường dẫn truyền dữ liệu đến Thời gian chạy của tác nhân

Các yêu cầu của ứng dụng nhắm đến một tác nhân được lưu trữ trên Agent Runtime sẽ được chuyển đến điểm cuối API aiplatform.googleapis.com.

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

Luồng giao tiếp đến này đến điểm cuối API đại diện cho đường dẫn truyền dữ liệu từ máy khách đến tác nhân.

Để bảo mật đường dẫn chuyển tiếp do Google quản lý này, Agent Gateway tích hợp trực tiếp với Google Front End (GFE) ở lớp cơ sở hạ tầng phân phát API. Khi triển khai một tác nhân được quản lý cho Agent Runtime, Google sẽ liên kết chính sách uỷ quyền cổng vào với các yêu cầu đến của máy khách ở rìa mạng.

figure2

Hình 2. Quản trị lưu lượng truy cập với Agent Gateway đến Agent Runtime

Vì quá trình kiểm tra diễn ra ở cấp giao diện người dùng trước khi các yêu cầu đi vào Thời gian chạy của tác nhân, nên cấu trúc này không làm tăng thêm chi phí mạng hoặc độ trễ của bước nhảy nội bộ. Cơ sở hạ tầng giao diện người dùng sẽ tự động xử lý việc mở rộng quy mô, giúp bạn không cần quản lý dải IP nội bộ, bộ cân bằng tải hoặc các tuyến DNS tuỳ chỉnh.

Dọn dẹp mối đe doạ nội tuyến bằng Model Armor

Việc đánh giá thông tin xác thực của người gọi và thực thi chế độ kiểm soát quyền truy cập IAM (roles/aiplatform.user) được xử lý một cách tự nhiên theo cấp lưu trữ API aiplatform. Bản thân cổng vào không thực hiện việc uỷ quyền danh tính mà tập trung vào bảo mật nội dung bằng cách sử dụng các tiện ích uỷ quyền được định cấu hình bằng hồ sơ CONTENT_AUTHZ. Cổng này đóng vai trò là một điểm thực thi chính sách nội tuyến, chặn các câu lệnh bằng ngôn ngữ tự nhiên trong quá trình truyền tải trước khi chúng đến được vòng lặp suy luận của tác nhân AI hoặc LLM cơ bản.

Khi một câu lệnh của người dùng đến dịch vụ giao diện người dùng, cổng sẽ bắt đầu một lệnh gọi ext_proc (xử lý bên ngoài) đến dịch vụ tiện ích uỷ quyền Model Armor theo khu vực, dịch vụ này sẽ truyền lệnh gọi đến dataplane Model Armor. Model Armor đóng vai trò như một tường lửa ngôn ngữ tự nhiên, đánh giá văn bản dựa trên các mẫu đang hoạt động để quét tìm các nguy cơ về an toàn và bảo mật:

  • Tiêm câu lệnh gián tiếp và các nỗ lực vượt qua biện pháp bảo vệ
  • URL độc hại, ngôn từ độc hại hoặc nội dung không an toàn
  • Rò rỉ thông tin nhận dạng cá nhân (PII) và dữ liệu nhạy cảm

Nếu mẫu có các bộ lọc Bảo vệ dữ liệu nhạy cảm (SDP), thì Model Armor sẽ thực hiện thêm một lệnh gọi gRPC đến dịch vụ SDP trên đám mây. Cloud SDP kiểm tra tải trọng bằng cách sử dụng mẫu đã chỉ định, thực hiện mọi thao tác xoá thông tin nhận dạng hoặc chỉnh sửa theo yêu cầu và trả về kết quả đã được dọn dẹp lên chuỗi để được chuyển tiếp một cách an toàn.

Nếu phát hiện thấy lỗi vi phạm chính sách hoặc dữ liệu nhạy cảm chưa được chỉnh sửa trùng khớp, thì cổng sẽ chặn hoặc chỉnh sửa tải trọng ở rìa trước khi vào thời gian chạy. Do đó, ứng dụng tác nhân AI đang chạy vẫn được bảo vệ và không bao giờ xử lý các tải trọng độc hại hoặc chưa được chỉnh sửa.

Đến đây là kết thúc phần khái niệm... tiếp theo là phần Thiết lập.

3. Thiết lập

Các vai trò IAM bắt buộc

Bạn cần có các vai trò sau đây để tạo tài nguyên trong Lớp học lập trình này:

Danh mục

Vai trò IAM bắt buộc (ID)

Mô tả

Quản lý API

roles/serviceusage.serviceUsageAdmin

Bật các dịch vụ Google Cloud API

Kết nối mạng và cổng

roles/networkservices.admin

Cấp phép Agent Gateway

Tiện ích dịch vụ

roles/serviceextensions.admin

Định cấu hình tiện ích định tuyến

Bảo mật mạng

roles/networksecurity.admin

Triển khai chính sách uỷ quyền

Bảo vệ dữ liệu nhạy cảm

roles/dlp.admin

Quản lý mẫu kiểm tra và xoá thông tin nhận dạng SDP

Model Armor

roles/modelarmor.admin

Tạo và quản lý mẫu an toàn

Nền tảng tác nhân

roles/aiplatform.admin

Triển khai khối lượng công việc Thời gian chạy tác nhân

Cloud Storage

roles/storage.admin

Quản lý các vùng chứa dữ liệu khách hàng và việc triển khai

Quản trị IAM

roles/resourcemanager.projectIamAdmin

Liên kết quyền ở cấp dự án cho danh tính của tác nhân

Nhật ký và kiểm tra

roles/logging.viewer

Kiểm tra dấu vết và nhật ký kiểm tra

Ngoài ra, hãy sử dụng một vai trò cơ bản rộng như roles/admin hoặc vai trò cũ roles/owner.

Truy cập vào dự án của bạn

Lớp học lập trình này sử dụng một dự án trên đám mây duy nhất trên Google Cloud. Các bước định cấu hình sử dụng CLI gcloud và các lệnh shell Linux.

Bắt đầu bằng cách truy cập vào dòng lệnh của dự án trên Google Cloud:

Đặt mã dự án

gcloud config set project SET_YOUR_PROJECT_ID_HERE

Xác thực phiên

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

Đặt các biến môi trường của trình bao

# 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

Nếu bạn đang chạy bản cài đặt do chính mình quản lý của Google Cloud SDK (tức là bên ngoài Cloud Shell), hãy cập nhật các thành phần lên phiên bản mới nhất.

# update gcloud cli
gcloud components update

Bật dịch vụ 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

Đến đây là kết thúc phần thiết lập... tiếp theo là phần Cổng.

4. Cổng

Triển khai một Cổng tác nhân do Google quản lý hoạt động ở chế độ từ máy khách đến tác nhân (CLIENT_TO_AGENT). Không giống như các cổng ra yêu cầu mối liên kết Agent Registry để định tuyến các lệnh gọi đi, cổng vào liên kết trực tiếp ở cấp độ giao diện người dùng để đóng vai trò là điểm thực thi nội tuyến cho các lời nhắc đến nhắm đến Agent Runtime.

Mặc dù các chính sách truyền dữ liệu thường bắt đầu ở chế độ DRY_RUN tại lớp cổng, nhưng hoạt động quản trị nội dung đầu vào (CONTENT_AUTHZ) được triển khai trực tiếp ở chế độ bắt buộc. Thay vào đó, tính năng ghi nhật ký chỉ kiểm tra chi tiết hoặc chặn chủ động được kiểm soát ở nguồn trong từng mẫu Model Armor.

Tạo cổng

# 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}

Xác minh cổng

# 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}

Đến đây là kết thúc phần cổng... tiếp theo là phần Model Armor.

5. Model Armor

Mẫu SDP

Tạo một mẫu kiểm tra và xoá thông tin nhận dạng Bảo vệ dữ liệu nhạy cảm (SDP) để sử dụng trong mẫu phản hồi Model Armor. Cấu hình này gắn cờ Số an sinh xã hội của Hoa Kỳ (SSN) để chỉnh sửa.

Tạo mẫu kiểm tra

Mẫu kiểm tra xác định thông tin nhạy cảm (US_SOCIAL_SECURITY_NUMBER) trong dữ liệu.

# 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

Tạo mẫu xoá thông tin nhận dạng

Mẫu xoá thông tin nhận dạng chỉ định phép biến đổi sẽ được áp dụng cho số an sinh xã hội mà mẫu kiểm tra tìm thấy. Trong trường hợp này, quá trình chuyển đổi là thay thế SSN bằng loại thông tin.

# 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

Xác minh mẫu 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

Mẫu Model Armor

Điểm cuối mặc định cho Model Armor API là trên toàn cầu (modelarmor.googleapis.com). Tuy nhiên, các tài nguyên Model Armor cho mẫu và công cụ đánh giá được bản địa hoá theo các khu vực địa lý cụ thể. Google Cloud Regional Endpoint Proxy (REP) hoặc điểm cuối API theo khu vực cho Model Armor là https://modelarmor.${LOCATION}.rep.googleapis.com/.

Theo mặc định, khi chạy gcloud model-armor ..., CLI sẽ cố gắng gửi các yêu cầu API đến điểm cuối tiêu chuẩn trên toàn cầu (https://modelarmor.googleapis.com/). Lệnh ghi đè điểm cuối API được dùng để chuyển hướng tất cả các yêu cầu HTTP của SDK/CLI cho Model Armor trực tiếp đến cấp API rep.googleapis.com theo khu vực nơi các mẫu bị ràng buộc theo vị trí đó thực sự được tạo, lưu trữ và truy vấn.

Đặt chế độ ghi đè API

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

Xác minh chế độ ghi đè API

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

Tạo mẫu bộ lọc yêu cầu

Tạo một mẫu bộ lọc yêu cầu để chặn lời nói hận thù, hành vi quấy rối, nội dung khiêu dâm và các cuộc tấn công chèn URI. Tính năng ghi nhật ký sẽ được bật để ghi lại thông tin chi tiết về sự kiện liên quan đến việc thực thi chính sách. Mã lỗi và thông báo tuỳ chỉnh cũng được định cấu hình khi một yêu cầu bị chặn.

# 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

Tạo mẫu bộ lọc phản hồi

Tạo mẫu bộ lọc phản hồi để chặn nội dung giống như mẫu bộ lọc yêu cầu. DLP được định cấu hình trên chân phản hồi để xoá thông tin nhận dạng số An sinh xã hội cho những tin nhắn trả về cho khách hàng từ tác nhân.

# 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

Xác minh các mẫu 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}

Quyền IAM

Model Armor thực hiện các lệnh gọi API để gọi dịch vụ Bảo vệ dữ liệu nhạy cảm (SDP). Cấp cho danh tính dịch vụ Model Armor các quyền IAM để sử dụng các mẫu kiểm tra và xoá thông tin nhận dạng SDP.

Liên kết chính sách IAM cho tính năng Bảo vệ dữ liệu nhạy cảm

# 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"

Xác minh quyền 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)"

Đến đây là kết thúc phần Model Armor (Mô hình bảo vệ)... tiếp theo là phần Uỷ quyền.

6. Ủy quyền

Quyền IAM

Để kiểm tra lưu lượng truy cập nội tuyến bằng Model Armor, tác nhân dịch vụ Tiện ích dịch vụ (DEP) yêu cầu các liên kết IAM rõ ràng (ngay cả trên các tài nguyên trong cùng một dự án):

  • roles/modelarmor.calloutUser và roles/serviceusage.serviceUsageConsumer: Được cấp trên dự án cổng để cho phép chú thích kiểm tra nội tuyến.
  • roles/modelarmor.user: Được cấp cho dự án mẫu để cho phép truy cập và đánh giá các mẫu Model Armor.

Liên kết chính sách IAM cho Model Armor

# 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"

Xác minh quyền 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)"

Tiện ích uỷ quyền

Cấu hình tiện ích uỷ quyền cho Agent Gateway xác định các chế độ cài đặt tích hợp sẽ áp dụng cho lưu lượng tải trọng đến và đi. Cấu hình này xác định dịch vụ xử lý bên ngoài (service) tham chiếu đến Model Armor API theo khu vực và liên kết đến các mẫu yêu cầu và phản hồi cụ thể bằng cách sử dụng trường siêu dữ liệu model_armor_settings.

Tạo tiện ích uỷ quyền

# 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

Nhập tiện ích uỷ quyền

# 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}

Xác minh tiện ích uỷ quyền

# 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}

Chính sách ủy quyền

Các chính sách uỷ quyền sử dụng hồ sơ chính sách để xác định loại hoạt động đánh giá được thực hiện. Mặc dù các hồ sơ dựa trên yêu cầu (REQUEST_AUTHZ) đánh giá tiêu đề HTTP, nhưng cấu hình này sử dụng hồ sơ uỷ quyền dựa trên nội dung (CONTENT_AUTHZ) để liên kết tiện ích Model Armor với cổng để kiểm tra tải trọng sâu.

Tạo chính sách uỷ quyền

# 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

Nhập chính sách uỷ quyền

# 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}

Xác minh chính sách uỷ quyền

# 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}

Đến đây là kết thúc phần uỷ quyền... tiếp theo là phần toàn bộ mã nguồn.

7. Toàn bộ mã nguồn

Mã tác nhân và dữ liệu tệp được dùng cho Lớp học lập trình này được duy trì trong một kho lưu trữ từ xa trên GitHub của Google Cloud. Các bước sau đây sẽ sao chép kho lưu trữ cục bộ, sao chép các tệp cần thiết vào cấu trúc thư mục đang hoạt động hiện tại, sau đó dọn dẹp các tệp tạm thời.

Tìm nạp cấu phần phần mềm từ xa

# 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 sử dụng một vùng lưu trữ để dàn dựng nhằm tải lên, tạo và triển khai mã ứng dụng tác nhân được đóng gói cũng như các cấu phần phần phụ thuộc của mã đó.

Tạo bộ chứa lưu trữ để dàn dựng

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

Xác minh bộ nhớ

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

Đến đây là kết thúc phần toàn bộ mã nguồn... tiếp theo là phần Dữ liệu khách hàng GCS.

Dữ liệu khách hàng

Tạo một bộ chứa Cloud Storage để lưu trữ dữ liệu khách hàng. Tác nhân sẽ đọc trực tiếp bằng cách sử dụng thư viện ứng dụng Google Cloud tiêu chuẩn để gọi điểm cuối MCP của Cloud Storage.

Tạo vùng lưu trữ để lưu trữ dữ liệu khách hàng

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

Xác minh bộ nhớ

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

Tải dữ liệu khách hàng lên

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

Xác minh dữ liệu khách hàng

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

Đến đây là kết thúc phần Dữ liệu khách hàng của GCS... tiếp theo là phần tác nhân ADK.

8. Tác nhân ADK

Tác nhân ADK agent-crm được triển khai cho Thời gian chạy tác nhân được định cấu hình bằng các chế độ cài đặt sau trong tập lệnh triển khai để tích hợp với Nền tảng tác nhân:

  • "identity_type": types.IdentityType.AGENT_IDENTITY để cung cấp một danh tính chính dựa trên SPIFFE riêng biệt cho tác nhân
  • "client_to_agent_config": {"agent_gateway": "${AGW_URI}"} để chuyển hướng tất cả lưu lượng truy cập đến cho tác nhân đến đường dẫn đánh giá và thực thi chính sách của Cổng tác nhân

Tác nhân này cũng được truyền URL máy chủ MCP mTLS cho máy chủ MCP Cloud Storage và tên vùng chứa dữ liệu để gọi công cụ MCP GCS qua một kết nối bảo mật.

Triển khai tác nhân

# 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

Xác minh quá trình triển khai

Tìm nạp chỉ số quan trọng về việc triển khai

# 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}

Xác minh cấu hình cổng

# 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}'

Quyền IAM

Liên kết các chính sách IAM cho danh tính của tác nhân

# 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"

Xác minh quyền 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)"

Đến đây là kết thúc phần tác nhân ADK... tiếp theo là phần Kiểm thử.

9. Thử nghiệm

Gửi truy vấn từ CLI

Thử một câu lệnh an toàn

# 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

Bạn sẽ thấy phản hồi như... "Khách hàng ở miền Tây của chúng tôi là: Bob Johnson và Alice Brown."

Kiểm thử điều kiện kích hoạt tính năng che giấu thông tin

# 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

Thử một câu lệnh an toàn khác

# 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

Nhật ký kiểm tra

Xem nhật ký theo dõi

Khi đo từ xa được bật, Agent Runtime sẽ truyền trực tuyến các sự kiện có cấu trúc đại diện cho truy vấn của người dùng, tham số công cụ, quy trình thực thi và đầu ra lựa chọn mô hình.

# 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 sẽ nhóm truy vấn của người dùng, các lệnh gọi công cụ trung gian và quyết định của mô hình lại với nhau thành một dòng thời gian duy nhất:

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

Xem nhật ký dọn dẹp Model Armor

Các nhật ký này cho thấy mối đe doạ nội tuyến hai chiều theo thời gian thực và quá trình dọn dẹp do Model Armor thực hiện khi lưu lượng truy cập đi qua Agent Gateway.

# 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
  )"

Hãy chú ý đến mục nhập nhật ký cho yêu cầu đã được dọn dẹp và bị chặn.

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?

Đến đây là kết thúc phần Kiểm thử... tiếp theo là phần Dọn dẹp.

10. Dọn dẹp

# 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

Đến đây là kết thúc phần dọn dẹp... tiếp theo là phần Kết luận.

11. Kết luận

Xin chúc mừng! Bạn đã triển khai thành công Agent Gateway và kiểm soát lưu lượng truy cập đến một tác nhân AI!

cosmopup

Cosmopup cho rằng Codelabs rất tuyệt vời!

Bước tiếp theo là gì?

Bạn có thể thoải mái đưa ra ý kiến nhận xét, câu hỏi hoặc nội dung chỉnh sửa bằng cách sử dụng biểu mẫu phản hồi này.

Cảm ơn bạn!