synops/docs/concepts/orkestrering.md
vegard 77e3d59b46 Orkestrering: AI kun ved oppretting, eventually-modus for Claude Code
Fundamentalt restrukturert:
- AI genererer script ved oppretting, ikke ved kjøring
- Vaktmesteren validerer script før lagring (verktøy finnes? variabler ok?)
- Systemprompt bygges automatisk fra cli_tool-noder i PG
- Kjøring er alltid deterministisk script, ingen AI
- Feil → work_item, ikke AI-eskalering
- "Eventually"-modus: forespørsel lagres, Claude Code (betalt)
  genererer script i neste sesjon med Opus — ingen API-kostnad,
  bedre kvalitet, ingen hastverk

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 16:40:34 +00:00

462 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Konsept: Orkestrering
**Filsti:** `docs/concepts/orkestrering.md`
## 1. Konsept
Orkestreringer er noder som *gjør* ting. De er oppskrifter —
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 vaktmesteren:
podcast-pipeline, AI-beriking, publisering. De er usynlige,
uendrelige og umulige å tilpasse for brukere.
Orkestreringer gjør automatisering til førsteklasses graf-
primitiver — synlige, redigerbare, delbare, versjonerbare.
## 3. Nodemodell
```
node_kind: 'orchestration'
title: "Podcast: opptak → publisering"
content: <script eller fritekst>
metadata: {
"trigger": {
"event": "communication.ended",
"conditions": {
"has_trait": "podcast",
"has_media": "audio"
}
},
"executor": "script" | "bot" | "dream",
"intelligence": 2,
"effort": 2
}
```
### Tre typer noder som gjør
| node_kind | Ikon | Aktivering | Eksempel |
|-----------|------|------------|----------|
| `cli_tool` | 🔧 | Kalles av andre | synops-transcribe |
| `ai_preset` | 🔧 | Kalles av AI-verktøy | "Rens tekst"-prompt |
| `orchestration` | ⚡ | **Trigger-drevet, utfører seg selv** | Podcast-pipeline |
⚡ signaliserer: "denne noden gjør noe av seg selv."
## 4. Tre utførelsesnivåer
### 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: AI-assistert oppretting
Brukeren beskriver hva de vil i naturlig språk. AI genererer
et deklarativt script (nivå 1) som vaktmesteren validerer.
**AI brukes ved oppretting, ikke ved kjøring.**
```
Bruker: "Gjør episoden klar for publisering"
→ synops-ai genererer script-forslag:
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 "Pipeline feilet" --tag bug
→ Vaktmesteren validerer:
✓ Alle verktøy finnes
✓ Alle variabler er gyldige
✓ Syntaks er korrekt
→ Bruker godkjenner eller justerer
→ Lagres som orchestration-node (nivå 1)
```
Etter oppretting kjører scriptet uten AI — deterministisk,
gratis, raskt. AI-kostnaden er én gang, ved oppretting.
### Nivå 3: Drømmemodus
Brukeren skriver hva de *ønsker*, uten å vite hvilke verktøy
som finnes. AI prøver å generere et script, og mangler
som oppdages blir feature requests.
```
Bruker: "Lag en lydfil med sammendrag og send til deltakerne"
→ synops-ai genererer:
1. synops-summarize --communication-id {event.communication_id}
2. synops-tts ??? ← finnes ikke
→ Vaktmesteren validerer:
✗ synops-tts finnes ikke
→ Tilbake til bruker:
"Scriptet refererer til synops-tts som ikke finnes.
Jeg oppretter en forespørsel om TTS-verktøy."
→ work_item "TTS for møteoppsummeringer" --tag feature
→ Delvis script lagres med synops-tts markert som manglende
```
### Systemprompt for script-generering
AI-modellen trenger kontekst for å generere gode scripts.
`synops-orchestrate --generate-system-prompt` bygger dette
automatisk fra PG:
```
Du er en orkestreringsplanlegger for Synops.
TILGJENGELIGE VERKTØY:
- synops-transcribe: Whisper-transkribering
Bruk: --cas-hash <hash> --model <model>
- synops-summarize: AI-oppsummering
Bruk: --communication-id <uuid>
- synops-rss: RSS-generering
Bruk: --collection-id <uuid>
[... hentet fra cli_tool-noder i PG]
SCRIPT-GRAMMATIKK:
NÅR <event> [HVIS <betingelse>]
<N>. <tool> <args...>
VED_FEIL: <tool> <args...> | work_item <title> [--tag <tag>]
Variabler: {event.<felt>}, {input.<felt>}
EKSEMPLER:
[... hentet fra eksisterende orchestration-noder]
Svar KUN med et gyldig orkestreringscript.
```
Systemprompt oppdateres automatisk når nye verktøy legges til.
Fungerer med alle modeller — Claude, Llama, Mixtral, Grok.
### Tre-stegs flyten
```
1. OPPRETTING (AI, én gang)
Bruker beskriver → synops-ai genererer script
→ vaktmester validerer → bruker godkjenner → lagres
2. KJØRING (ingen AI, hver gang)
Trigger → vaktmester parser script → utfører CLI-kall
→ logger resultat
3. FEILHÅNDTERING (AI, sjelden)
VED_FEIL feiler → eskalér til synops-ai
→ foreslå fix eller opprett work_item
```
AI-kostnad er nesten null i drift.
### Naturlig progresjon
```
Drømmemodus → Bruker beskriver ønsket resultat
↓ AI genererer script-forslag
Script → Vaktmester validerer, bruker godkjenner
↓ Kjører uten AI
Produksjon → Deterministisk, gratis, raskt
↓ Hvis mønster gjentas
Kode → Eget CLI-verktøy (synops-<verb>)
```
AI ved oppretting. Ingen AI ved kjøring. Script kan alltid
redigeres manuelt.
## 5. Strukturert trigger
Triggeren er alltid strukturert — vaktmesteren evaluerer
den effektivt uten LLM, uavhengig av utførelsesnivå:
```jsonc
{
"trigger": {
"event": "communication.ended",
"conditions": {
"has_trait": "podcast",
"has_media": "audio"
}
}
}
```
### Kjente trigger-events
| Event | Beskrivelse |
|-------|-------------|
| `node.created` | Ny node opprettet |
| `edge.created` | Ny edge opprettet |
| `communication.ended` | Samtale/innspilling avsluttet |
| `node.published` | Node publisert (belongs_to med slot) |
| `scheduled.due` | Planlagt tidspunkt nådd |
| `manual` | Bruker trykker "Kjør" |
### Betingelser
Filtrerer triggeren ytterligere:
- `has_trait` — noden/samlingen har denne traiten
- `has_media` — noden har media-edge av denne typen
- `has_tag` — noden har tagged-edge med denne verdien
- `node_kind` — noden er av denne typen
## 6. Utførelse
### Script-modus (nivå 1)
```
Trigger aktiveres
→ 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.
### Feilhåndtering ved kjøring
Alle orkestreringer kjører som script (nivå 1). Ved feil:
```
Script kjører steg 2
→ synops-summarize returnerer exit 1
→ VED_FEIL definert? → kjør alternativ
→ Alternativ feiler også?
→ Opprett work_item med feilbeskrivelse
→ Logger i orchestration_log
→ Stopp orkestreringen
```
Ingen AI-eskalering ved kjøring. Feil håndteres av scriptet
(VED_FEIL) eller blir work_items for manuell/AI-assistert
oppfølging.
### Asynkron modus: "eventually"
Brukeren kan godta at et svar eller script-generering kommer
*etterhvert* i stedet for umiddelbart:
```
Bruker: "Lag en orkestrering for podcast-pipeline"
→ [Nå] [Eventually]
"Eventually":
→ Forespørselen lagres som work_item med tag "script_request"
→ Neste Claude Code-sesjon (task runner) plukker den opp
→ Genererer script med Opus (full verktøy-tilgang, fillesing)
→ Scriptet valideres og lagres
→ Bruker varsles: "Orkestreringen din er klar"
```
Fordeler:
- **Ingen API-kostnad** — Claude Code (betalt) gjør jobben
- **Bedre kvalitet** — Opus med full kontekst, ikke en lettvekts API-modell
- **Ingen hastverk** — script-generering trenger ikke skje i sanntid
Dette gjelder også feilhåndtering: et feilet VED_FEIL-steg
blir en work_item som Claude Code løser i neste sesjon —
reparerer scriptet, legger til manglende verktøy, osv.
## 7. Koblinger mellom orkestreringer
Orkestreringer er noder med edges:
```
"Podcast: opptak → publisering"
──triggers──→ "Varsle redaksjonen om ny episode"
──triggers──→ "Post til sosiale medier"
```
Output fra én orkestrering kan trigge neste via `triggers`-edge.
Kaskade via edges, ikke hardkodet.
## 8. Brukergrensesnitt
```
┌─ Podcastorkestrering ⚡ ──────────────────┐
│ │
│ Trigger: [Innspilling avsluttet ▼] │
│ Betingelse: [Samling har podcast-trait ▼] │
│ Modus: [Script ▼] │
│ │
│ ┌─────────────────────────────────────┐ │
│ │ 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} │ │
│ └─────────────────────────────────────┘ │
│ │
│ [▶ 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) │
└───────────────────────────────────────────┘
```
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
| Edge | Source → Target | Betydning |
|------|----------------|-----------|
| `belongs_to` | orchestration → collection | Tilhører denne samlingen |
| `observes` | orchestration → any node | Overvåker denne noden for trigger-events |
| `triggers` | orchestration → orchestration | Kaskade-kobling |
| `uses` | orchestration → cli_tool | Bruker dette verktøyet |
| `mentions` | orchestration → any | Refererer til denne noden |
### `observes`-edge: eksplisitt kobling
Orkestreringen peker på det den observerer:
```
"Auto-clip URL-er" (orchestration)
──observes──→ #Redaksjonen (communication)
──observes──→ #Research (communication)
```
Legg til `observes`-edge → aktivert for den noden.
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."
- **Trigger-betingelser:** Implisitt. "Overvåk alt som matcher."
- `observes` overtrumfer: fjernet `observes`-edge betyr "ikke her",
selv om betingelsene matcher. Brukeren har kontroll.
## 10. Drømmemodus: brukeren skriver hva de vil
Brukeren begrenses ikke til kjente verktøy. De skriver fritt
— boten prøver, og mangler som oppdages blir feature requests.
```
Bruker skriver: "Lag en lydfil med sammendrag og send til deltakerne"
Boten sjekker:
✓ synops-summarize finnes → oppsummerer
✗ synops-tts finnes ikke → kan ikke lage lyd
Boten svarer:
"Jeg oppsummerte møtet, men Synops har ikke tekst-til-tale
ennå. Jeg oppretter en forespørsel?"
→ work_item i innboks:
title: "TTS for møteoppsummeringer"
tagged: "feature"
source_material → orkestreringsnoden
```
Systemet lærer hva brukerne vil ha fra det som ikke lykkes.
## 11. Prinsipp: målet er alltid script
Enhver orkestrering har som mål å bli et deklarativt script.
AI-nivåene (fritekst, drøm) er **verktøy for å komme dit**,
ikke permanente driftsmoduser.
Hvis en orkestrering ikke kan uttrykkes som script, betyr det
én av to ting:
1. **Verktøyet mangler.** Lag et nytt CLI-verktøy som dekker
det manglende steget. Da kan scriptet uttrykke det.
2. **Logikken er for vag.** Stram opp instruksjonene til de er
presise nok for et script. Hvis det ikke er mulig, er
orkestreringen kanskje ikke moden nok for automatisering.
AI som permanent feilhåndtering i en orkestrering er et tegn
på at noe er galt — enten med verktøyet eller med spesifikasjonen.
Det skal fikses, ikke kompenseres.
```
Drømmemodus → "Hva vil vi oppnå?"
Fritekst → "Hvordan gjør vi det?"
Script → "Nøyaktig dette."
Hvis script ikke er mulig:
→ Mangler verktøy? → Lag synops-<verb>
→ For vagt? → Stram opp spec
→ Umulig? → Ikke automatiser dette
```
## 12. Avgrensning
- Orkestreringer er **ikke** en generell workflow-engine.
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.
## 13. Komponenter
| Feature | Rolle |
|---------|-------|
| 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 |
## 14. 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