I'm building an AI workflow that uses a Vector Store Retriever connected to PGVector

Describe the problem/error/question

I’m building an AI workflow that uses a Vector Store Receiver connected to PGVector.Most of the time it retrieves the correct document, but occasionally a less relevant document is ranked higher than the one I actually expect. I have already confirmed the documents are embedded correctly and the same embedding model is used for indexing and querying. Is there anything to look at to improve retrieval quality

What is the error message (if any)?

Please share your workflow

(Select the nodes on your canvas and use the keyboard shortcuts CMD+C/CTRL+C and CMD+V/CTRL+V to copy and paste the workflow.)

Share the output returned by the last node

Information on your n8n setup

  • n8n version:1.123.x
  • Database (default: SQLite):
  • n8n EXECUTIONS_PROCESS setting (default: own, main):
  • Running n8n via (Docker, npm, n8n cloud, desktop app):
  • Operating system:

Hey @Oluwanifemi, while you wait for a response, here are some things that might help:

Suggested resources

Automatically matched to your question.

Docs:

Forum:

@merarisosa, @jabbson - you’ve helped with similar issues before, can you take a look?

Automatically suggested by n8n’s community bot. It’s a pilot - please share feedback here.

Hi @Oluwanifemi

If a less relevant document is ranking higher, it is often because the relevant document’s core message was “diluted” by surrounding text in its chunk, or the chunk was too small to provide enough context.

  • Adjust Chunk Size and Overlap: In your Text Splitter node, experiment with the chunk size. If chunks are too large (e.g., >1000 tokens), they contain multiple topics, making the vector an “average” of unrelated concepts. If they are too small (<200 tokens), they lack context. Try a size between 400–800 tokens with a 10–20% overlap.
  • Add Context to Chunks: If you are splitting large PDFs or markdown files, prepend the document title or section header to every chunk before embedding. You can do this using an Edit Fields (Set) node or a Code node right before the Vector Store Receiver. This forces the embedding model to anchor the vector to the specific document/section context.

Vector databases calculate distance between points using mathematical formulas. If the metric doesn’t match how your specific embedding model was trained, rankings will be skewed.

  • Check the PGVector Node: In your Vector Store Tool / PGVector node configuration, ensure the similarity metric is set to Cosine Distance (or Cosine Similarity). Most modern embedding models (like OpenAI’s text-embedding-3-*, Cohere, and Mistral) are optimized for Cosine similarity. If it is set to L2 (Euclidean) or Inner Product, it can cause irrelevant documents to score higher.

Does it help?

Something worth checking is what index type is on your PGVector column? If it is ivfflat or hnsw, that means you are truly doing approximate nearest neighbor search. If you are using the default settings, “approximate” can end up meaning the actual nearest match is missed and not simply deprioritized.

For ivfflat, check your list count and increase ‘probes’ at query time. More probes is closer to an exact search but even a higher probe count isn’t guaranteed equivalent. For hnsw, check ‘ef_search’. This is a quick thing to rule out before trying to rerank your search, especially because it could be a simple one-line SQL change.

Hi @Oluwanifemi
The vector store node has insert, get and retrieve modes but no replace or delete, so every run of your ingestion workflow appends a fresh copy of every chunk rather than updating the rows already there. Near-identical older copies then compete with the current chunk and can take the retriever’s top slots, which shows up exactly as an occasional wrong document winning. Count the rows and compare against what a single ingestion pass should produce:

SELECT count(*) FROM your_table;

If it comes out as a multiple of what you expect, clear the table, index once, and delete the rows for a source before re-indexing it from then on.

More on the wider RAG setup here:
https://axshul.site/n8n/guide/vector-stores-and-rag/

If you’ve already ruled out an embedding model mismatch, I’d look at how the documents are chunked. A lot of retrieval issues come from chunks containing multiple topics, which makes the embedding less focused

Another thing to check is whether your documents contain a lot of repeated wording or templates. If every record starts with the same paragraphs, the embeddings end up closer together than you’d expect, making it harder for PGVector to rank the best match

Finally, don’t rely too much on the similarity score itself. Compare the top 5 results instead. If the right document consistently appears near the top but not first, it’s usually a data quality issue rather than a problem with PGVector

Hi @Oluwanifemi

Its not a bug. This is the normal behaviour of single-stage vector search: cosine similarity measures topical similarity, not relevance. When two chunks are within ~0.03 cosine distance, which one ranks first is basically noise. Your embeddings being correct doesn’t prevent this.

Fix (5 minutes, native in n8n since 1.98):

  1. On your PGVector Vector Store node, set Limit to 20 (default is 4).
  2. Options → Add option → Rerank Results → enable.
  3. A Reranker connector appears at the bottom of the node — attach a Reranker Cohere sub-node, model rerank-v3.5.
  4. Leave the Vector Store Retriever and chain wiring as-is.

Why it works: your embedding model encoded the documents without ever seeing the query. The reranker is a cross-encoder — it reads query and chunk together and scores actual relevance. Too slow for the whole corpus, ideal over 20 candidates.

Set Limit as a plain number, not an expression — n8n#14151 silently ignores expressions there and falls back to 4.