how to build local knowledge base by LLM 1?

Table of Contents

we can build a local knowledge base powered by RAG (Retrieval-Augmented Generation) using:

  • 🔍 ChromaDB (embedded vector DB)
  • 🧠 Ollama (local LLM: Mistral, LLaMA, etc.)
  • ✍️ Go code to ingest documents, embed chunks, and query with context

✅ Prerequisites

Install the following tools:

# Chroma as a Python backend server
pip install chromadb

# Ollama for running local LLMs (like mistral)
brew install ollama      # or follow: https://ollama.com/download
ollama run mistral       # or other models

# Go packages
go get github.com/go-resty/resty/v2
go get github.com/google/uuid

🗂️ Project Structure

go-local-rag/
├── main.go
├── embedder.go       # Calls embedding model (local or OpenAI)
├── chroma_client.go  # Handles Chroma DB operations
├── prompt.go         # Constructs LLM prompts
├── docs/
│   └── sample.txt

🧠 Step-by-Step Sample: main.go

package main

import (
    "fmt"
    "log"
    "strings"

    "your_project/chroma"
    "your_project/embedder"
    "your_project/prompt"
)

func main() {
    docs := []string{
        "Go is a statically typed, compiled programming language designed at Google.",
        "It is syntactically similar to C, but with memory safety, garbage collection, and CSP-style concurrency.",
    }

    // Step 1: Embed and store in ChromaDB
    for _, doc := range docs {
        chunks := strings.Split(doc, ". ") // naive splitter
        for _, chunk := range chunks {
            vec := embedder.EmbedText(chunk)
            chroma.Upsert("golang-knowledge", chunk, vec)
        }
    }

    // Step 2: User query
    query := "What is Go and who designed it?"
    qvec := embedder.EmbedText(query)

    // Step 3: Retrieve relevant docs
    topChunks := chroma.Query("golang-knowledge", qvec)

    // Step 4: Compose prompt and send to LLM (Ollama)
    finalPrompt := prompt.Build(query, topChunks)
    resp := prompt.CallOllama("mistral", finalPrompt)

    fmt.Println("🔎 Answer:\n", resp)
}

🔌 Sample: embedder.go (Ollama embedding or OpenAI)

package embedder

import (
    "bytes"
    "encoding/json"
    "log"
    "net/http"
)

func EmbedText(text string) []float64 {
    payload := map[string]string{"model": "nomic-embed-text", "prompt": text}
    body, _ := json.Marshal(payload)

    resp, err := http.Post("http://localhost:11434/api/embeddings", "application/json", bytes.NewBuffer(body))
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    var result struct {
        Embedding []float64 `json:"embedding"`
    }
    json.NewDecoder(resp.Body).Decode(&result)
    return result.Embedding
}

🧱 Sample: chroma_client.go

package chroma

import (
    "bytes"
    "encoding/json"
    "fmt"
    "log"
    "net/http"

    "github.com/google/uuid"
)

type ChromaDoc struct {
    ID      string
    Content string
    Vector  []float64
}

var chromaURL = "http://localhost:8000"

func Upsert(collection string, content string, vec []float64) {
    payload := map[string]interface{}{
        "ids":     []string{uuid.New().String()},
        "documents": []string{content},
        "embeddings": [][]float64{vec},
    }
    data, _ := json.Marshal(payload)

    url := fmt.Sprintf("%s/api/v1/collections/%s/upsert", chromaURL, collection)
    _, err := http.Post(url, "application/json", bytes.NewReader(data))
    if err != nil {
        log.Fatal(err)
    }
}

func Query(collection string, vec []float64) []string {
    payload := map[string]interface{}{
        "embedding": vec,
        "n_results": 3,
    }
    data, _ := json.Marshal(payload)

    url := fmt.Sprintf("%s/api/v1/collections/%s/query", chromaURL, collection)
    resp, err := http.Post(url, "application/json", bytes.NewReader(data))
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    var result struct {
        Documents [][]string `json:"documents"`
    }
    json.NewDecoder(resp.Body).Decode(&result)
    if len(result.Documents) > 0 {
        return result.Documents[0]
    }
    return []string{}
}

🧠 Sample: prompt.go

package prompt

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
    "strings"
)

func Build(query string, contexts []string) string {
    contextStr := strings.Join(contexts, "\n")
    return fmt.Sprintf("Answer the question based on the context:\n\n%s\n\nQuestion: %s", contextStr, query)
}

func CallOllama(model, prompt string) string {
    payload := map[string]string{
        "model":  model,
        "prompt": prompt,
    }
    body, _ := json.Marshal(payload)

    resp, err := http.Post("http://localhost:11434/api/generate", "application/json", bytes.NewBuffer(body))
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    var out struct {
        Response string `json:"response"`
    }
    json.NewDecoder(resp.Body).Decode(&out)
    return out.Response
}

🧪 Running It

  1. Start ChromaDB:

    chroma run --path ./chroma-data
  2. Start Ollama:

    ollama run mistral
  3. Run your Go program:

    go run main.go

📌 Next Steps

  • Add PDF/Markdown ingestion using github.com/ledongthuc/pdf or blackfriday
  • Switch to LlamaIndex or LangChain via REST API for rapid prototyping
  • Store user feedback to improve answers
  • Build a simple web UI with Go + Svelte/React

Comments |0|

Legend *) Required fields are marked
**) You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>
Category: 似水流年