This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
Monday, 08 December 2025
La mayoría de las arquitecturas de software asumen que el sistema es estático. DiSE asume que el sistema está vivo.
Nota: Esta es la Parte 4 de la serie "Cocinar con DiSE". Parte 1, Parte 2: Aprendizajes graduados, y Parte 3: Dioses no confiables Este artículo es el resumen arquitectónico - y ahora tenemos una implementación C# de trabajo: mayormente lucid.botdetection.
Concepto clave: Enrutamiento conductual. Esta arquitectura permite una nueva categoría - donde "equipos" transparentes y ajustables de detectores y sistemas de aprendizaje recorren el tráfico reflexivamente basado en patrones de comportamiento aprendidos, no en reglas estáticas. YARP Gateway, los robots nunca llegan a tu motor. O utiliza el middleware para crear un enrutamiento de comportamiento directamente en tu capa de aplicación.
Durante años, hemos construido software como relojería: entradas, salidas, reglas, tuberías, pruebas, despliegues, todo lineal, todo predecible.
Pero los sistemas modernos - especialmente los reforzados con IA - ya no se comportan como relojes. Se comportan como ecosistemas:
Arquitecturas estáticas no pueden mantenerse al día, parcheas un hoyo, tres más aparecen, sintonizas un umbral, algo más se rompe, siempre reaccionas, nunca te adaptas.
DiSE Architecture (Directed Synthetic Evolution) trata el software de la manera en que los biólogos tratan a los organismos: como algo que debe adaptarse, autocorregirse y mejorar bajo presión.
Arquitectura tradicional:
flowchart LR
B[Build] --> S[Ship] --> P[Patch] --> B
style B stroke:#6366f1,stroke-width:2px
style P stroke:#ef4444,stroke-width:2px
Arquitectura 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 tiene cuatro comportamientos esenciales:
|-----------|--------------|----------------------| | Percibir Recoger señales del ambiente Órganos sensoriales | Evaluar Señales de puntuación contra objetivos Sistema nervioso | Mutar Generar variantes de estrategias Mutaciones genéticas | Seleccionar Mantenga lo que funciona, descarte lo que no lo hace Selección natural
Esto no es metáfora, es literalmente la forma en que tu sistema evoluciona comportamientos más inteligentes con el tiempo.
mayormente lucid.botdetection es la primera implementación C# de los principios DiSE. Rastreemos la arquitectura:
El sistema utiliza un orquestador de pizarra - los detectores aportan evidencia a un estado compartido, desencadenando otros detectores a medida que las señales se acumulan:
// 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; }
}
Múltiples detectores funcionan en ondas paralelas. Cada uno aporta una pieza de evidencia. El sistema percibe el medio ambiente a través de muchos lentes:
// 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;
}
La evidencia se agrega en una decisión utilizando la transformación sigmoide - esto aprovecha adecuadamente las señales fuertes de los detectores de 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 evaluación no es un solo modelo para tomar una decisión. consenso ponderado a través de múltiples detectores especializados - y crucialmente, cuando la IA no ha funcionado, la probabilidad es sujetados para evitar el exceso de confianza:
// CRITICAL: Clamp probability when AI hasn't run
var botProbability = aiRan
? rawBotProbability
: Math.Clamp(rawBotProbability, 0.20, 0.80);
El sistema no siempre ejecuta todos los detectores.
// 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
};
En producción, las solicitudes de alta confianza salen en menos de 10 ms Después de 2-3 detectores están de acuerdo. La tubería completa sólo funciona para casos inciertos.
Los detectores pueden fallar. El sistema se protege a sí mismo:
// 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
}
}
Los detectores fallidos se deshabilitan temporalmente. Después de un enfriamiento, se vuelven a probar (estado semiabierto). presión de selección a nivel de la infraestructura.
Cuando el sistema detecta con alta confianza, aprende publicando en el bus del evento de aprendizaje:
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
}
});
}
Esto se alimenta en la tienda de pesas, actualizando los pesos aprendidos del detector heurístico con el tiempo.
En lugar de un solo "cerebro AI", DiSE utiliza cognición en capas:
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
|-------|-------|---------|----------------------| | Heurística estática <1ms Respuestas instintivas Patrones de UA de bot conocidos | Detectores <10ms Observación del rasgo Análisis del encabezado, comprobaciones del IP | Heurística aprendida 1-5ms Clasificación dinámica Regresión logística con pesos aprendidos | LLM 50-500ms Razonamiento profundo Analizar patrones novedosos | Aprendizaje Antecedentes Formación de memoria Actualizaciones de peso, cambios de reputación
La visión clave: el sistema aprende sus propios pesos en tiempo real. No se requieren modelos externos de ML. El detector heurístico comienza con valores predeterminados sensibles y evoluciona basado en la retroalimentación de detección.
Futuro: Los modelos ONNX con RAG e incrustación están planeados para v2. La producción actual del modelo heurístico sirve como datos de entrenamiento etiquetados para futuros entrenamientos de modelos. La arquitectura ya tiene los artefactos "genéticos" (contribuciones, pesos, señales) que hacen que la adición directa de la evolución sintética - está diseñada como un punto de plugin.
Esto refleja la cognición biológica:
Aquí está el bit inteligente. En lugar de enviar un modelo ML externo, el sistema aprende su propio clasificador usando regresión logística simple con extracción dinámica de características:
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);
}
}
Las características se extraen dinámicamente de la solicitud y de las pruebas agregadas:
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;
}
}
Las nuevas características automáticamente obtienen pesos predeterminados y aprenden con el tiempo. El sistema descubre lo que importa.
Los pesos persisten en SQLite y se actualizan a través de un promedio móvil exponencial:
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, ...);
}
}
Esto es mutación a través de la observación. Cada detección enseña al sistema algo. Con el tiempo, los pesos convergen hacia valores óptimos para sus patrones de tráfico específicos.
En DiSE, la alucinación LLM no es un insecto, es el sustrato generativo de la evolución.
Un LLM de alta temperatura que propone diez variantes de una regla de detección no es "alucinante" - es mutar el 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);
}
Las mutaciones se evalúan a partir de los datos históricos:
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;
}
Sólo el más apto sobrevive a la producción. Esto transforma LLMs de chatbots quebradizos en operadores evolutivos.
En el centro de DiSE se encuentra la sistema de políticas - configuraciones nombradas que definen cómo se comporta la detección:
{
"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
}
}
}
Las políticas definen:
El sistema puede transición entre las políticas solicitud intermedia. Inciertos resultados fastpath escalar a la totalidad de la tubería de demostración. Esto es encaminamiento adaptativo - el genoma responde a la evidencia en tiempo real.
No envías configuraciones estáticas. especies conductuales que se adaptan al tráfico.
Cada sistema DiSE optimiza contra un paisaje de 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
}
}
No estamos optimizando solo para la precisión. supervivencia en un entorno dinámico.
Demasiada IA → alto costo, mala latencia. Muy poca IA → mala detección de nuevos ataques. Demasiado agresivo → falsos positivos, usuarios enojados. Demasiado indulgente → inundaciones de bot.
El sistema debe encontrar un equilibrio estable. La presión impulsa la evolución.
Un sistema DiSE maduro desplaza la IA fuera del camino caliente:
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
Con el tiempo:
AI se convierte en:
No es una dependencia de tiempo de ejecución, andamios adaptativos.
Con el YARP Gateway, esta arquitectura permite algo nuevo: Encaminamiento conductual.
El enrutamiento tradicional es estático: ruta → motor. El enrutamiento conductual es reflexivo: características del tráfico → decisiones de enrutamiento dinámico.
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
Los conceptos clave:
Esto no es sólo "detección de robots en el borde". Es una nueva ruta primitiva donde los flujos de tráfico están formados por patrones de comportamiento aprendidos, no sólo reglas estáticas.
La mayoría de las culturas de ingeniería temen:
DiSE utiliza los como Materiales de construcción.
Los sistemas estáticos mueren, los sistemas evolutivos sobreviven.
Cada industria que trata con adversarios, deriva, o presión de la escala eventualmente necesitará arquitecturas como esta:
Déjame ser claro sobre lo que esto no es:
DiSE es controlado, evolución explicable:
Es dirigida evolución - guiado, no al azar.
mayormente lucid.botdetection aplica estos principios:
dotnet add package Mostlylucid.BotDetection
builder.Services.AddBotDetection();
app.UseBotDetection();
Configurar para la producción con el aprendizaje habilitado:
{
"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
}
}
}
}
El detector heurístico aprende de cada solicitud, actualizando sus pesos para que coincidan con sus patrones de tráfico.
DiSE Architecture es el cambio de software como máquinas a software como organismos.
Es la arquitectura de:
Las arquitecturas estáticas no pueden mantenerse al día con los entornos dinámicos. Los atacantes evolucionan. Los usuarios evolucionan. Los requisitos evolucionan.
Los sistemas también deben evolucionar.
Si quieres sistemas que simplemente funcionen, constrúyelos a la antigua. Si quieres sistemas que Sobrevivir - ...constrúyelos con DiSE.
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.