Przestawianie kolejności dokumentów za pomocą Ollama i modelu Qwen3 Reranker - w języku Go

Wdrażanie RAG? Oto kilka fragmentów kodu w Go - 2...

Page content

Ponieważ standardowy Ollama nie ma bezpośredniego interfejsu API do ponownego rangowania, musisz zaimplementować ponowne rangowanie przy użyciu Qwen3 Reranker w GO generując embeddingi dla par zapytań i dokumentów oraz oceniając je.

Tydzień temu zrobiłem trochę ponownego rangowania dokumentów tekstowych z użyciem Ollama i modelu Qwen3 Embedding - w Go.

Dzisiaj spróbuję niektórych modeli Qwen3 Reranker. Jest dość duża liczba nowych modeli Qwen3 Embedding & Reranker na Ollama dostępnych, używam średniego - dengcao/Qwen3-Reranker-4B:Q5_K_M

ponowne rangowanie psów

Uruchomienie testu: TL;DR

Działa, i dość szybko, nie jest to bardzo standardowy sposób, ale nadal:

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

Używany model embedding: dengcao/Qwen3-Embedding-4B:Q5_K_M
Podstawowy adres URL Ollama: http://localhost:11434
Przetwarzanie pliku zapytania: ./example_query.txt, katalog docelowy: ./example_docs
Zapytanie: Co to jest sztuczna inteligencja i jak działa uczenie maszynowe?
Znaleziono 7 dokumentów
Wyodrębnianie embeddingu zapytania...
Przetwarzanie dokumentów...

=== RANGOWANIE NA PODSTAWIE PODOBIEŃSTWA ===
1. example_docs/ai_introduction.txt (Wynik: 0,451)
2. example_docs/machine_learning.md (Wynik: 0,388)
3. example_docs/qwen3-reranking-models.md (Wynik: 0,354)
4. example_docs/ollama-parallelism.md (Wynik: 0,338)
5. example_docs/ollama-reranking-models.md (Wynik: 0,318)
6. example_docs/programming_basics.txt (Wynik: 0,296)
7. example_docs/setup.log (Wynik: 0,282)

Przetworzono 7 dokumentów w 2,023s (średnio: 0,289s na dokument)
Ponowne rangowanie dokumentów przy użyciu modelu rerankera...
Implementowanie ponownego rangowania przy użyciu podejścia cross-encoder z `dengcao/Qwen3-Reranker-4B:Q5_K_M`

=== RANGOWANIE Z UŻYCIEM RERANKERA ===
1. example_docs/ai_introduction.txt (Wynik: 0,343)
2. example_docs/machine_learning.md (Wynik: 0,340)
3. example_docs/programming_basics.txt (Wynik: 0,320)
4. example_docs/setup.log (Wynik: 0,313)
5. example_docs/ollama-parallelism.md (Wynik: 0,313)
6. example_docs/qwen3-reranking-models.md (Wynik: 0,312)
7. example_docs/ollama-reranking-models.md (Wynik: 0,306)

Przetworzono 7 dokumentów w 1,984s (średnio: 0,283s na dokument)

Kod w Go do wywoływania Ollama przez rerankera

Weź większość kodu z wpisu ponowne rangowanie dokumentów tekstowych z użyciem Ollama i modelu embedding... i dodaj te fragmenty:

Na końcu funkcji runRnk():

  startTime = time.Now()
	// ponowne rangowanie przy użyciu modelu rerankera
	fmt.Println("Ponowne rangowanie dokumentów przy użyciu modelu rerankera...")

	// 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("Błąd ponownego rangowania dokumentów: %v", err)
	}

	fmt.Println("\n=== RANGOWANIE Z UŻYCIEM RERANKERA ===")
	for i, doc := range rerankedDocs {
		fmt.Printf("%d. %s (Wynik: %.3f)\n", i+1, doc.Path, doc.Score)
	}

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

	fmt.Printf("\nPrzetworzono %d dokumentów w %.3fs (średnio: %.3fs na dokument)\n",
		len(rerankedDocs), totalTime.Seconds(), avgTimePerDoc.Seconds())

Następnie dodaj kilka dodatkowych funkcji:

func rerankDocuments(validDocs []Document, query, rerankingModel, ollamaBaseURL string) ([]Document, error) {
	// Ponieważ standardowy Ollama nie ma bezpośredniego interfejsu API do ponownego rangowania, zaimplementujemy
	// ponowne rangowanie generując embeddingi dla par zapytań i dokumentów oraz oceniając je

	fmt.Println("Implementowanie ponownego rangowania przy użyciu podejścia cross-encoder z", rerankingModel)

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

	for i, doc := range validDocs {
		// Utwórz wskazówkę do ponownego rangowania łącząc zapytanie i dokument
		rerankPrompt := fmt.Sprintf("Zapytanie: %s\n\nDokument: %s\n\nZnaczenie:", query, doc.Content)

		// Pobierz embedding dla łącznej wskazówki
		embedding, err := getEmbedding(rerankPrompt, rerankingModel, ollamaBaseURL)
		if err != nil {
			fmt.Printf("Ostrzeżenie: Nie udało się uzyskać embeddingu do ponownego rangowania dla dokumentu %d: %v\n", i, err)
			// Ustawienie neutralnego wyniku jako domyślne
			rerankedDocs[i].Score = 0.5
			continue
		}

		// Użyj wielkości embeddingu jako wyniku znaczenia
		// (To uproszczone podejście - w praktyce użyłbyś wytrenowanego rerankera)
		score := calculateRelevanceScore(embedding)
		rerankedDocs[i].Score = score
		// fmt.Printf("Dokument %d ponownie zrangaowany z wynikiem: %.4f\n", i, score)
	}

	// Posortuj dokumenty według wyniku ponownego rangowania (malejąco)
	sort.Slice(rerankedDocs, func(i, j int) bool {
		return rerankedDocs[i].Score > rerankedDocs[j].Score
	})

	return rerankedDocs, nil
}

func calculateRelevanceScore(embedding []float64) float64 {
	// Proste ocenianie oparte na wielkości embeddingu i dodatnich wartościach
	var sumPositive, sumTotal float64
	for _, val := range embedding {
		sumTotal += val * val
		if val > 0 {
			sumPositive += val
		}
	}

	if sumTotal == 0 {
		return 0
	}

	// Normalizacja i połączenie wielkości z dodatnim biasem
	magnitude := math.Sqrt(sumTotal) / float64(len(embedding))
	positiveRatio := sumPositive / float64(len(embedding))

	return (magnitude + positiveRatio) / 2
}

Nie zapomnij zaimportować trochę matematyki

import (
	"math"
)

Teraz skompilujmy to

go build -o rnk

i teraz uruchommy ten prosty prototyp technologii RAG rerankera

./rnk ./example_query.txt ./example_docs

Przydatne linki