教程 / 后端集成 / 使用 Pinecone 实现向量搜索
📝 文字 ● 中级 更新于 2026-05-13

使用 Pinecone 实现向量搜索

将文档嵌入为向量,存储到 Pinecone,按语义相似度查询。这是 RAG、推荐系统、语义搜索以及"查找相似文档"功能的基础。

60 秒速成心智模型

0
  • 嵌入(Embedding) = 将文本(或图像)转换为固定长度浮点数组的函数(例如 OpenAI 的 text-embedding-3-small 输出 1536 维)。语义相近 → 向量距离近。
  • 向量数据库 = 存储数百万个向量数组,并在 100ms 内回答"哪 N 个向量与这个最接近?"。
  • 选择 Pinecone 的场景 — 你需要一个托管服务,无需自己运维基础设施,有慷慨的免费额度,且文档和 SDK 完善。
  • 替代方案Qdrant(开源,单节点最快)、Weaviate(开源,模块化)、pgvector(Postgres 扩展——直接用现有数据库!)、Supabase + pgvector(小数据量免费)、Chroma(Python 优先的原型工具)。
  • 不需要向量数据库的情况 — 文档少于 10K 条且全部能放入内存时,启动时嵌入全部文档,在进程内做余弦相似度计算即可,完全省去网络开销。
  • 注册app.pinecone.io,支持 Google/GitHub 登录。

创建索引

1

控制台 → Create Index,需要做以下选择:

  • 维度(Dimensions) — 必须与你的嵌入模型匹配。text-embedding-3-small = 1536,text-embedding-3-large = 3072,Cohere embed-v3 = 1024。创建后无法更改 — 选错了只能重建索引。
  • 距离度量(Metric)cosine(默认,归一化相似度)、euclideandotproduct。除非嵌入模型文档另有说明,否则使用 cosine
  • 云服务商 — AWS / GCP / Azure,选与你应用相同的云以降低延迟。
  • 类型Serverless(按查询计费,可缩容至零,默认推荐)或 Pod(专用资源,成本可预测,旧模式)。新项目选 serverless。

复制索引名称和 host URL。在 API Keys 中复制密钥。

PINECONE_API_KEY=…
PINECONE_INDEX=my-docs
OPENAI_API_KEY=…   # for embeddings

生成嵌入向量

2

你需要一个能生成向量的服务。OpenAI 的嵌入 API 是默认选择;CohereVoyage 以及通过 sentence-transformers 使用开源模型也是可行的替代方案。

npm install openai @pinecone-database/pinecone
import OpenAI from "openai";
const openai = new OpenAI();

async function embed(text) {
  const r = await openai.embeddings.create({
    model: "text-embedding-3-small",
    input: text,
  });
  return r.data[0].embedding;  // float[1536]
}

尽量批量处理 — 一次调用嵌入 100 条字符串比分 100 次调用便宜约 30 倍:

const r = await openai.embeddings.create({
  model: "text-embedding-3-small",
  input: chunks,  // array of strings, max 2048 inputs per call
});
const vectors = r.data.map(d => d.embedding);

分块处理文档

3

不要把一份 50 页的 PDF 作为单个向量嵌入 — 这样语义信息会被平均成一锅粥。应将其拆分为约 500 token 的片段,并保留约 50 token 的重叠(避免上下文在句子中间被截断)。

function chunkText(text, size = 1500, overlap = 150) {
  const chunks = [];
  let i = 0;
  while (i < text.length) {
    chunks.push(text.slice(i, i + size));
    i += size - overlap;
  }
  return chunks;
}

在生产环境中若需要尊重段落和句子边界的精细分块,可使用 LangChain 的 RecursiveCharacterTextSplitterLlamaIndex 节点解析器

分块策略是决定检索质量的最关键因素。糟糕的分块(太大、太小、在段落中间截断)会导致搜索结果很差,无论你用的是多好的数据库。

写入(Upsert)向量

4
import { Pinecone } from "@pinecone-database/pinecone";

const pc = new Pinecone({ apiKey: process.env.PINECONE_API_KEY });
const index = pc.index(process.env.PINECONE_INDEX);

async function ingestDocument(docId, text) {
  const chunks = chunkText(text);
  const vectors = await embedBatch(chunks);

  await index.upsert(
    chunks.map((chunk, i) => ({
      id: `${docId}#${i}`,             // deterministic ID — re-ingesting overwrites
      values: vectors[i],
      metadata: {
        docId,
        text: chunk,                    // store the chunk for retrieval
        chunkIndex: i,
        createdAt: Date.now(),
      },
    })),
  );
}

元数据使用建议:

  • 始终在元数据中存储原始文本 — 检索时可以将文本块直接传给大模型。
  • 添加过滤标签userIdworkspaceIddocumentTypetags: ["draft", "public"]。Pinecone 在服务端对元数据做过滤,开销很小。
  • 不要存储大体积数据 — 每个向量的元数据上限为 40KB。对于 PDF 文件,存储指向 S3/R2 的指针即可。

查询 — 语义搜索

5
async function search(query, userId) {
  const queryVec = await embed(query);

  const result = await index.query({
    vector: queryVec,
    topK: 5,
    includeMetadata: true,
    filter: { userId: { $eq: userId } },  // multi-tenant guard
  });

  return result.matches.map(m => ({
    text: m.metadata.text,
    docId: m.metadata.docId,
    score: m.score,  // 0..1 cosine similarity
  }));
}

过滤运算符:$eq$ne$gt$lt$in$nin$and$or。完整参考:Pinecone 元数据过滤

接入大模型(RAG)

6

检索增强生成(RAG):搜索 → 将结果塞入提示词 → 询问大模型。

async function answer(question, userId) {
  const docs = await search(question, userId);
  const context = docs.map(d => `[${d.docId}]\n${d.text}`).join("\n\n---\n\n");

  const r = await openai.chat.completions.create({
    model: "gpt-4o-mini",
    messages: [
      {
        role: "system",
        content: "Answer using only the context below. Cite [docId] inline. If the context doesn't contain the answer, say so.",
      },
      { role: "user", content: `Context:\n${context}\n\nQuestion: ${question}` },
    ],
  });

  return r.choices[0].message.content;
}

若需要更复杂的 RAG 流程(查询改写、多跳推理、重排序),可使用框架:LangChainLlamaIndex。对于上面这种简单的"搜索+填充"模式,引入框架反而是额外负担。

混合搜索(语义 + 关键词)

7

纯向量搜索会漏掉精确匹配的查询。例如"ZeroMQ"嵌入后与"RabbitMQ"向量接近,但用户查找 ZeroMQ 文档时需要的就是 ZeroMQ。混合搜索结合 BM25(关键词)和余弦相似度(语义)。

Pinecone 通过 稀疏-稠密向量 在 serverless 索引上支持混合搜索。可使用 Pinecone Inferencerank-bm25 生成稀疏向量。

如果需求比较简单,也可以并行跑两种搜索,再合并结果 — 互惠排名融合(reciprocal rank fusion)不超过 20 行代码,效果不错。

常见问题

8
  • 返回结果看起来毫不相关 — 通常是分块问题(太大),而不是数据库问题。尝试更小的块并加入重叠。检查实际返回的文本内容,不要只看分数。
  • 查询很慢 → 每次查询向量数太多topK: 100 几乎没用,5–10 是 RAG 的最佳区间。如果对质量有更高要求,可以用较大的 topK 配合 Cohere Rerank 重排序。
  • "Embedding dimension doesn't match index" — 你切换了模型。Pinecone 索引的维度是锁定的。创建一个新索引并重新写入数据。
  • 文档编辑后向量过期 — 你修改了源文档,但旧的文本块还以旧内容存在。使用确定性 ID(${docId}#${i})配合 upsert 覆盖写入,同时删除超出新分块数量的旧块。
  • 多租户数据泄露 — 每次查询都必须带上用户/工作区过滤条件。一旦遗漏就会跨客户泄露文档。将 SDK 封装为每用户的辅助函数并自动注入过滤器,永远不要在业务代码中直接调用 index.query
  • 嵌入费用超出预期 — 重新写入 100 万个文本块按 OpenAI 定价约 $20,不算贵,但第三次触发时就会觉得意外。按内容哈希缓存嵌入结果。
  • 免费层被驱逐 — Pinecone serverless 免费层有存储上限,超出后检索召回率会悄悄下降。在控制台中监控索引大小。

价格实情

9
  • Pinecone Serverless 免费层 — 2 GB 存储,每月 200 万读单元 / 100 万写单元。足够支撑不到 10 万个向量的应用。
  • 超出免费额度后 — 存储 $0.33/GB/月,读单元 $16/百万次,写单元 $4/百万次。一个读单元约等于一次相似度比较。
  • 嵌入成本高于数据库 — OpenAI text-embedding-3-small 为 $0.02/百万 token;嵌入 100 万篇文档(平均 500 token)= $10。重新嵌入所有数据才是贵的部分。
  • 在现有 Postgres 上用 pgvector = 免费 — 如果你已经在用 Postgres(Supabase / Neon / RDS),且向量数少于 1000 万,pgvector 是最便宜的选择。小规模时延迟相近,大规模时性能下降更快。
  • 自托管 Qdrant = 每月 $5–20 VPS — 开源,单二进制文件,单节点性能最快,需要自己运维。

定价详情:pinecone.io/pricing

官方参考资料

在尝试向量数据库之前,先试试内存计算。向量数少于 1 万时,在应用内对每个向量做 cosine(query, every_vector) 比请求 Pinecone 还快,而且免费。向量数据库在规模化后才物有所值 — 但大多数应用根本到不了那个量级。