Skip to main content
A DSPy program retrieves passages, then a module such as dspy.ChainOfThought writes the answer. This page keeps the passages in Pixeltable: one row per document, a view that splits each document into chunks, and an embedding index on the chunk text. search() ranks those chunks with similarity() and returns them as passages. The DSPy RAG tutorial calls any Python function as the retriever. search() is that function. It returns a dspy.Prediction whose passages list is the chunk text, the same attribute dspy.retrievers.Embeddings returns. dspy.Retrieve reads a retrieval model from dspy.settings.rm. Leave that unset and call search() from the module.
This page matches Pixeltable 0.7.23, DSPy 3.4.1, sentence-transformers 6.2.0, and mistune 3.4.0. The index uses sentence-transformers/all-MiniLM-L6-v2. The model runs locally, so retrieval needs no API key. The answer step below calls dspy.LM('openai/gpt-4o-mini'), which reads OPENAI_API_KEY. Other providers use the same call with a different model string (connecting a language model).

Install

Pixeltable needs Python 3.11 or later. sentence-transformers pulls in PyTorch. On a machine without a GPU, install the CPU wheel first, or pip fetches a CUDA build that can be several gigabytes:
Skip the torch line if you already have a GPU build of PyTorch. The first insert downloads all-MiniLM-L6-v2. mistune parses the Markdown files in the next section.

Build the chunk index

The script creates the directory, the document table, the chunk view, and the index. It deletes an existing dspy_rag directory first, including its rows, so you can run it again. An application file declares the same table, view, and index. In a new folder, pxt init writes pixeltable.toml and marks the project root. pxt schema update creates dspy_rag.docs and dspy_rag.chunks. The app file declares only those two, so pxt schema prune would drop the runs table created later on this page. Running docs.insert() again duplicates the documents, since only the script clears the directory.
separators='paragraph' yields one chunk per Markdown paragraph.

Insert the documents

topic is metadata on the document. The chunk view includes it, so a later where clause can limit retrieval to one topic. The two files are the corpus:
The insert writes the documents and the chunks, and it fills the embedding index.

Retrieve passages

search() orders chunks by similarity() and returns at most k texts. topic adds a where clause on the document’s topic column.
The first passage is the chunk that says the embedding index updates when you insert a new row.

Filter on a column

The same function with topic set drops every chunk from the other documents. A question about how bees share the location of flowers returns the waggle-dance chunk. The same question with topic='pixeltable' returns the chunk about a where clause limiting the ranking to one collection:
More on where clauses: Queries and expressions.

Answer from the passages

RAG is a dspy.Module. forward() retrieves, then dspy.ChainOfThought reads context and question and writes response. The module also fills reasoning. Run export OPENAI_API_KEY=... first.

Inserts update the index

Insert another document. The view adds a chunk for the new paragraph, and the embedding index adds its vector. The next call to search() returns that chunk.
The first passage is the new chunk, the one that names ChainOfThought.

Store the answer

To keep a DSPy answer, write the question and the response into a table:

Embedding indexes

Declare an index and rank rows with similarity().
Last modified on November 7, 2026