De flesta programvaruarkitekturer antar att systemet är statiskt. DiSE antar att systemet är vid liv.
Anmärkning: Detta är del 4 i serien "Cooking with DiSE". Se Häfte 1, Del 2: Utexaminerade lärlingsutbildningar, och Del 3: Otillförlitliga Gudar För bakgrund. Denna artikel är den arkitektoniska översikten - och nu har vi en fungerande C# genomförande: mestadels lucid.botdetektion.
Nyckelkoncept: Beteenderouting. Denna arkitektur möjliggör en ny kategori - där transparenta, justerbara "team" av detektorer och inlärningssystem reflexivt rutttrafik baserat på inlärda beteendemönster, inte statiska regler. YARP- inkörsport, bots når aldrig din backend. Eller använda middleware för att bygga beteende routing direkt i din app lager.
I åratal har vi byggt mjukvara som klockverk: ingångar, utgångar, regler, pipelines, tester, utplaceringar.
Men moderna system - speciellt AI-augmenterade - beter sig inte längre som klockor. De beter sig som ekosystem:
Statiska arkitekturer kan inte hålla jämna steg. Du lappar ett hål, tre till dyker upp. Du justerar en tröskel, något annat går sönder. Du reagerar alltid, anpassar dig aldrig.
DiSE-arkitektur (Directed Synthetic Evolution) behandlar programvara på samma sätt som biologer behandlar organismer: som något som måste anpassas, självkorrigeras och förbättras under tryck.
Traditionell arkitektur:
flowchart LR
B[Build] --> S[Ship] --> P[Patch] --> B
style B stroke:#6366f1,stroke-width:2px
style P stroke:#ef4444,stroke-width:2px
DiSE-arkitektur:
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
Ett diSE-system har fyra grundläggande beteenden:
"Behavande" Vad det gör "biologiska motsvarigheter" |-----------|--------------|----------------------| | Uppfattning Samla in signaler från miljön på Sensoriska organ | Utvärdera på Score signaler mot mål på Nervsystemet | Mutat på Generera varianter av strategier på Genetisk mutation | Välj Bevara det som fungerar, kasta det som inte är naturligt val
Det är bokstavligen så ditt system utvecklar smartare beteenden med tiden.
mestadels lucid.botdetektion är den första C# genomförandet av DiSE principer. Låt oss spåra arkitekturen:
Systemet använder en orkestrator för svarta tavlan - Detektorer bidrar med bevis till ett delat tillstånd, vilket utlöser andra detektorer när signaler ansamlas:
// 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; }
}
Flera detektorer körs i parallella vågor. Var och en bidrar med en bit bevis. Systemet uppfattar miljön genom många linser:
// 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;
}
Bevisaggregat till ett beslut med sigmoidtransformering - detta utnyttjar starka signaler från högviktsdetektorer på rätt sätt:
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);
}
Utvärderingen är inte en enda modell som fattar ett beslut. Vägt samförstånd över flera specialiserade detektorer - och avgörande, när AI inte har kört, sannolikheten är fastspänning För att undvika övertro:
// CRITICAL: Clamp probability when AI hasn't run
var botProbability = aiRan
? rawBotProbability
: Math.Clamp(rawBotProbability, 0.20, 0.80);
Systemet kör inte alltid alla detektorer.
// 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
};
Vid tillverkning Högt självförtroende begär utträde på mindre än 10 ms Efter bara 2-3 detektorer håller med. Hela rörledningen körs endast i osäkra fall.
Detektorer kan misslyckas. Systemet skyddar sig självt:
// 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
}
}
Misslyckade detektorer är tillfälligt inaktiverade. Efter en nedkylning prövas de igen (halvöppen tillstånd). Urvalstryck på infrastrukturnivå.
När systemet upptäcker med stort självförtroende, lär sig Genom att publicera till inlärningsbussen:
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
}
});
}
Detta matas in i viktaffären, uppdaterar den heuristiska detektorns inlärda vikter över tiden.
I stället för en enda "AI-hjärna" använder DiSE skiktad kognition:
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
Varvtal på lager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . |-------|-------|---------|----------------------| | Statisk heuristik Känd bot UA-mönster | Detektorer på < 10 ms på grund av förarens iakttagelse på huvudet, IP-kontroller | Lärd heuristisk på 1-5ms med dynamisk klassificering på logistisk regression med inlärda vikter | LLM Ordförande 50-500 ms på djupet resonemang och analys av nya mönster | Lärande och bakgrund på minnesbildning på Viktuppdateringar, ryktesförändringar
Nyckelinsikten: systemet lär sig sina egna vikter i realtid. Inga externa ML-modeller krävs. Den heuristiska detektorn börjar med förnuftiga standardvärden och utvecklas baserat på detektionsfeedback.
Framtiden: ONNX modeller med RAG och inbäddning planeras för v2. Den nuvarande heuristiska modellens resultat fungerar som märkta träningsdata för framtida modellträning. Arkitekturen har redan de "genetiska" artefakter (bidrag, vikter, signaler) som gör lägga till riktad syntetisk evolution enkelt - den är utformad som en plugin punkt.
Detta speglar biologisk kognition:
Här är den smarta biten. Istället för att frakta en extern ML-modell, lär sig systemet sin egen klassificering med hjälp av enkel logistisk regression med dynamisk funktion extraktion:
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);
}
}
Funktionerna extraheras dynamiskt från begäran och aggregerade bevis:
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;
}
}
Nya funktioner får automatiskt standardvikter och lär sig med tiden. Systemet upptäcker vad som är viktigt.
Vikter kvarstår i SQLite och uppdateras via exponentiellt glidande medelvärde:
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, ...);
}
}
Det här är mutation genom observation. Varje upptäckt lär systemet något. Med tiden konvergerar vikterna mot optimala värden för dina specifika trafikmönster.
I DiSE, LLM hallucination är inte en bugg. Det är det generativa substratet för evolution.
En hög temperatur LLM föreslår tio varianter av en detektion regel är inte "hallucinerande" - det är mutering av arvsmassan i systemet:
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);
}
Mutationerna utvärderas sedan mot bakgrund av historiska data:
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;
}
Endast de starkaste överlever till produktionen. Detta omvandlar LLMs från sköra chatbots till Utvecklingsaktörer.
I centrum av DiSE är Politiksystem - namngivna konfigurationer som definierar hur detektion beter sig:
{
"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
}
}
}
Politiken fastställer följande:
Systemet kan Övergång mellan politiken efter begäran. Osäkra snabba resultat eskalerar till full demo pipeline. Detta är Adaptive routing (adaptive routing) - genet reagerar på bevis i realtid.
Ni skeppar inte statiska konfigurationer. beteendearter som anpassar sig till trafiken.
Varje DiSE-system optimeras mot ett fitnesslandskap:
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
}
}
Vi optimerar inte precisionen ensam. överlevnad i en dynamisk miljö.
För mycket AI → hög kostnad, dålig latency. För lite AI → dålig upptäckt av nya attacker. För aggressiv → falska positiva, arga användare. För mild → bot översvämningar.
Systemet måste hitta en stabil jämvikt. Tryck driver utvecklingen.
Ett moget DiSE-system förskjuter AI ur den heta vägen:
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
Över tid:
AI blir:
Inte ett beroende av löptid, adaptiv byggnadsställning.
och YARP- inkörsport, denna arkitektur möjliggör något nytt: Beteendeplanering.
Traditionell routing är statisk: väg → backend. Beteende routing är reflexiv: trafikegenskaper → dynamiska routing beslut.
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
De viktigaste begreppen:
Detta är inte bara "bot upptäckt vid kanten." Det är en ny routing primitiv där trafikflöden formas av inlärda beteendemönster, inte bara statiska regler.
De flesta ingenjörskulturer fruktar:
DiSE använder dem som byggnadsmaterial.
Statiska system dör.
Varje industri som har att göra med motståndare, drift eller skaltryck kommer så småningom att behöva arkitekturer som denna:
Låt mig vara tydlig med vad detta inte är:
DiSE är kontrollerad, förklarande utveckling:
Det är riktad evolution - guidad, inte slumpmässig.
mestadels lucid.botdetektion genomföra dessa principer,
dotnet add package Mostlylucid.BotDetection
builder.Services.AddBotDetection();
app.UseBotDetection();
Anpassa för produktion med inlärning aktiverad:
{
"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
}
}
}
}
Se den utvecklas. Den heuristiska detektorn lär sig från varje begäran, uppdatera dess vikter för att matcha dina trafikmönster.
DiSE Architecture är övergången från programvara som maskiner till programvara som organismer.
Det är arkitekturen av:
Statiska arkitekturer kan inte hålla jämna steg med dynamiska miljöer. Anfallare utvecklas. Användare utvecklas. Krav utvecklas.
Systemen måste också utvecklas.
Om du vill ha system som bara körs, bygga dem på det gamla sättet. Om du vill ha system som överleva - Bygg dem med DiSE.
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.