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
- Simplicity is the ultimate complexity — minimální datový model, maximální flexibilita
- Content-agnostic — nod je nod: omluvenka, myšlenka, foto, odkaz, audio — stejná struktura
- Role-agnostic — graf je jeden, "čočka" (AI overlay) se mění podle role
- Incrementální buildovatelnost — začni s nody a feedem, přidávej AI postupně
- 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í:
- Vezme startovní bod — roli uživatele, aktuální dotaz, situaci
- Projde graf pomocí kombinace:
- Recency — čerstvé nody mají vyšší váhu
- Relevance — embedding similarity k dotazu/kontextu
- Role filter — rodič vidí jen své děti, učitel svou třídu
- Importance — AI-scored "zásadní změna" vs. "rutina"
- Graph distance — bližší nody (méně hran) mají vyšší váhu
- Složí context window — seřazený, sumarizovaný kontext (budget ~0.5M tokenů)
- 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 |
| 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)