Herordenen van tekstdocumenten met Ollama en Qwen3 Embedding model - in Go
RAG implementeren? Hier zijn enkele codefragmenten in Golang.
Dit kleine Reranking Go codevoorbeeld roept Ollama aan om embeddings te genereren voor de query en voor elk kandidaatdocument, en vervolgens sorteren in dalende volgorde op cosinus-afstand.
We hebben al een soortgelijke activiteit gedaan - Reranking met embeddingmodellen maar dat was in Python, met een andere LLM en bijna een jaar geleden.
TL;DR
Het resultaat ziet er zeer goed uit, de snelheid is 0,128s per document. Een vraag wordt als een document geteld. En het sorteren en afdrukken zijn ook opgenomen in deze statistiek.
LLM-geheugengebruik:
Hoewel de modelgrootte op de SSD (ollama ls
) minder is dan 3 GB
dengcao/Qwen3-Embedding-4B:Q5_K_M 7e8c9ad6885b 2,9 GB
Op de GPU VRAM neemt het (niet een beetje) meer in beslag: 5,5 GB. (ollama ps
)
NAAM ID GROOTTE
dengcao/Qwen3-Embedding-4B:Q5_K_M 7e8c9ad6885b 5,5 GB
Als je een GPU met 8 GB hebt - moet het wel lukken.
Testen van Reranking met Embeddingmodellen op Ollama - Voorbeelduitvoer
In alle drie de testgevallen was het reranking met embedding met behulp van het dengcao/Qwen3-Embedding-4B:Q5_K_M Ollama-model geweldig! Bekijk het zelf.
We hebben 7 bestanden met tekst die beschrijven wat hun bestandsnaam zegt:
- ai_introduction.txt
- machine_learning.md
- qwen3-reranking-models.md
- ollama-parallelism.md
- ollama-reranking-models.md
- programming_basics.txt
- setup.log
test runs:
Reranking test: Wat is kunstmatige intelligentie en hoe werkt machine learning?
./rnk example_query.txt example_docs/
Gebruikte embeddingmodel: dengcao/Qwen3-Embedding-4B:Q5_K_M
Ollama basis-URL: http://localhost:11434
Verwerken van querybestand: example_query.txt, doelmap: example_docs/
Query: Wat is kunstmatige intelligentie en hoe werkt machine learning?
7 documenten gevonden
Extractie van queryembedding...
Verwerken van documenten...
=== RANGSCHIKKEN OP LIJKBAREHEID ===
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 documenten verwerkt in 0,899s (gemiddeld: 0,128s per document)
Reranking test: Hoe verwerkt Ollama parallele aanvragen?
./rnk example_query2.txt example_docs/
Gebruikte embeddingmodel: dengcao/Qwen3-Embedding-4B:Q5_K_M
Ollama basis-URL: http://localhost:11434
Verwerken van querybestand: example_query2.txt, doelmap: example_docs/
Query: Hoe verwerkt Ollama parallele aanvragen?
7 documenten gevonden
Extractie van queryembedding...
Verwerken van documenten...
=== RANGSCHIKKEN OP LIJKBAREHEID ===
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 documenten verwerkt in 0,858s (gemiddeld: 0,123s per document)
Reranking test: Hoe kunnen we het reranking van het document met Ollama doen?
./rnk example_query3.txt example_docs/
Gebruikte embeddingmodel: dengcao/Qwen3-Embedding-4B:Q5_K_M
Ollama basis-URL: http://localhost:11434
Verwerken van querybestand: example_query3.txt, doelmap: example_docs/
Query: Hoe kunnen we het reranking van het document met Ollama doen?
7 documenten gevonden
Extractie van queryembedding...
Verwerken van documenten...
=== RANGSCHIKKEN OP LIJKBAREHEID ===
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 documenten verwerkt in 0,882s (gemiddeld: 0,126s per document)
Go Broncode
Zet alles in een map en compileer het zoals
go build -o rnk
Gebruik het vrijelijk voor entertainende of commerciële doeleinden of upload het naar GitHub als je dat leuk vindt. MIT-licentie.
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: "RAG-systeem met behulp van Ollama-embeddings",
Long: "Een eenvoudig RAG-systeem dat embeddings extrahiert en documenten rangschikt met behulp van 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", "Embeddingmodel dat moet worden gebruikt")
rootCmd.Flags().StringVarP(&ollamaBaseURL, "url", "u", "http://localhost:11434", "Ollama basis-URL")
}
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("Gebruikte embeddingmodel: %s\n", embeddingModel)
fmt.Printf("Ollama basis-URL: %s\n", ollamaBaseURL)
fmt.Printf("Verwerken van querybestand: %s, doelmap: %s\n", queryFile, targetDir)
// Lees query uit bestand
query, err := readQueryFromFile(queryFile)
if err != nil {
log.Fatalf("Fout bij het lezen van het querybestand: %v", err)
}
fmt.Printf("Query: %s\n", query)
// Zoek alle tekstbestanden in doelmap
documents, err := findTextFiles(targetDir)
if err != nil {
log.Fatalf("Fout bij het zoeken naar tekstbestanden: %v", err)
}
fmt.Printf("7 documenten gevonden\n", len(documents))
// Extract embeddings voor query
fmt.Println("Extractie van queryembedding...")
queryEmbedding, err := getEmbedding(query, embeddingModel, ollamaBaseURL)
if err != nil {
log.Fatalf("Fout bij het verkrijgen van queryembedding: %v", err)
}
// Verwerk documenten
fmt.Println("Verwerken van documenten...")
validDocs := make([]Document, 0)
for _, doc := range documents {
embedding, err := getEmbedding(doc.Content, embeddingModel, ollamaBaseURL)
if err != nil {
fmt.Printf("Waarschuwing: Mislukt om embedding te verkrijgen voor %s: %v\n", doc.Path, err)
continue
}
similarity := cosineSimilarity(queryEmbedding, embedding)
doc.Score = similarity
validDocs = append(validDocs, doc)
}
if len(validDocs) == 0 {
log.Fatalf("Geen documenten konden succesvol worden verwerkt")
}
// Sorteer op lijkbareheid (dalend)
sort.Slice(validDocs, func(i, j int) bool {
return validDocs[i].Score > validDocs[j].Score
})
// Toon resultaten
fmt.Println("\n=== RANGSCHIKKEN OP LIJKBAREHEID ===")
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("\n7 documenten verwerkt in %.3fs (gemiddeld: %.3fs per 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("Waarschuwing: Kon bestand %s niet lezen: %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("ollama API fout: %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 vertegenwoordigt de aanvraagpayload voor de Ollama embedding API
type OllamaEmbeddingRequest struct {
Model string `json:"model"`
Prompt string `json:"prompt"`
}
// OllamaEmbeddingResponse vertegenwoordigt de respons van de Ollama embedding API
type OllamaEmbeddingResponse struct {
Embedding []float64 `json:"embedding"`
}
// Document vertegenwoordigt een document met zijn metadata
type Document struct {
Path string
Content string
Score float64
}
Nuttige links
- Ollama cheat sheet
- Qwen3 Embedding & Reranker Modellen op Ollama: State-of-the-Art Prestaties
- https://en.wikipedia.org/wiki/Retrieval-augmented_generation
- Installeer en configureer Ollama modellenlocatie
- Hoe Ollama parallele aanvragen verwerkt
- Effectieve prompts schrijven voor LLMs
- Testen van LLMs: gemma2, qwen2 en Mistral Nemo op Ollama
- LLM vergelijking: Mistral Small, Gemma 2, Qwen 2.5, Mistral Nemo, LLama3 en Phi - Op Ollama
- Test: Hoe Ollama Intel CPU-prestaties en efficiënte kernen gebruikt
- Reranking met embeddingmodellen op Ollama in Python
- Vergelijken van LLM samenvattingsvermogen
- Cloud LLM leveranciers