# Pinecone 向量数据库实践:从托管检索到 LangChain 生产封装

如果说 FAISS 更像一个本地向量检索内核,那么 Pinecone 更像一个面向生产的托管向量数据库服务。

它把向量存储、相似度检索、元数据过滤、命名空间隔离、服务端扩展、监控和 API 运维都交给平台处理。对 Python RAG 项目来说,Pinecone 的价值不只是“能存向量”,而是能让团队少维护一套向量检索基础设施。

但 Pinecone 也不是随便 add_texts() 就能上线。生产里真正要设计的是:index 怎么建、dimension 怎么固定、namespace 怎么分、metadata 怎么建模、delete/update 怎么治理、成本和权限怎么控制。

# Pinecone 的核心层级

使用 Pinecone 前,先理解几个核心概念。

概念 作用 生产理解
Organization 组织账号维度 企业账号、团队账号、账单和成员管理的上层边界
Project 项目维度 隔离不同业务、环境或资源集合
Index 向量索引 存储同一维度、同一 metric 规则下的向量数据
Namespace 命名空间 在一个 index 内做逻辑分区,常用于租户、知识库、环境隔离
Record 记录 一条向量数据,通常包含 id、values、metadata

这里最容易混淆的是 index 和 namespace。

index 决定向量维度、相似度指标和底层服务形态。namespace 是 index 内部的逻辑分区。生产里常见做法是:同一个 embedding 模型和维度使用一个 index,不同租户、知识库或环境用 namespace 隔开。

# 安装与当前包路径

当前 LangChain 的 Pinecone 集成使用独立包:

pip install -U langchain-pinecone pinecone langchain-openai
1

导入方式:

from langchain_pinecone import PineconeVectorStore
1

不要再使用旧的 langchain.vectorstores.Pinecone 作为新项目主路径。现在更清晰的方式是使用 langchain_pinecone.PineconeVectorStore。

环境变量:

export PINECONE_API_KEY="your-pinecone-api-key"
export OPENAI_API_KEY="your-openai-api-key"
1
2

# 创建 Index

Pinecone index 的维度必须和 Embedding 模型输出维度一致。比如 text-embedding-3-small 默认是 1536 维,那么 Pinecone index 也要建成 1536 维。

import os
import time

from pinecone import Pinecone, ServerlessSpec


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

index_name = "help-center-v1"

existing_indexes = [item["name"] for item in pc.list_indexes()]

if index_name not in existing_indexes:
    pc.create_index(
        name=index_name,
        dimension=1536,
        metric="cosine",
        spec=ServerlessSpec(
            cloud="aws",
            region="us-east-1",
        ),
        deletion_protection="enabled",
    )

    while not pc.describe_index(index_name).status["ready"]:
        time.sleep(1)

index = pc.Index(index_name)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

生产里不要在每次服务启动时都创建 index。更合理的是把 index 创建纳入基础设施脚本、Terraform、发布平台或运维流程。应用启动时只连接已经存在的 index。

# 接入 LangChain

创建 PineconeVectorStore:

import os

from langchain_openai import OpenAIEmbeddings
from langchain_pinecone import PineconeVectorStore
from pinecone import Pinecone


embeddings = OpenAIEmbeddings(
    model="text-embedding-3-small",
    api_key=os.environ["OPENAI_API_KEY"],
)

pc = Pinecone(api_key=os.environ["PINECONE_API_KEY"])
index = pc.Index("help-center-v1")

vector_store = PineconeVectorStore(
    index=index,
    embedding=embeddings,
    namespace="prod-help-center",
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

也可以通过 index name 连接:

vector_store = PineconeVectorStore(
    index_name="help-center-v1",
    embedding=embeddings,
    namespace="prod-help-center",
)
1
2
3
4
5

生产里我更偏向显式创建 Pinecone client 和 index,再传给 PineconeVectorStore。这样连接配置、重试、host、监控和权限更容易统一管理。

# 写入文本和 metadata

最简单的写入方式是 add_texts():

texts = [
    "笨笨是一只很喜欢睡觉的猫。",
    "猫咪在窗台上打盹,看起来非常可爱。",
    "学习新技能是每个人都应该追求的目标。",
]

metadatas = [
    {"page": 1, "tenant_id": "t1", "source": "pet.md", "status": "published"},
    {"page": 2, "tenant_id": "t1", "source": "pet.md", "status": "published"},
    {"page": 3, "tenant_id": "t1", "source": "study.md", "status": "draft"},
]

ids = [
    "pet.md#chunk-0001",
    "pet.md#chunk-0002",
    "study.md#chunk-0001",
]

vector_store.add_texts(
    texts=texts,
    metadatas=metadatas,
    ids=ids,
    namespace="prod-help-center",
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

如果已经有 Document 对象,可以用 add_documents():

from langchain_core.documents import Document


documents = [
    Document(
        page_content="笨笨是一只很喜欢睡觉的猫。",
        metadata={"tenant_id": "t1", "source": "pet.md", "page": 1},
    )
]

vector_store.add_documents(
    documents=documents,
    ids=["pet.md#chunk-0001"],
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14

生产里强烈建议显式传 ids。不要完全依赖自动生成 id,否则后续更新、删除、回放、去重会非常麻烦。

# metadata 建模

Pinecone 支持把 metadata 用于搜索过滤和删除过滤。metadata 不是随便塞 JSON 的垃圾桶,它应该服务于检索、权限和治理。

常见 metadata:

{
    "tenant_id": "enterprise-a",
    "knowledge_base_id": "kb-help-center",
    "document_id": "doc-1001",
    "source": "refund-policy.md",
    "page": 3,
    "status": "published",
    "embedding_model": "text-embedding-3-small",
    "chunk_version": "chunk-v3",
    "created_at": "2026-08-12"
}
1
2
3
4
5
6
7
8
9
10
11

注意几个边界:

  • metadata 应该尽量扁平化。
  • 不要把大段正文重复塞进 metadata。
  • 权限字段必须可过滤。
  • 文档版本、chunk 版本、embedding 版本要能追踪。
  • 高基数字段会影响过滤和成本设计,要谨慎。

Pinecone 官方数据建模文档说明,metadata 可以用于搜索或删除过滤,并有大小限制。生产里应把 metadata 当成索引设计的一部分,而不是后期补丁。

# 相似度搜索

普通相似度检索:

docs = vector_store.similarity_search(
    "我养了一只猫,叫笨笨",
    k=3,
)
1
2
3
4

带分数:

results = vector_store.similarity_search_with_score(
    "我养了一只猫,叫笨笨",
    k=3,
)

for doc, score in results:
    print(score, doc.page_content, doc.metadata)
1
2
3
4
5
6
7

带相关性分数:

results = vector_store.similarity_search_with_relevance_scores(
    "我养了一只猫,叫笨笨",
    k=3,
)
1
2
3
4

分数的解释要和 index 的 metric 对齐。比如 cosine、dotproduct、euclidean 的分数意义不同,不要直接把一个阈值跨 index、跨模型复用。

# metadata filter

Pinecone 的一大优势是原生支持 metadata filter。

精确匹配:

docs = vector_store.similarity_search(
    "猫咪相关内容",
    k=3,
    filter={"tenant_id": {"$eq": "t1"}},
)
1
2
3
4
5

范围过滤:

docs = vector_store.similarity_search(
    "猫咪相关内容",
    k=3,
    filter={"page": {"$lte": 5}},
)
1
2
3
4
5

AND:

docs = vector_store.similarity_search(
    "猫咪相关内容",
    k=3,
    filter={
        "$and": [
            {"tenant_id": {"$eq": "t1"}},
            {"status": {"$eq": "published"}},
        ]
    },
)
1
2
3
4
5
6
7
8
9
10

OR:

docs = vector_store.similarity_search(
    "猫咪相关内容",
    k=3,
    filter={
        "$or": [
            {"page": {"$eq": 5}},
            {"account_id": {"$eq": 1}},
        ]
    },
)
1
2
3
4
5
6
7
8
9
10

常见操作符包括:

操作符 含义
$eq 等于
$ne 不等于
$gt 大于
$gte 大于等于
$lt 小于
$lte 小于等于
$in 包含在列表中
$nin 不包含在列表中
$exists 字段是否存在
$and 多条件同时满足
$or 多条件满足任一

权限过滤场景一定要把 tenant、user、role、document status 这类字段设计清楚。Pinecone 能做 metadata filter,不代表你可以不设计权限边界。

# namespace 的生产用法

namespace 是 Pinecone 里很关键的隔离手段。

常见 namespace 设计:

namespace 方式 适用场景
tenant_id 多租户数据强隔离
env + tenant_id 区分 dev/staging/prod
knowledge_base_id 同一租户多个知识库
index_version 灰度发布和回滚
tenant_id + kb_id 企业知识库隔离

比如:

namespace = "prod:tenant-a:kb-help-center:v2026-08-12"
1

namespace 不是越多越好。过度拆分会增加管理复杂度,也可能影响批处理和查询策略。一般原则是:安全边界优先用 namespace,普通检索条件用 metadata。

# 删除数据

按 id 删除:

vector_store.delete(
    ids=["pet.md#chunk-0001"],
    namespace="prod-help-center",
)
1
2
3
4

删除整个 namespace:

vector_store.delete(
    delete_all=True,
    namespace="prod-help-center-old",
)
1
2
3
4

按 metadata filter 删除:

vector_store.delete(
    namespace="prod-help-center",
    filter={"document_id": {"$eq": "doc-1001"}},
)
1
2
3
4

现在 Pinecone 已经支持按 metadata 删除记录,这对文档重建、租户清理、数据合规删除很有价值。但生产里仍建议把删除当成有审计的操作:记录删除条件、操作者、任务 ID、影响数量和执行时间。

# 更新与原始 Index

LangChain 的 PineconeVectorStore 覆盖了常见 RAG 操作,但 Pinecone 原生 SDK 能力更完整。

如果需要调用原始 index:

pinecone_index = vector_store.index

pinecone_index.update(
    id="pet.md#chunk-0001",
    set_metadata={"status": "archived"},
    namespace="prod-help-center",
)
1
2
3
4
5
6
7

或者在初始化时保留 index:

pc = Pinecone(api_key=os.environ["PINECONE_API_KEY"])
index = pc.Index("help-center-v1")

vector_store = PineconeVectorStore(
    index=index,
    embedding=embeddings,
    namespace="prod-help-center",
)
1
2
3
4
5
6
7
8

生产里建议把 LangChain VectorStore 当成 RAG 接口,把 Pinecone SDK 当成数据治理接口。检索走 VectorStore,批量更新、删除、统计、运维操作走原始 SDK 或后台任务。

# 批量写入与吞吐

add_texts() 支持批量参数:

vector_store.add_texts(
    texts=texts,
    metadatas=metadatas,
    ids=ids,
    namespace="prod-help-center",
    batch_size=64,
    embedding_chunk_size=1000,
)
1
2
3
4
5
6
7
8

生产批量入库要关注两段吞吐:

  • Embedding 模型吞吐。
  • Pinecone upsert 吞吐。

如果 Embedding 是外部 API,embedding_chunk_size 太大可能触发限流;如果 batch_size 太小,Pinecone 写入效率会低。建议做可观测指标:

  • 每批 chunk 数。
  • Embedding 耗时。
  • Upsert 耗时。
  • 失败重试次数。
  • 写入向量数。
  • Pinecone write units。

# 和 FAISS 的区别

维度 FAISS Pinecone
部署方式 本地库 云端托管服务
运维成本 自己维护 平台托管
metadata filter 主要靠封装层处理 原生支持
namespace 需要自己设计目录/索引 原生支持
高可用 自己做 平台能力
成本模型 机器和存储成本 服务计费和读写单元
适用场景 本地、离线、小中规模 线上、多租户、弹性扩展

如果只是本地 demo,FAISS 更轻。如果要做线上多租户知识库,Pinecone 这种托管服务会省掉很多基础设施工作。

# 生产封装建议

不要让业务代码散落 Pinecone 初始化和 namespace 拼接。可以封装成工厂:

from dataclasses import dataclass

from langchain_pinecone import PineconeVectorStore
from pinecone import Pinecone


@dataclass(frozen=True)
class PineconeSettings:
    api_key: str
    index_name: str
    namespace: str
    embedding_model: str
    dimension: int
    metric: str


def build_pinecone_vector_store(settings: PineconeSettings, embeddings):
    pc = Pinecone(api_key=settings.api_key)
    index = pc.Index(settings.index_name)

    return PineconeVectorStore(
        index=index,
        embedding=embeddings,
        namespace=settings.namespace,
    )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

再配合 manifest:

{
    "index_name": "help-center-v1",
    "namespace": "prod:tenant-a:kb-help-center:v2026-08-12",
    "embedding_model": "text-embedding-3-small",
    "dimension": 1536,
    "metric": "cosine",
    "chunk_version": "chunk-v3"
}
1
2
3
4
5
6
7
8

这样后续排查、回滚、迁移都能知道当前线上检索到底用了哪套向量数据。

# 上线前检查清单

  • Index dimension 是否和 Embedding 模型一致。
  • metric 是否和模型归一化策略一致。
  • namespace 是否能表达租户、知识库、环境或索引版本边界。
  • metadata 是否包含权限、状态、来源、文档版本。
  • ids 是否稳定可重放。
  • 删除是否有审计记录。
  • 是否有批量写入重试和幂等机制。
  • 是否记录 Pinecone read/write units 和延迟。
  • 是否有固定评估集验证 Recall@K、MRR 和答案质量。
  • 是否有索引版本切换和回滚方案。

# 问题

Pinecone 最常见的问题不是不会调用 API,而是数据建模不清楚。

第一,index 维度和 Embedding 维度不一致。这类问题通常在写入时直接失败。

第二,namespace 设计混乱。把租户、环境、知识库、版本都塞到 metadata 里,导致隔离边界变弱。

第三,metadata 太随意。字段类型不稳定、大小失控、权限字段缺失,会让后续过滤和删除非常痛苦。

第四,id 不稳定。每次入库自动生成新 id,会导致重复数据、难以更新、难以删除。

第五,把 delete_all 当成普通操作。清空 namespace 必须有权限控制和审计。

# 拓展

Pinecone 可以继续往三个方向扩展。

第一,混合检索。结合 dense vector、sparse vector 和 reranker,提高关键词和语义同时命中的能力。

第二,多租户架构。根据租户规模决定 namespace、index 或 project 隔离,避免一个策略管所有客户。

第三,索引生命周期。把构建、评估、发布、回滚、删除做成可审计流水线,而不是直接在业务代码里操作线上 index。

# 实际生产是否使用

会使用。

Pinecone 本来就是面向生产的托管向量数据库,适合希望快速上线 RAG、减少自建向量数据库运维、需要 metadata filter 和多租户隔离的团队。

但生产里不会只写几行 add_texts()。真正可用的系统还要包括文档解析、chunk 版本、Embedding 缓存、稳定 id、metadata schema、索引发布、评估集、权限过滤、审计和成本监控。

# 现在是否抛弃

没有抛弃。

LangChain 当前仍然有独立的 langchain-pinecone 包和 PineconeVectorStore 集成。Pinecone 官方也在持续强化 metadata、upsert、delete、fetch、托管索引等能力。

需要抛弃的是旧导入路径和 demo 式写法。新项目应该使用 langchain_pinecone.PineconeVectorStore,并把 index、namespace、metadata、id、版本治理当成架构设计的一部分。

# 最新生产如何实现

当前更推荐这样落地:

第一,基础设施层创建 Pinecone index,固定 dimension、metric、cloud、region 和 deletion protection。

第二,应用层通过 PineconeVectorStore(index=index, embedding=embeddings, namespace=...) 接入。

第三,写入时显式 ids、metadata、namespace,保证可重放、可删除、可审计。

第四,权限强隔离优先用 namespace 或独立 index,普通条件用 metadata filter。

第五,文档更新走索引版本或文档级 delete + upsert,不在旧数据上悄悄覆盖。

第六,线上查询记录 query、namespace、filter、k、score、latency、命中文档 id,接入 trace 和评估闭环。

最小生产查询结构:

def search_knowledge(
    vector_store: PineconeVectorStore,
    query: str,
    tenant_id: str,
    knowledge_base_id: str,
):
    return vector_store.similarity_search_with_score(
        query,
        k=5,
        filter={
            "$and": [
                {"tenant_id": {"$eq": tenant_id}},
                {"knowledge_base_id": {"$eq": knowledge_base_id}},
                {"status": {"$eq": "published"}},
            ]
        },
    )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

这段代码只是入口。真正上线时,tenant_id 必须来自认证态,knowledge_base_id 必须做授权校验,检索结果还要进入 RAG 生成链路,并记录可观测日志。

# 总结

Pinecone 的优势是托管、弹性、原生 metadata filter、namespace 隔离和成熟 API。它让 Python RAG 项目不用从零维护一套向量数据库基础设施。

但 Pinecone 不会替你设计数据模型。生产质量取决于 index 维度、namespace 策略、metadata schema、稳定 id、删除审计、批量写入、评估集和成本监控。

参考: