{"id":13370,"date":"2026-04-23T01:39:29","date_gmt":"2026-04-23T01:39:29","guid":{"rendered":"https:\/\/www.europesays.com\/ai\/13370\/"},"modified":"2026-04-23T01:39:29","modified_gmt":"2026-04-23T01:39:29","slug":"unlocking-data-insights-with-the-oracle-ai-database-agent-in-gemini-enterprise-part-2","status":"publish","type":"post","link":"https:\/\/www.europesays.com\/ai\/13370\/","title":{"rendered":"Unlocking Data Insights with the Oracle AI Database Agent in Gemini Enterprise: Part 2"},"content":{"rendered":"<p>Introduction<\/p>\n<p><a href=\"https:\/\/blogs.oracle.com\/developers\/unlocking-data-insights-with-the-oracle-ai-database-agent-in-gemini-enterprise-part-1\" data-type=\"link\" data-id=\"https:\/\/blogs.oracle.com\/developers\/unlocking-data-insights-with-the-oracle-ai-database-agent-in-gemini-enterprise-part-1\" rel=\"nofollow noopener\" target=\"_blank\">Part 1<\/a> showed the fastest way to connect Gemini Enterprise to live Oracle data without building a custom natural-language-to-SQL stack. This post is for developers who want to extend that foundation. Here, the Oracle AI Database Agent remains the governed Oracle data plane and SQL execution engine, while a custom ADK orchestrator running on Cloud Run adds composition: parallel data gathering, web enrichment, chart generation, and document delivery.<\/p>\n<p>That distinction matters. You are not replacing the Oracle AI Database Agent from <a href=\"https:\/\/blogs.oracle.com\/developers\/unlocking-data-insights-with-the-oracle-ai-database-agent-in-gemini-enterprise-part-1\" data-type=\"link\" data-id=\"https:\/\/blogs.oracle.com\/developers\/unlocking-data-insights-with-the-oracle-ai-database-agent-in-gemini-enterprise-part-1\" rel=\"nofollow noopener\" target=\"_blank\">Part 1<\/a>, and you are not copying Oracle data into a separate middleware layer just to make AI work. You are using the Oracle agent as one specialized agent inside a broader workflow, which allows you to give the model reliable, governed access to the system of record without building and maintaining custom data plumbing.<\/p>\n<p>By the end of this post, you will have a deployable reference architecture on Cloud Run that demonstrates how A2A lets multiple agents decompose a task, run in parallel, and synthesize a result that would be cumbersome to build inside a single agent.<\/p>\n<p>Target audience:<\/p>\n<p>Google Cloud developers building agentic applications with ADK<\/p>\n<p>Developers who want to use the Oracle AI Database Agent as a governed Oracle data-access component inside broader workflows<\/p>\n<p>Engineers evaluating A2A as an integration pattern for production systems<\/p>\n<p>What we are building<\/p>\n<p>The Oracle Sales Intelligence Hub is a developer reference application that can answer complex business questions about Oracle sales data by orchestrating five specialized agents. The Oracle AI Database Agent from <a href=\"https:\/\/blogs.oracle.com\/developers\/unlocking-data-insights-with-the-oracle-ai-database-agent-in-gemini-enterprise-part-1\" data-type=\"link\" data-id=\"https:\/\/blogs.oracle.com\/developers\/unlocking-data-insights-with-the-oracle-ai-database-agent-in-gemini-enterprise-part-1\" rel=\"nofollow noopener\" target=\"_blank\">Part 1<\/a> handles\u00a0Oracle data access;\u00a0the other agents add external context, visualization, synthesis, and delivery<\/p>\n<p>oracle_analyst Queries Oracle AI Database (Sales History schema) via the A2A remote agent<\/p>\n<p>web_analyst Searches the web for external market context using Gemini web search grounding<\/p>\n<p>charts_agent Generates a revenue bar chart and geographic map as PNG images<\/p>\n<p>synthesis_agent Combines all inputs into a structured executive briefing<\/p>\n<p>docs_writer_agent Uploads the full briefing to Cloud Storage and returns a signed URL<\/p>\n<p>A user types a single question such as, \u201cGive me a Q3 FY2025 briefing for the UK market\u201d and receives a briefing document with charts, market context, and a shareable link, all within a single Gemini Enterprise chat response.<\/p>\n<p>Agent topology<\/p>\n<p>The agents are arranged in a SequentialAgent pipeline with a ParallelAgent at the data gathering stage:<\/p>\n<p>root_agent (LlmAgent is the entry point)<br \/>\n\u2514\u2500\u2500 briefing_pipeline (SequentialAgent)<br \/>\n\u251c\u2500\u2500 data_gathering_team (ParallelAgent)<br \/>\n\u2502 \u251c\u2500\u2500 oracle_analyst this refers to Oracle Database AI<br \/>\nAgent[RemoteA2aAgent]<br \/>\n\u2502 \u2514\u2500\u2500 web_analyst for Gemini web search grounding<br \/>\n\u251c\u2500\u2500 charts_agent for bar chart png<br \/>\n\u251c\u2500\u2500 synthesis_agent to combine Oracle + web + charts<br \/>\n\u2514\u2500\u2500 docs_writer_agent to store in Cloud Storage<\/p>\n<p>The ParallelAgent runs oracle_analyst and web_analyst simultaneously. This is one of the key values of multi-agent: neither agent waits for the other, cutting total latency roughly in half compared to sequential execution.<\/p>\n<p>Prerequisites<\/p>\n<p>From <a href=\"https:\/\/blogs.oracle.com\/developers\/unlocking-data-insights-with-the-oracle-ai-database-agent-in-gemini-enterprise-part-1\" data-type=\"link\" data-id=\"https:\/\/blogs.oracle.com\/developers\/unlocking-data-insights-with-the-oracle-ai-database-agent-in-gemini-enterprise-part-1\" rel=\"nofollow noopener\" target=\"_blank\">Part 1<\/a> of this blog post<\/p>\n<p>Oracle agent URL: base URL of the Oracle NL2SQL A2A agent<\/p>\n<p>Oracle token URL: OAuth token endpoint for your database<\/p>\n<p>Oracle client ID and client secret : from the OAuth registration step<\/p>\n<p>Select AI profile: configured with the Sales History (SH) schema<\/p>\n<p>GCP setup requirements<\/p>\n<p>GCP project with the following APIs enabled:<\/p>\n<p>Vertex AI API<\/p>\n<p>Cloud Run API<\/p>\n<p>Cloud Build API<\/p>\n<p>Secret Manager API<\/p>\n<p>Cloud Storage API<\/p>\n<p>Maps Static API<\/p>\n<p>Service account with roles: Vertex AI User, Secret Manager Secret Accessor, Storage Object Admin, Logs Writer, Service Account Token Creator<\/p>\n<p>Cloud Storage bucket: multi-region recommended for signed URL delivery<\/p>\n<p>Google Maps API key: restricted to Maps Static API (GCP Console \u2192 APIs &amp; Services \u2192 Credentials)<\/p>\n<p>gcloud CLI installed and authenticated<\/p>\n<p>Project structure<\/p>\n<p>oracle-sales-hub\/<br \/>\n\u251c\u2500\u2500 orchestrator\/<br \/>\n\u2502 \u251c\u2500\u2500 demo_sales_hub\/ ADK agent package<br \/>\n\u2502 \u2502 \u251c\u2500\u2500 __init__.py<br \/>\n\u2502 \u2502 \u2514\u2500\u2500 agent.py all agent definitions<br \/>\n\u2502 \u251c\u2500\u2500 oracle_auth.py Oracle OAuth client factory<br \/>\n\u2502 \u251c\u2500\u2500 gcs_tool.py Cloud Storage upload<br \/>\n\u2502 \u251c\u2500\u2500 charts_tool.py Bar chart + Maps API map<br \/>\n\u2502 \u251c\u2500\u2500 main.py ADK FastAPI entry point<br \/>\n\u2502 \u251c\u2500\u2500 requirements.txt<br \/>\n\u2502 \u2514\u2500\u2500 Dockerfile<br \/>\n\u2514\u2500\u2500 infra\/<br \/>\n\u2514\u2500\u2500 deploy.sh Cloud Run deployment script<\/p>\n<p>Authentication note:\u00a0<a href=\"https:\/\/blogs.oracle.com\/developers\/unlocking-data-insights-with-the-oracle-ai-database-agent-in-gemini-enterprise-part-1\" data-type=\"link\" data-id=\"https:\/\/blogs.oracle.com\/developers\/unlocking-data-insights-with-the-oracle-ai-database-agent-in-gemini-enterprise-part-1\" rel=\"nofollow noopener\" target=\"_blank\">Part 1<\/a> described the managed user-facing flow, where queries run under each user\u2019s Oracle Database identity. This reference architecture introduces a developer-owned orchestrator on Cloud Run and uses service-to-service authentication between that orchestrator and the Oracle remote agent.<\/p>\n<p>Step 1: Oracle OAuth authentication<\/p>\n<p>The Oracle Database AI Agent uses OAuth 2.0 with the password grant (resource owner password credentials). This grant allows the orchestrator to obtain a token using a database username and password. The oracle_auth.py module implements an httpx.Auth class that fetches a Bearer token, caches it, and refreshes it 60 seconds before expiry. This is wired into ADK\u2019s RemoteA2aAgent via the a2a_client_factory parameter.<\/p>\n<p># oracle_auth.py (key excerpt)<br \/>\nclass OracleOAuthAuth(httpx.Auth):<br \/>\ndef __init__(self, token_url, client_id, client_secret, username,<br \/>\npassword):<br \/>\nself.token_url = token_url<br \/>\nself.client_id = client_id<br \/>\nself.client_secret = client_secret<br \/>\nself.username = username<br \/>\nself.password = password<br \/>\ndef _get_token(self) -&gt; str:<br \/>\nresp = client.post(self.token_url, data={<br \/>\n&#8220;grant_type&#8221;: &#8220;password&#8221;,<br \/>\n&#8220;username&#8221;: self.username,<br \/>\n&#8220;password&#8221;: self.password,<br \/>\n&#8220;client_id&#8221;: self.client_id,<br \/>\n&#8220;client_secret&#8221;: self.client_secret,<br \/>\n})<br \/>\nreturn resp.json()[&#8220;access_token&#8221;]<br \/>\ndef auth_flow(self, request):<br \/>\nrequest.headers[&#8220;Authorization&#8221;] = f&#8221;Bearer {self._get_token()}&#8221;<br \/>\nyield request<br \/>\ndef get_oracle_client_factory(token_url, client_id, client_secret,<br \/>\nusername, password):<br \/>\nauth = OracleOAuthAuth(token_url, client_id, client_secret, username,<br \/>\npassword)<br \/>\nasync_client = httpx.AsyncClient(<br \/>\ntimeout=httpx.Timeout(timeout=60.0),<br \/>\nauth=auth,<br \/>\nheaders={&#8220;Content-Type&#8221;: &#8220;application\/json&#8221;},<br \/>\n)<br \/>\nreturn<br \/>\nClientFactory(ClientConfig(httpx_client=async_client))<\/p>\n<p>This pattern is reusable for any A2A remote agent that requires non-Google OAuth. If you are integrating other third-party A2A agents, implement the same httpx. Auth subclass pattern with your own token endpoint and grant type.<\/p>\n<p>Step 2: Connecting to the Oracle NL2SQL A2A agent<\/p>\n<p>The Oracle Database AI Agent exposes an A2A endpoint at a path specific to Oracle\u2019s implementation. Oracle uses:<\/p>\n<p>ORACLE_AGENT_URL\/agents\/{agent-name}<\/p>\n<p>We pass a pre-built AgentCard object directly to RemoteA2aAgent instead of a URL. This bypasses the HTTP fetch entirely<\/p>\n<p>from a2a.types import AgentCard, AgentSkill,<br \/>\nAgentCapabilities<br \/>\ndef _make_oracle_remote_agent(name: str) -&gt; RemoteA2aAgent:<br \/>\nagent_card = AgentCard(<br \/>\nname=&#8221;Oracle AI Database Agent&#8221;,<br \/>\ndescription=&#8221;Queries Oracle Autonomous AI Database using natural<br \/>\nlanguage.&#8221;,<br \/>\nurl=ORACLE_AGENT_URL + AGENT_CARD_PATH,<br \/>\nversion=&#8221;1.0.0&#8243;,<br \/>\ndefault_input_modes=[&#8220;text\/plain&#8221;],<br \/>\ndefault_output_modes=[&#8220;text\/plain&#8221;],<br \/>\ncapabilities=AgentCapabilities(streaming=False),<br \/>\nskills=[AgentSkill(<br \/>\nid=&#8221;nl2sql&#8221;,<br \/>\nname=&#8221;NL2SQL Query&#8221;,<br \/>\ndescription=&#8221;Converts natural language to SQL and queries<br \/>\nOracle.&#8221;,<br \/>\ntags=[&#8220;oracle&#8221;, &#8220;sql&#8221;, &#8220;database&#8221;],<br \/>\nexamples=[&#8220;Show revenue by channel for Q3 FY2025&#8243;],<br \/>\n)],<br \/>\n)<br \/>\nreturn RemoteA2aAgent(<br \/>\nname=name,<br \/>\ndescription=&#8221;Queries Oracle Autonomous AI Database using natural<br \/>\nlanguage.&#8221;,<br \/>\na2a_client_factory=get_oracle_client_factory(<br \/>\ntoken_url=ORACLE_TOKEN_URL,<br \/>\nclient_id=ORACLE_CLIENT_ID,<br \/>\nclient_secret=ORACLE_CLIENT_SECRET,<br \/>\nusername=ORACLE_USERNAME,<br \/>\npassword=ORACLE_PASSWORD,<br \/>\n),<br \/>\nagent_card=agent_card, # pre-built object, no HTTP fetch<br \/>\nuse_legacy=True,<br \/>\n)<\/p>\n<p>It is important to note that NL2SQL and Oracle query execution still happen on the Oracle side. Your Cloud Run orchestrator does not translate natural language to SQL itself; it delegates Oracle data retrieval to the Oracle AI Database Agent through A2A.<\/p>\n<p>Step 3: Running Oracle and web search in parallel<\/p>\n<p>ADK\u2019s ParallelAgent runs all sub-agents simultaneously and collects their outputs into the shared conversation context. The data_gathering_team runs the Oracle query and the web search at the same time:<\/p>\n<p>data_gathering_team = ParallelAgent(<br \/>\nname=&#8221;data_gathering_team&#8221;,<br \/>\nsub_agents=[oracle_analyst, web_analyst],<br \/>\n)<\/p>\n<p>Each sub-agent prefixes its output with a clear header, so the downstream synthesis agent can reliably locate each data source.<\/p>\n<p>Step 4: Generating visual charts<\/p>\n<p>Gemini Enterprise renders PNG images inline in chat when the agent declares image\/png as an output mode in its agent card. The charts_tool.py module generates two images:<\/p>\n<p>Bar chart<\/p>\n<p>Generated with Matplotlib. Shows FY2024 vs FY2025 side-by-side bars per sales channel (Direct Sales, Tele Sales, etc.) with YoY percentage.<\/p>\n<p># charts_tool.py (bar chart excerpt)<br \/>\nbars_2024 = ax.bar(x &#8211; bar_width\/2, fy2024_vals, bar_width,<br \/>\nlabel=&#8221;FY2024&#8243;, color=&#8221;#B5D4F4&#8243;)<br \/>\nbars_2025 = ax.bar(x + bar_width\/2, fy2025_vals, bar_width,<br \/>\nlabel=&#8221;FY2025&#8243;, color=&#8221;#185FA5&#8243;)<br \/>\n# YoY % label above each FY2025 bar<br \/>\nfor i, (v24, v25) in enumerate(zip(fy2024_vals, fy2025_vals)):<br \/>\nyoy = ((v25 &#8211; v24) \/ v24) * 100<br \/>\ncolor = &#8220;#3B6D11&#8221; if yoy &gt;= 0 else &#8220;#A32D2D&#8221;<br \/>\nax.text(x[i] + bar_width\/2, v25, f&#8221;+{yoy:.1f}%&#8221;,<br \/>\nha=&#8221;center&#8221;, color=color, fontweight=&#8221;bold&#8221;)<\/p>\n<p>Revenue map for geographic distribution<\/p>\n<p>Generated using the Google Maps Static API. Country centroids are overlaid with circle markers sized and colored by revenue intensity. The map auto-centers and zooms based on which countries appear in the Oracle results.<\/p>\n<p># charts_tool.py (map excerpt)<br \/>\nparams = {<br \/>\n&#8220;size&#8221;: &#8220;800&#215;500&#8221;,<br \/>\n&#8220;scale&#8221;: &#8220;2&#8221;, # retina quality<br \/>\n&#8220;maptype&#8221;: &#8220;roadmap&#8221;,<br \/>\n&#8220;center&#8221;: f&#8221;{center_lat},{center_lon}&#8221;,<br \/>\n&#8220;zoom&#8221;: str(zoom),<br \/>\n&#8220;key&#8221;: MAPS_API_KEY,<br \/>\n}<br \/>\n# Add a circle marker per country, colored by revenue intensity<br \/>\nfor country, revenue in mapped.items():<br \/>\nlat, lon = COUNTRY_COORDS[country]<br \/>\nmarkers.append(f&#8221;color:{color}|size:{size}|{lat},{lon}&#8221;)<\/p>\n<p>Both images are uploaded to Cloud Storage and returned as signed URLs. The charts_agent runs after data_gathering_team completes, so it has access to the Oracle data it needs to parse revenue figures. The URLs appear in the Gemini Enterprise chat response as clickable links, users click to open the full-resolution chart in a new tab.<\/p>\n<p>Step 5: Synthesizing the briefing<\/p>\n<p>The synthesis agent receives three upstream inputs via the shared conversation context and combines them into a structured Markdown briefing. The output briefing follows a fixed structure with sections such as sales performance, geo distribution, market context, and recommendation<\/p>\n<p>Step 6: Delivering the briefing via Cloud Storage<\/p>\n<p>The docs_writer_agent calls upload_briefing_to_gcs() which converts the briefing to a HTML page and uploads it to your Cloud Storage bucket. For simplicity, we push the file to publicly readable storage. The final response in Gemini Enterprise chat includes the structured briefing text in Markdown, image links with bar chart and revenue map and a clickable URL to the full HTML report on Cloud Storage<\/p>\n<p>Step 7: Declaring image output in the agent card<\/p>\n<p>Declare image\/png as a supported output mode in the agent card JSON so Gemini Enterprise knows this agent can return image content. In practice, Gemini Enterprise renders images sent as binary blob attachments in the A2A response. Images referenced only as GCS URLs appear as clickable links rather than inline renders. For this demo, charts appear as clickable links that open in a new tab, and the full HTML briefing report includes the charts embedded inline.<\/p>\n<p>The agent card JSON you paste into Gemini Enterprise must include these fields:<\/p>\n<p>{<br \/>\n&#8220;protocolVersion&#8221;: &#8220;0.1&#8221;,<br \/>\n&#8220;name&#8221;: &#8220;Oracle Sales Intelligence Hub&#8221;,<br \/>\n&#8220;description&#8221;: &#8220;Answers business questions about Oracle sales<br \/>\ndata&#8230;&#8221;,<br \/>\n&#8220;url&#8221;: &#8220;https:\/\/YOUR_CLOUD_RUN_URL&#8221;,<br \/>\n&#8220;version&#8221;: &#8220;1.0.0&#8221;,<br \/>\n&#8220;defaultInputModes&#8221;: [&#8220;text\/plain&#8221;],<br \/>\n&#8220;defaultOutputModes&#8221;: [&#8220;text\/plain&#8221;, &#8220;image\/png&#8221;],<br \/>\n&#8220;capabilities&#8221;: { &#8220;streaming&#8221;: false },<br \/>\n&#8220;skills&#8221;: [{<br \/>\n&#8220;id&#8221;: &#8220;sales_briefing&#8221;,<br \/>\n&#8220;name&#8221;: &#8220;Sales Briefing&#8221;,<br \/>\n&#8220;description&#8221;: &#8220;Generates an executive sales briefing&#8230;&#8221;,<br \/>\n&#8220;tags&#8221;: [&#8220;sales&#8221;, &#8220;oracle&#8221;, &#8220;analytics&#8221;, &#8220;briefing&#8221;],<br \/>\n&#8220;examples&#8221;: [&#8220;Give me a Q3 FY2025 briefing for the UK market&#8221;,<br \/>\n&#8220;demo&#8221;]<br \/>\n}]<br \/>\n}<\/p>\n<p>Step 8: Serving the agent with to_a2a()<\/p>\n<p>ADK\u2019s get_fast_api_app() serves agents via ADK\u2019s own API format, not the A2A JSON-RPC standard that Gemini Enterprise expects. To expose a proper A2A-compliant endpoint, use ADK\u2019s to_a2a() function instead. Pass a Runner configured with session and artifact services:<\/p>\n<p># main.py<br \/>\nimport os<br \/>\nimport uvicorn<br \/>\nfrom google.adk.a2a.utils.agent_to_a2a import to_a2a<br \/>\nfrom google.adk.artifacts import InMemoryArtifactService<br \/>\nfrom google.adk.runners import Runner<br \/>\nfrom google.adk.sessions import InMemorySessionService<br \/>\nfrom demo_sales_hub.agent import root_agent<br \/>\nport = int(os.environ.get(&#8220;PORT&#8221;, &#8220;8080&#8221;))<br \/>\ncloud_run_url = os.environ.get(&#8220;CLOUD_RUN_URL&#8221;, &#8220;&#8221;)<br \/>\nhost = cloud_run_url.replace(&#8220;https:\/\/&#8221;, &#8220;&#8221;).replace(&#8220;http:\/\/&#8221;,<br \/>\n&#8220;&#8221;).rstrip(&#8220;https:\/\/blogs.oracle.com\/&#8221;)<br \/>\nrunner = Runner(<br \/>\nagent=root_agent,<br \/>\napp_name=root_agent.name,<br \/>\nsession_service=InMemorySessionService(),<br \/>\nartifact_service=InMemoryArtifactService(),<br \/>\n)<br \/>\napp = to_a2a(<br \/>\nagent=root_agent,<br \/>\nhost=host,<br \/>\nport=443,<br \/>\nprotocol=&#8221;https&#8221;,<br \/>\nrunner=runner,<br \/>\n)<br \/>\nif __name__ == &#8220;__main__&#8221;:<br \/>\nuvicorn.run(app, host=&#8221;0.0.0.0&#8243;, port=port)<\/p>\n<p>Step 9: Deploying to Cloud Run<\/p>\n<p>The deployment script handles all setup steps automatically. Fill in the placeholders at the top of the script and run it from your project root.<\/p>\n<p>Environment variables<\/p>\n<p>VariableDescriptionPROJECT_IDYour GCP project IDREGIONCloud Run region e.g. us-east4ORACLE_AGENT_URLBase URL of the Oracle AI Database AgentORACLE_TOKEN_URLOracle OAuth token endpointORACLE_CLIENT_IDOAuth client ID from Oracle registrationORACLE_CLIENT_SECRETOAuth client secret (stored in Secret Manager)ORACLE_USERNAMEOracle database username for password grant authenticationORACLE_PASSWORDOracle database password (stored in Secret Manager)GCS_BUCKET_NAMECloud Storage bucket for briefing outputMAPS_API_KEYGoogle Maps Static API key (stored in Secret Manager)CLOUD_RUN_URLYour Cloud Run service public URL (used to build agent card)SERVICE_ACCOUNT_EMAILService account email for GCS signed URL generationGOOGLE_GENAI_USE_VERTEXAISet to true to use Vertex AI instead of Gemini API keyGOOGLE_CLOUD_PROJECTGCP project ID passed to Vertex AI SDKGOOGLE_CLOUD_LOCATIONSet to global for broadest Gemini model availabilityGEMINI_MODELSet to gemini-2.5-flash<\/p>\n<p>Run the deployment<\/p>\n<p>chmod +x infra\/deploy.sh<br \/>\n.\/infra\/deploy.sh<\/p>\n<p>The script performs these steps:<\/p>\n<p>Sets the active GCP project<\/p>\n<p>Enables all required GCP APIs<\/p>\n<p>Grants Cloud Build service account deployment permissions<\/p>\n<p>Stores secrets in Secret Manager (Oracle client secret, Maps API key)<\/p>\n<p>Builds and pushes the Docker image via Cloud Build (no Docker required locally)<\/p>\n<p>Deploys the Cloud Run service with Zero Trust authentication (\u2013no-allow-unauthenticated)<\/p>\n<p>Grants the current user Cloud Run invoker access for testing<\/p>\n<p>Step 10: Connecting to Gemini Enterprise<\/p>\n<p>Once deployed, a Gemini Enterprise App Admin registers with your Cloud Run orchestrator as a custom A2A agent. Think of this as a second layer above the Oracle AI Database Agent from <a href=\"https:\/\/blogs.oracle.com\/developers\/unlocking-data-insights-with-the-oracle-ai-database-agent-in-gemini-enterprise-part-1\" data-type=\"link\" data-id=\"https:\/\/blogs.oracle.com\/developers\/unlocking-data-insights-with-the-oracle-ai-database-agent-in-gemini-enterprise-part-1\" rel=\"nofollow noopener\" target=\"_blank\">Part 1<\/a>: Gemini Enterprise talks to your orchestrator, and your orchestrator in turn talks to the Oracle agent.<\/p>\n<p>There are two separate connections in this architecture. The Oracle OAuth credentials from <a href=\"https:\/\/blogs.oracle.com\/developers\/unlocking-data-insights-with-the-oracle-ai-database-agent-in-gemini-enterprise-part-1\" data-type=\"link\" data-id=\"https:\/\/blogs.oracle.com\/developers\/unlocking-data-insights-with-the-oracle-ai-database-agent-in-gemini-enterprise-part-1\" rel=\"nofollow noopener\" target=\"_blank\">Part 1<\/a> are used by the orchestrator when it calls the Oracle AI Database Agent. Access from Gemini Enterprise to your Cloud Run service should match however you expose the custom agent endpoint. In the demo deployment in this post, the service is made reachable, so Gemini Enterprise can invoke it.<\/p>\n<p>Open the Gemini Enterprise Admin Console: go to Apps \u2192 Google Workspace \u2192 Gemini Enterprise<\/p>\n<p>Navigate to Agents\u00a0then\u00a0Add Agent\u00a0then\u00a0Custom agent via A2A<\/p>\n<p>Paste the agent card JSON: replace YOUR_CLOUD_RUN_URL with your actual service URL. The url field must point to your Cloud Run service base URL.<\/p>\n<p>Enter OAuth credentials: Gemini Enterprise prompts for Client ID, Client Secret, Authorization URL, and Token URL. Use the same Oracle OAuth credentials from <a href=\"https:\/\/blogs.oracle.com\/developers\/unlocking-data-insights-with-the-oracle-ai-database-agent-in-gemini-enterprise-part-1\" data-type=\"link\" data-id=\"https:\/\/blogs.oracle.com\/developers\/unlocking-data-insights-with-the-oracle-ai-database-agent-in-gemini-enterprise-part-1\" rel=\"nofollow noopener\" target=\"_blank\">Part 1<\/a>.<\/p>\n<p>Grant access: in the agent\u2019s Permissions panel, add the users, groups, or OUs that should have access<\/p>\n<p>Open Cloud Run to allUsers: Gemini Enterprise must be able to reach your service. Run:<\/p>\n<p>gcloud run services add-iam-policy-binding oracle-sales-hub \\<br \/>\n&#8211;region=YOUR_REGION \\<br \/>\n&#8211;member=&#8221;allUsers&#8221; \\<br \/>\n&#8211;role=&#8221;roles\/run.invoker&#8221;<\/p>\n<p>Note: this is reset on each redeployment. Add \u2013allow-unauthenticated to the gcloud run deploy command in deploy.sh to make it permanent.<\/p>\n<p>Demo output<\/p>\n<p>Chat<\/p>\n<p><img fetchpriority=\"high\" decoding=\"async\" width=\"938\" height=\"895\" src=\"https:\/\/www.europesays.com\/ai\/wp-content\/uploads\/2026\/04\/1776908367_59_image-13.png\" alt=\"The image shows the demo chat output \" class=\"wp-image-4831\"  \/>The chat output<\/p>\n<p>Charts (opens in new tab)<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"622\" src=\"https:\/\/www.europesays.com\/ai\/wp-content\/uploads\/2026\/04\/image-12-1024x622.png\" alt=\"Visualization chart shows the revenue by sales channel - Financial Year 2024 vs Financial Year 2025\" class=\"wp-image-4832\"  \/>Visualization chart<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"975\" height=\"813\" src=\"https:\/\/www.europesays.com\/ai\/wp-content\/uploads\/2026\/04\/image-14.png\" alt=\"The image shows the visualization of the results on the map: the revenue by sales channel - Financial Year 2024 vs Financial Year 2025\" class=\"wp-image-4833\"  \/>The output results on the map<\/p>\n<p>Troubleshooting<\/p>\n<p>IssueResolutionContainer fails to start on Cloud RunCheck logs: gcloud logging read with resource.type=cloud_run_revision. Most common cause is a missing environment variable causing a crash at import time.ModuleNotFoundError: google.adk.a2aThe A2A extras are not installed. Change google-adk&gt;=0.4.0 to google-adk[a2a]&gt;=0.4.0 in requirements.txt and redeploy.Agent card returns 404 from curlADK\u2019s get_fast_api_app() does not expose A2A endpoints. Switch to to_a2a() in main.py as described in Step 8.Cloud Run returns 403 to Gemini EnterpriseGemini Enterprise cannot reach the service. Run: gcloud run services add-iam-policy-binding oracle-sales-hub \u2013member=allUsers \u2013role=roles\/run.invokerGemini model 404 \/ publisher model not foundgemini-2.0-flash is not available for new projects. Use gemini-2.5-flash and set GOOGLE_CLOUD_LOCATION=global in deploy.sh.Signed URL error: credentials just contain a tokenCompute Engine credentials cannot sign URLs directly. Add SERVICE_ACCOUNT_EMAIL env var, update gcs_tool.py to use access_token parameter, and grant roles\/iam.serviceAccountTokenCreator to the service account.Oracle A2A returns ADB-00015 Invalid credentialThe client_id or client_secret is incorrect or expired. Re-run the Oracle OAuth registration curl to generate fresh credentials.Gemini Enterprise OAuth error unauthorizedGemini Enterprise sends authorization code flow without PKCE. If your Oracle OAuth client is registered with PKCE, register a new client without using token_endpoint_auth_method: client_secret_post.<\/p>\n<p>Links<\/p>\n","protected":false},"excerpt":{"rendered":"Introduction Part 1 showed the fastest way to connect Gemini Enterprise to live Oracle data without building a&hellip;\n","protected":false},"author":2,"featured_media":13371,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9],"tags":[1789,6580,2408,132,1430],"class_list":{"0":"post-13370","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-google","8":"tag-database","9":"tag-developers","10":"tag-gemini","11":"tag-google","12":"tag-google-gemini"},"_links":{"self":[{"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/posts\/13370","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/comments?post=13370"}],"version-history":[{"count":0,"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/posts\/13370\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/media\/13371"}],"wp:attachment":[{"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/media?parent=13370"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/categories?post=13370"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/tags?post=13370"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}