Riordinamento dei chunk recuperati
Riordinamento con cross-encoder.
Riordinamento dei chunk recuperati è una lezione AI Prompt Engineering gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento AI Prompt Engineering, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso AI Prompt Engineering include 4 lezioni in totale.
Perché fare il re-ranking
Il retrieval di prima fase (denso o sparso) ottimizza il recall su larga scala: trovare il chunk di riferimento da qualche parte nei primi 50. È veloce, ma grossolano. Un re-ranker di seconda fase riordina quindi quella rosa di candidati per la precisione, portando in cima i chunk realmente rilevanti.
Questo pattern, che consiste nel recuperare in modo ampio e poi eseguire un re-ranking preciso, è la struttura portante del RAG avanzato.
def two_stage(query, k_retrieve=50, k_final=5):
candidates = first_stage_retrieve(query, k_retrieve) # high recall
reranked = rerank(query, candidates) # high precision
return reranked[:k_final]Bi-encoder e cross-encoder a confronto
Un bi-encoder codifica query e documento separatamente in vettori e li confronta tramite il coseno: è veloce e indicizzabile, ma perde l'interazione tra query e documento. Un cross-encoder passa invece query e candidato insieme attraverso il modello e restituisce un punteggio di rilevanza, cogliendo le interazioni a grana fine.
I cross-encoder sono molto più accurati, ma non possono essere precalcolati, quindi vengono eseguiti solo sulla rosa di candidati.
# Bi-encoder: score = cos(enc(q), enc(d)) -> precomputable
# Cross-encoder: score = model(q, d) -> 0..1 -> per-pair, no index
def cross_encode(query, doc):
return cross_encoder.predict([(query, doc)])[0] # joint attentionUn passaggio di re-ranking con cross-encoder
Il re-ranker assegna un punteggio a ogni candidato rispetto alla query, quindi li ordina in senso decrescente. Poiché il cross-encoder presta attenzione congiuntamente a query e documento, risolve aspetti sottili della rilevanza che il bi-encoder non coglie: negazioni, esigenze di corrispondenza esatta e distinzione tra risposta e argomento.
Questa fase aumenta in genere l'accuratezza delle risposte più di qualsiasi altro singolo miglioramento del RAG.
def rerank(query, candidates):
pairs = [(query, c.text) for c in candidates]
scores = cross_encoder.predict(pairs) # batched
for c, s in zip(candidates, scores):
c.rerank_score = s
return sorted(candidates, key=lambda c: c.rerank_score, reverse=True)LLM come re-ranker
Quando nessun cross-encoder addestrato è adatto al proprio dominio, è possibile usare un LLM per il re-ranking. Il prompting listwise chiede al modello di ordinare un elenco di passaggi in base alla rilevanza con un'unica chiamata; quello pointwise assegna un punteggio a ogni passaggio in modo indipendente.
Il listwise coglie i confronti relativi ed è efficiente in termini di token, ma presti attenzione al bias di posizione e si assicuri che l'analisi dell'output sia robusta rispetto all'omissione o alla duplicazione degli ID da parte del modello.
def llm_listwise(query, candidates):
passages = '\n'.join(
'[' + str(i) + '] ' + c.text for i, c in enumerate(candidates)
)
prompt = (
'Rank the passages by relevance to the query. '
'Return only IDs, most relevant first.\nQuery: ' + query +
'\n' + passages
)
order = parse_ids(llm(prompt, temperature=0))
return [candidates[i] for i in order]Latenza e dimensione della rosa di candidati
Il costo del re-ranking cresce con la dimensione della rosa di candidati. Applicare un cross-encoder a 50 candidati è molto meno costoso che applicarlo a 500. Scelga un k della prima fase abbastanza grande da includere il chunk di riferimento (convalidi il recall@k), ma abbastanza piccolo da consentire il re-ranking entro il budget di latenza.
Raggruppi i punteggi delle coppie in batch ed esegua l'operazione su hardware accelerato; i cross-encoder si parallelizzano bene tra le coppie.
def tune_shortlist(eval_set, ks=(20, 50, 100, 200)):
# find smallest k where recall@k saturates -> rerank fewer pairs
return {k: (recall_at_k(eval_set, k), rerank_latency(k)) for k in ks}Prima fase ibrida e reranking
Il recall più elevato si ottiene con una prima fase ibrida (recupero denso e BM25 fusi), che alimenta una singola lista ristretta deduplicata per il reranker. Il recupero denso individua le parafrasi; quello sparso recupera gli identificatori esatti; il cross-encoder ordina quindi l'unione in base alla rilevanza effettiva.
Questa combinazione è robusta con diversi tipi di query, dalle domande in linguaggio naturale alle ricerche di corrispondenze letterali.
def hybrid_then_rerank(query, k_final=6):
dense = dense_retrieve(query, 50)
sparse = bm25_retrieve(query, 50)
fused = dedup(rrf(dense, sparse)) # reciprocal rank fusion
return rerank(query, fused)[:k_final]Soglie e limiti di punteggio
I punteggi del reranker possono essere calibrati. Invece di selezionare sempre i primi n risultati, applicare una soglia di rilevanza: mantenere i chunk che superano un determinato punteggio e, se nessuno la supera, restituire esplicitamente l'assenza di una risposta. In questo modo si evita di riempire il prompt con contenuti debolmente pertinenti.
Regolare la soglia su un set di validazione per bilanciare la copertura delle domande a cui è possibile rispondere e l'inclusione di elementi fuorvianti.
def threshold_select(reranked, tau=0.3, max_n=8):
kept = [c for c in reranked if c.rerank_score >= tau][:max_n]
if not kept:
return None # signal: no sufficiently relevant context
return keptDiversità dopo il reranking
Un ordinamento basato esclusivamente sulla rilevanza può restituire diversi chunk quasi duplicati dello stesso documento, sprecando il budget di contesto. Applicare MMR o limiti per documento dopo il reranking, così da garantire che il set finale copra aspetti e fonti distinti.
Questo è importante per le domande multi-hop, la cui risposta si estende su diversi documenti.
def diversify(reranked, max_per_doc=2, k=6):
out, per_doc = [], {}
for c in reranked:
d = c.meta['doc_id']
if per_doc.get(d, 0) < max_per_doc:
out.append(c)
per_doc[d] = per_doc.get(d, 0) + 1
if len(out) == k:
break
return outOrdinamento per il generatore
Dopo aver selezionato i chunk migliori, posizionarli in modo da sfruttare l'attenzione. Considerato l'effetto della perdita nel mezzo, collocare il singolo chunk con il punteggio più alto all'inizio o alla fine del contesto, senza seppellirlo tra gli altri.
Alcune pipeline ordinano i chunk in base alla rilevanza crescente, così che il migliore si trovi più vicino alla domanda, rispecchiando la strategia di recency dei prompt few-shot.
def order_for_llm(chunks):
chunks = sorted(chunks, key=lambda c: c.rerank_score) # ascending
return chunks # most relevant chunk ends up last,
# nearest the trailing questionValutazione del reranker
Misurare il reranker con metriche di ranking, principalmente NDCG e MRR, sulla rilevanza etichettata tra query e chunk, e successivamente con l'accuratezza della risposta. Un reranker che migliora l'NDCG ma non le risposte potrebbe limitarsi a riordinare chunk che il generatore gestiva già correttamente.
Chiudere sempre il ciclo valutando la qualità del compito finale, non soltanto le metriche di ranking.
import math
def ndcg_at_k(relevances, k):
dcg = sum(r / math.log2(i + 2) for i, r in enumerate(relevances[:k]))
ideal = sorted(relevances, reverse=True)
idcg = sum(r / math.log2(i + 2) for i, r in enumerate(ideal[:k]))
return dcg / idcg if idcg else 0.0Una pipeline di reranking per la produzione
Dall'inizio alla fine: recuperare 50 candidati con un approccio ibrido, deduplicare, applicare il reranking con un cross-encoder, applicare una soglia di punteggio, diversificare per documento, ordinare i chunk in base all'attenzione e generare la risposta con le citazioni. Utilizzare la soglia come condizione per restituire esplicitamente l'assenza di una risposta.
Memorizzare nella cache gli embedding e i punteggi del reranker per ogni coppia (query, chunk) quando il traffico ripete le stesse richieste, così da ridurre i costi.
def pipeline(query):
shortlist = hybrid_then_rerank(query, k_final=20)
kept = threshold_select(shortlist, tau=0.3, max_n=8)
if kept is None:
return 'No relevant information found.'
ctx = order_for_llm(diversify(kept))
return generate_with_citations(query, ctx)Verifica rapida
Scegliere l'architettura di reranking corretta.
Riepilogo
Punti chiave:
- Recuperare ampiamente per ottenere recall, quindi applicare il reranking alla lista ristretta per ottenere precisione.
- I cross-encoder valutano congiuntamente le coppie query-documento (accurati, ma non indicizzabili); i bi-encoder sono veloci, ma approssimativi.
- Il reranking listwise/pointwise con LLM è un'opzione di ripiego; prestare attenzione al bias di posizione e al parsing.
- Regolare la dimensione della lista ristretta fino a saturare il recall entro il budget di latenza; combinarla con il recupero ibrido nella prima fase.
- Applicare soglie di punteggio, diversificare per documento, ordinare i chunk in base all'attenzione e valutare con NDCG e accuratezza della risposta a valle.
Domande Frequenti
La lezione «Riordinamento dei chunk recuperati» è gratuita?
Sì — il testo completo di «Riordinamento dei chunk recuperati» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso AI Prompt Engineering, passa a CoddyKit PRO. Il corso AI Prompt Engineering include 4 lezioni in totale.
Cosa imparerò in «Riordinamento dei chunk recuperati»?
Riordinamento con cross-encoder. Eserciti AI Prompt Engineering con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare AI Prompt Engineering?
Non è richiesta alcuna esperienza precedente. AI Prompt Engineering su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.
Quanto tempo richiede la lezione «Riordinamento dei chunk recuperati»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione AI Prompt Engineering?
Sì. Ogni lezione AI Prompt Engineering include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Oltre il RAG ingenuo
- Riordinamento dei chunk recuperati
- Compressione del contesto
- Riscrittura delle query e HyDE