# StyloBot Release Series: Impararsi ad essere più veloci

*Heads up: questo è per i nerd. E' una profonda immersione tecnica nel StyloBotM SK3 il sistema di apprendimento adattativo ( le ultime versioni dell'EWMAMSC5 i sogliamenti di histeresiMST6 la cachinge dei verticiMst7 i guardiani delle variazioniMSt8 con un codice reale da `Mostlylucid.BotDetection` e puntatori verso l'equivalente macchina-Construisce l'apprendimento in modo che i lettori ML abbiano un punto di entrata . Se volete la pista dell'ascensore, leggete i post precedenti della pubblicazioneM SK3 se volete sapere come la memoria a quattro livelliMSC4 trasforma il traffico ripetuto in sottotassiMST5 decisioni in millisecondi mentre si recupera da falsi positiviMst6 leggere suM st7*

> ## PROJEtto
> 
> Questo è un progetto di lavoro nella StyloBot Release Series. NumeriM SK1 tastieri, e il nome potrebbe ancora cambiare prima della versione finale .

> **StyloBot Release Series**
> 
> 1. [**Comportamento, Non identità**](/blog/stylobot-fingerprint) - perché StyloBot modella i clienti comportamentalmente
> 2. [**Comportamento-Aware ASP.NET UI**](/blog/behaviour-aware-ux) - il server- restituisce la superficie su quel risultato di rilevamento.
> 3. [**Trovare e riparare la crescita senza limiti nei servizi NET Long-Running .**](/blog/stylobot-release-reliability) - la disciplina di affidabilità che mantiene il motore noioso nella produzione.
> 4. [**Comportamento-Conoscere l'interfaccia grafica del tipoScript**](/blog/typescript-sdk) - ExpressM SK1 Fastify, e componenti del browser
> 5. [**L'architettura della Sidecar**](/blog/sidecar-architecture) - come il motore di rilevazione si collega a stack non -.NET
> 6. **Imparare a diventare più veloce** - il sistema di apprendimento adattativo, quattroM SK2 memoria a livello di livello+, e la cassa del verdetto
> 7. [**Provare a fare ciò che non si ferma.**](/blog/stylobot-release-nondeterministic-testing) - la disciplina di verificazioneM SK1 un file BDF guida la regressione , carico, e calibrazione
> 8. [**StyloExtract - un convertitore locale di HTML per Markdown**](/blog/stylobot-release-styloextract) - l'HTML→Layere Markdown che si combina con il rilevatore , il walkeder bug lucidVIEW catturatoM SK3 e la catena di cibo per cani che lo rende onesto

<!--category-- ASP.NET, StyloBot, Bot Detection, Performance, Machine Learning -->
<datetime class="hidden">2026-05-13T11:00</datetime>

Il modello comportamentale si trova in [Comportamento, Non identità](/blog/stylobot-fingerprint); la disciplina di affidabilità in [Trovare e fissare la crescita senza limiti](/blog/stylobot-release-reliability); fonte a [github.comM SK1scottgal/stylobot](https://github.com/scottgal/stylobot).

[TOC]

---


## Perché StyloBot si preoccupa di questo?

StyloBot' l'intero lavoro consiste nell'informare gli esseri umani dai robot, e il modo in cui lo fa al livello più basilare è costruendo un'immagine di come appare un bot medio e di come si presenta un umano medio . Niente regole fissateM SK3 Nient'un regex sull'User MSC4 String degli agentiMska5 centroidi comportamentali in uno spazio vectore dimensionaleMske6 uno per gruppo di clienti che si muovono allo stesso modoM Ske8 Ogni osservazione o conferma un centroide esistente, o lo spinge a posizionarloMске9 Più questi centroidi si ottengonoMskie10 più accuratamente un nuovo visitatore può essere posizionato contro uno di loroMsete11 e più economico diventa questo posizionamentoMsze12 **Accuratezza migliora prima; La latenza migliora come conseguenza.** (Il lato di agglomerazione di questo è coperto da [Comportamento, Non identità](/blog/stylobot-fingerprint); questo post riguarda quello che succede con i centroidi una volta che li hai.

Questo meccanismo è anche quello che rende lo StyloBot applicabile in posti stupidamente diversi: un blog personale a 50 richiesteM SK2minuteMSC3 un sito di marketing che si scoppia durante il lancio del prodotto , uno SPA dove la maggior parte del traffico sono chiamate APIMST5 un checkout e-commerce dove una falsa affermazione su un cliente pagatore è denaro perso realeMSSK7 un portal di contenuti dove 70% del traffico è automatizzato ma gli 30% degli esseri umani hanno ancora bisogno di un percorso pulito attraverso l'. Lo stesso motore finisce con diversi centroidi su diversi siti perché vede diversi trafficiMSP11 il modo in cui li costruisce è lo stesso ovunqueMSM12

Un rilevatore che gestisce la catena intera su ogni richiesta è approfondito ma non economico. Un rilevante che archivia oscuramente i vertici è veloce ma fragile. Il StyloBot si trova tra questi due : il cache è il caso comuneMSC3 la catene intera è il percorso di recuperoM SK4

Per il contesto contro i nomi più familiari in questo spazio:

|
|---|---|---|---|
| Cloudflare Bot Management | Edge | ( | Il loro CDN |) | | Sub | - | Millisecondi al limite || | Blocco | / | sfida |/ | permettere prima che la vostra origine lo veda | |
| CHEQ / DataDome | Server- Callout API laterale ♫| ♫ ~50-150 ms ♫
| StyloBot | Nel processo o nella carrozza laterale locale del gRPC | **~10-50 μs** riscaldato tramite Skip, **400-900 μs** per un'elettricità di pipeline veloce -path run | Verdetto digitato PSK2`BotProbability`, `Confidence`, `RiskBand`, `ThreatScore`) direttamente nel vostro handler |

I prodotti di bordo sono grandi a "bloccare prima dell'origine"; quello che non possono fare è dare alla vostra applicazione una probabilità per la richiestaM SK2e lasciare che la vostra pagina di pagamento decide cosa farci con lei . I prodotti API lo fannoMSC4 ma paghiamo un salto in rete per ogni richiesta. Il StyloBot è il caso in cui il verdetto è nel processoMST6MTS7 il salto in network non esisteMSST8non esiste MST9 e il percorso caldo è più veloce di uno o l'altro ordine di grandezzaMSS10

La leva che rende la cache sicura è: **Policy-per-surface**. Il cache del verdetto è governato da un `SignatureCacheOptions` Registrare che 's è stato stabilito per -policy, così un punto di fine dell'amministratore può chiedere una 0.95 affidabilità e una ♫60-seconde freshness window mentre il percorso del contenuto accetta una \0.7 affidamento in più di un'oraM SK6 Lo stesso codiceMST7 diversi tastieriM ST8 senza forchettoM st9 Ciò che rende lo stesso motore applicabile come un [ASP.NET middleware](/blog/behaviour-aware-ux), come una [gRPC Sidecar](/blog/sidecar-architecture), o inM SK1processo dietro un plugin Caddy.

Il resto di questo post è il macchinario che rende la cassa sicura: EWMA updates, soglia d'isteresiM SK2 caching del verdicto , campioni di rifrescoMSC4 e guardiani delle variazioniMNK5 Il numero di titolo è latenzaMRK6 Il problema di progettazione reale è imparare senza rendere i vecchi errori permanentiMMK7

## L'impronta digitale metastabile

Tutto sotto è collegato a " l'impronta digitale ", quindi vale la pena essere precisi su cosa significhi qui. Nel StyloBot una impronta digitale non è un singolo campoM SK4 MaMSC5 è una posizione nello spazio vectorale comportamentale dimensionale di [Comportamento, Non identità](/blog/stylobot-fingerprint), disegno su TLS, ordine del sequenziamento delle sequenze di segnaliM SK2 timingMSC3 sequenza del percorsoMNK4 comportamento JSMRK5 e i segnali che essi arrivano da tutti 50 rilevatoriMMK7 un IP rotto può sott'altro muoverloMEK8 un utilizzatore rottoMK9 un agente può sottr'altro farloMKK10 cosa lo muove? *comportamentale.* cambiare: una diversa sequenza di percorsiM SK1 una different cadence, un nuovo modo per verificare l'errore .

Questo fa scattare l'impronta digitale. **Metastabile.**: abbastanza stabile da mantenere un attore riconoscibile per un breve periodo di tempo -turno di identità a lungo termineM SK2 evoluto abbastanza da mostrarsi un vero cambiamento comportamentale come movimento del punto. Non un'identità fissaMSC4 non rumore casualeMST5 Un'identità lentaMSV6moving signature with structureM SV7

Questa è la proprietà su cui si basa l'intero sistema di apprendimento. Se le impronte digitali fossero rumorose ( ogni richiesta atterrava da qualche parte casualmenteM SK2 L'evidenza media dell'EWMA non avrebbe affinato nulla e il cachingo sarebbe sempre sbagliato . Se fossero rigide ( la posizione non si muovesse maiMSC5 non ci sarebbe alcuna deriva da tracciare e il cane guardia non avrebbe niente da osservareMST6 La metastabilità è esattamente ciò che fa funzionare entrambi i lati : osservazioni sostenute possono raffinare un centroide perché il segnale sottostante è abbastanza stabile per affinareMSSK8 *e* Il drift può essere rilevato perché il cambiamento reale si presenta come un movimento contro uno sfondo altrimenti stabile.

L'argomentazione strutturale per il motivo per cui questa proprietà ha (umani rumorosi ma coerenti nella strutturaM SK1bots coerentivi ma sbagliati in struttura ) è la carica-proprizione portata da [**Comportamento, Non identità**](/blog/stylobot-fingerprint), e se c'è una geometria qui che non è chiara, ' è l'articolo da leggere prima. Tutto ciò che segue in questo post è quello che potete fare. *una volta.* che la prestazione è in mano.

## La tesi: la rilevazione dovrebbe diventare migliore e più economica più si vede.

Ogni osservazione fa due cose: : aggrandisce la centroide, l'impronta digitale appartiene a (accuratezza), e solleva il sistema ' la fiducia che questa impronta si comporti come quella centroide.

Un rilevatore robot viene chiesto la stessa domanda costosa milioni di volte al giorno: *Questo attore si comporta ancora come quello che abbiamo già imparato: ?.* Per la grande maggioranza di queste richieste, il sistema ha già avuto un'opinione. Ha visto lo stesso taglio digitale TLSM SK2 lo stesso ordine di giunte , lo stessi UsuarioMSC4 il modello di rotazione dell'agentePSK5 lo mismo quartiere IP dieci minuti fa–, e ha concluso `bot, 0.93`. Non c'è importanza di ripristinare il tubo completo del rilevatore sullo stesso attore a meno che qualcosa su quel attore non sia cambiato visibilmente.

StyloBot' il sistema di apprendimento è costruito intorno a quell'idea . Il tubo funziona completamente quando deve, e ogni tubo completo funziona così. *costruisce e raffina i centroidi.* in primo luogo. Quando il tubo di tubature non funziona'non funziona , l'implantazione precedente viene riutilizzata sotto condizioni controllateM SK3 la richiesta è ricevuta in microsecondiMSC4 e la ricevuzione continua a alimentare la memoria lungaMST5che funziona così che i centroidi rimangano in funzioneMSST6

> **ML a parte.** Questo è **computazione adattativa.** nelle reti neurali (e.gM SK2 [early-exit networks](https://arxiv.org/abs/1709.01686), Mixture-ofM SK2Experti che si radunanoMSC3classificatore economico primaMST4classificadore costoso solo quando l'unico economicoMSM5la fiducia è bassaMSSK6Il tubo del rilevatore è il percorso costoso di deduzioneMSV7il cache per le impronte digitali è il più economico MSV9avete già imparato questo?

## Quattro livelli di memoria

L'apprendimento in StyloBot è stratificato così che ogni livello corrisponde ad una vita diversa:

1. **Rapido-reputazione di percorso (instantM SK2** Una breve lista di modelli classificati come `ConfirmedBad` In questo caso, si tratta di un errore. `PatternReputation` conservare. Una richiesta che corrisponde a uno di questi aborta il pipeline con priorità 3 prima che qualsiasi altro rilevatore funzioniM SK2 L'invio richiede un punteggio ≥ | | 0.9 e supporta ♫≥ ♫
2. **Intra-board -request (per-re requestM SK3 millisecondiMSC4** I rilevatori scrivono segnali a un compartimento. `BlackboardState.Signals` scavare durante la richiesta; i rilevatori più tardi leggono quei segnali. Questo non è ' non è lungoMSC3 memoria a termineM SK4 èMST5 come i rilevanti MSL6 si coordinano in una sola richiesta senza che ognuno abbia bisogno di ricomputare le caratteristiche degli altri già estrattiMSR7
3. **Inter-request reputation (minute a giorniM SK2** Pattern-memoria di livello: `InMemoryPatternReputationCache` tracks per-pattern bot scores with online EWMA updates and time decay. Ogni schema ha un `ReputationState` (`Neutral` → `Suspect` → `ConfirmedBad`, più `ConfirmedGood`, `ManuallyBlocked`, `ManuallyAllowed`) con limiti asimmetrici di promozione e di deduzioneM SK1
4. **Per-cache del verdetto delle impresse dita M SK1minuti).** Per-memoria dell'attore: la finestra di scorrere in diretta `SignatureCoordinator` Contiene ogni impronta digitale osservata'L'impronta posteriore running,Il numero di campioniM SK2La ultima TAC3Il tempo visibile ,La più recente fascia di rischioMSC5E la più recente classifica delle minacceMSL6 `SignatureVerdictGate.DecideAsync` lo legge su ogni richiesta.

Tiero 1 breveM SK1 circola il tubo di tubature. Niveau 2 le coordinate all'interno del tubo,. Il livello 3 è la memoria lunga che sopravviverà per giorniMSC6 Il nivel M4 é la memoria breve che permette al traffico ripetitivo di saltare completamente il tusso di tubatura

```mermaid
flowchart LR
    classDef input fill:none,stroke:#3b82f6,stroke-width:2px
    classDef fast fill:none,stroke:#22c55e,stroke-width:2px
    classDef async fill:none,stroke:#a855f7,stroke-width:2px
    classDef store fill:none,stroke:#f59e0b,stroke-width:2px

    REQ["Incoming request<br\>(TLS fingerprint, headers, IP)"]:::input
    T1["Tier 1 · Fast-path reputation<br\>ConfirmedBad list<br\>priority 3, pre-pipeline"]:::fast
    T2["Tier 2 · Intra-request blackboard<br\>BlackboardState.Signals<br\>lifetime: one request"]:::async
    T3[("Tier 3 · PatternReputation<br\>EWMA + state machine<br\>lifetime: minutes to days")]:::store
    T4[("Tier 4 · SignatureCoordinator<br\>sliding window<br\>lifetime: minutes")]:::store
    PIPE["Detector pipeline<br\>(BlackboardOrchestrator)"]:::async
    OUT["Verdict"]:::fast

    REQ --> T1
    T1 -->|match| OUT
    T1 -->|no match| T4
    T4 -->|Skip| OUT
    T4 -->|Miss / Bias / Watchdog| PIPE
    PIPE <--> T2
    PIPE <--> T3
    PIPE --> T4
    PIPE --> OUT
```

## EWMA: come il sistema dimentica

Ogni memoria numerica del sistema usa la stessa regola di aggiornamento,. `Helpers/Ewma.cs`:

```csharp
public static class Ewma
{
    /// <summary>new = (1 - alpha) * previous + alpha * observation</summary>

    public static double Update(double previous, double observation, double alpha)
        => (1.0 - alpha) * previous + alpha * observation;
}
```

Quello è tutto. `alpha` è il peso della nuova osservazione; `1 - alpha` è l'inerzia del passato. Alfa più alta M SK1 reazione più veloce alla più recente osservazione; alpha = | | 0 congela

La reputazione del modello lo usa direttamente. Da `PatternReputationUpdater.ApplyEvidence`:

```csharp
// EMA update: alpha clamped to [0,1] preserves EMA semantics (alpha > 1 inverts
// the old score contribution).
var alpha = Math.Min(_options.LearningRate * evidenceWeight, 1.0);
var newScore = Ewma.Update(decayed.BotScore, label, alpha);
```

`LearningRate` defaults to 0.1, così una singola osservazione porta il punteggio con 10% dell'intervallo a quella osservazioneM SK2 L'altra 90% è qualunque sia stato lo schema storicamente fatto.

> **ML a parte.** EWMA = il classico [La media di movimento esponenziale.](https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average) che si presenta in questo [Adamo](https://arxiv.org/abs/1412.6980) (the `β₁`, `β₂` Accumulatori di dinamico e variazione), in [La media di Polyak](https://en.wikipedia.org/wiki/Stochastic_approximation), in BatchNorm running mean/varianceM SK2 in [Update dei valori TD(0)](https://en.wikipedia.org/wiki/Temporal_difference_learning) (`V ← V + α(r + γV' - V)`). `α = 0.1` qui è nella stessa gamma che vedete per i bufferi del momento: abbastanza lento che una cattiva osservazione non possa muovere il valore in funzione' abbastanza veloce che la valeur in funzione traccia un cambiamento sostenutoM SK4

Abbiamo scelto questa applicazione oltre l'ovvio-ma-l'alternativa sbagliata di conservare la probabilità più alta mai osservata . Un'archiviatura più grandeM SK3diMSC4 della storia permetterebbe ad un singolo pinto di picchio MST5 di creare un modello a SST6 per sempre MST7 non importa come si comporti dopoMSST8 L'archivio dell'EWMA ha la proprietà oppostaMSP9 un picco MSST10 seguito da centinaia di osservazioni benigne si decadono facilmente verso il buonoMSS11 I positivi falsi sono recuperabiliMMSST12

La decasione si estende ai modelli stessi. `ReputationOptions` (defaults from the code):

```csharp
public double ScoreDecayTauHours { get; set; } = 3;             // generic
public double SupportDecayTauHours { get; set; } = 6;
public double ConfirmedBadScoreDecayTauHours { get; set; } = 12; // ConfirmedBad
public double ConfirmedBadSupportDecayTauHours { get; set; } = 24;
public int GcEligibleDays { get; set; } = 90;
```

Un modello che non è stato visto in un giorno ha perso la maggior parte del suo supporto, uno che non é stato visto nei giorni 90 e che è tornato. `Neutral` è spazzatura-raccogliata. `ConfirmedBad` Gli schemi diventano più lunghi (hanno guadagnato il loro status grazie a forti prove e non dovrebbero 'la perdere in una sola ora tranquille).La memoria che non decade non si allontana dalla realtàM SK4

> **ML a parte.** Il punteggio-decayM SK1 verso-il primo passo è il primo -la media dell'ordine [Ornstein–Uhlenbeck](https://en.wikipedia.org/wiki/Ornstein%E2%80%93Uhlenbeck_process) forma): senza nuove proveM SK1 il punteggio si riporta al ritmo precedente. `1/τ`.

L'isteresia è integrata nella macchina dello stato. I limiti reali di `ReputationOptions`:

| Trasizione | Soglia di punteggio | Soglie di supporto S|
|---|---|---|
| `Neutral → Suspect` | ≥ 0.6 | ≥ 10 |
| `Suspect → ConfirmedBad` | ≥ 0.9 | ≥ 50 |
| `Suspect → Neutral` |  ≤
| `ConfirmedBad → Suspect` |  ≤
| `Neutral → ConfirmedGood` | ≤ 0.1 | ≥ 100 |

```mermaid
stateDiagram-v2
    direction LR
    [*] --> Neutral
    Neutral --> Suspect : score &ge; 0.6<br/>support &ge; 10
    Suspect --> ConfirmedBad : score &ge; 0.9<br/>support &ge; 50
    Suspect --> Neutral : score &le; 0.4
    ConfirmedBad --> Suspect : score &le; 0.5<br/>(support &ge; 100 OR<br/>support &lt; 50)
    Neutral --> ConfirmedGood : score &le; 0.1<br/>support &ge; 100
    ConfirmedBad --> [*] : 90 days unseen<br/>+ neutral + low support
    Neutral --> [*] : 90 days unseen<br/>+ low support

    note left of ConfirmedBad
        Fast-path eligible
        (Tier 1 short-circuit)
    end note
    note right of Neutral
        Promote at score 0.9 / sup 50
        Demote at score 0.5 / sup 100
        Wider gap = oscillation-resistant
    end note
```

L'intervallo di 0.4- tra il sogliatore di promutamento (0.9) e il soglietore di demolizione МSK2 per `ConfirmedBad` è deliberato:èM SK1è una oscillazione-un fascio dispiegamento .Tutto ciò che si ribalta tra MST4 e SST5 rimane un `Suspect`; solo prove sostenute in una direzione attraversano il confineM SK1

> **ML a parte.** Questo è un [Triggere Schmitt](https://en.wikipedia.org/wiki/Schmitt_trigger): più ampio-inveceM SK2salti simmetrici per le transizioni di statoMSC3 L'analogo ML è il rivestitore discrezionale -decision wrapper intorno a qualsiasi classificatore continuo dove non volete l'etichetta che cambia ogni lottoMST6 pensate al modo in cui i voti di gruppo sono normalmente confrontati con un requisito di margineMst7 o come i sistemi di allarme debloccano MST8di una sola pagina se la condizione dura N minutiMSt9 E' anche il motivo per cui i punti operativi in precisioneM st11la curva di richiamo è regolata per mst12directionMstr13

## La velocità della cache del verdetto: che dovete guadagnare

Il per-fingerprint sliding window è la strata che il percorso caldo di richiesta consulta ora direttamente. `SignatureVerdictGate.DecideAsync` E' l'intera decisione, e è il piccolo.

```csharp
public async Task<GateDecision> DecideAsync(
    string? signature, SignatureCacheOptions options, CancellationToken ct = default)
{
    if (!options.Enabled || string.IsNullOrEmpty(signature))
        return new GateDecision(GateAction.Miss, null);

    var verdict = await _coordinator.TryGetVerdictAsync(signature, ct);
    if (verdict is null)
        return new GateDecision(GateAction.Miss, null);

    if (verdict.Confidence < options.BiasMinConfidence)
        return new GateDecision(GateAction.Miss, verdict);          // too noisy

    var ageSeconds = (DateTime.UtcNow - verdict.LastSeenUtc).TotalSeconds;

    var skipEligible =
        verdict.Confidence >= options.SkipMinConfidence
        && ageSeconds <= options.SkipMaxAgeSeconds;

    if (skipEligible && !ShouldRefresh(signature, options.SkipSamplingRate))
        return new GateDecision(GateAction.Skip, verdict);          // cache hit

    var biasEligible = ageSeconds <= options.BiasMaxAgeSeconds;
    return new GateDecision(biasEligible ? GateAction.Bias : GateAction.Miss, verdict);
}
```

I limiti di politica per - sono: `SignatureCacheOptions` (defaults shown):

```csharp
public double SkipMinConfidence { get; init; } = 0.85;
public int    SkipMaxAgeSeconds { get; init; } = 300;        // 5 min
public double BiasMinConfidence { get; init; } = 0.30;
public int    BiasMaxAgeSeconds { get; init; } = 86_400;     // 24 h
public double SkipSamplingRate  { get; init; } = 0.05;       // 5 %
```

La fiducia stessa si deriva da `SignatureCoordinator.TryGetVerdictAsync` dalla dimensione dell'esempio, con la totale sicurezza alle osservazioni M SK1 e una rampa lineare sotto :

```csharp
var confidence = Math.Min(1.0, behavior.RequestCount / 10.0);
```

Quindi. `SkipMinConfidence = 0.85` significa che un'impronta digitale ha bisogno di osservazioni ~9 nella finestra attuale prima di essere Skip-eligibleM SK2 In basso , la porta preferisce il pregiudizio

Le quattro azioni:

- **Miss.** No usable record for this fingerprint, or one older than `BiasMaxAgeSeconds`. Funziona il tubo completo del rilevatoreM SK1 Il risultato alimenta la finestra di scorrere per la prossima volta.
- **Pregiudizi.** C'è un registro con una fiducia moderata, o è lo stessoM SK1 è leggermente in gamba. Il pipeline va avantiMSC3 ma il verdetto cacheto viene iniettato come una Wave -0 contributo precedenteMNK5 La parte posteriore viene spinta verso la parte precedente in proporzione alla fiducia precedente e ad una decasione lineare dell'etàMSL6
- **Skip.** Il record è recente e abbastanza sicuro che la contribuzione del pipeline sarebbe marginale. Il verdetto cache è messo in attoM SK1 l'orchestratore è stato trascurato. La richiesta viene ricevuta in microsecondi .
- **Cane da guardia-trip.** Passi-hit di cache elegibile, ma l'osservatore delle variazioni ha rilevato qualcosa di atypicoM SK2 Il verdetto cache è invalidato per questa richiesta

```mermaid
flowchart TD
    classDef input fill:none,stroke:#3b82f6,stroke-width:2px
    classDef decision fill:none,stroke:#f59e0b,stroke-width:2px
    classDef fast fill:none,stroke:#22c55e,stroke-width:2px
    classDef slow fill:none,stroke:#a855f7,stroke-width:2px
    classDef trip fill:none,stroke:#ef4444,stroke-width:2px

    REQ["Request arrives"]:::input --> LOOKUP{"TryGetVerdictAsync<br\>(sliding window<br\>+ family fallback)"}:::decision
    LOOKUP -->|"null OR low-conf OR stale"| MISS["MISS<br\>full pipeline"]:::slow
    LOOKUP -->|"record found"| CONF{"conf ≥ 0.85<br\>+ age ≤ 300s?"}:::decision
    CONF -->|"no"| BIAS["BIAS<br\>pipeline + prior contribution"]:::slow
    CONF -->|"yes"| REFRESH{"In 5% refresh<br\>(hash bucket)?"}:::decision
    REFRESH -->|"yes"| BIAS
    REFRESH -->|"no"| WD{"VarianceWatchdog<br\>OK?"}:::decision
    WD -->|"tripped"| TRIP["WATCHDOG-TRIP<br\>X-StyloBot-VerdictSource = pipeline<br\>X-StyloBot-WatchdogTrip = reason"]:::trip
    WD -->|"ok"| SKIP["SKIP<br\>X-StyloBot-VerdictSource = cache<br\>orchestrator bypassed"]:::fast

    MISS --> UPDATE[("Update sliding window<br\>+ RecordObservation")]:::slow
    BIAS --> UPDATE
    TRIP --> UPDATE
    SKIP --> UPDATE
```

Il middleware emette `X-StyloBot-VerdictSource` (`cache` o `pipeline`) e `X-StyloBot-WatchdogTrip` (stringo di risposta quando applicabile) sulla risposta così che gli operatori possano vedere in diretta quale percorso ha preso ogni richiestaM SK2

Skip è quello che succede quando non c'è nulla di nuovo da imparare. Una volta che si ha visto un'impronta digitale con abbastanza fiducia in una o l'altra direzione (la porta è la direzioneM SK2agnostica , così sicuraMSC4umana e sicura~-il robot è ugualmente qualificato~), ogni richiesta successiva costa solo il gate lookupMST7 un registro d'osservazione~MST8 e l'eseguimento delle politiche~Mst9 sulla scatola di riferimento |MST10 | MST11 | MacBook Air |MSST12 | MSST13 | GB | SST14 | che |SST15 | SMST16 | μs | FSST17 | rispetto a |SMST18 |μs per una velocità completa |SSST19 | passa del tubo di tubatura | SSST20 | Skip è circa un ordine di grandezza più veloce della linea completa economica e molti ordini più veloci di qualsiasi altra cosa che implichi un salto in rete |ESST21

## Il cache del verdetto come contributo, non come decisione

Prima di entrare nella meccanica della ripresa, la struttura che li rende coerenti: il verdetto cache non è una risposta archiviata che il sistema riutilizza in modo ciecoM SK2 Sul percorso dei pregiudizi, è iniettato come un'onda *Contribuzione*, weighted by confidence and ageM SK1 `FingerprintPriorContributor.ContributeAsync` è l'intero meccanico:

```csharp
var horizon = AgeDecayHorizon;                                 // default 86,400s
var decay = horizon > 0.0 ? Math.Max(0.0, 1.0 - age / horizon) : 1.0;
var effectiveWeight = conf * WeightMultiplier * decay;

if (effectiveWeight <= 0.0) return _emptyResult;

var delta = 2.0 * (prob - 0.5);   // map [0,1] probability to [-1,+1] confidence delta

var contribution = new DetectionContribution
{
    DetectorName = DetectorName,
    Category = DetectorName,
    ConfidenceDelta = delta,
    Weight = effectiveWeight,
    Reason = $"Cached fingerprint verdict (prob={prob:F2}, conf={conf:F2}, age={age:F0}s)"
};
```

Un 30-secondoM SK1verdetto con fiducia 0.9ancora fortemente l' posteriore SSK3 peso effettivo ≈ S0.9 \× ♪1.0 | | × \ \ 0.9997 ♫≈ |0.90). Un \23-ora-verdito con fiducia

> **ML a parte.** Questo è davvero **Bayesiano**: prima ( giudizio archiviato) × probabilità SSK4quella richiestaM SK5la provaMSC6 \→ posteriore `decay = 1 - age/horizon` Il termine gioca lo stesso ruolo di un fattore che dimentica. [Ricursiva stima Bayesiana](https://en.wikipedia.org/wiki/Recursive_Bayesian_estimation); i vecchi antenati perdono forza così la probabilità domina come le età precedentiM SK1 E' anche questo il motivo per cui il pannello mostra `RequestContributionDelta` (il cambiamento che questa richiesta ha fatto al posteriore running) piuttosto che alla probabilità di richiesta prodotta per -M SK3 sui vertici memorizzatiMSC4 la probabilità per \-la richiesta è più o meno l'affermazione precedenteMSL6 e `delta` E' la parte che ' in realtà contiene nuove informazioni.

## Come i falsi positivi non si combinano?

Fino a questo punto il sistema suona pericolosamente cache- pesante, quindi questa sezione è importante : come il progetto assume che i vertici cacheti siano alla fine sbagliatiM SK3 e quanto facilmente si nota e si recupera quando sono statiMSC4 Sei meccanismi indipendenti strati uno sopra l'altro così che ogni singolo che viene sconfitto lascia ancora cinque tra il cache e un verdetto falso permanenteMNK5

```mermaid
flowchart TD
    classDef store fill:none,stroke:#f59e0b,stroke-width:2px
    classDef defense fill:none,stroke:#22c55e,stroke-width:2px
    classDef recover fill:none,stroke:#a855f7,stroke-width:2px

    CACHE[("Cached verdict<br\>(EWMA posterior + sample count)")]:::store

    D1["Direction-agnostic confidence<br\>caches confident verdicts,<br\>not just bot verdicts"]:::defense
    D2["EWMA, not MAX<br\>one spike contributes 10%,<br\>not 100%"]:::defense
    D3["Sample-size gating<br\>full trust at 10 samples,<br\>Bias below that"]:::defense
    D4["Variance watchdog<br\>IP rotation, rate spike,<br\>path divergence"]:::defense
    D5["Skip-path observation<br\>cache hits still record,<br\>history stays whole"]:::recover
    D6["Refresh sampling<br\>5% of Skips downgraded<br\>to Bias by signature hash"]:::recover

    D1 --> CACHE
    D2 --> CACHE
    D3 --> CACHE
    D4 --> CACHE
    D5 --> CACHE
    D6 --> CACHE
```

**Direzione-confidenza agnosticaM SK1** `SkipMinConfidence` è controllata contro. `verdict.Confidence`, non contro `verdict.BotProbability`. Un vero -giudizio robot e un vero \ -giudzio umano sono ugualmente autorizzati a saltare. Quello 'è la differenza tra \"\ i giudizi bot del nostro cachingo |"\ \(\ un sistema predisposto ai falsi positivi *Convinta.* giudizi" (un sistema pregiusto in ogni direzione che le prove indicano

**EWMA, non MAX.** Un'osservazione unica di probabilità elevata - muove il punteggio in corso. `α = 0.1` Un vero attaccatore accumula delle prove rapidamente perché ha un'incredibile capacità di identificare gli attacchi. *ogni anno.* L'osservazione è ostile. Un visitatore legittimo che per caso sembra un spazzaturare per una richiesta si decade verso benigno sulle osservazioni successive.

**Esempio-innalzamento di dimensioneM SK1** `confidence = min(1, request_count / 10)`. Al di sotto ~9 osservazioni, persino un forte posteriore è tenuto a Pregiudizio piuttosto che SkipM SK3 La decisione di fidarsi del cache è presa dal fatto che l'EWMA sia significativoMSC4

> **ML a parte.** Questo è un **Rimpicciolimento anteriore**: finché non si raccolgono prove sufficienti , il posteriore non riesce a agire con sicurezzaM SK2. Stessa ragione [Banditi UCB](https://en.wikipedia.org/wiki/Multi-armed_bandit#Upper_Confidence_Bound) Includere un campione- termine di conteggioM SK1 piccolo `n`, ampio intervallo di fiduciaM SK1 non ' ancora sfruttato.

**Pedo di guardia della variabile.** Anche con un verdetto confidenziale, fresh cachedM SK1 Skip può essere vetoato per richiesta. `VarianceWatchdog.Check` conduce tre test indipendenti contro `VarianceWatchdogOptions` (defaults):

```csharp
public int    IpRotationWindowSeconds { get; init; } = 300;   // same fingerprint, new /24
public double RateSpikeMultiplier     { get; init; } = 10.0;  // last minute vs rolling 5
public bool   CheckPathCentroid       { get; init; } = true;  // never-seen path family
```

Il percorso-il controllo della divergenza ha bisogno almeno M SK1 di famiglie di percorso distinte registrate prima che possa sparare, così caldoMSC3che le richieste di aumento non si muovono da sé.

```csharp
if (options.CheckPathCentroid && hist.PathFamilyCount >= PathFamilyBaseline)
{
    var family = PathFamily(ctx.Request.Path.Value);
    if (family is not null && !ContainsFamily(hist, family))
        return new WatchdogResult(true, $"path-divergence:{family}");
}
```

Qualunque viaggio e il pipeline va fresco; la risposta porta. `X-StyloBot-WatchdogTrip: ip-rotation:1.2.3.0->5.6.7.0` (o qualunque cosa sia stata sparata ) così che gli operatori vedono la ragione.

> **ML a parte.** Il cane di guardia è **Concept-Detezione del drift** per un'impronta digitale. Gli analogi classici sono [ADWIN](https://www.cs.upc.edu/~gavalda/papers/adwin06.pdf), [Page–Hinkley](https://en.wikipedia.org/wiki/CUSUM), e [DDM](https://link.springer.com/chapter/10.1007/978-3-540-28645-5_29); tutte queste rilevano che la distribuzione di un flusso onlineM SK1 è cambiata abbastanza da rendere il modello esistente non più affidabile. I tre controlli qui sono i dominios - segnali specifici di dérivaMSC4 dériva dell'identità del network (IPMS), dérività di volume ( tasso MS), dérive comportamentale SSM9 centroidismo del percorso SSM10

**Skip-osservazione del percorso.** Anche quando la porta trascende il tubo di tubatura, il middleware registra ancora la richiesta:

```csharp
_watchdog.RecordObservation(precomputedSig, clientIp, pathStr);
_ = _signatureCoordinator.NotifyObservationAsync(
    precomputedSig, pathStr, v.BotProbability, context.RequestAborted);
```

Il fatto che la detezione sia stata trascurata non crea un buco nella storia di interimpronta digitale per -; clustering, drift detection, and the dashboard's per- fingerprint stats see every request whether or not the pipeline ran.

**Refresh sampling.** `ShouldRefresh` abbassa in maniera determinista una frazione configurabile di Skip- richieste elettribili a Bias così che il pipeline funzioni e rifreschi l'archivio in diretta. L'hash della firma decide quali richieste vengono rifrescate M SK2`DeterministicBucket.ShouldFire`), così i ritratti dal stesso cliente atterrano identicamenteM SK1 nel corso del tempo ogni impronta digitale riceve una revisione completa periodica

> **ML a parte.** Questa è una versione deterministica. **ε-esplorazione greda** dall'apprendimento di rafforzamento: la maggior parte del tempo sfrutta la politica attuale (SkipM SK2 una parte del momento paga il costo dell'esplorazione (BiasMSC4 così che il modello rimane calibratoMST5 Il determinismo con l'hash lo rende idempotenteMSV6 che conta quando lo stesso cliente si ritiriaM SV7

**Entità-caduta familiareM SK1** Quando un'impronta digitale ruota e la sua nuova identità non ha un verdetto cache da sola, la porta passa alla firma canonica della famiglia' [Anchora di un gruppo di Leiden](/blog/stylobot-fingerprint) dal modello comportamentale). Da `TryGetVerdictAsync`:

csharp
if (!_signatureCac
