StyloFlow: Begränsade fuzzysignaler -drivna arbetsflöden (Svenska (Swedish))

StyloFlow: Begränsade fuzzysignaler -drivna arbetsflöden

Sunday, 11 January 2026

//

22 minute read

Jag byggde StyloFlow eftersom jag fortsatte skriva samma mönster om och om igen: komponenter som reagerar på vad som hände innan, släpper ut självförtroende poäng, och ibland måste eskalera till mer dyrare analyser. De existerande arbetsflödemotorerna ville att jag skulle tänka i termer av DAG eller statmaskiner. Jag ville tänka i signaler.

NOTA: StyloFlow är inte än en färdig produkt; eftersom jag bygger lucidRAG och StyloBot IM SK2 jag lägger till förlorade egenskaper och polerar API: t på både StyloBllow och efemeral . DetMska4 är fortfarande i aktiv utvecklingMske5 men du kan pröva det och ge feedbackMsko6 Jag kommer att uppdatera det här senare Msek7 SignalSink-grejerna till exempel kommer att ändras för efemerala vMsku8 till att vara Mska9 bara läsbart')

StyloFlow är en signaldriven orkestrationsbibliotek som matchar hur jag tänker om AI-rör: komponenter deklarerar vad de producerar och vad de behöver

Det här är infrastrukturen som ger energi till klarRAG - ett graf RAG-verktyg som kombinerar DocSummarizer (dokument DataSummarizer (strukturerade data ), och ImageSummarizer (, bilder, ), till en förenade fråga, -, en upphöjningssystem med kunskapsgrafvisualisering. Stylobot ( ett avancerat robotskyddssystem ) och implementerar Reduzerad RAG mönster.

lucidRAG-gränssnitt

källa: GitHub - StyloFlow


Vad är detta?

StyloFlow är en fungerande prototyp APIn och formen kommer att utvecklas när jag bygger lucidRAG och Stylobot, men de här beskrivna sekvenserna och mönsterna är punktsignaler : som första signal, classfakter, , självförtroende,- driven branching, , och eskalation som ett strukturellt mönst,

Detta är ett nytt DSL- eller arbetsflödesspråk. avrättelsesemantik byggt runt signalern, ,, självförtroende, ,, och begränsad skalning. ., Idag kör den i en process med begränsad samspel, M SK4, imorgon delar den ut spår mellan maskiner samtidigt som signalerna är den stabila gränsen.


Problemet med traditionella arbetsflöden

Här är vad de flesta arbetsflödesmotorer ser ut som

// ❌ Traditional: Hardcoded dependencies
public async Task ProcessDocumentAsync(string path)
{
    var text = await ExtractTextAsync(path);
    var chunks = await ChunkTextAsync(text);
    var embeddings = await GenerateEmbeddingsAsync(chunks);
    var entities = await ExtractEntitiesAsync(chunks);
    await StoreEverythingAsync(embeddings, entities);
}

Detta fungerar tills:

  • Du vill hoppa över enhetens extraktion för enkla frågor
  • Du behöver köra extraktion och embedding parallellt.
  • Man vill utvecklas till ett bättre modell baserat på självförtroende.
  • Du behöver lägga till en ny processfas utan att röra vid den existerande koden

Du får antingen:

  1. Rigida ledningar som inte kan anpassa sig
  2. Massiva om/else träd för vägledning
  3. Gudklassar som vet allt

Fonden: Spädrig uträttelse

StyloFlow bygger på mestlucid.ephemeral - en bibliotek för begränsad , spårbar async exécution

Vinnig sammanfattning av vad ephemeral innebär:

// Bounded concurrent processing with full visibility
var coordinator = new EphemeralWorkCoordinator<DocumentJob>(
    async (job, operation, ct) => {
        await ProcessAsync(job, ct);
        operation.Signal("document.processed");
    },
    new EphemeralOptions { MaxConcurrency = 4 });

// Enqueue work
await coordinator.EnqueueAsync(new DocumentJob(filePath));

// Full observability
Console.WriteLine($"Active: {coordinator.ActiveCount}");
Console.WriteLine($"Completed: {coordinator.TotalCompleted}");

Nyckliga fördelar med ephemeral:

  • Avgränsad samtida ( inga uteglömda minnen )
  • LRU utfånganing av gamla operationer
  • Signalpublicering för cross-- komponentkoordination
  • Operation pinning för att förhindra för tidigt utfångande

För mer information Ogen och Don't Bort glöm.


Vad denna modell gör möjligt

Detta orkesteringsmodell förlänger ephemeral med:

  1. YAML-driven komponent manifester - Deklarerande konfiguration
  2. Trigger baserade på Signal- - Komponenter körs när signaler dyker upp
  3. Koordination - Prioritet-baserad exécution med samtida banor
  4. Escalationsmönster - Vänta dyr analys tills det behövs
  5. Entitetskontrakt - Typ
  6. Budjetstyre - Symbolgränser , kostnadsgränser

Här är nyckeln till den arkitektoniska förändringen.

graph TD
    subgraph Traditional["❌ Traditional: Hardcoded"]
        T1[Component A] -->|calls| T2[Component B]
        T2 -->|calls| T3[Component C]
        T3 -->|calls| T4[Component D]
    end

    subgraph StyloFlow["✅ StyloFlow: Signal-Driven"]
        S1[Component A]
        S2[Component B]
        S3[Component C]
        S4[Component D]
        SS[Signal Sink]

        S1 -.emits.-> SS
        S2 -.emits.-> SS
        S3 -.emits.-> SS
        SS -.triggers.-> S2
        SS -.triggers.-> S3
        SS -.triggers.-> S4
    end

    style T1 stroke:#ff6b6b
    style T2 stroke:#ff6b6b
    style T3 stroke:#ff6b6b
    style T4 stroke:#ff6b6b
    style S1 stroke:#51cf66
    style S2 stroke:#51cf66
    style S3 stroke:#51cf66
    style S4 stroke:#51cf66
    style SS stroke:#339af0

Komponenter ropar aldrig på varandra . De släpper ut signaler och reagerar på signalern.


Kernkoncept : Signaler och ägande

Signaler är fakta om vad som hände, inte kommandor eller händelser . De ' är förändringslösa M SK3 är nummertagna i tid, , och bär på självförtroende värden MSC5 Varje atom äger sina signaler Mska6 de Mske7 är externt förändringsiva M Ska8 Inget annat kan modifiera den listan

public record Signal
{
    public required string Key { get; init; }           // "document.chunked"
    public object? Value { get; init; }                 // Optional payload
    public double Confidence { get; init; } = 1.0;      // 0.0 to 1.0
    public required string Source { get; init; }        // Which component
    public DateTime Timestamp { get; init; }
    public Dictionary<string, object>? Metadata { get; init; }
}

Kritisk arkitektonisk punkt: SignalSink är en uthärdlig historisk syn på saker och ting

Signalsink förmedlar en sökbar bild över alla operationer på alla koordinatorer som delar den. signaler över hela koordinatorns livscykel | - | när en operation flyttar bort från sin koordinator

// Create a shared signal sink (no parameters, signals persist)
var sink = new SignalSink();

// Coordinators manage operation lifetime, NOT signal lifetime
var coordinator = new EphemeralWorkCoordinator<string>(
    ProcessAsync,
    new EphemeralOptions
    {
        MaxConcurrency = 8,
        MaxTrackedOperations = 100,         // Operations evict after this
        MaxOperationLifetime = TimeSpan.FromMinutes(5),  // Or after this time
        Signals = sink                      // Share the persistent view
    });

// Operations emit via their emitter
public async Task ProcessAsync(string docId, SignalEmitter emitter, CancellationToken ct)
{
    // Store actual data externally (cache, database, blob storage)
    await cache.SetAsync($"doc-{docId}", documentData);

    // Signal carries a REFERENCE, not the data
    emitter.Emit("document.chunked", key: docId); // Key references external data
}

// SignalSink is readonly - it cannot alter signals
// Signals persist until their operation evicts from the coordinator

SignalSink ger två koordineringsmönster:

1. Push

// Subscribe to the sink for push notifications
sink.Subscribe(signal => {
    if (signal.Is("document.chunked"))
    {
        // React immediately - signal includes OperationId
        Console.WriteLine($"Op {signal.OperationId} chunked doc at {signal.Timestamp}");
    }
});

// Returns IDisposable for cleanup
using var subscription = sink.Subscribe(HandleSignal);

2. Gå tillväga-baserade Fråg

// Get all signals for a specific operation
var opSignals = sink.GetOpSignals(operationId);

// Detect if any operation has emitted a signal
if (sink.Detect("embeddings.generated"))
{
    // At least one operation has generated embeddings
}

// Sense all signals matching a condition
var recentErrors = sink.Sense(s =>
    s.Signal.StartsWith("error.") &&
    s.Timestamp > DateTimeOffset.UtcNow.AddMinutes(-5)
);

// Get operation summary from its signal history
var summary = sink.GetOp(operationId);
Console.WriteLine($"Operation ran for {summary?.Duration}");

Varför är detta viktigt:

  • Readonly se - SignalSink kan inte ändra signaler ; den erbjuder bara query-utgång
  • Signaler förblir kvar - Signaler är kvar under bevakning tills deras operation avlägsnar koordinatorn
  • Signaler är koordination, inte transport - Signaler bär nycklar /referencer till externa data, inte själva datan
  • Koordinatorns självständighet - Flera koordinatorer kan dela med sig av en anslutning ; signalernas historia omfattar alla
  • Thread-säkra - Slucka - fria svar för query-operationer
  • Att både trycka och dra - Abonnerande () för reaktiva , Bemärkelse M SK3 Detektor
  • Operationskorrelation - Varje signal inkluderar OperationId för att spåra över koordinatorer
  • Pattern matchning - Frågan efter exakt matchning , prefix

Nyckeldesignprincipen: Speichera stora data (documenter, bilderM SK2 vektorer ) i kassor eller databaserMSC4 Signaler bär bara referensramar som "cache://doc-123" eller operativa nyckeln.

Till exempel koordinering:

// Operation emits signal via ISignalEmitter interface
public async Task ProcessAsync(Item item, ISignalEmitter emitter, CancellationToken ct)
{
    // Emit to the sink
    emitter.Emit("processing.started");

    await DoWorkAsync(item, ct);

    emitter.Emit("processing.completed");
}

// Wave checks if it should run by querying sink
public bool ShouldRun(string path, AnalysisContext ctx)
{
    // Pull pattern: query the sink via context
    return ctx.Detect("document.chunked");
}

// UI subscribes to sink for reactive updates
sink.Subscribe(signal => {
    if (signal.Signal.StartsWith("document."))
    {
        // Push pattern: react immediately
        UpdateProgressUI(signal);
    }
});

Escalation sker på två nivåer

  1. I en koordinator -vågor kontrollerar signalförtroende och kör villkorlig dyr analys
  2. Mellan koordinatorer - EscalatorAtomstrålar signaler från en snabb koordinator → dyr koordinator baserat på signalkriterien
// Pattern 1: Intra-coordinator escalation (wave checks signals)
public bool ShouldRun(string path, AnalysisContext ctx)
{
    var quality = ctx.GetSignal("quality.score");
    return quality?.Confidence < 0.7; // Only run if quality is low
}

// Pattern 2: Inter-coordinator escalation (atom routes to another coordinator)
// Option A: Explicit escalation signal
typed.Raise("escalate.to.expensive", payload, key: "doc-123");

// Option B: EscalatorAtom examines signals and decides
new EscalatorAtomOptions<T> {
    ShouldEscalate = evt => evt.Payload.Confidence < 0.7
}

Flera koordinatorer funkar oberoende av varandra . EscalatorAtom tittar på signaler från en koordinator och förflyttar arbetet till en annan när det behövs.

För teorin bakom detta, ,, se Begränsat Fuzzy Kontexttdragning.

Signaler bär referensramar , Nej Data

Kritiskt Signaler är koordineringshändelser , inte dataöverföring. stora data | ( | dokument | , | bilder |, | inbäddar | ) | borde finnas i ett externt lager

// ❌ BAD: Carrying data in signals (memory pressure, boxing)
var imageBytes = await ProcessImageAsync(input);
emitter.Emit("image.processed", metadata: new { Data = imageBytes });

// ✅ GOOD: Store externally, signal the reference
var imageBytes = await ProcessImageAsync(input);
var cacheKey = $"processed/{docId}";
await cache.SetAsync(cacheKey, imageBytes);
emitter.Emit("image.processed", key: cacheKey);

// Later: Retrieve when needed
if (sink.Detect("image.processed"))
{
    var signals = sink.GetOpSignals(operationId);
    var imageKey = signals.FirstOrDefault(s => s.Signal == "image.processed")?.Key;
    if (imageKey != null)
    {
        var bytes = await cache.GetAsync<byte[]>(imageKey);
    }
}

De bästa metoderna:

  • Använd kaser (in-memory eller fördeladeM SK2 för efemerala data
  • Använd databaser för hållbara data
  • Använd blob-skivor för stora filer
  • Signal URIs: "cache://key", "blob://container/file", "db://table/id"
  • Håll signalerna i lättvikt - bara nyckeln , självförtroende poängen

Kernkoncept : Komponentmanifester

Manifest deklarerar kontrakt. (, vad får mig att göra det , ",, vad jag utsläpper, ,, vad det kostar" separat från implementering. Den här separationen existerar så att man kan förstå arbetsflödet utan att läsa koden , och ändra exécutionsorten utan att kompilera

name: BotDetector
priority: 10              # Lower runs first
enabled: true

# What kind of component is this?
taxonomy:
  kind: analyzer          # sensor|analyzer|proposer|gatekeeper
  determinism: probabilistic
  persistence: ephemeral

# When should this run?
triggers:
  requires:
    - signal: http.request.received
      condition: exists

# What does it produce?
emits:
  on_complete:
    - key: bot.detected
      confidence_range: [0.0, 1.0]

  conditional:
    - key: bot.escalation.needed
      when: confidence < 0.7

# Resource limits
lane:
  name: fast              # fast|normal|slow|llm
  max_concurrency: 8

budget:
  max_duration: 100ms

# Configuration values
defaults:
  confidence:
    bot_detected: 0.6
  timing:
    timeout_ms: 100

Möjligheter:

  1. Runtime-konfiguration - ändra prioritering utan att kompilera
  2. Omgivning överrider - Överrider via appsettings.json
  3. Klara kontrakt - Se vilka signaler som utlöser vad
  4. Själva dokumentering - Manifestet är specifikationen

Visual Workflow Builder

Medan du kan skriva YAML-manifester av hand., StyloFlow innehåller en visuell arbetsflödesbyggare som låter dig designa signal.

StyloFlow Workflow Builder

Interfejsen erbjuder:

  • Drag- -- och - komponenter ur taksonomen (sensorer , analyser M SK2 förespråkare
  • Signaldragglackande - Koppla utgångar till ingångar visuellt
  • Liv manifest föreställning - Se skapade YAML medan du bygger
  • Triggervisualisering - Se vilka signaler som utlöser vilka komponenter
  • Längsläggning - Drag komponenter in i snabba spurr
  • Verklig validation - Ta tag i oäkta signalreferenser omedelbart

Detta gör det enkelt att experimentera med olika arbetsflödes former utan att skriva YAML på hand.


Kernkoncept: Weller

Ett våg är en komponerande analyssstadium. Den här gränssnittet existerar för att göra " skulle vi kunna köra ?" som ett första beslut, inte ett implementeringsdetail begravt i villkorlig logik .

public interface IContentAnalysisWave
{
    string Name { get; }
    int Priority { get; }               // Higher runs first
    bool Enabled { get; set; }

    // Quick filter - avoid expensive work
    bool ShouldRun(string contentPath, AnalysisContext context);

    // Do the analysis
    Task<IEnumerable<Signal>> AnalyzeAsync(
        string contentPath,
        AnalysisContext context,
        CancellationToken ct);
}

Enkel våg exempel:

public class FileTypeWave : IContentAnalysisWave
{
    public string Name => "FileType";
    public int Priority => 100;
    public bool Enabled { get; set; } = true;

    public bool ShouldRun(string path, AnalysisContext ctx)
    {
        // Skip if we already know the type
        return ctx.GetSignal("file.type") == null;
    }

    public async Task<IEnumerable<Signal>> AnalyzeAsync(
        string path,
        AnalysisContext ctx,
        CancellationToken ct)
    {
        var extension = Path.GetExtension(path);
        var mimeType = GetMimeType(extension);

        return new[]
        {
            new Signal
            {
                Key = "file.type",
                Value = mimeType,
                Confidence = 1.0,
                Source = Name
            }
        };
    }
}

Koordination av vågor

Den WaveCoordinator kör vågor i prioritära ordning:

var coordinator = new WaveCoordinator(waves, profile);
var context = new AnalysisContext();

var results = await coordinator.ExecuteAsync(filePath, context, ct);

// All signals from all waves
foreach (var signal in context.GetAllSignals())
{
    Console.WriteLine($"{signal.Key}: {signal.Value}");
}

Konkurenserande vägar

Rör vågor på vägar med olika samtida gränser:

Gadan syfte Konkurens
fast Vinniga kontroller IP-uppsök , File type
normal Standardbearbetande Parsering
io IM SK1O bunden (filer läser , API-sändningar) M 32
llm dyra LLM-sändningar

Detta förhindrar dyra operationer från att blockera billiga


Arkitektur: Hur det passar ihop

Här är den kompletta bilden.

graph TB
    subgraph Input["Input Layer"]
        REQ[HTTP Request]
        FILE[File Upload]
        JOB[Background Job]
    end

    subgraph Ephemeral["Ephemeral Layer"]
        COORD[Work Coordinator]
        OPS[Operations<br/>own signals]
        SINK[SignalSink<br/>read-only view]
    end

    subgraph StyloFlow["StyloFlow Layer"]
        MAN[Manifests]
        WAVE[Wave Coordinator]
        ATOMS[Atoms<br/>own signals]
    end

    subgraph Execution["Execution"]
        FAST[Fast Lane]
        NORM[Normal Lane]
        LLM[LLM Lane]
    end

    subgraph Output["Output"]
        RES[Results]
        ESCAL[Escalation]
        STORE[Persistence]
    end

    REQ --> COORD
    FILE --> COORD
    JOB --> COORD

    COORD --> OPS
    SINK -.queries.-> OPS

    WAVE -.reads.-> SINK
    MAN -.configures.-> WAVE
    WAVE --> ATOMS

    ATOMS --> FAST
    ATOMS --> NORM
    ATOMS --> LLM

    SINK -.queries.-> FAST
    SINK -.queries.-> NORM
    SINK -.queries.-> LLM

    SINK -.read for.-> RES
    SINK -.read for.-> ESCAL
    SINK -.read for.-> STORE

    style COORD stroke:#339af0
    style SINK stroke:#339af0
    style WAVE stroke:#51cf66
    style ATOMS stroke:#51cf66

Flow:

  1. Input kommer: ( Http-ansökande , file , job
  2. Ephemeral koordinator skapar operation ( som ägs en tom signallista )
  3. Operationen lägger till signaler i dess egen lista
  4. Bandkoordinator READS signaler via SignalSink-uppfattning för att kontrollera utlösande tillstånd
  5. vågor vars utlösare matchar körs i prioritära ordning inom samtida banor
  6. Varje våg lägger till signaler i sin aktivitet
  7. Bandkoordinator fortsätter att läsa signaler för att hitta nytillfredsställda utlösande
  8. Slutliga utgångsfragen SignalSink för att läsa signaler och bedöma handlingar

Eigenschaftsmodell: Varje operation/ ägs sina signaler, atomen . SignalSink ger en läsning, - bara syn över alla operationer, . Signaler kan upphöjas, | ( | kopieras, M SK5 | eller ekaderas, ♫ ( | bevaras när utskjutts, ♪ ♪ ), | men listan som ägs är externt oänderbar, ♪

Den nuvarande Ausführungsmodellen: Enkela operationer med LRU-utskjutning

Framstående Ausführungsmodell: Distribuerade banor över maskiner, SignalSink frågar fjärran operationer , atomer utförs på olika värden. Signaler förblir den stabila gränsen -, de , ', är redan serialiserade, ,, stängstimmade och självbevarade. -, består av ., ägandes modellen ändrar inte på ' eller .."

Implementeringen i processen validerar semantiken.


Använd fall: lucidRAG Dokumentbehandling

Låt oss se hur lucidRAG använder StyloFlow:

Etappe 1: Initiell upptäckt

public class FileTypeDetectorWave : IContentAnalysisWave
{
    public int Priority => 100;  // Run first

    public async Task<IEnumerable<Signal>> AnalyzeAsync(...)
    {
        var extension = Path.GetExtension(path);

        return new[]
        {
            new Signal
            {
                Key = "file.extension",
                Value = extension,
                Source = "FileTypeDetector"
            }
        };
    }
}

Etappen 2: Förstärkning ( aktiverad av filen.upplängningM SK3

// In manifest:
// triggers:
//   requires:
//     - signal: file.extension
//       condition: in
//       value: [".pdf", ".docx", ".md"]

public class ChunkingWave : ConfiguredComponentBase, IContentAnalysisWave
{
    public int Priority => 80;

    public async Task<IEnumerable<Signal>> AnalyzeAsync(...)
    {
        var chunks = await ChunkDocumentAsync(path);

        ctx.SetCached("chunks", chunks);  // Share with other waves

        return new[]
        {
            new Signal
            {
                Key = "document.chunked",
                Value = chunks.Count,
                Source = Name
            }
        };
    }
}

Etappe 3: Embedding ( trigerade av dokumentet. instängdaM SK3

public class EmbeddingWave : ConfiguredComponentBase, IContentAnalysisWave
{
    public int Priority => 60;

    public bool ShouldRun(string path, AnalysisContext ctx)
    {
        // Only run if chunking succeeded
        return ctx.GetSignal("document.chunked") != null;
    }

    public async Task<IEnumerable<Signal>> AnalyzeAsync(...)
    {
        var chunks = ctx.GetCached<List<Chunk>>("chunks");
        var embeddings = await GenerateEmbeddingsAsync(chunks);

        ctx.SetCached("embeddings", embeddings);

        return new[]
        {
            new Signal
            {
                Key = "embeddings.generated",
                Value = embeddings.Count,
                Source = Name
            }
        };
    }
}

Etappe 4: Enhetens extraktion ♫ ( ♫ likvärdig med inbäddning ♫

public class EntityExtractionWave : ConfiguredComponentBase, IContentAnalysisWave
{
    public int Priority => 60;  // Same as embedding - runs in parallel

    public async Task<IEnumerable<Signal>> AnalyzeAsync(...)
    {
        var chunks = ctx.GetCached<List<Chunk>>("chunks");

        // Use deterministic IDF scoring, not LLM per chunk
        // (See Reduced RAG pattern)
        var entities = await ExtractEntitiesAsync(chunks);

        return new[]
        {
            new Signal
            {
                Key = "entities.extracted",
                Value = entities.Count,
                Confidence = CalculateConfidence(entities),
                Source = Name
            }
        };
    }
}

Etappe 5: Jaktkontroll

public class QualityCheckWave : ConfiguredComponentBase, IContentAnalysisWave
{
    public int Priority => 40;  // After embedding + entities

    public async Task<IEnumerable<Signal>> AnalyzeAsync(...)
    {
        var embeddingSignal = ctx.GetSignal("embeddings.generated");
        var entitySignal = ctx.GetSignal("entities.extracted");

        var embeddingCount = (int)embeddingSignal.Value;
        var entityConfidence = entitySignal.Confidence;

        var quality = CalculateQuality(embeddingCount, entityConfidence);

        var signals = new List<Signal>
        {
            new Signal
            {
                Key = "quality.score",
                Value = quality,
                Source = Name
            }
        };

        // Trigger escalation if quality is poor
        if (quality < GetParam<double>("quality_threshold", 0.7))
        {
            signals.Add(new Signal
            {
                Key = "escalation.needed",
                Value = "low_quality_document",
                Source = Name
            });
        }

        return signals;
    }
}

Möjligheterna med den här metoden

  1. Parallellt utförande - Embedding och entite extraktion körs samtidigt
  2. Bedingande granskning - Kvalitekontroll bestämmer om eskalering behövs
  3. Samlat sammanhang - Waves får tillgång till chunks utan att skicka dem explicit
  4. Lika att sträcka ut - Lägg till en ny våg utan att ändra de som redan finns
  5. Observerbara - Varje steg emiterar signaler som du kan övervaka

Det här är Reduzerad RAG mönster i aktion: deterministisk extraktion framifrån , LLMs bara för syntesten.


Använd fall: Stylobot Bot Detektor

Stylobot är ett avancerat Bot-detektorsystem som använder StyloFlow för multi- -detektorn.


Signal-drivna orkestreringsmönster

Pattern 1: Ventilator

Ett signal utlöser flera vågor:

graph LR
    S1[document.uploaded] --> W1[ChunkingWave]
    S1 --> W2[MetadataWave]
    S1 --> W3[LanguageDetectionWave]

    W1 -.signal.-> S2[document.chunked]
    W2 -.signal.-> S3[metadata.extracted]
    W3 -.signal.-> S4[language.detected]

    style S1 stroke:#339af0
    style S2 stroke:#339af0
    style S3 stroke:#339af0
    style S4 stroke:#339af0
    style W1 stroke:#51cf66
    style W2 stroke:#51cf66
    style W3 stroke:#51cf66

Pattern 2: Sekvationell beroende

vågor väntar på tidigare signaler:

graph LR
    W1[ExtractWave] -.signal.-> S1[text.extracted]
    S1 --> W2[ChunkWave]
    W2 -.signal.-> S2[text.chunked]
    S2 --> W3[EmbedWave]
    W3 -.signal.-> S3[embeddings.generated]

    style S1 stroke:#339af0
    style S2 stroke:#339af0
    style S3 stroke:#339af0
    style W1 stroke:#51cf66
    style W2 stroke:#51cf66
    style W3 stroke:#51cf66

Pattern 3: villkorlig böjning

Olika vågor körs baserat på signaler:

graph TD
    W1[DetectorWave] -.signal.-> S1{confidence}

    S1 -->|< 0.4| W2[RejectWave]
    S1 -->|0.4-0.7| W3[EscalateWave]
    S1 -->|> 0.7| W4[AcceptWave]

    W2 -.signal.-> S2[rejected]
    W3 -.signal.-> S3[escalated]
    W4 -.signal.-> S4[accepted]

    style S1 stroke:#ffd43b
    style S2 stroke:#ff6b6b
    style S3 stroke:#ff922b
    style S4 stroke:#51cf66
    style W1 stroke:#339af0
    style W2 stroke:#ff6b6b
    style W3 stroke:#ff922b
    style W4 stroke:#51cf66

Pattern 4: Aggregation

Flera signaler aktiverar en våg:

graph LR
    W1[Wave A] -.signal.-> S1[a.complete]
    W2[Wave B] -.signal.-> S2[b.complete]
    W3[Wave C] -.signal.-> S3[c.complete]

    S1 --> T{All Ready?}
    S2 --> T
    S3 --> T

    T -->|Yes| W4[AggregatorWave]
    W4 -.signal.-> S4[aggregation.complete]

    style S1 stroke:#339af0
    style S2 stroke:#339af0
    style S3 stroke:#339af0
    style S4 stroke:#51cf66
    style T stroke:#ffd43b
    style W4 stroke:#51cf66

Escalationsmönster

StyloFlow stödjer eskalation på två nivåer:

  1. Internal koordinator:vågor kontrollerar signalförtroende och kör villkorligt dyra steg
  2. Inter-koordinator: EscalatorAtomvägssignaler från en snabb koordinator → dyr koordinator

Ett exempel från lucidRAG: Först körs snabb enhetens extraktion. Om kvalitet M SK2 0.7, EscalatorAtom förflyttar dokumentet till en dyr LLM förnyelsekoordinatörMSC4 Detta sparar 20x på kostnaderna genom att undvika dyra LLM-sändningar efter högkvalitetsextraktionerMska7


Minimala form

Det här är inte en snabb guide, det är det minsta exemplet som visar hur modellen passar ihop.

Installation:

dotnet add package StyloFlow.Complete

Conceptual entry point:

// 1. Define a wave
public class MyAnalysisWave : IContentAnalysisWave
{
    public string Name => "MyAnalysis";
    public int Priority => 50;
    public bool Enabled { get; set; } = true;

    public bool ShouldRun(string path, AnalysisContext ctx) => true;

    public async Task<IEnumerable<Signal>> AnalyzeAsync(
        string path,
        AnalysisContext ctx,
        CancellationToken ct)
    {
        // Your analysis logic here
        var result = await AnalyzeAsync(path);

        return new[]
        {
            new Signal
            {
                Key = "my.signal",
                Value = result,
                Confidence = 1.0,
                Source = Name
            }
        };
    }
}

// 2. Register waves
var waves = new List<IContentAnalysisWave>
{
    new MyAnalysisWave(),
    new AnotherWave(),
};

// 3. Create coordinator
var coordinator = new WaveCoordinator(
    waves,
    CoordinatorProfile.Default);

// 4. Execute
var context = new AnalysisContext();
var results = await coordinator.ExecuteAsync(filePath, context);

// 5. Read signals
foreach (var signal in context.GetAllSignals())
{
    Console.WriteLine($"{signal.Key}: {signal.Value} ({signal.Confidence})");
}

Med manifester:

// Load manifests from directory
var loader = new FileSystemManifestLoader("./manifests");
var manifests = await loader.LoadAllAsync();

// Build waves from manifests
var waves = manifests
    .Where(m => m.Enabled)
    .OrderBy(m => m.Priority)
    .Select(m => WaveFactory.Create(m))
    .ToList();

var coordinator = new WaveCoordinator(waves, profile);

För kompletta exempel, se StyloFlow GitHubs arkiv.


Workflow Discoverability: YAML Manifest

Manifeste deklarerar vad som utlöser en våg, ,, vad den utsläpper, ,, resursersgränser,,, och kostnaderna.

exempel: Deterministisk våg

name: ChunkingWave
priority: 80
description: Splits documents into semantic chunks

taxonomy:
  kind: extractor
  determinism: deterministic

triggers:
  requires:
    - signal: file.extension
      condition: in
      value: [".pdf", ".docx", ".md"]

emits:
  on_complete:
    - key: document.chunked
      type: integer

lane:
  name: normal
  max_concurrency: 8

budget:
  max_duration: 30s

defaults:
  chunking:
    max_chunk_size: 512
    overlap: 50

exempel: Bestämd escalation

name: QualityCheckWave
priority: 40
description: Validates extraction quality

triggers:
  requires:
    - signal: embeddings.generated
    - signal: entities.extracted

emits:
  on_complete:
    - key: quality.score
      confidence_range: [0.0, 1.0]

  conditional:
    - key: escalation.needed
      when: quality.score < 0.7  # Trigger LLM refinement

lane:
  name: fast
  max_concurrency: 16

Möjligheter:

  • Ausführungsord synliga från prioriteringar
  • beroenden klarar sig från de nödvändiga signalerna
  • Begränsningar av resurser är uppenbara (planneringar
  • villkorlig logisk deklaration

Nyckliga egenskaper

1. Deklarerande sammansättning

Komponenter deklarerar sina kontrakt. ( triggar, , signaler, M SK2 budget, ),, inte deras beroenden. ♫ . ♫ Systemet räknar ut vad som utförs, ♫ МSK5 ♫ Det här är ingen egenskap, ♪ ' ♫ det är - ♫ ♫ vad som händer när man först skapar signaler,

2. Observerbart standardmässigt

Varje handling är en signal.

3. Adaptiv utführung

Tillförtroende poäng driver branchning utan explicit vägledningslogik. Springa över dyra steg när det är onötigt , upphöja när du inte är säker M SK2 avbryta tidigt på hög nivå - tillförtrode misslyckanden

4. Testbarhet utan att mocka ramverk

Mocksignaler, inte komponenter :

var context = new AnalysisContext();
context.AddSignal(new Signal
{
    Key = "document.chunked",
    Value = 10,
    Confidence = 1.0,
    Source = "Test"
});

var wave = new EmbeddingWave();
var results = await wave.AnalyzeAsync(path, context, ct);

Assert.Single(results);
Assert.Equal("embeddings.generated", results.First().Key);

5. Tillväxt komplexitet

Börja enkelt:

var coordinator = new EphemeralWorkCoordinator<Job>(ProcessAsync);

Lägg till signaler när det behövs:

new EphemeralOptions { Signals = signalSink }

Lägg till vågor för multi

var waveCoordinator = new WaveCoordinator(waves, profile);

Lägg till manifester för deklarativ config:

name: MyWave
triggers: [...]
emits: [...]

jämförelse med andra Workflow-motorer

Funktionen StyloFlow ♫ ♫ ♫ Temporal ♫
Koordinering Signal -driven ♫ ♫ ♫ RPC ♫
Deklarativt ✅ YAML-manifester
Bedingande ✅ Signaltrigger ✅ villkoren МSK5 böjning ✅ valval
Eskalation ✅ byggt- i SSK3 ❌ Handbook SSK6 handbook
Observerbarhet ✅ Signalspårning МSK3 Historien av arbetsflödet
Budjetkontroll ♫ ✅ ♫ Token ♫
Lokalt utförande ✅ Iprocessen - 3 4 Benötigt kluster МSK6 Befordrar kluster
Konkurenssbanor ✅ Fast/NormalM SK3LLM ♫ ♫ ♫

Där den här modellen passar naturligt

  • AI/ML-rörsledningar med uppbyggnad (billigt → dyrt
  • Dokumentbehandling med villkorliga steg
  • Bot-detektor med multi-steganalys
  • RAG-system med hybridsökning + generation
  • Alla arbetsflöden där komponenter reagerar på förtroendevärderingar

Där det inte fungerar

  • Enkela sequentiella jobb (en tidiga koordinator är tillräckligt
  • Fortskridna, fördelade arbetsflöden över datacenter (Temporal löser detta)
  • lång tid-drivande arbetsflöden med mänskligt godkännande
  • Workflowversioner med schema migration (Temporal)

Var den här modellen leder

De är naturliga förlängningar av modellen, inte åtaganden att implementera den specifikt.

När semantiken stabiliseras genom utveckling av lucidRAG och Stylobot, så blir dessa mönster livskraftiga.

1. Utlärning från signaler

Träg vilka eskalationsvägar som fungerar bäst:

// Did the LLM escalation improve accuracy?
// Learn to skip it if behavioral analysis is sufficient

2. kostnadsoptimering

Automatisk körning baserat på tidigare resultat:

// If a "slow" wave completes quickly, promote to "normal"

3. Signaluppspelning

Debug genom att spela upp signalsekvenser igen:

var replay = SignalReplay.FromFile("trace.jsonl");
await coordinator.ReplayAsync(replay);

4. Multi-

Distribuera banor över maskiner samtidigt som signalerna är centraliserade.


Varför signaler är viktiga

Kerninsikten är den här: i AI-system, ,, har varje komponent självförtroende.

Traditionella arbetsflöden förutsätter framgång

  • Förtroende poäng - Hur säker är vi på det?
  • Bedingligt utförande - Gå över dyra steg om du är självsäker
  • Eskalation - Försök hårdare när du inte är säker
  • Aggregation - Kombinera flera signaler

Signaler ger detta naturligt:

// Multiple detectors vote
var signals = context.GetSignals("bot.detected");

// Aggregate by confidence
var verdict = signals
    .OrderByDescending(s => s.Confidence)
    .First();

// Or majority vote
var isBot = signals
    .Count(s => (bool)s.Value) > signals.Count() / 2;

// Or weighted average
var score = signals
    .Sum(s => (bool)s.Value ? s.Confidence : -s.Confidence)
    / signals.Count();

Det är därför StyloFlow fungerar bra för Reduzerad RAG - varje extraktionsstadium producerar en självförtroende poäng , och syntetiseringen sker bara när självför tro är tillräckligt hög


sammanfattning

Ausführungsmodellen:

  • Signaler som första fakta i klass - (, inte händelser eller meddelanden
  • Tilltrovärdighet drivkraftskontrollflöde
  • Rörtor koordinerar genom aktivatorer, inte direkt anslutningar
  • Ständer tvinga resursers gränser
  • byggt på mestlucid.ephemeral

Varför är signaler viktiga:

  1. Stabila fördelningsgränser - Serialiserad , timestampedd, självgåendeM SK3inklusiv
  2. Deklarerande sammansättning - Manifest definierar kontrakt
  3. Adaptiv ruttering - Confidence gör eskalering utan att ha hårdkoderade ramverk
  4. Enkel observabilitet - Varje handling är redan en signal
  5. Klara ägande - Atomernas egna signaler

Arbetsimplementeringar:

  • lucidRAG - Cross -modell dokument Q&A med villkorlig väsenutvinning
  • Stylobot - Bot-detektorn med självförtroende-driven escalation IP | | → | beteendet
  • Reduzerad RAG - Deterministisk extraktion

Related articles:

källkod: GitHub - StyloFlow


Kerninsikten

Traditionella arbetsflödesmotorer ber dig att vad händer sedan?. Den här modellen ber komponenterna att deklarera vad de producerar och vad de behöver, så låter signalerna koordinera utdraget

Nyckelns förändring: signaler avkopplar , förtroende vägledningar, banaer skyddar.

Det här handlar inte om att välja StyloFlow över Temporal eller Airflow, utan att - löser olika problem.

Om du bygger AI, /, ML, rör där?

  • Förtroende är viktigt ( inte bara framgång
  • Eskalation är strukturell ( inte en särskild fall )
  • Komponenter borde inte tala om varandra
  • Upplevbarheten bör vara inhärdlig ( inte bostad på )

"...", då passar den här tolkningssemantiken kanske till vad du tror

Den tidiga bibliotek är den stabila grunden.

För frågor eller feedback GitHubs arkiv.

Finding related posts...
logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.