-----------------------------------

Acquista i software ArcGIS tramite Studio A&T srl, rivenditore autorizzato dei prodotti Esri.

I migliori software GIS, il miglior supporto tecnico!

I migliori software GIS, il miglior supporto tecnico!
Azienda operante nel settore GIS dal 2001, specializzata nell’utilizzo della tecnologia ArcGIS e aderente ai programmi Esri Italia Business Network ed Esri Partner Network

-----------------------------------



venerdì 23 ottobre 2026

Cache Engineering for AI Agents

NICOGIS TECH WEEKLY · DEEP DIVE · 23 OTTOBRE 2026

Cache Engineering for AI Agents: KV Cache, Position Encoding, Prefill Optimization e Inference Serving

Un agente che esegue centinaia di tool call non consuma risorse soltanto quando genera token. Ogni osservazione allunga la traiettoria, può aumentare lo stato di attenzione da conservare e rende più costoso il successivo prefill. Il problema diventa: quale stato computazionale conservare, riutilizzare, eliminare o riposizionare senza compromettere la correttezza?

Questo deep dive parte dalla matematica dei transformer, analizza il paper Cache Engineering for Agents e arriva a un laboratorio riproducibile con vLLM e C#/.NET 10. Distingueremo rigorosamente il prefix caching già disponibile nei server di inferenza dalle operazioni sperimentali drop e repos proposte da AKV.

1. La traiettoria di un agente è diversa da una chat

In una chat tradizionale il contesto aumenta con messaggi relativamente brevi. In un agente, una singola tool response può introdurre migliaia di token: log, file sorgente, risultati di ricerca, JSON, documenti. Un ciclo LLM → tool → observation → LLM ripetuto decine o centinaia di volte amplifica il problema.

System + task + tools
        |
        v
     Agent LLM ----> Tool A ----> Observation A
        |                            |
        +----------------------------+
        |
        +-------------> Tool B ----> Observation B
        |                            |
        +----------------------------+
        |
        v
   Next prefill / decode

È necessario separare quattro grandezze: token della cronologia logica, token realmente elaborati durante il prefill, byte di KV cache allocati in GPU e byte trasferiti fra GPU, CPU e storage. Una API commerciale può fatturare token cached senza esporre la KV cache fisica. Un inference server self-hosted può riutilizzare blocchi KV senza cambiare il testo del prompt.

2. KV cache: matematica e dimensionamento

In un transformer autoregressivo, per ogni layer si calcolano le proiezioni Q = XWq, K = XWk e V = XWv. La self-attention causale è, schematicamente, softmax(QKᵀ / sqrt(d) + mask) V. Durante il decode i token precedenti non cambiano: ricalcolarne ogni volta key e value sarebbe uno spreco. La KV cache conserva queste rappresentazioni per riutilizzarle.

Per L layer, Hkv KV head, dimensione D per head, N token e B byte per elemento, una stima è:

M_KV ≈ 2 × L × Hkv × D × N × B

Il fattore due rappresenta K e V. Consideriamo 32 layer, 8 KV head, dimensione 128, BF16 (2 byte), 32.768 token:

2 × 32 × 8 × 128 × 32768 × 2
= 4,294,967,296 byte ≈ 4 GiB per sequenza

Otto sequenze indipendenti di quella lunghezza richiederebbero circa 32 GiB di sola KV cache, prima di pesi del modello, buffer, frammentazione e altre allocazioni. GQA e MQA riducono il numero di KV head; la quantizzazione KV riduce i byte per elemento ma introduce vincoli di precisione e compatibilità. La dimensione massima di contesto pubblicizzata dal modello non coincide con la capacità di servire contemporaneamente molte richieste a quella lunghezza.

3. Prefill e decode: due workload differenti

FaseAttivitàCollo di bottiglia tipicoMetriche
PrefillElabora il prompt e costruisce KVCompute e lunghezza inputPrefill latency, TTFT
DecodeGenera token incrementaliBandwidth, letture KV, batchingITL, output token/s
SchedulerDistribuisce risorse fra richiesteMemoria, queueing, preemptionP95/P99, queue time

Il Time to First Token osservato dal client include rete, eventuale coda, prefill e prima emissione. Ridurre il prefill può migliorare il TTFT senza incidere proporzionalmente sulla durata totale di una risposta lunga. Il continuous batching permette di introdurre nuove richieste mentre altre sono in decode; il chunked prefill suddivide prompt lunghi per condividere meglio la GPU.

4. RoPE: perché eliminare token non risolve il problema delle posizioni

Le Rotary Position Embeddings (RoPE) codificano la posizione ruotando query e key: q′p = R(p)qp e k′p = R(p)kp. Per ogni coppia di coordinate, R(p) è una rotazione il cui angolo dipende dalla posizione e dalla frequenza. Il prodotto scalare fra query e key incorpora così una relazione posizionale.

Supponiamo di avere una traiettoria di 100.000 token e di eliminare dalla cache gli stati di 80.000 token intermedi. La cache attiva è più piccola, ma le posizioni dei token rimasti possono essere ancora molto elevate. Indice logico della traiettoria, numero di elementi KV attivi e indice RoPE non sono la stessa cosa.

Nel caso RoPE standard, un key già ruotato alla posizione p può essere idealmente trasportato alla posizione q applicando:

k′q = R(q) · R(p)^(-1) · k′p

È una proprietà della composizione delle rotazioni. Non implica però che un runtime generico possa spostare arbitrariamente tutti gli stati KV senza conseguenze: gli hidden state dipendono dalle informazioni che erano visibili quando sono stati calcolati, e varianti di RoPE possono avere requisiti differenti.

Errore concettuale frequente: ricollocare un key nella dimensione posizionale non equivale a ricalcolare il token con una storia di attenzione diversa.

5. Prefix caching e PagedAttention non sono sinonimi

Il prefix caching evita di ripetere il prefill quando due richieste condividono lo stesso prefisso tokenizzato. È particolarmente efficace con system prompt, istruzioni sugli strumenti e documenti statici. La somiglianza semantica non basta: serve l'identità dei token e degli altri fattori rilevanti per la cache.

Request A: [system][tools][document][question A]
Request B: [system][tools][document][question B]
            +----- shared prefix -----+
                     cached KV

In vLLM l'Automatic Prefix Caching usa hash dei blocchi che dipendono dai token e dal prefisso precedente. La gestione comprende reference count ed eviction. La documentazione descrive anche un cache_salt opzionale per isolare gruppi di fiducia, riducendo il rischio di canali laterali temporali fra tenant.

PagedAttention affronta invece l'allocazione: divide la KV cache in blocchi fisici non necessariamente contigui, mappati a una sequenza logica. Riduce sprechi di memoria e facilita la condivisione. Prefix caching è una tecnica di riutilizzo; PagedAttention è una tecnica di gestione della memoria. Possono essere combinate.

SGLang utilizza strutture radix per il riuso dei prefissi. Nei modelli ibridi, che combinano full attention, sliding-window attention e stati ricorrenti, la semantica del riuso cambia a seconda della componente: un checkpoint ricorrente non è intercambiabile con un normale blocco KV.

6. Il paper Cache Engineering for Agents: append, drop, repos

Il lavoro Cache Engineering for Agents propone di gestire la cache direttamente, senza riscrivere continuamente la traiettoria testuale dell'agente. Le operazioni fondamentali sono:

OperazioneEffettoObiettivo
append(x)Aggiunge KV dei nuovi tokenProseguire l'esecuzione
drop(P)Esclude selettivamente KV dalla futura attenzioneRidurre contesto attivo e memoria
reposModifica gli encoding posizionali dei key conservatiCompattare le posizioni senza re-prefill completo

La differenza rispetto alla summarization è decisiva. Se si sostituisce un tratto del prompt con un riassunto, il prefisso cambia e può essere necessario ricalcolare stati successivi. Se il runtime supporta drop, può escludere porzioni della cache dalle attenzioni future senza necessariamente rigenerare gli stati conservati.

Context engineering:
[A][B][C][D][E] -> summarize -> [A][S][E]
                              prefix modified / possible re-prefill

Cache engineering:
[A][B][C][D][E] -> drop KV(B,C,D) -> active [A][E]
                              retained states may be reused
                  -> repos -> adjusted position encodings

Negli esperimenti degli autori, la policy tool-response rolling-drop ottiene +1,7 punti di score medio e −27,7% di costo medio rispetto alla summarization a soglia. AKV riporta fino a 1,2× di throughput di output e fino a 1,5× di miglioramento relativo della latenza P95 nel proprio setup. Sono risultati sperimentali specifici, non prestazioni garantite di qualsiasi inference server.

7. History dependence: stessi token, cache diversa

Questo è il passaggio più sottile del paper. Immaginiamo due richieste che contengono gli stessi token A, B e C. Nel primo caso C è stato elaborato quando B era visibile; nel secondo B era già stato escluso con drop. Il vettore KV di C può essere diverso perché è diversa la storia dell'attenzione. Di conseguenza la sola identità dei token non determina più lo stato della cache.

Il serving engine AKV, costruito su SGLang, utilizza una struttura radix estesa con nodi che rappresentano le operazioni di drop, gestione dell'eviction consapevole dei riferimenti e segment-batch attention per ricostruire stati coerenti. Il riposizionamento richiede inoltre versioni dei key con encoding posizionali appropriati. È un problema di provenienza dello stato, non semplicemente di cache hit.

8. Inference serving: scheduling, eviction e offloading

TecnicaBeneficioTrade-off
Prefix cachingEvita prefill duplicatoMemoria per blocchi riutilizzabili
PagedAttentionRiduce frammentazioneGestione tabelle di blocchi
Chunked prefillMigliora coesistenza con decodeScheduling più complesso
KV quantizationMeno byte/tokenPossibili effetti numerici
CPU offloadingMaggiore capacità effettivaTrasferimenti e latenza
Disaggregated prefill/decodeWorker specializzatiTrasferimento KV fra worker
AKV drop/reposControllo del contesto attivoRuntime specializzato e semantica complessa

In un sistema disaggregato il costo di trasferimento della KV cache può diventare di primo ordine. Un risparmio nel calcolo non garantisce quindi un miglioramento end-to-end. Occorre misurare anche traffico GPU-GPU, GPU-CPU, congestione di rete, code e preemption.

9. Laboratorio: vLLM e client C#/.NET 10

Obiettivo: confrontare richieste con prefisso invariato e prefisso variabile, misurando TTFT e latenza totale. Il laboratorio verifica il prefix caching standard di vLLM, non implementa le operazioni AKV drop/repos. Per replicare AKV è necessario il repository degli autori e l'infrastruttura SGLang modificata.

9.1 Prerequisiti e server

Occorrono una GPU NVIDIA supportata, ambiente CUDA/driver compatibile, vLLM e .NET SDK 10. Il modello seguente è relativamente piccolo ma richiede comunque VRAM adeguata. Verificare le opzioni effettive con vllm serve --help, perché possono cambiare fra versioni.

vllm serve Qwen/Qwen2.5-1.5B-Instruct \
  --host 0.0.0.0 --port 8000 \
  --enable-prefix-caching --max-model-len 8192

# In un altro terminale:
dotnet new console -n KvCacheBench --framework net10.0
cd KvCacheBench

9.2 Program.cs completo, senza pacchetti esterni

using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Text.Json;

var endpoint = Environment.GetEnvironmentVariable("LLM_URL")
    ?? "http://localhost:8000";
var model = Environment.GetEnvironmentVariable("LLM_MODEL")
    ?? "Qwen/Qwen2.5-1.5B-Instruct";

using var http = new HttpClient {
    BaseAddress = new Uri(endpoint.TrimEnd('/') + "/"),
    Timeout = TimeSpan.FromMinutes(5)
};

// Il prefisso lungo rimane identico nei casi A e B.
var shared = string.Join("\n", Enumerable.Range(0, 100)
    .Select(i => $"Regola {i:D3}: descrivi trade-off " +
        "fra KV cache, prefill, decode e scheduling GPU."));

Console.WriteLine("case,iteration,ttft_ms,total_ms,output_chars");
foreach (var testCase in new[] { "A", "B", "C" }) {
    for (int i = 0; i < 14; i++) {
        var prefix = testCase == "C"
            ? $"Variante {i:D4}\n" + shared : shared;
        var payload = new {
            model,
            stream = true,
            temperature = 0,
            max_tokens = 120,
            messages = new object[] {
                new { role = "system", content =
                    "Rispondi in italiano, in modo tecnico e conciso." },
                new { role = "user", content = prefix +
                    $"\n\nSpiega il concetto {i:D2} di KV cache." }
            }
        };
        using var req = new HttpRequestMessage(
            HttpMethod.Post, "v1/chat/completions") {
            Content = new StringContent(JsonSerializer.Serialize(payload),
                Encoding.UTF8, "application/json")
        };
        var watch = Stopwatch.StartNew();
        using var res = await http.SendAsync(req,
            HttpCompletionOption.ResponseHeadersRead);
        res.EnsureSuccessStatusCode();
        await using var stream = await res.Content.ReadAsStreamAsync();
        using var reader = new StreamReader(stream);
        double? ttft = null;
        int chars = 0;
        while (await reader.ReadLineAsync() is { } line) {
            if (!line.StartsWith("data: ", StringComparison.Ordinal))
                continue;
            var data = line[6..];
            if (data == "[DONE]") break;
            using var doc = JsonDocument.Parse(data);
            if (!doc.RootElement.TryGetProperty("choices", out var choices)
                || choices.GetArrayLength() == 0) continue;
            var choice = choices[0];
            if (!choice.TryGetProperty("delta", out var delta)
                || !delta.TryGetProperty("content", out var content)
                || content.ValueKind != JsonValueKind.String) continue;
            var fragment = content.GetString();
            if (string.IsNullOrEmpty(fragment)) continue;
            ttft ??= watch.Elapsed.TotalMilliseconds;
            chars += fragment.Length;
        }
        watch.Stop();
        if (i >= 2) Console.WriteLine(string.Join(",",
            testCase, (i - 2).ToString(),
            ttft?.ToString("F2", CultureInfo.InvariantCulture) ?? "",
            watch.Elapsed.TotalMilliseconds.ToString("F2",
                CultureInfo.InvariantCulture), chars.ToString()));
    }
}

Il programma esegue 2 richieste di warm-up e 12 richieste misurate per ciascun caso. A e B condividono il prefisso; C lo altera prima del blocco comune. Le richieste sono sequenziali per isolare il riuso, quindi non costituiscono un test di concorrenza. L'ordine A-B-C può favorire il warm-up della cache: ripetere i test invertendo l'ordine e riavviando il server fra configurazioni.

9.3 Esecuzione e protocollo

# Linux / macOS
export LLM_URL=http://localhost:8000
export LLM_MODEL=Qwen/Qwen2.5-1.5B-Instruct
dotnet run -c Release > results.csv

# PowerShell
$env:LLM_URL = "http://localhost:8000"
$env:LLM_MODEL = "Qwen/Qwen2.5-1.5B-Instruct"
dotnet run -c Release | Out-File results.csv

Ripetere con prefix caching abilitato e disabilitato, mantenendo identici modello, precisione, GPU e limiti di contesto. Eseguire più repliche indipendenti, annotare la versione del runtime e acquisire anche le metriche server. Il ttft_ms misura il primo frammento di contenuto nello stream, non il primo byte di rete. output_chars non equivale ai token generati: per token/s usare i contatori del server o un tokenizer compatibile.

10. Metriche e interpretazione corretta

MetricaSignificatoAttenzione
TTFTTempo al primo token visibileInclude rete, coda e prefill
ITL / TPOTTempo fra token successiviDipende dal batching
P50/P95/P99Distribuzione delle latenzeServono campioni sufficienti
Prefix cache hit rateQuota di prefisso riutilizzataDefinizione runtime-specifica
KV cache usagePressione sulla cacheNon è tutta la VRAM usata
Output token/sThroughput di generazioneDistinguere per richiesta e aggregato
Cost per correct taskCosto per task verificatoRichiede un valutatore esterno

vLLM espone metriche Prometheus su /metrics; nomi e disponibilità dipendono dalla versione. Fra le famiglie rilevanti: utilizzo KV, query e hit del prefix cache, richieste attive, TTFT e latenza end-to-end. Un hit rate su finestra temporale va calcolato usando variazioni dei contatori nella stessa finestra, non confrontando valori cumulativi di intervalli differenti.

La metrica decisiva per gli agenti non è il solo costo/token: costo per task completato correttamente. Una policy aggressiva può ridurre il contesto attivo e peggiorare l'accuratezza. La valutazione deve includere task fissi, verificatori deterministici, errori di tool calling e regressioni funzionali.

11. Architettura di produzione: cache non significa memoria durevole

ASP.NET Core / Agent API
          |
    AuthN / AuthZ / Tenant
          |
    Agent Orchestrator ---- Durable state / checkpoint
          |                       |
      Model Gateway        Memory / tool results
          |
    Cache policy + routing
          |
   +------+-------+
   |              |
GPU worker A   GPU worker B
KV cache       KV cache
   |              |
   +------+-------+
          |
 Metrics / tracing / audit

Lo stato durevole dell'agente descrive decisioni, tool call, risultati e checkpoint necessari al recovery. La KV cache è un'accelerazione computazionale, spesso volatile. Se un worker GPU si arresta, l'agente deve poter ripartire dalla traiettoria persistita anche quando tutta la cache è andata persa.

Per il multi-tenant, attenzione al riuso di prefissi fra gruppi di fiducia: differenze di latenza possono rivelare indirettamente informazioni. Isolamento della cache, autorizzazioni e politiche di retention devono essere coerenti. Inoltre una tool response malevola rimane non attendibile anche se è già stata memorizzata nella cache.

12. Conclusioni

Il Cache Engineering sposta l'ottimizzazione degli agenti dal solo testo del prompt allo stato computazionale dell'attenzione. Prefix caching e PagedAttention sono oggi strumenti concreti di ottimizzazione del serving; le operazioni drop e repos di AKV rappresentano una direzione di ricerca più avanzata che richiede un runtime specializzato.

Le tre regole operative sono: misurare separatamente prefill, decode e occupazione KV; non riutilizzare stati senza rispettarne la storia; non confondere mai cache GPU e persistenza dell'agente. Il miglioramento più importante non è il costo per token, ma il costo per task corretto e verificabile.

Fonti tecniche e scientifiche

  1. Cache Engineering for Agents — paper (2026)
  2. AKV — codice degli autori
  3. vLLM — Automatic Prefix Caching
  4. vLLM — documentazione e serving
  5. PagedAttention — Hugging Face
  6. Rotary Embeddings — Hugging Face
  7. Transformers — Cache Explanation
  8. SGLang — Unified Radix Cache

Nota metodologica: i numeri AKV sono risultati sperimentali degli autori. Il laboratorio proposto verifica il prefix caching standard, non replica AKV. Controllare la versione delle CLI e la compatibilità GPU prima dell'esecuzione.

Nessun commento: