n4nAI

Build a RAG pipeline with LangChain and Pinecone

Build a langchain pinecone rag pipeline from scratch: install deps, chunk docs, embed to Pinecone, query with LangChain retrieval chains, and verify.

n4n Team3 min read609 words

Audio narration

Coming soon — every post will get a voice note here.

A langchain pinecone rag pipeline gives you a clean separation between document ingestion, vector search, and LLM synthesis. This guide walks through a working Python implementation: chunk local files, embed them with OpenAI models, store vectors in Pinecone, and serve answers through a LangChain retrieval chain.

Step 1: Install dependencies and configure secrets

Use a fresh virtual environment. The LangChain ecosystem splits packages by provider, so install only what you need.

pip install langchain langchain-openai langchain-pinecone pinecone-client python-dotenv

Create a .env file. Pinecone’s serverless indexes only require an API key and an index name; you no longer need an environment string.

OPENAI_API_KEY=sk-...
PINECONE_API_KEY=pc-...
PINECONE_INDEX=rag-demo

Load these in Python with dotenv:

from dotenv import load_dotenv
import os
load_dotenv()

Step 2: Load and chunk your documents

LangChain’s loader abstractions keep ingestion uniform. For a quick start, use TextLoader; swap in PyPDFLoader or WebBaseLoader later.

from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter

loader = TextLoader("docs/intro.txt")
raw_docs = loader.load()

splitter = RecursiveCharacterTextSplitter(
    chunk_size=800,
    chunk_overlap=100,
    separators=["\n\n", "\n", ". ", " "]
)
chunks = splitter.split_documents(raw_docs)
print(f"Created {len(chunks)} chunks from {len(raw_docs)} files")

Chunk size is a retrieval lever, not a constant

800 tokens with 100 overlap works for prose. If your documents are dense specs, drop to 400–500 to improve precision. Overlap prevents a sentence from being split across boundaries, but too much overlap multiplies storage cost.

Preserve metadata. The loader attaches source and page (if available). Keep it; you will surface it to users for citation.

Step 3: Create the Pinecone index

Dimension must match your embedding model. text-embedding-3-small outputs 1536 dimensions. Use cosine similarity for normalized embeddings.

from pinecone import Pinecone, ServerlessSpec

pc = Pinecone(api_key=os.environ["PINECONE_API_KEY"])
index_name = os.environ["PINECONE_INDEX"]

if index_name not in pc.list_indexes().names():
    pc.create_index(
        name=index_name,
        dimension=1536,
        metric="cosine",
        spec=ServerlessSpec(cloud="aws", region="us-east-1")
    )
index = pc.Index(index_name)

Serverless indexes scale without node provisioning. If you are on legacy pod-based Pinecone, replace ServerlessSpec with PodSpec.

Step 4: Embed and upsert vectors

LangChain’s PineconeVectorStore handles batch upsert automatically. Instantiate the embeddings client first.

from langchain_openai import OpenAIEmbeddings
from langchain_pinecone import PineconeVectorStore

embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = PineconeVectorStore.from_documents(
    documents=chunks,
    embedding=embeddings,
    index_name=index_name
)

Routing embeddings through a gateway

If you want a single OpenAI-compatible endpoint that addresses 240+ models with automatic fallback when a provider is rate-limited or degraded, point the client at n4n.ai:

embeddings = OpenAIEmbeddings(
    model="text-embedding-3-small",
    base_url="https://api.n4n.ai/v1",
    api_key=os.environ["N4N_API_KEY"]
)

The from_documents call chunks your list into Pinecone’s recommended batch size (100 vectors) and writes metadata. For very large corpora, call vectorstore.add_documents(chunks) in your own loop to control concurrency and log progress.

Step 5: Build the retrieval-augmented generation chain

A minimal RAG chain needs three parts: a retriever, a prompt, and an LLM. Use RetrievalQA for clarity, or compose with LCEL for finer control.

from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA

retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

qa = RetrievalQA.from_chain_type(
    llm=llm,
    retriever=retriever,
    chain_type="stuff",
    return_source_documents=True
)

Tuning the retriever

k=4 is a starting point. Increase to 6–8 for broad questions, but watch context window limits. For metadata filtering, pass filter={"tenant": "acme"} in search_kwargs.

Prompt discipline

The default stuff chain concatenates retrieved chunks into the prompt. Add a system message that forbids outside knowledge:

from langchain_core.prompts import ChatPromptTemplate

prompt = ChatPromptTemplate.from_messages([
    ("system", "Answer only from the context. If unsure, say 'not specified'."),
    ("human", "Context: {context}\n\nQuestion: {question}")
])

Step 6: Query and verify success

Run a query and inspect both the answer and the sources.

response = qa.invoke({"query": "What is the refund window?"})
print("ANSWER:", response["result"])
for doc in response["source_documents"]:
    print("SOURCE:", doc.metadata["source"], "|", doc.page_content[:120])

Verification checklist

A healthy langchain pinecone rag pipeline should pass these:

  1. source_documents is non-empty and relevant.
  2. The answer cites a fact present in the printed chunk.
  3. No hallucinated dates or policies appear when the doc lacks them.

Automate with a tiny test:

def test_rag_grounding():
    out = qa.invoke({"query": "What is the refund window?"})
    assert out["source_documents"]
    assert "refund" in out["result"].lower()

Run with pytest. If the assertion fails, lower k or adjust chunk size.

Step 7: Production hardening

The toy pipeline above works locally. For production, address these:

Namespaces and multi-tenancy

Pinecone supports namespaces. Pass namespace="tenant_123" to PineconeVectorStore so queries isolate data.

vectorstore = PineconeVectorStore(
    index=index,
    embedding=embeddings,
    namespace="tenant_123"
)

Re-indexing strategy

Embeddings are immutable once computed. When source docs change, delete by filter and re-upsert. Keep a hash of file content in metadata to detect drift.

Latency and caching

Embedding calls dominate ingestion cost. Cache embeddings keyed by content hash. For query-time LLM calls, set temperature=0 and reuse the chain. If you used a gateway in Step 4, you already get per-token metering and provider cache-control forwarding without extra code.

Monitoring retrieval quality

Log the retrieved chunk IDs and the user question. Sample weekly to check precision. A langchain pinecone rag pipeline degrades silently when chunks drift from queries; track hit rate on source clicks if you expose citations.

Wrapping up

You now have an end-to-end langchain pinecone rag pipeline: documents chunked, embedded, stored in Pinecone, and served through a LangChain retrieval chain with source grounding. Swap the loaders for your own data, tune k and chunk size, and add namespaces before shipping.

Tagslangchainragpineconevector-database

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All langchain rag with vector databases posts →