Cymbal Support Agent — AI Agent Automated Deployment & Cloud Setup Guide (DEPLOYMENT_INSTRUCTION.md)¶
Target Audience: AI Agents (e.g., Antigravity, Gemini CLI,
Claude Code) and Google Cloud Solutions Architects. Scope: This document
covers 100% of required dependencies, GCP API enablement, SCRAPI installation,
infrastructure provisioning, container builds, and agent deployment for a
brand new GCP project. Primary Agent:
src/gecx_agent/cymbal_support_agent (Legacy agents cymbal-support-agent
and cymbal_ccaas_agent are strictly excluded).
0. Mandatory Proactive AI Agent Intake & Configuration Protocol¶
**MANDATE FOR ALL AI AGENTS (Antigravity / Gemini CLI / Claude
Code)**: When a user says "I want to deploy this solution" or invokes
deployment, DO NOT passively fail if .env or terraform.tfvars is missing
or empty. You MUST proactively execute this 3-step Intake Protocol FIRST:
Step 1: Proactive Interactive Interview¶
Ask the user the following 4 key deployment questions (presenting defaults where applicable):
- Target GCP Project ID: "Which Google Cloud Project ID would you like to
deploy this solution to? (e.g.,
<your-project-id>)" - Target Region: "Which GCP region should we use? (default:
us-central1)" - Agent Assist Conversation Profile: "Do you have an existing Dialogflow
Agent Assist Conversation Profile ID, or should Terraform automatically
create and deploy the
Cymbal DemoAI Coach for you? (default: Auto-create / leave empty)" - Secret Key: "Do you want to use the default cryptographic secret key
(
cymbal-secure-secret-999) or provide a custom key?"
Step 2: Automated Configuration Generation¶
Once the user answers (or confirms defaults), automatically generate or update both configuration files:
- Create/update the root
.envfile:
GCP_PROJECT_ID="<USER_TARGET_PROJECT_ID>"
GCP_REGION="<USER_REGION>"
CONVERSATION_PROFILE_ID="<USER_PROFILE_ID_OR_EMPTY>"
SECRET_KEY="<USER_SECRET_KEY>"
- Automatically synchronize
terraform/terraform.tfvars:
project_id = "<USER_TARGET_PROJECT_ID>"
region = "<USER_REGION>"
conversation_profile_id = "<USER_PROFILE_ID_OR_EMPTY>"
ces_service_agent = "service-508593438664@gcp-sa-ces.iam.gserviceaccount.com"
Step 3: Pre-Flight Confirmation Table¶
Display a clear summary table of the configured parameters to the user and request explicit confirmation ("Proceed to deploy?") before initiating Terraform provisioning or build scripts.
Autonomous / Sub-Agent Execution Mode: If a Target GCP Project ID
(e.g., <your-project-id>) is already specified in your prompt or task
description when you are invoked as an autonomous agent or sub-agent, you MUST
bypass interactive prompting, apply that Target Project ID directly to .env
and terraform.tfvars, and proceed immediately through all 7 Phases below
without waiting for user input.
1. Architectural Overview & Prerequisite Verification¶
This repository deploys an enterprise-grade Google Customer Engagement Suite
(GECX / CXAS) virtual agent (cymbal_support_agent) integrated with Google
Cloud Run, BigQuery, Dialogflow CX Agent Assist, and Vertex AI.
graph TD
User["Web Chat Widget - User"] -->|HTTPS - allUsers Anonymous| WebUI["GECX WEB_UI Channel: cymbal-support-agent-web"]
WebUI -->|Orchestration| GECX["GECX Agent: cymbal_support_agent"]
GECX -->|OpenAPI REST - OIDC| Webhook["Cloud Run: cymbal-gecx-webhook-PROJECT_ID"]
GECX -->|GCP Native MCP| BigQuery["BigQuery Dataset: ds1_PROJECT_ID_CLEAN.t1"]
Webhook -->|Backend Services| BQTickets["BigQuery: cymbal_demo_PROJECT_ID_CLEAN.support_tickets"]
GECX -->|RAG File Search| VertexRAG["Vertex AI RAG Corpora - GCS"]
AgentAssist["Dialogflow Agent Assist: Cymbal Demo Coach"] -->|Real-time AI Coaching| AssistUI["CCAI - Assist Workstation"]
2. Zero-to-Hero Clean Environment Runbook (New GCP Project)¶
To clone this repository and deploy to a brand new GCP project from scratch, follow these 7 chronological phases:
Phase 0: System & CLI Prerequisite Installation¶
Mandatory Billing & Quota Setup: To prevent quota exhaustion
or permission errors (e.g., 403 Forbidden on service usage APIs) during
deployment, you MUST complete these steps before initializing Terraform:
- Link a Billing Account: Confirm that the target GCP project has a valid billing account linked.
- Configure ADC Quota Project: Force your Application Default Credentials (ADC) to bill quota to the target project:
- Assign IAM Roles: The deploying identity requires the
OwnerorEditorrole, PLUSroles/resourcemanager.projectIamAdminto configure resource-level IAM policies for service accounts.
Ensure the local workstation or AI Agent container has the following system dependencies installed:
- Google Cloud SDK (
gcloudCLI) with beta components:
gcloud --version
gcloud components install beta --quiet
gcloud auth login
gcloud auth application-default login --quiet
gcloud auth application-default set-quota-project "${GCP_PROJECT_ID}" --quiet
gcloud config set billing/quota_project "${GCP_PROJECT_ID}" --quiet
- HashiCorp Terraform CLI (v1.3+ / v1.5+):
- Python 3.10+ & Virtual Environment:
- Git:
Phase 1: New GCP Project Mandatory API Enablement¶
Execute the following command to enable 100% of required Google Cloud API services on the target project:
gcloud services enable \
cloudresourcemanager.googleapis.com \
serviceusage.googleapis.com \
orgpolicy.googleapis.com \
iam.googleapis.com \
aiplatform.googleapis.com \
run.googleapis.com \
bigquery.googleapis.com \
cloudbuild.googleapis.com \
artifactregistry.googleapis.com \
dialogflow.googleapis.com \
discoveryengine.googleapis.com \
compute.googleapis.com \
iap.googleapis.com --project="${GCP_PROJECT_ID}" --quiet
Phase 1.2: Disable requireInvokerIam Organization Policy (for Public Cloud Run Access)¶
By default, enterprise Google Cloud organizations enforce
constraints/run.managed.requireInvokerIam, which blocks binding allUsers
(roles/run.invoker) on Cloud Run services.
Automated in ./scripts/setup.sh: Our deployment script
(./scripts/setup.sh) automatically checks and resets both
constraints/run.managed.requireInvokerIam and
constraints/iam.allowedPolicyMemberDomains (allowAll: true) via Org Policy
V2 before running Terraform! You do not need to run manual org-policy commands
if you use ./scripts/setup.sh.
Phase 2: Environment Variable Configuration (.env)¶
Create a .env file in the repository root containing your target project
settings:
GCP_PROJECT_ID="YOUR_NEW_GCP_PROJECT_ID"
GCP_REGION="us-central1"
CONVERSATION_PROFILE_ID=""
SECRET_KEY="cymbal-secure-secret-999"
(Note: Leave CONVERSATION_PROFILE_ID empty so Terraform automatically creates
and deploys the Cymbal Demo AI Coach Conversation Profile).
Phase 3: SCRAPI (cxas-scrapi) & Python Package Installation¶
This project relies on SCRAPI (cxas-scrapi) — the canonical Google
Customer Experience Agent Studio CLI and Python SDK (cxas). To install SCRAPI
and all required backend dependencies (FastAPI, uvicorn,
google-cloud-bigquery, google-cloud-dialogflow):
# 1. Create and activate a clean Python virtual environment
python3 -m venv venv
source venv/bin/activate
# 2. Upgrade pip and install all required packages from requirements.txt
# Note: If your local pip mirror lacks cxas-scrapi, pass --index-url=https://pypi.org/simple/
pip install --upgrade pip
pip install --index-url=https://pypi.org/simple/ --require-hashes -r scripts/requirements.txt
# 3. Verify SCRAPI (cxas CLI) installation
./venv/bin/cxas --help
Why this matters: scripts/requirements.txt includes cxas-scrapi, which
provides both the terminal command ./venv/bin/cxas and the cxas_scrapi
Python API imported by our deployment scripts.
3. Infrastructure as Code (Terraform Provisioning)¶
Zero-Mock Policy & Safe IaC Mandate: Never execute
terraform destroy without explicit human authorization. Always review
terraform plan before applying.
Step-by-Step Execution¶
cd terraform
terraform init
terraform validate
terraform plan -out=tfplan
terraform apply tfplan
cd ..
What This Terraform Provisions¶
-
BigQuery Datasets & Tables:
-
cymbal_demo_${GCP_PROJECT_ID_UNDERLINE}.support_tickets: Primary CRM ticketing database. (WhereGCP_PROJECT_ID_UNDERLINEis the project ID with hyphens replaced with underscores). -
ds1_${GCP_PROJECT_ID_UNDERLINE}.t1: Operational metrics table (c1STRING,c2STRING,c3INTEGER) queried directly bycymbal_support_agent. -
Dialogflow Agent Assist Conversation Profile:
-
Automatically deploys
terraform/assets/coaches/ai-coach.json(Cymbal DemoAI Coach, version2.5,en-US,END_OF_UTTERANCEtrigger) whenCONVERSATION_PROFILE_IDis empty. -
Cloud Run Microservices:
-
cymbal-gecx-webhook-${GCP_PROJECT_ID}: OpenAPI tool execution backend. -
cymbal-bff-web-${GCP_PROJECT_ID}: Customer-facing Web portal and WebRTC intake interface. -
IAM Role Bindings:
-
Grants
roles/bigquery.dataViewerandroles/bigquery.jobUserto the GECX/CES service agent (service-NUMBER@gcp-sa-ces.iam.gserviceaccount.com). - Grants
roles/run.invokertoallUsers(anonymous access) on both Cloud Run services so external users and chat widgets can connect without 403 errors.
4. Container Build & Cloud Run Service Configuration¶
Rebuild and deploy all application containers using the canonical setup script:
- Why
./scripts/setup.sh? It checks and resets Org Policy constraints (requireInvokerIamandallowedPolicyMemberDomains), computes a SHA-256 content hash ofsrc/, pushes immutable container tags (:sha-xxxx) to Artifact Registry, applies Terraform, and automatically deploys and links theCymbal DemoAI Coach Generator to your Conversation Profile!
5. Deploying the Agent (cymbal_support_agent)¶
To deploy the GECX agent and configure the web widget channel, execute:
Automatic Actions Performed by deploy_agent.py¶
- Webhook URL Auto-Resolution: Queries
gcloud run services describe cymbal-gecx-webhook-${GCP_PROJECT_ID}to obtain the live HTTPS URL. - OpenAPI Schema Generation: Reads
open_api_schema.template.yaml, substitutes${webhook_cloud_run_url}, and generates production-ready.yamlfiles. - GECX App Bootstrapping & SCRAPI Push (
cxas push): Automatically checks ifcymbal-support-agentexists; if not, executescxas createfirst to prevent 500 API errors, then synchronizessrc/gecx_agent/cymbal_support_agentwith Google Customer Engagement Suite usingcxas_scrapi. - Channel Creation & Public Access Enablement: Creates the
WEB_UIchannel (cymbal-support-agent-web) and updates its security settings to enable public access.
5.3 Deploy and Link Dialogflow AI Coach Generator (manage_agent_assist_coaches.py)¶
To provision the real-time generative AI Coach (Cymbal Demo, version 2.5)
and attach it to your Dialogflow Conversation Profile:
# 1. Deploy the AI Coach Generator brain from JSON
python3 scripts/manage_agent_assist_coaches.py deploy \
--project="${GCP_PROJECT_ID}" \
--config="terraform/assets/coaches/ai-coach.json" \
--id="generator-ai-coach"
# 2. Link the Generator to the Conversation Profile
python3 scripts/manage_agent_assist_coaches.py link \
--project="${GCP_PROJECT_ID}" \
--profile="Cymbal Coach - Cymbal Demo" \
--generator="projects/${GCP_PROJECT_ID}/locations/global/generators/generator-ai-coach"
Automated in ./scripts/setup.sh: Our deployment script
(./scripts/setup.sh) automatically executes
manage_agent_assist_coaches.py deploy and link after Terraform finishes!
You only need to run these commands manually if you skipped setup.sh or want
to re-link your coach.
6. Critical Tricks & Traps (Lessons Learned)¶
The AI Agent MUST observe these 14 critical architectural rules and workarounds:
Trick 1: The .template.yaml OpenAPI Webhook URL Resolution Trick¶
- The Trap: GECX OpenAPI tool schemas require an absolute URL in
servers[].url. Hardcoding URLs causes failures across different GCP projects or Cloud Run deployments. - The Trick: Keep
*.template.yamlfiles in Git containing- url: ${webhook_cloud_run_url}. Inscripts/deploy_agent.py, theupdate_openapi_schema_urls()function dynamically fetches the live Cloud Run webhook URL viagcloudand generatesopen_api_schema.yamlbefore runningcxas push.
Trick 2: Public Access Security FieldMask Trap on WEB_UI Channels¶
- The Trap: Calling
deployments_client.create_deployment(...)creates a Web UI channel with public access disabled by default, resulting in400 FAILED_PRECONDITION: Public access is not enabled for the deploymentwhen users open the chat widget. - The Trick: Immediately after channel creation, invoke
deployments_client.update_deployment(...)withFieldMask(paths=["channel_profile.web_widget_config.security_settings.enable_public_access"])and setenable_public_access = True.
Trick 3: Dynamic GCP Project ID Lookup in Python Tools¶
- The Trap: Python tool functions (e.g.,
query_bigquery_metrics) copied from demo repos often have hardcoded project IDs ("robertortega-ai-demo"). - The Trick: Use runtime detection via the Google Cloud Metadata Server
Bridge
(
http://metadata.google.internal/computeMetadata/v1/project/project-idwithMetadata-Flavor: Google) with a fallback toos.getenv("GCP_PROJECT_ID").
Trick 4: BigQuery MCP Toolset IAM Authorization¶
- The Trap:
toolsets/bigquery_mcp/bigquery_mcp.jsonauthenticates viaserviceAgentIdTokenAuthConfig. If the GECX service account lacks IAM permissions, BigQuery tool calls fail silently. - The Trick: Ensure
terraform/main.tfexplicitly grantsroles/bigquery.dataViewerandroles/bigquery.jobUsertoserviceAccount:${var.ces_service_agent}.
Trick 5: Anonymous Public Access (allUsers) on Cloud Run Services¶
- The Trap: If Cloud Run IAM invokers are restricted to a specific user
email or service account, external visitors or developers testing via browser
or curl encounter
403 Forbidden. - The Trick: In
terraform/main.tf, configuremember = "allUsers"ongoogle_cloud_run_v2_service_iam_member.gecx_invokerandweb_invokerto permit anonymous public HTTP access.
Trick 6: Dialogflow Conversation Profile Auto-Creation vs Override & Dynamic Environment Variable Fallback¶
- The Trap: If
var.conversation_profile_idis non-empty, Terraform should not attempt to re-create or collide with an existing manual profile. Conversely, ifvar.conversation_profile_idis empty (""), settingCONVERSATION_PROFILE_ID = var.conversation_profile_idon the Cloud Run web service results in an empty string at runtime, causingConfiguration error: CONVERSATION_PROFILE_ID environment variable is missingwhen human agents answer Agent Assist calls. - The Trick: In
terraform/conversation_profiles.tf, usefor_each = var.conversation_profile_id == "" ? local.coaches : {}to auto-provision the AI Coach profile only when omitted. Interraform/main.tf, configure the Cloud Run environment variable using a ternary fallback:value = var.conversation_profile_id != "" ? var.conversation_profile_id : google_dialogflow_conversation_profile.coach_profiles["ai-coach"].id.
Trick 7: Terraform null_resource Build Triggering¶
- The Trap: Editing Python code in
src/does not automatically trigger Cloud Run container rebuilds in Terraform if the Docker tag is unchanged. - The Trick: In
terraform/main.tf, computelocal.image_tagfrom a SHA256 content hash ofsrc/and includeimage_tag = local.image_taginresource "null_resource" "build_image"triggers.
Trick 8: Python Virtual Environment Isolation for SCRAPI (cxas)¶
- The Trap: Calling global
cxasor running scripts without activatingvenvcan cause package import failures. - The Trick: Always activate
./venv/bin/activateor use./venv/bin/python3. The deployment script importscxas_scrapidirectly from the virtual environment.
Trick 9: Organization Policy run.managed.requireInvokerIam Blocking allUsers¶
- The Trap: When Terraform attempts to bind
member = "allUsers"to Cloud Run services (gecx_invokerandweb_invoker), GCP may throw an Organization Policy violation ifconstraints/run.managed.requireInvokerIamis enforced by the parent organization. - The Trick: Before running
terraform apply,./scripts/setup.shautomatically resetsconstraints/run.managed.requireInvokerIamandconstraints/iam.allowedPolicyMemberDomainsvia Org Policy V2 on the target project.
Trick 10: Terraform Provider Limitation on AI Coach Generators¶
- The Trap: Terraform
google_dialogflow_conversation_profileandgoogle_dialogflow_generatorin thegoogle-betaprovider do not supportagent_coaching_contextor AI Coach generator attachments in HCL syntax. Relying solely on Terraform creates an empty Conversation Profile without AI coaching enabled. - The Trick:
./scripts/setup.shautomatically executesscripts/manage_agent_assist_coaches.py deployandlinkpost-Terraform to programmatically provision the AI Coach Generator via the Dialogflow v2beta1 Python SDK and bind it tohuman_agent_assistant_config.
Trick 11: Brand New Project GECX App Bootstrapping (cxas create before cxas push)¶
- The Trap: On a brand new GCP project, calling
cxas push --to projects/.../apps/cymbal-support-agentfails with HTTP 500 (internal error 13) if the app resource does not exist yet. - The Trick: In
scripts/deploy_agent.py, theensure_gecx_app_exists()function checks for app existence viacxas apps getbefore pushing; if not found, it automatically executescxas createto initialize the app skeleton.
Trick 12: Dialogflow Conversation Profile Display Name vs Resource ID Resolution in manage_agent_assist_coaches.py link¶
- The Trap: When running
manage_agent_assist_coaches.py link --profile="Cymbal Coach - Cymbal Demo", the command can fail with404 NOT_FOUND: Session Profile [Cymbal Coach - Cymbal Demo] not found in projectif the script treats--profileas an alphanumeric resource ID rather than a display name. Furthermore, inscripts/setup.sh, appending|| trueto thelinkcommand masks this failure and silently leaves the Conversation Profile without AI coaching enabled. - The Trick: In
scripts/manage_agent_assist_coaches.py,link_generator_to_profile()must resolve display names by queryingConversationProfilesClient.list_conversation_profiles(parent=parent)when--profiledoes not contain a slash (/), matching againstp.display_name(orp.name), and using the canonical resource.name(projects/<project_id>/locations/global/conversationProfiles/<id>).
Trick 13: SCRAPI (cxas) CLI Syntax for apps get and IAM Propagation Delay / 409 Resource Already Exists Handling¶
- The Trap: Calling
cxas get --app <app_id>fails with exit code 2 because'get'is not a valid top-level command in SCRAPI (cxas), and--appis an invalid option for checking apps. This causesensure_gecx_app_exists()to falsely assume the app does not exist and callcxas create, which then crashes withHTTP 409 Resource already exists. On a brand new project, callingcxas createimmediately after API enablement can also fail withHTTP 500 internal error 13due to GCP regional IAM/identity propagation latency. - The Trick: In
scripts/deploy_agent.py, writecheck_cmd = ["apps", "get", f"projects/{project_id}/locations/{location}/apps/{app_id}"](using positional app resource name). In thecxas createexception block, implement a 3-attempt retry loop with 15-second backoff for GCP IAM propagation delay, and explicitly catch'already exists'inres.stderrso the script can proceed without crashing.
Trick 14: Application Default Credentials (ADC) Quota Project Ordering on Brand New Projects¶
-
The Trap: When running
scripts/manage_agent_assist_coaches.py deployandlinkviascripts/setup.sh, the Dialogflow API calls may fail with:403 Your application is authenticating by using local Application Default Credentials. The dialogflow.googleapis.com API requires a quota project, which is not set by default.If
gcloud auth application-default set-quota-project "${GCP_PROJECT_ID}"is executed before Phase 1 (API Enablement), it fails withSERVICE_DISABLEDforcloudresourcemanager.googleapis.com. -
The Trick: Execute
gcloud auth application-default set-quota-project "${GCP_PROJECT_ID}" --quietafter Phase 1 (gcloud services enable ...) has enabled the Cloud Resource Manager API on the target project, or ensurescripts/setup.shsets the ADC quota project immediately after enabling APIs.
7. Post-Deployment Functional Verification¶
After deployment completes, perform the following end-to-end functional verifications on your newly provisioned environment:
- Retrieve the Live Web Application URL:
gcloud run services describe cymbal-bff-web-${GCP_PROJECT_ID} --region=us-central1 --format='value(status.url)'
-
End-to-End Customer Engagement & Voice Coaching Verification:
-
Web Chat Agent Interaction: Open the retrieved HTTPS URL in a browser, click the bottom-right chat bubble (
Cymbal Support), and verify conversational responses fromcymbal_support_agent. - BigQuery Tool Invocation (
ds1_${GCP_PROJECT_ID_UNDERLINE}.t1): Ask the agent for live operational metrics or ticket intake to verify real-time SQL execution againstds1_${GCP_PROJECT_ID_UNDERLINE}.t1andcymbal_demo_${GCP_PROJECT_ID_UNDERLINE}.support_tickets. - Agent Assist Real-Time Coaching: Initiate a WebRTC voice call and open the
agent assist workstation to confirm Dialogflow CX Agent Assist streams live
suggestions from the
Cymbal DemoAI Coach as the customer speaks.
8. Restrictive Environment Troubleshooting & State Hygiene¶
1. Quota & Billing Project Override Failures (403 Forbidden)¶
- The Issue: In some restrictive GCP folders or organizations, using target
project billing quota override (
user_project_override = true) is prohibited for the deploying identity. This will cause Terraform init or plan to fail with quota configuration errors. -
The Fix: If you are blocked by organization policies, you must manually edit
terraform/main.tfto setuser_project_override = falseand removebilling_projectfrom the provider blocks:
2. Terraform State Contamination & Access Errors¶
- The Issue: If you previously ran Terraform against a different project and
then changed
project_idinterraform.tfvars, Terraform will attempt to read/refresh resources from the old project, failing with 403 Forbidden because you may no longer have permissions (or are using a different context). -
The Fix: Reset your local state environment to start fresh: