Reranking des documents avec Ollama et le modèle Qwen3 Reranker - en Go

Mise en œuvre de RAG ? Voici quelques extraits de code en Go - 2...

Sommaire

Comme Ollama standard ne dispose pas d’API de rerank directe, vous devrez implémenter le reranking avec Qwen3 Reranker en GO en générant des embeddings pour les paires de requêtes et de documents et en les notant.

La semaine dernière, j’ai fait un peu de Reranking de documents textuels avec Ollama et Qwen3 Embedding model - en Go.

Aujourd’hui, je vais essayer certains modèles Qwen3 Reranker. Il existe un ensemble assez important de nouveaux Qwen3 Embedding & Reranker Models sur Ollama disponibles, j’utilise medium - dengcao/Qwen3-Reranker-4B:Q5_K_M

reranking dogs

Le test Run: TL;DR

Ça marche, et assez rapide, pas très standard, mais tout de même :

$ ./rnk ./example_query.txt ./example_docs

Using embedding model: dengcao/Qwen3-Embedding-4B:Q5_K_M
Ollama base URL: http://localhost:11434
Processing query file: ./example_query.txt, target directory: ./example_docs
Query: What is artificial intelligence and how does machine learning work?
Found 7 documents
Extracting query embedding...
Processing documents...

=== RANKING BY SIMILARITY ===
1. example_docs/ai_introduction.txt (Score: 0.451)
2. example_docs/machine_learning.md (Score: 0.388)
3. example_docs/qwen3-reranking-models.md (Score: 0.354)
4. example_docs/ollama-parallelism.md (Score: 0.338)
5. example_docs/ollama-reranking-models.md (Score: 0.318)
6. example_docs/programming_basics.txt (Score: 0.296)
7. example_docs/setup.log (Score: 0.282)

Processed 7 documents in 2.023s (avg: 0.289s per document)
Reranking documents with reranker model...
Implementing reranking using cross-encoder approach with dengcao/Qwen3-Reranker-4B:Q5_K_M

=== RANKING WITH RERANKER ===
1. example_docs/ai_introduction.txt (Score: 0.343)
2. example_docs/machine_learning.md (Score: 0.340)
3. example_docs/programming_basics.txt (Score: 0.320)
4. example_docs/setup.log (Score: 0.313)
5. example_docs/ollama-parallelism.md (Score: 0.313)
6. example_docs/qwen3-reranking-models.md (Score: 0.312)
7. example_docs/ollama-reranking-models.md (Score: 0.306)

Processed 7 documents in 1.984s (avg: 0.283s per document)

Code de reranker en Go pour appeler Ollama

Prendre la plupart du code de l’article Reranking text documents with Ollama using Embedding... et ajouter ces éléments :

À la fin de la fonction runRnk() :

  startTime = time.Now()
	// rerank using reranking model
	fmt.Println("Reranking documents with reranker model...")

	// rerankingModel := "dengcao/Qwen3-Reranker-0.6B:F16"
	rerankingModel := "dengcao/Qwen3-Reranker-4B:Q5_K_M"
	rerankedDocs, err := rerankDocuments(validDocs, query, rerankingModel, ollamaBaseURL)
	if err != nil {
		log.Fatalf("Error reranking documents: %v", err)
	}

	fmt.Println("\n=== RANKING WITH RERANKER ===")
	for i, doc := range rerankedDocs {
		fmt.Printf("%d. %s (Score: %.3f)\n", i+1, doc.Path, doc.Score)
	}

	totalTime = time.Since(startTime)
	avgTimePerDoc = totalTime / time.Duration(len(rerankedDocs))

	fmt.Printf("\nProcessed %d documents in %.3fs (avg: %.3fs per document)\n",
		len(rerankedDocs), totalTime.Seconds(), avgTimePerDoc.Seconds())

Ensuite, ajouter quelques fonctions supplémentaires :

func rerankDocuments(validDocs []Document, query, rerankingModel, ollamaBaseURL string) ([]Document, error) {
	// Comme Ollama standard ne dispose pas d'API de rerank directe, nous allons implémenter
	// le reranking en générant des embeddings pour les paires de requêtes et de documents et en les notant

	fmt.Println("Implementing reranking using cross-encoder approach with", rerankingModel)

	rerankedDocs := make([]Document, len(validDocs))
	copy(rerankedDocs, validDocs)

	for i, doc := range validDocs {
		// Créer un prompt pour le reranking en combinant la requête et le document
		rerankPrompt := fmt.Sprintf("Query: %s\n\nDocument: %s\n\nRelevance:", query, doc.Content)

		// Obtenir l'embedding pour le prompt combiné
		embedding, err := getEmbedding(rerankPrompt, rerankingModel, ollamaBaseURL)
		if err != nil {
			fmt.Printf("Warning: Failed to get rerank embedding for document %d: %v\n", i, err)
			// Fallback à une note neutre
			rerankedDocs[i].Score = 0.5
			continue
		}

		// Utiliser la magnitude de l'embedding comme note de pertinence
		// (C'est une approche simplifiée - en pratique, vous utiliseriez un reranker entraîné)
		score := calculateRelevanceScore(embedding)
		rerankedDocs[i].Score = score
		// fmt.Printf("Document %d reranked with score: %.4f\n", i, score)
	}

	// Trier les documents par note de reranking (décroissante)
	sort.Slice(rerankedDocs, func(i, j int) bool {
		return rerankedDocs[i].Score > rerankedDocs[j].Score
	})

	return rerankedDocs, nil
}

func calculateRelevanceScore(embedding []float64) float64 {
	// Scoring simple basé sur la magnitude de l'embedding et les valeurs positives
	var sumPositive, sumTotal float64
	for _, val := range embedding {
		sumTotal += val * val
		if val > 0 {
			sumPositive += val
		}
	}

	if sumTotal == 0 {
		return 0
	}

	// Normaliser et combiner la magnitude avec un biais positif
	magnitude := math.Sqrt(sumTotal) / float64(len(embedding))
	positiveRatio := sumPositive / float64(len(embedding))

	return (magnitude + positiveRatio) / 2
}

N’oubliez pas d’importer un peu de math

import (
	"math"
)

Maintenant, compilons-le

go build -o rnk

et maintenant, exécutons ce prototype simple de RAG reranker

./rnk ./example_query.txt ./example_docs

Liens utiles