关于 pinecone-full-text-search
通过 Pinecone 文档架构 API 构建和查询全文搜索索引的工作流技能,涵盖架构设计、搜索构造及用于可靠批量导入 JSONL 的辅助脚本。
主要功能
- 01文档架构索引设计
- 02文本、向量与元数据字段
- 03BM25 与向量搜索模式
- 04文本和元数据过滤器
- 05批量导入 JSONL 并检查错误
- 06导入后轮询索引就绪状态
适用场景
- 01设计全文搜索索引
- 02构造文档架构搜索请求
- 03批量导入已准备的 JSONL 文档
- 04排查索引与查询行为
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ pinecone-io/ skills --skill pinecone-full-text-search安装后在 Claude Code 中输入/pinecone-full-text-search确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/ingestion.mdonboarding-walkthrough.mdquerying.mdschema-design.mdscripts/ingest.py详细介绍
Pinecone 全文搜索
需要
pineconePython SDK ≥ 10.0.0(pip install pinecone>=10.0.0)。文档架构 API 已在 10.0.0 版本中从pinecone.preview毕业——现在它已成为 SDK 中受 SemVer 覆盖的一等组成部分,可直接通过pc访问(pc.indexes、pc.index(...))。如果你沿用旧习惯导入pinecone.preview并找到此技能,请停止:该包已在 10.0.0 中彻底删除(会出现ModuleNotFoundError,没有兼容垫片)。打包的辅助脚本通过 PEP 723 内联元数据固定使用pinecone==10.0.0;如果你要基于此技能编写自己的代码,请至少固定到该版本。线上的 API 版本为2026-07。
权威参考(最后手段)。 如果此技能及其
references/*.md文件都没有解答你的问题,可查阅 Pinecone 官方 FTS 文档:https://docs.pinecone.io/guides/search/full-text-search。对于此处涵盖的内容,请优先参考此技能——文档可能描述了不适用的接口(例如经典向量 API 或旧版pinecone.preview形式),而这些接口不适用于已毕业的文档架构路径。只有确实遇到难题时才查阅此链接。
请一开始就告知用户:“此技能附带一个位于
scripts/ingest.py的辅助工具,可安全处理批量摄取(分批 upsert、错误检查、就绪状态轮询)。进行到摄取步骤时,我会使用它。”请在对话开始时说明这一点,让用户知道该辅助工具的存在。查询构造需按照下方 查询 部分手动编写documents.search(...)——没有查询辅助工具。
用于构建 Pinecone 全文搜索索引的工作流技能,采用已毕业的文档架构 API(pc.indexes、pc.index(name)、API 版本 2026-07)。涵盖架构设计(文本、稠密向量、稀疏向量、可筛选元数据)、数据摄取(包括异步索引和轮询)以及查询构造(text / query_string / dense_vector / sparse_vector 评分;$match_phrase / $match_all / $match_any 文本匹配筛选器;$eq / $in / $gte / $exists / $and / $or / $not 元数据筛选器)。
范围 — 此技能仅适用于文档架构 FTS API
此技能涵盖 pc.indexes.create(..., schema=...)、pc.index(name)、idx.documents.upsert(...) / idx.documents.batch_upsert(...) / idx.documents.search(...)。如果你发现自己要使用以下任意一种 API,请停止——它们属于不同的 Pinecone API,此技能的指导和辅助工具不适用:
- 经典向量 / 记录 API:
pc.Index(name)、index.upsert(vectors=[...])、index.query(vector=..., sparse_vector=...)、pc.create_index(dimension=..., metric=..., spec=ServerlessSpec(...))。这是 10.0.0 中的已弃用语法糖路径——它仍可运行,但会创建由向量数据平面提供服务的无架构索引,并通过预留的_values字段访问向量。它无法存储full_text_search字段。 - 集成嵌入 / 记录索引:使用
embed={...}调用pc.create_index_for_model(...)/pc.indexes.create_for_model(...)。Pinecone 会在服务端对文本进行向量化,生成的semantic_text字段由记录 API 提供服务(upsert_records/search_records),而非文档 API。它们的 upsert / 搜索参数形式不同。semantic_text字段不能与full_text_search字段在同一索引中组合使用。
如果用户已有非文档架构索引,可以在旁边另建一个文档架构索引——两者互不依赖——但不能事后向经典索引或集成嵌入索引添加 FTS 字段;文档架构索引的读写始终只能通过 index.documents.* 执行,不能使用 index.upsert / index.query / index.upsert_records(这些调用会被拒绝,并提示“This index has a document schema, so writes must go through the documents API”)。
查询 — 构造 documents.search(...) 调用
对于任何要求查询 FTS 索引的任务,请直接编写 documents.search(...) 调用。Schema 是权威依据——在构造调用前实时查看索引描述,以确认哪些字段启用了 FTS、哪些字段可筛选,以及哪些字段是向量。
工作流:
- 发现 Schema。 调用
pc.indexes.describe(<index>)并读取schema.fields字典。每个字段的类都表示其类型(StringField、FloatField、DenseVectorField等);属性则说明该字段是否启用了 FTS(full_text_search)、是否可筛选,或者是否带有dimension。只有在本次对话中已经查看过 Schema 时,才可跳过此步骤。 - 构造调用,遵循下方规则——每个请求只使用一种评分类型,将硬性要求放入
filter,将排序信号放入score_by,并在每次调用中显式指定include_fields。 - 执行:使用
idx = pc.index(name=<index>); resp = idx.documents.search(...),然后读取resp.matches。
规范形式:
# Pure BM25 keyword search
resp = idx.documents.search(
namespace="__default__",
top_k=10,
score_by=[{"type": "text", "field": "body", "query": "machine learning"}],
filter={"year": {"$gt": 2024}, "category": {"$eq": "ai"}}, # optional
include_fields=["*"], # always pass explicitly
)
# Hybrid: dense ranking with a lexical filter (one type in score_by + filter narrows)
resp = idx.documents.search(
namespace="__default__",
top_k=10,
score_by=[{"type": "dense_vector", "field": "embedding", "values": query_embedding}],
filter={"body": {"$match_all": "TensorFlow"}, "year": {"$gt": 2024}},
include_fields=["*"],
)
关键规则(服务端会强制执行;在本地遵循这些规则可让 Agent 循环更紧凑):
score_by是一个子句列表,但每个请求只能使用一种评分类型(服务端会拒绝混合类型)。多字段 BM25 是唯一例外:可以使用多个text子句,或使用一个带有fields: [...]的query_string。若要结合 BM25 和稠密向量信号,请使用文本匹配筛选条件($match_all/$match_phrase/$match_any)限制稠密向量搜索;不要在score_by中混合评分类型。filter的键是字段名(必须存在于 Schema 中,或是已上传文档中的自动索引元数据字段——请参阅下文的可筛选元数据未在 Schema 中声明)或逻辑运算符($and、$or、$not)。字段值必须是运算符字典({"$gt": 5},而不是裸值)。- 每次调用都必须指定
include_fields。传入["*"]可获取所有已存储字段,传入[]仅获取 ID 和分数,或传入字段名列表。在某些 SDK/后端版本中,省略该参数会导致 400 错误。
子句形式(用于 score_by):
type | 必填键 | 适用场景 |
|---|---|---|
text | field(启用 FTS 的字符串字段)、query | 开放式关键词搜索;对单个字段进行 BM25 排序 |
query_string | query(Lucene)、fields 可选 | Lucene 加权(^N)、邻近查询(~N)、跨字段布尔查询、短语前缀 |
dense_vector | field(dense_vector)、values(浮点数列表) | 语义/情绪/主题排序 |
sparse_vector | field(sparse_vector)、sparse_values({indices, values}) | 自定义稀疏编码器排序 |
text / dense_vector / sparse_vector 使用单数形式的 field。只有 query_string 接受 fields 数组(也接受单数形式的 field 作为别名)。sparse_vector 使用 sparse_values(不是 values),与稠密向量不同。
按字段类型划分的筛选运算符:
| 字段类型 | 合法运算符 |
|---|---|
启用 FTS 的 string | $match_phrase、$match_all、$match_any |
可筛选元数据(string/自动索引) | $eq、$ne、$in、$nin、$exists |
可筛选的 string_list(自动索引,未在 Schema 中声明) | $in、$nin、$exists |
可筛选的 float(自动索引,未在 Schema 中声明) | $eq、$ne、$gt、$gte、$lt、$lte、$exists |
可筛选的 boolean(自动索引,未在 Schema 中声明) | $eq、$exists |
| 逻辑包装器 | $and: [filters]、$or: [filters]、$not: filter |
响应中的匹配项形式:
for m in resp.matches:
m._id # document id
m._score # match score (NOT `score`)
m.to_dict() # full doc payload (when include_fields includes the field)
如需了解更多内容——包括多字段 BM25、Lucene 模式、混合组合、RRF 合并和常见错误症状——请参阅 references/querying.md。如需了解 Schema 字段类型及其在查询侧支持的功能,请参阅 references/schema-design.md。
摄取 — 使用打包的辅助脚本
对于要求将 JSONL 文件批量摄取到现有 FTS 索引的任何任务,标准做法是调用随附的辅助脚本,而不是手写 Python 脚本。不要读取脚本源码——本节已包含所需的一切信息。
该脚本会完成三件裸 LLM 摄取代码通常会漏掉的事,每件都对应一种悄无声息的生产故障:
- 分批批量 upsert。 不要逐个文档循环调用
upsert。 - 检查每批结果。 即使个别文档失败,
batch_upsert也会返回 202;失败信息保存在result.errors/result.has_errors中。如果不检查,“已摄取 100 个文档”就会悄无声息地变成“已摄取 73 个文档,另有 27 个丢失”。 - 轮询,直到文档可搜索。 upsert 后,Pinecone 仍在构建倒排索引。在这段时间内调用
documents.search会返回空结果。如果不进行轮询,用户可能会调试自己的查询代码一小时,却始终找不到索引竞态问题。
你只需提供准备好的、符合架构的 JSONL 文件和索引名称;其余工作由脚本完成。架构验证属于上游环节(你的准备流水线,或待推出的 prepare_documents.py)——ingest.py 会信任你交给它的内容。
调用方式:
uv run --script scripts/ingest.py \
--data processed.jsonl \
--index <index_name> \
--sentinel-field <fts_field>
标志:
| 标志 | 短标志 | 必需 | 用途 |
|---|---|---|---|
--data | -d | 是 | 包含准备好的文档的 JSONL 文件路径(每行一个) |
--index | -i | 是 | Pinecone 索引名称(必须已存在) |
--sentinel-field | -f | 是 | 索引中启用了 FTS 的字段,用于就绪状态轮询查询。请选择架构中最长的自由文本字段。 |
--namespace | -n | 否 | 默认为 __default__ |
--batch-size | -b | 否 | 默认为 50(与 SDK 自身的 batch_upsert 默认值一致)。对于大型稠密向量,请调小此值。 包含 50 个文档、每个向量为 3072 维浮点数的批次约为 5-10 MB,可能会被拒绝;维度较高时,请调至 --batch-size 25(或更低)。 |
--max-concurrency | — | 否 | 默认为 4。用于上传批次的并行 HTTP 连接数。 |
--poll-deadline | — | 否 | 默认为 300(秒)。等待文档变为可搜索状态的最长时间,超时后放弃。 |
--sentinel | -s | 否 | 用于就绪状态轮询查询的令牌。默认值:doc[0][sentinel-field] 中按空白字符分隔的第一个词。 |
脚本会打印的内容:
Loading processed.jsonl ...
Loaded 5000 document(s).
Sentinel: body='The'
Upserting in batches of 50 ...
batch @ 0: 50 docs in 0.31s (total: 50/5000)
batch @ 50: 50 docs in 0.29s (total: 100/5000)
...
Upsert complete: 5000 doc(s) in 21.4s.
Polling for searchability (deadline 300s) ...
Searchable after 12.3s (3 probe(s)).
Done — total 33.7s.
如果某个批次失败,脚本会打印每条错误消息并以非零状态退出。如果轮询超时,脚本会提示可能的原因(sentinel 字段未启用 FTS、截止时间过短、文档已在结构上完成 upsert,但被倒排索引构建器拒绝),并以非零状态退出。不要屏蔽这些错误——它们揭示了数据或索引的真实问题。
以下情况不应使用此脚本:
- 用户正在进行逐文档补丁更新。部分字段更新请使用
documents.update(...)(参见references/ingestion.md中的更新文档)——此脚本用于批量加载,不适用于逐条记录操作。 - 用户要从非 JSONL 数据源摄取数据(CSV、Parquet、Postgres 转储)。请先转换为 JSONL;此脚本不会解析其他格式。
- 用户明确要求你从头编写摄取代码(教学场景)。请按要求执行,并遵循标准模式:
documents.batch_upsert+ 检查result.has_errors+ 使用 sentinel 和截止时间轮询documents.search。
脚本位于相对于此技能目录的 scripts/ingest.py。这是采用 PEP 723 内联元数据的脚本——首次调用时,uv run --script 会自动安装 typer 和 pinecone。无需设置。
使用场景
以下是三种具体模式,可作为你处理任务的参考。根据用户的请求选择最接近的模式并遵循其步骤;如果任务确实是混合场景,则灵活处理。
UC-1:端到端索引新语料库
触发条件。“为这个 CSV / JSONL / 文件夹建立搜索索引”、“基于[我的文章 / 产品 / 工单 / 转录文本]构建搜索后端”、“让我的[数据集]可搜索”。
**对于尚未处理或杂乱的数据,请先加载入门指南。**如果用户提供的是原始数据(字段类型不明确、可能包含超出 FTS 限制的长文本字段、逗号分隔的标签字符串、字符串格式的日期、可能重复的 ID 等),且没有提供明确的架构,**请阅读 references/onboarding-walkthrough.md 并逐步遵循其中的流程。**这是一份对话式指南:先了解数据,向用户说明处理决策,提出架构,创建前先确认,然后共同处理、摄取并验证。之所以提供这份指南,是因为架构不可变,而“为新语料库建立索引”属于高风险流程,在每个决策点明确征得用户同意会更稳妥。
如果用户已经提供了格式规范的 JSONL 和架构规范,请按以下精简步骤操作。
步骤(数据已准备好且架构已确定时):
- 检查语料库的结构——文本字段、结构化元数据,以及是否还需要向量。将其与
references/schema-design.md中的规范结构(文章、产品、工单、图像库、代码)匹配。 - 为每个文本字段选择分析器设置——
language、stemming、stop_words。长篇文本启用词干提取,专有名词 / 标识符则关闭。确定哪些字段用于 FTS,哪些仅作为可筛选元数据——请参见下文的可筛选元数据不在架构中声明;仅用于筛选的元数据属于文档侧的决策,而不是架构字段。 - 使用
SchemaBuilder组装架构,并在调用indexes.create之前与用户确认——架构在2026-07中不可变,因此错误的调用会导致必须重新摄取。 - 创建索引。默认情况下,
pc.indexes.create(...)会轮询,直到索引就绪——除非你传入了timeout=-1,否则无需单独的等待循环。 - 运行
scripts/ingest.py --data <jsonl> --index <name> --sentinel-field <fts_field>——参见上文的摄取——使用打包的辅助工具部分。该脚本会在一次调用中处理batch_upsert、逐批检查错误以及更新后的就绪状态轮询。除非用户明确要求,否则不要手写循环。 6.(脚本会自动轮询——正常退出时,索引已经可以搜索。如果跳过脚本并自行实现,则必须使用哨兵查询和截止时间轮询documents.search;batch_upsert返回 ≠ 已可搜索——这是文档索引的等待,与步骤 4 中的索引创建等待相互独立,且需要额外等待。) - 使用一到两个探测查询验证已知包含哨兵内容的字段。
**结果。**针对用户数据的可用 documents.search 调用,并返回按相关性排序的匹配结果。
UC-2:向纯文本语料添加稠密(或稀疏)信号
触发条件。“添加语义搜索”“添加嵌入”“让它支持混合搜索”,或任何描述了仅凭文本无法满足的查询模式(视觉相似度、情绪、跨模态“看起来像什么”)的提示。
步骤。
- 确认新信号代表文本无法表达的模态或信号——图像 / 音频 / 外部评分,或与现有 FTS 字段不同的语料。将相同文本重新编码到稠密字段中属于反模式(
references/schema-design.md→“何时添加稠密字段”)。 - 由于架构不可变,规划创建新索引,而不是迁移。重新创建前先征得用户确认。混合索引必须在创建时显式声明其
sparse_vector字段——之后无法再添加。 - 选择嵌入提供方,并在架构阶段固定其输出维度。注意原生维度可能引发的载荷大小问题——Gemini-3072 等需要截断(
references/ingestion.md→“稠密向量载荷大小”)。 - 架构 → 创建(默认会阻塞,直到就绪)→ 内联提供嵌入或使用预缓存嵌入进行摄取。
- 使用混合查询进行验证:
dense_vectorscore_by+ 文本匹配筛选器($match_phrase/$match_all)。这是受支持的单次调用跨模态查询形式。
结果。 一个索引,两种检索形式——纯文本和稠密向量 + 筛选器混合检索——无需进一步设置即可运行。
UC-3:根据自然语言用户提示构建 documents.search 调用(Agent 模式)
触发条件。 Agent 收到用户提示,例如“查找有关机器学习、提到了 TensorFlow 且发表于 2024 年之后的文章”,或“按与这段文字的相似度排列气候政策相关文档”。索引已存在。
步骤。
- (可选)发现架构:调用
pc.indexes.describe(<NAME>)并读取schema.fields。如果你已在此前的对话中了解字段类型,则跳过。 - 使用下方的 Agent 模式分解表,将用户提示拆解为
score_by/filter形式。(硬性要求 →filter。排序信号 →score_by。始终显式设置include_fields。) - 按照上方“查询”部分的规则构建
documents.search(...)调用——每个请求只使用一种评分类型,运算符与字段类型匹配,并始终设置include_fields。 - 执行调用。响应包含
resp.matches;遍历结果可通过m.to_dict()获取m._id、m._score和字段值。按用户要求的形式使用这些匹配结果。 - 如果结果为空或不正确,请按《常见问题》的故障排查树逐步检查。
结果。 与用户意图相符的实时搜索结果。
应主动避免的四种常见 UC-3 错误:
- 在
score_by中混用评分类型(服务端会拒绝)。将硬性要求放入filter;在score_by中按一种信号排序。 - 将硬性要求作为 BM25 词项放入
score_by,而不是作为$match_all/$match_phrase放入filter(会返回排序后的结果,但不能保证包含该词项)。 - 运算符与字段类型不匹配(例如,在浮点数字段上使用
$match_all,或在字符串字段上使用$gt)。请参阅“查询”部分的运算符表。 - 忽略
include_fields(某些 SDK / 后端构建版本会返回 400)。务必显式传入。
Agent 模式下的查询分解
将用户提示词线索映射到 API 形式。按从上到下的顺序阅读——识别线索,然后复制对应的形式。
| 用户提示词线索 | API 形式 |
|---|---|
| 开放式关键词(“关于机器学习的文章”、搜索栏查询) | score_by=[{"type": "text", "field": "<field>", "query": "<terms>"}] — BM25 词元 OR |
| 精确短语,用于驱动排名(“按‘文笔优美’进行排名”) | score_by=[{"type": "query_string", "query": '<field>:("phrase here")'}] |
| 精确短语,硬性要求(“必须包含‘machine learning’”) | filter={"<field>": {"$match_phrase": "machine learning"}} |
| 必须包含的词元,顺序不限(“必须提到 TensorFlow”、“必须与 Illinois 有关”) | filter={"<field>": {"$match_all": "tokens space-separated"}} — 优先于 query_string 中的 +token,因为它是真正的硬性筛选条件,不会影响得分 |
| 至少包含其中一个词元(“包含 AI 或 ML 或 robotics”) | filter={"<field>": {"$match_any": "AI ML robotics"}} |
| 排除词元(“与 deprecated 无关”、“不要评论文章”) | filter={"$not": {"<field>": {"$match_any": "deprecated opinion"}}} — 或在 query_string 中使用 -token |
| 布尔条件 / 加权 / 邻近度 / 短语前缀(“将‘eagle’的权重设为 3 倍”、“在 N 个词以内”) | score_by=[{"type": "query_string", "query": '<expr with ^N / ~N / "…"*>'}] — 只有 Lucene 支持这些功能 |
| 跨字段布尔条件(“标题或正文包含 X”) | score_by=[{"type": "query_string", "query": 'title:(X) OR body:(X)'}] |
| 数值 / 日期 / 范围 / 布尔元数据(“2024 年之后”、“评分 > 4”、“有库存”) | filter={"<field>": {"$gt": ..., "$gte": ..., "$eq": ..., "$exists": true}} |
| 类别 / 标签 / 列表成员关系(“category = fiction”、“标记为 X”) | filter={"<field>": {"$in": [...]}}(适用于普通可筛选元数据和可筛选的 string_list 字段) |
| 语义相似度 / 情绪 / 主题(“关于 ML 的文章”、“感觉忧郁的文档”) | score_by=[{"type": "dense_vector", "field": "<embedding_field>", "values": embed(<text>)}] — 需要 dense_vector 字段 |
| 视觉外观 / 针对图像语料的跨模态文本查询 | 使用相同的 dense_vector 形式,并采用生成已存储图像向量的嵌入模型。多模态嵌入器(如 Gemini-2)会将文本查询映射到图像空间。 |
| 混合:词法要求 + 语义排名(“关于 ML 且提到 TensorFlow 的文章”) | 词法 → filter($match_all / $match_phrase);语义 → score_by(dense_vector)。单次调用。 |
Agent 必须强制遵守的两条结构规则,不得例外:
- 每个请求只能使用一种评分类型。
score_by接受text/query_string/dense_vector/sparse_vector,但每个请求只能按一种类型排名。不要在score_by中混用 dense 和 text——服务端会拒绝。多字段 BM25 是唯一允许的“列表”模式(多个text子句,或一个跨字段query_string)。 - 混合 = filter + score_by,而不是两个 score_by 子句。 当提示词同时包含词法要求和语义排名信号时,词法部分放入
filter(使用$match_*运算符),语义部分放入score_by。如果确实需要让两种信号都影响排名,则运行两次搜索,并在客户端合并 ID。
可筛选元数据根本不在架构中声明
这是与旧版 pinecone.preview API 相比最大的结构变化,也很容易只理解一半。
对于 托管型索引(本技能中的所有示例都使用这种部署类型——{"deployment_type": "managed", "cloud": ..., "region": ...};省略 deployment= 时也默认使用这种类型),架构中只能声明参与搜索的字段:dense_vector、sparse_vector,以及启用了 full_text_search 的 string 字段。**所有其他字段类型——string(可筛选、不启用 FTS)、string_list、float、boolean——只要出现在架构中,创建时就会被拒绝并返回 400。**这已通过实际验证,而不只是文档说明:服务端的错误信息明确列出了这四种类型——“架构只接受用于搜索的字段(字段类型为 dense_vector、sparse_vector,以及配置了 full_text_search 的 string)。若要将字段 '<name>' 用于筛选(字段类型为 boolean、float、string 或 string_list),请将其从架构中省略,并将其包含在文档中。”(此限制仅适用于托管型/BYOC 部署——只有 pod 部署才允许在架构中声明可筛选元数据,本技能不涉及这种部署。)SchemaBuilder 的 add_float_field、add_boolean_field 和 add_string_list_field 方法仍然存在,并且在 pod 部署中仍可正常使用;但对于本技能始终使用的托管型部署,不要调用这些方法。
正确做法是:**不要在架构中声明任何仅用于筛选的字段,无论其类型是什么。**只需将字段包含在要执行 upsert 的文档中——无论该字段是否出现在架构中,Pinecone 都会自动为 upsert 文档中的字段建立索引以供筛选(字符串和数字/布尔值支持精确匹配,列表支持成员匹配),无需任何配置。
# WRONG on a managed index — the server 400s on EVERY one of these, not just category:
schema = (
SchemaBuilder()
.add_string_field("body", full_text_search={"language": "en"})
.add_string_field("category", filterable=True) # <-- rejected
.add_float_field("year", filterable=True) # <-- also rejected
.add_string_list_field("tags", filterable=True) # <-- also rejected
.build()
)
# RIGHT — the schema declares only search fields. category/year/tags are
# simply included on upserted documents and auto-index for filtering.
schema = (
SchemaBuilder()
.add_string_field("body", full_text_search={"language": "en"})
.build()
)
idx.documents.upsert(namespace=NAMESPACE, documents=[{
"_id": "doc-1",
"body": "...",
"year": 2025.0, # not in the schema — still filterable via $gt / $gte / $eq
"tags": ["classic"], # not in the schema — still filterable via $in / $nin
"category": "fiction", # not in the schema — still filterable via $eq / $in / $exists
}])
旧版预览文档中 references/schema-design.md 的前瞻性说明——“现在就声明元数据字段,为未来做好准备”——已不再适用;现在声明任何仅用于筛选的字段都是错误做法,而非仅仅没有必要。
工作流概览
分为三个阶段。每个阶段都有自己的参考文件——编写该阶段的代码前请先查阅。
- 设计架构。确定哪些字符串字段需要全文搜索(在架构中声明)、是否需要
dense_vector字段(以及它是否值得占用位置),以及是否还需要sparse_vector字段——这些是唯一应该出现在架构中的内容。任何可筛选元数据字段——无论是string、string_list、float还是boolean——都不声明;只需将其包含在 upsert 文档中。在2026-07中,索引创建后架构固定不变——请谨慎规划。→references/schema-design.md - **摄取文档。**如果要从已准备好的 JSONL 批量加载,请运行随附的
scripts/ingest.py辅助脚本(它会按正确方式执行batch_upsert、检查错误并轮询就绪状态——详见上文的 摄取——使用打包的辅助脚本部分)。如果要逐文档修补更新,请使用documents.update(...)。无论哪种方式,HTTP 调用返回后,文档都会异步建立索引;batch_upsert返回 202 ≠ 文档已可搜索。→ 详见references/ingestion.md中的规范模式。 - 查询索引。单个搜索请求只能按一种评分类型排序——在
score_by中准确传入text、query_string、dense_vector或sparse_vector中的一种(支持通过多个text子句或跨字段query_string实现多字段 BM25)。添加filter={...},用于文本匹配($match_phrase/$match_all/$match_any)和元数据筛选($eq/$in/$gte/$exists/$and/$or/$not)。使用include_fields控制响应内容。→references/querying.md
快速模板
用于最小文本索引和可筛选元数据索引的端到端骨架。复制后,编辑所有标记为 # TODO: 的位置。此模板有意省略外部嵌入调用,以保持通用性;有关稠密/稀疏字段模式和嵌入服务提供方集成,请参阅 references/ingestion.md;有关四种评分形式以及文本匹配和元数据筛选器,请参阅 references/querying.md。
import time
from pinecone import Pinecone, SchemaBuilder
INDEX_NAME = "my-fts-index" # TODO: name your index (lowercase alphanumeric + hyphens, 1-45 chars)
NAMESPACE = "__default__" # TODO: pick a namespace; auto-created on first upsert
pc = Pinecone() # reads PINECONE_API_KEY
# TODO: preprod backends require an x-environment header on the client:
# pc = Pinecone(additional_headers={"x-environment": "preprod-aws-0"})
# 1. Schema — one FTS string field. That's the only kind of field that goes
# here: `category` and `year` below are deliberately NOT in the schema —
# see "Filterable metadata isn't declared in the schema at all" in
# SKILL.md. Field names must NOT start with `_` (reserved for `_id` /
# `_score`) or `$` (reserved for filter operators), and are limited to 64
# bytes.
schema = (
SchemaBuilder()
.add_string_field("body", full_text_search={"language": "en"}) # TODO: rename for your content
.build()
)
# 2. Create the index. Polls until ready by default (pass timeout=-1 to
# return immediately instead). Deployment defaults to managed/aws/us-east-1
# when omitted; pass `deployment=` explicitly to pick a different region.
# read_capacity defaults to {"mode": "OnDemand"}; pass
# {"mode": "Dedicated", ...} only if you specifically want provisioned reads.
if not pc.indexes.exists(INDEX_NAME):
pc.indexes.create(
name=INDEX_NAME,
schema=schema,
deployment={"deployment_type": "managed", "cloud": "aws", "region": "us-east-1"},
)
idx = pc.index(name=INDEX_NAME)
# 3. Upsert a single document. `_id` is required, every other field is optional.
# `category` and `year` aren't in the schema but are still filterable —
# see above.
# upsert REPLACES the document on conflict; use documents.update(...) for
# per-field patches (references/ingestion.md).
idx.documents.upsert(
namespace=NAMESPACE,
documents=[{
"_id": "doc-1",
"body": "Full-text search is great for keyword queries.",
"category": "intro",
"year": 2025.0,
}],
)
# 4. Poll until the FTS side is searchable (upsert returns BEFORE docs are indexed).
deadline = time.time() + 300
while time.time() < deadline:
resp = idx.documents.search(
namespace=NAMESPACE, top_k=1,
score_by=[{"type": "text", "field": "body", "query": "search"}], # TODO: sentinel query likely to hit
include_fields=[], # required on every search; [] = lightest payload (ids + _score only)
)
if resp.matches:
break
time.sleep(5)
# 5. Search — text scoring composed with metadata filter.
resp = idx.documents.search(
namespace=NAMESPACE,
top_k=5,
score_by=[{"type": "text", "field": "body", "query": "keyword queries"}],
filter={"year": {"$gte": 2024}}, # TODO: adjust filter or drop it
include_fields=["*"], # "*" = all stored fields; [] = `_id` + `_score` only
)
for m in resp.matches:
print(m._id, m._score, m.to_dict())
常见注意事项
- 在托管索引的架构中,不应声明任何可筛选的元数据字段,无论是 string、string_list、float 还是 boolean。
schema=中只允许使用dense_vector、sparse_vector和启用了 FTS 的string字段;其他所有字段类型都会被拒绝,并返回400。这已通过真实 API 在线确认,并非仅见于文档。省略这些字段,让系统根据已 upsert 的文档自动建立索引。请参阅上文 完全不在架构中声明可筛选元数据——这最可能导致从旧版pinecone.previewAPI 迁移而来的代码出问题,因为旧 API 允许声明这类字段。 - 每个搜索请求只能使用一种评分类型。
score_by接受text、query_string、dense_vector或sparse_vector,但请求只能按一种类型排序。多字段 BM25 没有问题(传入多个text子句,或一个跨字段的query_string)。若要结合 BM25 排序和dense_vector(或sparse_vector)信号,请在词法字段上使用文本匹配filter运算符($match_phrase/$match_all/$match_any)限制稠密搜索,而不是在score_by中混用类型。服务器会拒绝“在score_by中混合稠密向量和文本子句”的模式。 - 文本匹配筛选运算符是跨模态的关键。
$match_phrase(精确短语)、$match_all(匹配所有词元,顺序不限)、$match_any(至少匹配一个词元)是作用于full_text_search字段的筛选运算符。每个运算符接受一个字符串(最多 128 个词元)。它们会复用字段的分词器/词干提取器,可组合使用$and/$or/$not,也是将词法预筛选与稠密或稀疏排序组合起来的受支持方式。短语邻近度("…"~N)、词项加权(^N)和短语前缀("… word"*)只能用于评分——它们属于query_string,不能用于filter。 - 预生产环境的后端需要在
Pinecone()客户端中设置additional_headers={"x-environment": "..."}。 缺少此标头会让请求进入生产环境,导致出现“找不到索引”/结果为空等症状,看起来像代码错误,但实际上不是。 - 每次调用
documents.search(...)都必须传入include_fields。 传入["*"]可包含所有已存储字段;也可以传入字段名称列表以选择要返回的字段。在某些 SDK/后端构建版本中,省略该参数会返回400,而非采用合理的默认值;请始终显式传入,避免意外情况。 - 匹配分数是
_score;文档 ID 是_id。 系统匹配分数始终位于_score字段,因此用户元数据中名为score的字段可以与之共存。请始终读取m._score,不要读取m.score。 - 保留字段名称:不能以下划线
_或美元符号$开头,且最长为 64 字节。_用于系统字段(_id、_score);$用于筛选运算符。架构验证会拒绝违反任一规则的名称。长度上限按字节计算,而不是按字符计算——使用非 ASCII 名称时请格外注意。 - 向量字段基数:在
2026-07中,每个索引最多只能有一个dense_vector和一个sparse_vector字段。 文本字段可以有多个。 - 混合索引必须在创建时声明其
sparse_vector字段——之后无法再添加。 在2026-07中,仅在稠密字段上设置metric="dotproduct"并不代表创建混合索引(旧版预览 API 中则是如此)。无论是否声明该字段,创建调用都会成功;如果缺少sparse_vector字段,之后只有稀疏写入会被拒绝,而且通常发生在代码库的其他部分。如果你要迁移旧版预览架构,请在重新创建索引前检查每个设置了metric="dotproduct"的稠密字段,确认是否缺少稀疏字段。 - 默认情况下,
batch_upsert失败时不会显式报错。 返回值包含has_errors、failed_batch_count,以及带有error_message的BatchError对象列表。如果不检查这些内容,就会看到“已上传 0 / N”和无限期的“尚未建立索引”轮询;真正的原因(负载过大、架构不匹配、保留字段名称)会被隐藏。请务必在后续步骤前打印result.errors[*].error_message。 - 批次中的稠密向量负载大小很重要。 一个含 50 个文档、向量维度为 3072 的批次,大小约为 5–10 MB,可能会被拒绝。如果每个批次都失败,可先尝试通过服务提供商的截断参数降低嵌入维度(例如 Gemini 的
output_dimensionality=768),再排查架构问题。 - 异步建立索引:
batch_upsert返回不等于可以搜索。 HTTP 调用返回后,服务器仍会在后台构建倒排索引。如果立即查询,结果集会为空。请始终使用哨兵查询轮询documents.search,并设置截止时间(参见references/ingestion.md中的模式)。这与pc.indexes.create()自带的默认轮询(等待索引就绪)是两回事,且需要额外执行。 - 字符串 FTS 字段的形状是
full_text_search={...}(字典),也可以使用True采用服务器默认值。用户可设置的子字段:language、stemming、stop_words、ngram。由服务器设置(可在describe()响应中看到,但创建索引时无法设置):lowercase(默认为true)和max_term_len。词干提取默认关闭(false),若设置stop_words=True则必须启用。托管索引中的字符串字段只能启用 FTS 或可筛选,不能同时启用——在full_text_search旁传入filterable=True会导致服务器静默保留筛选配置并丢弃搜索配置。 - 在
2026-07中,索引创建后架构固定不变。 不支持创建后添加、移除或更改字段类型。要更改现有向量字段的维度或度量标准,必须创建新索引。请一次性规划好架构。 - 支持按字段更新:
documents.update(...)。 可传入形如documents=[{"_id": ..., "set_fields": {...}}]的记录,也可以使用filter=+set_fields=/remove_fields=,通过元数据匹配一次性修改多份文档。如果你想完全替换文档,documents.upsert在_id冲突时仍会执行完整替换。请参阅references/ingestion.md→“更新文档”。 - 文档操作:搜索、获取和删除现在都支持
filter。documents.fetch和documents.delete均新增了filter参数——请传入ids、filter中的一个,或者(仅限删除)传入delete_all。按筛选条件获取的结果采用分页方式(通过pagination_token每页最多 10,000 份文档);按 ID 获取的结果永不分页。documents.delete会返回一个响应对象,其中包含matched_records(按筛选条件删除时为某一时点的匹配数量;按 ID 列表或使用delete_all删除时为None——删除操作本身会异步执行)。 documents.list(...)可枚举某个命名空间中的文档 ID;旧版预览 API 没有等效功能。 结果采用惰性分页,并按 ID 排序,还可选择使用prefix筛选。请参阅references/querying.md→“documents.list— 枚举文档 ID”。- 首次 upsert 时会自动创建命名空间。 将任意命名空间字符串传给
documents.upsert/batch_upsert,即可按需创建命名空间;不同命名空间中的文档彼此完全隔离。如果不需要分区,请使用"__default__"。 - 命名空间管理和
describe_index_stats现在可用于文档架构索引。idx.create_namespace(name=...)、idx.list_namespaces()、idx.describe_namespace(name=...)、idx.delete_namespace(name=...)和idx.describe_index_stats()均可使用,并已确认正常工作——签名和示例请参阅references/ingestion.md→“命名空间管理”。 - 文档和请求大小限制:每份文档最大 2 MB;每个请求最大 2 MB 和 1000 份文档;每个启用 FTS 的
string字段最大 100 KB 和 10,000 个词元(超过 256 字节的词元会被分析器截断);每份文档的可筛选元数据(即 FTS 字段以外的所有内容)最大 40 KB。架构最多可声明 100 个 FTS 字符串字段。对于长篇文本语料,请在导入前分块——参阅references/ingestion.md。 score_by子句格式——对于text/dense_vector/sparse_vector,规范写法使用单数field;只有query_string使用fields数组。text:{"type":"text", "field":"<fts_field>", "query":"<terms>"}。query_string:{"type":"query_string", "query":"<lucene>", "fields":["<a>","<b>"]}(可选的fields数组;query_string也接受裸字符串"fields":"body",以及作为别名的旧式"field":"body")。dense_vector:{"type":"dense_vector", "field":"<dense_field>", "values":[/*floats*/]}。sparse_vector:{"type":"sparse_vector", "field":"<sparse_field>", "sparse_values":{"indices":[...],"values":[...]}}——注意,稀疏子句使用sparse_values(不是values)。
- 不支持单词项前缀通配符。
auto*在query_string中不起作用;请使用短语前缀("machine lea"*——短语必须至少包含两个词项,最后一个词项会按前缀匹配)。 - 在启用了 CMEK 的项目中,只要索引包含任意
full_text_search字段,就无法创建索引;2026-07中,文档型索引也不支持备份/恢复。 如果这两项要求中的任意一项不可或缺,那么文档架构 FTS 功能目前还不能满足需求。 - 支持模糊搜索(
term~N)和正则表达式搜索(field:/pattern/), 但仅限type: "query_string"——请参阅 查询语法 和references/querying.md。这两种语法都不能用于type: "text"。 - Pinecone 支持从对象存储批量导入文档型索引(参见 导入数据)——只是此 skill 尚未实现该功能。此处请使用
documents.upsert/documents.batch_upsert(通过scripts/ingest.py,参见上文)导入数据;也可以在有专用导入 skill 后使用它。
扩展点
目前已随项目发布在 scripts/ 下:
scripts/ingest.py— 将准备好的 JSONL 批量写入现有 FTS 索引。会以安全的批次大小处理batch_upsert,检查每个批次的result.errors,并在失败时明确中止;随后使用哨兵值和截止时间轮询documents.search,直到文档可搜索。与架构无关:只接受--data、--index和--sentinel-field。用法请参阅上文 导入 — 使用打包的辅助工具 一节。
查询构造没有打包的辅助工具——请按照上文 查询 一节,直接编写 documents.search(...) 调用。