Orkestrering: tre nivåer (script/fritekst/drøm) + rename portvokter→vaktmester
Orkestrering restrukturert med deklarativt script som primærnivå:
- Nivå 1: eksakte CLI-kall med {event.*}-variabler, ingen AI
- Nivå 2: fritekst tolket av bot med function calling
- Nivå 3: drømmemodus — bruker skriver fritt, mangler→work_items
- Auto-eskalering: script→bot ved uventet feil
Rename portvokter→vaktmester i alle docs — bedre navn for en
tjeneste som gjør ting, ikke bare sjekker legitimasjon.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
68f7b10e9b
commit
fd0b75ee13
7 changed files with 188 additions and 144 deletions
|
|
@ -182,7 +182,7 @@ Kanal 2 er usynlig for brukeren. De ser bare svaret.
|
|||
|
||||
1. En bruker skriver i en kommunikasjonsnode der bot-agenten er
|
||||
`member_of`.
|
||||
2. Portvokteren trigger `agent_respond`-jobb (eksisterende flow).
|
||||
2. Vaktmesteren trigger `agent_respond`-jobb (eksisterende flow).
|
||||
3. Boten svarer i samtalen.
|
||||
4. Boten vurderer: er noe her actionable?
|
||||
5. Hvis ja: oppretter riktig node-type med riktig tag.
|
||||
|
|
@ -213,7 +213,7 @@ Vegard triagerer innboksen og prioriterer.
|
|||
|
||||
### Ruting
|
||||
|
||||
`@bot` rutes av portvokteren basert på kontekst:
|
||||
`@bot` rutes av vaktmesteren basert på kontekst:
|
||||
- Standard: Claude (nåværende `agent_respond`-flyt)
|
||||
- Fremtidig: spesialiserte agenter, andre modeller, eller
|
||||
regelbaserte svar — uten endring i brukergrensesnittet
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@
|
|||
## 1. Konsept
|
||||
|
||||
Orkestreringer er noder som *gjør* ting. De er oppskrifter —
|
||||
sekvenser av CLI-verktøy og bot-instruksjoner som utføres
|
||||
automatisk når en trigger aktiveres. Brukeren skriver hva som
|
||||
skal skje i naturlig språk. Boten tolker og utfører.
|
||||
sekvenser av CLI-verktøy som utføres automatisk når en trigger
|
||||
aktiveres. Tre nivåer: deklarativt script (ingen AI),
|
||||
fritekst med AI-tolkning, og full drømmemodus.
|
||||
|
||||
## 2. Hvorfor
|
||||
|
||||
Synops har mange automatiseringer hardkodet i portvokteren:
|
||||
Synops har mange automatiseringer hardkodet i vaktmesteren:
|
||||
podcast-pipeline, AI-beriking, publisering. De er usynlige,
|
||||
uendrelige og umulige å tilpasse for brukere.
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ primitiver — synlige, redigerbare, delbare, versjonerbare.
|
|||
```
|
||||
node_kind: 'orchestration'
|
||||
title: "Podcast: opptak → publisering"
|
||||
content: "Når en innspilling avsluttes..."
|
||||
content: <script eller fritekst>
|
||||
metadata: {
|
||||
"trigger": {
|
||||
"event": "communication.ended",
|
||||
|
|
@ -31,10 +31,9 @@ metadata: {
|
|||
"has_media": "audio"
|
||||
}
|
||||
},
|
||||
"executor": "bot",
|
||||
"executor": "script" | "bot" | "dream",
|
||||
"intelligence": 2,
|
||||
"effort": 2,
|
||||
"compiled": false
|
||||
"effort": 2
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -48,38 +47,95 @@ metadata: {
|
|||
|
||||
⚡ signaliserer: "denne noden gjør noe av seg selv."
|
||||
|
||||
## 4. Fritekst-instruksjoner
|
||||
## 4. Tre utførelsesnivåer
|
||||
|
||||
Stegene i en orkestrering er naturlig språk:
|
||||
### Nivå 1: Deklarativt script (ingen AI)
|
||||
|
||||
Eksakte CLI-kall med variabler fra trigger-konteksten.
|
||||
Vaktmesteren parser og eksekverer direkte — ingen LLM.
|
||||
|
||||
```
|
||||
NÅR innspilling.avsluttet
|
||||
HVIS samling.har_trait("podcast")
|
||||
|
||||
1. synops-transcribe --cas-hash {event.cas_hash} --model large
|
||||
VED_FEIL: synops-transcribe --cas-hash {event.cas_hash} --model medium
|
||||
2. synops-summarize --communication-id {event.communication_id}
|
||||
3. synops-rss --collection-id {event.collection_id}
|
||||
|
||||
VED_FEIL: work_item "Podcast-pipeline feilet" --tag bug
|
||||
```
|
||||
|
||||
**Grammatikk:**
|
||||
|
||||
```
|
||||
TRIGGER NÅR <event> [HVIS <betingelse>]
|
||||
STEP <N>. <tool> <args...>
|
||||
FALLBACK VED_FEIL: <tool> <args...> | work_item <title> [--tag <tag>]
|
||||
VARIABLE {event.<felt>} — substitueres fra trigger-konteksten
|
||||
```
|
||||
|
||||
Enkelt nok til å parse med en liten Rust-parser i vaktmesteren.
|
||||
Deterministisk, gratis, raskt. **De fleste produksjons-
|
||||
orkestreringer vil være på dette nivået.**
|
||||
|
||||
### Nivå 2: Fritekst med AI-tolkning
|
||||
|
||||
Naturlig språk som boten tolker og utfører steg for steg
|
||||
med function calling. For orkestreringer der stegene ikke
|
||||
er helt forutsigbare.
|
||||
|
||||
```
|
||||
Når en innspilling i en samling med podcast-trait avsluttes:
|
||||
|
||||
1. Transkriber lydfilen med synops-transcribe (model: large)
|
||||
2. Generer oppsummering med synops-summarize
|
||||
1. Transkriber lydfilen med stor modell
|
||||
2. Generer oppsummering
|
||||
3. Foreslå kapitler basert på transkripsjonen
|
||||
4. Generer show notes
|
||||
5. Oppdater RSS-feed med synops-rss
|
||||
5. Oppdater RSS-feed
|
||||
|
||||
Hvis transkribering feiler, prøv igjen med model: medium.
|
||||
Hvis RSS feiler, opprett work_item med tag 'bug'.
|
||||
Hvis transkribering feiler, prøv igjen med medium modell.
|
||||
Hvis RSS feiler, opprett en oppgave.
|
||||
```
|
||||
|
||||
Brukeren skriver hva de vil. Boten tolker instruksjonene og
|
||||
utfører med function calling — kaller CLI-verktøy via
|
||||
portvokteren (se `docs/infra/robusthet.md` § function calling).
|
||||
Boten mapper "transkriber lydfilen" → `synops-transcribe`,
|
||||
"oppsummer" → `synops-summarize`, osv. Krever LLM-kall
|
||||
per kjøring (Haiku er nok for de fleste).
|
||||
|
||||
### Hvorfor fritekst, ikke DSL?
|
||||
### Nivå 3: Drømmemodus
|
||||
|
||||
- Ingen DSL å lære for brukeren
|
||||
- Boten resonnerer om feil og edge cases
|
||||
- Feilhåndtering er naturlig ("hvis X feiler, prøv Y")
|
||||
- Endring er å redigere tekst, ikke debugge JSON
|
||||
Brukeren skriver hva de *ønsker*, uten å vite hvilke verktøy
|
||||
som finnes. Boten prøver, og mangler som oppdages blir
|
||||
feature requests.
|
||||
|
||||
```
|
||||
Gjør episoden klar for publisering. Lag en lydfil med
|
||||
sammendrag og send den til alle deltakere.
|
||||
```
|
||||
|
||||
Boten sjekker tilgjengelige verktøy, gjør det den kan,
|
||||
og oppretter work_items for det som mangler.
|
||||
|
||||
### Naturlig progresjon
|
||||
|
||||
```
|
||||
Drømmemodus → Bruker beskriver ønsket resultat
|
||||
↓ AI foreslår
|
||||
Fritekst → Bruker justerer steg i naturlig språk
|
||||
↓ AI foreslår kompilering etter N kjøringer
|
||||
Script → Deklarative CLI-kall, ingen AI
|
||||
↓ manuelt
|
||||
Kode → Eget CLI-verktøy (synops-<verb>)
|
||||
```
|
||||
|
||||
Hvert nivå kan fryses til nivået under. AI foreslår
|
||||
kompilering, bruker godkjenner. Scriptet kan alltid
|
||||
redigeres manuelt.
|
||||
|
||||
## 5. Strukturert trigger
|
||||
|
||||
Triggeren er den eneste strukturerte delen — portvokteren
|
||||
evaluerer den effektivt uten LLM:
|
||||
Triggeren er alltid strukturert — vaktmesteren evaluerer
|
||||
den effektivt uten LLM, uavhengig av utførelsesnivå:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
|
|
@ -114,11 +170,26 @@ Filtrerer triggeren ytterligere:
|
|||
|
||||
## 6. Utførelse
|
||||
|
||||
### Bot-modus (default)
|
||||
### Script-modus (nivå 1)
|
||||
|
||||
```
|
||||
Trigger aktiveres
|
||||
→ Portvokteren finner matchende orchestration-node
|
||||
→ Vaktmesteren finner matchende orchestration-node
|
||||
→ Parser scriptet
|
||||
→ Substituerer {event.*}-variabler fra trigger-kontekst
|
||||
→ Utfører steg sekvensielt via generisk dispatch
|
||||
(synops-{tool} --payload-json)
|
||||
→ Ved feil: kjør VED_FEIL-steg eller opprett work_item
|
||||
→ Logger i orchestration_log
|
||||
```
|
||||
|
||||
Ingen LLM. Deterministisk. Raskt.
|
||||
|
||||
### Bot-modus (nivå 2)
|
||||
|
||||
```
|
||||
Trigger aktiveres
|
||||
→ Vaktmesteren finner matchende orchestration-node
|
||||
→ Sender til bot med function calling:
|
||||
- Trigger-kontekst (hvilken node, hvilken event)
|
||||
- Instruksjonene fra orchestration.content
|
||||
|
|
@ -128,39 +199,24 @@ Trigger aktiveres
|
|||
→ Ved feil: resonnerer og prøver alternativ
|
||||
```
|
||||
|
||||
### Kompilert modus (optimalisert)
|
||||
### Drømmemodus (nivå 3)
|
||||
|
||||
Når en orkestrering har kjørt mange ganger med samme mønster,
|
||||
kan den kompileres til en direkte pipeline:
|
||||
Som bot-modus, men med høyere intelligens (Sonnet+) og
|
||||
instruks om å opprette work_items for manglende verktøy.
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"compiled": true,
|
||||
"pipeline": [
|
||||
{ "tool": "synops-transcribe", "args_map": {"cas_hash": "input.cas_hash", "model": "large"} },
|
||||
{ "tool": "synops-summarize", "args_map": {"communication_id": "input.id"} },
|
||||
{ "tool": "synops-rss", "args_map": {"collection_id": "input.collection_id"} }
|
||||
],
|
||||
"fallback": "bot"
|
||||
}
|
||||
```
|
||||
### Auto-eskalering
|
||||
|
||||
Ingen LLM-kall for standardsteg. Direkte dispatch. Mye raskere,
|
||||
billigere, deterministisk. `fallback: "bot"` betyr: ved feil
|
||||
eller uventet situasjon, fall tilbake til fritekst-instruksjonene.
|
||||
|
||||
### Naturlig progresjon
|
||||
Script-modus faller tilbake til bot-modus ved uventet feil:
|
||||
|
||||
```
|
||||
1. Fritekst → Brukere skriver, boten tolker
|
||||
2. Observér → Boten logger hvilke verktøy/sekvenser som gjentas
|
||||
3. Foreslå → "Denne har kjørt 50 ganger. Kompilere?"
|
||||
4. Kompilér → Fast pipeline, ingen LLM for standardsteg
|
||||
5. DSL (senere) → Kun hvis kompilerte pipelines trenger mer uttrykk
|
||||
Script kjører steg 2
|
||||
→ synops-summarize returnerer exit 1
|
||||
→ VED_FEIL er definert → prøv alternativ
|
||||
→ Alternativ feiler også
|
||||
→ Eskalér til bot-modus for dette steget
|
||||
→ Boten resonnerer om feilen og prøver å løse det
|
||||
```
|
||||
|
||||
DSL-en designes ikke nå — den oppstår fra observerte mønstre.
|
||||
|
||||
## 7. Koblinger mellom orkestreringer
|
||||
|
||||
Orkestreringer er noder med edges:
|
||||
|
|
@ -181,29 +237,36 @@ Kaskade via edges, ikke hardkodet.
|
|||
│ │
|
||||
│ Trigger: [Innspilling avsluttet ▼] │
|
||||
│ Betingelse: [Samling har podcast-trait ▼] │
|
||||
│ Modus: [Script ▼] │
|
||||
│ │
|
||||
│ Steg: │
|
||||
│ ┌─────────────────────────────────────┐ │
|
||||
│ │ Transkriber lydfilen med stor │ │
|
||||
│ │ modell. Hvis det feiler, bruk │ │
|
||||
│ │ medium. │ │
|
||||
│ ├─────────────────────────────────────┤ │
|
||||
│ │ Oppsummer samtalen. │ │
|
||||
│ ├─────────────────────────────────────┤ │
|
||||
│ │ Foreslå kapitler. │ │
|
||||
│ ├─────────────────────────────────────┤ │
|
||||
│ │ Oppdater RSS-feed. │ │
|
||||
│ │ NÅR innspilling.avsluttet │ │
|
||||
│ │ HVIS samling.har_trait("podcast") │ │
|
||||
│ │ │ │
|
||||
│ │ 1. synops-transcribe │ │
|
||||
│ │ --cas-hash {event.cas_hash} │ │
|
||||
│ │ --model large │ │
|
||||
│ │ VED_FEIL: ... --model medium │ │
|
||||
│ │ 2. synops-summarize │ │
|
||||
│ │ --communication-id {event.id} │ │
|
||||
│ │ 3. synops-rss │ │
|
||||
│ │ --collection-id {event.coll_id} │ │
|
||||
│ └─────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ [+ Legg til steg] [▶ Test kjøring] │
|
||||
│ [▶ Test kjøring] [↑ Konverter til AI] │
|
||||
│ │
|
||||
│ Status: Aktiv Kjørt: 47 ganger │
|
||||
│ Sist: 2026-03-18 12:30 (OK) │
|
||||
│ │
|
||||
│ ℹ Tilgjengelig: synops-transcribe, │
|
||||
│ synops-rss, synops-render... │
|
||||
│ (12 verktøy) │
|
||||
└───────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Hvert steg er en tekstboks. "Test kjøring" sender til boten
|
||||
med dry-run. Historikk synlig.
|
||||
Modusvelger: Script / Fritekst / Drømmemodus.
|
||||
"Test kjøring" utfører med dry-run.
|
||||
"Konverter til AI" løfter scriptet til fritekst-modus.
|
||||
|
||||
## 9. Edge-modell
|
||||
|
||||
|
|
@ -226,12 +289,13 @@ Orkestreringen peker på det den observerer:
|
|||
```
|
||||
|
||||
Legg til `observes`-edge → aktivert for den noden.
|
||||
Fjern edge → deaktivert. Samme orkestrering kan observere mange noder.
|
||||
Fjern edge → deaktivert. Opprettet via drag-and-drop
|
||||
(se `docs/retninger/interaksjonsmodell.md`).
|
||||
|
||||
### Implisitt vs eksplisitt
|
||||
|
||||
- **`observes`-edge:** Eksplisitt. "Denne orkestreringen overvåker
|
||||
denne noden." Opprettet via drag-and-drop (se § interaksjonsmodell).
|
||||
denne noden."
|
||||
- **Trigger-betingelser:** Implisitt. "Overvåk alt som matcher."
|
||||
- `observes` overtrumfer: fjernet `observes`-edge betyr "ikke her",
|
||||
selv om betingelsene matcher. Brukeren har kontroll.
|
||||
|
|
@ -241,8 +305,6 @@ Fjern edge → deaktivert. Samme orkestrering kan observere mange noder.
|
|||
Brukeren begrenses ikke til kjente verktøy. De skriver fritt
|
||||
— boten prøver, og mangler som oppdages blir feature requests.
|
||||
|
||||
### Slik fungerer det
|
||||
|
||||
```
|
||||
Bruker skriver: "Lag en lydfil med sammendrag og send til deltakerne"
|
||||
|
||||
|
|
@ -250,7 +312,7 @@ Boten sjekker:
|
|||
✓ synops-summarize finnes → oppsummerer
|
||||
✗ synops-tts finnes ikke → kan ikke lage lyd
|
||||
|
||||
Boten svarer i chatten:
|
||||
Boten svarer:
|
||||
"Jeg oppsummerte møtet, men Synops har ikke tekst-til-tale
|
||||
ennå. Jeg oppretter en forespørsel?"
|
||||
|
||||
|
|
@ -260,50 +322,32 @@ Boten svarer i chatten:
|
|||
source_material → orkestreringsnoden
|
||||
```
|
||||
|
||||
Hver feilet steg som skyldes manglende verktøy er en **feature
|
||||
request** — gratis, kontekstuell, fra faktisk behov. Systemet
|
||||
lærer hva brukerne vil ha fra orkestreringer som ikke lykkes.
|
||||
|
||||
### Diskret hint, ikke begrensning
|
||||
|
||||
I editoren vises tilgjengelige verktøy som hint, men brukeren
|
||||
kan skrive hva som helst:
|
||||
|
||||
```
|
||||
┌─ Steg 3 ──────────────────────────────────┐
|
||||
│ Lag en lydfil med oppsummeringen og send │
|
||||
│ den til alle deltakere │
|
||||
│ │
|
||||
│ ℹ Tilgjengelig: synops-tts, synops-rss, │
|
||||
│ synops-render, synops-transcribe... │
|
||||
│ (12 verktøy) — men skriv hva du vil │
|
||||
└────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Brukeren drømmer. Systemet vokser.
|
||||
Systemet lærer hva brukerne vil ha fra det som ikke lykkes.
|
||||
|
||||
## 11. Avgrensning
|
||||
|
||||
- Orkestreringer er **ikke** en generell workflow-engine.
|
||||
De er oppskrifter som boten følger.
|
||||
- Fritekst-instruksjoner er primær. Kompilering er
|
||||
optimalisering, ikke krav.
|
||||
- Triggere evalueres av portvokteren, utførelse av boten.
|
||||
Klar ansvarsfordeling.
|
||||
- Feilhåndtering er botens ansvar. Ingen retry-DSL.
|
||||
De er oppskrifter — deklarative eller AI-tolkede.
|
||||
- Deklarativt script er primær for produksjon. AI er for
|
||||
oppretting, feilhåndtering og drømmemodus.
|
||||
- Triggere evalueres av vaktmesteren, utførelse av
|
||||
vaktmesteren (script) eller bot (fritekst/drøm).
|
||||
- Brukeren begrenses aldri til kjente verktøy. Manglende
|
||||
funksjonalitet fanges opp som feature requests.
|
||||
|
||||
## 12. Komponenter
|
||||
|
||||
| Feature | Rolle |
|
||||
|---------|-------|
|
||||
| Portvokteren | Evaluerer triggere, dispatcher til bot |
|
||||
| @bot | Tolker instruksjoner, utfører med function calling |
|
||||
| Vaktmesteren | Evaluerer triggere, parser/utfører scripts, dispatcher til bot |
|
||||
| @bot | Tolker fritekst-instruksjoner, utfører med function calling |
|
||||
| CLI-verktøy | Gjør det faktiske arbeidet |
|
||||
| Arbeidstavlen | Work items opprettes ved feil |
|
||||
| Responskvalitet | Intelligence/effort per orkestrering |
|
||||
|
||||
## 13. Bygger på
|
||||
- `docs/retninger/unix_filosofi.md` — CLI-verktøy som byggeklosser
|
||||
- `docs/retninger/interaksjonsmodell.md` — drag-and-drop for observes-edge
|
||||
- `docs/concepts/arbeidstavlen.md` — @bot, work items ved feil
|
||||
- `docs/infra/robusthet.md` — function calling, fallback
|
||||
- `docs/features/responskvalitet.md` — intelligence/effort-nivåer
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ For hver tjeneste:
|
|||
- Avhengigheter
|
||||
- Healthcheck
|
||||
|
||||
Tjenester: Caddy, portvokteren, SvelteKit, PostgreSQL,
|
||||
Tjenester: Caddy, vaktmesteren, SvelteKit, PostgreSQL,
|
||||
Authentik, LiteLLM, faster-whisper, LiveKit.
|
||||
|
||||
### 4. CLI-verktøy
|
||||
|
|
@ -77,7 +77,7 @@ men hvorfor det er bygget slik:
|
|||
|
||||
- **Alt er noder og edges.** Ingen separate tabeller. Visninger
|
||||
er spørringer mot grafen.
|
||||
- **Unix-filosofi.** Portvokteren orkestrerer, CLI-verktøy gjør
|
||||
- **Unix-filosofi.** Vaktmesteren orkestrerer, CLI-verktøy gjør
|
||||
jobben. Én ting, gjort godt. Delt verktøykasse.
|
||||
- **Selvdokumenterende system.** Systemet dokumenterer seg selv
|
||||
som noder i seg selv. Docs er ikke filer — de er graf-data.
|
||||
|
|
@ -87,7 +87,7 @@ men hvorfor det er bygget slik:
|
|||
by design, ikke konfigurasjon.
|
||||
- **Noder er sentrum.** Brukere, team, innhold — alt er noder.
|
||||
Tilgang via materialisert tilgangsmatrise fra edges.
|
||||
- **Portvokteren er tynn.** Auth, HTTP-ruting, STDB-synk.
|
||||
- **Vaktmesteren er tynn.** Auth, HTTP-ruting, STDB-synk.
|
||||
All logikk i CLI-verktøy og synops-common.
|
||||
- **Generisk dispatch.** `synops-{job_type} --payload-json`.
|
||||
Nytt verktøy = binary i PATH. Ingen rekompilering.
|
||||
|
|
@ -108,7 +108,7 @@ Nummerert sekvens fra ren server til kjørende system:
|
|||
4. Start Docker-tjenester (PG, Authentik, LiteLLM, Whisper, LiveKit)
|
||||
5. Kjør PG-migrasjoner (skjema + seed)
|
||||
6. Konfigurer Authentik (OIDC-provider, redirect URIs)
|
||||
7. Bygg og deploy portvokteren (cargo build, systemd)
|
||||
7. Bygg og deploy vaktmesteren (cargo build, systemd)
|
||||
8. Bygg og deploy SvelteKit (npm build, systemd)
|
||||
9. Bygg CLI-verktøy (cargo build per tool)
|
||||
10. Konfigurer Caddy (Caddyfile, reload)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
```
|
||||
Lag 1: LLM-leverandør (Claude API, OpenAI, etc.) → utenfor vår kontroll
|
||||
Lag 2: LiteLLM (AI Gateway) → lokal Docker
|
||||
Lag 3: Portvokteren → lokal native (systemd)
|
||||
Lag 3: Vaktmesteren → lokal native (systemd)
|
||||
Lag 4: CLI-verktøy (synops-*) → per-kall, isolert
|
||||
Lag 5: PostgreSQL → lokal Docker, SPOF
|
||||
```
|
||||
|
|
@ -14,11 +14,11 @@ Lag 5: PostgreSQL → lokal Docker, SPOF
|
|||
|
||||
### Bot-nedetid
|
||||
|
||||
Portvokteren overvåker `synops-respond` sin exit-kode. Ved feil:
|
||||
Vaktmesteren overvåker `synops-respond` sin exit-kode. Ved feil:
|
||||
|
||||
```
|
||||
@bot (ingen respons på 30 sek)
|
||||
→ Portvokteren sender automatisk i chatten:
|
||||
→ Vaktmesteren sender automatisk i chatten:
|
||||
"⚠ Boten er midlertidig utilgjengelig.
|
||||
Meldingen din er lagret og blir besvart
|
||||
når tjenesten er tilbake."
|
||||
|
|
@ -31,12 +31,12 @@ kan den prosessere ubesvarte meldinger.
|
|||
|
||||
### Healthcheck
|
||||
|
||||
Portvokteren eksponerer `/health` som sjekker:
|
||||
Vaktmesteren eksponerer `/health` som sjekker:
|
||||
- PG-tilkobling
|
||||
- LiteLLM-tilgjengelighet
|
||||
- Disk-status
|
||||
|
||||
Systemd restarter portvokteren ved gjentatte feil.
|
||||
Systemd restarter vaktmesteren ved gjentatte feil.
|
||||
|
||||
## LLM fallback-kjede
|
||||
|
||||
|
|
@ -53,18 +53,18 @@ synops-respond → LiteLLM
|
|||
|
||||
Brukeren merker kanskje kvalitetsforskjell, men tjenesten er
|
||||
oppe. Fallback-kjeden konfigureres i LiteLLM — ingen endring
|
||||
i portvokteren eller synops-respond.
|
||||
i vaktmesteren eller synops-respond.
|
||||
|
||||
## Ekstern API-bot og CLI-verktøy
|
||||
|
||||
En LLM via API har ikke shell-tilgang. Den trenger det heller
|
||||
ikke — portvokteren er mellomleddet.
|
||||
ikke — vaktmesteren er mellomleddet.
|
||||
|
||||
### Function calling / tool use
|
||||
|
||||
```
|
||||
Bruker: "@bot sjekk om RSS-feeden oppdaterte seg"
|
||||
→ portvokteren → synops-respond → LLM API
|
||||
→ vaktmesteren → synops-respond → LLM API
|
||||
|
||||
LLM svarer med structured output:
|
||||
{
|
||||
|
|
@ -75,21 +75,21 @@ LLM svarer med structured output:
|
|||
]
|
||||
}
|
||||
|
||||
Portvokteren:
|
||||
Vaktmesteren:
|
||||
1. Poster chat-svaret i samtalen
|
||||
2. Spawner: synops-rss --payload-json '{"collection_id":"abc123"}'
|
||||
3. Poster resultatet som oppfølgingsmelding
|
||||
```
|
||||
|
||||
LLM-en beskriver *hva* som skal gjøres. Portvokteren *gjør*
|
||||
det. Navnekonvensjonen (`synops-{tool}`) betyr at portvokteren
|
||||
LLM-en beskriver *hva* som skal gjøres. Vaktmesteren *gjør*
|
||||
det. Navnekonvensjonen (`synops-{tool}`) betyr at vaktmesteren
|
||||
kan dispatche uten hardkodet mapping — samme generiske dispatch
|
||||
som jobbkøen (se `docs/retninger/unix_filosofi.md`).
|
||||
|
||||
### Tilgangskontroll for actions
|
||||
|
||||
Ikke alle brukere skal kunne trigge alle verktøy via `@bot`.
|
||||
Portvokteren sjekker:
|
||||
Vaktmesteren sjekker:
|
||||
|
||||
1. Brukerens rolle i konteksten (owner/admin/member/reader)
|
||||
2. Verktøyets tilgangsnivå (fra `cli_tool`-nodens metadata)
|
||||
|
|
@ -104,9 +104,9 @@ En `reader` kan spørre `@bot` om informasjon, men ikke trigge
|
|||
|-----------|-----------|----------|------------------|
|
||||
| Claude API nede | LiteLLM timeout | Neste modell i kjeden | Svarer, kanskje litt dårligere |
|
||||
| Alle LLM-er nede | synops-respond exit != 0 | Statisk "utilgjengelig" + work_item | Vet at meldingen er mottatt |
|
||||
| Portvokteren nede | Systemd healthcheck → restart | CLI fungerer for Claude Code | Web-brukere venter, terminal funker |
|
||||
| Vaktmesteren nede | Systemd healthcheck → restart | CLI fungerer for Claude Code | Web-brukere venter, terminal funker |
|
||||
| PG nede | Connection refused | Alt stopper | Eneste reelle SPOF |
|
||||
| WebSocket nede | Portvokteren restarter | Frontend rekobler automatisk | Sanntid midlertidig borte, data trygt |
|
||||
| WebSocket nede | Vaktmesteren restarter | Frontend rekobler automatisk | Sanntid midlertidig borte, data trygt |
|
||||
|
||||
## PG som eneste SPOF
|
||||
|
||||
|
|
@ -123,7 +123,7 @@ Mitigering:
|
|||
## Onboarding som statisk dispatch
|
||||
|
||||
Første `@bot` i en ny brukers velkomst-chat trenger ikke
|
||||
LLM-roundtrip. Portvokteren gjenkjenner mønsteret og serverer
|
||||
LLM-roundtrip. Vaktmesteren gjenkjenner mønsteret og serverer
|
||||
onboarding-noden direkte:
|
||||
|
||||
```rust
|
||||
|
|
@ -141,6 +141,6 @@ Etter første melding fungerer chatten normalt med LLM.
|
|||
## Bygger på
|
||||
- `docs/infra/ai_gateway.md` — LiteLLM fallback-kjeder
|
||||
- `docs/retninger/unix_filosofi.md` — generisk dispatch
|
||||
- `docs/retninger/maskinrommet.md` — portvokter-rollen
|
||||
- `docs/retninger/maskinrommet.md` — vaktmester-rollen
|
||||
- `docs/concepts/arbeidstavlen.md` — @bot-konvensjonen
|
||||
- `docs/concepts/selvdokumenterende_system.md` — onboarding-node
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
**Status: Besluttet. Revidert mars 2026 — SpacetimeDB fjernet.**
|
||||
|
||||
> PostgreSQL er eneste datakilde. Sanntid via PG `LISTEN/NOTIFY`
|
||||
> og WebSocket i portvokteren. CAS lagrer binærdata.
|
||||
> og WebSocket i vaktmesteren. CAS lagrer binærdata.
|
||||
> Apache AGE legges til ved behov for Cypher-traverseringer.
|
||||
|
||||
## Lagmodell
|
||||
|
|
@ -12,29 +12,29 @@
|
|||
GUI (SvelteKit)
|
||||
│ skriv │ les (sanntid, WebSocket)
|
||||
▼ ▼
|
||||
Portvokteren (Rust) Portvokteren ──WebSocket──→ GUI
|
||||
Vaktmesteren (Rust) Vaktmesteren ──WebSocket──→ GUI
|
||||
│ validering ▲
|
||||
└──→ PostgreSQL ──NOTIFY──→──┘
|
||||
```
|
||||
|
||||
### Skrivestien
|
||||
GUI → portvokteren → validering → PG. Frontend oppdateres via
|
||||
GUI → vaktmesteren → validering → PG. Frontend oppdateres via
|
||||
WebSocket-push utløst av PG NOTIFY.
|
||||
|
||||
### Lesestien (sanntid)
|
||||
PG → portvokteren → WebSocket → GUI.
|
||||
Portvokteren holder en in-memory cache av aktive subscriptions
|
||||
PG → vaktmesteren → WebSocket → GUI.
|
||||
Vaktmesteren holder en in-memory cache av aktive subscriptions
|
||||
og pusher relevante endringer til tilkoblede klienter.
|
||||
|
||||
### Lesestien (tunge spørringer)
|
||||
GUI → portvokteren → PG.
|
||||
GUI → vaktmesteren → PG.
|
||||
Fulltekstsøk, pgvector, statistikk, AGE-traverseringer.
|
||||
|
||||
## PostgreSQL — eneste datakilde
|
||||
|
||||
Én sannhetskilde. Ingen synk, ingen konsistensproblemer:
|
||||
|
||||
- **Sanntid:** `LISTEN/NOTIFY` → portvokteren → WebSocket
|
||||
- **Sanntid:** `LISTEN/NOTIFY` → vaktmesteren → WebSocket
|
||||
- **Fulltekstsøk:** `tsvector` på `nodes.content` og `nodes.title`
|
||||
- **Semantisk søk:** pgvector for embedding-basert likhet
|
||||
- **Graftraversering:** rekursive CTEs, Apache AGE ved behov
|
||||
|
|
@ -63,11 +63,11 @@ Se [maskinrommet](maskinrommet.md).
|
|||
|
||||
## Sanntid via PG LISTEN/NOTIFY
|
||||
|
||||
PG har innebygd pub/sub. Portvokteren lytter og videresender:
|
||||
PG har innebygd pub/sub. Vaktmesteren lytter og videresender:
|
||||
|
||||
```
|
||||
PG: NOTIFY node_changed, '{"id":"abc","kind":"content"}'
|
||||
→ Portvokteren mottar
|
||||
→ Vaktmesteren mottar
|
||||
→ Sjekker tilgangsmatrise: hvem skal se denne endringen?
|
||||
→ Pusher til relevante WebSocket-tilkoblinger
|
||||
→ Frontend oppdaterer reaktivt
|
||||
|
|
@ -97,9 +97,9 @@ CREATE TRIGGER nodes_notify
|
|||
|
||||
Tilsvarende for edges.
|
||||
|
||||
### WebSocket i portvokteren
|
||||
### WebSocket i vaktmesteren
|
||||
|
||||
Portvokteren holder:
|
||||
Vaktmesteren holder:
|
||||
- Map av tilkoblede klienter → brukerens node_id
|
||||
- Map av node_id → synlige noder (fra node_access)
|
||||
- Ved NOTIFY: filtrer på tilgang, push til relevante klienter
|
||||
|
|
@ -130,6 +130,6 @@ lærdommer: `docs/erfaringer/spacetimedb_integrasjon.md`.
|
|||
beregnet fra edge-grafen, brukes for WebSocket-filtrering
|
||||
- [Universell input og mottak](universell_input.md) — noder og edges
|
||||
er datamodellen for alle tre primitiver
|
||||
- [Maskinrommet / Portvokteren](maskinrommet.md) — CAS-pruning,
|
||||
- [Maskinrommet / Vaktmesteren](maskinrommet.md) — CAS-pruning,
|
||||
edge-drevet ressursorkestrering, validering før skriving,
|
||||
WebSocket-endepunkt for sanntid
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ samme maskin.
|
|||
|
||||
Compute-separasjon er en konfigurasjon, ikke en arkitekturendring.
|
||||
|
||||
## Evolusjon: Maskinrommet → Portvokteren
|
||||
## Evolusjon: Maskinrommet → Vaktmesteren
|
||||
|
||||
Maskinrommet ble bygget som en monolitt — auth, validering,
|
||||
prosessering, jobbkø i én binær. Med unix-filosofi-
|
||||
|
|
@ -193,20 +193,20 @@ prosessering til CLI-verktøy. Det som blir igjen er:
|
|||
3. **Sanntid** — PG LISTEN/NOTIFY → WebSocket til frontend
|
||||
4. **Jobbkø-dispatch** — poll PG, spawn CLI-verktøy
|
||||
|
||||
Dette er en **portvokter**, ikke et maskinrom. Når uttynningen er
|
||||
ferdig, renames `maskinrommet/` til `portvokteren/` og systemd-
|
||||
Dette er en **vaktmester**, ikke et maskinrom. Når uttynningen er
|
||||
ferdig, renames `maskinrommet/` til `vaktmesteren/` og systemd-
|
||||
tjenesten oppdateres. Navnet skal reflektere rollen: vokter porten,
|
||||
gjør ikke jobben.
|
||||
|
||||
**Hva dette gir:**
|
||||
- Portvokteren dør → frontend stopper, men CLI-verktøy fungerer.
|
||||
- Vaktmesteren dør → frontend stopper, men CLI-verktøy fungerer.
|
||||
Claude jobber videre, scripts kjører, terminalen funker.
|
||||
- Et CLI-verktøy dør → bare den funksjonen stopper. Alt annet
|
||||
er upåvirket.
|
||||
- Portvokteren blir så enkel at den nesten aldri feiler.
|
||||
- Vaktmesteren blir så enkel at den nesten aldri feiler.
|
||||
|
||||
**Edge-validering og tilgangskontroll** flyttes til `synops-common`
|
||||
(delt lib) — brukes av både portvokteren og CLI-verktøy.
|
||||
(delt lib) — brukes av både vaktmesteren og CLI-verktøy.
|
||||
|
||||
## Forhold til andre retninger
|
||||
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ synops-transcribe --cas-hash abc123 --model medium
|
|||
- **Navnekonvensjon:** `synops-<verb>` (f.eks. `synops-transcribe`)
|
||||
- **Input:** args + stdin + env-variabler (DATABASE_URL, CAS_ROOT)
|
||||
- **Payload-modus:** `--payload-json <json>` for jobbkø-dispatch
|
||||
(verktøyet parser selv, portvokteren trenger ikke kjenne argumentene)
|
||||
(verktøyet parser selv, vaktmesteren trenger ikke kjenne argumentene)
|
||||
- **Output:** stdout (strukturert — JSON eller markdown)
|
||||
- **Feilhåndtering:** stderr for feilmeldinger, exit-kode != 0 ved feil
|
||||
- **Ingen tilstandsendring uten flagg:** lesing er default, skriving krever
|
||||
|
|
@ -78,7 +78,7 @@ synops-transcribe --cas-hash abc123 --model medium
|
|||
|
||||
## Generisk dispatch
|
||||
|
||||
Portvokteren trenger null konfigurasjon per verktøy. Bare en
|
||||
Vaktmesteren trenger null konfigurasjon per verktøy. Bare en
|
||||
navnekonvensjon:
|
||||
|
||||
```rust
|
||||
|
|
@ -89,10 +89,10 @@ cmd.arg("--payload-json").arg(&job.payload_json);
|
|||
|
||||
`job_type: "transcribe"` → `synops-transcribe --payload-json '{...}'`.
|
||||
Verktøyet parser payload selv. Nytt verktøy = legg binary i PATH,
|
||||
bruk riktig `job_type` i køen. Ingen rekompilering av portvokteren.
|
||||
bruk riktig `job_type` i køen. Ingen rekompilering av vaktmesteren.
|
||||
|
||||
`cli_tool`-noder i PG bærer metadata (timeout, cpu_weight,
|
||||
dokumentasjon) — men ikke dispatch-logikk. Portvokteren leser
|
||||
dokumentasjon) — men ikke dispatch-logikk. Vaktmesteren leser
|
||||
timeout og cpu_weight fra noden, men selve invokasjon er
|
||||
konvensjonsbasert.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue