Ollama と Qwen3 Reranker モデルを使用したドキュメントの再ランキング - Go 言語で

RAGを実装中ですか?ここにGoのコードの一部 - 2...

目次

標準的な Ollama には直接のリランク API がありませんので、 クエリとドキュメントのペアに対して埋め込みを生成し、それらをスコアリングすることで、Qwen3 リランカーを使用したリランキング(GO 言語で) を実装する必要があります。

先週、私は少し Ollama と Qwen3 埋め込みモデルを使用したテキストドキュメントのリランキング - GO 言語で を試しました。

今日はいくつかの Qwen3 リランカー モデルを試してみましょう。 Ollama 上で利用可能な新しい Qwen3 埋め込み & リランカー モデル がいくつかあります。私は中規模の dengcao/Qwen3-Reranker-4B:Q5_K_M を使用しています。

リランキング ドッグス

テスト実行: TL;DR

動作します。また、かなり速く、標準的な方法ではありませんが、それでも:

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

使用する埋め込みモデル: dengcao/Qwen3-Embedding-4B:Q5_K_M
Ollama のベース URL: 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 秒/ドキュメント)
リランカー モデルを使用してドキュメントをリランクしています...
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 秒/ドキュメント)

Ollama を呼び出すための Go 言語でのリランカー コード

投稿 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("クロスエンコーダー方式でリランクを実装しています:", 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

有用なリンク