Pinecone RAG教程:构建向量检索问答系统

本教程面向有编程基础的开发者,深入讲解如何构建一个基于Claude、Pinecone和Voyage AI的检索增强生成(RAG)系统。通过一个亚马逊产品数据集,演示了从数据嵌入、向量存储、语义查询到利用Claude生成关键词优化搜索,最终结合检索结果生成答案的完整流程。教程不仅提供代码实现,还剖析了RAG架构的设计思想、关键组件交互、常见错误排查及生产环境优化策略,帮助开发者掌握构建可靠、高效RAG应用的核心技能。

技术背景

大型语言模型(LLM)如Claude虽然强大,但存在知识截止日期、无法访问私有数据、可能产生幻觉等问题。检索增强生成(RAG)通过将外部知识库(如向量数据库)与LLM结合,在生成答案前先检索相关信息,从而提升回答的准确性、时效性和可解释性。本教程选择Pinecone作为向量数据库,因其托管服务、易于扩展、支持多种索引类型;Voyage AI提供高质量嵌入模型,能将文本转换为语义向量;Claude负责理解问题、生成搜索关键词和最终答案。该方案适用于问答系统、智能客服、内容推荐等场景,但需注意嵌入模型的选择、索引配置和检索质量对最终效果的影响。

核心原理

RAG的核心思想是“先检索,后生成”。流程如下:1)离线阶段:将文档(如产品描述)通过嵌入模型(Voyage AI)转换为向量,并连同元数据(如原始文本)一起存入Pinecone索引。2)在线查询阶段:用户问题被嵌入为查询向量,在Pinecone中通过相似度计算(如点积)检索最相关的文档片段。3)生成阶段:将检索到的文档片段与用户问题组合成提示词,交给Claude生成最终答案。本教程还展示了优化检索的进阶技巧:利用Claude从用户问题中生成多个搜索关键词,分别检索后合并结果,以提高召回率。关键设计决策包括:使用ServerlessSpec简化部署、选择点积作为相似度度量(适用于归一化向量)、批量嵌入和上传以提高效率、处理速率限制等。

架构分析

系统由四个主要组件构成:1)数据源:亚马逊产品描述JSONL文件,通过pandas加载。2)嵌入服务:Voyage AI客户端,调用vo.embed将文本转为1024维向量。3)向量数据库:Pinecone索引,配置为Serverless(AWS us-west-2),维度1024,度量方式为点积。4)LLM:Anthropic Claude,通过client.completions.create生成关键词和最终答案。数据流:离线时,数据分批(每批100条)嵌入并上传至Pinecone;在线时,用户问题先嵌入查询,或先由Claude生成关键词再嵌入查询,检索结果经格式化后与问题一起输入Claude。架构设计思想强调解耦:嵌入、存储、生成各自独立,便于替换组件(如换用其他嵌入模型或向量库)。同时,通过批量处理和错误重试机制保证系统稳定性。

完整实战教程

使用Pinecone进行检索增强生成

本笔记本演示如何通过一种称为检索增强生成(RAG)的技术,将Claude与Pinecone向量数据库中的数据连接起来。我们将涵盖以下步骤:

  1. 使用Voyage AI的嵌入模型对数据集进行嵌入
  2. 将嵌入上传到Pinecone索引
  3. 从向量数据库中检索信息
  4. 使用Claude结合数据库中的信息回答问题

设置

首先,让我们安装必要的库并设置本笔记本中需要使用的 API 密钥。我们需要获取一个 Claude API 密钥、一个免费的 Pinecone API 密钥 和一个免费的 Voyage AI API 密钥。

%pip install anthropic datasets pinecone-client voyageai
# Insert your API keys here
ANTHROPIC_API_KEY = "<YOUR_ANTHROPIC_API_KEY>"
PINECONE_API_KEY = "<YOUR_PINECONE_API_KEY>"
VOYAGE_API_KEY = "<YOUR_VOYAGE_API_KEY>"

下载数据集

现在让我们下载包含超过1万条亚马逊产品描述的亚马逊产品数据集,并将其加载到DataFrame中。

import pandas as pd

# Download the JSONL file
!wget  https://www-cdn.anthropic.com/48affa556a5af1de657d426bcc1506cdf7e2f68e/amazon-products.jsonl

data = []
with open("amazon-products.jsonl") as file:
    for line in file:
        try:
            data.append(eval(line))  # noqa: S307
        except (SyntaxError, ValueError):
            # Skip malformed lines in the dataset
            pass

df = pd.DataFrame(data)
display(df.head())
len(df)

向量数据库

要创建我们的向量数据库,首先需要从 Pinecone 获取一个免费的 API 密钥。拿到密钥后,我们可以按如下方式初始化数据库:

from pinecone import Pinecone

pc = Pinecone(api_key=PINECONE_API_KEY)

接下来,我们设置索引规范,这允许我们定义要部署索引的云提供商和区域。你可以在此处找到所有可用提供商和区域的列表。

from pinecone import ServerlessSpec

spec = ServerlessSpec(cloud="aws", region="us-west-2")

然后,我们初始化索引。我们将使用 Voyage 的 "voyage-2" 模型来创建嵌入,因此我们将维度设置为 1024。

import time

index_name = "amazon-products"
existing_indexes = [index_info["name"] for index_info in pc.list_indexes()]

# check if index already exists (it shouldn't if this is first time)
if index_name not in existing_indexes:
    # if does not exist, create index
    pc.create_index(
        index_name,
        dimension=1024,  # dimensionality of voyage-2 embeddings
        metric="dotproduct",
        spec=spec,
    )
    # wait for index to be initialized
    while not pc.describe_index(index_name).status["ready"]:
        time.sleep(1)

# connect to index
index = pc.Index(index_name)
time.sleep(1)
# view index stats
index.describe_index_stats()

我们应该看到新的 Pinecone 索引的 total_vector_count 为 0,因为我们还没有添加任何向量。

嵌入

要开始使用 Voyage 的嵌入功能,请前往此处获取 API 密钥。

现在让我们设置 Voyage 客户端,并演示如何使用 embed 方法创建嵌入。要了解有关将 Voyage 嵌入与 Claude 结合使用的更多信息,请参阅此笔记本。

import voyageai

vo = voyageai.Client(api_key=VOYAGE_API_KEY)

texts = ["Sample text 1", "Sample text 2"]

result = vo.embed(texts, model="voyage-2", input_type="document")
print(result.embeddings[0])
print(result.embeddings[1])

将数据上传到 Pinecone 索引

设置好嵌入模型后,我们现在可以获取产品描述,对其进行嵌入,并将嵌入向量上传到 Pinecone 索引。

from time import sleep

from tqdm.auto import tqdm

descriptions = df["text"].tolist()
batch_size = 100  # how many embeddings we create and insert at once

for i in tqdm(range(0, len(descriptions), batch_size)):
    # find end of batch
    i_end = min(len(descriptions), i + batch_size)
    descriptions_batch = descriptions[i:i_end]
    # create embeddings (try-except added to avoid RateLimitError. Voyage currently allows 300/requests per minute.)
    done = False
    while not done:
        try:
            res = vo.embed(descriptions_batch, model="voyage-2", input_type="document")
            done = True
        except Exception:
            sleep(5)

    embeds = [record for record in res.embeddings]
    # create unique IDs for each text
    ids_batch = [f"description_{idx}" for idx in range(i, i_end)]

    # Create metadata dictionaries for each text
    metadata_batch = [{"description": description} for description in descriptions_batch]

    to_upsert = list(zip(ids_batch, embeds, metadata_batch, strict=False))

    # upsert to Pinecone
    index.upsert(vectors=to_upsert)

进行查询

索引填充完毕后,我们就可以开始进行查询以获取结果了。我们可以将一个自然语言问题嵌入,然后针对索引进行查询,以返回语义相似的产品描述。

USER_QUESTION = (
    "I want to get my daughter more interested in science. What kind of gifts should I get her?"
)

question_embed = vo.embed([USER_QUESTION], model="voyage-2", input_type="query")
results = index.query(vector=question_embed.embeddings, top_k=5, include_metadata=True)
results

优化搜索

这些结果已经不错,但我们还可以进一步优化。利用Claude,我们可以从用户的问题中生成搜索关键词。这样我们就能在索引上进行更广泛、更多样化的搜索,从而获得更相关的产品描述。

import anthropic

client = anthropic.Anthropic(api_key=ANTHROPIC_API_KEY)


def get_completion(prompt):
    completion = client.completions.create(
        model="claude-2.1",
        prompt=prompt,
        max_tokens_to_sample=1024,
    )
    return completion.completion
def create_keyword_prompt(question):
    return f"""\n\nHuman: Given a question, generate a list of 5 very diverse search keywords that can be used to search for products on Amazon.

The question is: {question}

Output your keywords as a JSON that has one property "keywords" that is a list of strings. Only output valid JSON.\n\nAssistant:{{"""

在完成 Anthropic 客户端设置并创建提示词后,我们现在可以开始根据问题生成关键词。我们将以 JSON 对象的形式输出关键词,以便轻松从 Claude 的输出中解析它们。

keyword_json = "{" + get_completion(create_keyword_prompt(USER_QUESTION))
print(keyword_json)
import json

# Extract the keywords from the JSON
data = json.loads(keyword_json)
keywords_list = data["keywords"]
print(keywords_list)

现在我们的关键词已经在一个列表中,让我们对每个关键词进行嵌入,在索引中查询,并返回最相关的3个产品描述。

results_list = []
for keyword in keywords_list:
    # get the embeddings for the keywords
    query_embed = vo.embed([keyword], model="voyage-2", input_type="query")
    # search for the embeddings in the Pinecone index
    search_results = index.query(vector=query_embed.embeddings, top_k=3, include_metadata=True)
    # append the search results to the list
    for search_result in search_results.matches:
        results_list.append(search_result["metadata"]["description"])
print(len(results_list))

使用 Claude 回答

现在我们有了产品描述列表,让我们将它们格式化为 Claude 训练过的搜索模板,并将格式化后的描述传递给另一个提示。

# Formatting search results
def format_results(extracted: list[str]) -> str:
    result = "\n".join(
        [
            f'<item index="{i + 1}">\n<page_content>\n{r}\n</page_content>\n</item>'
            for i, r in enumerate(extracted)
        ]
    )

    return f"\n<search_results>\n{result}\n</search_results>"


def create_answer_prompt(results_list, question):
    return f"""\n\nHuman: {format_results(results_list)} Using the search results provided within the <search_results></search_results> tags, please answer the following question <question>{question}</question>. Do not reference the search results in your answer.\n\nAssistant:"""

最后,让我们提出原始用户的问题,并从Claude那里获得答案。

answer = get_completion(create_answer_prompt(results_list, USER_QUESTION))
print(answer)

常见问题

常见错误及排查:1)API密钥错误:确保三个密钥正确设置,且环境变量或代码中无拼写错误。2)Pinecone索引未就绪:创建索引后需轮询状态直到ready,否则连接失败。3)嵌入维度不匹配:确保dimension=1024与voyage-2模型输出一致。4)速率限制(RateLimitError):Voyage AI限制每分钟请求数,代码中已用while循环和sleep(5)重试,可调整等待时间。5)JSON解析错误:Claude输出可能包含额外文本,需确保只解析有效JSON,可增加后处理。6)检索结果不相关:可调整top_k值、优化关键词生成提示词,或尝试不同嵌入模型。生产环境优化:1)使用异步或批处理提高吞吐量。2)为Pinecone索引配置副本和分片以提升性能。3)监控API调用和延迟,设置缓存减少重复查询。4)定期更新索引以反映数据变化。5)考虑使用Claude的流式输出改善用户体验。

企业应用扩展

本教程可扩展的方向:1)替换数据集:支持PDF、网页等格式,需增加文档解析和分块逻辑。2)多模态检索:结合图像嵌入,实现图文混合检索。3)混合检索:结合关键词(BM25)和向量检索,提高精确率。4)对话式RAG:维护对话历史,使Claude能结合上下文回答。5)评估与调优:使用RAGAS等框架评估检索和生成质量,调整嵌入模型、分块大小、top_k等参数。6)部署为API服务:使用FastAPI封装,提供REST接口。7)安全与隐私:对敏感数据加密,控制访问权限。8)成本优化:使用Pinecone的按需计费,合理设置索引生命周期。

总结

本教程面向有编程基础的开发者,深入讲解如何构建一个基于Claude、Pinecone和Voyage AI的检索增强生成(RAG)系统。通过一个亚马逊产品数据集,演示了从数据嵌入、向量存储、语义查询到利用Claude生成关键词优化搜索,最终结合检索结果生成答案的完整流程。教程不仅提供代码实现,还剖析了RAG架构的设计思想、关键组件交互、常见错误排查及生产环境优化策略,帮助开发者掌握构建可靠、高效RAG应用的核心技能。

内容来源与验证

继续系统学习

站内还有更多完整实战教程

加入会员,解锁全部教程、完整代码与持续更新内容。

加入会员
查看全部RAG 与知识库教程