Building a Semantic Search Engine with LangChain: A Step-by-Step Guide
- Authors
- Name
- Abhishek Thakur
- @abhi_____thakur
LangChain is a flexible framework that makes it easy to build applications using LLMs, including semantic search engines. In this guide, you'll learn how to build a semantic search engine on top of PostgreSQL using LangChain and pgvector.
🧱 Overview
We'll use:
- LangChain: For orchestrating LLM pipelines
- pgvector: PostgreSQL extension for vector similarity search
- Hugging Face Embeddings: To convert text into embeddings
- PostgreSQL: As our structured data source and vector store
📦 Prerequisites
Python ≥ 3.8
A PostgreSQL database with data to search
Install
pgvector
in PostgreSQL:CREATE EXTENSION IF NOT EXISTS vector;
Install required Python packages:
pip install langchain langchain-community pgvector psycopg2-binary pip install sentence-transformers
🗃️ Step 1: Connect to PostgreSQL
Use psycopg2 to connect to your PostgreSQL instance.
import psycopg2
connection = psycopg2.connect(
dbname="mydb",
user="user",
password="password",
host="localhost",
port=5432
)
🧠 Step 2: Generate Embeddings
Use sentence-transformers
to generate semantic embeddings.
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('all-MiniLM-L6-v2')
texts = ["This is a semantic search example.", "Another document."]
embeddings = model.encode(texts).tolist()
📥 Step 3: Store Embeddings in PostgreSQL (pgvector)
Create a table with a vector
column and insert data.
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
content TEXT,
embedding vector(384)
);
cursor = connection.cursor()
for text, emb in zip(texts, embeddings):
cursor.execute(
"INSERT INTO documents (content, embedding) VALUES (%s, %s)",
(text, emb)
)
connection.commit()
🔍 Step 4: Query Similar Documents
Use vector similarity with pgvector
:
query_text = "Find examples of semantic search"
query_vector = model.encode([query_text])[0].tolist()
cursor.execute(
"SELECT content FROM documents ORDER BY embedding <-> %s LIMIT 5",
(query_vector,)
)
results = cursor.fetchall()
for result in results:
print(result[0])
🔁 Optional: Add LLM for Summarization
Use OpenAI or Ollama with LangChain for summarizing the results.
from langchain_community.llms import Ollama
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
llm = Ollama(model="mistral")
prompt = PromptTemplate.from_template("Summarize these results:
{results}")
chain = LLMChain(llm=llm, prompt=prompt)
response = chain.run(results="
".join(r[0] for r in results))
print(response)
🧰 Useful Tips
Create an index on the vector column for faster queries:
CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
Normalize embeddings for better cosine similarity:
import numpy as np def normalize(v): return v / np.linalg.norm(v)
🚀 Conclusion
You've now built a simple yet powerful semantic search engine using LangChain, pgvector, and Hugging Face embeddings. Extend it with:
- A web interface (Streamlit/FastAPI)
- LangChain Retriever for full RAG pipelines
- LLM integration for natural language querying