StyloBot Release Series: Testing the Thing That Won't Sit Still (Italiano (Italian))

StyloBot Release Series: Testing the Thing That Won't Sit Still

Monday, 01 June 2026

//

25 minute read

Come si testa un rilevatore la cui risposta dovrebbe cambiare mentre impara? Questo post è su BDF, il formato di definizione comportamentale che rende lo StyloBot testabile : un file definisce una classe di trafficoM SK3 poi guida una riproduzione di regressioneMSC4 generatura di caricoMNK5 e un'udizione di calibrazioneMRK6

StyloBot

StyloBot Release Series

  1. Comportamento, Non identità: perché StyloBot modella i clienti comportamentalmente
  2. Comportamento-Aware ASP.NET UI: il server- restituisce la superficie su quel risultato di rilevamento.
  3. Trovare e riparare la crescita senza limiti nei servizi NET Long-Running .: la disciplina di affidabilità che mantiene il motore noioso nella produzione.
  4. Comportamento-Conoscere l'interfaccia grafica del tipoScript: ExpressM SK1 Fastify, e componenti del browser
  5. L'architettura della Sidecar: 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.: la disciplina di verificazioneM SK1 un file BDF guida la regressione , carico, e calibrazione
  8. StyloExtract - un convertitore locale di HTML per Markdown: 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

La disciplina della affidabilità è in Trovare e fissare la crescita senza limiti; il sistema di apprendimento adaptivo in Imparare a diventare più veloce; fonte a github.comM SK1scottgal/stylobot.


Come si testa qualcosa che impara?

Questa è la domanda scomoda dietro lo StyloBot, e non è retorica.

Un test convenzionale punta una funzione al posto: input X restituisce output YM SK1 Funziona quando la cosa sotto prova è stabile. StyloBot non è intenzionalmente stabile a quel livelloMSC3 Accumula prove comportamentaliMST4 una singola richiestaMSP5 il giudizio dipende dalla richiesta stessaMSR6 l'impronta digitaleMRS7 la deriva dall'ancora dell'archetipo imparatoM SR8 il comportamento della sessione MSR9 e se il sistema ha già visto abbastanza per saltare drittamente verso il percorso veloceMSL10 Ogni impronta digitale inizia a puntare alla superficie più vicina all'archeipo Msl11 la sua precedenteMRSS12 quando si atterrano nuove osservazioni si muoveMSSR13 e il movimento stesso è il segnaleMRSA14 Il conciliatore delle impronte digitali metastabili risolve un vettore rumoroso ad un'identità stabile attraverso due match di passaggi, il cui Pass MSRS16 può rivedere il Pass |MRS17 | L'allocazione |MSRS18 | MSRS19 | l'archedipo |SRS20 | il modello dell'ancraggio |SMRS21 | la superficie di derivazione \MSRS22 | e le impronte digitale metastabile sono ricoperte Imparare a diventare più veloce.)

Il verdetto per la richiesta 20, allora, non è una funzione della richiesta ♫20. E' una funzione delle richieste ♫ 1 attraverso ♫ МSK4 Quindi l'obiettivo di test non è affatto una richiesta . È il comportamento del sistema su una sequenza di queste

Questo non è possibile. Assert.Equal. La domanda che un test può porre non è più " richiede X risposta giudizi YM SK2 Diventerà ♫" data questa classe di comportamento ♫ , il sistema si converge alla risposta giusta ♫

Gli analogi .NET più vicini sono: Verify e FsCheck, ma il BDF non è né uno né l'altroM SK1 Verify approva un artefatto , poi fallisce nel costruire su qualsiasi diffusivo preciso. FsCheck asserts properties over randomly generated inputsMNK4 Il BDF si trova tra loroMMK5 l'artefatto approvato è una definizione comportamentaleMBK6 ma la condizione di passaggio è probabilisticaMGK7 Non " ha corrisponso il risultato a questo scatto riproduttivo??" ma SNK10 questa distribuzione si converge al lato attenduto della confine?MNK11 con i segnali giusti presenti.?"

Una definizione comportamentale, tre sistemi di verifica.

Il trucco che rende questo file BDF risolvibile: non è un caso di prova. È una Contratto comportamentale eseguibile, una definizione di come si comporta una classe di cliente . Scritto una volta, che un file è consumato in tre modiM SK3

  1. Riprodotto attraverso il righello di integrazione, una richiesta alla volta contro l'orchestratore reale, per catturare il segnaleM SK2 rigressioni di flusso che la suite dell'unità non riesce a vedere
  2. Re-sampled sotto k6, molti utenti virtuali contemporaneamente disegnano un nuovo tempo dalla stessa definizioneM SK1 per generare una carica realistica.
  3. Controllato contro i segnali una corsa vera misurata in realtà, per catturare il drift di calibrazione.
flowchart TD
    classDef def fill:none,stroke:#3b82f6,stroke-width:2px
    classDef rig fill:none,stroke:#a855f7,stroke-width:2px
    classDef out fill:none,stroke:#22c55e,stroke-width:2px

    BDF["BDF behavioural definition<br/>clientProfile · timingProfile<br/>requests · evidence · labels"]:::def

    Replay["Integration replay<br/>slim form · real orchestrator<br/>cache disabled · identity reset"]:::rig
    K6["k6 load harness<br/>full form · re-sampled per VU<br/>burst + jitter"]:::rig
    Calibration["Calibration audit<br/>claimed evidence<br/>vs measured signals"]:::rig

    Signals["Signal-flow regressions caught<br/>merged ev.Signals reaches<br/>dashboard · persistence · threat report"]:::out
    Metrics["Load envelope verified<br/>latency · detection_rate<br/>burst_detected"]:::out
    Drift["Calibration drift surfaced<br/>stale claims · aged signatures<br/>moved detection surface"]:::out

    BDF --> Replay --> Signals
    BDF --> K6 --> Metrics
    BDF --> Calibration --> Drift

Rigressione, carico, e calibrazione sono di solito tre sistemi di test con tre fonti di verità che si allontananoM SK2 Qui ci sono tre letturas di un file . Il resto del post è ciascuna lettura a turnoMSC4

Perché gli esami delle unità perdono la classe di fallimento?

La prima versione di questo lavoro aveva centinaia di test per ogni unità di rilevatore con contesti modellati e titoli in canne.. They were fast, deterministic, and blind to the failure class I cared about

L'orchestratore fonde i contributi in un singolo. ev.Signals dizionario che i consumatori sottocorrenti (dashboard, persistenzaM SK2 narrative builder , threat reportMST4 reading fromMSC5 Un refactore che scende primary_signature dalla superficie fusionata non va male per-test dell'unità di rilevatore:il rilevatore continuava a funzionare,la contribuzione continuava ad trasportare il segnale,ha semplicemente smesso di raggiungere chiunque ne avesse bisogno.Il pannello dashboard'l'impronta digitale del tavolo è vuoto.La persistenza salta fuori dalla sequenza.L'unidad suite rimane verde perché nessuno si avvicina alla fusione.

Questo non è un'ipotesi.. Signature-documento di contratti registra esattamente questa regressione: un cambiamento che ha fermato la fusione dei segnali " ha sopravvissuto per sei giorni in produzione nonostante 1957 sia passato gli esperimenti di unitàM SK3 perché evidence.Signals."

Il test di integrazione a BdfReplayTests.Integration.cs è diretto sul perché esiste:

Questo rigo esiste perché la classe di fallimento che cattura ( i consumatori in aval ev.Signals si degrada silenziosamente quando l'orchestratore smette di fondere i segnali) non fa fallire nessun test di unità.

L'orchestratore non è una funzione; è un tubo di tubature il cui valore è quello che viene fuori dalla fusione dopo che ogni contributo ha fatto funzionare. L'unico modo per affermarlo è fare una richiesta reale attraverso un vero orchestratore e sondare la superficie fusionataM SK2 La mozione della fusione sconfigge l'esperimento

BDF: una definizione comportamentale, non un test scritto

Un file BDF (Behavioural Definition Format) descrive come un Classe Le parti interessanti del schema sono statistiche: un profilo cliente che cattura l'identità di distribuzione, un profil temporale che definisce una scoppiatura - conM SK5 la regola di campionamento di vibrazioni piuttosto che dei ritardo fissoMSC6 una serie di prove di predicati ponderati sui segnali comportamentaliMST7 e una fiducia precedenteMst8 Qui c'è una vera firma MSt9bot-signatures/python-requests-bdf.json):

{
  "scenarioName": "python-requests-bdf",
  "scenario": "A bot/scraper using python-requests/2.31.0 with specific behavior patterns.",
  "confidence": 0.85,

  "clientProfile": {
    "userAgent": "python-requests/2.31.0",
    "cookieMode": "none",
    "headerCompleteness": "minimal",
    "clientHintsPresent": false,
    "robotsConsulted": false
  },

  "timingProfile": {
    "burstRequests": 10,
    "delayAfterMs":      { "min":   20, "max":   150 },
    "pauseAfterBurstMs": { "min":  500, "max":  2000 }
  },

  "requests": [
    { "method": "GET",  "path": "/",                "expectedStatusAny": [200,301,302], "expectedOutcome": "indexing", "successCondition": "any 2xx" },
    { "method": "HEAD", "path": "/admin",           "expectedStatusAny": [200,403],     "expectedOutcome": "indexing", "successCondition": "any 2xx" },
    { "method": "GET",  "path": "/api/data?page=1", "expectedStatusAny": [200,403],     "expectedOutcome": "indexing", "successCondition": "any 2xx" },
    { "method": "GET",  "path": "/api/data?page=2", "expectedStatusAny": [200,403],     "expectedOutcome": "indexing", "successCondition": "any 2xx" },
    { "method": "GET",  "path": "/api/data?page=3", "expectedStatusAny": [403,404],     "expectedOutcome": "indexing", "successCondition": "any 4xx" }
  ],

  "labels": ["Scraper", "RobotsIgnore"],

  "evidence": [
    { "signal": "interval_ms_p95", "op": "<", "value": 200,           "weight": 0.35 },
    { "signal": "requestInterval", "op": "<", "value": "burst <150ms", "weight": 0.70 }
  ],

  "patterns":  { "requestInterval": "burst <150ms" },
  "reasoning": "The bot/scraper uses python-requests/2.31.0 to access various endpoints, including the root path and admin pages, while also enumerating API paths and testing different HTTP methods."
}

La maggior parte della superficie è statistica, e la maggior parte è ciò che rende il BDF un Definizione Invece di un codice di testo. (Il schema del campo completoM SK2parlo diMSC3 è docs/bdf-v2-schema.json.)

confidence non è un'affermazione. 0.85 dice " questo dovrebbe atterrare in alto- il robot di fiducia quando il sistema è sanoM SK3 Il rig non controlla che il punteggio maturo sia uguale a 0.85; controlla come il verdetto atterri sulla parte del bot della confineMSC5 Il precedente è la fascia la firmaMST6 l'autore SST7LLM o l'uomoM ST8 pensa che il sistema dovrebbe raggiungereMSS9 La deriva qui è una storia di calificazioneMSST10 non un'unitàMSR11un fallimento di provaMRS12

clientProfile è una categoria di cliente. cookieMode: none E' una cosa importante. categorie di comportamento (no cookie jarM SK1 ogni richiesta inizia fresca), non un'etichetta specifica . headerCompleteness: minimal dice " una richiesta di questo cliente porta solo quello che le biblioteche della classe - hanno stabilitoM SK2 un fatto sulla popolazione delle richieste che questo cliente emette, non una lista fissa di titoliMSC4 Il convertitore k 6 li materializza in pacchetti di titoli al tempo di esecuzioneMNK6 il BDF conserva un po' diverso. di cliente.

timingProfile è una regola di campionamento. burstRequests: 10 più. delayAfterMs: {min: 20, max: 150} più. pauseAfterBurstMs: {min: 500, max: 2000} definisce un generatore: dieci richiedi con un'uniformitàM SK1 spazi casuali 20 a 150msMSC4 poi un'unità- pausa casuale da 500 ad |2000ms~, ripetereMNK9 Lo stesso BDF ripetuto due volte produce due diversi flussi di richiede con la stessa distribuzione statisticaMST10 Questo è il comportamento reale StyloBotMSSK11 il rilevatore di periodicità e la sessione\MST12 il compactore del vettore stanno cercando di riconoscereMSS13 un vettore di ritardo fisso avrebbe testato una distribuzione diversa interamente\MSS14

evidence è un predicato ponderato sui segnali. Ogni annota è una richiesta del modulo. signal OP value, weight w. {signal: "interval_ms_p95", op: "<", value: 200, weight: 0.35} dice "l'intervallo di richiesta per questo scenario dovrebbe essere sotto 200msM SK4 e questo è il valore 0.35 del verdetto". Il BDF sostiene a livello statisticoMSC7 non M"request P7 returned botMSL10trueMLS11 ma MSSL12la popolazione che questo cliente genera dovrebbe produrre una distribuzione di intervalli la cui pMSSL13 scende sotto \200ms\MSL15 Un scenario la cui prova afferma di essere diverso da quello che misura il sistema in funzione è una firma che si è buttata fuori dalla calibrazioneMSS16

labels sono taxonomia. [Scraper, RobotsIgnore] è la classe per cui il scenario è stato generato per . Selezione del scenario di drive delle etichette nell'attrezzatura di carico (run solo Scraper scenario, escludere RobotsIgnore) senza che nessuno scrivesse un regex sui nomi del scenario.

requests descrivere cosa fa il cliente, non quello che dovrebbe succedere dopo. expectedStatusAny: [200, 403] tollera una fetta successiva o un blocco ravvicinato, perché entrambi sono produzioni valide di una sonda del percorso ostile. expectedOutcome: indexing E' l'elemento più importante. l'intenzione del cliente' (enumerare le pagine APIM SK1 non il server' la rispostaMSC3 successCondition: "any 4xx" su /api/data?page=3 è il cliente' l'euristica per " ha fatto questo lavoroM SK2 un scraper che riceve 4xx sulla terza pagina ha successo nel suo lavoro di recensionamento ♫( ha scoperto la scoglieraMSC5 Il BDF cattura l'assimmetria tra quello che il cliente sta cercando di fare e ciò che il sistema dovrebbe fare a riguardoMST6

Un BDF e un centroide: la stessa ideaM SK1 invertito.

La parola. Definizione e collega il BDF al concetto al centro del rilevatore del StyloBot. Il motore classifica il traffico contro i comportamenti. Centroidi: punti di riferimento nello spazio dimensionale 130+ da Comportamento, Non identità, ciascuna è un'ancora imparata per una classe di clienti che si muovono allo stesso modo.

Un BDF descrive la stessa cosa ( una classe di cliente) con il ruolo invertitoM SK2 Il centroide è il Ricognizione: chiede " questa richiesta assomiglia a quella classe? generatore.: risponde "produce un flusso di richiesta da quella classeM SK2 Lo stesso comportamento, direzione oppostaMSC4 che è esattamente quello che rende un BDF riproducibile e un centroide nonMNK5

Il motore's SignatureToBdfMapper bridges the two: prende una firma comportamentale catturata dal traffico reale e la scrive fuori come un BDF che si può leggere e ripetere. Un LLM fa lo stesso lavoro dall'altro latoM SK2 trasformando la descrizione di un attaquanto in una nuova . Che dà un circuitoMSC4 il traffico osservato diventa una firmaMNK5 la firma diventa un BAFMRK6 e il BDF può ripeterne il comportamento che l'ha prodottoMسک7

Quindi bot-signatures/ Il corpus non è solo un sistema di prova; è una biblioteca di definizioni comportamentaliM SK1 un file per classe di cliente StyloBot ragioni su. Le signature lì sono state generate da un modello (ministral-3:3b, per il directoryM SK1s README) dati spunti che descrivono una famiglia di clienti che colpisce un insieme di punti finali , e la stessa superficie è aperta ad un autore umanoMSC4 Scrivere un BDF per una nuova famiglia di smascheratori e avete una definizione comportamentaleMNK5 uno scenario di regressioneMZK6 e una caricaMMK7 un test inserimento in un singolo fileMRK8

Una forma di riproduzione più bassa, -, vive sotto. test-suites/{bots,humans,adversarial}/*.bdf.json mantenendo solo requests[].method/path/headers/delayAfter In più, un soft. expectedDetection. Quel sottoset è quello che il righello dell'integrazione mette al punto finale di riproduzione, che non ha modo di sintetizzare il comportamento di distribuzione da un singolo riproiettamento ( nessun VU simultaneoMSC3 solo loopbackM SK4 quindi TLSMNK5 le dimensioni delle impronte digitali TCP si degradano con la costruzioneMRK6 La forma più ricca guida l'attrezzatura della caricaMMK7 dove i VU simultani possono effettivamente realizzare la distribuzione del tempoMسک8

Il punto finale della riproduzione passa attraverso l'orchestratore reale.

L'impianto di integrazione mette ogni scenario in POST /bot-detection/bdf-replay/replay, un punto finale che vive nel prodotto (BdfReplayEndpoints.cs), non nell'esperimento di provaM SK1 Il posizionamento è carico-portareMSC3 il punto finale si risolve IDetectionOrchestrator dal DI e va attraverso qualunque orchestratore sia attualmente registrato, sotto DetectionPolicy.Default. La versione precedente ha codificato un orchestratore specifico e ha mascherato le regressioni nell'alternativa.

Poiché il punto di fine vive nel prodotto, è confuso come una superficie del prodotto . BdfReplay è spento per default; quando è attivato, le chiamate richiedono un valido X-BdfReplay-Api-Key header e passare un per-IP-limito di tasso, così che il percorso di riproduzione non sia un bypass di rilevamento rimasto in giroM SK2 L'attrezzatura authentica con quella tastiera . Il punto è di esercitare la strada reale

flowchart LR
    classDef test fill:none,stroke:#3b82f6,stroke-width:2px
    classDef proc fill:none,stroke:#a855f7,stroke-width:2px
    classDef out fill:none,stroke:#22c55e,stroke-width:2px

    Test["BDF replay rig<br/>authenticated with API key"]:::test --> Endpoint["Product replay endpoint<br/>identity reset · cache disabled"]:::proc
    Endpoint --> Orch["Current DI-registered<br/>orchestrator"]:::proc
    Orch --> Signals["Merged ev.Signals"]:::proc
    Signals --> Assert["Contract assertions<br/>verdict · signal probes<br/>convergence bound"]:::out

Una politica deliberata sovraride: il cache per la signature verdict è disattivato per ripartire.

var replayPolicy = Policies.DetectionPolicy.Default with
{
    SignatureCache = Policies.DetectionPolicy.Default.SignatureCache with { Enabled = false }
};

Il cache's Skip path bypasses the matcher entirely once a primary signature has a confident cached verdictM SK1 In produzione questo è esattamente quello che volete (it is the whole point of Imparare a diventare più veloce); per un rig che misura l'accuratezza di rilevamento e il flusso del segnale nasconde il comportamento di richiesta per ogni -request che il rig sta cercando di applicare a

I scenari sono anche isolati l'uno dall'altro. Ogni scenario riceve una unica IP sintetica derivata da un xxHash deterministico di nome (a 192.0.x.y L'indirizzo del RFC 5737 TESTM SK1 NET range), quindi la reputazione subnettaMSC3 non sanguina mai tra i scenari . E il rig chiama POST /bot-detection/bdf-replay/reset-identity prima di ogni scenario per truncare l'impronta digitale ; senza questo , scenario N eredita gli scenari di impronte digitali 1..N-1 creati e le affermazioni di stabilità per M-scenario diventano ordinantiM SK5dipendenteMSC6

Affermazione su una superficie non deterministica

Il rig fa tre affermazioni su ogni scenario. Ognuno è un modello per testare sistemi come questo.

Verdetto maturo, non per-verdetto di richiestaM SK2 Esisteno scenari di bot last.Actual.IsBot è vero; i scenari umani sostengono che la maggioranza. di richieste classificate come umane. Affermare su ogni singola richiesta associasse l'esperimento alla traiettoria di deriva lontano dall'ancra dell'archetipoM SK1 rilassarsi a " fissata alla fine " testare il contratto realeMSC4

// Some heuristics legitimately escalate on outlier rates; assert majority human, not all.
var humanCount = response.Results.Count(r => r.Actual is { IsBot: false });
var botCount = response.Results.Count - humanCount;
Assert.True(humanCount >= botCount,
    $"{response.ScenarioName}: {botCount}/{response.Results.Count} requests classified as bot, " +
    $"expected majority human. Last verdict: {last.Actual!.RiskBand} prob={last.Actual.BotProbability:F2}");

Probe segnali chiamate, non conta i segnaliM SK1 Il flusso di segnali viene analizzato per chiave, non in totale. L'affermazione di conteggio è fragileM SK2 un nuovo rilevatore che emette un nuovo segnale maschera la perdita di uno esistante critico ( il conteggio rimane lo stessoMSC4 sparitoMST5 l'identità della chiave è invisibileM ST6 Una sonda per M st7 identifica il consumatore che si rompe MST8 nel messaggio di fallimentoMst9

Assert.True(probes.TryGetValue(SignalKeys.PrimarySignature, out var hasSig) && hasSig,
    $"{scenarioName}: {SignalKeys.PrimarySignature} missing from ev.Signals — " +
    "RequestPersistenceService skips persistence, dashboard fingerprint table goes blank");

Il messaggio di fallimento è il test's spec. Tre mesi dopo non dovete ricordare perché il segnale ha avuto importanzaM SK2 l'affermazione vi dice

Convergenza limitata, non uguaglianza accurataM SK1 Il conciliatore metastabile delle impronte digitali risolve un vettore rumoroso ad un'identità stabile. La composizione del vettore include le dimensioni di sessione (entropia del percorsoM SK2età della sessione ) che si sposta per richiestaMSC4 così che il corrispondenzamento dei due passeMNK5 può occasionalmente cadere fuori dalla sua banda libera e allocareMMK6 Affermare su un singolo id di impronte digitale per tutte le richieste sarebbe sbagliatoMEK7 Affirmare MNK8no holeMK9 e convergere a non più di ceil(N/2) impronte digitali distinte" è il contratto reale.

var distinctFps = withFingerprints
    .Select(r => r.Actual!.IdentityFingerprintId!)
    .Distinct(StringComparer.OrdinalIgnoreCase)
    .Count();
var allowed = Math.Max(1, (int)Math.Ceiling(response.Results.Count / 2.0));
Assert.True(distinctFps <= allowed,
    $"{scenarioName}: {distinctFps} distinct fingerprints across {response.Results.Count} requests " +
    $"(allowed {allowed}). The matcher isn't converging — every request is allocating new, suggesting " +
    "vector composition is unstable or LooseThreshold is unreachable.");

ceil(N/2) non è magico. codifica una politica: la prima richiesta assegna sempre l'allocazioneM SK2 le richieste successive dovrebbero più o meno corrispondere a L 1 confermare o Pass MSC4 L'allocuzione occasionale sotto alta varianza di percorso è accettabile ogni anno. la richiesta è una regressione. Il confine è abbastanza libero da assorbire il rumore il conciliatore è progettato per assorbirlo, abbastanza stretto da catturare il modo di fallimento dove smette di convergere a tutti

Tutti i tre modelli condividono una proprietà: affermano sul contratto che il comportamento dovrebbe essere soddisfatto , non sui numeri specifici che la attuale implementazione produceM SK2 Quando la implementzione cambia, l'esperimento è ancora valido se il contratto lo è ancoraMSC4 Questo è ciò che rende stabile un test non deterministicoMST5Mst6

Spegnere: lo stesso corpus sotto pressione

Un file BDF è solo JSON. Il righello di integrazione consuma la forma flessibileM SK1 La presa di carico consuma la completa forma statistica: scripts/convert-bdf-to-k6-v2.csx legge un directorio di firma e emette uno script k6 che Re-esempi Ogni firma's distribuzione per VU per iterazione.

Re-esempi sta facendo un lavoro reale in quella frase . Il script kM SK2 non ripete una traccia catturata; sta realizzando clientProfile e timingProfile come generatore vivente. Ogni iterazione VU sceglie una firma, costruisce i titoli dalla propria. headerCompleteness e clientHintsPresent flags, attacca un barattolo di biscotti che corrisponde al suo cookieMode, prende robots.txt Se robotsConsulted è vero, poi disegna nuovi per-delitti di richiesta da delayAfterMs.min..max e una nuova pausa inter-- pauseAfterBurstMs.min..max. Due VU con la stessa firma emetteno due diverse correnti di richiesta con la misma distribuzione statistica, che è esattamente quello che il tubo di rilevamento dovrebbe riconoscere come uno. un po' diverso. di cliente.

// Main test function - each VU picks random scenario and replays with burst/jitter.
// Multiple VUs running concurrently provide natural request interleaving.
export default function() {
    const sig = signatures[Math.floor(Math.random() * signatures.length)];
    // ... robots.txt, cookie jar, header bundle built from sig.clientProfile ...

    for (let i = 0; i < sig.requests.length; i++) {
        const req = sig.requests[i];
        const url = `${TARGET_URL}${req.path}`;
        const headers = buildHeaders(req.headers || {}, sig.clientProfile);
        const res = http.request(req.method, url, null, params);

        if (sig.timingProfile) {
            if (requestCount < sig.timingProfile.burstRequests) {
                sleep(randomBetween(
                    sig.timingProfile.delayAfterMs.min / 1000,
                    sig.timingProfile.delayAfterMs.max / 1000));
            } else {
                sleep(randomBetween(
                    sig.timingProfile.pauseAfterBurstMs.min / 1000,
                    sig.timingProfile.pauseAfterBurstMs.max / 1000));
                requestCount = 0;
                burstRate.add(1);
            }
        }
    }
}

La proprietà del titolo: Il corpus che testate per la correttezza è il corpus che stressate per il risultato.. Non ci sono passazioni per i test di integrazione, ma il traffico di produzione non assomiglia ai test di integrazione. Si tratta di un sistema multifunzionale. il generatore di traffico. Quando un cliente segnala una famiglia di bot mancante, aggiungete un BDF e si unisce sia alla suite di regressione che al test di caricoM SK2 Nessuna traduzione , nessuna seconda fonte di veritàMST4 nessun driftMSS5

Le metriche k6 parlano la stessa lingua che la superficie del BDF.

kM SK1 metrologico Cosa misura
bot_scenarios / human_scenarios Contatori per classe di scenario
detection_rate Frazione di scenari della classe botM SK1 segnati al bordo
interval_ms InterM SK1trend di gap request; controlla il profilo temporale mantenuto sotto carico
sensitive_path_rate Frazione di richieste colpite /admin, /api, file di punti
burst_detected I colpi di confine di scoppio derivati dal profilo del tempo
http_req_duration Istogramma standard della latenza per i soglia di p95/pM SK2

I limiti su questi diventano uno specifico esecuzionebile per l'enveloppe di carico:

thresholds: {
    http_req_duration: ['p(95)<1000'],
    http_req_failed: ['rate<0.1'],
    'detection_rate': ['rate>0.3'],
},

Un refactore che regressisce l'accuratezza della rilevazione sotto carico (la guardia del cache del verdicto chiede di sfuggire alle visite. detection_rate. Un refactore che introduce un percorso lento durante i viaggi di contenzione. http_req_duration p95. I dati della stessa fonteM SK1 sono state catture due regressioni.

Calibrazione: la terza applicazione dello stesso file

A questo punto il BDF ha già fatto due compiti: ha controllato l'orchestratore' il contratto di segnale sotto riproduzioneM SK2 e generato una carica realistica sotto kMSC3 Il terzo uso è quello che trovo più interessante , perché qui il BAF viene controllato contro da sé stesso..

Ogni elemento di prova è una richiesta del modulo. signal OP value, weight w. Una volta riproiettata una firma ( sotto loopback o kM SK2 il sistema ha prodotto una distribuzione misurata per gli stessi segnali interval_ms_p95 < 200 La domanda è controllabile contro il p95. misurato. cookie_count >= 2 è controllabile contro il conteggio dei cookie della richiesta realmente portata. header_count >= 8 è controllabile contro i titoli che sono stati atterrati. M SK1I segnali che possono apparire in evidence sono enumerati nel BDF v2 schema: interval_ms_p95, interval_ms_p50, sensitive_path_rate, error_rate, burst_detected, header_count, cookie_count.)

Quando le misurazioni divergeno da quelle sostenute, la firma è fuori calibrazione. O è stata sovra-specificata per il sistema contro cui è stata approvataM SK2 o il sistema si è spostato sotto di esso . Entrambi sono utiliMSC4 la prima dice di riproducere la firma dall'osservazioneMNK6 la seconda dice che un refactore ha spostato la superficie della detezione in modo che nessun test funzionale avrebbe catturato.

L'informazione figura nella parte dispositiva. python-requests-bdf.json mostrato prima è un piccolo esempio dal vivo del perché la revisione è necessaria per niente. La seconda fila della prova porta una stringa value ("burst <150ms") dove il schema richiede un numero, e la requestInterval Il segnale che si chiama non è affatto nell'enum delle prove. Un modello ha scritto che la sequenza , e nessun test di unità la respingeM SK2 Solo una corsa misurata che confronta le prove sostenute contro i segnali osservati supera una affermazione che prima di tutto non era mai controllabile .

Questo trasforma il BDF da un artefatto di regressione in un artefacto di calibrazione. Le firme sotto bot-signatures/ se LLM- è stata generata contro una versione precedente del tubo di rilevatore; le loro affermazioni di prove codificano ciò che che Version thought distinguished each client family. Re-la calibrazione in corso oggi vi dice quali revendicazioni sono ancora validi e quali sono ormai vecchieM SK2 nello stesso modo in cui un'ancora archetica a cui nulla si è avvicinato per settimane cessà di essere utile primaMSC3 Il corpo stesso -

Sei regole per testare sistemi non deterministici.

I sistemi non determinanti non richiedono test non determinativi1;MSK3MSK4MSK5MSK6MSK7MSK8MSK9MSK10MSK11MSK12MSK13MSK14MSK15MSK16MSK17MSK18MSK19MSK20MSK21MSK23MSK24MSK25MSK26MSK28MSK27MSK29MSK30MSK31MSK32MSK33MSK35MSK34MSK36MSK42MSK43MSK44MSK45MSK46MSK47MSK48MSK52MSK49MSK50MSK51MSK54MSK55MSK53MSK56MSK58MSK59MSK60MSK64MSK65MSK66MSK62MSK63MSK67MSK69MSK70MSK61MSK71MSK72MSK68MSK75MSK80MSK76MSK86MSK88MSK90MSK87MSK81MSK83MSK84MSK89MSK85MSK99MSK91MSK96-MSK40MSK41MSK94MSK95MSK98MSK97MSK101MSK100MSK00MSK39MSK82MSK92MSK93MSK04MSK22MSK38MSK37MSK74MSK57MSK73MSK77MSK78MSK79MSKXMSK07MSK09MSK08MSK110MSK01MSK05MSK03MSK06MSK105MSK120-MMK10Msk10MMK11MNK11MMK12MMK60MK11Msk0MMK0Msk11MRK12Msk12MNK10MZK11M SK10MNK12MRK11MSC10MRK10MOKMSKJ11MK10MCK11MTK11MZKMSKMSK119MSK02MSK costruita One. I modelli che funzionano per StyloBot generalizzano

  1. Definire l'input come una distribuzione, non una traccia. A. timingProfile con min/max gaps è un generatore; una traccia di richiesta catturata è una trazione da essaM SK2 Testare contro la traccia e voi testate la trazione , non la distribuzioneMSC4 La stessa logica per il profilo cliente MSSK5il modo dei cookies e l'integrità delle sequenze descrivono una popolazioneMST6non una lista fissa di sequenziMSS7
  2. Esprime il contratto come predicati ponderati. L'informazione figura nella parte dispositiva. evidence l'array è la cosa più vicina che il sistema ha ad una unità-test assertionM SK1 e i predicati sono sopra i segnali di distribuzione (interval_ms_p95 < 200), non valori puntiniM SK1 Predicate con pesci compongono; affermazioni di uguaglianza donMSC3tMNK4
  3. Assert on the destination, not the path. Per i sistemi il cui stato è un'impronta digitale che si sposta da un archetypo precedente, per-asserzioni di passaggio coperte con l'implementazione
  4. Probe la superficie fusa, non i componenti. I modelli di classe fallimento che non riescono a catturare sono quelli in cui i componenti sono individualmente corretti ma la composizione lascia qualcosa fuori.
  5. Costruire la convergenza, non la riparare. Un corrispondente che risolve l'input rumoroso ad un'identità stabile allocaterà occasionalmente. L'affermazione è M SK1 rimane sotto il confine ", scelto dalla politica, non dai numeri osservatiMSC4
  6. Condividere il formato di input attraverso il rig, caricoM SK1 e calibrazione. Quando il scenario è un contratto comportamentale eseguibile piuttosto che uno script, lo stesso file guida una righistica di regressione, un'attrezzatura perfM SK2 e una revisione di calibrazione . Il corpus non si divide

Dove questo si inserisce nella serie di release?

Trovare e fissare la crescita senza limiti La memoria è ristretta; Imparare a diventare più veloce Made Repet Detection cheap. This is the third leg: verifying a system whose output won 't sit stillM SK3

Il metodo è un unico corpus. Le stesse signature BDF guidano la regressione, il caricoM SK2 e l'etalonizzazioneMSC3 così la manutenzione di test si trasforma in manutenzione di corpus . Una nuova firma è qualcosa che un LLM può aiutare a disegnare da una descrizione di un attaquanto

Quindi questa è la risposta a "come si può testare qualcosa come il StyloBot?" Non lo si congelaM SK2 si definisce una classe di traffico , si fa funzionare il sistema reale contro di esso , e si verificano tre coseMSC5 che il verdetto si converge ancoraMska6 che i segnali di cui i consumatori dipendono arrivano ancora, e che le prove che la definizione afferma ancora corrispondono a quelle misurate durante l'esecuzioneMST8 L'esame non è un pinMst9 È un circuitoMSt10


L'attrezzatura di riproduzione del BDF vive a BdfReplayTests.Integration.cs. Scenari di riproduzione sottili sono sotto test-suites/{bots,humans,adversarial}/*.bdf.json; le complete firme statistiche (con clientProfile, timingProfile, evidence) sono sotto bot-signatures/*.json. Il convertitore k6 è scripts/convert-bdf-to-k6-v2.csx. Il punto finale di riproduzione che entrambi usano è BdfReplayEndpoints.cs. Il contratto di segnale che proteggono questi test è documentato in docs/architecture/signal-contracts.md. Tutte le fonti a github.comM SK1scottgal/stylobot. Motore viventeM SK1 Armatura dashboard, e controlli commerciali a stylobot.net.

Finding related posts...
logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.