- Always read
PLANNING.mdat the start of a new conversation to understand the project's architecture, goals, style, and constraints. - Check
TASK.mdbefore starting a new task. If the task isn’t listed, add it with a brief description and today's date. - Use consistent naming conventions, file structure, and architecture patterns as described in
PLANNING.md. - Use venv_linux (the virtual environment) whenever executing Python commands, including for unit tests.
- Never create a file longer than 500 lines of code. If a file approaches this limit, refactor by splitting it into modules or helper files.
- Organize code into clearly separated modules, grouped by feature or responsibility.
For agents this looks like:
agent.py- Main agent definition and execution logictools.py- Tool functions used by the agentprompts.py- System prompts
- Use clear, consistent imports (prefer relative imports within packages).
- Use clear, consistent imports (prefer relative imports within packages).
- Use python_dotenv and load_env() for environment variables.
- Always create Pytest unit tests for new features (functions, classes, routes, etc).
- After updating any logic, check whether existing unit tests need to be updated. If so, do it.
- Tests should live in a
/testsfolder mirroring the main app structure.- Include at least:
- 1 test for expected use
- 1 edge case
- 1 failure case
- Include at least:
- Mark completed tasks in
TASK.mdimmediately after finishing them. - Add new sub-tasks or TODOs discovered during development to
TASK.mdunder a “Discovered During Work” section.
-
Use Python as the primary language.
-
Follow PEP8, use type hints, and format with
black. -
Use
pydanticfor data validation. -
Use
FastAPIfor APIs andSQLAlchemyorSQLModelfor ORM if applicable. -
Write docstrings for every function using the Google style:
def example(): """ Brief summary. Args: param1 (type): Description. Returns: type: Description. """
- Update
README.mdwhen new features are added, dependencies change, or setup steps are modified. - Comment non-obvious code and ensure everything is understandable to a mid-level developer.
- When writing complex logic, add an inline
# Reason:comment explaining the why, not just the what.
- Never assume missing context. Ask questions if uncertain.
- Never hallucinate libraries or functions – only use known, verified Python packages.
- Always confirm file paths and module names exist before referencing them in code or tests.
- Never delete or overwrite existing code unless explicitly instructed to or if part of a task from
TASK.md.
This is an SRE Assistant Agent built with Google's Agent Development Kit (ADK) for helping Site Reliability Engineers with operational tasks, particularly focused AWS interactions. The system includes a web interface, API, and Slack bot integration.
# Build and start all services
docker-compose build
docker-compose up -d
# Start specific services
docker-compose up -d sre-bot-web # Web interface on port 8000
docker-compose up -d sre-bot-api # API server on port 8001
docker-compose up -d slack-bot # Slack bot on port 8002
# View logs
docker-compose logs [service-name]
# Stop services
docker-compose down# Run linting and formatting
ruff check .
ruff format .
ruff check . --fix
# Run pre-commit hooks manually
pre-commit run --all-files
pre-commit run ruff-formatFor Bot Testing and Development:
# Install dependencies
pip install -r agents/sre_agent/requirements.txt
pip install -r requirements-dev.txt
# Use built-in ADK web interface for rapid bot testing
adk web --session_service_uri=postgresql://postgres:password@localhost:5432/srebotFor API Server Development:
# Use custom serve.py for API-only development with health checks
cd agents/sre_agent
python serve.pyagents/sre_agent/: Main ADK agent implementation
serve.py: FastAPI server with health checks and session managementagent.py: Clean agent definition and orchestrationsub_agents/: Modular sub-agent implementationsaws_cost/: AWS cost analysis sub-agentagent.py: Agent configuration and initializationtools/: AWS cost analysis toolsprompts/: Agent-specific prompts and instructions
tools/: Shared tool implementationsutils.py: Utility functionssettings.py: Configuration settings
slack_bot/: Slack integration
main.py: FastAPI-based Slack bot with session management- Communicates with SRE agent API for processing queries
This project follows Google ADK's latest patterns and best practices:
Agent Configuration:
- Uses
LlmAgent(aliased asAgent) with declarative Python configuration - Each agent has:
name,model,instruction,description, andtools - Model-agnostic design supporting Gemini, Claude, GPT-4o, and other models via LiteLlm
Multi-Agent System (MAS):
- Structured parent-child relationships with
sub_agents - Specialized workflow agents (
SequentialAgent,ParallelAgent,LoopAgent) - Modularity through composition of smaller, specialized agents
Tool Integration:
- Tools as Python functions or
BaseToolclasses - Custom toolsets like
BigQueryToolsetandVertexAiSearchTool AgentToolfor delegating to other agents
Session & State Management:
Sessionholds conversation history for continuous dialogueStateacts as scratchpad for data sharing between stepsMemoryprovides long-term recall across sessions- Callbacks for dynamic state management via
callback_context.state
Code Reuse & Duplication Prevention:
- Before implementing utility functions, check if similar functionality exists in
agents/sre_agent/utils.py - Always prefer using shared utilities over duplicating code across modules
- When creating commonly-needed functions (like file loading, formatting, etc.), add them to the shared utils module
- Example: Use
load_instruction_from_file()from utils for loading agent prompts from markdown files - If you find duplicated code patterns, refactor them into shared utilities immediately
Logging Standards:
-
Always use shared logging utilities from
agents/sre_agent/utilsinstead of configuring logging directly -
Standard usage:
from agents.sre_agent.utils import get_loggerthenlogger = get_logger(__name__) -
Custom configuration: Use
setup_logger()for modules requiring specific log formats or levels -
Environment control: Use
LOG_LEVELenvironment variable (DEBUG, INFO, WARNING, ERROR, CRITICAL) -
Consistent format: Shared utilities provide standardized timestamp and module name formatting
-
Examples:
# Basic logging (recommended for most modules) from agents.sre_agent.utils import get_logger logger = get_logger(__name__) # Custom logging (for servers or special requirements) from agents.sre_agent.utils import setup_logger logger = setup_logger("SERVICE_NAME", format_string="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
-
Never use:
logging.basicConfig()orlogging.getLogger()directly in new code
Built using Slack Bolt's async patterns:
AsyncApp Configuration:
from slack_bolt.async_app import AsyncApp
from slack_bolt.adapter.fastapi.async_handler import AsyncSlackRequestHandler
app = AsyncApp(
signing_secret="your_signing_secret",
token="your_bot_token"
)
@app.event("app_mention")
async def handle_app_mention(body, say):
await say("Hello!")FastAPI Integration:
- Uses
AsyncSlackRequestHandlerto bridge FastAPI and Bolt - Async event handling with
@app.eventdecorators - Proper
awaitusage for all utility functions (ack(),say(),respond())
The system uses different session management approaches:
-
Slack Interactions:
- SessionManager in
slack_bot/main.pycreates sessions with IDs likeu_{slack_user_id}ands_{channel_id}_{thread_ts} - Makes API calls to create/retrieve sessions in PostgreSQL via DatabaseSessionService
- Ensures conversation continuity within Slack threads
- Follows Slack Bolt's OAuth installation patterns with
AsyncInstallationStore
- SessionManager in
-
Direct API/Web UI:
- Uses DatabaseSessionService in
agents/sre_agent/agent.py - Defaults to
APP_NAME="sre_agent",USER_ID="test_user"if not specified - Session data persisted in PostgreSQL database
- ADK's
Session,State, andMemoryabstractions
- Uses DatabaseSessionService in
The main agent (agents/sre_agent/agent.py) orchestrates multiple specialized sub-agents following ADK's MAS patterns:
- aws_core_mcp_agent: General AWS service interactions via MCP
- aws_cost_analysis_mcp_agent: AWS cost querying and analysis
- aws_cost_agent: Specialized cost analysis sub-agent with dedicated tools
The SRE Bot uses a clean separation between static configuration and secrets:
Static Configuration (docker-compose.yml):
- Database connection details (host, port, database name)
- Container ports and networking
- Python environment settings
- Service dependencies
Secrets & User-Specific (.env files):
- API keys and tokens
- AWS profiles and credentials
- User-specific tool configurations
agents/.env.example # SRE Agent secrets and user configs
slack_bot/.env.example # Slack Bot secrets and behavioral configs
Required Secrets:
GOOGLE_API_KEY,ANTHROPIC_API_KEY: AI model access keysDB_PASSWORD: Database password (matches docker-compose)
User-Specific:
AWS_PROFILE,AWS_REGION: Your AWS configurationKUBE_CONTEXT: Your Kubernetes context
Optional Overrides:
LOG_LEVEL: Development logging levelGOOGLE_AI_MODEL: AI model selection
Required Secrets:
SLACK_BOT_TOKEN,SLACK_SIGNING_SECRET,SLACK_APP_TOKEN: Slack app credentials
Behavioral Settings:
SRE_AGENT_API_TIMEOUT: API timeout settingsSESSION_TIMEOUT_MINUTES,MAX_ACTIVE_SESSIONS: Session management
- Copy each
.env.examplefile to.envin the same directory - Configure only the secrets and user-specific values
- Static configurations are already set in docker-compose.yml
- Never commit actual
.envfiles to version control
- Secrets go in .env files (API keys, tokens, passwords)
- Static configs go in docker-compose.yml (ports, database settings)
- No duplication between docker-compose environment and .env files
- Local development defaults are provided in docker-compose.yml
PostgreSQL database for session persistence:
- Host: postgres (in Docker), localhost (local)
- Database: srebot
- User: postgres
- Tables managed by DatabaseSessionService
AWS capabilities through MCP agents:
- Core AWS service interactions
- Cost analysis and reporting
- Usage optimization insights
Production/Containerized Setup:
- sre-bot-web: Web interface using
adk webwith built-in UI (for testing) - sre-bot-api: API-only server using custom
serve.py(for Slack app integration) - slack-bot: FastAPI Slack integration communicates with sre-bot-api
- postgres: Session data persistence
The custom serve.py provides production-ready API server with:
Health Checks for Monitoring:
/health- General health check with system info/health/readiness- Kubernetes readiness probe/health/liveness- Kubernetes liveness probe
Production Features:
- Request logging middleware for debugging
- PostgreSQL session management
- Environment-based configuration
- Optimized for API-only usage (no UI overhead)
Environment Variables:
PORT: Server port (default: 8000)- Database:
DB_HOST,DB_PORT,DB_NAME,DB_USER,DB_PASSWORD
- Local Bot Testing: Use
adk webfor rapid development with built-in UI - API Development: Use
serve.pyfor API-only server features and health checks - Production: Slack app talks to containerized
serve.pyAPI-only server
Mounts ~/.kube and ~/.aws for credential access.
Requires Slack app configuration with:
- Bot scopes:
app_mentions:read,chat:write,channels:join, etc. - Event subscriptions pointing to ngrok/external URL
- Environment file at
slack_bot/.envwith tokens
Follow ADK's declarative approach for new agents:
from google.adk.agents import Agent
from google.adk.models.lite_llm import LiteLlm
# Basic agent configuration
my_agent = Agent(
name="my_specialized_agent",
model="gemini-2.5-flash", # or other supported models
instruction="You are a specialized assistant for...",
description="Brief description for other agents",
tools=[tool1, tool2] # List of tools/functions
)
# Multi-agent system with sub-agents
coordinator_agent = Agent(
name="coordinator",
model="gemini-2.5-flash",
description="Coordinates multiple specialized agents",
sub_agents=[my_agent, another_agent]
)Create tools as Python functions or BaseTool classes:
# Function-based tool
async def my_custom_tool(parameter: str) -> str:
"""Tool description for the agent"""
# Tool implementation
return result
# Class-based tool
from google.adk.tools import BaseTool
class MyCustomTool(BaseTool):
def __init__(self, config):
self.config = config
async def execute(self, **kwargs):
# Tool implementation
return resultFor new Slack event handlers:
@app.event("message")
async def handle_message(body, say, logger):
"""Handle direct messages"""
logger.info(f"Message received: {body}")
# Process with SRE agent
response = await send_to_sre_agent(body['text'])
await say(response)
@app.command("/custom-command")
async def handle_custom_command(ack, body, respond):
"""Handle custom slash commands"""
await ack()
# Process command
result = await process_command(body['text'])
await respond(f"Result: {result}")Create session:
curl -X POST http://localhost:8001/apps/sre_agent/users/u_123/sessions/s_123 \
-H "Content-Type: application/json" \
-d '{"state": {"key1": "value1"}}'Send message:
curl -X POST http://localhost:8001/run \
-H "Content-Type: application/json" \
-d '{
"app_name": "sre_agent",
"user_id": "u_123",
"session_id": "s_123",
"new_message": {
"role": "user",
"parts": [{"text": "How many pods are running?"}]
}
}'-
Create Sub-Agent Module:
- Create directory:
agents/sre_agent/sub_agents/[agent_name]/ - Add subdirectories:
tools/,prompts/ - Create
agent.pywith agent configuration - Create
__init__.pywith exports
- Create directory:
-
Implement Tools: Define tools in
sub_agents/[agent_name]/tools/ -
Create Prompts: Add agent instructions in
sub_agents/[agent_name]/prompts/ -
Integrate: Import and add to main agent in
agents/sre_agent/agent.py -
Test: Use API testing commands above
-
Slack Integration: Add handlers in
slack_bot/main.pyif needed
Example structure for a new sub-agent:
agents/sre_agent/sub_agents/my_agent/
├── __init__.py # Exports agent function
├── agent.py # Agent configuration and tools
├── tools/
│ ├── __init__.py # Tool exports
│ └── my_tools.py # Tool implementations
└── prompts/
└── system_prompt.md # Agent instructions
- 'use docker compose instead of deprecated docker-compose'