Vai al contenuto principale
Questa guida associa i sintomi che è più probabile riscontrare in produzione a una diagnosi concreta e a una soluzione, in modo che tu possa risolvere gli incidenti usando gli strumenti che già possiedi, senza dover implementare infrastrutture di osservabilità aggiuntive. Copre il server, il collector, il dashboard, l’assistente AI, Python SDK, il monitoraggio della salute e dei certificati, i backup, le analitiche supportate da ClickHouse e il multi-tenancy. Le pagine del dashboard hanno ambito dell’organizzazione sotto /<org-slug>/…, e lo stream degli eventi è la home dell’organizzazione (/<org-slug>/). I nomi delle pagine in questa guida (ad esempio /sessions, /queries) si riferiscono a questi percorsi con ambito organizzativo.

Visualizzazione dei log

AgentEye non include uno stack di logging o monitoring. Sia il server che il dashboard scrivono log strutturati su stdout, quindi puoi leggerli direttamente con kubectl o docker; non è richiesto alcun aggregatore.

Kubernetes

Segui i log live per il server e il dashboard:
Varianti utili:
ObiettivoComando
Ultime 200 righe (senza follow)kubectl logs -n agenteye -l app=server --tail=200 --timestamps
Log dal crash precedentekubectl logs -n agenteye <pod-name> --previous
Traccia tutte le repliche contemporaneamentekubectl logs -n agenteye -l app=server --max-log-requests=10 -f
Postgres (StatefulSet)kubectl logs -n agenteye postgres-0 -f

Docker Compose

Correlazione di una singola richiesta tra dashboard e server

Ogni richiesta del dashboard è etichettata con un request_id e propagata al server tramite l’intestazione x-request-id. Il server lo ripete nelle sue intestazioni di risposta e in ogni riga di log che emette per quella richiesta. Per tracciare una richiesta end-to-end:
  1. Cattura l’id dall’intestazione di risposta, ad esempio:
  2. Esegui grep nei log di entrambi i pod per quell’id:
Vedrai le righe proxy passthrough, withAuth: authorized e upstream response del dashboard insieme alla coppia http request received / http request completed del server, condividendo tutte lo stesso request_id.

Log JSON e jq

Imposta AE_LOG_JSON=1 sul dashboard (è attivato per impostazione predefinita quando NODE_ENV=production) per emettere un oggetto JSON per riga. Quindi filtra strutturalmente:
Il server Rust emette coppie di traccia key=value che grep funziona bene senza jq:

Aumento della verbosità

ComponenteVariabile d’ambienteEsempio
ServerRUST_LOGRUST_LOG=debug o RUST_LOG=agenteye_server=debug,info
DashboardAE_LOG_LEVELAE_LOG_LEVEL=debug
debug sul server aggiunge una riga api key authenticated per auth. debug sul dashboard aggiunge righe upstream request, session validated e proxy passthrough.

Conservazione dei log

Lo stdout del contenitore è effimero; kubelet ruota i file di log (default ~10 MiB per contenitore) e ne mantiene un numero ridotto su disco. Una volta eliminato un pod i log sono spariti. Se hai bisogno di una conservazione più lunga o di una ricerca cross-pod, punta il tuo cluster a un collector di log (Loki, CloudWatch, Cloud Logging, Datadog, ecc.) che traccia /var/log/containers/. AgentEye non richiede o prescrive alcuna scelta specifica.

Problemi di autenticazione

docker pull fallisce con “unauthorized”

Assicurati di aver autenticato Docker su GHCR con il tuo AGENTEYE_TOKEN:
Il token deve avere permesso read:packages sull’org agenteye-enterprise. Contatta support@exosphere.host se il tuo token non funziona.

gh release download restituisce 404 o 401

  • Conferma che AGENTEYE_TOKEN è esportato nella tua shell: echo $AGENTEYE_TOKEN
  • Conferma che stai usando GITHUB_TOKEN=$AGENTEYE_TOKEN gh release download ... (la CLI gh legge GITHUB_TOKEN)
  • Il token ha bisogno di contents:read su agenteye-enterprise/releases

Problemi del server

Il server fallisce con “invalid port number”

La POSTGRES_PASSWORD (o un’altra credenziale) contiene caratteri speciali URL (/, +, =) che interrompono l’analisi di DATABASE_URL. Rigenera la password utilizzando la codifica hex:
Quindi aggiorna il secret Kubernetes e la password all’interno di Postgres (o ricrea il .env per Docker Compose), e riavvia il server. Vedi i passaggi completi in enterprise-docs/kubernetes-deployment.md § “PostgreSQL credentials”.

Il server esce immediatamente all’avvio

Controlla i log del contenitore:
Cause comuni:
  • DATABASE_URL non impostato o malformato: il server registrerà l’errore e uscirà.
  • Postgres non è raggiungibile: conferma che il contenitore Postgres o il DB gestito è in esecuzione e che host/porta sono corretti.
  • Le migrazioni sono fallite: controlla i log per errori SQL.

GET /health restituisce non-200 o timeout

Il server potrebbe ancora eseguire le migrazioni al primo avvio. Attendi alcuni secondi e riprova:
Se il problema persiste, controlla docker logs agenteye-server per gli errori.

GET /ready restituisce 503

/ready è il probe di readiness: restituisce 503 quando il server non riesce a raggiungere Postgres o ClickHouse. Il corpo nomina la dipendenza non funzionante:
Correggi qualunque dipendenza riporta come down: il pod ClickHouse/Postgres è Running? CLICKHOUSE_URL / DATABASE_URL è corretto e raggiungibile? Su Kubernetes il pod legge NotReady finché /ready non si recupera; è previsto ed è esattamente il segnale su cui gli alert di monitoraggio della salute si basano. Redis non è mai una causa: è riportato ma non fa fallire la readiness.

Il collector restituisce 401 Unauthorized

La chiave API del collector non ha l’autorizzazione events:add, oppure la chiave è stata disabilitata. Crea una nuova chiave con il permesso corretto:

Le richieste autenticate sono improvvisamente diventate lente (~200ms invece di ~5ms)

Questo è il sintomo di Redis che è inattivo mentre REDIS_URL è impostato. Ogni chiamata cache timeout dopo 100ms e poi fallisce in Postgres; sui percorsi auth e OTP la richiesta effettua due tali fallimenti. Conferma nei log del server:
Risoluzione:
  1. redis-cli -h <your-redis> ping per confermare che Redis è raggiungibile sulla rete del cluster.
  2. Se Redis era brevemente inattivo ed è ora di nuovo disponibile, riavvia i pod del server. redis::aio::ConnectionManager non ristabilisce in modo affidabile dopo che la connessione sottostante si interrompe; un riavvio del pod raccoglie la nuova connessione in modo pulito. Lo stesso vale per il dashboard.
  3. Se non vuoi eseguire Redis in questo momento, cancella REDIS_URL nell’implementazione e riavvia. Entrambi i servizi funzionano senza la cache (la correttezza è conservata; la latenza ritorna alla baseline pre-Redis).

Il server riporta OTP request rate-limited nei log ma l’utente dice che ha provato solo una volta

Controlla se Redis era irraggiungibile. Il percorso di fallback utilizza SELECT COUNT(*) FROM otp_codes WHERE created_at > now() - interval '15 minutes', che vede le righe OTP precedentemente generate. Se l’utente è stato a fare clic ripetuto su “Resend” per un’ora, la finestra di 15 minuti potrebbe ancora contenere ≥5 codici. Risolvi aspettando che la finestra scorra o eseguendo DELETE FROM otp_codes WHERE user_id = $1 AND created_at > now() - interval '15 minutes' (console dell’operatore).

Ho cambiato ALLOWED_EMAILS / SESSION_TTL_SECS / OTP_TTL_SECS e riavviato; nulla è cambiato

Queste variabili d’ambiente sono seed del primo avvio solo. Una volta che la tabella settings ha una riga per la chiave corrispondente, quella riga è la fonte di verità; la variabile d’ambiente viene letta una volta al primo avvio e quindi ignorata in ogni riavvio successivo. Per cambiarli dopo il primo avvio, accedi al dashboard e modificali in /settings. Il cambiamento si applica entro pochi secondi su tutte le repliche; nessun riavvio richiesto. Se hai bisogno di forzare un re-seed da env (raro, tipicamente utile solo in sviluppo), esegui DELETE FROM settings WHERE key = '<key>' e riavvia il server. Il bootstrap raccoglierà il valore della variabile d’ambiente corrente al prossimo avvio. La modifica tramite /settings è il percorso supportato in produzione.

Problemi del collector

Il collector si avvia ma gli eventi non appaiono nel dashboard

  1. Conferma che il collector è in esecuzione: systemctl status agenteye-collector (Linux) o controlla il processo.
  2. Conferma che AGENTEYE_URL punta a http(s)://your-server-host:8080/events (nota: percorso /events).
  3. Esegui un flusso una tantum per vedere l’output immediato:
  4. Controlla che Python SDK stia effettivamente scrivendo file: ls ${AGENTEYE_HOME:-~/.agenteye}/events/
  5. Se esistono file in ${AGENTEYE_HOME:-~/.agenteye}/failed/, i caricamenti stanno fallendo. Controlla i log del collector per l’errore, probabilmente un 4xx (chiave o URL errato) o un problema di rete.

I file si stanno accumulando in $AGENTEYE_HOME/events/ e non vengono caricati

  • Il collector potrebbe non essere in esecuzione. Avvialo: agenteye-collector start; scarica automaticamente gli eventi preesistenti all’avvio.
  • Controlla la salute del collector: agenteye-collector health
  • Il collector potrebbe essere in esecuzione ma non riuscire a raggiungere il server. Controlla le regole firewall tra gli host collector e server.

File in $AGENTEYE_HOME/failed/

I file si spostano in failed/ dopo che tutti i tentativi di ripetizione sono esauriti (predefinito: 5 tentativi con backoff esponenziale). Ciò significa che:
  • Il server ha restituito un errore 4xx (chiave errata, URL sbagliato o problema di payload)
  • Il server era irraggiungibile per l’intera finestra di ripetizione
Correggi il problema sottostante, quindi ricoda manualmente:

Il collector riporta network error su ogni caricamento (handshake TLS fallisce)

Se curl -k contro AGENTEYE_URL ha successo ma il file binario del collector fallisce ogni caricamento con error sending request for url (...), il server AgentEye sta presentando un certificato TLS che non è firmato da una CA pubblicamente attendibile. Il percorso di produzione è il nome host di ingestion ACME configurato in deploy/base/certificates/domain.env (vedi kubernetes-deployment.md Fase 3.1 / 4.2). Una volta che INGEST_DOMAIN si risolve verso il public Traefik LB e cert-manager ha emesso il certificato Let’s Encrypt, i collector verificano il certificato del server rispetto all’archivio di fiducia del sistema senza AGENTEYE_TLS_CA necessario; cancellalo dalla configurazione del collector se era impostato rispetto a un’implementazione autofirmata più vecchia. Sintomo: il collector ha funzionato ieri, fallisce oggi dopo un intervallo di ~90 giorni. Ciò significa che l’implementazione è ancora sull’issuer selfsigned legacy per ingest-tls. Il certificato di 90 giorni ha ruotato e il file CA ancorato è obsoleto. Correggi permanentemente passando il cluster all’issuer ACME (Fase 3.1 della guida di implementazione). Sblocco a breve termine: estrai nuovamente il certificato del server corrente e aggiorna AGENTEYE_TLS_CA:
AGENTEYE_TLS_CA aggiunge un ancoraggio di fiducia aggiuntivo; le radici pubbliche standard sono ancora attendibili.

Il certificato ingest-tls è bloccato con Ready: False dopo la distribuzione

Guarda gli Events e l’Order / Challenge a cui si fa riferimento. Cause comuni:
  • DNS non risolve verso il public LB. Il validatore HTTP-01 non riesce a raggiungere INGEST_DOMAIN. Verifica con dig +short INGEST_DOMAIN; dovrebbe risolvere allo stesso indirizzo del EXTERNAL-IP del LoadBalancer traefik-public. cert-manager ritenta automaticamente una volta propagato il DNS; non è necessario eliminare il certificato.
  • Porta 80 bloccata al load balancer / security group. HTTP-01 richiede che la porta 80 sia raggiungibile dai validatori pubblici di Let’s Encrypt. Se hai un WAF a monte o SG che limita :80, aprilo (la configurazione di Traefik reindirizza a HTTPS, ma Boulder segue il reindirizzamento e accetta la risposta).
  • dnsNames non sostituito. Se kubectl get certificate ingest-tls -n agenteye -o jsonpath='{.spec.dnsNames}' mostra INGEST_DOMAIN_PLACEHOLDER, hai saltato il passaggio domain.env; crealo da domain.env.example e riapplica.
  • Rate limited da Let’s Encrypt. Gli ordini ripetuti non riusciti per lo stesso nome host attivano i limiti di certificato duplicato o convalida non riuscita. Attendi almeno un’ora prima di riprovare; controlla lo stato dell’ordine per il messaggio di rate-limit esatto.

Il certificato dashboard-tls è bloccato con Ready: False / il browser mostra ancora un avviso

Lo stesso flusso di diagnosi di ingest-tls sopra (kubectl describe certificate dashboard-tls -n agenteye); le cause DNS, porta-80, placeholder e rate-limit si applicano tutte, più due specifiche del dashboard:
  • DASHBOARD_DOMAIN risolve al LoadBalancer sbagliato. Deve puntare al LB Traefik del dashboard, non a quello di ingestion pubblico. Esegui dig +short sul nome host e confronta con l’indirizzo del LB del dashboard.
  • L’istanza Traefik del dashboard non può servire la sfida. Deve essere installata con il file di valori del dashboard bundle, che abilita un provider Ingress con ambito per il risolutore HTTP-01 di cert-manager. Senza di esso il risolutore non è instradabile e l’ordine rimane pending per sempre. Aggiorna l’istanza con i valori forniti; la sfida pendente si completa quindi da sola.
  • Il LoadBalancer era limitato IP. I range di origine si applicano anche alla porta 80, il che blocca i validatori di Let’s Encrypt — sia il primo rilascio che ogni rinnovo di ~75 giorni. Riapri il LB, o coordina un risolutore DNS-01 con il supporto prima di bloccarlo.
Durante il fallimento dell’emissione, il dashboard continua a servire il suo certificato precedente (o il valore predefinito dell’ingress su un’installazione nuova) — l’accesso è degradato da un avviso del browser, mai inattivo.

La CLI continua a saltare la verifica TLS dopo che il dashboard ha ottenuto un certificato attendibile

--insecure è persistito su cli.json al login. Una volta che il dashboard serve un certificato pubblicamente attendibile, accedi di nuovo con agenteye --base-url https://<your-dashboard-domain> --secure login; la verifica viene salvata di nuovo e l’avviso di avvio scompare.

Problemi del dashboard

Impossibile disabilitare o modificare l’utente ADMIN_EMAIL

Per progettazione. L’utente che corrisponde a ADMIN_EMAIL è contrassegnato come protetto ad ogni avvio del server: il dashboard nasconde il pulsante Disabilita per quella riga e l’API rifiuta DELETE /users/:id e PUT /users/:id contro di essa con 403 Forbidden. Un trigger del database rifiuta anche istruzioni UPDATE dirette che disabiliterebbero la riga protetta. Per ruotare l’admin di bootstrap, cambia ADMIN_EMAIL nel tuo ambiente e riavvia il server. Il nuovo email è upsertato come protetto. L’admin precedente mantiene il flag protetto fino a quando non viene cancellato nel database (tipicamente va bene, poiché l’email precedente è comunque un admin valido fino a quando non lo rimuovi esplicitamente).

Il dashboard non mostra eventi

  1. Conferma che l’URL del server e la chiave API sono corretti nelle variabili d’ambiente del dashboard (AGENTEYE_SERVER_URL, AGENTEYE_API_KEY).
  2. La chiave API del dashboard ha bisogno dell’autorizzazione events:read.
  3. Conferma che gli eventi sono stati effettivamente acquisiti: curl http://your-server:8080/events -H "Authorization: Bearer $ADMIN_KEY"

/errors è vuoto ma /events mostra righe rosse

Le versioni più recenti di SDK emettono guasti come eventi agent_end / tool_result / hook_completed con outcome: "error" nel payload, anziché come una riga event_type: "error" dedicata. La pagina /errors ora corrisponde a entrambi: qualsiasi riga che il flusso /events pinta di rosso (esplicito event_type='error', payload outcome/status nell’insieme di guasto, is_error: true, o un campo error veritiero) appare su /errors. Se in precedenza hai visto “nessun errore in questa finestra” mentre le righe rosse erano visibili su /events, aggiorna il dashboard + server insieme (il filtro ampliato è errored=true su GET /events) e le due viste saranno d’accordo.

/models, /tools o /hooks è lento o non riesce a caricare su ampi intervalli di tempo

Sintomo: su una grande tabella di eventi (milioni di righe), l’apertura di /models, /tools o /hooks — o l’ampliamento dell’intervallo di tempo a 7d, 30d o all — i grafici girano e poi mostrano un errore di caricamento. Il server registra un ClickHouse MEMORY_LIMIT_EXCEEDED (Codice 241) o un timeout della query per la richiesta latency_aggregate. Causa: le build più vecchie calcolavano i rollup di latenza e distribuzione di queste pagine con una query che leggeva il payload dell’evento grezzo completo e associava gli eventi di richiesta/risposta con un’ordinazione e un join in memoria. Il picco di memoria della query quindi cresceva con la dimensione della finestra, quindi su un tenant occupato un’ampia gamma potrebbe superare il ceiling di memoria per query di ClickHouse. Correzione: aggiorna a una build che include questa correzione. Il rollup ora legge solo le colonne promosse compatte e associa gli eventi con un’aggregazione di streaming, quindi il picco di memoria non scala più con il payload grezzo — le finestre larghe rimangono ben all’interno del ceiling di memoria e vengono restituite in una frazione del tempo. Il miglioramento è interamente lato query: si applica a tutti i dati esistenti al successivo caricamento della pagina, senza ricoingestione o backfill.

Il dashboard non riesce a caricare / pagina vuota

Controlla i log del contenitore del dashboard:
La causa più comune è che AGENTEYE_SERVER_URL o AGENTEYE_API_KEY manchi o punti a un server irraggiungibile.

Analitiche/telemetria del dashboard

Il dashboard invia analitiche sull’utilizzo dei prodotti anonime a PostHog per impostazione predefinita, instradate tramite il percorso /ingest del dashboard stesso (un proxy inverso a https://us.i.posthog.com). L’invio in primo piano significa che i blocker di annunci del browser non li eliminano. Questo è indipendente dalla funzionalità principale del dashboard:
  • Il contenitore dashboard (non il browser) è ciò che raggiunge PostHog. Se il suo accesso in uscita a https://us.i.posthog.com è bloccato, la telemetria silenziamente non-op; il dashboard funziona normalmente e nessun errore viene visualizzato agli utenti.
  • Non vengono mai inclusi dati di agenti, sessioni o eventi, solo l’utilizzo dell’UI del dashboard.
  • Per disabilitare la telemetria interamente, imposta AE_ANALYTICS_DISABLED=1 sul contenitore del dashboard e riavvia. Vedi Telemetria & privacy nella guida di distribuzione.

Telemetria della CLI / telemetria

La CLI agenteye invia analitiche sull’utilizzo anonime a PostHog per impostazione predefinita: quali comandi vengono eseguiti, stato di successo/uscita e durata. Questo è indipendente dalla funzionalità della CLI:
  • La macchina che esegue la CLI raggiunge direttamente https://us.i.posthog.com. Se il suo accesso in uscita è bloccato, la telemetria silenziamente non-op (l’invio è limitato nel tempo, quindi non ritarda mai un comando) e la CLI funziona normalmente.
  • Non vengono mai inclusi dati di agenti, sessioni o eventi: gli argomenti e i valori del flag del comando (URL del dashboard, token, email, ID della sessione, filtri di query) non vengono mai inviati.
  • Per disabilitarlo, imposta AGENTEYE_ANALYTICS_DISABLED=1 (o il cross-tool DO_NOT_TRACK=1) nell’ambiente della CLI. Vedi Telemetria & privacy nella guida della CLI.

Problemi dell’assistente AI

Vedi enterprise-docs/assistant.md per la configurazione completa.

La bolla dell’assistente non appare

La bolla è nascosta a meno che tutti questi non siano veri:
  • L’utente connesso ha l’autorizzazione agent:use.
  • AGENTEYE_AGENT_URL è impostato sul dashboard e il servizio agent è raggiungibile.
  • Un endpoint LLM è configurato sul servizio agent (ANTHROPIC_API_KEY, un gateway tramite ANTHROPIC_BASE_URL, o Bedrock/Vertex). Senza alcuno impostato, l’agente riporta “non configurato” e la bolla rimane nascosta.
Controlla la salute dell’agente dall’host del dashboard: curl http://agent:9100/health dovrebbe restituire {"status":"ok","llm_configured":true,...}.

L’assistente dice che non può leggere qualcosa

Gli strumenti sono controllati per utente. Se un utente non dispone di evaluations:read (o events:read, dashboards:read), gli strumenti corrispondenti non vengono offerti e l’assistente dirà che non può leggere quei dati. Concedi il permesso di lettura pertinente.

”assistente non configurato” (HTTP 503) durante l’invio

Il contenitore agent non ha un endpoint LLM configurato, oppure il AGENTEYE_AGENT_TOKEN del dashboard non corrisponde a quello dell’agente. Imposta entrambi e riavvia.

Il contenitore agent si riavvia / OOM sotto carico

Ogni conversazione genera un processo figlio di breve durata. Assicurati che il contenitore venga eseguito con un processo init (l’immagine usa tini; in Compose imposta init: true) e dagli limiti di memoria adeguati. Riduci AGENTEYE_AGENT_MAX_STEPS se necessario.

Problemi della CLI

agenteye non riesce ad avviarsi con ModuleNotFoundError: No module named 'click'

Un’installazione nuova della CLI agenteye alla versione 0.1.6 può bloccarsi all’avvio con:
0.1.6 si affidava a click per essere installato indirettamente da typer; i rilasci typer attuali non lo estraggono più, quindi un ambiente pulito finisce per mancare il pacchetto. Aggiorna a 0.1.7 o versioni successive, che dipendono da click direttamente:
Vedi enterprise-docs/cli.md per la guida di installazione.

Problemi di Python SDK

Nessun file che appare in $AGENTEYE_HOME/events/

L’SDK memorizza nel buffer gli eventi e scarica ogni 500 ms per impostazione predefinita. Se il tuo processo esce prima dello scaricamento, gli eventi potrebbero andare persi. Chiama agenteye.configure(flush_interval=0.1) per uno scaricamento più veloce negli script di breve durata, o assicurati che il tuo processo sia in esecuzione abbastanza a lungo per un ciclo di scaricamento. Se AGENTEYE_HOME è impostato, verifica che l’SDK stia scrivendo in $AGENTEYE_HOME/events/ e non in ~/.agenteye/events/ (richiede SDK ≥ 0.0.1b5).

ValueError: Reserved field names cannot be used as custom fields

I nomi timestamp, type e environment sono riservati e non possono essere usati come campi personalizzati. Passare uno qualsiasi di loro solleva:
Rinomina il campo personalizzato offensivo. Nota che session_id e agent_id sono parametri espliciti della chiamata dell’evento, non campi personalizzati; passare uno di essi nuovamente come campo personalizzato solleva TypeError.

Problemi di monitoraggio della salute

Nessun avviso in arrivo su Slack (Robusta)

L’avviso della salute di Robusta è opt-in; non invia nulla finché non è installato e puntato a un canale Slack. Verifica il rilascio e il suo sink:
Cause comuni: api_key / slack_channel di Slack non erano impostati (o il token è stato revocato); api_key è un token di relay cloud di Robusta (robusta integrations slack) ma il disableCloudRouting: true in bundle ha bisogno di un bot token Slack self-hosted (xoxb-…), o imposta disableCloudRouting: false; l’ambito del sink esclude lo spazio dei nomi in cui i tuoi pod vengono eseguiti (i valori in bundle hanno ambito agenteye); o ancora non c’è stata alcuna interruzione. Forza un avviso di test eliminando un pod:
Vedi enterprise-docs/health-monitoring.md per l’installazione e la configurazione.

Il server continua a fluttuare NotReady

Il probe di readiness colpisce /ready, che fallisce quando Postgres o ClickHouse è irraggiungibile. Se il server cicla dentro e fuori da NotReady, una dipendenza è intermittentemente non disponibile; controlla i pod di ClickHouse e Postgres e il CLICKHOUSE_URL / DATABASE_URL del server. Conferma cosa riporta /ready:
Questo probe è deliberatamente tollerante (un generoso threshold di fallimento), quindi la fluttuazione sostenuta indica un vero problema di dipendenza piuttosto che un probe troppo aggressivo. La liveness rimane su /health, quindi la fluttuazione della readiness non riavvierà il pod.

Problemi di monitoraggio dei certificati

CronJob non sta inviando notifiche Slack

cert-renewal-check CronJob richiede un URL webhook Slack archiviato in un Secret. Verifica che esista:
Se manca, crealo:
Senza il secret, CronJob continua a essere eseguito e registra i risultati su stdout. Controlla i log con:

Il certificato client è scaduto prima che venisse ricevuta una notifica

CronJob viene eseguito ogni 12 ore. Se non è stato eseguito, controlla il suo stato:
Attiva un controllo manuale:
Per ri-emettere immediatamente il certificato scaduto:
Quindi applica il collector-mtls-secret.yaml rigenerato nel cluster(i) che esegue i collector e riavviali:

Problemi di backup

agenteye-backup fallisce con “No space left on device”

CronJob agenteye-backup scarica Postgres + ClickHouse in un backup-tmp volume scratch emptyDir (predefinito 30Gi), quindi effettua lo streaming dell’archivio tar direttamente verso S3 — l’archivio compresso non viene mai riscritto sul scratch, quindi lo scratch deve solo contenere i dump grezzi, non dump + una seconda copia di archivio su disco. Un pod evitato / No space left on device significa quindi che i dump grezzi superano la dimensione del scratch (il dump events di ClickHouse domina e cresce nel tempo). Controlla i log del job non riuscito:
Correzione: nel tuo overlay, aumenta il sizeLimit dell’emptyDir del backup-tmp di CronJob sopra il totale del dump grezzo, e assicurati che il dispositivo di archiviazione effimera del nodo possa effettivamente contenerlo (sizeLimit è un cap, non una prenotazione). Se i dump superano il disco di un singolo nodo, sostituisci l’emptyDir con un PVC (EBS/PD) per backup-tmp, o comprimi i dump all’origine.
Le versioni più vecchie scrivevano il .tar.gz nello stesso scratch 20Gi dei dump, quindi dump + archivio lo ha superato e il pod è stato evitato prima che l’upload fosse eseguito — il che sembra un guasto S3 ma è veramente disco. Lo streaming dell’upload rimuove quel raddoppio.

agenteye-backup fallisce installando curl

Il job viene eseguito sull’immagine postgres:16 e installa curl all’avvio per il dump HTTP di ClickHouse. Su un cluster senza uscita verso i mirror del pacchetto Debian, il passaggio apt-get fallisce. Consenti quel egress dal pod di backup, o costruisci curl in un’immagine di backup personalizzata/con mirror e referenziala nel tuo overlay.

agenteye-backup viene eseguito ma nulla arriva nell’object storage

La base spedisce un vero BACKUP_BUCKET (ts-prod-agenteye/backups) e il ServiceAccount agenteye-backup. Il job effettua lo streaming dell’archivio verso S3 (tar cz … | aws s3 cp - s3://…). Se il pod di backup non ha accesso in scrittura al bucket, l’upload fallisce — e poiché lo script viene eseguito in set -euo pipefail, un fallimento in qualsiasi punto del pipe fallisce l’intero job al passaggio upload piuttosto che silenziare non-op (il trap EXIT del pod registra backup FAILED during step: upload). Questo è anche il passaggio che raggiungi dopo aver corretto un’evizione di spazio scratch, quindi se i backup erano precedentemente evitati al passaggio di archivio, verifica che l’upload ora arriva. Grep il log del job non riuscito per l’errore di accesso S3:
Correzione: nel tuo overlay imposta BACKUP_BUCKET su un bucket che possiedi e annota il ServiceAccount agenteye-backup esistente con accesso in scrittura (IRSA / Workload Identity / Pod Identity). Vedi la sezione Backups di enterprise-docs/kubernetes-deployment.md.

Valutazioni supportate da ClickHouse / sessioni / query

La barra laterale della pagina /queries è vuota dopo l’aggiornamento

Sono previste tre tabelle (events, evaluations, agent_sessions). Se la barra laterale SchemaBrowser è vuota dopo l’aggiornamento, il server non ha applicato il DDL di ClickHouse all’avvio. Controlla i log del server per failed to apply CH DDL statement:
La causa più comune è che ClickHouse non sia raggiungibile durante l’esecuzione delle migrazioni. Il server rifiuta di avviarsi se non riesce a raggiungere CH, quindi un pod bloccato di solito ha un CrashLoopBackOff piuttosto che una pagina di query silenziosamente interrotta, ma un’applicazione DDL parziale (un’istruzione OK, i prossimi 5xx) lascia lo schema mezzo-cotto. Riavvia il pod del server dopo che è stato verificato che CH è raggiungibile:

Le nuove valutazioni non appaiono in /sessions o /queries

Dopo l’aggiornamento, le nuove valutazioni vengono scritte su ClickHouse, non su Postgres, e appaiono in /sessions (con gatekeeping su evaluations:read) e in /queries. Se non appaiono:
  1. Conferma che la pipeline dell’evaluator sia abilitata (EVALUATOR_ENDPOINT impostato sul server) e stia producendo risultati terminali; controlla le righe evaluation_finalized.
  2. Conferma che CH sia raggiungibile dal server: kubectl exec -n agenteye deploy/server -- curl -fsS http://clickhouse:8123/ping.
  3. Spot-check la tabella CH: kubectl exec -n agenteye clickhouse-0 -- clickhouse-client -q 'SELECT count() FROM agenteye.evaluations'.

Le query falliscono sotto carico con “Memory limit exceeded”, oppure ClickHouse è OOMKilled

Sintomo: sotto carico pesante di dashboard/query, le pagine analitiche (lo stream di eventi, /sessions, la visualizzazione latenza/modelli, l’editor SQL) iniziano a fallire o timeout; il server brevemente fluttua NotReady; e il pod di ClickHouse mostra un conteggio di riavvio in aumento. Questo è quasi sempre memoria, non CPU o disco. Conferma che sia memoria (non un problema di throughput che la replica risolverebbe):
  1. Controlla il pod per kill da mancanza di memoria:
    Reason: OOMKilled / Exit Code: 137 con un conteggio di riavvio crescente è il contrassegno.
  2. Chiedi a ClickHouse cosa sta rifiutando:
    Un grande conteggio di MEMORY_LIMIT_EXCEEDED è la firma. Il messaggio legge “maximum: N GiB” — quel N è 0.9 × il limite di memoria del pod (il max_server_memory_usage_to_ram_ratio in deploy/base/clickhouse/configmap.yaml). Se le tue letture pesanti hanno bisogno di più di N, vengono rifiutate.
  3. Escludi le cose che non sono il problema — se CPU, conteggio parti e disco sono tutto basso, aggiungere repliche/sharding sarebbe costo sprecato:
Causa: il limite di memoria del pod di ClickHouse è troppo piccolo per il set di lavoro analitico. Le letture più pesanti estraggono la colonna JSON payload grezza, eseguono JSONExtract* su di essa, e usano FINAL — ognuna può avere bisogno di diversi GiB. Se le cache configurate (mark_cache_size + uncompressed_cache_size) sono più grandi del pod, le compongono: le cache sono caricate contro lo stesso budget e affollano la memoria di query. Correzione — scala la memoria di ClickHouse:
  1. Aumenta il limite di memoria di ClickHouse nel tuo overlay patchando le resources del contenitore StatefulSet clickhouse (lo stesso meccanismo di overlay usato per le resources degli altri componenti). Il budget del server utilizzabile è 0.9 × limit, quindi un limite 6Gi dà ~5.4 GiB, 16Gi dà ~14 GiB. Imposta anche requests.memory a un floor vero, quindi lo scheduler lo prenota. L’applicazione di questo ricrea il pod CH (singola replica → ~30–60s di downtime analitico); fallo in una finestra di traffico basso.
  2. Mantieni le cache in deploy/base/clickhouse/configmap.yaml proporzionate al limite — cache piccole (pochi centinaio MiB) sono sicure su un pod piccolo; aumentale solo insieme a un corrispondente aumento del limite di memoria. max_memory_usage per query è impostato esplicitamente nel profilo users.xml (vedi la sezione di nodo fisso di seguito) e viene mantenuto sotto il cap a livello di server (0.9 × limit) quindi nessuna singola query è consentita più RAM del contenitore.
  3. Se il nodo stesso è il ceiling, controlla la memoria dell’host che ClickHouse può vedere:
    Se è solo un po’ più in alto del limite del pod, sposta ClickHouse su un nodo più grande (ottimizzato per memoria) — tramite un selettore di nodo/affinità nel tuo overlay — prima di aumentare ulteriormente il limite.
Quando non puoi aggiungere memoria: esegui le query in RAM e fallisci velocemente — non versare su un disco lento. Se il nodo è fisso e il pod non può crescere, cappare quello che qualsiasi singola query può usare (quindi una query non può prendere l’intero nodo) e, su un disco dati lento (non-SSD), non lasciare che le aggregazioni/ordinamenti grandi si versino su disco. Versare su un disco lento è più lento del timeout di lettura del client del server, quindi una query che versa restituisce un dashboard 500 a metà volo mentre ClickHouse continua a macinare — mantenere le query in RAM e rifiutare la rara sovraspesa velocemente (MEMORY_LIMIT_EXCEEDED, sub-secondo) è ciò che ripristina il caricamento. Nota una gotcha di ClickHouse per l’applicazione di questi:
  • Questi sono impostazioni di profilo, e ClickHouse legge <profiles> solo da users_config (users.xml / users.d/*.xml) — mai da config.d. Un blocco <profiles> posto in config.d/agenteye.xml è silenziosamente ignorato (max_execution_time, max_memory_usage, ecc. semplicemente non si applicano). La configurazione in bundle quindi li spedisce come una chiave users.xml su ConfigMap clickhouse-config, montata in /etc/clickhouse-server/users.d/agenteye.xml.
  • I default spediti: max_memory_usage (per-query ceiling — una query non può consumare l’intero budget del server), max_bytes_before_external_group_by / max_bytes_before_external_sort = 0 (versamento disabilitato) quindi le query rimangono in RAM invece di strisciare sul disco lento, e max_execution_time (guardia runaway, allineato al timeout di lettura del client del server).
  • Verifica che siano live (questo è anche come tu rilevi la gotcha di config.d):
    Aspettati un max_memory_usage diverso da zero e max_bytes_before_external_group_by = 0. Se max_memory_usage legge 0/default, il profilo non viene applicato — controlla che le impostazioni vivano in un mount users.d, non config.d.
Trade-off: con versamento disabilitato, una query il cui set di lavoro eccede max_memory_usage è rifiutato (MEMORY_LIMIT_EXCEEDED) piuttosto che completarsi lentamente — su un disco lento quel rifiuto veloce è preferibile, perché una query che versa supererebbe il timeout del client e fallirebbe comunque. Se il tuo disco dati è veloce (SSD), puoi invece aumentare le soglie di max_bytes_before_external_* per lasciare che le query grandi si versino su disco e si completino.

Multi-tenancy (organizzazioni)

Errori durante l’aggiornamento che abilita le organizzazioni (pod del server vecchio/nuovo misti)

Sintomo: durante un’implementazione rolling della release che abilita l’org, alcuni richieste falliscono: i log del server mostrano there is no unique or exclusion constraint matching the ON CONFLICT specification nel percorso api_keys, e/o gli avvisi/Slack/canali webhook smettono di funzionare mentre il rollout è in volo. Causa: l’aggiornamento sostituisce il vecchio indice univoco a livello di istanza su api_keys(name) con indici parziali per-org, e sposta le impostazioni del canale di allerta (e default_user_permissions) dalla tabella settings globale all’org per-org org_settings. Un pod del server vecchio ancora emette ON CONFLICT (name) (ora nessun constraint corrispondente) e legge ancora la configurazione del canale dalle vecchie righe settings (ora vuote). I pod vecchi e nuovi non possono coesistere in sicurezza per questi due percorsi. Correzione: non eseguire lentamente roll questo particolare aggiornamento su versioni miste. Passare in modo pulito: scalare il server vecchio a zero (o utilizzare una breve finestra di manutenzione) e portare la nuova versione con le sue migrazioni, piuttosto che eseguire repliche vecchie e nuove fianco a fianco. Il traffico normale e l’ingest riprendono immediatamente dopo il cutover; questo influisce solo sulla finestra di transizione della versione.

Il provisioning di un’organizzazione fallisce su CREATE USER / CREATE ROW POLICY, oppure un’org può leggere i dati di un’altra org

Sintomo: la creazione di un’org restituisce un errore che menziona CREATE USER, CREATE ROW POLICY, o “access management is disabled”; o, peggio, i membri di un’org vedono gli eventi/valutazioni di un’altra org nell’editor SQL o nell’assistente. Causa: l’isolamento per-org è applicato da un utente ClickHouse dedicato + politica di riga per org. Questo richiede access management di SQL per essere abilitato e users_without_row_policies_can_read_rows=false su ClickHouse. Con access management spento, il provisioning non può creare l’utente/politica; con il default della politica di riga lasciato al suo valore permissivo, un utente che ha SELECT ma nessuna politica legge tutte le righe (fail-open). Correzione: usa la configurazione deploy/base/clickhouse/ in bundle, che imposta entrambe. Se esegui la tua configurazione ClickHouse, abilita SQL access management sull’utente server-interno e imposta users_without_row_policies_can_read_rows=false (vedi deploy/base/clickhouse/configmap.yaml), quindi riavvia ClickHouse e ricrea l’org con la CLI agenteye-orgctl (vedi enterprise-docs/tenant-management.md).

Gli utenti dell’org perdono l’accesso a ClickHouse dopo il cambiamento di ORG_CH_SECRET

Sintomo: l’editor SQL e l’assistente AI improvvisamente restituiscono errori di autenticazione ClickHouse per ogni organizzazione, immediatamente dopo che ORG_CH_SECRET è stato modificato o impostato in modo incoerente su repliche. Causa: la password ClickHouse di ogni org è derivata come HMAC di ORG_CH_SECRET. Ruotarlo (o eseguire repliche con valori diversi) invalida le credenziali ClickHouse memorizzate di ogni org; la password derivata non corrisponde più all’utente provisioning. Correzione: imposta ORG_CH_SECRET su un unico valore forte prima di provisioning una seconda org e mantienilo stabile e identico su ogni replica del server. Il riconciliarsi del tempo di avvio del server riprovvede l’utente ClickHouse di ogni org dal secret corrente all’avvio, quindi un riavvio del server su tutte le repliche (con il secret coerente) guarisce gli utenti orfani. Tratta il valore come un secret di lunga durata; non ruotarlo casualmente. Come rete di sicurezza, se ORG_CH_SECRET è lasciato al default di sviluppo built-in (cioè non impostato), il riconciliarsi del tempo di avvio salta le organizzazioni non predefinite e registra un errore anziché riscrivere le loro credenziali ClickHouse al valore dev pubblicamente noto, quindi una singola replica che si riavvia senza il secret non può rompere le altre repliche. Imposta il secret in modo coerente e riavvia per provisioning quelle org.

L’assistente AI restituisce 400 / rifiuta di chattare dopo l’abilitazione delle organizzazioni

Sintomo: la dock dell’assistente si carica ma ogni messaggio torna con un errore (HTTP 400), e l’agente registra una richiesta /chat senza org rifiutata. Causa: l’agente è consapevole dell’org e fallisce chiuso; rifiuta un /chat che non porta contesto organizzativo. Questo accade durante un rollout transizionale dove l’agente è stato aggiornato ma il dashboard che invia la richiesta non è ancora org-aware. Correzione: termina il rollout in modo che il dashboard invii il contesto dell’org (lo stato finale normale, nessun flag necessario). Per colmare il divario mentre un dashboard non ancora org-aware parla a un agente org-aware, imposta AGENTEYE_AGENT_ALLOW_NO_ORG=1 sul servizio agent in modo che effettui il fallback all’org default anziché rifiutare, e cancellalo una volta che l’aggiornamento del dashboard arriva. Vedi il riferimento env in enterprise-docs/assistant.md.

Audit

Un audit non viene mai eseguito (il prossimo run continua a scivolare, nessuna cronologia di esecuzione)

Sintomo: la pagina di audit mostra last run: never, o next run continua a muoversi nel futuro senza una riga che appare nella cronologia di esecuzione. Causa: l’audit è disabilitato (gli audit disabilitati non hanno voce nella coda), oppure i worker di audit del server non riescono a rivendicare il lavoro. Correzione: conferma che l’audit sia abilitato (il pulsante esegui-ora lo richiede). Quindi controlla i log del server per audits pipeline started all’avvio e per errori audits: — una riga claim_due failed punta alla connettività di Postgres. AUDIT_WORKERS predefinito 1; deve essere ≥ 1 affinché qualsiasi audit venga eseguito.

Le esecuzioni di audit hanno successo ma non trovano nulla

Sintomo: la cronologia di esecuzione mostra succeeded con findings: 0 anche se /errors chiaramente mostra guasti. Causa: la finestra di scansione non copre i guasti, o i filtri di ambito li escludono. Correzione: controlla la finestra della riga di esecuzione (window_from → window_to) rispetto a quando i guasti si sono verificati — in modalità since_last ogni esecuzione esegue la scansione solo dall’esecuzione precedente riuscita, quindi i guasti più vecchi vengono visualizzati solo dalla prima esecuzione o da un audit a fixed-window. Amplia scope (ambienti / id agenti). Le statistiche di esecuzione mostrano policy_hits (quanti criteri deterministici si sono attivati) e improvements (quanti l’indagine AI ha registrato) — se entrambi sono 0, la finestra/ambito genuinamente non ha visto nulla.

L’esecuzione dice analysis_unavailable e produce solo risultati di politica

Sintomo: le statistiche di esecuzione includono analysis_unavailable e i soli risultati sono kind: policy; nessun miglioramento AI appare. Causa: l’indagine agenziale non poteva essere eseguita: il server non riesce a raggiungere il servizio agent (AGENTEYE_AGENT_URL / AGENTEYE_AGENT_TOKEN non impostato sul server — l’audit riutilizza la connessione dell’assistente), il servizio assistente non ha LLM configurato, oppure la chiamata ha generato errore/timeout (la stringa analysis_unavailable ha il dettaglio). Il pass di politica deterministica è il floor — sempre viene eseguito — quindi l’audit comunque ha successo con i suoi risultati di sicurezza. Correzione: imposta AGENTEYE_AGENT_URL (ad es. http://agent:9100) e AGENTEYE_AGENT_TOKEN sul server — gli stessi valori che l’assistente dashboard usa già (i manifesti/compose in bundle ora li innestano) — e configura un LLM sul servizio assistente (vedi assistant.md), quindi esegui di nuovo. Una grande indagine potrebbe avere bisogno di un AUDIT_LLM_TIMEOUT_MS più grande (server) — mantenerlo superiore al AGENTEYE_AUDIT_TIMEOUT_MS dell’agente.

La sandbox del codice di audit è disabilitata (sandbox_available: false)

Sintomo: /health dell’agente mostra sandbox_available: false, e le esecuzioni di audit notano che la sandbox non è disponibile; l’AI indaga solo con SQL. Causa: la sandbox bubblewrap in-pod ha bisogno di user namespace non privilegiati, che il profilo seccomp del pod o il kernel del nodo stanno bloccando. Correzione: imposta seccompProfile: Unconfined (k8s) o security_opt: [seccomp:unconfined] (compose) sull’agente, e conferma che il kernel del nodo consenta namespace utente non privilegiati (alcune immagini gestite, ad es. GKE COS, le disabilitano). Dove non puoi abilitarlo, questo è previsto e sicuro — l’auditor degrada a SQL-only automaticamente. Vedi deployment.md.

Il rapporto di audit email non viene consegnato

Sintomo: un audit ha esposto nuovi risultati ma nessun email è arrivato. Causa: l’audit non ha un canale email allegato, l’email è disabilitato org-wide in alerts.enabled_channels, non ci sono destinatari, o SMTP non è configurato. Correzione: allega un canale email all’audit, assicurati che email sia in alerts.enabled_channels, imposta i destinatari (sul canale o tramite alerts.email_default_recipients), e configura SMTP (lo stesso trasporto utilizzato dagli alert + email OTP). L’email viene inviata solo quando un’esecuzione produce almeno un risultato nuovo.

Un motivo mutato o dismesso mantiene la sua vecchia pagina di risultati ma mai si riclassa

Sintomo: dopo aver messo a tacere un risultato, le esecuzioni successive non fanno mai riapparire quel motivo — anche se ancora si verifica. Causa: questo è il comportamento progettato: mute/dismiss sono soppressioni durevoli keyed sull’impronta del motivo. Correzione: apri il risultato e usa reopen per cancellare la soppressione; l’esecuzione successiva classificherà di nuovo il motivo. Usa resolve (non mute) per i motivi “fixed” di cui vorresti sentire parlare se regressano.

Ottenere aiuto

Contatta support@exosphere.host con:
  • La tua versione di AgentEye (dal tag di rilascio)
  • Log di contenitore pertinenti (docker logs <container>)
  • Una descrizione del problema e di ciò che hai già provato