Přeskočit obsah

RFC: Universal Knowledge Graph

Stav: Draft Datum: 2. dubna 2026 Autor: docek + Claude


Motivace

Kosmo je dnes školní IS — tabulky studentů, docházka, profily. Ale informace, které školní komunita potřebuje sdílet, jsou přirozeně graf, ne tabulky: omluvenka se týká dítěte, dítě patří do třídy, třídu vede učitelka, učitelka napsala poznámku, poznámka odkazuje na projekt...

Cíl: vybudovat univerzální knowledge graph — jednoduchou, content-agnostic datovou strukturu, nad kterou AI dynamicky generuje kontext pro libovolnou roli a úlohu.

Principy

  1. Simplicity is the ultimate complexity — minimální datový model, maximální flexibilita
  2. Content-agnostic — nod je nod: omluvenka, myšlenka, foto, odkaz, audio — stejná struktura
  3. Role-agnostic — graf je jeden, "čočka" (AI overlay) se mění podle role
  4. Incrementální buildovatelnost — začni s nody a feedem, přidávej AI postupně
  5. Osifikace datové struktury — stabilní základ, dynamické vrstvy nad ním

Datový model

Node — atomická jednotka obsahu

nodes
├── id              UUID, PK
├── author_id       UUID → profiles
├── created_at      TIMESTAMPTZ
├── updated_at      TIMESTAMPTZ
│  # Obsah
├── content_raw     TEXT — originální obsah (markdown, URL, reference na media)
├── content_md      TEXT — human-readable markdown verze
├── media_url       TEXT — odkaz na uložené médium (audio, video, foto)
├── media_type      TEXT — MIME type (image/jpeg, audio/webm, ...)
│  # AI metadata (generované automaticky)
├── ai_transcript   TEXT — přepis audia/videa, OCR z obrázku
├── ai_summary      TEXT — krátké summary (1-2 věty)
├── ai_analysis     TEXT — dlouhá analýza + co přináší nového do kontextu
├── embedding       VECTOR(1536) — pro semantic search
│  # Systém
└── metadata        JSONB — volná metadata (source, tags, ...)

Nod nemá type enum. Typ je odvozený z obsahu, hran a tagů. AI ho může reklasifikovat.

Edge — vztah mezi nody

edges
├── id              UUID, PK
├── source_id       UUID → nodes
├── target_id       UUID → nodes
├── edge_type       TEXT — volný string: responds_to, about, contradicts, supersedes, related, tagged_with, ...
├── weight          FLOAT — důležitost vztahu (default 1.0, AI může upravovat)
├── created_by      TEXT — 'user' | 'ai:linker' | 'ai:enrichment' | ...
└── created_at      TIMESTAMPTZ

Hrany mohou směřovat i na existující entity (profily, studenty, třídy) přes referenční nody nebo přímé UUID.


AI Overlay — "čočka"

Overlay není tabulka. Je to procedura (agent), která na vyžádání:

  1. Vezme startovní bod — roli uživatele, aktuální dotaz, situaci
  2. Projde graf pomocí kombinace:
  3. Recency — čerstvé nody mají vyšší váhu
  4. Relevance — embedding similarity k dotazu/kontextu
  5. Role filter — rodič vidí jen své děti, učitel svou třídu
  6. Importance — AI-scored "zásadní změna" vs. "rutina"
  7. Graph distance — bližší nody (méně hran) mají vyšší váhu
  8. Složí context window — seřazený, sumarizovaný kontext (budget ~0.5M tokenů)
  9. Výstup je ready pro LLM — začni novou úlohu s kompletním kontextem

Příklad: context window pro roli "táta"

[Kontext role: Rodič | Děti: Eliška (3.A), Matěj (1.B)]

## Aktuální (posledních 7 dní)
- Eliška: projekt Sopka — prezentace v pátek, potřebuje noviny a lepidlo
- Matěj: nemocný Po-Út, omluvenka odeslána
- Třída 3.A: plánovaný výlet na Říp (15.4.)

## Důležité změny
- Nový rozvrh kroužků od dubna
- Změna vyzvedávání: babička přidána jako oprávněná osoba

## Relevantní kontext (starší, stále platný)
- Eliška: alergie na ořechy (zdravotní poznámka)
- Kontakt třídní: p. Nováková (novakova@sofie.education)

Agenti

Agent Trigger Co dělá
Ingestion Nový obsah (email, webhook, UI, API) Normalizuje na nod + základní hrany
Enrichment Nový nod vytvořen Transcript, OCR, summary, embedding, context delta
Linker Po enrichmentu Hledá implicitní vztahy (semantic similarity, entity extraction)
Compiler Na vyžádání (chat, feed, API) Skládá context window pro roli/úlohu
Curator Periodicky (denně/týdně) Přehodnocuje importance, merguje duplicity, archivuje
Compactor Periodicky + on-demand Vytváří kompaktující nody — sumarizace clusterů nodů

Kompaktující nody

Klíčový mechanismus pro škálovatelnost grafu. Compactor agent vytváří syntetické nody, které sumarizují cluster souvisejících nodů:

[Kompakt nod: "Eliška — březen 2026"]
  content_md: "Březen: 3 absence (nemoc), projekt Sopka (prezentace
               úspěšná), pokrok v matematice (zlatý materiál zvládnut).
               Nový kroužek keramika od 10.3."
  ai_summary: "Eliška měla produktivní březen s drobnými absencemi"
  metadata: { "compact_type": "monthly_student", "generated_at": "2026-04-01" }
      ├── compacts ──→ [Nod: Omluvenka 3.3.]
      ├── compacts ──→ [Nod: Omluvenka 7.3.]
      ├── compacts ──→ [Nod: Omluvenka 12.3.]
      ├── compacts ──→ [Nod: Foto projekt Sopka]
      ├── compacts ──→ [Nod: Poznámka učitelky — matematika]
      └── compacts ──→ [Nod: Zápis do kroužku keramika]

Vlastnosti: - Hrana compacts odkazuje na zdrojové nody — nic se nemaže, jen se přidá vrstva - Compiler přednostně používá kompaktní nody (méně tokenů, stejná informace) - Pokud potřebuje detail, sleduje compacts hrany k originálům - Přegenerovatelné — kompakt nod je vždy odvoditelný ze zdrojových nodů, může se přegenerovat s novým kontextem (jiná role, jiný čas, nové informace) - Víceúrovňové: denní kompakty → týdenní → měsíční → kvartální

Příklad víceúrovňové kompaktace:

[Kvartální kompakt: "Eliška Q1 2026"]
    ├── compacts ──→ [Měsíční kompakt: "Eliška — leden 2026"]
    ├── compacts ──→ [Měsíční kompakt: "Eliška — únor 2026"]
    └── compacts ──→ [Měsíční kompakt: "Eliška — březen 2026"]
                          ├── compacts ──→ [Nod: Omluvenka 3.3.]
                          ├── compacts ──→ [Nod: Foto projekt Sopka]
                          └── ...

Kontextová přegenerace: Stejný cluster nodů může mít víc kompaktů — jeden z pohledu rodiče ("jak se Elišce daří"), jiný z pohledu učitelky ("pedagogický pokrok"), jiný pro ředitele ("docházka a výjimky"). Kompakt se přegeneruje když: - Přibude nový nod do clusteru - Změní se kontext (nová informace mění interpretaci starých) - Periodicky (curator trigger)

Implementace agentů

Agenti běží jako n8n workflows: - Ingestion: webhook endpoint, přijme cokoliv, vytvoří nod v Supabase - Enrichment: database trigger (on insert into nodes), zavolá Gemini pro AI metadata - Linker: po enrichmentu, vector search pro podobné nody, vytvoří hrany - Compiler: API endpoint, na vstupu role + dotaz, na výstupu context MD - Curator: cron (denně), projde staré nody, přepočítá váhy


Ingestion kanály

Nod je nod bez ohledu na zdroj. Každý kanál volá stejný Ingestion endpoint:

Kanál Jak Příklad
Web UI Formulář v Kosmu Rodič píše omluvenku
Mobilní PWA Quick capture: fotka + hlas + text Učitel fotí projekt žáka
n8n webhook Automaticky z externích systémů Email → nod
Email Cloudflare Email Worker → n8n → nod Rodič pošle email třídní
API REST/GraphQL Integrace třetích stran
Chat (Sofie) Konverzace s AI → extrakce nodů "Eliška zítra nepřijde" → omluvenka nod

Příklad: omluvenka jako graf

[Nod A: Omluvenka]
  content_md: "Eliška nebude ve škole 3.–5. dubna, je nemocná."
  author: táta
  ai_summary: "Omluvenka Elišky, 3.–5.4., nemoc"
      ├── about ──→ [Nod: Eliška (profil studenta)]
      ├── tagged_with ──→ [Nod: attendance]
      ├── responds_to ──→ [Nod: Dotaz učitelky na docházku]
      └── relates_to ──→ [Nod: Předchozí omluvenka z února]

Stejná omluvenka se zobrazí: - Tátovi: v jeho feedu jako "odesláno" - Učitelce: jako notifikace + automaticky v docházce - AI overlay: aktualizuje kontext "Eliška — aktuálně nemocná"


Open questions

# Otázka Poznámka
KG1 Privacy/RLS — jak řešit viditelnost nodů? Supabase RLS per nod? Per hrana? Děděná z grafu?
KG2 Multi-tenancy — jedna instance grafu per škola, nebo sdílený? Pro univerzálnost: tenant_id na nodech
KG3 Media storage — kam ukládat audio/video/foto? Rozhodnuto: Supabase Storage. ~10–20 GB/rok, RLS z boxu. GCS až při potřebě.
KG4 Škálovatelnost overlay — 0.5M tokenů kontextu pro jednoho uživatele Kolik nodů to je? Jak efektivně prořezávat?
KG5 Embedding model — jaký model pro vektory? OpenAI ada-002 (1536d), Gemini embedding, lokální?
KG6 Edge types — otevřený string vs. řízený slovník? Flexibilita vs. konzistence
KG7 Verzování nodů — editace = nový nod + supersedes hrana? Nebo prostě UPDATE?
KG8 Odkaz na existující entity — profily, studenti, třídy Referenční nody, nebo hrany přímo na UUID jiných tabulek?

Fáze implementace

Fáze 0: Datový základ

  • Supabase migrace: tabulky nodes, edges
  • Základní RLS politiky
  • pgvector extension pro embeddingy

Fáze 1: Ingestion + Enrichment

  • n8n Ingestion workflow (webhook → nod)
  • n8n Enrichment workflow (Gemini summary + embedding)
  • Jednoduchý Web UI pro vytváření nodů

Fáze 2: Feed + Linker

  • Feed view v Kosmu (chronologický, filtrovaný per role)
  • Linker agent (semantic similarity → hrany)
  • Základní graph traversal

Fáze 3: AI Overlay + Compiler

  • Compiler agent (context window pro roli)
  • Integrace s Sofie chatem (context-aware odpovědi)
  • Curator agent (údržba grafu)

Fáze 4: Advanced

  • Multi-kanálový ingestion (email, PWA, chat extraction)
  • Adaptivní importance scoring
  • Cross-tenant knowledge sharing (opt-in)