Я створив StyloFlow, бо продовжував писати те ж саме знову і знову: компоненти, які реагують на те, що сталося до того , виділяють показники самовпевненості МSK2 і іноді потребують ескалізації до більш дорогих аналізів M SK3 існуючі двигуни робочого потоку хотіли, щоб я думав з точки зору DAG або станових машин
NOTA: StyloFlow - це ще не готовий продуктM SK1 бо я створюю lucidRAG і StyloBot I ' я доставляю недоліки і поліпшую API на обох stylоФлоу та efeмерних. ЦеMSC4 все ще в активному розробці МSK5 але ви можете спробувати його і дати відгукиМSK6 Я обновлюю тут пізніше MSSK7 наприклад, штука SignalSink зміниться для efeмерного vMST8 буде МST9 читати тільки МST10
StyloFlow - це сигнал--", керований оркестровою бібліотекою, що відповідає як я думаю про Трубопроводи штучного інтелекту: компоненти заявляють, що вони виробляють і чого їм потрібно
Це інфраструктура, яка дає енергію прозораРАГ - - інструмент для RAG, що об 'єднує DocSummarizer МSK0документи), DataSummarizer МSK0структуровані дані), і ImageSummarizer (обрисиM SK1 перетворюють в об 'єднане питання-систему підсилення з візуалізацією графів знань Стілобот (сучасна система захисту роботів ) і впроваджує Reduced RAG шаблон.

Источник: GitHub - StyloFlow
StyloFlow - це робочий прототип сигналу API та форма розвиватимуться, коли я будую lucidRAG і Stylobot,, але тут описана семантика виконання та шаблони є точним : сигналами як першими МSK2 класовими фактами M SK3 довірою - рухомим відрізаннямМSK5 і ескалацією як структурним шаблоном MSC6
Це не "'", це не новий DSL або мова робочого потоку, а симантика виконання збудований навколо сигналів, довіруM SK1 та обмеженої ескалації . Сьогодні він працює в-процесі з обмеженою співпадковістю МSK4 Завтра він розповсюдить ланцюги між машинами, зберігаючи сигнали як стабільну межу M SK5
Ось як виглядає більшість двигунів робочого потоку.
// ❌ 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);
}
Це працює до :
В результаті ви отримаєте або
StyloFlow будується на більш-менш яскравий.нефемеральний - bibliotekа для обмеженого , відслідковуючих асинхових виконок МSK2
Краткое підсумок того, що дає efeмерний препарат:
// 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}");
Головні переваги efeмерного лікування:
Для деталей, читайте Огонь і не забувай.
Ця модель оркестрування розширює ефемеральну з:
Ось ключова зміна в архітектурі:
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
Комп 'ютери ніколи не телефонують одне одному. Вони випускають сигнали і реагують на нихM SK1
Сигнали - це факти про те, що сталося, не накази чи події . Вони МSK2 є незмінними M SK3 записані в певний час, МSK4 і несуть показники довіри, . Кожен атом має свої сигнали. МSK6 вони М SK7 є зовнішнім незмінним . Ніщо інше не може змінити цей список
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; }
}
Критичний архітектурний момент: Сигнальний синк - це тривалий історічний поглядM SK1
Сигнальний запах надає пошуковий вид на всіх операціях на всіх координаторах, які його розділяють. Сигнали продовжують існувати впродовж життєвого циклу координатора - коли операція виїжджає з його координатора МSK2 сигнали залишаються в раковині до того, як їх зачистять ручно
// 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
Сигналсинк дає два моделі координації:
1. ПоштовхуванняM SK1на основі
// 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. ПрийтиM SK1на основі (Запрос):
// 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}");
Чому це важливо:
Головний принцип проектування: Поміщати велику кількість даних (документиM SK1 зображенняМSK2 вектори) в кэшах або базах данихMSC4 Сигнали несуть тільки такі reference, як "cache://doc-123" або операційні ключі.
Наприклад, координація:
// 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);
}
});
Ескаляція відбувається на двох рівнях
// 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
}
Багато координаторів працюють незалежно. ЕскалаторАтом спостерігає за сигналами від одного координатора і передає роботу до іншого, коли це потрібноM SK1
Теорія, що стоїть за цим МСК0 , представлена в Перетягування обмеженого неясного контексту.
Критичний: Сигнали - це координаційні події, не транспортування данихM SK1 великі дані ( документи M SK3 зображення , вбудовані матеріали MSC5 мають бути розташовані в зовнішній накопичуванні МSK6
// ❌ 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);
}
}
Найкращі практики:
"cache://key", "blob://container/file", "db://table/id"Маніфести оголошують контракти: МСК0, що мене мотивує , ,, що я викидаю , відокремлена від застосування. Ця відокруження існує таким чином, що ви можете зрозуміти робочий процес без читання коду
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
Прибутки:
В той час, як ви можете написати YAML манифести вручну, StyloFlow включає візуальний будівник робочого потоку, який дозволяє проектувати сигналиM SK1пілотовані робочі потоки за допомогою модульного-synthMスク3таッチування стилюMSC4

В інтерфейсі є::
Таким чином, можна легко експериментувати з різними формами робочого потоку, не пишучи YAML вручну.
Вона - це складний аналіз. Цей інтерфейс існує для того, щоб зробити "мусимо запустити ?" першим МSK2 рішенням класу, це не деталі розробки, закопані в логіці умовних речей
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);
}
Ось приклад простих хвиль:
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
}
};
}
}
Координація хвиль:
І WaveCoordinator пробігає хвилі у пріоритетному порядку:
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}");
}
Конкуренційні траси:
Води бігають на дорогах з різними обмеженнями співпадіння:
| Шоссе МSK1 Мета МSK2 Конкуренція | ||
|---|---|---|
fast |
Швидкі перевірки ( Проверка IP МSK2 тип файлуM SK3 МSK4 \16 | |
normal |
Стандартне оброблення | |
io |
IM SK1O пов 'язане (файл читає, API дзвінкиMSC4 МSK5 \32 | |
llm |
дорогі LLM дзвінки |
Це запобігає блокуванню дорогих операцій
Ось цілковита картина
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
Потік:
Модель власності: Кожна операція /атом має свої сигнали МSK1 Сигналсинк дає читаний МSK2 тільки для всіх операцій . Сигнали можуть бути ескалізовані | ( | копіовані \ ) | чи відековані | МSK6 | зачувані, коли їх викидають |), | але список власників не змінюється ззовні
Текучий модель виконання: pojedynczy-процесM SK1 обмежене співпадіння , спостереження за операціями з виведенням LRU.
Майбутня модель виконання: Розповсюдині ланцюги через машини, СигналSink запитує віддалені операціїM SK1 атоми виконуються на різних хостах. Сигнали залишаються стабільними - вони ' вже сеrialізовані МSK2 в timestamped МSK3 і саморобні
Implementation in- підтверджує семантику . Distribution about scaling the execution substrate
Давайте подивимось, як lucidRAG використовує StyloFlow:
Стадія 1: Початкове виявлення
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"
}
};
}
}
Стадія 2: Поєднання ( з 'єднане файлом
// 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
}
};
}
}
Стадія 3: Втілення ( з 'єднане документом
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
}
};
}
}
Стадія 4: Видобуток об 'єктів МSK1паралельний з вмонтуванням МSK2
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
}
};
}
}
Стадія 5: Проверка якості
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;
}
}
Прибутки такого підходу
Це Reduced RAG візерунок в дії: детерміністичне видобуток на передній плані , ЛЛМ лише для синтезу
Стілобот це розвинена система виявлення роботів, яка використовує систему StyloFlow для аналізу загроз у різних стадіях.
Модель 1: вентилятор - Виход
Один сигнал активує багато хвиль:
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
Модель 2: Последовательна залежність
Води чекають на попередні сигнали:
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
Модель 3: Кондиційне відокремлення
Різні хвилі запускаються на основі сигналів:
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
Модель 4: Об 'єднання
Багато сигналів активують одну хвилю:
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
StyloFlow підтримує ескаляцію на двох рівнях:
До прикладу з lucidRAG: Спочатку запускається швидке видобуток об 'єктів. Якщо якість M SK2 0.7, EscalatorAtom спрямовує документ до дорогих координаторів покращення LLMMSC4 Це заощаджує МSK5x kosztów, не звертаючи уваги на дорогі виклики LLM для високої якості extractions
Це не швидкий-початковий інструктор; цеM SK2 найменший приклад, який показує, як модель вписується в одне ціле
Установлення:
dotnet add package StyloFlow.Complete
Концепційна точка входу:
// 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})");
}
З manifestами:
// 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);
Для повних прикладів, см. Репозиторium StyloFlow GitHub.
Однією з ключових характеристик StyloFlow' є відкритість робочого потоку - ви можете зрозуміти цілий трубопровод, просто читаючи manifests . Ніхто не повинен занурюватися в код
Ось структура manifest directory для lucidRAG:
manifests/
├── 01-file-type-detector.yaml
├── 02-chunking.yaml
├── 03-embedding.yaml
├── 04-entity-extraction.yaml
├── 05-quality-check.yaml
└── 06-escalation.yaml
МСК0файлМSK1типМСК2детекторМ СК3yamlMСК4
name: FileTypeDetector
priority: 100
enabled: true
description: Detects file type from extension
taxonomy:
kind: sensor
determinism: deterministic
persistence: ephemeral
triggers:
requires:
- signal: document.uploaded
condition: exists
emits:
on_start:
- file.detection.started
on_complete:
- key: file.extension
type: string
confidence_range: [1.0, 1.0]
- key: file.mime_type
type: string
confidence_range: [1.0, 1.0]
lane:
name: fast
max_concurrency: 16
budget:
max_duration: 10ms
МSK0чункинг.yamlM SK2
name: ChunkingWave
priority: 80
enabled: true
description: Splits documents into semantic chunks
taxonomy:
kind: extractor
determinism: deterministic
persistence: ephemeral
input:
accepts:
- document.pdf
- document.docx
- document.markdown
required_signals:
- file.extension
triggers:
requires:
- signal: file.extension
condition: in
value: [".pdf", ".docx", ".md", ".txt"]
emits:
on_complete:
- key: document.chunked
type: integer
confidence_range: [1.0, 1.0]
- key: chunks.cached
type: boolean
lane:
name: normal
max_concurrency: 8
budget:
max_duration: 30s
defaults:
chunking:
max_chunk_size: 512
overlap: 50
respect_boundaries: true
МSK0введення.yamlM SK2
name: EmbeddingWave
priority: 60
ires:
- signal: file.extension
condition: in
value: [".pdf", ".docx", ".md", ".txt"]
emits:
on_complete:
- key: document.chunked
type: integer
confidence_range: [1.0, 1.0]
- key: chunks.cached
type: boolean
lane:
name: normal
max_concurrency: 8
budget:
max_duration: 30s
defaults:
chunking:
max_chunk_size: 512
overlap: 50
respect_boundaries: true
МSK0введення.yamlM SK2
name: EmbeddingWave
priority: 60
enabled: true
description: Generates ONNX embeddings for chunks
taxonomy:
kind: embedder
determinism: deterministic
persistence: cached
input:
required_signals:
- document.chunked
- chunks.cached
triggers:
requires:
- signal: document.chunked
condition: ">"
value: 0
emits:
on_complete:
- key: embeddings.generated
type: integer
confidence_range: [1.0, 1.0]
lane:
name: normal
max_concurrency: 4
budget:
max_duration: 2m
max_cost: 0.0 # Local ONNX model
defaults:
embedding:
model: all-MiniLM-L6-v2
batch_size: 32
МSK0ентність-виштовхуванняM SK2yaml:
name: EntityExtractionWave
priority: 60 # Same as embedding - runs in parallel
enabled: true
description: Extracts entities using IDF scoring
taxonomy:
kind: extractor
determinism: deterministic
persistence: persisted
input:
required_signals:
- document.chunked
triggers:
requires:
- signal: document.chunked
condition: ">"
value: 0
emits:
on_complete:
- key: entities.extracted
type: integer
confidence_range: [0.0, 1.0] # Confidence varies
lane:
name: normal
max_concurrency: 8
budget:
max_duration: 1m
defaults:
entity:
min_idf_score: 2.5
min_frequency: 2
max_entities: 100
МSK0якісність- перевіркаM SK2yaml:
name: QualityCheckWave
priority: 40
enabled: true
description: Validates extraction quality
taxonomy:
kind: gatekeeper
determinism: deterministic
persistence: ephemeral
input:
required_signals:
- embeddings.generated
- entities.extracted
triggers:
requires:
- signal: embeddings.generated
condition: ">"
value: 0
- signal: entities.extracted
condition: exists
emits:
on_complete:
- key: quality.score
type: double
confidence_range: [0.0, 1.0]
conditional:
- key: escalation.needed
when: quality.score < 0.7
lane:
name: fast
max_concurrency: 16
defaults:
quality:
min_embeddings: 5
min_entity_confidence: 0.5
threshold: 0.7
МSK0ескаляція.yamlM SK2
name: EscalationWave
priority: 20
enabled: true
description: Improves low-quality extractions using LLM
taxonomy:
kind: proposer
determinism: probabilistic
persistence: persisted
input:
required_signals:
- escalation.needed
triggers:
requires:
- signal: escalation.needed
condition: exists
skip_when:
- signal: budget.exhausted
emits:
on_complete:
- key: escalation.complete
type: boolean
- key: entities.improved
type: integer
confidence_range: [0.7, 1.0]
lane:
name: llm
max_concurrency: 2 # Expensive
budget:
max_duration: 30s
max_tokens: 4000
max_cost: 0.05
defaults:
llm:
model: gpt-4o-mini
temperature: 0.1
prompt_template: entity_extraction
Дивлячись на ці файли, ви відразу знаєте:
Немає потреби читати код. Рабочий процес є саморегулюваним
Попри те, що вище згадані приклади показують повністю декларативні хвилі YAML, код-на основі атомів звертається на manifests:
name: CustomAnalyzer
priority: 50
enabled: true
description: Custom analysis logic
# Reference a code-based atom implementation
implementation:
assembly: MyProject.Analyzers
type: MyProject.Analyzers.CustomAnalyzerWave
method: AnalyzeAsync
# The manifest still declares the contract
taxonomy:
kind: analyzer
determinism: probabilistic
triggers:
requires:
- signal: data.ready
emits:
on_complete:
- key: analysis.complete
confidence_range: [0.0, 1.0]
lane:
name: normal
max_concurrency: 4
# Configuration values passed to the atom
defaults:
threshold: 0.75
max_iterations: 10
Implementation C#
public class CustomAnalyzerWave : ConfiguredComponentBase, IContentAnalysisWave
{
public async Task<IEnumerable<Signal>> AnalyzeAsync(
string path,
AnalysisContext ctx,
CancellationToken ct)
{
// Access manifest config
var threshold = GetParam<double>("threshold", 0.75);
var maxIterations = GetParam<int>("max_iterations", 10);
// Custom logic here
var result = await PerformComplexAnalysis(path, threshold, maxIterations);
return new[]
{
new Signal
{
Key = "analysis.complete",
Value = result.Score,
Confidence = result.Confidence,
Source = Name
}
};
}
}
Прибутки:
Цей гібридний підхід дає вам декларативне відкриття робочого потоку, зберігаючи складну логіку в підтримці C#.
Ця структура робить створення візуалізації банальним:
graph TD
DOC[document.uploaded] --> FT[FileTypeDetector<br/>Priority: 100<br/>Lane: fast]
FT --> EXT[file.extension]
EXT --> CH[ChunkingWave<br/>Priority: 80<br/>Lane: normal]
CH --> CHUNKED[document.chunked]
CHUNKED --> EMB[EmbeddingWave<br/>Priority: 60<br/>Lane: normal]
CHUNKED --> ENT[EntityExtractionWave<br/>Priority: 60<br/>Lane: normal]
EMB --> EMBGEN[embeddings.generated]
ENT --> ENTEX[entities.extracted]
EMBGEN --> QC[QualityCheckWave<br/>Priority: 40<br/>Lane: fast]
ENTEX --> QC
QC --> QSCORE[quality.score]
QC -.conditional.-> ESC_NEED[escalation.needed]
ESC_NEED -.-> ESC[EscalationWave<br/>Priority: 20<br/>Lane: llm]
ESC --> ESC_DONE[escalation.complete]
style DOC stroke:#339af0
style FT stroke:#51cf66
style CH stroke:#51cf66
style EMB stroke:#51cf66
style ENT stroke:#51cf66
style QC stroke:#ffd43b
style ESC stroke:#ff922b
Ця діаграма була створена програмістично з YAML manifests - без ручних малюнківM SK1
Одна неочікувана властивість StyloFlow - це те, що він створює систему ЛЛМ можуть обдумати безпеку.
Більшість спроб використовувати LLM для виправлення або оркестрування провалюються, тому що система, в яку вони потрапили - непрозрачна.
StyloFlow робить протилежне. Він виявляє невідмінні факти про те, що сталося
Це робить кодові LLM дійсно корисними, не як актори, а як аналітики.
У StyloFlow , LLM ніколи :
Замість ",", він отримує Вид SignalSink і ставили такі питання, як:
Наприклад, введення коду LLM:
{
"operation": "doc-123",
"signals": [
{ "key": "document.chunked", "value": 12, "confidence": 1.0, "source": "ChunkingWave" },
{ "key": "entities.extracted", "value": 4, "confidence": 0.42, "source": "EntityWave" },
{ "key": "quality.score", "value": 0.39, "source": "QualityCheckWave" },
{ "key": "escalation.needed", "source": "QualityCheckWave" }
]
}
Це не журналовий поток підґрунтування.
Код LLM може тепер:
Все без довіри зробити будь-що.
Традиційне "LLM виправлення МSK1 намагається відтворити світ
" Тут ' це код і деякі журнали МSK2 що пішло не так
Розладнання StyloFlow простіше:
" Ось точний стан системи, спостеріганої
Через те, що сигнали не змінюються і належать до них. МСК0, ви не маєте. МSK1, вам не потрібно перезапускати. -, ви нічого не виконуєте.
Причина LLM щодо фактів, які ви вже довіряєте.
Це працює лише через жорсткі обмеження:
Немає циклу зворотнього зв 'язку, коли LLM " визначає МSK1 наступний крок МSK2 Найчастіше , запропонує пояснения або конфигуративні підказки, що людська M( чи детерміністична політика МСК5 може бути застосована пізніше
Ця асимметрія навмисно створена.
Як тільки сигнали, впевненістьM SK1 і результати є експлуатованими , ви їжа пізніше:
Жодна з них не вимагає, щоб LLM запускав систему.
LLM стає діагностична лінза, не контрольна поверхня
StyloFlow не тільки гарантує безпеку систем, що базуються на упевненості
Це робить їх зрозуміло — для людей , для тестів МSK2 і для ЛЛМ МSK3 без відмови від контролю
Це - різниця між
Тільки одна з тих шкал.
1. Декларативний состав
Комп 'ютери розповідають про свої контракти ( триггери , сигнали МSK2 бюджет M SK3 не їхні відлежності . Система визначає порядок виконання MSC5 Це не МSK6 це не функція МСК7 це MСК8 це те, що відбувається, коли ви створюєте сигнали першимиМСК9 клас М СК10
2. По замовчуванню
Кожна дія - це сигнал.
3. Адаптивна виконання
Результати впевненості спричиняють розгалуження без чіткої логіки маршрутізації. Перемикаємо дорогоцінні етапи, коли це необов 'язковоM SK1 ескалізуємо, коли не впевнимосяМSK2 перекидаємо на ранній час на високий рівеньMSC3 невдачі в упевненні. Контрольний поток виникає з візерунок сигналівMスク5
4. Випробовуваність без вмонтування каркасів
Помітні сигнали, не складовіM SK1
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. Зростає складність
Почнемо з простого:
var coordinator = new EphemeralWorkCoordinator<Job>(ProcessAsync);
Додайте сигнали, коли це потрібно:
new EphemeralOptions { Signals = signalSink }
Додайте хвилі для мульти-станційM SK1
var waveCoordinator = new WaveCoordinator(waves, profile);
Додайте manifests для декларативної konfiguracji:
name: MyWave
triggers: [...]
emits: [...]
Ось цілковита lucidRAG (Подивіться на деталізовані приклади коду в секції "Use CaseM SK2 lucidRAG Document Processing
document.uploaded сигналfile.extension сигналtext.extracted сигналdocument.chunked сигналembeddings.generated сигналentities.extracted сигналquality.score сигналescalation.complete сигналstorage.complete сигналВ інтерфейсі підключається до SignalSink для реального-поновлення прогресу часу M SK1похибка шаблону):
// Subscribe to sink for push notifications
signalSink.Subscribe(signal => {
if (signal.Key.StartsWith("document."))
{
await _hub.Clients.User(userId)
.SendAsync("DocumentProgress", new
{
stage = signal.Key,
progress = CalculateProgress(signal),
operationId = signal.OperationId
});
}
});
Або використовуйте візерунок, якщо хочете проголосувати
// Query SignalSink for document progress (pull pattern)
var documentSignals = signalSink.GetSignals()
.Where(s => s.Key.StartsWith("document.") &&
s.Timestamp > lastCheck);
foreach (var signal in documentSignals)
{
UpdateProgressUI(signal);
}
Ключовий момент: Виsubscribe to the SINK (which views all atoms ), not to individual operationsM SK2 Atoms own signals МSK3 the sink provides push MSC4Subscribe) and pull МСК6query М СК7 access to them M СК8
Ось як lucidRAG обробляє документи, даніM SK1 і зображення через об 'єднаний сигнал-штовхну трубу МSK3 з' єднання DocSummarizer, DataSummarizer, і ImageSummarizer під одним оркестровим шаром.
Ось цілковита Стілобот (Подивіться на детальний код в МSK1ЕскаляціяM SK2 Від швидкого до складного"):
bot.detected сигнал з впевненістюbot.detected сигналbot.detected сигналГоловною перевагою є зростання рівня VPK0, що базується на довірі, VPK1
// ❌ Traditional: Every request gets expensive analysis
var reputation = await CheckIpAsync(ip);
var behavior = await AnalyzeBehaviorAsync(session); // Even if IP is known bad
var llmScore = await LlmAnalysisAsync(conversation); // Always expensive
// ✅ StyloFlow: Waves run based on confidence signals
// BehaviorAnalysis only runs if confidence is ambiguous (0.4-0.7)
// LLM analysis only runs if still unsure after behavior check
Розділ витрат: Проверка IP: $0 і запускає МSK1 часу МSK2 Анализ поведінки запускається МСК3 MСК4 неоднозначні випадки М СК5 аналіз ЛЛМ запускається $0.0001 проти наївного "LLM всеM SK1 в $0.002 МSK0x заощадження).
| Funkція МSK1 StyloFlow МSK2 Temporal | Airflow မ်SK4 Поступні функції мSK5 | ||||
|---|---|---|---|---|---|
| Координація | Сигнал МSK1пілотований МSK2 РПЦ -на основі СМСК4 DAG СМСК5на базі МСК6 Стаціонарний станок MSК7 | ||||
| Засвідчення МSK0 ✅ YAML-моніфести МSK2 ❌ Код мSK4перший МСК5 МСК6 DAGs M | За умови МSK0 ✅ Сигнал триггерів МSK2 ✅ Ситуація МСК4 МСК5 Розподіл СМСК6 СМСК7 Статі вибору MSК8 | ||||
| Ескаляція МSK0 ✅ ЗбудованийM SK2всередині МSK3 ♫ ♫ МSK4 ♫ Руководство ♫ | ♫ | ||||
| Обсервабельність | МSK1 Сигналовий слід МSK2 \ ✅ Історія робочого потоку | ✅ Записи задач ♫ | ♫ | МSK7 ♫ Історії виконання ♫ МSK8 ♫ | |
| Kontrolа бюджету МSK0 ✅ Знак МSK2Ceilingи витрат | |||||
| Локальне виконання МSK0 ✅ ВМСК2процесі МСК3 \MСК4 Потрібний кластер мСК5 | МСк6 | Потрібна кластер | |||
| Конкуренційні траси МSK0 ✅ Гнучкий МSK2Normalny /LLM MSК4 \❌ Ручний МСК6 | ✅ Бассейни СМСК8 ♫ | СМСК9 Ограничення служби СМСк10 |
Де ця модель підходить природно:
Там, де він не виграв
Це природні розширення моделі, а не зобов 'язання нанести певний результат.
Коли семантика стабілізується через розвиток lucidRAG та Stylobot, ці шаблони стають жизнетворними.
1. Навчання з сигналів
Прослідкувати, які шляхи эскалації найкраще працюють:
// Did the LLM escalation improve accuracy?
// Learn to skip it if behavioral analysis is sufficient
2. Оптимізація витрат
Автоматичний розподіл ланцюга на основі історичного результату:
// If a "slow" wave completes quickly, promote to "normal"
3. Сигнальний повтор
Розлад, відтворюючи послідовності сигналів:
var replay = SignalReplay.FromFile("trace.jsonl");
await coordinator.ReplayAsync(replay);
4. Мульти-
Поширювати лінію між машинами, зберігаючи центральні сигнали.
Найголовнішим моментом є це. в системах штучного інтелекту, МСК0, кожен компонент має впевненість.
Традиційні робочі потоки припускають успіх
Сигнали роблять це природно:
// 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();
Ось чому StyloFlow працює добре для Reduced RAG - кожен етап видобутку виробляє показник самодовіри ,, а синтез відбувається лише тоді, коли самодоверість достатньо висока M SK2
Модель виконання:
Чому сигнали важливі:
Працові втілення:
Подібні статті:
вихідний код: GitHub - StyloFlow
Традиційні машини робочого потоку просять вас оголосити що ж відбувається далі. Ця модель просить компоненти заявити що вони виробляють і що вони потребують, тоді дозволяє сигналам координувати виконання
Переміщення ключів: сигнали відокремлюються, інструкції довіриM SK1 захист.
Це не "'", це StyloFlow , а Temporal чи Airflow . - , які розв 'язують різні проблеми. МSK2 , Durabilitatea execution , , , версування робочого потоку, , , розподілена координація між центрами обробки даних, МSK5 . Це articulating a different orchestration model , МSK6 , де контрольний потік виникає з візерунок сигналів, замість того, щоб бути явно програмованим.
Якщо ви будуєте трубопроводи AI/ML де
... тоді ця семантика виконання могла б підійти до того, як ви думаєте
І тимчасова бібліотека є стабільним фундаментом. StyloFlow додає сигналу - підштовхований оркестровий шар зверху МSK2 Обидві з них розвиваються через реальне використання в lucidRAG та Stylobot .
Для питань або зворотнього зв 'язку, читайте Репозиторія GitHub.
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.