尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

rig-sqlite 实战:在 Rust 中构建基于 SQLite 的向量存储与语义检索

rig-sqlite 实战:在 Rust 中构建基于 SQLite 的向量存储与语义检索 AI AgentAgent 框架RAG后端【免费下载链接】rig⚙️ Build modular and scalable LLM Applications in Rust项目地址https://gitcode.com/GitHub_Trending/rig2/rig点击查看免费下载rig-sqlite 是 rig 生态中基于 SQLite 的向量存储配套 crate它借助sqlite-vec扩展把文档嵌入向量直接落在 SQLite 数据库中让 RAG 应用的检索能力与事务型数据存储共用同一个文件。本文以 crates/rig-sqlite/CHANGELOG.md 记载的功能演进为主线结合 crates/rig-sqlite/src/lib.rs、crates/rig-sqlite/README.md 与测试代码完整讲解依赖接入、表结构设计、写入/检索、JSON 元数据过滤、距离度量选择以及 KNN 候选上限等关键机制读完即可在自有 Rust 项目中落地一套可运行的 SQLite 向量检索方案。一、定位与能力全景rig-sqlite 是 rig 官方提供的 companion crate职责是基于 SQLite 的 Rig 向量存储实现见 crates/rig-sqlite/Cargo.toml 的 description。它位于 rig-core 的vector_store抽象之下实现了VectorStoreIndex与InsertDocuments两个核心 trait因此可以无缝接入 rig 的 RAG 链路甚至直接作为 Agent 的 RAG 检索存储使用。从 CHANGELOG 可以看到这条能力演进的完整路径0.1.02024-12-03首个版本新增 SQLite 向量存储支持PR #122并修复了 Cargo.toml 中缺失版本号的问题0.1.162025-08-05新增余弦相似度向量检索PR #664使默认语义从欧氏距离走向余弦相似度0.1.252025-10-27支持VectorSearchRequest过滤器PR #9520.1.272025-11-10新增后端专属向量搜索过滤器PR #10320.1.312026-01-06修复 rig-sqlite 误用欧氏距离的缺陷PR #1217从此以余弦相似度为默认度量0.1.322026-01-20改进向量存储文档与过滤器 ergonomics破坏性变更PR #1258并让集成过滤器可作为 rig Agent 的 RAG store 使用PR #12490.2.32026-03-29为 sqlite 实现InsertDocumentstraitPR #14780.3.02026-06-02支持反序列化 JSON 列PR #1797、支持文档元数据过滤器PR #1793、修复 sqlite 阈值与 null 工具调用流式处理PR #1786并补充 JSON 元数据 ergonomics 文档PR #17980.39.02026-06-19当 KNN 候选数超过 sqlite-vec 的 k 上限时回退到暴力全表扫描PR #19200.40.02026-07-10恢复 doctest 运行rig-sqlite 不再跳过 doctestPR #19390.42.02026-08-16OneOrManyT全面退场改为VecTInsertDocuments::insert_documents签名变更为Vec(Doc, VecEmbedding)仅源码级破坏性变更序列化形式不变修复SqliteSearchFilter::default()的组合语义PR #2273。当前仓库中 rig-sqlite 与 rig-core 版本已统一为 0.42.0crates/rig-sqlite/Cargo.toml 使用 workspace 版本依赖rig-core 0.42.0。二、快速开始依赖接入与 sqlite-vec 扩展初始化1. 添加依赖在Cargo.toml中加入 rig-sqlite 与 rig-core[dependencies] rig-sqlite 0.42.0 rig-core 0.42.0 serde { version 1, features [derive] } serde_json 1也可以使用cargo add自动添加最新版本cargo add rig-sqlite rig-core serde_json cargo add serde --features deriverig-sqlite 的底层依赖见 crates/rig-sqlite/Cargo.toml包括rusqlite启用bundled特性免去系统 SQLite 依赖、tokio-rusqlite异步连接、sqlite-vec向量扩展与tracing。若使用根rigfacade crate只需开启sqlitefeature 即可通过rig::sqlite访问见 src/lib.rs 与根 Cargo.toml。2. 必须先注册 sqlite-vec 扩展在使用 SQLite 向量存储之前必须先向 SQLite 注册sqlite-vec扩展。在创建连接之前执行如下代码use rusqlite::ffi::sqlite3_auto_extension; use sqlite_vec::sqlite3_vec_init; unsafe { sqlite3_auto_extension(Some(std::mem::transmute(sqlite3_vec_init as *const ()))); }crates/rig-sqlite/examples/vector_search_sqlite.rs 中给出了更严谨的写法将扩展函数指针显式转成sqlite3_auto_extension期望的函数签名类型后再注册type SqliteExtensionFn unsafe extern C fn(*mut sqlite3, *mut *mut c_char, *const sqlite3_api_routines) - i32; unsafe { sqlite3_auto_extension(Some(std::mem::transmute::*const (), SqliteExtensionFn( sqlite3_vec_init as *const (), ))); }这一步是硬性前置条件遗漏它会导致建表或 KNN 查询时无法识别vec0虚拟表。3. 建立异步连接rig-sqlite 使用tokio_rusqlite::Connection提供异步接口let conn Connection::open(vector_store.db).await?;三、定义文档表SqliteVectorStoreTable 与 Columnrig-sqlite 要求文档类型实现SqliteVectorStoreTabletraitcrates/rig-sqlite/src/lib.rs该 trait 由四个方法组成name()文档表名schema()返回Column列表定义表结构id()文档主键column_values()把文档的每个字段转成(static str, Boxdyn ColumnValue)对。一个最小实现如下出自 crates/rig-sqlite/src/lib.rs 的文档示例use rig_core::Embed; use serde::{Deserialize, Serialize}; use rig_sqlite::{Column, ColumnValue, SqliteVectorStoreTable}; #[derive(Embed, Clone, Debug, Deserialize, Serialize)] struct Document { id: String, #[embed] content: String, } impl SqliteVectorStoreTable for Document { fn name() - static str { documents } fn schema() - VecColumn { vec![ Column::new(id, TEXT PRIMARY KEY), Column::new(content, TEXT), ] } fn id(self) - String { self.id.clone() } fn column_values(self) - Vec(static str, Boxdyn ColumnValue) { vec![ (id, Box::new(self.id.clone())), (content, Box::new(self.content.clone())), ] } }Column 与 ColumnValueColumn::new(name, col_type)声明一列。ColumnValue决定每种 Rust 类型如何映射到 SQLite 值rig-sqlite 内置了下列映射见 crates/rig-sqlite/src/lib.rs 的impl_column_value!宏Rust 类型声明的 SQLite 类型存储方式StringTEXT文本i64/i32INTEGER整数f64/f32FLOAT实数boolBOOLEAN0 / 1 整数serde_json::ValueJSONJSON 文本关键进阶Column::indexed()Column::indexed()crates/rig-sqlite/src/lib.rs把一列标记为可过滤列。它的双重作用体现在建表 SQL 中crates/rig-sqlite/src/lib.rs在文档表上创建普通索引idx_table_column把该列同步复制为 sqlite-vecvec0虚拟表的元数据列从而允许过滤器在 KNN 候选搜索阶段被下推执行。标记为indexed的列在vec0表中只能使用有限的元数据类型TEXT、INTEGER、FLOAT、BOOLEANcrates/rig-sqlite/src/lib.rs 的SqliteMetadataType::from_column_type会按 SQLite 类型亲和性做归一化。声明类型无法归入这四类如BLOB或NUMERIC的indexed列会在建库时报UnsupportedMetadataColumn错误。实践建议对于高频使用的标量过滤器如分类、优先级用Column::indexed()让过滤发生在候选搜索阶段对于低频或 JSON 内嵌字段走文档表后置过滤详见第五节。四、建库、写入与检索1. 创建向量存储SqliteVectorStore::new(conn, ndims)以余弦相似度创建存储with_distance_metric(conn, ndims, metric)可指定度量crates/rig-sqlite/src/lib.rslet vector_store: SqliteVectorStoreDocument SqliteVectorStore::with_distance_metric( conn, model.capabilities().ndims, SqliteDistanceMetric::Cosine, ) .await?;ndims必须与后续索引所用的嵌入模型宽度model.capabilities().ndims一致否则检索结果无意义。建库过程会自动创建三张表crates/rig-sqlite/src/lib.rstable_name文档表table_name_embeddingsUSING vec0(...)的向量虚拟表列定义为embedding float[ndims] distance_metriccosine度量写死在表定义里外加各 indexed 元数据列table_name_embedding_mapembedding_rowid→document_rowid的映射表支持一个文档拥有多条嵌入向量。建库时若发现已有vec0表的实际distance_metric与请求不一致会返回DistanceMetricMismatch错误缺少元数据列则返回MetadataSchemaMismatchcrates/rig-sqlite/src/lib.rs。2. 写入文档与嵌入生成嵌入后用InsertDocuments::insert_documents写入基于 0.42.0 的签名let embeddings EmbeddingsBuilder::new(model.clone()) .documents(documents)? .build() .await?; vector_store.insert_documents(embeddings).await?;在 0.42.0 中insert_documents接收Vec(Doc, VecEmbedding)这是OneOrManyT非空容器被仓库级移除后的源码级破坏性变更CHANGELOG 0.42.0 条目序列化后的 embedding 形式不变。insert_documents内部先把Doc序列化为 JSON 再反序列化为表类型T然后委托给add_rowscrates/rig-sqlite/src/lib.rs。add_rows在单个事务中批量插入add_rows_with_txn对重复 id 采用先删旧嵌入与映射、再插入新行的幂等策略crates/rig-sqlite/src/lib.rs实测用例live_reinsert_same_document_id_removes_stale_vec0_candidates验证了重复写入不会残留旧向量占用候选名额crates/rig-sqlite/src/tests.rs。3. 创建索引并检索let index vector_store.index(model); let req VectorSearchRequest::builder() .query(What is a linglingdong?) .samples(1) .build(); let results index.top_n::Document(req.clone()).await?; let id_results index.top_n_ids(req).await?;top_n::D返回Vec(f64, String, D)得分、文档 id、反序列化后的文档top_n_ids只返回Vec(f64, String)。完整可运行示例见 crates/rig-sqlite/examples/vector_search_sqlite.rs集成测试见 tests/integrations/sqlite.rs用 httpmock 模拟 OpenAI embedding 接口验证add_rows与带过滤的top_n。4. 检索的内部 SQL 形态search_rows生成的 SQL 以 CTE 为核心crates/rig-sqlite/src/lib.rsWITH scored AS ( SELECT m.document_rowid AS __rig_document_rowid, {score_expression} AS __rig_score, ROW_NUMBER() OVER ( PARTITION BY m.document_rowid ORDER BY {score_expression} DESC, e.rowid ASC ) AS __rig_rank FROM {table}_embeddings e JOIN {table}_embedding_map m ON e.rowid m.embedding_rowid {where_clause} ) SELECT {columns}, scored.__rig_score FROM scored JOIN {table} d ON scored.__rig_document_rowid d.rowid WHERE scored.__rig_rank 1 {document_filter_clause} ORDER BY scored.__rig_score DESC, d.id ASC LIMIT ?其中ROW_NUMBER() PARTITION BY document_rowid实现每个文档只保留得分最高的那条嵌入的多向量去重语义ORDER BY score DESC, d.id ASC保证同分结果按 id 稳定排序测试live_equal_score_results_are_ordered_by_document_id验证crates/rig-sqlite/src/tests.rs。五、过滤体系下推过滤与后置过滤1. 过滤器类型SqliteSearchFilter实现 rig-core 的SearchFiltertraitcrates/rig-sqlite/src/lib.rs并扩展了更丰富的算子方法语义说明eq/ne!相等 / 不等基于SearchFiltergt/gte/lt/lte大小比较gte/lte由not(gt/lt)推导and/or逻辑组合or会整体走文档表后置过滤not取反简单比较取反可下推为!、、between(range)闭区间非布尔 indexed 列可下推is_null/is_not_null空值判断后置过滤glob(pattern)/like(pattern)模式匹配后置过滤default()空过滤器恒真0.42.0 修复后的语义2. 两类执行路径渲染器render_split把过滤器拆成native与post两组crates/rig-sqlite/src/lib.rsnative下推针对 indexed 元数据列的简单比较、!、、、、和between直接拼进vec0KNN 查询的WHERE如WHERE e.embedding MATCH ? AND k ? AND (e.category ?)。测试indexed_filter_uses_vec0_metadata_constraintcrates/rig-sqlite/src/tests.rs精确断言了这条 SQLpost后置JSON 表达式、OR、空值判断、LIKE/GLOB以及所有未标记 indexed 的文档表列都在候选搜索完成后拼接到外层WHERE ... AND (...)。测试or_filter_uses_document_filter_to_preserve_boolean_semanticscrates/rig-sqlite/src/tests.rs验证OR不会被部分下推而是整体保真到文档表过滤。限制BOOLEAN 元数据列只支持/!范围比较与between会直接报错拒绝boolean_range_filter_is_rejectedcrates/rig-sqlite/src/tests.rs元数据列的类型与过滤值不匹配如对 INTEGER 列传浮点也会在渲染阶段报出带明确原因的FilterErrormismatched_metadata_filter_value_types_are_rejectedcrates/rig-sqlite/src/tests.rs。注入防护方面过滤器 key 必须是指针表达式普通标识符或带-/-的 JSON 表达式否则以不支持的文档过滤表达式拒绝unsupported_document_filter_expressions_are_rejectedcrates/rig-sqlite/src/tests.rs。3. 后置过滤的代价候选数必须穷举因为后置过滤发生在 KNN 候选搜索之后、随时可能淘汰候选rig-sqlite 会把候选数放大到穷举级别以保证samples数量正确crates/rig-sqlite/src/lib.rs 的candidate_limitsamples 0候选数为 0直接返回空存在后置过滤exhaustive候选数 max(embedding_count, samples)即全量扫描无后置过滤且某文档拥有多条嵌入候选数 min(samples (embedding_count - document_count), embedding_count)这个上界保证按文档去重后仍能凑齐 top-samples其余情况候选数 samples。这正是 CHANGELOG 0.3.0 中JSON 元数据过滤后置、但保持samples(1)正确性的实现基础。六、JSON 元数据存储与过滤1. 存储 JSON 列在schema()中用Column::new(metadata, JSON)声明 JSON 列字段类型使用serde_json::Value。rig-sqlite 把值以 JSON 文本写入并在向量检索返回文档时解析回结构化 JSONcrates/rig-sqlite/src/lib.rs 的sqlite_json_text_value。CHANGELOG 0.3.0 的反序列化 JSON 列修复正是这一往返能力的来源测试serde_json_value_column_value_round_trips_json_columncrates/rig-sqlite/src/tests.rs验证了serde_json::Value→ JSON 文本 → 结构化 JSON 的完整闭环live_json_column_structured_metadata_round_trips_in_top_ncrates/rig-sqlite/src/tests.rs进一步验证了 top_n 返回时能反序列化为结构化元数据。注意只有声明为JSON的列才做解析普通TEXT列中长得像 JSON 的文本仍按字符串返回text_column_json_looking_text_stays_stringcrates/rig-sqlite/src/tests.rs。2. 过滤 JSON 字段在过滤器 key 中使用 SQLite 的 JSON 提取运算符即可直接命中 JSON 列内的字段crates/rig-sqlite/README.md 的 Filtering JSON Metadata 一节use rig_core::vector_store::request::{SearchFilter, VectorSearchRequest}; use rig_sqlite::SqliteSearchFilter; let req VectorSearchRequest::builder() .query(release notes) .samples(5) .filter(SqliteSearchFilter::eq( metadata-$.source, serde_json::json!(docs), )) .build();两种运算符的差异必须分清-SQLite 把 JSON 值当作 SQL 标量文本、数字、布尔比较Rig 直接绑定右侧 JSON 值的标量形式-SQLite 保留 JSON 文本形态比较Rig 会把右侧值序列化为 JSON 文本如vvv带引号再绑定。测试json_metadata_arrow_expression_binds_rhs_as_json_textcrates/rig-sqlite/src/tests.rs断言了-右侧绑定为\vvv\chained_json_metadata_expression_uses_final_operator_for_param_modecrates/rig-sqlite/src/tests.rs验证了链式表达式metadata-$.nested-$.xxx以最后一个运算符决定参数模式。JSON 元数据过滤器作用于文档表因此必然走 post 后置过滤路径crates/rig-sqlite/src/tests.rs 的json_metadata_expression_uses_document_filter。如前所述这会触发穷举候选检索——对高频标量过滤场景更好的做法是把值复制进一个Column::indexed()的普通列让过滤下推到 KNN 阶段。七、距离度量Cosine、L2 与 L1SqliteDistanceMetriccrates/rig-sqlite/src/lib.rs提供三种度量默认是Cosine度量vec0 名称返回得分语义Cosinecosine1 - cosine_distance余弦相似度越大越相似默认值L2l2-vec_distance_l2(...)负欧氏距离越大越近L1l1-vec_distance_l1(...)负曼哈顿距离越大越近三种度量的得分都是越高越好阈值threshold统一按score threshold过滤。得分表达式由score_expression生成crates/rig-sqlite/src/lib.rs并同时用于候选搜索、阈值过滤、排序与返回得分保证整条链路度量一致。度量名还会写进vec0表定义建库时若与已有表不一致会直接报错。CHANGELOG 中与度量相关的两次关键变更0.1.16新增余弦相似度检索0.1.31修复误用欧氏距离的缺陷PR #1217此后默认语义统一为余弦相似度。测试threshold_filter_uses_computed_similarity_expression与l2_threshold_filter_uses_l2_score_expressioncrates/rig-sqlite/src/tests.rs分别断言 Cosine 与 L2 的阈值 SQLlive_l2_metric_is_consistentcrates/rig-sqlite/src/tests.rs验证 L2 排序与阈值的一致性。八、KNN k 上限与暴力扫描回退sqlite-vec的vec0KNN 查询对k有硬上限SQLITE_VEC_VEC0_K_MAX 4096代码常量SQLITE_VEC_MAX_Kcrates/rig-sqlite/src/lib.rs超出会报 k value in knn query too large。当计算出的候选数见第五节第 3 点超过 4096 时rig-sqlite 会丢弃MATCH/k约束改用标量距离函数vec_distance_*对全表逐行打分排序crates/rig-sqlite/src/lib.rs 的build_search_query。这是 CHANGELOG 0.39.0 的修复内容PR #1920代价只是失去 SIMD/分块快路径结果保持精确一致vec0KNN 本身就是精确暴力扫描。实现细节值得注意暴力路径上WHERE子句在无其他条件时整体省略避免裸WHERE语法错误参数绑定必须同步调整KNN 路径绑定[query_vec, query_vec, candidate_limit, ...过滤器参数]暴力路径只剩[query_vec, ...过滤器参数]注释明确警告只删 token 不删参数会导致下游过滤器参数静默错位crates/rig-sqlite/src/lib.rs。测试覆盖了边界情形candidate_limit_at_k_cap_still_uses_knn_path恰好在 4096 时仍走 KNNcrates/rig-sqlite/src/tests.rscandidate_limit_above_k_cap_falls_back_to_brute_force_scan超过 4096 时去掉 MATCH/k 且 WHERE 为空crates/rig-sqlite/src/tests.rsbrute_force_scan_keeps_filter_params_aligned暴力路径下阈值参数仍正确对齐crates/rig-sqlite/src/tests.rslive_multivector_search_beyond_knn_k_cap_succeeds单文档 4100 条嵌入触发暴力路径仍返回精确 top-ncrates/rig-sqlite/src/tests.rslive_post_filter_search_beyond_knn_k_cap_succeeds4096 条噪声之上非 indexed 过滤仍能通过穷举路径找回相似度最差的wanted文档crates/rig-sqlite/src/tests.rs。九、0.42.0 的两个值得关注的变更1. default() 过滤器现在可以正常组合此前SqliteSearchFilter::default()生成一个内部Raw { condition: 1 1 }节点只在顶层被特判一旦嵌套在or之下就会落到文档表渲染器抛出以公共 API 从未暴露过的概念命名的错误raw filters cannot be validated as document-table constraints导致SqliteSearchFilter::default().or(other)这种自然写法直接失败CHANGELOG 0.42.0 Fixed 条目。现在default()返回一个专用的无操作节点Noopcrates/rig-sqlite/src/lib.rs顶层贡献零约束被and/or/not组合进文档路径时渲染为恒真式1 1crates/rig-sqlite/src/lib.rs。回归测试default_filter_composes_under_or_as_a_tautologycrates/rig-sqlite/src/tests.rs断言组合结果为AND ((1 1) OR (d.category ?))。2. InsertDocuments 签名从 OneOrMany 改为 Vec仓库级移除OneOrManyT后PR #2273insert_documents从接收Vec(Doc, OneOrManyEmbedding)变为接收Vec(Doc, VecEmbedding)crates/rig-sqlite/src/lib.rs。CHANGELOG 明确指出这是仅源码级签名变更序列化后的嵌入形式不变升级时只需把单向量包装成单元素Vec即可。十、测试与质量保障rig-sqlite 的测试体系crates/rig-sqlite/src/tests.rs约 3000 行分为三类纯渲染单元测试不依赖真实 SQLite直接对生成的 WHERE 子句与参数做字符串断言过滤下推、取反、区间、JSON 运算符、类型校验、注入拒绝等live 集成测试内存 SQLite 注册 sqlite-vec 扩展验证真实检索的排序、阈值、多向量去重、重复写入、L2 一致性、4096 上限回退等仓库级集成测试tests/integrations/sqlite.rs 通过rig::sqlitefacade 与 httpmock 模拟的 OpenAI embedding 接口端到端验证add_rows与过滤检索。此外CHANGELOG 0.40.0 记录了恢复 doctest 的改动PR #1939——crates/rig-sqlite/src/lib.rs 中SqliteVectorStoreTable与SqliteVectorIndex的rust文档块都是可编译的 doctestCargo.toml中doctest true保证它们持续生效0.1.32 与 0.1.21 还记录了过滤器 ergonomics 文档与where子句 trait 约束的工程化改进PR #1258、PR #749。十一、版本选型与升级建议如果从旧版本升级需要重点核对三类变更0.42.0insert_documents签名从OneOrMany改为Vecdefault()过滤组合行为修复对使用default().or(...)拼接过滤器的代码是行为修复可直接受益0.3.0JSON 列反序列化、元数据过滤、阈值处理都是新增能力升级后可安全启用 JSON 元数据检索0.1.31距离度量语义变更——若依赖旧版欧氏距离行为升级后默认检索语义变为余弦相似度注意核对评分阈值。依赖方面crates/rig-sqlite/Cargo.toml 显示当前版本与 rig-core 同步为 0.42.0且 rig-core 以default-features false方式引入只开derive避免被强制拉入不必要的特性rusqlite 与 tokio-rusqlite 均启用bundled开箱即用无需系统库。Rust 工具链版本以仓库根 rust-toolchain.toml 为准。十二、小结围绕 crates/rig-sqlite/CHANGELOG.md 的演进记录可以看清 rig-sqlite 的成熟路径从 0.1.0 的能存能查到余弦相似度与过滤器支撑语义检索再到 JSON 元数据、InsertDocuments标准化、KNN 上限回退与default()组合修复最终收敛为 0.42.0 的稳定形态。使用时的核心决策点有三个用Column::indexed()把高频标量过滤下推到 KNN 阶段、用-/-精确控制 JSON 字段的比较语义、理解多向量去重与候选数放大机制对samples正确性的保障。掌握这些之后一个可持久化、可过滤、支持多向量文档的 SQLite 向量检索后端就可以直接嵌入你的 Rust 应用。赞分享AI AgentAgent 框架RAG后端【免费下载链接】rig⚙️ Build modular and scalable LLM Applications in Rust项目地址https://gitcode.com/GitHub_Trending/rig2/rig点击查看免费下载相关推荐基于 SQLite 的向量存储实战使用 rig-sqlite 在 Rust 中构建可检索的文档库基于 SQLite 的向量存储实战使用 rig sqlite 在 Rust 中构建可检索的文档库 本文围绕 crates/rig sqlite/README.AI 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后端上一篇AdaRank 实战指南基于模块分歧预测逐层秩实现更优的低秩微调LoRA下一篇Hugo Shortcode.Store 方法详解在短代码内持久化键值数据的完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表