ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

PocketFlow 向量数据库实战指南:7 大主流向量检索方案选型对比与 Python 接入代码

PocketFlow 向量数据库实战指南:7 大主流向量检索方案选型对比与 Python 接入代码 人工智能大模型AI Agent工作流自动化RAG【免费下载链接】PocketFlowPocket Flow: 100-line LLM framework. Let Agents build Agents!项目地址https://gitcode.com/gh_mirrors/poc/PocketFlow点击查看免费下载导读在 LLM 应用中向量检索是 RAG检索增强生成、记忆召回、语义搜索等能力的核心底座文本先被 embedding 模型映射为稠密向量再由向量数据库完成最近邻检索。本文以 PocketFlow 官方文档《Vector Databases》为主体完整梳理 FAISS、Pinecone、Qdrant、Weaviate、Milvus、Chroma、Redis 七种主流向量检索方案的免费额度、定价模型与 Python 接入代码并结合仓库内的 RAG 设计模式 与 Chat with Memory 示例 的源码实现说明如何把向量检索真正接入 PocketFlow 的 Node/Flow 流程。读完本文你将掌握各方案的选型要点、可复制的接入代码以及一套与 PocketFlow Agent 工作流结合的落地范式。一、向量检索在 LLM 应用中的位置为什么要选型向量数据库解决的是语义相似度检索问题。在 PocketFlow 的 RAG 设计模式 中完整的 RAG 管线被拆成两个阶段离线阶段构建索引文档分块Chunk→ 每个块生成 Embedding → 把向量存入向量数据库在线阶段检索问答用户问题生成 Embedding → 在索引中检索最相关的块 → 把问题与上下文一起交给 LLM 生成答案。其中生成 Embedding对应文档 Embedding存入向量数据库/执行检索正是本文要展开的环节。二者共同构成 RAG 的语义检索链路Embedding 负责把文本变成向量向量数据库负责在向量空间里快速找最近邻。因此选型向量数据库的实质是在部署成本、检索性能、功能丰富度、与现有技术栈的契合度之间做权衡。二、主流向量检索方案横向对比文档 Vector Databases 给出了一份主流向量检索方案对比表覆盖免费额度与定价模型两个关键选型维度工具免费额度Free Tier定价模型Pricing Model说明FAISS无自托管self-host开源Meta 开源的向量索引库非独立数据库服务内嵌在应用进程中Pinecone2GB 免费每月 25 美元起全托管云服务无需运维索引Qdrant1GB 免费云额度按量付费pay-as-you-goRust 实现支持过滤与负载均衡Weaviate14 天沙箱试用每月 25 美元起自带 GraphQL 查询层与多种模块Milvus5GB 免费云额度按量付费或每月 99 美元专属实例面向大规模向量的分布式系统支持 GPU 加速Chroma无自托管免费Apache 2.0 协议轻量级嵌入式向量库适合原型与本地开发Redis30MB 免费每月 5 美元起在既有 Redis 上叠加向量检索能力RediSearch 模块2.1 按部署形态理解这张表进程内库In-processFAISS 和 Chroma 不需要独立服务直接作为 Python 库/嵌入式数据库随应用运行零运维成本最契合原型验证与单机场景。仓库中的 Chat with Memory 示例 正是采用 FAISS 以进程内方式完成向量索引。全托管云服务Managed CloudPinecone、Qdrant Cloud、Milvus Cloud、Weaviate Cloud 负责索引构建、分片与扩容代价是网络往返延迟与按用量计费。通用存储的向量扩展Redis 借助 RediSearch 模块在 KV 存储之上提供 KNN 检索适合已在用 Redis 的团队复用基础设施。2.2 从文档 Embedding 得到的选型提示文档在 Embedding 章节给出了一条重要实践建议Embedding 相对于 Flow 设计而言只是微优化建议先选最顺手的方案之后再优化。这条原则同样适用于向量数据库选型——先用最容易跑通、免费额度最充裕的方案把流程串起来等检索规模与性能瓶颈真正出现时再迁移而不是在项目初期就陷入分布式调优。三、七种方案的 Python 接入实战以下是文档提供的各工具基础用法代码。所有示例均遵循同一逻辑创建索引/集合 → 写入向量 → 用查询向量做最近邻检索。示例统一使用 128 维向量d 128以保持一致性实际接入时请替换为你所用 embedding 模型的真实维度。3.1 FAISS开源自托管import faiss import numpy as np # Dimensionality of embeddings d 128 # Create a flat L2 index index faiss.IndexFlatL2(d) # Random vectors data np.random.random((1000, d)).astype(float32) index.add(data) # Query query np.random.random((1, d)).astype(float32) D, I index.search(query, k5) print(Distances:, D) print(Neighbors:, I)要点说明IndexFlatL2是暴力精确检索exhaustive search适合中小规模索引数据量增大后可替换为IndexIVFFlat倒排量化等近似最近邻索引。输入向量必须是float32的 NumPy 数组这是 FAISS 的性能前提。返回值D是距离矩阵I是命中向量的下标矩阵shape 均为(查询数, k)。3.2 Pinecone全托管云服务import pinecone pinecone.init(api_keyYOUR_API_KEY, environmentYOUR_ENV) index_name my-index # Create the index if it doesnt exist if index_name not in pinecone.list_indexes(): pinecone.create_index(nameindex_name, dimension128) # Connect index pinecone.Index(index_name) # Upsert vectors [ (id1, [0.1]*128), (id2, [0.2]*128) ] index.upsert(vectors) # Query response index.query([[0.15]*128], top_k3) print(response)要点说明upsert的每个元素是(id, vector)二元组id用于后续删除与更新。查询时只需传入查询向量与top_k服务端自动完成最近邻计算并返回相似度得分。代码中的environment参数与新版 Pinecone 客户端 API 存在差异实际使用时以你所安装客户端版本的初始化方式为准。3.3 QdrantRust 高性能方案import qdrant_client from qdrant_client.models import Distance, VectorParams, PointStruct client qdrant_client.QdrantClient( urlhttps://YOUR-QDRANT-CLOUD-ENDPOINT, api_keyYOUR_API_KEY ) collection my_collection client.recreate_collection( collection_namecollection, vectors_configVectorParams(size128, distanceDistance.COSINE) ) points [ PointStruct(id1, vector[0.1]*128, payload{type: doc1}), PointStruct(id2, vector[0.2]*128, payload{type: doc2}), ] client.upsert(collection_namecollection, pointspoints) results client.search( collection_namecollection, query_vector[0.15]*128, limit2 ) print(results)要点说明Qdrant 的亮点是payload 附带与过滤每个PointStruct可携带任意元数据如示例中的type字段检索时可按 payload 条件先过滤再搜索。距离度量通过Distance.COSINE显式声明可选COSINE、EUCLIDL2、DOT点积。recreate_collection会重建集合生产环境应改用create_collection或先判断集合是否存在。3.4 WeaviateGraphQL 语义层import weaviate client weaviate.Client(https://YOUR-WEAVIATE-CLOUD-ENDPOINT) schema { classes: [ { class: Article, vectorizer: none } ] } client.schema.create(schema) obj { title: Hello World, content: Weaviate vector search } client.data_object.create(obj, Article, vector[0.1]*128) resp ( client.query .get(Article, [title, content]) .with_near_vector({vector: [0.15]*128}) .with_limit(3) .do() ) print(resp)要点说明通过 Schema 先定义类classvectorizer: none表示向量由外部提供即使用你自己的 embedding 模型。查询走 GraphQL 语法get指定类与返回字段with_near_vector传入查询向量with_limit控制返回条数。Weaviate 同样支持把向量化模型作为模块内置实现文本进、结果出的端到端能力。3.5 Milvus大规模分布式检索from pymilvus import connections, FieldSchema, CollectionSchema, DataType, Collection import numpy as np connections.connect(aliasdefault, hostlocalhost, port19530) fields [ FieldSchema(nameid, dtypeDataType.INT64, is_primaryTrue), FieldSchema(nameembedding, dtypeDataType.FLOAT_VECTOR, dim128) ] schema CollectionSchema(fields) collection Collection(MyCollection, schema) emb np.random.rand(10, 128).astype(float32) ids list(range(10)) collection.insert([ids, emb]) index_params { index_type: IVF_FLAT, params: {nlist: 128}, metric_type: L2 } collection.create_index(embedding, index_params) collection.load() query_emb np.random.rand(1, 128).astype(float32) results collection.search(query_emb, embedding, param{nprobe: 10}, limit3) print(results)要点说明Milvus 的流程更偏数据库工程先connect到服务再定义字段 Schema 创建集合。插入后需要显式建索引IVF_FLAT、nlist控制聚类数量并load()到内存才能执行search。search中的nprobe是 IVF 检索时探测的聚类数越大召回越准、耗时越长metric_type支持L2、IP内积、COSINE。3.6 Chroma轻量嵌入式Apache 2.0import chromadb from chromadb.config import Settings client chromadb.Client(Settings( chroma_db_implduckdbparquet, persist_directory./chroma_data )) coll client.create_collection(my_collection) vectors [[0.1, 0.2, 0.3], [0.2, 0.2, 0.2]] metas [{doc: text1}, {doc: text2}] ids [id1, id2] coll.add(embeddingsvectors, metadatasmetas, idsids) res coll.query(query_embeddings[[0.15, 0.25, 0.3]], n_results2) print(res)要点说明Settings中的persist_directory指定持久化目录duckdbparquet为旧版默认存储引擎新版 Chroma 已调整了持久化配置方式使用时请对照所装版本的 API。add同时接收embeddings、metadatas、ids三组数据元数据随向量一并存储。不传embeddings时 Chroma 也可调用内置 embedding 函数自动向量化适合快速原型。3.7 Redis在 KV 存储上做 KNNimport redis import struct r redis.Redis(hostlocalhost, port6379) # Create index r.execute_command( FT.CREATE, my_idx, ON, HASH, SCHEMA, embedding, VECTOR, FLAT, 6, TYPE, FLOAT32, DIM, 128, DISTANCE_METRIC, L2 ) # Insert vec struct.pack(128f, *[0.1]*128) r.hset(doc1, mapping{embedding: vec}) # Search qvec struct.pack(128f, *[0.15]*128) q *[KNN 3 embedding $BLOB AS dist] res r.ft(my_idx).search(q, query_params{BLOB: qvec}) print(res.docs)要点说明依赖 Redis 的 RediSearch 模块FT.CREATE命令需确保 Redis 服务端已加载该模块。向量先通过struct.pack(128f, ...)打包为二进制BLOB存入 Hash 字段。检索语法*[KNN 3 embedding $BLOB AS dist]表示对embedding字段执行 KNN 检索、返回前 3 个结果并把距离别名设为dist$BLOB由query_params注入查询向量。四、把向量检索接入 PocketFlow仓库源码级实现理解了各方案的接入代码之后关键问题是如何把它变成 PocketFlow 流程中的一个环节。仓库中的 Chat with Memory 示例 提供了完整的参考实现其结构是向量索引工具层 Node 流程层两层设计。4.1 工具层对 FAISS 的统一封装utils/vector_index.py 把 FAISS 的琐碎细节封装为三个函数屏蔽了向量形状转换、ntotal计数等易错点import numpy as np import faiss def create_index(dimension1536): return faiss.IndexFlatL2(dimension) def add_vector(index, vector): # Make sure the vector is a numpy array with the right shape for FAISS vector np.array(vector).reshape(1, -1).astype(np.float32) # Add the vector to the index index.add(vector) # Return the position (index.ntotal is the total number of vectors in the index) return index.ntotal - 1 def search_vectors(index, query_vector, k1): Search for the k most similar vectors to the query vector k min(k, index.ntotal) if k 0: return [], [] query_vector np.array(query_vector).reshape(1, -1).astype(np.float32) distances, indices index.search(query_vector, k) return indices[0].tolist(), distances[0].tolist()这段封装有两点值得借鉴add_vector返回index.ntotal - 1即新向量在索引中的位置。由于 FAISS 索引本身不保存原始数据这个位置号需要与业务数据如对话记录一一对应由调用方维护一个平行的列表。search_vectors对k做了min(k, index.ntotal)的边界保护避免检索数量超过索引规模时报错k 0时直接返回空结果保证空索引下流程仍能正常运行。从源码结构看这种业务数据列表 FAISS 位置号的配对方式正是进程内向量库的标准用法向量库负责相似度计算业务数据留在应用侧二者通过下标关联。4.2 流程层EmbedNode 与 RetrieveNode在 nodes.py 中向量检索被拆成两个职责单一的 Node写入侧EmbedNode归档旧对话并写入索引def exec(self, conversation): # Combine user and assistant messages into a single text for embedding user_msg next((msg for msg in conversation if msg[role] user), {content: }) assistant_msg next((msg for msg in conversation if msg[role] assistant), {content: }) combined fUser: {user_msg[content]} Assistant: {assistant_msg[content]} # Generate embedding embedding get_embedding(combined) return {conversation: conversation, embedding: embedding} def post(self, shared, prep_res, exec_res): # Initialize vector index if not exist if vector_index not in shared: shared[vector_index] create_index() shared[vector_items] [] # Track items separately # Add the embedding to the index and store the conversation position add_vector(shared[vector_index], exec_res[embedding]) shared[vector_items].append(exec_res[conversation]) return question读取侧RetrieveNode按当前问题召回最相关的历史对话def exec(self, inputs): query inputs[query] vector_index inputs[vector_index] vector_items inputs[vector_items] # Create embedding for the query query_embedding get_embedding(query) # Search for the most similar conversation indices, distances search_vectors(vector_index, query_embedding, k1) if not indices: return None conversation vector_items[indices[0]] return {conversation: conversation, distance: distances[0]} def post(self, shared, prep_res, exec_res): if exec_res is not None: shared[retrieved_conversation] exec_res[conversation] print(f Retrieved conversation (distance: {exec_res[distance]:.4f})) else: shared[retrieved_conversation] None return answer值得注意的工程细节向量索引与业务数据都挂在shared字典上shared[vector_index]与shared[vector_items]这符合 PocketFlow 以shared为流程共享存储的设计约定检索失败索引为空或没有命中时返回None由post兜底写入shared[retrieved_conversation] None下游AnswerNode据此跳过上下文注入——检索环节的失败不会中断整个对话流程距离distance作为可解释性指标打印出来方便调试召回质量。4.3 流程编排flow.py 用 PocketFlow 的动作action语法把写入与召回串进对话循环question_node - retrieve retrieve_node retrieve_node - answer answer_node answer_node - embed embed_node answer_node - question question_node embed_node - question question_node return Flow(startquestion_node)这里展示了一种典型的记忆增强循环对话进行时最旧的对话对触发embed分支被向量化归档下一轮提问时retrieve分支在向量索引中找回语义最相关的历史对话注入上下文。整个循环不需要额外引入后台任务完全由 PocketFlow 的条件跳转Node 的post返回动作字符串如question、retrieve、embed、answer驱动。4.4 RAG 场景中的向量数据库接入若目标是文档问答而非对话记忆可参考 RAG 设计模式 与 pocketflow-rag 示例 的两阶段编排离线索引 FlowChunkDocumentsNode EmbedDocumentsNode CreateIndexNode把分块后的文档依次向量化并写入索引flow.py在线问答 FlowEmbedQueryNode RetrieveDocumentNode GenerateAnswerNode对问题向量化后从索引中检索最相关块拼接进 prompt 交给 LLMflow.py。其中向量写入与检索即可替换为本文第三部分任意一种方案的接入代码——这就是向量数据库选型与Agent 流程设计的解耦点PocketFlow 负责编排向量库只负责被调用。五、落地建议在 PocketFlow 中如何选与如何接综合文档对比表与仓库实践给出如下落地建议原型阶段优先 FAISS 或 Chroma进程内运行、零服务依赖与 vector_index.py 的封装模式一致几分钟即可跑通 RAG/记忆流程。需要过滤与元数据检索时选 Qdrant 或 Weaviatepayload/GraphQL 过滤能力让按条件缩小检索范围变得自然适合文档带标签、权限等业务属性的场景。超大向量规模百万级以上考虑 Milvus其分布式架构与显式索引管理面向生产级规模代价是需要维护独立集群。已在用 Redis 的团队可复用基础设施通过 RediSearch 模块在既有 Redis 上叠加向量能力但免费额度30MB较小适合轻量场景。全托管Pinecone/Milvus Cloud 等适合不想运维索引的团队把构建索引、扩容的负担交给服务商按量付费。无论选择哪一种建议保持工具层封装 Node 流程层调用的结构把向量库 API 收敛到独立的 utils 模块如vector_index.pyNode 只依赖封装后的create_index/add_vector/search_vectors三个语义稳定的函数这样未来切换向量数据库时只需替换工具层实现PocketFlow 的 Node 与 Flow 编排完全不受影响。六、小结本文从 PocketFlow 官方文档《Vector Databases》出发完成了三件事完整对比了七种主流向量检索方案的免费额度、定价模型与适用场景并给出了全部 Python 接入代码可直接复制运行结合仓库源码剖析了 vector_index.py 的 FAISS 封装模式、nodes.py 中写入/召回 Node 的实现细节以及 flow.py 中由动作驱动的检索循环给出了选型与落地的具体建议明确了向量库负责检索、PocketFlow 负责编排的解耦原则。向量数据库只是 RAG 链条中的一环与之配套的文本分块与 Embedding 接入可继续阅读 Chunking 指南 和 Embedding 指南完整的两阶段 RAG 编排见 RAG 设计模式。将本文的向量接入代码与这些模式组合即可在 PocketFlow 中搭建出可运行的语义检索 Agent。赞分享人工智能大模型AI Agent工作流自动化RAG【免费下载链接】PocketFlowPocket Flow: 100-line LLM framework. Let Agents build Agents!项目地址https://gitcode.com/gh_mirrors/poc/PocketFlow点击查看免费下载相关推荐Quivr数据库选型关系型vs向量数据库对比Quivr数据库选型关系型vs向量数据库对比 引言AI时代的数据存储挑战 在构建智能助手应用时传统的关系型数据库已无法满足现代AI应用对语义搜索和向量相似人工智能AI 应用大模型RAG后端前端QuickRecorder免费 macOS 录屏工具5 分钟录出干净屏幕视频QuickRecorder免费 macOS 录屏工具5 分钟录出干净屏幕视频 今天要录一段 30 分钟的线上会议又不想把重型软件装进电脑试试 Quick桌面应用音视频屏幕录制all-in-rag 实战向量数据库选型与 FAISS 本地向量存储实现指南all in rag 实战向量数据库选型与 FAISS 本地向量存储实现指南 导读 本文是 Datawhale《大模型应用开发实战一RAG 技术全栈指南》中教程人工智能大模型RAG创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表