Přeskočit na obsah

OPSLOOP Intelligence Loop — Developer Reference

OPSLOOP Intelligence Loop — Developer Reference

Section titled “OPSLOOP Intelligence Loop — Developer Reference”

OPSLOOP je pipeline pro zpracování incidentů, která proměňuje alerty z Alertmanageru (a další platformní události) na záznamy incident, obohacuje je o kontext, řadí pravděpodobné příčiny a — u modulů, které jsou zapnuté — spouští nápravu. Většina jejích schopností nad rámec základní alert-to-incident pipeline je opt-in, zamčená za environment flagy.

KomponentaUmístěníRole
opsloop-consumeropsloop-consumer/Fastify HTTP server + RabbitMQ consumer — dedup/korelace alertů, sběr kontextu, RCA, remediace, forecasting
llm-servicellm-service/Fastify fasáda nad Anthropic (výchozí) / libovolným OpenAI-kompatibilním endpointem (fallback), port 4430
Lumen AI panelsencai.space-frontend/Frontend intelligence panel (stores/lumen.ts, lib/lumen/)
Strapi content-typesencai.space/src/api/incident/Jediný CT incident — korelace alertů i RCA kontext žijí jako JSON pole přímo na incidentu

Neexistuje žádný opsloop-incident, opsloop-context-bundle ani opsloop-runbook-suggestion content-type. Vše je uloženo na jediném collection type incident: kontext je JSON pole context_bundle, seřazené důkazy o root cause jsou JSON pole rca_candidates, obojí se patchuje na tentýž záznam postupně, jak pipeline pokračuje.

Fastify fasáda (port 4430), která směruje LLM volání na nakonfigurovaný backend a vynucuje per-org autorizaci, rate limiting a rozpočtové stropy. Je provider-agnostická:

  • Výchozí provider: Anthropic (@anthropic-ai/sdk), model claude-haiku-4-5-20251001 (LLM_DEFAULT_MODEL)
  • Fallback provider: LLM_PROVIDER=openai_compat — funguje proti libovolnému OpenAI-kompatibilnímu endpointu (OPENAI_COMPAT_BASE_URL), včetně self-hosted řešení jako Ollama, vLLM nebo LocalAI

Endpointy (bez prefixu /api/, žádný /embed endpoint):

POST /chat
Body: { messages: [{ role: 'user'|'assistant', content: string }], model?: string, org_id?: number }
Response: { content: string, model: string, usage: { input_tokens, output_tokens, cost_usd } }
POST /complete
Body: { prompt: string, model?: string, org_id?: number }
Response: { content: string, model: string, usage: { input_tokens, output_tokens, cost_usd } }

Autentizace je na obou endpointech povinná — vyžaduje se Keycloak Bearer JWT, ověřený přes JWKS (pouze RS256). org_id použité pro rate limiting, evidenci spotřeby a vynucení rozpočtu se vždy odvozuje ze skutečného záznamu volajícího v Strapi (organisation-member, dohledaný podle ověřeného Keycloak sub) — nikdy se nedůvěřuje hodnotě z těla požadavku. org_id zaslané v těle požadavku je porovnáno se skutečným členstvím volajícího a při neshodě zamítnuto (403 llm.auth.org_mismatch).

Vynucení rozpočtu: každá organizace má měsíční strop útraty, LLM_BUDGET_USD_PER_ORG_MONTH (výchozí $10). Po dosažení stropu jsou požadavky zamítnuty s 402 (llm.budget.exceeded) ještě předtím, než dojde k volání providera.

Rate limiting: denní počítadla požadavků per-org, rozlišená podle tarifu — free vs. pro plán (RATE_LIMIT_FREE_PER_DAY výchozí 100, RATE_LIMIT_PRO_PER_DAY výchozí 5000).

Rozpočtový tracker i rate limiter jsou pouze in-memory — stav se při restartu služby ztrácí, žádná Redis perzistence zatím neexistuje.

Lumen je frontendový intelligence povrch v sencai.space-frontend (stores/lumen.ts, components/layout/Lumen*.vue, skládatelný panel).

Privacy filtr: než je jakýkoliv kontext předán LLM adaptéru, sencai.space-frontend/lib/lumen/privacy-filter.ts (scrubContext()) provede allowlist-based očištění snapshotu context busu: neznámé druhy položek se zahodí zcela, u každého druhu položky přežijí jen povolené klíče, jakýkoliv klíč odpovídající vzoru citlivého jména (secret, password, token, credential, api[-_]?key, auth, …) je redigován a hodnoty, které vypadají jako JWT, API klíče ve stylu Stripe/OpenAI, AWS access-key ID nebo connection stringy s přihlašovacími údaji, jsou rovnou zahozeny. Tento filtr žije na straně klienta ve frontendu, ne v llm-service.

Primární, vždy zapnutá pipeline:

Alertmanager → POST /webhook/alertmanager (HMAC ověřeno přes ALERTMANAGER_WEBHOOK_SECRET)
→ processWebhookBatch()
→ buildDedupKey() — SHA-256 seřazených labelů alertu, bez `instance`
→ in-memory dedup okno (5 min, CORRELATION_WINDOW_MS) + fallback lookup ve Strapi
→ existující incident v okně → inkrementace alert_count
→ jinak → createIncident() → Strapi POST /api/incidents
→ collectContext() [async] → context_bundle napatchován na incident
→ scoreAndPatchRcaCandidates() [async] → heuristické skórování rca-engine.ts → napatchováno rca_candidates
→ handleIncidentAutoRemediation() [async, fire-and-forget] → matchování remediation-rule + cooldown
ExchangeTypQueueRouting keyZdroj / účel
alertmanager.eventstopicopsloop.alertsalert.firingPrimární dedup/incident pipeline (dostupná i přes HMAC-ověřený POST /webhook/alertmanager)
platform.eventsfanoutopsloop.alert-correlatormonitoring.alert.*, agent.alert.*, cloud-instance.error (filtrováno v consumeru)Alert correlator
platform.eventsfanoutrca-generator.dispatchincident.resolved (filtrováno v consumeru)AI-asistovaný RCA generátor
platform.eventsfanoutcode-review.dispatchcode.review.# (filtrováno v consumeru)Handler code review dispatche
opsloop.dlxtopicopsloop.alerts.dead#Sdílená dead-letter queue pro všechny čtyři fronty výše

platform.events je deklarován jako fanout exchange — každá navázaná queue dostane každou zprávu a filtrování podle routing-key vzoru si dělá sám každý consumer (amqp-topic-match.ts), místo aby se spoléhal na broker-side topic routing.

Prefetch je napříč consumerem 10. Při selhání zpracování jsou zprávy nackovány bez requeue rovnou do dead-letter queue — momentálně neexistuje žádný retry counter ani exponenciální backoff; zpráva jde do dead-letter fronty hned při první chybě.

Nad rámec vždy zapnuté dedup/context/RCA-heuristic/auto-remediation cesty výše nabízí opsloop-consumer zhruba deset dalších schopností. Většina je opt-in přes environment flag a defaultně vypnutá; dvě z nich (heuristický RCA engine a auto-remediace) jsou vždy aktivní, protože nemají vlastní nezávislý flag — jejich bezpečnostní pojistka žije na úrovni remediation-rule (confidence_threshold/cooldown_minutes), ne jako globální vypínač.

ModulSouborPopisZapnutí přes
Alert correlatorservices/alert-correlator.service.tsCross-service korelace alertů z platform.events do incidentů (klíč org:severity:resource-type, klouzavé 5min okno)OPSLOOP_DEDUP_ENABLED=true
Context collector (rozšířený)services/context-collector.service.tsSbírá časovou osu změn kolem okna incidentu — Gitea commity, IAM audit události, drift, provisioning — tenant-scoped na orgIdCONTEXT_COLLECTION_ENABLED=true
RCA enginerca-engine.tsHeuristické skórování kandidátů na root cause (deploy_recent, iam_change, drift_event, config_change, resource_exhaustion). Nikdy nevolá LLM a nikdy nevydává jediný autoritativní verdikt — jen řadí důkazy pro operátoraVždy aktivní (volán z context collectoru)
RCA generátor (AI-asistovaný)rca-generator.tsPři incident.resolved sestaví evidence bundle a zavolá llm-service (model claude-haiku-4-5-20251001); při selhání nebo nedostupnosti LLM volání spadne na rule-based shrnutí. Odlišný od vždy zapnutého RCA enginu výšeINCIDENT_RCA_ENABLED=true
RCA rankerservices/rca-ranker.service.tsVáhované skórování důkazů podle jistoty plus Jaccard-similarity dedup RCA kandidátůVolán z pipeline RCA generátoru
Auto-remediaceauto-remediation.tsPo vytvoření incidentu: dohledá remediation-rule (regex/klíčové slovo na dedup_key/titulek), aplikuje práh jistoty + cooldown a pak buď automaticky spustí runbook (auto_approve=true), nebo otevře change request na lidské schváleníVždy aktivní — bez nezávislého flagu
SRE runbook auto-triggerservices/auto-trigger.service.tsAlternativní/doplňková cesta k auto-remediaci — práh závažnosti + cooldown, emituje ops.auto_trigger.fired/skippedAUTO_TRIGGER_ENABLED=true
Capacity forecastcapacity-forecast.service.tsTýdenní cron (neděle 05:00 UTC) — lineární regrese nad agent-metric-snapshot (cpu/paměť/disk), prahové recommended_size, upsertuje lumen-recommendation (right-sizing)CAPACITY_FORECAST_ENABLED=true
Workload profilerservices/workload-profiler.service.tsDenní cron (04:00 UTC) — bucketing CPU/paměti podle hodiny dne do profile_type (cpu_bound/memory_bound/idle/io_bound/mixed), LLM-asistované doporučení migrace s rule-based fallbackemWORKLOAD_PROFILER_ENABLED=true
Pattern correlatorpattern-correlator.tsBěží každých 15 minut — cross-service detekce anomálií: kaskádové selhání (≥3 incidenty / 10 min), opakovaný alert (stejný dedup_key ≥3× / 24h), upsertuje correlation-findingsCORRELATION_ENABLED=true
Code review handlercode-review-handler.tsPři code.review.requested: stáhne surový diff z Gitea Apps API (max 2 MB), zapíše diff_patch do Strapi, spustí POST /api/code-review-requests/:id/processVždy aktivní (registrován bezpodmínečně v consumer.ts)

Auto-remediace ani SRE auto-trigger cesta neprodukují volný {cmd, timeout} návrh. Pracují proti skutečnému schématu Strapi content-type runbook:

{
"name": "Restart nginx on node-abc",
"trigger_type": "alert",
"trigger_condition": { "metric": "cpu_percent", "operator": ">", "threshold": 90 },
"actions": [
{ "type": "restart_service", "params": { "service": "nginx" } }
],
"confirmation_required": true,
"cooldown_minutes": 60
}

actions[].type je jedno z restart_service, clear_disk_space, kill_process, run_approved_script. Runbooky s confirmation_required: true (výchozí hodnota schématu) se nevykonávají přímo — jsou odeslány jako approval-request (POST /api/approval-requests, action_type: "runbook.execute") na lidské schválení, se 4hodinovým TTL (expires_at) a uloženým blast_radius/rollback_plan. V tomto flow neexistuje žádný “Action Gateway” ani “cloud-approval-request” content-type — skutečným mechanismem je approval-request.

Eskalace na externího poskytovatele pagingu nežije v opsloop-consumer. Je implementována v cloud-connector/src/services/escalation.service.ts, která čte konfiguraci politiky ze Strapi content-type escalation-policy (provider: pagerduty nebo opsgenie, per-org routing_key / šifrovaný API klíč, zamčeno za ESCALATION_ENABLED).

escalateIncident() v této službě posílá na PagerDuty Events v2 API nebo Opsgenie Alerts API podle providera nastaveného v politice. K okamžiku psaní tohoto textu je funkce plně implementovaná, ale není volána z žádné cesty vytváření incidentu v repozitáři — při startu workeru běží jen startEscalationConsumer(), který ale nedělá nic víc než zaloguje připravenost. Před spoléháním na živou PagerDuty/Opsgenie eskalaci v produkci ověřte aktuální stav zapojení s vlastníkem služby.

Syntetické kontroly spouští cloud-connector (src/services/synthetic-check.service.ts, zamčeno za SYNTHETIC_CHECK_ENABLED), ne Alertmanager. Cron běžící každou minutu znovu spustí jakoukoliv synthetic-check, jejíž interval vypršel, a výsledek odešle do Strapi:

cloud-connector cron (každou minutu)
→ GET /api/synthetic-checks (zapnuté kontroly)
→ per kontrola: HTTP požadavek, porovnání statusu/těla s očekáváním
→ POST /api/synthetic-results (X-Service-Secret)
→ PUT /api/synthetic-checks/:id (last_check_at, last_status)

Momentálně neexistuje žádný lifecycle hook, RabbitMQ publish ani routing ve stylu anomaly.events z vytvoření synthetic-result do incidentní pipeline — selhávající syntetická kontrola se zaznamená ve Strapi, ale automaticky neotevře incident ani neuvědomí opsloop-consumer. Jakoukoliv automatizaci synthetic-check → incident považujte za zatím neimplementovanou; před dokumentováním nebo spoléháním na ni ověřte s vlastníkem služby.

  • Health: GET /health
  • Metriky: GET /metrics — Prometheus textový formát (text/plain; version=0.0.4), včetně httpRequestsTotal, httpRequestDurationMs, alertsProcessedTotal, incidentsCreatedTotal
  • Error tracking: @sentry/node, mířící na self-hosted instanci GlitchTip (kompatibilní se Sentry ingestion protokolem, ne Sentry SaaS — profil glitchtip v orchestraci), v souladu s platformní politikou žádného placeného SaaS. beforeSend PII scrubbing probíhá v src/sentry-pii-scrub.ts. Prázdné SENTRY_DSN je bezpečný no-op — pro lokální vývoj není potřeba žádná instance GlitchTipu.
  • Audit: non-HTTP mutace (dedup, sběr kontextu, RCA skórování/generování, remediace, forecasting) publikují přímo přes vendorovaný balíček @sencai/audit (např. incident.deduplicated, incident.context.collected, incident.rca.candidates_updated, rca.generated, ops.remediation.triggered/completed, ops.auto_trigger.fired/skipped, capacity.forecast.generated, workload.profile.generated). Vytvoření incidentu přes Strapi REST API navíc automaticky prochází globálním middlewarem audit-logger.
  1. Publikujte do příslušného exchange — alertmanager.events (routing key alert.firing) pro zdroje ve tvaru alertu, nebo platform.events (fanout; zvolte konvenci routing key a filtrujte na ni v consumeru přes amqp-topic-match.ts) pro cokoliv jiného.
  2. Přidejte modul consumeru/handleru v opsloop-consumer/src/ (nebo src/services/) podle existujícího vzoru (deklarace queue s x-dead-letter-exchange: opsloop.dlx, bind, filtrování podle routing key).
  3. Zapojte ho v consumer.ts (nebo index.ts pro HTTP/cron moduly) a zamkněte ho za nový environment flag, defaultně false, pokud nejde o nízkorizikovou read-only cestu.
  4. Pokud má zdroj sytit root-cause analýzu, rozšiřte context-collector.ts / context-collector.service.ts o nový typ důkazu a případně přidejte odpovídající typ kandidáta do rca-engine.ts.