La maggior parte delle architetture software presume che il sistema sia statico. DiSE assume che il sistema sia vivo.
Nota: Questa è la parte 4 della serie "Cooking with DiSE." Vedi Parte 1, Parte 2: Apprendisti laureati, e Parte 3: Dei inaffidabili per il background. Questo articolo è la panoramica architettonica - e ora abbiamo una implementazione C# di lavoro: per lo piùlucid.botdetection.
Concetto chiave: Routing comportamentale. Questa architettura consente una nuova categoria - dove "squadre" trasparenti e regolabili di rilevatori e sistemi di apprendimento reflexively routing traffico basato su modelli di comportamento appresi, non regole statiche. Porta YARP, bot non raggiungere mai il tuo backend. O utilizzare il middleware per costruire routing comportamentale direttamente nel vostro livello app.
Per anni, abbiamo costruito software come orologio: ingressi, uscite, regole, condotte, test, implementazioni. Tutti lineari, tutti prevedibili.
Ma i sistemi moderni - soprattutto quelli con l'AI - non si comportano più come orologi. Si comportano come ecosistemi:
Le architetture statiche non riescono a tenere il passo. Si patch un buco, altri tre appaiono. Si sintonizza una soglia, si rompe qualcos'altro. Si reagisce sempre, non si adatta mai.
Architettura DiSE (Directed Synthetic Evolution) tratta il software nel modo in cui i biologi trattano gli organismi: come qualcosa che deve adattarsi, autocorreggersi e migliorare sotto pressione.
Architettura tradizionale:
flowchart LR
B[Build] --> S[Ship] --> P[Patch] --> B
style B stroke:#6366f1,stroke-width:2px
style P stroke:#ef4444,stroke-width:2px
Architettura DiSE:
flowchart LR
P[Perceive] --> E[Evaluate] --> M[Mutate] --> S[Select] --> P
style P stroke:#10b981,stroke-width:2px
style M stroke:#f59e0b,stroke-width:2px
style S stroke:#6366f1,stroke-width:2px
Un sistema DiSE ha quattro comportamenti essenziali:
| Comportamento | Che cosa fa | Equivalente biologico |
|---|---|---|
| Percepibile | Raccogliere segnali provenienti dall'ambiente | Organi sensoriali |
| Valuta | Segnali di punteggio contro obiettivi | Sistema nervoso |
| Mutato | Generare le varianti delle strategie | Mutazione genetica |
| Seleziona | Mantenere ciò che funziona, scartare ciò che non funziona | Selezione naturale |
Questa non è metafora, è letteralmente il modo in cui il sistema evolve comportamenti più intelligenti nel tempo.
per lo piùlucid.botdetection è la prima implementazione C# dei principi DiSE. Tracciamo l'architettura:
Il sistema utilizza un orchestratore lavagna - i rivelatori forniscono prove a uno stato condiviso, innescando altri rivelatori man mano che i segnali si accumulano:
// Detectors emit contributions (evidence), not verdicts
public sealed record DetectionContribution
{
public required string DetectorName { get; init; }
public required string Category { get; init; }
// Positive = bot signal, Negative = human signal
public required double ConfidenceDelta { get; init; }
public double Weight { get; init; } = 1.0;
public required string Reason { get; init; }
public BotType? BotType { get; init; }
// Signals for triggering other detectors
public ImmutableDictionary<string, object> Signals { get; init; }
}
Esegue più rilevatori in onde parallele. Ognuno fornisce una prova. Il sistema percepisce l'ambiente attraverso molte lenti:
// Wave 0: All detectors with no trigger conditions run in parallel
// Wave N: Detectors whose triggers are now satisfied run in parallel
while (waveNumber < MaxWaves && !cancellationToken.IsCancellationRequested)
{
var readyDetectors = availableDetectors
.Where(d => !ranDetectors.Contains(d.Name))
.Where(d => CanRun(d, state.Signals))
.ToList();
await ExecuteWaveAsync(readyDetectors, state, aggregator, ...);
// Check for early exit on high confidence
if (aggregator.ShouldEarlyExit)
break;
}
Evidence aggregati in una decisione utilizzando la trasformazione sigmoid - questo sfrutta correttamente segnali forti da rilevatori ad alto peso:
private (double botProbability, double confidence) CalculateWeightedScore()
{
var weighted = _contributions
.Where(c => c.Weight > 0)
.Select(c => (delta: c.ConfidenceDelta, weight: c.Weight))
.ToList();
var weightedSum = weighted.Sum(w => w.delta * w.weight);
// Sigmoid maps any real number to (0, 1)
// Strong human signal (-3) → ~5% bot probability
// Neutral (0) → 50% bot probability
// Strong bot signal (+3) → ~95% bot probability
var botProbability = 1.0 / (1.0 + Math.Exp(-weightedSum));
return (botProbability, confidence);
}
La valutazione non e' un solo modello che prende una decisione. consenso ponderato tra più rivelatori specializzati - e di fondamentale importanza, quando l'IA non ha funzionato, la probabilità è Bloccato per evitare un eccesso di fiducia:
// CRITICAL: Clamp probability when AI hasn't run
var botProbability = aiRan
? rawBotProbability
: Math.Clamp(rawBotProbability, 0.20, 0.80);
Il sistema non sempre esegue tutti i rilevatori. Quando le prime prove sono conclusive, esce velocemente:
// Early exit on verified bots (good or bad)
public static DetectionContribution VerifiedGoodBot(
string detector, string botName, string reason) => new()
{
DetectorName = detector,
Category = "Verification",
ConfidenceDelta = 0,
TriggerEarlyExit = true,
EarlyExitVerdict = EarlyExitVerdict.VerifiedGoodBot
};
In produzione, richieste di alta sicurezza escono in meno di 10m dopo appena 2-3 rilevatori d'accordo. La condotta completa funziona solo per casi incerti.
I rilevatori possono fallire. Il sistema protegge se stesso:
// Circuit breaker per detector
private void RecordFailure(string detectorName)
{
var state = _circuitStates.GetOrAdd(detectorName, _ => new CircuitState());
state.FailureCount++;
state.LastFailure = DateTimeOffset.UtcNow;
if (state.FailureCount >= CircuitBreakerThreshold)
{
state.State = CircuitBreakerState.Open;
// Detector disabled until reset time passes
}
}
I rilevatori falliti sono temporaneamente disabilitati. Dopo un raffreddamento, vengono riprovati (mezzo stato aperto). pressione di selezione a livello dell'infrastruttura.
Quando il sistema rileva con alta sicurezza, impara pubblicando al bus dell'evento di apprendimento:
private void PublishLearningEvent(AggregatedEvidence result, ...)
{
var eventType = result.BotProbability >= 0.8
? LearningEventType.HighConfidenceDetection
: LearningEventType.FullDetection;
_learningBus.TryPublish(new LearningEvent
{
Type = eventType,
Confidence = result.Confidence,
Label = result.BotProbability >= 0.5,
Metadata = new Dictionary<string, object>
{
["botProbability"] = result.BotProbability,
["categoryBreakdown"] = result.CategoryBreakdown,
["contributingDetectors"] = result.ContributingDetectors
}
});
}
Questo si alimenta nel negozio di peso, aggiornando i pesi del rilevatore euristico imparato nel tempo.
Invece di un singolo "cervello IA," DiSE utilizza la cognizione stratificata:
flowchart TB
subgraph Fast["Fast Path (< 100ms)"]
H[Static Heuristics]
D[Detectors]
ML[Learned Heuristic Model]
end
subgraph Slow["Slow Path (Async)"]
LLM[Ollama LLM]
end
subgraph Learn["Learning Layer"]
W[Weight Store]
Rep[Reputation]
end
Fast --> |escalate uncertain| Slow
Slow --> |label| Learn
Learn --> |update weights| Fast
style Fast stroke:#10b981,stroke-width:2px
style Slow stroke:#6366f1,stroke-width:2px
style Learn stroke:#f59e0b,stroke-width:2px
| Layer | Speed | Purpose | Bot Detection Example |
|---|---|---|---|
| Euristica statica | <1ms | Risposte istintive | Modelli UA noti bot |
| Rilevatori | <10ms | Osservazione del tratto | Analisi dell'intestazione, controlli IP |
| Imparato Euristico | 1-5ms | Classificazione dinamica | Regressione logistica con pesi appresi |
| LLM | 50-500 m | ragionamento profondo | analisi di nuovi modelli |
| Apprendimento | Sfondo | Formazione della memoria | Aggiornamenti del peso, cambiamenti di reputazione |
L'intuizione chiave: il sistema impara i propri pesi in tempo reale. Non sono richiesti modelli ML esterni. Il rilevatore euristico inizia con predefiniti sensibili e si evolve in base al feedback di rilevamento.
Futuro: I modelli ONNX con RAG e l'integrazione sono previsti per v2. L'uscita attuale del modello euristico serve come dati di formazione etichettati per il futuro training del modello. L'architettura ha già gli artefatti "genetici" (contributo, pesi, segnali) che rendono semplice l'aggiunta di evoluzione sintetica diretta - è progettato come punto plugin.
Questo riflette la cognizione biologica:
Qui è la punta intelligente. Invece di spedire un modello esterno di ML, il sistema impara il suo proprio classificatore usando la regressione logistica semplice con l'estrazione dinamica delle caratteristiche:
public class HeuristicDetector : IDetector
{
// Default weights - sensible starting points
private static readonly Dictionary<string, float> DefaultWeights = new()
{
// Human-like patterns (negative = more likely human)
["hdr:accept-language"] = -0.6f,
["hdr:referer"] = -0.4f,
["fp:received"] = -0.7f, // Fingerprint = strong human signal
["fp:legitimate"] = -0.8f,
// Bot indicators (positive = more likely bot)
["ua:contains_bot"] = 0.9f,
["ua:headless"] = 0.8f,
["ua:selenium"] = 0.7f,
["ua:curl"] = 0.6f,
["accept:wildcard"] = 0.4f,
};
private (bool IsBot, double Probability) RunInference(Dictionary<string, float> features)
{
// Simple linear model: score = bias + Σ(feature * weight)
float score = _bias;
foreach (var (featureName, featureValue) in features)
{
var weight = _weights.TryGetValue(featureName, out var w)
? w
: DefaultNewFeatureWeight;
score += featureValue * weight;
}
// Sigmoid gives us probability
var probability = 1.0 / (1.0 + Math.Exp(-score));
return (probability > 0.5, probability);
}
}
Le caratteristiche sono estratte dinamicamente dalla richiesta e dagli elementi di prova aggregati:
public static class HeuristicFeatureExtractor
{
public static Dictionary<string, float> ExtractFeatures(
HttpContext context,
AggregatedEvidence evidence)
{
var features = new Dictionary<string, float>();
// Request metadata
features["req:header_count"] = Math.Min(headers.Count / 20f, 1f);
features["req:cookie_count"] = Math.Min(cookies.Count / 10f, 1f);
// Header presence
features["hdr:accept-language"] = headers.ContainsKey("Accept-Language") ? 1f : 0f;
// UA patterns (dynamic - only present if detected)
if (ua.Contains("bot")) features["ua:contains_bot"] = 1f;
if (ua.Contains("selenium")) features["ua:selenium"] = 1f;
// Detector results (named by actual detector)
foreach (var contrib in evidence.Contributions)
features[$"det:{contrib.DetectorName}"] = contrib.ConfidenceDelta;
// Client-side fingerprint - STRONG human signal
if (hasFingerprint)
{
features["fp:received"] = 1f;
features["fp:legitimate"] = 1f;
}
return features;
}
}
Nuove caratteristiche automaticamente ottenere pesi di default e imparare nel tempo. Il sistema scopre ciò che conta.
I pesi persistono in SQLite e l'aggiornamento tramite media mobile esponenziale:
public class SqliteWeightStore : IWeightStore
{
public async Task RecordObservationAsync(
string signatureType,
string signature,
bool wasBot,
double detectionConfidence,
CancellationToken ct = default)
{
// EMA update: weight = weight * (1 - α) + new_value * α
var alpha = 0.1; // Learning rate
var weightDelta = wasBot ? detectionConfidence : -detectionConfidence;
var sql = @"
INSERT INTO learned_weights (signature_type, signature, weight, ...)
VALUES (@type, @sig, @delta, ...)
ON CONFLICT(signature_type, signature) DO UPDATE SET
weight = weight * (1 - @alpha) + @delta * @alpha,
confidence = MIN(1.0, confidence + @conf * 0.01),
observation_count = observation_count + 1,
last_seen = @now
";
await ExecuteAsync(sql, ...);
}
}
Questo e' mutazione attraverso l'osservazione. Ogni rilevamento insegna qualcosa al sistema. Nel tempo, i pesi convergono verso valori ottimali per i vostri schemi di traffico specifici.
In DiSE, l'allucinazione LLM non è un insetto, è il substrato generativo dell'evoluzione.
Un LLM ad alta temperatura che propone dieci varianti di una regola di rilevamento non è "allucinante" - è mutare il genoma del sistema:
public async Task<List<DetectionRule>> GenerateMutationsAsync(DetectionContext context)
{
var prompt = $"""
Given this traffic pattern:
{JsonSerializer.Serialize(context)}
Generate 5 variant detection rules that might catch similar patterns.
Return JSON array of rules with: pattern, weight, confidence.
Be creative. Some variants should be strict, some lenient.
""";
var response = await _ollama.GenerateAsync(new GenerateRequest
{
Model = "gemma3:1b",
Prompt = prompt,
Options = new RequestOptions { Temperature = 0.9 } // High creativity
});
return ParseRules(response.Response);
}
Le mutazioni vengono poi valutate in base ai dati storici:
public async Task<DetectionRule?> SelectFittestAsync(
List<DetectionRule> mutations,
List<LabeledRequest> testData)
{
var results = new List<(DetectionRule Rule, double Fitness)>();
foreach (var rule in mutations)
{
var tp = testData.Count(r => rule.Matches(r) && r.IsBot);
var fp = testData.Count(r => rule.Matches(r) && !r.IsBot);
var fn = testData.Count(r => !rule.Matches(r) && r.IsBot);
// F1 score as fitness
var precision = tp / (double)(tp + fp);
var recall = tp / (double)(tp + fn);
var f1 = 2 * (precision * recall) / (precision + recall);
results.Add((rule, f1));
}
return results.OrderByDescending(r => r.Fitness).First().Rule;
}
Solo i più adatti sopravvivono alla produzione. operatori evolutivi.
Al centro di DiSE si trova la sistema politico - configurazioni con nome che definiscono come si comporta il rilevamento:
{
"Policies": {
"fastpath": {
"Description": "Fast path + Heuristic for sync decisions",
"FastPath": ["UserAgent", "Header", "Ip", "Behavioral", "ClientSide", "Inconsistency", "VersionAge"],
"AiPath": ["Heuristic"],
"EscalateToAi": true,
"EarlyExitThreshold": 0.15,
"ImmediateBlockThreshold": 0.90,
"Weights": {
"ClientSide": 0.2,
"Heuristic": 1.5
},
"Transitions": [
{ "WhenRiskExceeds": 0.5, "WhenRiskBelow": 0.85, "GoTo": "demo" }
]
},
"demo": {
"Description": "Full pipeline sync for demonstration",
"FastPath": [],
"AiPath": [],
"BypassTriggerConditions": true,
"ForceSlowPath": true
}
}
}
Le politiche definiscono:
Il sistema può transizione tra le politiche a metà richiesta. Incerti risultati di fastpath escalation verso l'intera pipeline demo. Questo è routing adattivo - il genoma risponde alle prove in tempo reale.
Non spedisci config statici. specie comportamentale che si adattano al traffico.
Ogni sistema DiSE ottimizza un paesaggio fitness:
public class FitnessEvaluator
{
public double Evaluate(GenomeConfig genome, EvaluationData data)
{
var fpRate = data.FalsePositives / (double)data.TotalHumans;
var fnRate = data.FalseNegatives / (double)data.TotalBots;
var latency = data.P99LatencyMs;
var cost = data.AiCallsPerRequest * _config.CostPerAiCall;
// Multi-objective fitness
return 1.0
- (fpRate * _config.FalsePositivePenalty) // Don't block humans
- (fnRate * _config.FalseNegativePenalty) // Don't miss bots
- (latency / _config.MaxLatencyMs) // Stay fast
- (cost / _config.MaxCostPerRequest); // Stay cheap
}
}
Non stiamo ottimizzando solo per l'accuratezza, stiamo ottimizzando per sopravvivenza in un ambiente dinamico.
Troppa IA → ad alto costo, scarsa latenza. Troppo poco AI → scarsa individuazione di nuovi attacchi. Troppo aggressivi → falsi positivi, utenti arrabbiati. Troppo indulgente → alluvioni bot.
Il sistema deve trovare un equilibrio stabile.
Un sistema DiSE maturo sposta l'intelligenza artificiale fuori dal percorso caldo:
flowchart TB
subgraph Early["Early Stage"]
AI1[AI handles most cases]
end
subgraph Middle["Middle Stage"]
AI2[AI handles edge cases]
H1[Heuristics handle common cases]
end
subgraph Mature["Mature Stage"]
AI3[AI teaches and mutates]
H2[Heuristics handle almost everything]
M[Memory provides context]
end
Early --> Middle --> Mature
style Early stroke:#ef4444,stroke-width:2px
style Middle stroke:#f59e0b,stroke-width:2px
style Mature stroke:#10b981,stroke-width:2px
Col passare del tempo:
L'IA diventa:
Non una dipendenza da runtime, impalcature adattive.
Con la Porta YARP, questa architettura permette qualcosa di nuovo: routing comportamentale.
Il routing tradizionale è statico: percorso → backend. Il routing comportamentale è riflessivo: caratteristiche del traffico → decisioni dinamiche di routing.
flowchart LR
subgraph Gateway["YARP Gateway"]
D[Detector Team]
P[Policy Engine]
R[Router]
end
Traffic[Traffic] --> D
D --> P
P --> R
R -->|Human| App[Your App]
R -->|Bot| Block[403]
R -->|Uncertain| Challenge[Challenge]
R -->|Learning| Queue[Async Analysis]
style Gateway stroke:#10b981,stroke-width:2px
I concetti chiave:
Non si tratta solo di "riconoscere i robot sul bordo." E' un nuovo routing primitivo dove i flussi di traffico sono modellati da modelli di comportamento appresi, non solo regole statiche.
La maggior parte delle culture ingegneristiche teme:
DiSE usa quelli come materiali da costruzione.
I sistemi statici muoiono, i sistemi in evoluzione sopravvivono.
Ogni industria che abbia a che fare con avversari, deriva o pressione su scala alla fine avrà bisogno di architetture come questa:
Voglio essere chiaro su ciò che non è:
DiSE è controllato, evoluzione spiegabile:
E' diretto evoluzione - guidata, non casuale.
per lo piùlucid.botdetection attua tali principi:
dotnet add package Mostlylucid.BotDetection
builder.Services.AddBotDetection();
app.UseBotDetection();
Configurare per la produzione con l'apprendimento abilitato:
{
"BotDetection": {
"BotThreshold": 0.7,
"Policies": {
"default": {
"FastPath": ["UserAgent", "Header", "Ip", "Behavioral", "ClientSide", "Inconsistency", "VersionAge"],
"AiPath": ["Heuristic", "Llm"],
"EscalateToAi": true,
"EarlyExitThreshold": 0.85
}
},
"AiDetection": {
"Provider": "Heuristic",
"Heuristic": {
"Enabled": true,
"LoadLearnedWeights": true,
"EnableWeightLearning": true,
"LearningRate": 0.01
}
}
}
}
Guardarlo evolversi. Il rivelatore euristico impara da ogni richiesta, aggiornando i suoi pesi per abbinare i vostri modelli di traffico.
DiSE Architecture è il passaggio da software come macchine a software come organismi.
È l'architettura di:
Le architetture statiche non possono tenere il passo con gli ambienti dinamici. Gli attacchi si evolvono. Gli utenti evolvono. I requisiti evolvono.
Anche i sistemi devono evolversi.
Se vuoi sistemi che funzionino, costruiscili alla vecchia maniera. Se volete sistemi che sopravvive - costruirli con DiSE.
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.