Building an AI Health Agent with AWS Strands - Part 1: Architecture

29 Jan 2026

This is the first in a series documenting how I built “Stella,” a women’s health AI assistant using AWS Strands SDK, Bedrock, and Django. The system handles personal health tracking, cycle predictions, and provides evidence-based health guidance.

Series Overview

  1. Part 1: Architecture (this post) - System design and component overview
  2. Part 2: User-Scoped Tools - Binding tools to users without breaking decorators
  3. Part 3: Prompts & Guardrails - AI safety and behavior control
  4. Part 4: Anti-Hallucination - Making AI truthful about missing data
  5. Part 5: Bedrock vs Strands - Comparing managed vs self-hosted agents

The Architecture

┌─────────────────┐     ┌──────────────────┐     ┌─────────────────┐
│   Flutter App   │────▶│  Django Backend  │────▶│  Strands Agent  │
│                 │     │  (GraphQL API)   │     │    (Stella)     │
└─────────────────┘     └──────────────────┘     └────────┬────────┘
                                                          │
                        ┌─────────────────────────────────┴─────────┐
                        │                                           │
                        ▼                                           ▼
            ┌───────────────────────┐              ┌────────────────────────┐
            │   User-Scoped Tools   │              │    Amazon Bedrock      │
            │  (health_agent_tools) │              │  Claude 3.5 Sonnet     │
            └───────────┬───────────┘              └────────────────────────┘
                        │
                        ▼
            ┌───────────────────────┐
            │   Aurora PostgreSQL   │
            │   + pgvector          │
            └───────────────────────┘

Key Components

Component Purpose
Flutter App Mobile client for iOS/Android
Django Backend GraphQL API, auth, business logic
Strands Agent AI orchestration with tool calling
User-Scoped Tools Database queries bound to specific users
Bedrock (Claude) LLM for natural language understanding
Aurora + pgvector Health data storage and vector search

Why Strands SDK?

AWS Strands SDK is a Python framework for building AI agents. Compared to alternatives:

Feature Strands LangChain Raw Bedrock
Tool calling Built-in Built-in Manual
Agent loop Automatic Automatic Manual
AWS integration Native Plugin Native
Memory Configurable Configurable None
Complexity Low High Medium

Strands gives you the agent loop and tool orchestration without LangChain’s complexity. It’s essentially “Bedrock Agents, but you control the code.”


The Conversation Flow

  1. User sends message via GraphQL mutation askQuestion
  2. Django validates auth using JWT + SecureUser hash
  3. Strands agent receives query with user-scoped tools
  4. Claude decides whether to call tools or respond directly
  5. Tools query PostgreSQL for user’s health data
  6. Agent formulates response using tool results + medical knowledge
  7. Response streamed back to Flutter app

GraphQL Mutations

mutation StartConversation {
  startConversation(title: "Health Chat") {
    conversationId
    title
  }
}

mutation AskQuestion($input: AskQuestionInput!) {
  askQuestion(input: $input) {
    messageId
    content
    nudges { type message }
  }
}

User-Scoped Tools

The core challenge: tools need to query data for a specific user, but the AI shouldn’t know about user IDs. Solution: create tools that capture user_id in closures.

def create_user_scoped_tools(user_id: str) -> List[Callable]:
    @tool
    def get_cycle_history(days: int = 90) -> Dict:
        """Get user's menstrual cycle history."""
        # user_id captured in closure
        return query_user_cycles(user_id, days)
    
    @tool
    def log_symptom(symptom: str, severity: int = 5) -> Dict:
        """Log a symptom for the user."""
        return save_symptom(user_id, symptom, severity)
    
    return [get_cycle_history, log_symptom]

Why not functools.partial? It strips the @tool decorator metadata. Part 2 covers this in detail.


Available Tools

Tool Purpose
get_cycle_history Period dates, cycle length, flow data
get_tracking_summary Symptoms, moods, energy, sleep
get_cycle_prediction Next period, ovulation, fertility windows
get_hormone_levels Hormone tracking data
log_symptom Record symptoms
favorite_symptom Save symptoms to favorites
analyze_patterns Find correlations in tracking data
search_knowledge_base General health information (RAG)

Data Model

The health tracking uses a flexible measurement system:

-- Base measurement (what was tracked)
CREATE TABLE health_item_measurements (
    id UUID PRIMARY KEY,
    health_item_id UUID REFERENCES health_items(id),
    secure_user_id UUID NOT NULL,
    tracked_at TIMESTAMP WITH TIME ZONE,
    measurement_type VARCHAR(50)  -- 'RANGE', 'BOOLEAN', etc.
);

-- Severity level for RANGE measurements
CREATE TABLE health_item_range_measurements (
    id UUID PRIMARY KEY,
    measurement_id UUID REFERENCES health_item_measurements(id),
    level INTEGER  -- 1-10 scale
);

-- User favorites for quick access
CREATE TABLE health_item_favorites (
    id UUID PRIMARY KEY,
    health_item_id UUID REFERENCES health_items(id),
    secure_user_id UUID NOT NULL
);

Authentication: Two-Header System

Every authenticated request requires two headers:

x-compass-jwt-token: <JWT from login>
x-compass-hash-id: <SHA256 hash of user_id + passphrase>

The JWT identifies the account. The hash ID identifies the SecureUser for health data. This separation means:


What’s Next


Building Stella has been a journey in practical AI engineering. These patterns work in production with real users tracking real health data.