Teksten herordenen met Ollama en Qwen3 Embedding LLM - 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.
Een andere soortgelijke code, maar met gebruik van Qwen3 Reranker:
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 Embeddings op Ollama - Voorbeelduitvoer
In alle drie de testgevallen was het reranking met embeddings 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
Queryembedding extraheren...
Documenten verwerken...
=== 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 parallelle 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 parallelle aanvragen?
7 documenten gevonden
Queryembedding extraheren...
Documenten verwerken...
=== 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
Queryembedding extraheren...
Documenten verwerken...
=== 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 vrij in elk onderhoudend of commercieel doel of upload het naar GitHub als je dat wilt. 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))
// Extraheer embeddings voor query
fmt.Println("Queryembedding extraheren...")
queryEmbedding, err := getEmbedding(query, embeddingModel, ollamaBaseURL)
if err != nil {
log.Fatalf("Fout bij het krijgen van de queryembedding: %v", err)
}
// Verwerk documenten
fmt.Println("Documenten verwerken...")
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 krijgen 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",
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
- Reranking van tekstdocumenten met Ollama en Qwen3 Rerankermodel - in Go
- Qwen3 Embedding & Rerankermodellen op Ollama: State-of-the-Art-prestaties
- https://en.wikipedia.org/wiki/Retrieval-augmented_generation
- Installeer en configureer Ollama modellenlocatie
- Hoe Ollama parallelle 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
- Vergelijking van LLM-samenvattingen
- Cloud LLM-aanbieders