Skip to main content
The built-in inmemory.Store loses all history on restart and returns every message regardless of size. The Qdrant adapter gives you:
  • Persistence — history survives process restarts.
  • Semantic retrievalGet returns the most recent turns; Search finds the most relevant turns for a query.
  • Scale — only the top-K messages enter the context window, so sessions can grow indefinitely.
The store plugs into the existing core.MemoryStore interface. The agent loop, orchestrator, and providers need zero changes.

Installation

go get github.com/lioarce01/chainforge
The Qdrant gRPC client (github.com/qdrant/go-client) is pulled in automatically.

Quick constructors

Two one-call constructors handle the most common configurations:
// OpenAI embeddings + any Qdrant instance
store, err := qdrantmem.NewWithOpenAI(
    "localhost:6334",                    // Qdrant URL
    "",                                  // Qdrant API key (empty for local)
    os.Getenv("OPENAI_API_KEY"),         // OpenAI API key
    qdrantmem.WithCollectionName("chat"), // optional extras
)

// Ollama embeddings + any Qdrant instance
store, err := qdrantmem.NewWithOllama(
    "localhost:6334",          // Qdrant URL
    "http://localhost:11434",  // Ollama base URL
    "nomic-embed-text",        // model name
    768,                       // embedding dimensions
)
Both accept optional ...Option arguments (e.g. WithCollectionName, WithTopK) applied after the defaults.

Quick start

import (
    chainforge "github.com/lioarce01/chainforge"
    qdrantmem "github.com/lioarce01/chainforge/pkg/memory/qdrant"
    "github.com/lioarce01/chainforge/pkg/memory/qdrant/embedders"
)

// OpenAI embeddings + local Qdrant
store, err := qdrantmem.New(
    qdrantmem.WithURL("localhost:6334"),
    qdrantmem.WithEmbedder(embedders.OpenAI(os.Getenv("OPENAI_API_KEY"))),
)
if err != nil { log.Fatal(err) }
defer store.Close()

agent, err := chainforge.NewAgent(
    chainforge.WithProvider(provider),
    chainforge.WithModel("your-model"),
    chainforge.WithMemory(store),
)

agent.Run(ctx, "session-1", "My name is Alice.")
agent.Run(ctx, "session-1", "What is my name?") // remembers across restarts

Qdrant setup

docker run -p 6333:6333 -p 6334:6334 qdrant/qdrant
gRPC is on port 6334 (used by chainforge). The REST dashboard is on 6333.
The collection is created automatically on first use.

Embedders

OpenAI (text-embedding-3-small, 1536 dims)

import "github.com/lioarce01/chainforge/pkg/memory/qdrant/embedders"

e := embedders.OpenAI(os.Getenv("OPENAI_API_KEY"))
Custom model:
import gogpt "github.com/sashabaranov/go-openai"

e := embedders.OpenAIWithModel(apiKey, gogpt.LargeEmbedding3, 3072)
Azure / proxy:
e := embedders.OpenAIWithBaseURL(apiKey, "https://my-azure.openai.azure.com/", gogpt.SmallEmbedding3, 1536)

Ollama (no new dependencies)

// nomic-embed-text runs locally and produces 768-dim vectors
e := embedders.Ollama("http://localhost:11434", "nomic-embed-text", 768)
Pull the model first:
ollama pull nomic-embed-text

MethodWhat it returnsWhen to use
Get(ctx, sessionID)Most recent TopK messages, ordered oldest→newestDefault agent loop — feed the LLM a recent window
Search(ctx, sessionID, query, topK)Most semantically similar messages to queryExplicit retrieval (e.g. “find what the user said about X”)
Get uses a Qdrant scroll ordered by sequence_num DESC — no embedding call needed. Search is only available on the concrete *Store type (not on core.MemoryStore):
store, _ := qdrantmem.New(...)

// Cast to access Search
results, err := store.Search(ctx, "session-1", "payment preferences", 5)

Configuration reference

OptionDefaultDescription
WithURL(rawURL)"localhost:6334"Parse host, port, and TLS from a URL string
WithHost(host, port)Set host and port directly
WithAPIKey(key)Qdrant Cloud API key
WithCollectionName(name)"chainforge_memory"Qdrant collection name
WithTopK(k)20Max messages returned by Get
WithEmbedder(e)Required. Embedding backend
WithTLS(enabled)auto-detected from URLForce TLS on/off

Clearing a session

err := store.Clear(ctx, "session-1")
All points for that session are deleted from Qdrant and the in-process sequence counter is reset.

Custom embedder

Implement the two-method interface to use any embedding API:
type Embedder interface {
    Embed(ctx context.Context, text string) ([]float32, error)
    Dims() uint64
}
Pass it with WithEmbedder(myEmbedder).

Full example

See examples/qdrant-memory-agent/ for a runnable interactive agent that persists memory across restarts.
docker run -p 6334:6334 qdrant/qdrant

OPENROUTER_API_KEY=... OPENAI_API_KEY=sk-... MODEL=... \
  go run ./examples/qdrant-memory-agent/