Pinecone Integration Guide
What is a vector database?
ANN indexes (HNSW) over learned embeddings — RAG's retrieval half.
Pinecone is serverless ANN search over dense, sparse, or hybrid vectors. The killer feature for everyone tired of running an embedding service is integrated inference: `createIndexForModel` binds an embedding model (e.g. `multilingual-e5-large`) to the index, so you `upsertRecords` and `searchRecords` with raw text — Pinecone embeds server-side. Records are scoped by `namespace` (per-tenant isolation, free). At query time you can layer a reranker on top of the ANN result to boost precision. For codeAmani the e5-large model is the unlock: Swahili and English live in the same 1024-dim space, so a Lugha-mixed user query retrieves either-language docs without translation.
The five primitives
Index holds the data; namespace isolates it; records carry it; sparse-dense and rerank turn good results into great ones. Tap a card to dig in.
Feel the top-K happen
Twelve documents across three topics, laid out where their meanings would put them. Click anywhere in the plot — or pick a preset — and watch the nearest neighbours re-rank. Real embeddings live in 384+ dimensions, but the intuition transfers: near in space ≈ near in meaning.
Tip: click anywhere in the plot to move the query — top-K updates instantly.
- 1RLS policies in Supabase85.8%
- 2Clerk session management82.5%
- 3JWT signing and verification79.8%
In a real index these points live in 384–1024 dimensions, not two — but the rule is the same: documents about the same thing cluster together. Click halfway between two clusters and watch how the ranking mixes — that's the same behaviour you'd see on a query like “how do I bill for Daraja API access?” which sits between Payments and AI/Pricing.
The codeAmani angle: Pinecone's default multilingual-e5-large places Swahili and English about the same concept right next to each other — a query in either language retrieves docs in both, with zero translation step.
██████╗ ██╗███╗ ██╗███████╗ ██████╗ ██████╗ ███╗ ██╗███████╗
██╔══██╗██║████╗ ██║██╔════╝██╔════╝██╔═══██╗████╗ ██║██╔════╝
██████╔╝██║██╔██╗ ██║█████╗ ██║ ██║ ██║██╔██╗ ██║█████╗
██╔═══╝ ██║██║╚██╗██║██╔══╝ ██║ ██║ ██║██║╚██╗██║██╔══╝
██║ ██║██║ ╚████║███████╗╚██████╗╚██████╔╝██║ ╚████║███████╗
╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝Pinecone Integration Guide
Focus: The retrieval layer for codeAmani's AI. Pinecone is a serverless vector database for RAG — store knowledge, retrieve the most relevant chunks, and feed them into a Claude prompt. With integrated inference you skip running an embedding model entirely; Pinecone embeds text server-side (incl.
multilingual-e5-largefor Swahili+English).
Overview
Two ways to use Pinecone — pick based on whether you want Pinecone to do the embedding:
- Integrated inference (recommended for RAG):
createIndexForModelbinds an embedding model to the index. You thenupsertRecords/searchRecordswith raw text — no separate embeddings step, no vector math in your app. Optional server-side reranking (bge-reranker-v2-m3) sharpens results. - Bring-your-own-vectors: create a serverless index with an explicit
dimension, embed text yourself (e.g. via the Vercel AI SDK / Gemini / OpenAI), thenupsert/queryraw vectors. Use when you need a specific embedding model.
Namespaces partition an index (e.g. per tenant/user) — the multi-tenant isolation primitive. In Claude Code, the Pinecone MCP can create indexes and search records directly.
Here is the integrated-inference path at a glance — notice the embedding happens server-side, so your app never touches a vector:
RAG decision notes
The headline trade-off — Pinecone's integrated inference vs. running your own embeddings — is summarized in the Insight callout at the top of this page. A few more decision points worth knowing:
- When NOT to use integrated inference: if you need a specific embedding model (e.g. to match vectors you already store elsewhere, or a domain-tuned model), use the bring-your-own-vectors path (§4) and embed with the AI SDK instead.
- Reranking earns its cost: a 2-stage retrieve→rerank (
bge-reranker-v2-m3) noticeably lifts answer quality for FAQ/support RAG — keeptopKwide (e.g. 20) andtopNtight (e.g. 3). - Chunking matters more than the DB: retrieval quality is dominated by how you split source docs (size + overlap), not by Pinecone config — tune chunks first.
- Serverless = scale-to-zero: no idle index cost, so spinning up a per-product knowledge base is cheap for early-stage SME features.
Chunking strategy
Since chunking dominates retrieval quality (see the note above), here is concrete guidance — all sizes are in tokens, not characters.
Size — start fixed, then iterate. Pinecone's chunking guide recommends starting with fixed-size chunking and only moving to fancier strategies once it proves insufficient. For sizing, the guide says to "start by exploring a variety of chunk sizes, including smaller chunks (e.g. 128 or 256 tokens) ... and larger chunks (e.g. 512 or 1024 tokens)" (pinecone.io/learn/chunking-strategies). Practical default for FAQ/support docs: ~512 tokens. Smaller chunks give sharper, more precise matches; larger chunks carry more context per hit but dilute the embedding.
Overlap — 10–20% is the commonly-documented range. Pinecone's guide itself does not prescribe a fixed overlap percentage; it instead favours chunk expansion (pulling neighbouring chunks at query time) to recover context. Across the broader RAG literature, a 10–20% overlap (e.g. 50–100 tokens on a 512-token chunk) is the widely-cited starting point to avoid splitting a sentence's meaning across a boundary. Treat it as a knob to tune, not gospel — some recent benchmarks find overlap adds indexing cost with little gain, so measure on your own corpus.
Semantic vs fixed splitting — when to switch:
- Fixed-size (token windows + overlap): deterministic, fast, zero document understanding. Use as your default and for uniform text (support tickets, chat logs, plain prose).
- Content-aware / recursive (split on
\n\n,\n, sentences, then fall back): respects structure. Use for Markdown/HTML docs, USSD scripts, code where paragraph and heading boundaries are meaningful. - Semantic (embed sentences, cut where the topic shifts): highest quality, highest cost. Use only for long, topic-switching documents (multi-section policy/compliance PDFs) where fixed splitting visibly hurts answer quality.
Keep chunk_text (the field in your index fieldMap) as the only text field per record,
and store source metadata (doc_id, section, chunk_index) so you can rebuild context.
// Fixed-size, overlapping chunker — run BEFORE upsertRecords (§2).
// Approximate tokens with a chars-per-token ratio (English ~4; tune for Swahili).
const CHARS_PER_TOKEN = 4;
function chunkText(
text: string,
{ chunkTokens = 512, overlapTokens = 80 } = {},
): string[] {
const size = chunkTokens * CHARS_PER_TOKEN; // ~2048 chars
const overlap = overlapTokens * CHARS_PER_TOKEN; // ~320 chars
const stride = Math.max(size - overlap, 1); // guard: overlap < size
const chunks: string[] = [];
for (let start = 0; start < text.length; start += stride) {
const slice = text.slice(start, start + size).trim();
if (slice) chunks.push(slice);
}
return chunks;
}
// Wire chunks into the §2 integrated-inference upsert:
const records = chunkText(sourceDoc).map((chunk, i) => ({
id: `faq-mpesa#${i}`, // stable id = doc + chunk index
chunk_text: chunk, // must match the index fieldMap
doc_id: "faq-mpesa",
chunk_index: i,
topic: "payments",
}));
await index.upsertRecords({ records });Gotcha: chunk sizes are measured in tokens, but most splitters (including the char-based helper above) cut on characters. The ~4-chars-per-token ratio is an English approximation — Swahili and other non-English text often run fewer chars per token, so a "512-token" char window can silently overshoot the embedding model's context limit and get truncated server-side. For production, count with a real tokenizer (e.g.
tiktoken/js-tiktoken) instead of a fixed ratio, and verify against your model's window (multilingual-e5-large= 512 tokens).
Official Documentation
| Resource | URL |
|---|---|
| Docs home | https://docs.pinecone.io/ |
| Quickstart | https://docs.pinecone.io/guides/get-started/quickstart |
| Integrated inference | https://docs.pinecone.io/guides/inference/understanding-inference |
| Chunking strategies | https://www.pinecone.io/learn/chunking-strategies/ |
| TypeScript client | https://github.com/pinecone-io/pinecone-ts-client |
| Python SDK | https://github.com/pinecone-io/python-sdk |
1. Install + credentials
npm install @pinecone-database/pinecone # JS/TS
pip install pinecone # Python 3.10+PINECONE_API_KEY=... # server-side only (.env.local / Vercel env, or Infisical)2. RAG with integrated inference (no embedding layer) — TypeScript
import { Pinecone } from "@pinecone-database/pinecone";
const pc = new Pinecone({ apiKey: process.env.PINECONE_API_KEY! });
// 2a. Create an index bound to an embedding model (multilingual = Swahili + English)
const model = await pc.createIndexForModel({
name: "kb",
cloud: "aws",
region: "us-east-1",
embed: { model: "multilingual-e5-large", fieldMap: { text: "chunk_text" } },
waitUntilReady: true,
});
const index = pc.index({ host: model.host });
// 2b. Upsert RAW TEXT — Pinecone embeds it server-side
await index.upsertRecords({
records: [
{ id: "doc1", chunk_text: "M-Pesa STK Push prompts the user on their phone.", topic: "payments" },
{ id: "doc2", chunk_text: "Daraja tokens expire after one hour.", topic: "payments" },
],
});
// 2c. Search with a text query (+ optional rerank)
const hits = await index.searchRecords({
query: { topK: 4, inputs: { text: "how does M-Pesa checkout work?" }, filter: { topic: { $eq: "payments" } } },
rerank: { model: "bge-reranker-v2-m3", topN: 2, rankFields: ["chunk_text"] },
fields: ["chunk_text", "topic"],
});3. Close the RAG loop (Pinecone → Claude via the AI SDK)
You are one short hop from a full answer — retrieved chunks become Claude's context, with Pinecone as memory and Claude as generator:
import { generateText } from "ai";
const context = hits.result.hits.map(h => h.fields.chunk_text).join("\n---\n");
const { text } = await generateText({
model: "anthropic/claude-sonnet-4-20250514", // via Vercel AI Gateway
prompt: `Answer using ONLY this context:\n${context}\n\nQ: How does M-Pesa checkout work?`,
});4. Bring-your-own-vectors (Python, explicit dimension)
from pinecone import Pinecone, ServerlessSpec
pc = Pinecone(api_key="...")
pc.indexes.create(name="kb", dimension=1536, metric="cosine",
spec=ServerlessSpec(cloud="aws", region="us-east-1"))
index = pc.index("kb")
index.upsert(vectors=[("id1", embedding_1536d)], namespace="tenant-a")
res = index.query(vector=query_vec, top_k=10, namespace="tenant-a")codeAmani notes
- Fills the retrieval gap: this is the missing layer for RAG over codeAmani docs/FAQs/
product knowledge. Retrieve top-k chunks, then pass them to Claude via the Vercel AI SDK
(see
vercel-ai-sdk). Keep Claude as the generator; Pinecone as the memory. - Multilingual by default:
multilingual-e5-largeembeds Swahili and English in one space — East-African content (mixed-language support FAQs, USSD scripts) works without a custom model. Strong fit for theAFRICAN_MARKET_GUIDE.mdaudience. - Multi-tenant: use a namespace per customer/SME for isolation (RLS-like separation).
- Security:
PINECONE_API_KEYis server-side only (.env.local/ Vercel env, or move it into Infisical). Query from API routes / server actions, never the browser. - Cost: serverless pay-per-use; reads cost readUnits and reranking costs rerankUnits —
cache hot queries (pairs with the Upstash cache) and keep
topK/topNtight. - Claude Code: the Pinecone MCP (
create-index-for-model,search-records,upsert-records) lets you build and inspect indexes interactively without leaving the editor.