Public-facing Agentic AI chatbot sample. The solution includes working citations, a dual-agent setup (primary + fallback), a floating widget frontend, a FastAPI backend, and production deployment assets.
The backend uses the Foundry Responses API pattern with agent_reference (name/version).
- Public documents are indexed in Azure AI Search or use Azure Web IQ
- Citations resolve to real document URLs.
- Dual-agent behavior is implemented (primary + optional fallback).
- URL mapping from document IDs to public URLs is implemented.
- Docker and Terraform deployment assets are available.
- Fixed HTML links across local pages.
- FastAPI backend with chat and search endpoints.
- Azure AI Search integration.
- Dual-agent routing behavior.
- Working citations (no
doc_0placeholders in final links). - URL mapping utility for citations.
- Floating, responsive chat widget.
- Chat mode and search mode.
- Conversation continuity using
previous_response_idmapped to browser thread IDs. - Native search integration from page search forms to widget search.
- Enhanced citation cards in the widget.
- Resizable widget UI.
- Request logging for diagnostics.
- Docker + Terraform deployment assets.
va-chat/
├── backend/
│ ├── main.py # FastAPI + Foundry Responses API + dual-agent routing
│ └── foundry-agent/ # Agent configuration files
├── frontend/
│ ├── va-chat-widget.css
│ ├── va-chat-widget.js
│ ├── va-chat-injector.js
│ └── simple-integration.html
├── utilities/
│ ├── mapping.csv # Document ID to URL mappings
│ └── map_sourceurl2_foundryIQ_kstore.py
├── sample-site/
│ ├── *.html
│ ├── va-chat-widget.css
│ ├── va-chat-widget.js
│ ├── va-search-integration.js
│ └── test-chat-widget.html
├── scripts/
│ ├── fix-links.py
│ ├── serve-samplesite.py # Dev static server for sample-site/
│ ├── build-and-push.ps1
│ ├── build-and-push.sh
│ └── inject-widget.ps1
├── terraform/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
├── experiments/
├── .env
├── requirements.txt
└── Dockerfile
- Python 3.11+
- Azure CLI authenticated (
az login) - Azure Foundry project and agent configured
- Activate virtual environment.
.\.venv\Scripts\Activate.ps1- Install dependencies.
pip install -r requirements.txt- Configure environment variables (
.env).
AZURE_FOUNDRY_PROJECT_ENDPOINT=your-endpoint
AZURE_FOUNDRY_AGENT_NAME=your-primary-agent-name
AZURE_FOUNDRY_AGENT_VERSION=optional-version-or-latest
# Optional fallback agent
AZURE_FOUNDRY_FALLBACK_AGENT_NAME=your-fallback-agent-name
AZURE_FOUNDRY_FALLBACK_AGENT_VERSION=optional-version-or-latest
# Model deployment name (preferred)
FOUNDRY_MODEL=gpt-5.4-mini-1
# Optional backward-compatible alias
AZURE_FOUNDRY_DEPLOYMENT_NAME=gpt-5.4-mini-1
# Optional OpenAI API version passed when supported
OPENAI_API_VERSION=2024-10-21
PORT=8080
LOG_LEVEL=INFO
UVICORN_RELOAD=falseNotes:
AZURE_FOUNDRY_PROJECT_ENDPOINTis required.AZURE_FOUNDRY_AGENT_NAMEis required.AZURE_FOUNDRY_AGENT_VERSIONis optional; latest is used when omitted.- Fallback agent settings are optional.
- Auth is handled by
DefaultAzureCredential.
Use two terminals.
C:/code/va-chat/.venv/Scripts/python.exe backend/main.pyC:/code/va-chat/.venv/Scripts/python.exe backend/main.py- Backend URL: http://localhost:8080
- Endpoints:
/api/chat,/api/search,/api/health
C:/code/va-chat/.venv/Scripts/python.exe scripts/serve-samplesite.py- Frontend URL: http://localhost:9000
- Serves files from
sample-site/
- Open a page:
- http://localhost:9000/test-chat-widget.html
- http://localhost:9000/index.html
- http://localhost:9000/services.html
-
Click the chat button in the bottom-right corner.
-
Try test prompts:
- What services do you provide?
- How do I apply for a job?
- Tell me about public education programs.
- Test both modes:
- Chat mode: conversational answers + citations
- Search mode: structured search-style results
Request:
{
"message": "What services are available?",
"thread_id": "optional-thread-id",
"mode": "chat"
}Response:
{
"message": "The organization provides...",
"thread_id": "2f7f0dfd-9f12-44d1-9a80-98f7ac4e4f56",
"mode": "chat",
"citations": [
{
"title": "Our Services",
"url": "https://example.gov",
"snippet": "..."
}
]
}Alias for /api/chat with mode: "search".
Health endpoint.
To add the widget to a page:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="frontend/va-chat-widget.css">
</head>
<body>
<!-- page content -->
<script src="frontend/va-chat-widget.js"></script>
<!-- optional, if page has search forms -->
<script src="sample-site/va-search-integration.js"></script>
</body>
</html>flowchart TB
subgraph browser[Browser Client]
page[Rendered Website]
widget[Chat Widget]
search[Search Integration]
page --> widget
search --> widget
end
subgraph edge[Azure Edge]
frontdoor[Azure Front Door]
end
subgraph web[Website Hosting]
staticweb[Static Web App]
siteassets[Public Website HTML, JS, CSS]
staticweb --> siteassets
end
subgraph app[Azure Application]
containerapp[Container App]
fastapi[FastAPI Backend]
identity[Managed Identity]
logs[Log Analytics]
containerapp --> fastapi
identity --> fastapi
containerapp --> logs
end
subgraph ai[Azure AI Services]
foundry[Foundry Responses API]
agent[Agent]
bing[Grounding with Bing Tool]
fallback[Optional Fallback Agent]
foundryiq[Azure Foundry IQ Knowledge Source]
foundry --> agent
agent --> bing
foundry -.->|optional fallback| fallback
fallback --> foundryiq
end
subgraph deployment[Deployment Pipeline]
docker[Docker Build]
acr[Container Registry]
terraform[Terraform IaC]
docker --> acr
acr --> containerapp
terraform --> containerapp
terraform --> identity
end
page -->|requests site| frontdoor
frontdoor -->|/*| staticweb
siteassets -->|rendered in browser| page
widget -->|/api/*| frontdoor
frontdoor -->|/api/*| containerapp
fastapi --> foundry
classDef browserNode fill:#E8F1FB,stroke:#2F6B9A,color:#12344D,stroke-width:1.5px;
classDef edgeNode fill:#D9F3F0,stroke:#1E7F78,color:#0F403B,stroke-width:1.5px;
classDef webNode fill:#E7F6E8,stroke:#4E8B57,color:#1F4628,stroke-width:1.5px;
classDef appNode fill:#FFF0D9,stroke:#B7791F,color:#5F370E,stroke-width:1.5px;
classDef aiNode fill:#FBE7E7,stroke:#B85C5C,color:#5C2424,stroke-width:1.5px;
classDef deployNode fill:#ECECEC,stroke:#6B7280,color:#1F2937,stroke-width:1.5px;
class page,widget,search browserNode;
class frontdoor edgeNode;
class staticweb,siteassets webNode;
class containerapp,fastapi,identity,logs appNode;
class foundry,agent,bing,fallback,foundryiq aiNode;
class docker,acr,terraform deployNode;
style browser fill:#F7FBFF,stroke:#2F6B9A,stroke-width:2px,color:#12344D;
style edge fill:#EEFBF9,stroke:#1E7F78,stroke-width:2px,color:#0F403B;
style web fill:#F4FBF4,stroke:#4E8B57,stroke-width:2px,color:#1F4628;
style app fill:#FFF8ED,stroke:#B7791F,stroke-width:2px,color:#5F370E;
style ai fill:#FFF4F4,stroke:#B85C5C,stroke-width:2px,color:#5C2424;
style deployment fill:#F7F7F7,stroke:#6B7280,stroke-width:2px,color:#1F2937;
Production hosting for the mini-site stack is Browser -> Azure Front Door -> Static Web App for site assets and Browser -> Azure Front Door -> Container App for /api/*. The browser renders the site, but Static Web App hosts the public website assets. Local development still uses direct localhost access.
sequenceDiagram
participant Browser
participant FrontDoor as Azure Front Door
participant FastAPI
participant Identity as Managed Identity
participant EntraID as Microsoft Entra ID
participant Foundry as Foundry Responses API
Browser->>FrontDoor: POST /api/chat
FrontDoor->>FastAPI: Route /api/* to Container App backend
FastAPI->>Identity: Request credentials
Identity->>EntraID: Authenticate
EntraID-->>Identity: Access token
Identity-->>FastAPI: Credentials
FastAPI->>Foundry: responses.create(model, input, agent_reference)
Foundry->>Foundry: Continue with previous_response_id when available
Foundry-->>FastAPI: Response + citations
FastAPI-->>FrontDoor: JSON response
FrontDoor-->>Browser: JSON response
- User sends message from widget.
- Widget calls
/api/chat. - Backend maps thread ID to prior
response_id. - Backend calls primary named agent reference.
- Response and citations are returned to widget.
- User submits search query.
- Widget calls
/api/search. - Backend adds search-oriented instruction text.
- Backend runs primary and optional fallback flows.
- Results are merged and rendered as search results.
- Floating/minimizable bubble
- Expand/collapse transitions
- Responsive design for desktop/mobile
- Accessibility-oriented markup
- Thread persistence in browser state
- Intercepts
form.searchsubmissions - Opens widget directly in search mode
- Avoids page reloads for results
- Provides search functionality where native search is unavailable
The repo includes Terraform for Azure Container Apps deployment.
- Configure Terraform.
cd terraform
Copy-Item terraform.tfvars.example terraform.tfvars
notepad terraform.tfvars- Build and push image.
cd ..
.\scripts\build-and-push.ps1- Deploy infra.
cd terraform
terraform init
terraform applySee DEPLOYMENT.md for full deployment steps.
- Verify Azure auth:
az account show - Verify
.envendpoint, agent name, and model - Verify port 8080 is free
- Check browser console
- Confirm backend is running on 8080
- Confirm frontend is running on 9000
- Check backend logs
- Verify agent name/version values in
.env - Test SDK access with
utilities/foundry2_agent_test.py
- Run
python scripts/fix-links.py - Ensure sample pages are under
sample-site/
Updates local HTML links.
python scripts/fix-links.pyServes sample-site/ on port 9000.
python scripts/serve-samplesite.py- Reload is off by default in
backend/main.py; setUVICORN_RELOAD=truefor live reload. - Keep backend and frontend terminals separate.
- Use venv python explicitly on Windows if shell activation behaves inconsistently.
- Use FastAPI docs at http://localhost:8080/docs.
- Agent test utility:
utilities/foundry2_agent_test.py - Backend logs: terminal output from backend process
Status: Phase 1A Complete + Azure AI Search Integration Last Updated: June 8, 2026 Version: 1.3.0