Reranking des documents textuels avec Ollama et le modèle d'embedding Qwen3 - en Go
Mise en œuvre de RAG ? Voici quelques extraits de code en Golang.
Ce petit exemple de code Go pour le reranking appelle Ollama pour générer des embeddings pour la requête et pour chaque document candidat, puis trie en ordre descendant selon la similarité cosinus.
Nous avons déjà fait une activité similaire - Reranking avec des modèles d’embeddings mais cela était en python, avec un autre LLM et presque un an plus tôt.
TL;DR
Le résultat semble très bon, la vitesse est de 0,128s par document. La question est comptée comme un document. Et le tri et l’impression sont également inclus dans cette statistique.
Consommation de mémoire LLM :
Même si la taille du modèle sur le disque dur (ollama ls
) est inférieure à 3 Go
dengcao/Qwen3-Embedding-4B:Q5_K_M 7e8c9ad6885b 2,9 Go
Sur la VRAM de la GPU, cela prend (pas un peu) plus : 5,5 Go. (ollama ps
)
NAME ID SIZE
dengcao/Qwen3-Embedding-4B:Q5_K_M 7e8c9ad6885b 5,5 Go
Si vous avez une GPU de 8 Go - devrait être OK.
Test du reranking avec des embeddings sur Ollama - Exemple de sortie
Dans les trois cas de test, le reranking avec embeddings en utilisant le modèle dengcao/Qwen3-Embedding-4B:Q5_K_M d’Ollama était incroyable ! Voyez-le par vous-mêmes.
Nous avons 7 fichiers contenant certains textes décrivant ce que leur nom de fichier indique :
- ai_introduction.txt
- machine_learning.md
- qwen3-reranking-models.md
- ollama-parallelism.md
- ollama-reranking-models.md
- programming_basics.txt
- setup.log
tests :
Test de reranking : Qu’est-ce que l’intelligence artificielle et comment fonctionne l’apprentissage automatique ?
./rnk example_query.txt example_docs/
Utilisation du modèle d'embedding : dengcao/Qwen3-Embedding-4B:Q5_K_M
URL de base d'Ollama : http://localhost:11434
Traitement du fichier de requête : example_query.txt, répertoire cible : example_docs/
Requête : Qu'est-ce que l'intelligence artificielle et comment fonctionne l'apprentissage automatique ?
7 documents trouvés
Extraction de l'embedding de la requête...
Traitement des documents...
=== RANGEMENT PAR SIMILARITÉ ===
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)
7 documents traités en 0,899s (moyenne : 0,128s par document)
Test de reranking : Comment ollama gère les requêtes parallèles ?
./rnk example_query2.txt example_docs/
Utilisation du modèle d'embedding : dengcao/Qwen3-Embedding-4B:Q5_K_M
URL de base d'Ollama : http://localhost:11434
Traitement du fichier de requête : example_query2.txt, répertoire cible : example_docs/
Requête : Comment ollama gère les requêtes parallèles ?
7 documents trouvés
Extraction de l'embedding de la requête...
Traitement des documents...
=== RANGEMENT PAR SIMILARITÉ ===
1. example_docs/ollama-parallelism.md (Score : 0,557)
2. example_docs/qwen3-reranking-models.md (Score : 0,532)
3. example_docs/ollama-reranking-models.md (Score : 0,498)
4. example_docs/ai_introduction.txt (Score : 0,366)
5. example_docs/machine_learning.md (Score : 0,332)
6. example_docs/programming_basics.txt (Score : 0,307)
7. example_docs/setup.log (Score : 0,257)
7 documents traités en 0,858s (moyenne : 0,123s par document)
Test de reranking : Comment faire le reranking d’un document avec ollama ?
./rnk example_query3.txt example_docs/
Utilisation du modèle d'embedding : dengcao/Qwen3-Embedding-4B:Q5_K_M
URL de base d'Ollama : http://localhost:11434
Traitement du fichier de requête : example_query3.txt, répertoire cible : example_docs/
Requête : Comment faire le reranking d'un document avec ollama ?
7 documents trouvés
Extraction de l'embedding de la requête...
Traitement des documents...
=== RANGEMENT PAR SIMILARITÉ ===
1. example_docs/ollama-reranking-models.md (Score : 0,552)
2. example_docs/ollama-parallelism.md (Score : 0,525)
3. example_docs/qwen3-reranking-models.md (Score : 0,524)
4. example_docs/ai_introduction.txt (Score : 0,369)
5. example_docs/machine_learning.md (Score : 0,346)
6. example_docs/programming_basics.txt (Score : 0,316)
7. example_docs/setup.log (Score : 0,279)
7 documents traités en 0,882s (moyenne : 0,126s par document)
Code source Go
Mettez-le tout dans un dossier et compilez-le comme suit
go build -o rnk
N’hésitez pas à l’utiliser à des fins amusantes ou commerciales ou à l’uploader sur GitHub si vous le souhaitez. Licence MIT.
main.go
package main
import (
"fmt"
"log"
"os"
"sort"
"time"
"github.com/spf13/cobra"
)
var rootCmd = &cobra.Command{
Use: "rnk [query-file] [target-directory]",
Short: "Système RAG utilisant des embeddings Ollama",
Long: "Un simple système RAG qui extrait des embeddings et classe les documents à l'aide d'Ollama",
Args: cobra.ExactArgs(2),
Run: runRnk,
}
var (
embeddingModel string
ollamaBaseURL string
)
func init() {
rootCmd.Flags().StringVarP(&embeddingModel, "model", "m", "dengcao/Qwen3-Embedding-4B:Q5_K_M", "Modèle d'embedding à utiliser")
rootCmd.Flags().StringVarP(&ollamaBaseURL, "url", "u", "http://localhost:11434", "URL de base d'Ollama")
}
func main() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func runRnk(cmd *cobra.Command, args []string) {
queryFile := args[0]
targetDir := args[1]
startTime := time.Now()
fmt.Printf("Utilisation du modèle d'embedding : %s\n", embeddingModel)
fmt.Printf("URL de base d'Ollama : %s\n", ollamaBaseURL)
fmt.Printf("Traitement du fichier de requête : %s, répertoire cible : %s\n", queryFile, targetDir)
// Lire la requête à partir du fichier
query, err := readQueryFromFile(queryFile)
if err != nil {
log.Fatalf("Erreur lors de la lecture du fichier de requête : %v", err)
}
fmt.Printf("Requête : %s\n", query)
// Trouver tous les fichiers texte dans le répertoire cible
documents, err := findTextFiles(targetDir)
if err != nil {
log.Fatalf("Erreur lors de la recherche des fichiers texte : %v", err)
}
fmt.Printf("Trouvé %d documents\n", len(documents))
// Extraire les embeddings pour la requête
fmt.Println("Extraction de l'embedding de la requête...")
queryEmbedding, err := getEmbedding(query, embeddingModel, ollamaBaseURL)
if err != nil {
log.Fatalf("Erreur lors de l'obtention de l'embedding de la requête : %v", err)
}
// Traiter les documents
fmt.Println("Traitement des documents...")
validDocs := make([]Document, 0)
for _, doc := range documents {
embedding, err := getEmbedding(doc.Content, embeddingModel, ollamaBaseURL)
if err != nil {
fmt.Printf("Avertissement : Échec de l'obtention de l'embedding pour %s : %v\n", doc.Path, err)
continue
}
similarity := cosineSimilarity(queryEmbedding, embedding)
doc.Score = similarity
validDocs = append(validDocs, doc)
}
if len(validDocs) == 0 {
log.Fatalf("Aucun document n'a pu être traité avec succès")
}
// Trier par score de similarité (descendant)
sort.Slice(validDocs, func(i, j int) bool {
return validDocs[i].Score > validDocs[j].Score
})
// Afficher les résultats
fmt.Println("\n=== RANGEMENT PAR SIMILARITÉ ===")
for i, doc := range validDocs {
fmt.Printf("%d. %s (Score : %.3f)\n", i+1, doc.Path, doc.Score)
}
totalTime := time.Since(startTime)
avgTimePerDoc := totalTime / time.Duration(len(validDocs))
fmt.Printf("\nTraité %d documents en %.3fs (moyenne : %.3fs par document)\n",
len(validDocs), totalTime.Seconds(), avgTimePerDoc.Seconds())
}
documents.go
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
)
func readQueryFromFile(filename string) (string, error) {
content, err := os.ReadFile(filename)
if err != nil {
return "", err
}
return strings.TrimSpace(string(content)), nil
}
func findTextFiles(dir string) ([]Document, error) {
var documents []Document
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && isTextFile(path) {
content, err := os.ReadFile(path)
if err != nil {
fmt.Printf("Avertissement : Impossible de lire le fichier %s : %v\n", path, err)
return nil
}
documents = append(documents, Document{
Path: path,
Content: string(content),
})
}
return nil
})
return documents, err
}
func isTextFile(filename string) bool {
ext := strings.ToLower(filepath.Ext(filename))
textExts := []string{".txt", ".md", ".rst", ".csv", ".json", ".xml", ".html", ".htm", ".log"}
for _, textExt := range textExts {
if ext == textExt {
return true
}
}
return false
}
embeddings.go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func getEmbedding(text string, model string, ollamaBaseURL string) ([]float64, error) {
req := OllamaEmbeddingRequest{
Model: model,
Prompt: text,
}
jsonData, err := json.Marshal(req)
if err != nil {
return nil, err
}
resp, err := http.Post(ollamaBaseURL+"/api/embeddings", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("erreur de l'API Ollama : %s", string(body))
}
var embeddingResp OllamaEmbeddingResponse
if err := json.NewDecoder(resp.Body).Decode(&embeddingResp); err != nil {
return nil, err
}
return embeddingResp.Embedding, nil
}
similarity.go
package main
func cosineSimilarity(a, b []float64) float64 {
if len(a) != len(b) {
return 0
}
var dotProduct, normA, normB float64
for i := range a {
dotProduct += a[i] * b[i]
normA += a[i] * a[i]
normB += b[i] * b[i]
}
if normA == 0 || normB == 0 {
return 0
}
return dotProduct / (sqrt(normA) * sqrt(normB))
}
func sqrt(x float64) float64 {
if x == 0 {
return 0
}
z := x
for i := 0; i < 10; i++ {
z = (z + x/z) / 2
}
return z
}
types.go
package main
// OllamaEmbeddingRequest représente la charge utile de la requête pour l'API d'embedding d'Ollama
type OllamaEmbeddingRequest struct {
Model string `json:"model"`
Prompt string `json:"prompt"`
}
// OllamaEmbeddingResponse représente la réponse de l'API d'embedding d'Ollama
type OllamaEmbeddingResponse struct {
Embedding []float64 `json:"embedding"`
}
// Document représente un document avec ses métadonnées
type Document struct {
Path string
Content string
Score float64
}
Liens utiles
- Feuille de rappel Ollama
- Modèles Qwen3 d’embedding et de reranking sur Ollama : Performance d’avant-garde
- https://fr.wikipedia.org/wiki/Retrieval-augmented_generation
- Installer et configurer l’emplacement des modèles Ollama
- Comment Ollama gère les requêtes parallèles
- Écrire des prompts efficaces pour les LLM
- Test des LLM : gemma2, qwen2 et Mistral Nemo sur Ollama
- Comparaison des LLM : Mistral Small, Gemma 2, Qwen 2.5, Mistral Nemo, LLama3 et Phi - Sur Ollama
- Test : Comment Ollama utilise les performances du processeur Intel et les cœurs efficaces
- Reranking avec des modèles d’embedding sur Ollama en Python
- Comparaison des capacités de résumé des LLM
- Fournisseurs de LLM en nuage