Salta al contenuto principale

Costruire un Assistente SRE AI da Zero: Architettura di un Investigatore Infrastrutturale Autonomo

Indice dei contenuti
Cosa succederebbe se il tuo ingegnere di on-call non dormisse mai, avesse accesso istantaneo a ogni repository e ogni account AWS, e potesse tracciare un problema di produzione dal DNS al database in meno di un minuto? Questo articolo descrive ogni layer dell’architettura — dal sistema di autenticazione al framework agente, al registry dei tool, all’infrastruttura di streaming e al deployment.
Nota

Questo articolo descrive un assistente SRE AI costruito con l’SDK Strands Agents, Django, React e AWS. L’agente indaga autonomamente i problemi infrastrutturali combinando ragionamento LLM con integrazioni profonde in GitLab e AWS.


Il Problema
#

I team di piattaforma su larga scala affrontano una sfida ricorrente: investigare i problemi di produzione richiede correlare informazioni su più sistemi. Una tipica investigazione potrebbe essere così:

  1. Qualcuno segnala un errore 503 su un servizio
  2. Controlli lo stato del target group ALB
  3. I target sono non-sani, quindi controlli il servizio ECS
  4. ECS mostra un deployment in corso, quindi controlli la task definition
  5. Il tag dell’immagine punta a un commit, quindi controlli GitLab
  6. L’ultima MR ha cambiato un endpoint di health check, quindi leggi il codice
  7. Il codice ha un bug nel nuovo health check, quindi controlli i log di CloudWatch per confermare
  8. I log confermano l’errore, scrivi il report

Ogni passo richiede di cambiare contesto, autenticarsi su un sistema diverso, navigare un’interfaccia diversa e correlare mentalmente i risultati. Per un ingegnere esperto, questo richiede 15-30 minuti. Per uno junior, possono volerci ore.

L’assistente SRE automatizza questo intero flusso. Digiti “investiga gli errori 503 sul servizio X” e l’agente autonomamente trova il repo in GitLab, legge la struttura e la configurazione del repository, identifica l’account e la regione AWS, controlla la salute ALB, lo stato ECS e i deployment recenti, legge i log CloudWatch per pattern di errore e correla tutti i risultati in un report strutturato.

L’intuizione chiave è che l’investigazione SRE è in gran parte procedurale. I passi seguono pattern. Quello che rende bravo un buon SRE non è memorizzare quei pattern, ma sapere quali applicare e come interpretare i risultati. È esattamente ciò in cui eccellono gli LLM.


Panoramica dell’Architettura
#

L’assistente è un monolite distribuito come singolo container:

flowchart TD
    VPN([VPN]) -->|HTTPS 443| ALB[Internal ALB\nTLS 1.3]
    ALB --> Fargate[ECS Fargate\nport 8080]
    Fargate --> Nginx[Nginx]
    Nginx -->|/assets/*| SPA[React SPA\nfile statici]
    Nginx -->|/api/*| Uvicorn[Uvicorn\nDjango ASGI\nport 8000]
    Uvicorn --> PG[(Aurora PostgreSQL\nServerless v2)]
    Uvicorn --> Redis[(ElastiCache\nRedis)]
    Uvicorn --> Bedrock[AWS Bedrock\nAgentCore Memory]
    Uvicorn --> LLM[LLM Gateway\nOAuth2]

Il frontend è una SPA React servita da Nginx. Le richieste API sono proxate al backend Django in esecuzione su Uvicorn con supporto ASGI. PostgreSQL archivia utenti, sessioni, cronologia chat e snapshot KPI. Redis fa cache delle credenziali AWS e delle risposte GitLab.

Perché un monolite? Perché la complessità operativa di un’architettura a microservizi non è giustificata quando hai un singolo team, un singolo target di deployment e una singola applicazione. Il monolite si distribuisce in secondi, ha un unico set di log da controllare e un unico servizio da monitorare.


Stack Tecnologico
#

  • Python 3.12+ con Django 6.0
  • Supporto async via adrf
  • Uvicorn come server ASGI
  • strands-agents per il framework agente AI
  • httpx per client HTTP asincroni
  • msal per Microsoft Entra ID OAuth2
  • React 19 con TypeScript 5
  • Vite 7 come build tool
  • Tailwind CSS 3
  • Radix UI per primitive accessibili
  • Framer Motion per le animazioni
  • Server-Sent Events per lo streaming real-time
  • AWS ECS Fargate (Spot + On-Demand)
  • Aurora PostgreSQL Serverless v2
  • ElastiCache Redis
  • Internal ALB con WAF
  • Terraform per IaC
  • Docker multi-stage build

Autenticazione: Quattro Layer in Profondità
#

L’autenticazione non riguarda solo l’identificazione dell’utente. Riguarda la creazione di catene di fiducia su quattro sistemi distinti.

Layer 1: Microsoft Entra ID (Identità Utente)
#

L’autenticazione primaria usa il flusso OAuth2 Authorization Code con PKCE:

auth/views.py
class AuthCallbackView(APIView):
    async def post(self, request):
        code = request.data["code"]
        code_verifier = request.data["code_verifier"]

        msal_app = ConfidentialClientApplication(
            client_id=settings.ENTRA_CLIENT_ID,
            authority=f"https://login.microsoftonline.com/{settings.ENTRA_TENANT_ID}",
            client_credential=settings.ENTRA_CLIENT_SECRET,
        )

        result = msal_app.acquire_token_by_authorization_code(
            code=code,
            scopes=["User.Read"],
            redirect_uri=settings.ENTRA_REDIRECT_URI,
            code_verifier=code_verifier,
        )

        id_token = validate_and_decode_token(result["id_token"])
        user, _ = await User.objects.aupdate_or_create(
            entra_id=id_token["oid"],
            defaults={"display_name": id_token["name"]},
        )

        session = await UserSession.objects.acreate(
            user=user,
            expires_at=now() + timedelta(hours=24),
        )

        response = Response({"user": UserSerializer(user).data})
        response.set_cookie(
            "session_id", session.token,
            httponly=True, secure=True, samesite="Strict",
        )
        return response

Layer 2: Federazione SAML AWS
#

Questo è il layer di autenticazione più complesso. Gli ingegneri hanno bisogno di accesso a 100+ account AWS, ognuno con più ruoli:

sequenceDiagram
    participant User
    participant ADFS as Corporate ADFS
    participant KC as Keycloak
    participant STS as AWS STS

    User->>ADFS: credenziali (NTLM)
    ADFS-->>KC: asserzione SAML
    KC->>User: sfida OTP
    User->>KC: codice OTP
    KC-->>STS: risposta SAML AWS
    STS-->>User: Credenziali temporanee (TTL 1h)
    Note over User,STS: Cache in Redis, auto-refresh

Quando il segreto OTP è configurato, l’assistente può ri-autenticarsi automaticamente quando le credenziali scadono. Le credenziali temporanee sono in cache in Redis con un TTL che corrisponde alla loro scadenza STS.


Il Sistema Agente
#

Il cuore dell’assistente è l’agente, costruito sul framework strands-agents:

agent/builder.py
def build_agent(user, user_settings, session_id):
    model = OpenAIModel(
        model_id="gpt-5.1",
        client_args={
            "base_url": LLM_GATEWAY_URL,
            "api_key": get_gateway_token(user_settings),
        },
        params={"temperature": 0.0},
    )

    tools = []
    if user_settings.gitlab_token:
        tools.extend(GITLAB_TOOLS)
    if has_aws_credentials(user):
        tools.extend(AWS_TOOLS)
    tools.extend(CORE_TOOLS)

    return Agent(
        model=model,
        tools=tools,
        tool_executor=ConcurrentToolExecutor(),
        conversation_manager=SlidingWindowConversationManager(window_size=40),
        system_prompt=SYSTEM_PROMPT,
    )

La temperatura è impostata a 0.0 perché l’investigazione SRE richiede determinismo. Vuoi che l’agente segua le stesse procedure in modo costante.

Il System Prompt
#

Il system prompt supera le 8.000 parole. Alcune direttive chiave:

Investigazione autonoma, non consigli:

“Quando viene chiesto di investigare, tu investighi. Non suggerire cosa dovrebbe controllare l’utente. Lo controlli tu stesso.”

Mandato context-first:

“Quando viene menzionato un nome di servizio, chiama SEMPRE gather_context() prima di qualsiasi analisi.”

Zero-hallucination:

“Non fare mai riferimento a risorse AWS o progetti GitLab che non hai scoperto tramite tool call in questa conversazione.”


Registry dei Tool: 30+ Tool Specializzati
#

L’assistente ha accesso a oltre 30 tool, ognuno progettato per un compito specifico di investigazione.

tools/aws_tools.py
@tool
def aws_ecs(
    action: str,
    parameters: dict,
    account_id: str | None = None,
    region: str = "eu-central-1",
) -> dict:
    """Interroga le risorse AWS ECS (sola lettura).

    Azioni comuni: describe_services, describe_tasks, list_services,
    describe_task_definition, list_task_definitions
    """
    if not action.startswith(("describe_", "list_", "get_")):
        return {"error": f"Azione '{action}' non consentita (sola lettura)"}

    credentials = get_cached_aws_credentials(account_id)
    client = boto3.client(
        "ecs",
        region_name=region,
        aws_access_key_id=credentials["AccessKeyId"],
        aws_secret_access_key=credentials["SecretAccessKey"],
        aws_session_token=credentials["SessionToken"],
    )

    method = getattr(client, action)
    return truncate_response(method(**parameters), max_chars=12000)

Architettura Sub-Agente
#

Le investigazioni complesse spesso richiedono flussi di lavoro paralleli. L’assistente usa un pattern sub-agente:

tools/sub_agents.py
@tool
def gather_context(service_name: str) -> dict:
    """Scopre autonomamente tutto su un servizio."""
    sub_agent = Agent(
        model=model,
        tools=[gitlab, gitlab_get_file, gitlab_list_tree, gitlab_search_group],
        system_prompt=GATHER_CONTEXT_PROMPT,
    )
    result = sub_agent(
        f"Trova e analizza il servizio '{service_name}'. "
        f"Restituisci il project ID GitLab, account AWS, regione, "
        f"nome del cluster e dettagli di configurazione chiave."
    )
    return parse_context(result)

Infrastruttura di Streaming
#

Lo streaming real-time è essenziale. Le investigazioni possono richiedere 30-60 secondi. Senza streaming, l’utente fissa uno spinner senza visibilità su cosa sta accadendo.

chat/views.py
class ChatStreamView(APIView):
    async def post(self, request):
        agent = build_agent(request.user, request.user_settings, session_id)

        async def event_stream():
            queue = asyncio.Queue()
            task = asyncio.create_task(run_agent(agent, message, queue))

            while True:
                event = await queue.get()
                if event["type"] == "done":
                    yield format_sse(event)
                    break
                if event["type"] in ("text_delta", "tool_start", "tool_end"):
                    yield format_sse(event)

            await task

        response = StreamingHttpResponse(
            event_stream(),
            content_type="text/event-stream",
        )
        response["X-Accel-Buffering"] = "no"  # Disabilita il buffering Nginx
        return response

L’header X-Accel-Buffering: no è critico. Senza di esso, Nginx bufferizza lo stream SSE e lo consegna a blocchi, distruggendo l’esperienza real-time.


Schema del Database
#

erDiagram
    User ||--|| UserSettings : has
    User ||--o{ UserSession : has
    User ||--o{ ChatSession : owns
    ChatSession ||--o{ Message : contains
    Message ||--o{ ToolInvocation : triggers

    User {
        uuid id PK
        string entra_id
        string display_name
        string role
    }
    ChatSession {
        uuid id PK
        string title
        string output_mode
    }
    Message {
        uuid id PK
        string role
        text content
        json metrics
    }
    ToolInvocation {
        uuid id PK
        string tool_name
        json input_params
        string status
    }

Deployment
#

terraform/ecs.tf
resource "aws_ecs_service" "tars" {
  name          = "tars"
  desired_count = 2

  capacity_provider_strategy {
    capacity_provider = "FARGATE_SPOT"
    weight            = 100
    base              = 1
  }

  deployment_circuit_breaker {
    enable   = true
    rollback = true
  }
}

resource "aws_ecs_task_definition" "tars" {
  cpu    = 1024
  memory = 2048

  runtime_platform {
    cpu_architecture = "ARM64"
  }
}

Lezioni Imparate
#

Temperatura 0.0 per gli agenti di investigazione
L’investigazione SRE richiede determinismo. Vuoi che l’agente segua le stesse procedure in modo costante. Le risposte creative sono un rischio quando si fa debug in produzione.
I sub-agenti sono essenziali per investigazioni complesse
Un singolo agente che cerca in GitLab, legge codice, controlla AWS e analizza log perderà il filo del suo piano di investigazione. I sub-agenti con set di tool focalizzati producono risultati molto migliori.
Lo streaming non è opzionale
Le investigazioni richiedono 30-60 secondi. Senza streaming real-time delle tool call e del ragionamento, gli utenti non si fidano del sistema. Vedere il processo mentale dell’agente costruisce fiducia nei risultati.
Il troncamento dell'output previene il context collapse
Una singola chiamata get_log_events di CloudWatch può restituire megabyte di dati. Senza troncamento aggressivo (12.000 caratteri), la context window si riempie di rumore e l’agente perde il filo dell’investigazione.
Sola lettura per default, sempre
Un agente AI che può modificare l’infrastruttura è un rischio. L’assistente è in sola lettura per AWS by design. Il peggio che può accadere è una MR indesiderata, che è banalmente reversibile.
Credenziali per utente, mai condivise
Ogni utente si autentica con il proprio token GitLab e le proprie credenziali AWS SAML. Ogni azione dell’agente è tracciabile al singolo utente.
Il system prompt è il prodotto
L'80% della qualità dell’agente viene dal system prompt, non dal modello o dai tool. Investire settimane nella definizione di vincoli comportamentali precisi, procedure di investigazione e regole di formattazione dell’output paga enormemente.
Cache alla giusta granularità
La strategia di caching a bucket fissi della dashboard KPI (fetch bucket da 15/31/91 giorni indipendentemente dalla richiesta dell’utente) ha migliorato i cache hit rate dal ~20% all’~85%.

Cosa Viene Dopo
#

L’evoluzione naturale è espandere dall’investigazione alla remediation. Oggi l’assistente ti dice cosa c’è di sbagliato. Domani potrebbe:

  • Creare un branch hotfix con la modifica di codice corretta
  • Aprire una MR con la fix, contrassegnata per revisione urgente
  • Redigere il report dell’incidente basandosi sui risultati dell’investigazione
  • Suggerire aggiornamenti al runbook in base ai nuovi modi di fallimento che scopre

Questo è l’endgame: un SRE AI che indaga come il tuo miglior ingegnere, comunica come il tuo miglior technical writer e non ha mai bisogno di dormire.

Articoli correlati