资讯动态

从零构建一个向量数据库检索器:用 TaoToken 统一 Key 打通嵌入与检索链路

发布时间:2026/10/18 11:38:28 来源:尧图企业网站定制
1. 从零构建向量数据库检索器为什么嵌入与检索要拆开管向量数据库检索器说白了就是一套“把文字变成向量、存进库、再按相似度捞回来”的流水线。它要解决的核心问题是当你有几百上千份文档时怎么在毫秒级找到和问题最相关的几段内容。适合谁适合正在做 RAG 应用、知识库问答、文档语义搜索的开发者尤其是那些已经跑通 demo、但被嵌入模型调用和向量库配置搅得头大的同学。我见过太多人卡在同一个地方嵌入模型走一个通道向量库走另一个通道两边的 Key、Base URL、模型名各管各的。一旦要换嵌入模型或者想从 OpenAI 切到别的供应商整条链路就得重配一遍。更麻烦的是嵌入调用和检索调用分散在不同文件里排查问题时根本不知道是嵌入没生成对还是索引没写进去。所以这篇教程的思路很明确用 TaoToken 统一 Key 和 API 通道把嵌入生成和向量检索这两件事的调用入口收敛到一处。向量库这边我用 Pinecone 做参照编排层用 LlamaIndex因为这两个组合在社区里最常被拿来搭检索器。你跟着走完会得到一份可复制的环境变量配置、一段能直接跑的写入加查询代码以及一套遇到报错时能自己定位的排查方法。整条链路分四步先配好统一通道再生成嵌入并写入索引然后做一次 top-k 相似度查询最后把结果解析成节点集放进自定义检索器。每一步我都会给出实际命令和参数你照着改就能用。重点不是背 API而是理解“嵌入调用”和“向量查询”这两个动作怎么通过同一个 Base URL 串起来。2. TaoToken 前置统一 Key 与 Base URL 的接入准备在动手写检索器之前先把调用通道理顺。TaoToken 在这里扮演的角色是统一的 API 入口嵌入模型调用和后续可能的对话模型调用都走同一个 Base URL 和同一把 Key。这样做的好处是你的代码里只需要维护一份凭证换模型时改一个 Model ID 就行不用满项目找散落的 api_key。你需要准备两样东西一把 TaoToken 的 API Key以及确认要用的嵌入模型 ID。Key 在控制台的 API Keys 页面创建地址是 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 。创建后复制出来后面写进环境变量。模型 ID 方面嵌入场景常用的是文本嵌入类模型具体名称以你控制台里可用的为准写代码时填进embed_model参数。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有 Base URL 的完整写法和各语言示例。核心就一句所有请求的 Base URL 统一用https://taotoken.net/api注意这个地址不带任何查询参数直接作为api_base或base_url填进去。环境变量我建议这样组织放在项目根目录的.env里# TaoToken 统一通道 TAOTOKEN_API_KEYsk-你的key TAOTOKEN_BASE_URLhttps://taotoken.net/api # 嵌入模型 ID以控制台实际可用为准 EMBED_MODEL_ID你的嵌入模型ID # Pinecone 配置 PINECONE_API_KEY你的pinecone key PINECONE_ENVus-west1-gcp PINECONE_INDEXquickstart这里有个容易踩的坑Pinecone 的 Key 和 TaoToken 的 Key 是两套东西别混。TaoToken 管的是模型调用通道Pinecone 管的是向量存储。两者通过 LlamaIndex 的PineconeVectorStore桥接起来各用各的凭证。如果你还没装依赖先把这几个包装上pip install llama-index-core llama-index-vector-stores-pinecone llama-index-embeddings-openai pinecone python-dotenv pymupdf装完之后在代码开头加载环境变量import os from dotenv import load_dotenv load_dotenv() api_key os.environ[TAOTOKEN_API_KEY] base_url os.environ[TAOTOKEN_BASE_URL] embed_model_id os.environ[EMBED_MODEL_ID]到这一步通道就准备好了。接下来所有模型调用都复用base_url和api_key不再出现第二个入口。3. 可复制配置嵌入模型与 Pinecone 索引的对接片段这一节给你可以直接复制的配置片段分三块嵌入模型初始化、Pinecone 索引创建、LlamaIndex 的 VectorStore 包装。三块拼起来就是检索器的存储底座。先看嵌入模型。LlamaIndex 里用OpenAIEmbedding类来调嵌入接口关键是把它指向 TaoToken 的 Base URLfrom llama_index.embeddings.openai import OpenAIEmbedding embed_model OpenAIEmbedding( modelembed_model_id, api_keyapi_key, api_basebase_url, )注意参数名是api_base不是base_url这是 LlamaIndex 这个类的写法。填错会直接报连接错误。model填你在控制台确认的嵌入模型 ID。接着创建 Pinecone 索引。维度要和嵌入模型输出维度一致常见嵌入模型是 1536 维如果你用的模型维度不同把dimension改掉import pinecone pinecone.init( api_keyos.environ[PINECONE_API_KEY], environmentos.environ[PINECONE_ENV], ) index_name os.environ[PINECONE_INDEX] if index_name not in pinecone.list_indexes(): pinecone.create_index( nameindex_name, dimension1536, metriceuclidean, ) pinecone_index pinecone.Index(index_name)这里metric用euclidean还是cosine取决于你的嵌入模型训练时用的距离度量。大多数文本嵌入用 cosine 更稳但如果你参照的示例是 euclidean保持一致也能跑通。关键是写入和查询用同一个 metric别中途换。然后用 LlamaIndex 的包装器把 Pinecone 索引接进来from llama_index.vector_stores.pinecone import PineconeVectorStore vector_store PineconeVectorStore(pinecone_indexpinecone_index)到这里存储层就绪。接下来是写入。加载文档、切分、生成嵌入、写进 Pinecone这一串用VectorStoreIndex.from_documents一次完成from llama_index.core import VectorStoreIndex, StorageContext from llama_index.core.node_parser import SentenceSplitter from llama_index.readers.file import PyMuPDFReader loader PyMuPDFReader() documents loader.load(file_path./data/your_doc.pdf) splitter SentenceSplitter(chunk_size1024) storage_context StorageContext.from_defaults(vector_storevector_store) index VectorStoreIndex.from_documents( documents, transformations[splitter], storage_contextstorage_context, embed_modelembed_model, )这段代码执行时LlamaIndex 会调用embed_model把每个 chunk 转成向量再通过vector_store写进 Pinecone。嵌入调用走的是 TaoToken 的 Base URL向量写入走的是 Pinecone 自己的通道两者互不干扰。如果你想把配置写成 JSON 或 TOML 方便复用可以这样组织{ taotoken: { base_url: https://taotoken.net/api, api_key_env: TAOTOKEN_API_KEY }, embedding: { model_id: 你的嵌入模型ID, dimension: 1536 }, pinecone: { index_name: quickstart, metric: euclidean, environment: us-west1-gcp } }这份配置的好处是嵌入模型和向量库的参数集中在一处换模型时只改model_id和dimension代码逻辑不动。4. 验证请求一次写入加一次查询确认检索可复现配置写完必须做一次端到端验证写入一批数据然后查一次看结果是否稳定复现。这一步不做后面出问题你都不知道是嵌入没生成还是索引没写进去。先确认写入成功。写入后查一下索引的统计信息stats pinecone_index.describe_index_stats() print(stats)如果total_vector_count大于 0说明向量已经进去了。如果还是 0回去检查from_documents那步有没有报错以及embed_model是否真的调通了。接着做查询。查询分两步先把问题转成查询嵌入再用这个嵌入去 Pinecone 做 top-k 相似度检索。query_str 你的问题文本 query_embedding embed_model.get_query_embedding(query_str) from llama_index.core.vector_stores import VectorStoreQuery vector_store_query VectorStoreQuery( query_embeddingquery_embedding, similarity_top_k2, modedefault, ) query_result vector_store.query(vector_store_query)similarity_top_k2表示返回最相似的 2 个节点。modedefault是密集向量检索如果你后面想试稀疏或混合模式改这个参数即可。拿到结果后把节点和分数解析出来from llama_index.core.schema import NodeWithScore from typing import Optional nodes_with_scores [] for idx, node in enumerate(query_result.nodes): score: Optional[float] ( query_result.similarities[idx] if query_result.similarities else None ) nodes_with_scores.append(NodeWithScore(nodenode, scorescore)) for node in nodes_with_scores: print(fScore: {node.score:.4f}) print(node.node.get_content()[:300]) print(---)跑通后你会看到两段文本和对应的相似度分数。验证可复现的关键动作是同一个query_str连续查两次返回的节点顺序和分数应该一致。如果两次结果不同检查是不是索引里混入了重复数据或者similarity_top_k设得太大导致边界抖动。把这段逻辑封装成自定义检索器方便后面接查询引擎from llama_index.core import QueryBundle from llama_index.core.retrievers import BaseRetriever from typing import Any, List class PineconeRetriever(BaseRetriever): def __init__(self, vector_store, embed_model, query_modedefault, similarity_top_k2): self._vector_store vector_store self._embed_model embed_model self._query_mode query_mode self._similarity_top_k similarity_top_k super().__init__() def _retrieve(self, query_bundle: QueryBundle) - List[NodeWithScore]: query_embedding self._embed_model.get_query_embedding(query_bundle.query_str) vector_store_query VectorStoreQuery( query_embeddingquery_embedding, similarity_top_kself._similarity_top_k, modeself._query_mode, ) query_result self._vector_store.query(vector_store_query) nodes_with_scores [] for idx, node in enumerate(query_result.nodes): score query_result.similarities[idx] if query_result.similarities else None nodes_with_scores.append(NodeWithScore(nodenode, scorescore)) return nodes_with_scores retriever PineconeRetriever(vector_store, embed_model) retrieved_nodes retriever.retrieve(query_str)到这里一次完整的写入加查询就验证完了。检索结果可复现说明嵌入通道和向量库通道都通了。5. 本篇常见错排查401、local proxy failed 与 reading choices跑这条链路时报错基本集中在几个地方。我按实际遇到的频率排一下每个都给出定位方法。401 错误最常见的是 Key 没配对。如果你看到401 Unauthorized先确认TAOTOKEN_API_KEY有没有正确加载。用print(api_key[:8])看前几位确认不是空值或占位符。另一个可能是把 Pinecone 的 Key 填到了 TaoToken 的位置或者反过来。两套 Key 分开检查别混用。local proxy failed这个报错通常出现在请求发不出去的时候。检查api_base是不是写成了https://taotoken.net/api有没有多写斜杠或漏写。另外确认你的运行环境能正常访问外网接口本地防火墙或公司网络策略有时会拦截。如果是在容器里跑检查容器的网络配置。reading choices 相关报错如果你在解析响应时看到类似reading choices的报错说明返回结构和你预期的不一致。这通常是因为嵌入接口和对话接口的返回格式不同你把对话的解析逻辑用到了嵌入上。嵌入调用走get_query_embedding返回的是向量列表不要去读choices字段。OAuth 或认证类报错如果你看到 OAuth 相关提示检查是不是误用了需要额外认证的端点。TaoToken 的 API 调用用 Key 认证即可Base URL 填对、Key 填对不需要额外的 OAuth 流程。Pinecone 维度不匹配报错信息里会提到 dimension mismatch。回去检查create_index时的dimension和嵌入模型实际输出维度是否一致。1536 是常见值但如果你换的模型输出 768 或 1024索引维度必须同步改。改维度需要重建索引旧数据要重新写入。查询结果为空如果query_result.nodes是空的先确认索引里确实有数据describe_index_stats看 count再确认查询嵌入和写入嵌入用的是同一个模型。用不同模型生成的向量不在同一空间相似度计算没有意义。排查时有个通用思路把链路拆成“嵌入生成”和“向量查询”两段分别验证。先单独调embed_model.get_query_embedding(test)看能不能返回向量再单独用这个向量查 Pinecone看能不能返回节点。哪段断了问题就在哪段。6. 语义一致 CTA把统一通道用进你的检索器项目走到这里你已经有了一个能跑通的向量数据库检索器嵌入生成走 TaoToken 统一通道向量存储走 Pinecone编排用 LlamaIndex。整条链路的 Key 和 Base URL 收敛在一处换模型时只改一个 Model ID。如果你接下来要把它接进实际项目几个方向可以继续一是把PineconeRetriever接到RetrieverQueryEngine加上对话模型做完整 RAG 问答二是试不同的mode参数对比密集、稀疏、混合检索的效果三是把嵌入模型换成别的验证统一通道下切换成本有多低。需要继续拿 Key 或看接入细节的API Keys 在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。想先验证模型对话效果的可以去 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite 试一下。如果你打算长期做编码和 Agent 类项目Coding Plan 在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 。最后留一个实用技巧把embed_model和vector_store的初始化封装成一个工厂函数传入配置字典就返回组装好的检索器。这样你在不同项目里复用同一套通道配置不用每次重写初始化逻辑。检索器这东西配置对了就稳配置错了就到处报错把入口收敛好后面省很多事。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑