Introduzione: la ricerca AI accessibile per il prossimo Transformer

Tre mesi fa è stata avviata una rinascita di Papers with Code, una piattaforma il cui obiettivo è rendere la ricerca AI aperta accessibile e digeribile, consentendo alle persone di trovare facilmente gli artefatti correlati a un articolo, scoprire lo stato dell'arte (SOTA) nei vari domini dell'intelligenza artificiale, condividere ricerche interessanti e costruire sulle spalle dei lavori altrui. In altre parole, l'obiettivo è alimentare l'ondata di ricerca che porta al prossimo Transformer. Per raggiungere questo scopo, la piattaforma richiede un motore di ricerca potente in grado di servire sia gli utenti umani che gli agenti software, accessibile tramite il sito web o il comando CLI pwc search, che gli agenti possono utilizzare tramite Skill.

La sfida della ricerca nei documenti scientifici

La ricerca di articoli scientifici non è esattamente come la ricerca di testo ordinario. Un motore di ricerca utile per articoli accademici deve essere in grado di trovare titoli esatti o identificatori arXiv, ma deve anche comprendere query concettuali come "small language models for code generation" anche quando quelle parole non compaiono insieme in un articolo. Deve riconoscere che "the original BERT paper" è una richiesta navigazionale, tollerare titoli incompleti o errori di battitura, e rispondere comunque rapidamente quando un servizio di modello è freddo o temporaneamente indisponibile.

Per affrontare queste sfide, il team di Papers with Code ha costruito un sistema di ricerca ibrido, basato sull'esperienza precedente in ML6, dove sono stati sviluppati sistemi basati su RAG per i clienti. La ricerca ibrida tipicamente supera i sistemi basati solo su keyword o solo su vettori, poiché combina il meglio di entrambi i mondi. La ricerca per keyword trova menzioni esatte, mentre la ricerca vettoriale trova termini più sfumati e semanticamente simili. È importante notare che i reranker (anche chiamati cross-encoder) possono migliorare ulteriormente i risultati, sebbene con un overhead aggiuntivo e latenza.

Architettura del sistema: separazione tra offline e online

Papers with Code si affida a un database PostgreSQL, quindi le sue capacità di ricerca full-text forniscono una veloce baseline lessicale. Per i dense embeddings, viene utilizzato pgvector per aggiungere il semantic recall, e l'algoritmo reciprocal rank fusion (RRF) combina i due approcci. Tre servizi di Hugging Face vengono utilizzati per i dense embeddings: Inference Endpoints, Jobs e Storage Buckets.

Il team ha deliberatamente diviso la ricerca in una costruzione offline del corpus e un servizio di ricerca online. Il lavoro costoso e orientato al throughput viene eseguito come Jobs. Gli artefatti durevoli vivono in un Bucket. Solo il piccolo passo dell'embedding delle query si trova nel percorso della richiesta, dietro un Inference Endpoint protetto, per alimentare la ricerca online. Se quell'endpoint è freddo, occupato o non disponibile, la ricerca ricade immediatamente su un'estrazione full-text. Questa separazione rende il sistema sia potente che veloce.

Versioning dell'embedding come API

Le pipeline di embedding spesso falliscono in modi sottili: una revisione del modello cambia, i prompt query e documento vengono confusi, i vettori vengono troncati diversamente, o un abstract aggiornato non corrisponde più al suo vettore memorizzato. Per evitare questi problemi, il team tratta il formato di embedding come un'API versionata. Ogni articolo viene codificato con un contratto che segue l'embedding dall'esportazione, attraverso l'inferenza GPU, in PostgreSQL, e infine nel recupero online.

La generazione di produzione utilizza Qwen/Qwen3-Embedding-0.6B, bloccato a una revisione esatta, con vettori L2-normalizzati a 256 dimensioni. Il modello è stato selezionato con l'aiuto della leaderboard MTEB, il benchmark di riferimento per il confronto dei modelli di embedding. È importante notare che i modelli di embedding più recenti come Qwen3 consentono due nuove funzionalità: la compressione dimensionale senza riqualificazione e la regolazione della lunghezza della query.

Jobs: embedding del corpus in batch

L'embedding a corpus intero è un classico carico di lavoro batch. Necessita di una GPU per un periodo relativamente breve, beneficia di un throughput elevato e non dovrebbe consumare risorse tra le esecuzioni. Hugging Face Jobs si adatta perfettamente a questa forma: un Job è definito da un comando, da una configurazione hardware e opzionalmente da un'immagine Docker, e può eseguire script uv con le loro dipendenze dichiarate inline.

La costruzione del corpus inizia esportando l'ultima versione di ogni articolo da uno snapshot PostgreSQL con lettura ripetibile. L'esportatore trasmette le righe piuttosto che caricare il catalogo in memoria, scrive shard JSONL limitati e crea un manifesto contenente conteggi di righe e checksum SHA-256.

Il team sincronizza quella directory di esecuzione immutabile in un Bucket di archiviazione privato e monta il Bucket direttamente (usando hf-mount) in un Job l4x1 (una GPU NVIDIA L4, che ha 24GB di VRAM). Dal punto di vista del worker, è semplicemente un filesystem. Ogni shard completato ha il suo marcatore, quindi un Job riavviato può saltare il lavoro verificato. Questo è utile per un corpus di grandi dimensioni: i tentativi dovrebbero semplicemente riprendere il lavoro piuttosto che sovrascrivere gli embedding esistenti.

Nel pilot di 5.000 articoli, il Job Qwen ha codificato circa 75 articoli al secondo con 1024 dimensioni su una GPU L4. Lo stesso passaggio potrebbe essere materializzato deterministicamente a 512 e 256 dimensioni, in modo da poter confrontare i compromessi di archiviazione e recupero senza pagare per più inferenze.

Storage Buckets: il confine tra sistemi

Storage Buckets sono archiviazione di oggetti mutevole e simile a S3 sull'Hub, ottimizzata per i carichi di lavoro AI. Possono essere accessibili tramite percorsi hf://buckets/... e montati in lettura-scrittura in Jobs senza costruire un'integrazione di archiviazione separata.

Per Papers with Code, il Bucket è più di un semplice posto dove mettere i vettori. È il confine tra tre sistemi con cicli di vita diversi:

  • Pipeline di embedding, che scrivono shard di embedding da nuove esecuzioni
  • Importatore, che valida e carica nel database
  • Servizio di query, che legge indici costruiti

Gli artefatti sono organizzati sotto prefissi di esecuzione immutabili, che contengono shard di embedding, manifesti e metadati. I Bucket stessi sono intenzionalmente mutevoli, quindi l'immutabilità è una regola a livello di applicazione: un ID di esecuzione non viene mai sovrascritto, e ogni artefatto è coperto da un manifesto e checksum.

Solo dopo che l'importatore ha verificato schema, checksum, dimensioni, normalizzazione, ID di articoli unici e hash di contenuto corrente, i vettori vengono caricati in PostgreSQL. Viene quindi costruito un indice HNSW separato per la nuova generazione e contrassegnato atomicamente come attivo solo quando ogni articolo corrente idoneo è coperto. HNSW è l'algoritmo basato su grafici che abilita la ricerca vettoriale veloce.

Inference Endpoints: embedding delle query in tempo reale

Gli embedding batch risolvono il lato dei documenti del recupero. Una query dell'utente deve comunque essere incorporata al momento della richiesta utilizzando lo stesso contratto di modello.

Il team distribuisce il modello bloccato come un Inference Endpoint autenticato supportato da Text Embeddings Inference (TEI). L'endpoint accetta il testo della query e restituisce un vettore normalizzato a 256 dimensioni utilizzando il prompt di query del modello. Si noti che si potrebbe anche sfruttare vLLM o SGLang qui.

L'API quindi esegue una ricerca a distanza del coseno sulla generazione attiva di pgvector. L'indice HNSW mantiene questa ricerca veloce. Nel pilot di 5.000 articoli, l'indice Qwen a 256 dimensioni ha ottenuto un Recall@20 di 0.9955 rispetto alla ricerca esatta, con latenza HNSW p50 di 1.31 ms e p95 di 2.21 ms. La sua tabella e indice hanno utilizzato circa il 27% dell'archiviazione della versione a 1024 dimensioni mantenendo essenzialmente lo stesso ANN recall in quel test.

L'Endpoint è configurato con un massimo di una replica e può scalare a zero quando inattivo. Questo è un utile leva di costo, poiché significa che non si paga quando non c'è utilizzo. Tuttavia, questo significa che gli avvii a freddo devono far parte del design dell'applicazione piuttosto che essere trattati come un evento eccezionale, poiché ci vuole tempo perché l'endpoint si avvii e serva il traffico.

Fallback e resilienza: progettazione per l'affidabilità

Il client di query ha quindi deliberatamente un comportamento rigoroso. Se l'endpoint sta scalando, scade il timeout, restituisce un vettore malformato, o non ha concorrenza disponibile, il team salta immediatamente il ramo semantico. Gli utenti ricevono comunque risultati lessicali invece di aspettare una dipendenza inaffidabile.

Inference Endpoints funziona in modo molto affidabile e include una bella dashboard in modo da poter vedere rapidamente le analitiche chiave. Per ogni query, il ramo lessicale recupera fino a 50 candidati utilizzando la ricerca full-text PostgreSQL ponderata. Il ramo semantico recupera fino a 50 candidati da pgvector.

Reciprocal rank fusion: combinazione dei risultati

I ranghi vengono combinati utilizzando reciprocal rank fusion ponderata (RRF): score(d)=∑r∈{lexical,semantic}wrk+rankr(d) RRF è semplice e robusto, perché combina i ranghi piuttosto che i punteggi di due sistemi con scale diverse. Fondamentalmente, se un articolo è classificato alto sia dal ramo lessicale che dal ramo semantico, ha una probabilità più alta di essere classificato alto dalla ricerca ibrida. Il team attualmente utilizza pesi di ramo uguali e (k=60) (k è la "costante di rango", un iperparametro dell'algoritmo RRF).

La recupera densa migliora il richiamo per le query concettuali. La recupera full-text rimane eccellente per la terminologia esatta, gli identificatori e i nomi rari. Il team preserva inoltre il comportamento deterministico dell'identità in cima alla fusione di ranking. È importante notare che la ricerca ibrida non è sempre la migliore opzione. Si consiglia di iniziare con la ricerca per keyword come baseline economica e veloce, e solo aggiungere ricerca semantica e/o ibrida quando risulta che quelle forniscono un aumento ragionevole nella qualità del recupero. Si potrebbe ulteriormente migliorare la ricerca aggiungendo un reranker dopo il recupero di keyword/semantico/ibrido, utilizzando un modello come Qwen3-Reranker.

Aggiornamenti incrementali: mantenimento del corpus in sincronia

Il grande corpus iniziale viene incorporato con Jobs, ma Papers with Code cambia continuamente. Nuovi articoli arrivano, i riassunti vengono corretti e nuove versioni di arXiv diventano correnti. Lanciare un GPU Job per una manciata di righe modificate aggiungerebbe un overhead di avvio e orchestrazione non necessario.

Invece, un processo incrementale orario seleziona articoli mancanti o con contenuto modificato e invia un delta limitato allo stesso Endpoint TEI, questa volta con il prompt di documento. Ogni esecuzione elabora al massimo 500 articoli in batch di 16. Prima che un embedding venga scritto, la riga di origine viene bloccata e il suo hash di contenuto viene ricontrollato. Se un articolo cambia durante l'inferenza, quel vettore viene scartato e ripreco dalla prossima esecuzione.

Il percorso orario mantiene l'indice attivo vicino al catalogo live senza trasformare un endpoint online in un processore batch illimitato. Gli stessi embedding di documento alimentano anche le raccomandazioni di articoli correlati su ogni pagina di articolo. Poiché l'articolo di origine ha già un vettore memorizzato, il recupero di articoli correlati non richiede alcuna chiamata di modello al momento della richiesta. È una singola query di nearest-neighbor sulla generazione attiva. Se un vettore è temporaneamente mancante, l'applicazione può utilizzare una fallback lessicale.

Lezioni apprese e best practice

L'esperienza di Papers with Code con la ricerca ibrida ha fornito diverse lezioni importanti per chiunque implementi sistemi di recupero per corpus scientifici o di ricerca. In primo luogo, la separazione tra processi offline e online è cruciale. I lavori di embedding batch possono essere costosi computazionalmente e non devono competere con il traffico di query in tempo reale. Memorizzare gli artefatti in Buckets muta crea un confine naturale tra sistemi con cicli di vita diversi.

In secondo luogo, il versioning e i contratti API sono essenziali. Trattando il formato di embedding come un'API versionata, il sistema evita insidiosissimi bug in cui il modello cambia, i prompt vengono mescolati o i vettori vengono calcolati in modo incoerente. Ogni deployment produce un ID di esecuzione unico con manifesti e checksum che possono essere verificati e controllati.

In terzo luogo,