The Tourist Scheduling System is a reference implementation of a multi-agent system built with Google's Agent Development Kit (ADK). It demonstrates how autonomous agentsβrepresenting Tourists, Guides, and a Schedulerβcan collaborate to solve complex coordination problems in real-time.
This application showcases advanced patterns for building production-ready agentic workflows, including:
- Dynamic Service Discovery: Agents publish their capabilities (A2A Cards) to a central Agent Directory, enabling runtime discovery without brittle point-to-point configuration.
- Secure Agent-to-Agent (A2A) Communication: Uses SLIM (Secure Layer for Intelligent Messaging) to establish encrypted, authenticated channels between agents.
- Observability: Full integration with OpenTelemetry and Jaeger to trace requests and tool executions across distributed agent processes.
- Human-in-the-Loop: A real-time Dashboard provides visibility into the negotiation and scheduling process.
- Features
- Integrations
- Quick Start
- Project Structure
- Architecture
- SLIM Transport
- Kubernetes Deployment
- Distributed Tracing
- Dashboard Features
- CLI Reference
- Development
- License
- Multi-Agent Coordination: Scheduler, guides, and tourists working together
- Dynamic Discovery: Agents register and discover capabilities via the Agent Directory
- A2A Communication: Full A2A compliance with SLIM transport support
- Real-Time Dashboard: Live monitoring with WebSocket updates
- Distributed Tracing: OpenTelemetry integration with Jaeger visualization
- LLM-Powered Agents: Azure OpenAI and Google Gemini integration via LiteLLM
This application leverages several key integrations to provide a robust, production-ready agent ecosystem:
- Agent Directory: A centralized registry where agents publish their A2A Cards (capabilities) and endpoints. This enables dynamic discovery, allowing the Scheduler to find available Guides and Tourists at runtime.
- SLIM Transport: Secure Layer for Intelligent Messaging. Provides encrypted, authenticated communication channels between agents, ensuring data privacy and integrity.
- OpenTelemetry & Jaeger: Comprehensive observability stack. Traces requests across agent boundaries, visualizing the full lifecycle of a scheduling task from Tourist request to Guide assignment.
- LLM Providers: Flexible integration with Azure OpenAI and Google Gemini via LiteLLM, powering the intelligent decision-making of the agents.
- FastAPI: High-performance web framework used for the Agent Directory and agent HTTP interfaces.
- Python 3.12+
- UV package manager
- Docker (for SLIM transport and tracing)
- Azure OpenAI API key or Google Gemini API key
# Clone the repository
git clone https://github.com/agntcy/agentic-apps.git
cd agentic-apps/tourist_scheduling_system
# Create virtual environment
uv venv
source .venv/bin/activate
# Install dependencies
uv sync
# Configure Azure OpenAI
export AZURE_OPENAI_API_KEY="your-key"
export AZURE_OPENAI_ENDPOINT="https://your-endpoint.openai.azure.com"
# Or Configure Google Gemini
export MODEL_PROVIDER="gemini"
export GOOGLE_GEMINI_API_KEY="your-google-api-key"
# Optional: Specify model (default: gemini/gemini-3-pro-preview)
export MODEL_NAME="gemini/gemini-3-pro-preview"# Start infrastructure (SLIM + Jaeger)
./setup.sh start
# Run the demo with SLIM transport
source run.sh --transport slim --tracingAccess Points:
- Dashboard: http://localhost:10021
- Jaeger UI: http://localhost:16686 (when tracing is enabled)
The system includes a modern Flutter-based frontend for a richer user experience.
Prerequisites:
- Flutter SDK installed
Launch Instructions:
- Ensure the backend agents are running (see "Run the Demo" above).
- Open a new terminal window.
- Navigate to the frontend directory and launch the app:
cd frontend
./setup.sh
flutter run -d web-server- Open the URL provided by Flutter (typically
http://localhost:web-port) in your browser.
tourist_scheduling_system/
βββ src/
β βββ agents/ # Agent implementations
β β βββ scheduler_agent.py # Main scheduler (A2A server, port 10000)
β β βββ ui_agent.py # Dashboard web app (A2A server, port 10021)
β β βββ guide_agent.py # Tour guide agent (A2A client)
β β βββ tourist_agent.py # Tourist agent (A2A client)
β β βββ tools.py # ADK tools (register, match, etc.)
β βββ core/ # Core utilities
β βββ a2a_cards.py # Agent A2A card definitions
β βββ dashboard.py # Starlette dashboard app
β βββ models.py # Pydantic data models
β βββ slim_transport.py # SLIM transport adapter
β βββ tracing.py # OpenTelemetry setup
β βββ messages.py # Message types
β βββ logging_config.py # Logging configuration
βββ scripts/ # Shell scripts for infrastructure
β βββ spire.sh # SPIRE server/agent deployment
β βββ slim-controller.sh # SLIM controller deployment
β βββ slim-node.sh # SLIM data plane node deployment
β βββ directory.sh # Agent Directory deployment
β βββ run_adk_demo.py # Main demo runner (Python CLI)
β βββ slim-control-csid.yaml.tpl # SPIRE ID template for Controller
β βββ slim-node-csid.yaml.tpl # SPIRE ID template for Node
β βββ *-values.yaml # Helm values files
βββ deploy/
β βββ k8s/ # Kubernetes manifests
β βββ namespace.yaml # Namespace and ConfigMap
β βββ scheduler-agent.yaml # Scheduler Deployment
β βββ ui-agent.yaml # UI Dashboard Deployment
β βββ guide-agent.yaml # Sample guide agent Jobs
β βββ tourist-agent.yaml # Sample tourist agent Jobs
β βββ deploy.sh # Deployment helper script
β βββ spawn-agents.sh # Scale multiple guides/tourists
β βββ templates/ # Job templates for dynamic generation
β βββ README.md # K8s deployment docs
βββ setup.sh # Local infrastructure management
βββ run.sh # Demo launcher script (sourceable)
βββ slim-config.yaml # SLIM node configuration
βββ slim-config-otel.yaml # SLIM config with OpenTelemetry
| Agent | Port | Role |
|---|---|---|
| Scheduler | 10000 | Central coordinator, matches guides to tourists |
| Dashboard | 10021 | Real-time web UI with WebSocket updates |
| Guides | (via A2A) | LLM-powered tour guides with specializations |
| Tourists | (via A2A) | Visitors requesting specific tour experiences |
| Directory | 8888 | Service registry for agent discovery |
ββββββββββββββββ
β Directory β
β (port 8888) β
ββββββββ²ββββββββ
β Register/Lookup
β
ββββββββββββββββ A2A/SLIM ββββββββΌββββββββ
β Guide Agent β βββββββββββββββββΆ β Scheduler β
ββββββββββββββββ β Agent β
β (port 10000)β
ββββββββββββββββ A2A/SLIM β β
β Tourist Agentβ βββββββββββββββββΆ β β
ββββββββββββββββ ββββββββ¬ββββββββ
β
β HTTP/WS
βΌ
ββββββββββββββββ
β Dashboard β
β (port 10021)β
ββββββββββββββββ
SLIM provides encrypted, high-performance messaging:
# Start SLIM node
./setup.sh slim start
# Configure SLIM endpoint
export SLIM_ENDPOINT=http://localhost:46357
export SLIM_SHARED_SECRET=supersecretsharedsecret123456789
export SLIM_TLS_INSECURE=true
# Run with SLIM transport
source run.sh --transport slimSee slim-config.yaml for node configuration. Key settings:
storage:
type: InMemory
transport:
type: HTTP
security:
shared_secret: ${SLIM_SHARED_SECRET}Deploy the Tourist Scheduling System to Kubernetes with support for both HTTP and SLIM transport modes.
- Kubernetes cluster (MicroK8s, GKE, EKS, etc.)
kubectlconfiguredenvsubst(comes with gettext)- Container images pushed to registry
cd deploy/k8s
# Set environment
export NAMESPACE=lumuscar-jobs
export IMAGE_REGISTRY=ghcr.io/agntcy/apps
export IMAGE_TAG=latest
# Deploy with HTTP transport (default)
./deploy.sh http
# Or deploy with SLIM transport (requires SLIM infrastructure)
./deploy.sh slim
# Create secrets
kubectl create secret generic azure-openai-credentials \
--from-literal=api-key=$AZURE_OPENAI_API_KEY \
--from-literal=endpoint=$AZURE_OPENAI_ENDPOINT \
--from-literal=deployment-name=${AZURE_OPENAI_DEPLOYMENT_NAME:-gpt-4o} \
-n $NAMESPACE
# Check status
./deploy.sh statusFor SLIM transport with mTLS authentication (via SPIRE):
# Install SPIRE (identity provider)
./scripts/spire.sh install
# Install SLIM controller (with SPIRE enabled)
export SPIRE_ENABLED=true
./scripts/slim-controller.sh install
# Install SLIM node (with SPIRE enabled)
# Default strategy is StatefulSet
export SPIRE_ENABLED=true
./scripts/slim-node.sh install
# Or install SLIM node as DaemonSet
export SLIM_STRATEGY=daemonset
export SPIRE_ENABLED=true
./scripts/slim-node.sh install
# Install Agent Directory (optional)
./scripts/directory.sh install
# Verify
./scripts/spire.sh status
./scripts/slim-controller.sh status
./scripts/slim-node.sh status# Using envsubst for variable substitution
export NAMESPACE=lumuscar-jobs
export IMAGE_REGISTRY=ghcr.io/agntcy/apps
export IMAGE_TAG=latest
export TRANSPORT_MODE=http # or slim
# Apply manifests
envsubst < deploy/k8s/namespace.yaml | kubectl apply -f -
envsubst < deploy/k8s/scheduler-agent.yaml | kubectl apply -f -
envsubst < deploy/k8s/ui-agent.yaml | kubectl apply -f -
# Run client agents (Jobs)
envsubst < deploy/k8s/guide-agent.yaml | kubectl apply -f -
envsubst < deploy/k8s/tourist-agent.yaml | kubectl apply -f -Spawn many guides and tourists with randomized configurations:
cd deploy/k8s
# Spawn 10 guides and 50 tourists
./spawn-agents.sh guides 10
./spawn-agents.sh tourists 50
# Or spawn both at once
./spawn-agents.sh both 10 50
# Check status
./spawn-agents.sh status
# Clean up
./spawn-agents.sh cleanSee deploy/k8s/README.md for full documentation.
Full OpenTelemetry integration with Jaeger:
# Start Jaeger
./setup.sh tracing
# Run with tracing
source run.sh --tracing
# View traces
open http://localhost:16686Trace features:
- Request-level spans
- Cross-agent trace propagation
- Tool execution timing
- Error tracking
The real-time dashboard shows:
- Guide Pool: Available guides with specializations and ratings
- Tourist Queue: Pending tourist requests with preferences
- Active Assignments: Current guide-tourist matches in progress
- Completed Tours: Historical data with ratings
- Communication Log: Agent message history (guide/tourist/system)
WebSocket provides instant updates as the scheduler processes requests.
./setup.sh start # Start SLIM + Jaeger containers
./setup.sh stop # Stop all containers
./setup.sh clean # Remove containers and data
./setup.sh slim # Start only SLIM node
./setup.sh tracing # Start only Jaeger
./setup.sh status # Show container statusThe script can be sourced to preserve environment variables or run directly:
# Source to inherit current shell's env vars (recommended)
source run.sh [options]
# Or run directly
./run.sh [options]
# Options
--transport MODE # http (default) or slim
--provider NAME # Model provider: azure (default) or google
--tracing # Enable OpenTelemetry tracing
--scheduler-port N # Scheduler port (default: 10000)
--ui-port N # Dashboard port (default: 10021)
--guides N # Number of guides (default: 2)
--tourists N # Number of tourists (default: 3)
--duration N # Duration in minutes (0=single run)
--interval N # Delay between requests (default: 1.
--real-agents # Use real ADK guide/tourist agents instead of simulation0s)
--no-demo # Start servers only, no demo traffic
# Control
./run.sh stop # Stop all agents
./run.sh clean # Stop agents and clean upFor direct Python control:
# Interactive console demo
.venv/bin/python scripts/run_adk_demo.py --mode console
# Full multi-agent demo (spawns all processes)
.venv/bin/python scripts/run_adk_demo.py --mode multi
# Simulation only (requires agents already running)
.venv/bin/python scripts/run_adk_demo.py --mode sim --port 10000 --ui-port 10021
# With SLIM transport
.venv/bin/python scripts/run_adk_demo.py --mode multi --transport slim
# Options
--mode MODE # console, server, multi, or sim
--port N # Scheduler port (default: 10000)
--ui-port N # Dashboard port (default: 10021)
--guides N # Number of guides (default: 2)
--tourists N # Number of tourists (default: 3)
--transport MODE # http or slim
--slim-endpoint URL # SLIM node URL
--tracing/--no-tracing # Enable OpenTelemetry
--duration N # Duration in minutes (0=single run)
--interval N # Delay between requests
--fast/--no-fast # Skip LLM calls for testing# Required (Choose one provider)
export AZURE_OPENAI_API_KEY="your-key"
# OR
export GOOGLE_GEMINI_API_KEY="your-google-api-key"
# Optional
export MODEL_PROVIDER="openai" # or "gemini"
export MODEL_NAME="gemini/gemini-3-pro-preview" # for Gemini
export AZURE_OPENAI_ENDPOINT="https://..."
export TRANSPORT=slim # Default transport
export SLIM_ENDPOINT=http://localhost:46357 # SLIM node URL
export SLIM_SHARED_SECRET=your-secret # SLIM auth secret
export SCHED_PORT=10000 # Scheduler port
export UI_PORT=10021 # Dashboard port
export DIR_PORT=8888 # Agent Directory port
export DIRECTORY_CLIENT_SERVER_ADDRESS=localhost:8888 # Directory address
# Proxy Configuration (if behind a corporate proxy)
export HTTP_PROXY="http://proxy.example.com:8080"
export HTTPS_PROXY="http://proxy.example.com:8080"
# Ensure localhost is excluded from proxy
export NO_PROXY="localhost,127.0.0.1,::1"To distribute the frontend as a Docker container:
cd frontend
docker build -t tourist-scheduling-frontend .
docker run -p 8080:80 tourist-scheduling-frontendAccess the frontend at http://localhost:8080.
uv sync ./setup.sh install # Ensure dependencies uv run pytest tests/
### Adding New Agents
1. Create agent in `src/agents/`
2. Define A2A card in `src/core/a2a_cards.py`
3. Add tools in `src/agents/tools.py`
4. Update `run_adk_demo.py` to spawn agent
### Logs
Logs are written to `logs/` directory:
- `scheduler_agent.log`
- `ui_agent.log`
- OpenTelemetry trace files (`.json`)
## π License
Apache 2.0 - See [LICENSE](../LICENSE)
