Переранжирование документов с использованием Ollama и модели Qwen3 Reranker - на языке Go

Реализуете RAG? Вот несколько фрагментов кода на Go - 2...

Содержимое страницы

Поскольку стандартный Ollama не имеет прямого API для переупорядочивания, вам нужно реализовать переупорядочивание с использованием Qwen3 Reranker на GO, генерируя векторы представлений для пар запрос-документ и оценивая их.

На прошлой неделе я немного переупорядочил текстовые документы с использованием Ollama и Qwen3 Embedding model - на Go.

Сегодня попробую некоторые модели Qwen3 Reranker. Доступно довольно большое количество новых Qwen3 Embedding & Reranker Models на Ollama, я использую среднюю - dengcao/Qwen3-Reranker-4B:Q5_K_M

переупорядочивание собак

Тестовый запуск: TL;DR

Это работает, и довольно быстро, не очень стандартный способ, но всё равно:

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

Используется модель векторного представления: dengcao/Qwen3-Embedding-4B:Q5_K_M
Базовый URL Ollama: http://localhost:11434
Обработка файла запроса: ./example_query.txt, целевая директория: ./example_docs
Запрос: Что такое искусственный интеллект и как работает машинное обучение?
Найдено 7 документов
Извлечение вектора запроса...
Обработка документов...

=== УПОРЯДОЧЕНИЕ ПО ПОДОБИЮ ===
1. example_docs/ai_introduction.txt (Оценка: 0.451)
2. example_docs/machine_learning.md (Оценка: 0.388)
3. example_docs/qwen3-reranking-models.md (Оценка: 0.354)
4. example_docs/ollama-parallelism.md (Оценка: 0.338)
5. example_docs/ollama-reranking-models.md (Оценка: 0.318)
6. example_docs/programming_basics.txt (Оценка: 0.296)
7. example_docs/setup.log (Оценка: 0.282)

Обработано 7 документов за 2.023с (в среднем: 0.289с на документ)
Переупорядочивание документов с использованием модели переупорядочивания...
Реализация переупорядочивания с использованием подхода cross-encoder с dengcao/Qwen3-Reranker-4B:Q5_K_M

=== УПОРЯДОЧЕНИЕ С ИСПОЛЬЗОВАНИЕМ МОДЕЛИ ПЕРЕУПОРЯДОЧИВАНИЯ ===
1. example_docs/ai_introduction.txt (Оценка: 0.343)
2. example_docs/machine_learning.md (Оценка: 0.340)
3. example_docs/programming_basics.txt (Оценка: 0.320)
4. example_docs/setup.log (Оценка: 0.313)
5. example_docs/ollama-parallelism.md (Оценка: 0.313)
6. example_docs/qwen3-reranking-models.md (Оценка: 0.312)
7. example_docs/ollama-reranking-models.md (Оценка: 0.306)

Обработано 7 документов за 1.984с (в среднем: 0.283с на документ)

Код модели переупорядочивания на Go для вызова Ollama

Беру большую часть кода из поста Reranking text documents with Ollama using Embedding... и добавляю эти фрагменты:

В конце функции runRnk():

  startTime = time.Now()
	// переупорядочивание с использованием модели переупорядочивания
	fmt.Println("Переупорядочивание документов с использованием модели переупорядочивания...")

	// 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("Ошибка переупорядочивания документов: %v", err)
	}

	fmt.Println("\n=== УПОРЯДОЧЕНИЕ С ИСПОЛЬЗОВАНИЕМ МОДЕЛИ ПЕРЕУПОРЯДОЧИВАНИЯ ===")
	for i, doc := range rerankedDocs {
		fmt.Printf("%d. %s (Оценка: %.3f)\n", i+1, doc.Path, doc.Score)
	}

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

	fmt.Printf("\nОбработано %d документов за %.3fs (в среднем: %.3fs на документ)\n",
		len(rerankedDocs), totalTime.Seconds(), avgTimePerDoc.Seconds())

Затем добавить несколько дополнительных функций:

func rerankDocuments(validDocs []Document, query, rerankingModel, ollamaBaseURL string) ([]Document, error) {
	// Поскольку стандартный Ollama не имеет прямого API для переупорядочивания, мы реализуем
	// переупорядочивание, генерируя векторы представлений для пар запрос-документ и оценивая их

	fmt.Println("Реализация переупорядочивания с использованием подхода cross-encoder с", rerankingModel)

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

	for i, doc := range validDocs {
		// Создание промпта для переупорядочивания, объединяющего запрос и документ
		rerankPrompt := fmt.Sprintf("Query: %s\n\nDocument: %s\n\nRelevance:", query, doc.Content)

		// Получение вектора представления для объединенного промпта
		embedding, err := getEmbedding(rerankPrompt, rerankingModel, ollamaBaseURL)
		if err != nil {
			fmt.Printf("Предупреждение: Не удалось получить вектор переупорядочивания для документа %d: %v\n", i, err)
			// Заглушка с нейтральной оценкой
			rerankedDocs[i].Score = 0.5
			continue
		}

		// Использование величины вектора представления в качестве оценки релевантности
		// (Это упрощённый подход - на практике вы бы использовали обученную модель переупорядочивания)
		score := calculateRelevanceScore(embedding)
		rerankedDocs[i].Score = score
		// fmt.Printf("Документ %d переупорядочен с оценкой: %.4f\n", i, score)
	}

	// Сортировка документов по оценке переупорядочивания (по убыванию)
	sort.Slice(rerankedDocs, func(i, j int) bool {
		return rerankedDocs[i].Score > rerankedDocs[j].Score
	})

	return rerankedDocs, nil
}

func calculateRelevanceScore(embedding []float64) float64 {
	// Простая оценка на основе величины вектора и положительных значений
	var sumPositive, sumTotal float64
	for _, val := range embedding {
		sumTotal += val * val
		if val > 0 {
			sumPositive += val
		}
	}

	if sumTotal == 0 {
		return 0
	}

	// Нормализация и комбинация величины с смещением в сторону положительных значений
	magnitude := math.Sqrt(sumTotal) / float64(len(embedding))
	positiveRatio := sumPositive / float64(len(embedding))

	return (magnitude + positiveRatio) / 2
}

Не забудьте импортировать немного math

import (
	"math"
)

Теперь соберите его

go build -o rnk

и теперь запустите этот простой прототип RAG-модели переупорядочивания

./rnk ./example_query.txt ./example_docs

Полезные ссылки