AI AgentAgent 框架RAG后端【免费下载链接】rig⚙️ Build modular and scalable LLM Applications in Rust项目地址https://gitcode.com/GitHub_Trending/rig2/rig点击查看免费下载本文围绕 Rig 开源仓库Rust 的模块化 LLM 应用框架中的rig-qdrantcrate系统讲解如何让 Qdrant 向量数据库作为 Rig 的向量存储后端从依赖安装、端到端检索示例到QdrantVectorStore的底层实现原理再到基于QdrantFilter的元数据过滤查询。读完本文你将掌握在 Rig 应用中搭建文档入库 → 稠密向量检索 → 过滤查询完整链路的具体方法并理解其与 Rig 抽象层VectorStoreIndex/InsertDocuments/VectorSearchRequest之间的协作机制。一、rig-qdrant 是什么rig-qdrant是 Rig 框架的向量存储索引集成 crate为 Qdrant核心说明见 crates/rig-qdrant/README.md。该集成支持两条检索能力路径稠密向量检索直接使用 Rig 的 embedding provider如 OpenAI 的text-embedding-ada-002对查询文本做向量化然后在 Qdrant collection 上执行最近邻搜索可扩展的混合查询通过QdrantFilter类型的元数据过滤能力可以进一步窄化检索范围为将来对接 Qdrant 支持的各类混合查询hybrid queries预留了扩展空间。从源码结构看crates/rig-qdrant/src/lib.rs整个 crate 只有两个核心模块lib.rs定义QdrantVectorStore向量存储实现filter.rs定义QdrantFilter元数据过滤条件并通过pub use filter::QdrantFilter;从 crate 根导出。此外rig门面 crate 在启用qdrant特性后会将本 crate 重新导出为rig::qdrant见 Cargo.toml 中的qdrant [dep:rig-qdrant]。二、安装与依赖配置在Cargo.toml中添加依赖即可[dependencies] rig-qdrant 0.2.5 rig-core 0.36.0两种方式都可以使用方式一推荐单独引入rig-qdrantcrate它内部依赖rig-core当前工作区版本为 0.42.0见 crates/rig-qdrant/Cargo.toml方式二门面方式如果项目已经在使用根rig门面 crate只需启用qdrant特性即可通过rig::qdrant路径访问QdrantVectorStore和QdrantFilter。关于rig-qdrant的底层依赖从 crates/rig-qdrant/Cargo.toml 可以看到它基于rig-core提供Embed、embeddings::Embedding、vector_store抽象层qdrant-client工作区版本 1.10见 Cargo.toml官方 Qdrant gRPC/REST 客户端serde/serde_json文档序列化与过滤条件树的表示uuid启用v4特性为插入的每个点生成 UUID 作为 PointId。三、快速上手一个完整的向量检索示例仓库自带的端到端示例位于 crates/rig-qdrant/examples/qdrant_vector_search.rs。它完整演示了创建 collection → 生成 embedding → 写入文档 → 相似度检索 → 过滤检索的完整流程可以直接作为 RAG 应用的最小原型。3.1 运行前提根据示例文件头部的注释运行前需要export OPENAI_API_KEYYOUR-API-KEY docker run -p 6333:6333 -p 6334:6334 qdrant/qdrant cargo run --release --example qdrant_vector_searchQdrant 官方 Docker 镜像同时暴露 6333HTTP/REST与 6334gRPC端口示例使用 gRPC 端口 6334 建立连接运行后可打开 Qdrant 的 Web Dashboard 查看数据http://localhost:6333/dashboard示例默认依赖rig-core/derive特性用于#[derive(Embed)]这一点在 crates/rig-qdrant/Cargo.toml 的[[example]]段中有声明required-features [rig-core/derive]。3.2 定义可嵌入文档结构#[derive(Embed, serde::Deserialize, serde::Serialize, Debug)] struct Word { id: String, #[embed] definition: String, }关键点通过#[derive(Embed)]让结构体具备嵌入能力来自rig-core#[embed]属性标记的字段definition才是真正参与向量化的文本id字段仅作为元数据Payload随点一起存入 Qdrant该结构体同时实现了Serialize/Deserialize因为文档既要被序列化为 Qdrant Payload又要在检索结果中反序列化回Word。3.3 创建 Qdrant 客户端与 collectionlet client Qdrant::from_url(http://localhost:6334).build()?; const COLLECTION_NAME: str rig-collection; if !client.collection_exists(COLLECTION_NAME).await? { client .create_collection( CreateCollectionBuilder::new(COLLECTION_NAME) .vectors_config(VectorParamsBuilder::new(1536, Distance::Cosine)), ) .await?; }两个容易踩坑的点示例注释已明确提示维度必须匹配VectorParamsBuilder::new(1536, ...)中的 1536 是 OpenAItext-embedding-ada-002输出的向量维度。如果换成其他 embedding 模型必须同步修改此处的维度否则写入和检索都会失败距离度量选择示例使用Distance::Cosine余弦相似度。rig-core的相似度检索依赖一致的度量语义实际项目中应根据 embedding 模型的性质归一化与否选择 Cosine / Dot / Euclid。3.4 生成 embedding 并写入文档let openai_client OpenAI::from_env()?; let model openai_client .embedding(openai::TEXT_EMBEDDING_ADA_002, None) .erase(); let documents EmbeddingsBuilder::new(model.clone()) .document(Word { id: 0981d983-a5f8-49eb-89ea-f7d3b2196d2e.to_string(), definition: Definition of a *flurbo*: A flurbo is a green alien that lives on cold planets.to_string(), })? .document(Word { /* ... 第二个文档 ... */ })? .document(Word { /* ... 第三个文档linglingdong ... */ })? .build() .await?; let query_params QueryPointsBuilder::new(COLLECTION_NAME).with_payload(true); let vector_store QdrantVectorStore::new(client, model, query_params.build()); vector_store .insert_documents(documents) .await .map_err(|err| anyhow!(Couldnt insert documents: {err}))?;这段代码做了三件事用OpenAI::from_env()读取OPENAI_API_KEY创建 OpenAI 客户端并选择text-embedding-ada-002作为 embedding 模型通过.erase()擦除具体类型以统一接口用EmbeddingsBuilder为每份文档批量生成 embedding注意一份文档可以对应多个 embeddingrig-core的InsertDocuments接口接收Vec(Doc, VecEmbedding)构造QueryPointsBuilder携带 collection 名并开启with_payload(true)让检索结果回传文档 Payload将其作为QdrantVectorStore::new的查询参数模板传入最后调用insert_documents入库。3.5 执行检索与过滤检索let query What is a linglingdong?; let req VectorSearchRequest::builder() .query(query) .samples(1) .build(); let results vector_store.top_n::Word(req).await?; println!(Results: {results:?}); let filtered_req VectorSearchRequest::QdrantFilter::builder() .query(query) .samples(1) .filter(QdrantFilter::eq( id, serde_json::json!(f9e17d59-32e5-440c-be02-b2759a654824), )) .build(); let filtered_results vector_store.top_n::Word(filtered_req).await?; println!(Filtered results: {filtered_results:?});第一次查询不做任何过滤直接按相似度取 top-1第二次查询通过QdrantFilter::eq(id, ...)将结果限定为指定id的文档验证过滤条件下仍能返回正确结果top_n::Word会把 Qdrant 返回的 Payload 反序列化成Word最终返回Vec(score, id, Word)三元组。四、深入 QdrantVectorStore 的实现原理QdrantVectorStore是本节的核心类型定义在 crates/rig-qdrant/src/lib.rs。它的结构体只有三个字段pub struct QdrantVectorStore { model: rig_core::DynModelrig_core::operation::Embedding, client: Qdrant, query_params: QueryPoints, }4.1 构造函数与查询参数模板pub fn new( client: Qdrant, model: impl Intorig_core::DynModelrig_core::operation::Embedding, query_params: QueryPoints, ) - Self从源码注释可以明确其设计意图每次搜索都会克隆query_params并覆盖其中的 query、limit、threshold 与 filter。也就是说QueryPointsBuilder中预设的 collection 名、with_payload开关等参数会成为每次检索的固定模板而每次top_n调用只需临时替换查询向量、返回条数、阈值和过滤条件即可。这也解释了为什么QdrantVectorStore::new需要同时接收 client、model 和 query_params 三个要素。另外QdrantVectorStore的文档注释强调了一个使用前提查询必须使用与写入 collection 时相同的 embedding 模型否则检索结果毫无意义。这正是model字段被固化在 store 内部的原因。4.2 查询向量生成与参数装配async fn generate_query_vector(self, query: str) - ResultVecf32, VectorStoreError { let embedding self.model.embed_text(query).await?; Ok(embedding.vec.iter().map(|x| x as f32).collect()) } fn prepare_query_params( self, query: OptionQuery, limit: usize, threshold: Optionf64, filter: OptionFilter, ) - QueryPoints { let mut params self.query_params.clone(); params.query query; params.limit Some(limit as u64); params.score_threshold threshold.map(|x| x as f32); params.filter filter; params }实现细节值得注意generate_query_vector用 store 内置模型对查询文本做嵌入并将每个分量收窄为f32这是 Qdrant 向量类型的硬性要求prepare_query_params把VectorSearchRequest中的samples转成u64的limit、thresholdOptionf64转成Optionf32的score_threshold、过滤条件装配进克隆的模板run_query中有一个优先级规则如果query_params模板中已预设了 query则优先使用预设查询否则才把请求文本嵌入后包装成Query::new_nearest。这为固定向量重复检索等高级用法留了口子。4.3 写入InsertDocuments 的实现InsertDocumentstrait 的实现crates/rig-core/src/vector_store/mod.rs展示了文档是如何变成 Qdrant 点的每个文档先serde_json::to_value序列化为 JSON再转换成 Qdrant 的Payload文档对应的每个 embedding 都生成一个独立的PointStruct点 ID 由Uuid::new_v4()随机生成向量分量同样收窄为f32使用UpsertPointsBuilder::new(collection_name, ...).wait(true)执行写入wait(true)表示等待 Qdrant 确认索引更新完成后再返回避免后续检索读不到刚写入的数据所有 Qdrant 调用错误通过VectorStoreError::datastore统一包装wasm 与非 wasm 目标下的 trait bound 差异在该错误类型内部做了处理。4.4 读取VectorStoreIndex 的两个查询入口VectorStoreIndextrait 提供两个方法均在 crates/rig-core/src/vector_store/mod.rs 中定义top_nT返回Vec(f64, String, T)即(score, id, 反序列化后的文档)。内部流程是run_query→ 逐点取出PointId通过stringify_id支持Num和Uuid两种形式点无 ID 时报MissingIdError→ 把 score 升为f64→ 把 Payload 反序列化为Ttop_n_ids返回Vec(f64, String)只取(score, id)跳过 Payload 反序列化适合只需要文档 ID 的场景如去重、二次拉取。两个方法共享run_query即都走嵌入查询文本 → 装配参数 →client.query(params)这条路径。五、QdrantFilter元数据过滤条件5.1 设计定位QdrantFilter定义在 crates/rig-qdrant/src/filter.rs本质是一个包装serde_json::Value的新类型#[derive(Debug, Clone, Serialize, Deserialize)] pub struct QdrantFilter(serde_json::Value);它实现了rig-core的SearchFiltertraitcrates/rig-core/src/vector_store/request.rs该 trait 采用 tagless final 编码为所有向量库后端提供统一的过滤构造语义eq、gt、lt、and、or。这意味着同样的过滤表达式写法可以跨后端复用而类型系统会自动推断出具体后端的过滤类型。QdrantFilter内部以JSON 条件树的形式保存过滤条件最终由QdrantFilter::interpret翻译成 Qdrant 原生Filter。5.2 基础构造器SearchFilter trait 要求SearchFiltertrait 要求实现五个方法QdrantFilter对应的 JSON 表示为方法语义生成的 JSON 条件树eq(key, value)字段精确匹配{key: key, match: {value: value}}gt(key, value)字段值大于{key: key, range: {gt: value}}lt(key, value)字段值小于{key: key, range: {lt: value}}and(rhs)逻辑与{must: [self, rhs]}or(rhs)逻辑或{should: [self, rhs]}例如 README 中的过滤写法use rig_core::vector_store::request::{SearchFilter, VectorSearchRequest}; use rig_qdrant::QdrantFilter; let req VectorSearchRequest::QdrantFilter::builder() .query(What is a linglingdong?) .samples(1) .filter(QdrantFilter::eq( id, serde_json::json!(f9e17d59-32e5-440c-be02-b2759a654824), )) .build();这里QdrantFilter::eq需要显式指定VectorSearchRequest的泛型参数QdrantFilter让 builder 的filter()方法接收 Qdrant 专属的过滤类型。5.3 扩展构造器QdrantFilter 专属除了 trait 要求的基础方法QdrantFilter还提供了大量专有构造器覆盖 Qdrant 过滤的大部分能力not()取反生成{must_not: [self]}exists(key)字段存在生成{key: key, is_null: {value: false}}is_null(key)字段为 null生成{key: key, is_null: {value: true}}is_empty(key)字段为空数组/字符串生成{is_empty: {key: key}}四种区间过滤闭区间端点语义各不相同range_exclusive(key, lo, hi)lo value higtltrange_lower_inclusive(key, lo, hi)lo value higtlterange_higher_inclusive(key, lo, hi)lo value higteltrange_inclusive(key, lo, hi)lo value higtelte。这些构造器的组合能力加上and/or/not的嵌套足以表达大部分业务过滤条件。5.4 interpret条件树到 Qdrant Filter 的翻译QdrantFilter::interpretcrates/rig-qdrant/src/filter.rs是连接 JSON 条件树与 Qdrant 原生过滤器的桥梁其转换规则可以总结为空过滤短路条件树为null或空对象{}时返回Ok(None)即不附加任何过滤is_empty→ Qdrant 的IsEmptyConditionis_null→is_null: true时翻译为IsNullConditionis_null: false时翻译为字段非空must_not: [IsEmptyCondition]组合节点含must/must_not/should键→ 递归解释为嵌套FilterConditionOneOf::Filter字段节点含key键→ 解析match精确匹配与range区间两部分生成FieldConditionmatch 值的类型约束字符串 →MatchValue::Keyword布尔 →MatchValue::Boolean整数 →MatchValue::Integer浮点数不被 match 接受会返回FilterError::Expected { expected: Integer, ... }其他类型返回FilterError::TypeErrorrange 边界gt/gte/lt/lte全部按f64比较as_f64。与interpret配套的错误类型是rig-core定义的FilterErrorcrates/rig-core/src/vector_store/request.rs包含Expected、TypeError、MissingField、Must、Serialization等变体当条件树无法翻译为合法 Qdrant 过滤器时给出可诊断的错误信息。六、VectorSearchRequest统一的检索请求载体无论是否过滤所有检索都通过VectorSearchRequest描述。它的完整字段定义在 crates/rig-core/src/vector_store/request.rs字段类型说明queryString待嵌入并用于相似度检索的查询文本必填samplesu64最大返回条数必填thresholdOptionf64最小相似度阈值低于该值的结果被丢弃可选additional_paramsOptionserde_json::Value后端专属参数原样透传可选filterOptionF过滤表达式泛型F决定过滤后端可选使用 builder 构造crates/rig-core/src/vector_store/request.rs时注意两点query和samples是必填项builder 通过类型状态Missing/Provided在编译期强制约束漏填则无法调用build()threshold会被QdrantVectorStore::prepare_query_params映射为 Qdrant 的score_thresholdf64→f32作为相似度下限直接传给 Qdrant 服务端过滤。七、测试与可用性验证仓库为rig-qdrant提供了单元级验证测试文件 crates/rig-qdrant/tests/filter_export.rs 验证了QdrantFilter可以从 crate 根导出并且能作为VectorSearchRequestQdrantFilter的泛型过滤类型正常使用#[test] fn qdrant_filter_is_available_from_crate_root() { let request VectorSearchRequest::QdrantFilter::builder() .query(search text) .samples(3) .filter(QdrantFilter::eq(document_id, serde_json::json!(doc-1))) .build(); assert!(request.filter().is_some()); }该测试同时印证了两点过滤条件会被正常携带进请求对象filter().is_some()以及rig-qdrant的过滤类型可以直接充当 Rig Agent RAG 场景中的检索过滤条件这一点在 crates/rig-qdrant/CHANGELOG.md 中也有记录。此外从 crates/rig-qdrant/Cargo.toml 的 dev-dependencies 可以看出项目使用testcontainers拉起真实 Qdrant 容器与httpmock模拟 provider API组合进行集成测试说明该集成在真实 Qdrant 环境下有验证支撑。八、注意事项与最佳实践维度必须与 embedding 模型一致创建 collection 时VectorParamsBuilder::new(1536, Distance::Cosine)的维度必须等于所用模型输出维度这是最常见的配置错误查询与写入必须共用同一 embedding 模型QdrantVectorStore将模型固化在内部换模型等于换向量空间检索结果将失去意义浮点数不能用于match精确匹配QdrantFilter::eq的 match 值只接受字符串、布尔和整数需要数值条件时改用gt/lt/range_*系列写入时务必wait(true)示例中的UpsertPointsBuilder显式启用wait(true)确保索引就绪后再检索避免刚写入就查不到的时序问题点 ID 由集成自动生成insert_documents为每个 embedding 随机生成 UUID 点 ID文档自身字段如id只是 Payload 的一部分因此按业务 ID 过滤要用元数据条件如QdrantFilter::eq(id, ...)而不是点 ID过滤条件树可以嵌套组合and/or/not/range_*可以任意组合构成从简单等值到复杂区间的多条件检索如需自定义查询预设QdrantVectorStore::new的query_params模板中预设的 query 会优先于请求文本可据此实现固定向量检索等高级用法。九、总结rig-qdrant以极小的代码面两个源文件完整实现了 Rig 向量存储抽象QdrantVectorStore承担文档入库 稠密检索的职责QdrantFilter负责把后端无关的SearchFilter语义翻译成 Qdrant 原生过滤条件。二者通过VectorSearchRequestcrates/rig-core/src/vector_store/request.rs与VectorStoreIndex/InsertDocumentstraitcrates/rig-core/src/vector_store/mod.rs实现松耦合协作使 Rig Agent 可以像使用内存向量库一样使用 Qdrant同时保留 Qdrant 的过滤与混合查询扩展空间。如果要开始动手实践直接运行 crates/rig-qdrant/examples/qdrant_vector_search.rs 是最快的路径在此基础上把其中的Word结构体换成你自己的业务文档、把 embedding 模型与 collection 维度对齐即可快速搭建出基于 Qdrant 的 RAG 检索服务。赞分享AI AgentAgent 框架RAG后端【免费下载链接】rig⚙️ Build modular and scalable LLM Applications in Rust项目地址https://gitcode.com/GitHub_Trending/rig2/rig点击查看免费下载相关推荐用 Rig 在 Rust 中集成 Milvus 向量数据库RAG 检索实战指南用 Rig 在 Rust 中集成 Milvus 向量数据库RAG 检索实战指南 rig milvus 是 Rig 框架官方提供的 Milvus 向量库集成 cAI AgentAgent 框架RAG后端用 Rig 在 Rust 中构建基于 MongoDB Atlas Vector Search 的向量存储rig-mongodb 实战指南用 Rig 在 Rust 中构建基于 MongoDB Atlas Vector Search 的向量存储rig mongodb 实战指南 MongoDB AtAI AgentAgent 框架RAG后端rig pgvector在 Rust 中构建基于 PostgreSQL 的向量检索实战指南rig pgvector在 Rust 中构建基于 PostgreSQL 的向量检索实战指南 本篇指南围绕 Rig 仓库中的配套 crate rig posAI AgentAgent 框架RAG后端上一篇ascend-transformer-boost 融合算子 GatherPreRmsNorm 源码导读Gather、残差相加与 RMSNorm 的一体化推理实现下一篇django-allauth 账户适配器AccountAdapter完全指南定制 DefaultAccountAdapter 的钩子方法与实战创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?