Quickstart

Install HybridDB, create your first table, and run keyword, semantic, and hybrid search — plus versioned agent memory — in about 60 seconds.

Install

  1. Install the package
    pip install hybriddb

    HybridDB uses ChromaDB’s bundled local MiniLM embedding by default — no API key required. Optional extras:

    pip install "hybriddb[analytics]"   # DuckDB columnar analytics
  2. Create a table and insert rows

    Column types drive the search indexes automatically: TEXT gets FTS5 keyword search, LONGTEXT gets both keyword and vector search.

    from hybriddb import HybridDB, LONGTEXT, TEXT
    
    db = HybridDB("./my_data")
    db.create_table("docs", {"title": TEXT, "body": LONGTEXT})
    
    db.insert("docs", {"title": "Getting Started", "body": "A guide to using HybridDB..."})
    db.insert("docs", {"title": "API Reference", "body": "Full API documentation..."})
  3. Run your first hybrid search
    # Search every text column at once
    db.search_all("docs", "getting started")
    
    # Search one column — hybrid (RRF fusion of keyword + semantic) is the default
    db.search("docs", "body", "how do I begin", mode="hybrid")
    
    # Structured query with parameters
    db.query("docs", where="title LIKE ?", params=("%start%",))

Try the three search modes

from hybriddb import SearchMode

# Keyword only — fast, exact, great for names and titles
db.search("contacts", "name", "Alice", mode="keyword")

# Semantic only — finds "9am standup" when searching for "morning meetings"
db.search("memories", "content", "team rituals", mode=SearchMode.SEMANTIC)

# Hybrid — best of both, RRF fusion, the default
db.search("docs", "body", "getting started guide", mode=SearchMode.HYBRID)

# Search across ALL text columns at once
db.search_all("contacts", "engineering manager")
Hybrid is the right default

Measured on real BEIR benchmarks, hybrid fusion beats both single modes on every metric — +11% nDCG over keyword on NFCorpus, +5% on SciFact. See Benchmarks.

Next: agent memory with versioned tables

Opt any table into versioned history with a tamper-evident hash chain — built for agent memory, session logs, and audit trails:

db = HybridDB("./agent_memory")
db.create_table("memories", {"id": TEXT, "content": LONGTEXT}, versioned=True)
db.author = "assistant"                       # recorded on every event

db.upsert("memories", {"id": "m1", "content": "User prefers morning meetings"})
db.upsert("memories", {"id": "m1", "content": "User prefers afternoon standups"})

db.history("memories", key="m1")              # every version, with hashes
db.diff("docs", from_seq=1, to_seq=2)         # what changed between two points
db.as_of("memories", seq=3)                   # what the agent knew at seq 3

cp = db.checkpoint("memories", "before-cleanup")
db.rollback("memories", checkpoint="before-cleanup")   # rewind — nothing erased
db.verify_chain("memories")                   # -> {"valid": True, ...}
History is append-only

Rollback re-applies restored state as new versions, so the audit trail stays complete. verify_chain() detects any direct tampering with the history store.

Async (for FastAPI and friends)

All core operations have async wrappers that run blocking SQLite/ChromaDB work in a worker thread:

await db.acreate_table("messages", {"content": LONGTEXT})
await db.ainsert("messages", {"content": "async-safe memory"})
results = await db.asearch("messages", "content", "memory")

Continue to Core concepts for column types, versioned tables, and the journal — or jump to the API reference.

Source of truth for this page: HybridDB · open-assistants-lab/HybridDB