Evoke — Getting Started

Install Evoke (II-42/ii42) with Docker or package managers, create your first hybrid keyword-plus-semantic index, run your first ii42_query, and understand the async write path.

October 7, 2026
evokeii42postgresgetting-startedsetupsql

Getting Started with Evoke

Evoke ships as a Postgres extension named ii42. This page walks install → first index → first query → how the write path behaves in production.

The quickest start is the PostgreSQL 18 image. On a fresh data volume it creates the extension and enables the shared runtime:

docker pull ghcr.io/intelligent-internet/ii-42:pg18-v0.2.5

Start the container with the command from the README, then build an index and query it:

CREATE INDEX docs_semantic_idx ON docs USING ii42 (body) WITH (sae = true);

SELECT d.id, d.title,
       ii42_query('docs_semantic_idx'::regclass, 'database search architecture') AS score
FROM docs AS d
ORDER BY score DESC
LIMIT 10;

BM25 is the default behavior; sae = true turns on Sparse Semantic Retrieval — the learned-sparse expansion terms that make meaning-aware matches possible. Launch semantic search only after you've confirmed keyword search behaves the way you expect; one toggle, one index.

Package installs (PG17/PG18)

Official packages for PostgreSQL 17 and 18 on Linux x86-64, with checksums, are on the v0.2.5 release page. Two notes that will save you an hour:

  • Package installs load the extension through shared_preload_libraries and require a PostgreSQL restart — don't skip the restart when the query function isn't found.
  • Existing databases follow a dedicated upgrade guide rather than treating Evoke like a plain extension bump.

Air-gapped installs are a design goal, not an afterthought:

The release includes a checksummed offline Docker archive built for deployments where text should not leave the server. Verify checksums at load time; treat unsigned copies of the image as a supply-chain risk, same as any model binary.

Your first index: what happens on write

INSERT INTO docs (body, title) VALUES ('...long text...', 'A doc');

Three things occur, and only the first is on the critical path:

  1. Keyword evidence is stored and the write commits immediately. Your INSERT does not wait for the model.
  2. The row is queued for semantic encoding by shared background workers — every connection hands its work to the same workers instead of loading its own model copy.
  3. Background workers encode the row with ONNX Runtime on ordinary CPUs, then publish the expansion terms into the same inverted index, in a separate namespace from keyword terms.

This split is the difference between Evoke and an embedding pipeline: the meaning layer cannot stall your write path, and there is no separate sync job that can silently drift.

Read path: one lookup

Every ii42_query() call is a single lookup against the one index. Keyword and semantic evidence add to one score per document, and each returned row is checked against the table as it currently stands — a deleted document can't resurface from a stale copy. Index maintenance follows PostgreSQL's own rules for row visibility, crash recovery, and physical replication.

Practical query checklist:

  • Exact identifiers still win. Product codes, case numbers, and error strings search as literal terms. If you only get keyword-quality results, confirm sae = true on the index.
  • Order by the returned score, not a mix — there is no second list to blend.
  • Sketch how many rows you'll fetch. Evoke is a first-stage retriever; for production pipelines, feed the top ~1,000 to a reranker rather than showing the raw order to users.

Model notes

The model (~30M parameters, built on IBM's Granite-Embedding-30M-Sparse) is bundled with ONNX Runtime in the official packages and Docker image — nothing separate to download. Source builds pull it from Hugging Face. English text is what's evaluated today; test your own corpus before committing a non-English deployment.

Verify it works

Sanity checks after install:

-- 1. Does the extension load?
SELECT * FROM pg_extension WHERE extname = 'ii42';

-- 2. Did the semantic index build?
SELECT indexrelid::regclass FROM pg_index WHERE indexrelid = 'docs_semantic_idx'::regclass;

-- 3. Does semantic expansion fire? (requires docs containing paraphrase-style matches)
SELECT d.id, ii42_query('docs_semantic_idx'::regclass, 'different words, same point') AS score
FROM docs AS d ORDER BY score DESC LIMIT 5;

If step 3 returns scores comparable to a pure keyword query, sae is off or encoding workers haven't finished backfill — give the queue a moment and check logs.