S

Starbucks Signals

Agentic site-selection copilot — ArcGIS JS + Azure OpenAI

Ask a question in plain English — "Find 5 candidates in Austin with good demographics and low competition" — and a GPT-4.1 agent drives real spatial tools against ArcGIS Online, ArcGIS Places, and Placer AI to propose, score, and rank new Starbucks locations. The map updates live as the agent reasons.

Environment
Checking…
Launch app →

01 How to run it

Two processes: a FastAPI proxy that holds the secret keys (port 8082) and the static frontend (port 8083). Always start the proxy first.

One-time setup

In proxy/: python -m venv .venv.venv\Scripts\pip install -r requirements.txt → copy .env.example to .env and fill in AZURE_API_KEY, ARCGIS_API_KEY, and (optional) PLACER_API_KEY.

Terminal A — start the proxy

# from starbucks-signals/proxy
.venv\Scripts\activate
run.bat    # uvicorn on http://127.0.0.1:8082

Terminal B — start the frontend

# from starbucks-signals/
serve.bat  # python http.server on http://localhost:8083

Open the app

You're already here — click Launch app above. The two top-right chips should go green (AGOL sign-in, Azure OpenAI proxy).

02 Try these prompts

Paste any of these into the agent chat panel on the right side of the app.

Find 5 candidates in Austin, TX within 3 miles.
Scan the market around Miami Beach and tell me where Starbucks is under-represented.
Score 500 Congress Ave, Austin with a 10-minute drive time.
Compare the top candidates and write me a brief for the real estate team.

03 Tools the agent can call

scan_market
Counts SBUX + competitor stores inside a radius. Breaks down competitors by brand. Uses AGOL FeatureLayers.
propose_candidates
Pulls real POIs (malls, plazas, grocery, universities, hotels…) from ArcGIS Places. Scores with buffer + anchor logic, re-ranks by Placer foot traffic.
score_site
Real drive-time polygon + GeoEnrichment demographics for a specific address. Pulls Placer monthly visits for the site.
compare_candidates
Ranks everything scored so far in a table. Returns the full state to the LLM for narration.
brief
Structured recommendation payload. The LLM writes the prose; this returns the numbers to ground it in.

04 Under the hood

Browser (:8083)  ──►  FastAPI proxy (:8082)  ──►  Azure OpenAI (GPT-4.1)
index.html · app.html          proxy/main.py                     via APIM
app.js · tools.js              holds AZURE_API_KEY               tool-calling loop
                               holds ARCGIS_API_KEY
                               holds PLACER_API_KEY

Why the proxy? Three secrets — Azure OpenAI, ArcGIS Places, Placer AI — must never touch the browser. The proxy terminates CORS and injects keys server-side. AGOL spatial queries (FeatureLayers, drive-time, GeoEnrichment) still run client-side against a scoped AGOL token, so the map stays live as the agent works.

Security rule: the Azure OpenAI key lives only in proxy/.env, which is gitignored. Never inline it into HTML or JS, never commit it.

05 Troubleshooting

Azure OpenAI chip is red. Make sure the proxy is running (Terminal A). Check http://127.0.0.1:8082/api/health — it should return {"ok":true}.
Agent says upstream_error. The APIM key or URL in proxy/.env is wrong. The proxy echoes Azure's error body into the response — check the chat UI or the proxy terminal.
Map is empty / "Params are not set" in console. That's the ArcGIS 5.0 projection engine failing to load its WASM data. Check the importmap path in app.htmlassetsPath must be https://js.arcgis.com/5.0/, not .../@arcgis/core/assets/.
Slow candidate scoring. Expected — AGOL buffer queries run serialized per grid cell. Reduce radius_miles or lower n in your prompt.