- 后端
- 人工智能
- 机器学习
- RAG
- 向量数据库
【免费下载链接】postgresml
Postgres with GPUs for ML/AI apps.
2023 年 3 月,PostgresML 团队正式发布了官方 SDK 的 1.0 稳定版本,同时覆盖 Python 与 JavaScript 两种语言。本文以这篇发布公告为主线,结合当前仓库中 pgml-sdks 的 Rust 核心源码,完整讲解 Collection(文档集合)、Pipeline(处理流水线)、语义搜索 / 全文搜索 / 混合搜索、HuggingFace 模型接入、DEBUG 调试与 ER 图生成等核心能力,帮助读者掌握在纯 PostgreSQL 环境中从"建集合、写文档、跑流水线"到"向量检索"的完整落地路径。
1.0 版本:官方 SDK 的稳定与定型
在过去几个月里,PostgresML 团队持续对 JavaScript 和 Python 两个版本的 SDK 进行稳定化与功能收尾,最终在 2023 年 3 月 4 日发布 1.0 正式版。这一版本的发布公告正是本文关联的文档 the-1.0-sdk-is-here.md。
1.0 版本带来了大量性能改进与新特性,其核心能力可以概括为以下几点:
- Collection:创建用于存储、检索和管理成组文档的集合;
- Pipeline:定义强大且灵活的流水线,统一编排文档的摄入(ingest)、切分(splitting)、嵌入(embedding)与索引(indexing);
- 多模态搜索:支持语义搜索(semantic search)、全文搜索(full text search)以及二者的混合搜索(hybrid search),并可基于文档的额外元数据做丰富的过滤;
- 模型生态:几乎可以使用 HuggingFace 上所有主流 embedding 模型;
- 一切皆 SQL:Collection 在数据库中就是一张张真实的表,可以用 ER 图直观查看表结构,甚至绕过 SDK 直接用 SQL 查询。
SDK 本身是专为"搜索"这一任务而设计的。根据原公告,PostgresML 团队用它驱动自家官网的搜索功能,并在 ChatBot 演示中完成 RAG(检索增强生成)流程。从当前仓库的源码结构也可以印证这一技术路线:pgml-dashboard/src/utils/markdown.rs 承担了官网内容渲染与检索相关的逻辑。
为什么说这次发布值得兴奋
任何一个公司 SDK 的本质工作都是相同的:把管理 SQL 表、拼接复杂查询这些"无聊且重复"的工作抽象掉。单就 SDK 本身而言,它并不算开创性发明。但 PostgresML 1.0 SDK 让人兴奋的原因在于它底层所依赖的技术:
SDK 完全依托于开源的 PostgresML PostgreSQL 扩展,用 SQL 完成机器学习任务。闪电般快速的文档嵌入和魔术般的混合搜索,本质上都是调用 PostgresML 扩展的简单 SQL 查询。所有计算都在你的数据库本地完成,全程没有任何网络调用。
这句话值得展开理解。当你在 Python/JavaScript 中写下collection.upsert_documents(documents)时,SDK 并不是把数据发到某个 SaaS 平台,而是在你的 PostgreSQL 实例中执行 INSERT、调用pgml.embed()生成向量、写入text_chunks/text_embeddings等表,并借助 PgVector 扩展建立 HNSW 向量索引。这条技术路线与 pgml-sdks/pgml/python/README.md 中"构建端到端向量搜索应用,无需 OpenAI 与 Pinecone"的定位完全一致。
快速体验:从建集合到向量检索
下面这段代码是公告中的核心示例,完整展示了 1.0 SDK 的三个关键动作:创建 Collection 与 Pipeline、Upsert 文档、执行向量搜索。Python 与 JavaScript 两个版本的 API 几乎一一对应。
JavaScript 版本
// Create Collection and Pipeline const collection = pgml.newCollection("my_collection"); const pipeline = pgml.newPipeline("my_pipeline", { text: { splitter: { model: "recursive_character" }, semantic_search: { model: "Alibaba-NLP/gte-base-en-v1.5", }, }, }); await collection.add_pipeline(pipeline); // Upsert a document const documents = [ { id: "document_one", text: "Here is some hidden value 1000" } ]; await collection.upsert_documents(documents); // Search over our collection const results = await collection.vector_search( { query: { fields: { text: { query: "What is the hidden value?" }, }, }, limit: 5, }, pipeline, ); console.log(results);Python 版本
# Create Collection and Pipeline collection = Collection("my_collection") pipeline = Pipeline( "my_pipeline", { "text": { "splitter": {"model": "recursive_character"}, "semantic_search": { "model": "Alibaba-NLP/gte-base-en-v1.5", }, }, }, ) # Upsert a document documents = [{"id": "document_one", "text": "Here is some hidden value 1000"}] await collection.upsert_documents(documents) # Search over our collection results = await collection.vector_search( { "query": { "fields": { "text": {"query": "What is the hidden value?"}, }, }, "limit": 5, }, pipeline, ) print(results)这段示例展示了 1.0 SDK 的"声明式流水线"设计:你只需要告诉 Pipeline"对text字段先用recursive_character切分器切块,再用Alibaba-NLP/gte-base-en-v1.5模型生成语义向量",剩下的建表、建索引、切块、嵌入、检索全部由 SDK 自动完成。
从源码看 Collection 与 Pipeline 的实现
在 pgml-sdks/pgml/src/collection.rs 中,Collection::new会校验集合名只能包含字母、数字、空白、-与_,并据此生成<name>.pipelines与<name>.documents两张表名;集合本身在数据库中对应pgml.collections表,并与pgml.projects项目记录关联,创建时还会写入SDK_VERSION(当前源码中为"1.0.0",见 pgml-sdks/pgml/src/lib.rs)用于版本兼容性管理。
再看add_pipeline的调用链(pgml-sdks/pgml/src/collection.rs),其内部流程是:
- 若 Collection 尚不存在则先创建它;
- 在
pipelines表中以ACTIVE = TRUE写入 Pipeline 记录; - 立即对 Pipeline 执行一次resync——删除旧的 chunks、embeddings、tsvectors 并按新 schema 重建。
也就是说,"把 Pipeline 加进 Collection"这个动作本身就触发了对已有文档的首次索引构建,这与公告中"Pipeline 自动编排摄入、切分、嵌入、索引"的描述完全吻合。
Pipeline schema 的字段级配置
Pipeline 的 schema 按"文档字段"(field)维度组织,每个字段可以配置三类动作(见 pgml-sdks/pgml/src/pipeline.rs):
| 配置项 | 作用 | 关键子参数 |
|---|---|---|
splitter | 文本切分 | model(如recursive_character)、parameters(如chunk_size、chunk_overlap) |
semantic_search | 语义向量化 | model(HuggingFace 模型名)、parameters(如prompt)、source(本地/远程)、hnsw |
full_text_search | PostgreSQL 全文检索 | configuration(如english) |
其中semantic_search的hnsw参数可以自定义 HNSW 向量索引的构建参数,默认值为m = 16、ef_construction = 64(见 pgml-sdks/pgml/src/pipeline.rs)。在 pgml-sdks/pgml/src/lib.rs 的can_specify_custom_hnsw_parameters_for_pipelines测试中,可以看到传入"hnsw": {"m": 100, "ef_construction": 200}后,SDK 生成的索引定义正是CREATE INDEX ... USING hnsw (embedding vector_cosine_ops) WITH (m='100', ef_construction='200')——索引参数完全透传到 PostgreSQL。
切分器的默认模型是recursive_character(pgml-sdks/pgml/src/splitter.rs),未指定时自动使用;在 pgml-sdks/pgml/src/lib.rs 的测试can_add_pipeline_and_upsert_documents中,一个带chunk_size: 1000、chunk_overlap: 40参数的 Pipeline 会把 2 个文档的body字段切出 12 个 chunk,并同步生成 12 行 tsvector,验证了切分与全文索引的联动行为。
揭开魔术的面纱:vector_search 背后的 SQL
SDK 的封装看似"魔术",但公告特意强调:你看到的每次搜索,底层都是一段可审计的 SQL。上面vector_search调用实际生成的 SQL 如下:
WITH "pipeline" ( "schema" ) AS ( SELECT "schema" FROM "my_collection"."pipelines" WHERE "name" = 'my_pipeline' ), "text_embedding" ( "embedding" ) AS ( SELECT pgml.embed (transformer => ( SELECT SCHEMA #>> '{text,semantic_search,model}' FROM pipeline), text => 'What is the hidden value?', kwargs => '{}') AS "embedding" ) SELECT "document", "chunk", "score" FROM ( SELECT 1 - (embeddings.embedding <=> ( SELECT embedding FROM "text_embedding")::vector) AS score, "documents"."id", "chunks"."chunk", "documents"."document" FROM "my_collection_my_pipeline"."text_embeddings" AS "embeddings" INNER JOIN "my_collection_my_pipeline"."text_chunks" AS "chunks" ON "chunks"."id" = "embeddings"."chunk_id" INNER JOIN "my_collection"."documents" AS "documents" ON "documents"."id" = "chunks"."document_id" ORDER BY embeddings.embedding <=> ( SELECT embedding FROM "text_embedding")::vector ASC LIMIT 5) AS "s" ORDER BY "score" DESC LIMIT 5这段 SQL 有几点值得拆解:
- 查询文本的嵌入是在数据库内部完成的:
pgml.embed(...)直接从pipelines表的 JSONB schema 中取出{text,semantic_search,model}对应的模型名(Alibaba-NLP/gte-base-en-v1.5),对查询串在线生成向量,全程不离开数据库; - 相似度用余弦距离计算:
1 - (embedding <=> query_embedding)把 PgVector 的余弦距离换算成相似度分数score; - 三表 JOIN:
text_embeddings(向量)→text_chunks(文本块)→documents(原始文档),搜索结果自然包含命中的 chunk 文本与完整 document。
关于冗余 LIMIT 的说明:这段 SQL 是程序化生成的,设计目标是为了兼容"一个查询搜索多个字段"的场景。因此在单字段情况下你会看到一次多余的排序与 LIMIT。公告明确指出:它对这个查询的速度没有实质性影响。
这套"查询生成器"的实现位于 pgml-sdks/pgml/src/vector_search_query_builder.rs(向量搜索)与 pgml-sdks/pgml/src/search_query_builder.rs(混合搜索)。在 pgml-sdks/pgml/src/collection.rs 的vector_search方法中,SDK 会执行生成好的 SQL,并把结果组装为{document, chunk, score, rerank_score}四个字段的 JSON 数组返回。
三种搜索模式与过滤能力
公告列举了 1.0 SDK 的搜索能力:语义搜索、全文搜索、混合搜索,以及基于额外元数据的丰富过滤。对应到源码,Collection上同时提供search(可混合多字段多模式)与vector_search(纯向量召回)两条入口。
在 pgml-sdks/pgml/src/lib.rs 的测试can_search_with_local_embeddings中可以看到混合搜索的完整 query 形态:
{ "query": { "full_text_search": { "title": { "query": "test 9", "boost": 4.0 }, "body": { "query": "Test", "boost": 1.2 } }, "semantic_search": { "title": { "query": "This is a test", "parameters": { "prompt": "query: " }, "boost": 2.0 }, "body": { "query": "This is the body test", "parameters": { "prompt": "query: " }, "boost": 1.01 } }, "filter": { "id": { "$gt": 1 } } }, "limit": 5 }要点:
- 逐字段配置:
full_text_search与semantic_search可以作用于不同的字段,各自的 query 独立书写; - boost 加权:每个字段的查询可以设置
boost权重,用于调整混合打分中各路信号的贡献比例; - 元数据过滤:顶层
filter使用$gt、$eq等操作符对文档元数据做条件过滤,其底层实现是 pgml-sdks/pgml/src/filter_builder.rs 中的FilterBuilder,会把 JSON 过滤条件翻译成 SQL WHERE 子句; - 结果可追溯:混合搜索会在数据库中写入
searches与search_results表,记录每次搜索的 query 与排序后的结果列表,并可通过add_search_event(pgml-sdks/pgml/src/collection.rs)记录点击等行为事件,为后续的效果监控与调优留好数据。
向量搜索(vector_search)同样支持full_text_filter、boost、字段级parameters(如 query 侧的prompt前缀)以及document.keys(只返回文档的指定字段,测试见 pgml-sdks/pgml/src/lib.rs)。
打开调试日志,看见每一次 SQL
SDK 运行时的每一次数据库查询都是透明的。公告给出了开启 DEBUG 日志的方法,Python 与 JavaScript 写法一致:
pgml.init_logger("DEBUG");pgml.init_logger("DEBUG")在源码层面,init_logger的实现位于 pgml-sdks/pgml/src/lib.rs,其日志级别映射为TRACE/DEBUG/INFO/WARN/ERROR(未识别时默认ERROR),输出格式可选Pretty(默认)或Json。除代码传参外,还支持通过环境变量LOG_LEVEL与LOG_FORMAT控制,方便在容器或生产环境中直接配置,无需改动代码。
当你把日志级别开到 DEBUG 时,就能看到 SDK 实际执行的每一条 SQL——包括建表语句、切块/嵌入的同步语句、搜索的 CTE 查询等,这既是排查问题的利器,也是理解"一切皆 SQL"这一理念的最直观方式。
用 ER 图看清你的 Collection
"It's all SQL!" 意味着 Collection 的每一个组成部分在数据库里都是真实可查的表。公告展示了如何直接打印 Collection 与 Pipeline 的实体关系图:
console.log(await collection.generate_er_diagram(pipeline));print(await collection.generate_er_diagram(pipeline))上述代码会输出一段PlantUML 脚本,将其粘贴到 PlantUML 在线解释器中即可渲染出关系图。
在 pgml-sdks/pgml/src/collection.rs 的generate_er_diagram实现中,可以看到这张图实际上就是 SDK 在数据库里创建的表结构全景:
pgml.collections:集合主表(id、created_at、name、active、project_id、sdk_version);<collection>.documents:文档表(id、source_uuid、documentJSONB);<collection>.pipelines:流水线表(name、active、schemaJSONB);<collection>_<pipeline>.<field>_chunks:切分后的文本块表(document_id、chunk_index、chunk);<collection>_<pipeline>.<field>_embeddings:向量表(chunk_id、embeddingvector),仅在字段配置了semantic_search时生成;<collection>_<pipeline>.<field>_tsvectors:全文检索向量表(chunk_id、tsvector),仅在字段配置了full_text_search时生成。
图上的表间关系(documents → chunks → embeddings / tsvectors)与搜索 SQL 中的 JOIN 链一一对应。换句话说,ER 图不只是"查看工具",它直接反映了你在 SQL 层可以如何绕过 SDK 进行自定义查询——这正是公告强调"query from it however you want"的含义。
进阶:让 1.0 SDK 在生产中更好用
除了公告中直接演示的能力,当前仓库的源码还揭示了 1.0 SDK 面向生产使用的一系列细节:
1. 批量 Upsert 与并行
upsert_documents支持两个可选参数batch_size(默认 100)与parallel_batches(默认 1),SDK 会把文档分批并用 tokio 的JoinSet并行提交事务(见 pgml-sdks/pgml/src/collection.rs)。对应测试can_add_pipeline_and_upsert_documents_with_parallel_batches(pgml-sdks/pgml/src/lib.rs)以batch_size: 2、parallel_batches: 5并行写入 20 个文档并断言了 chunk 数量。批量导入大量文档时,合理调大这两个参数能显著提升吞吐。
2. 连接与环境变量
SDK 通过环境变量PGML_DATABASE_URL(或回退到DATABASE_URL)定位数据库连接串,并以全局连接池的方式复用连接;池的规模可通过PGML_POOL_MAX_CONNECTIONS(默认 10)、PGML_POOL_MIN_CONNECTIONS(默认 0)、PGML_POOL_ACQUIRE_TIMEOUT/PGML_CHECKOUT_TIMEOUT(默认 30000 毫秒)、PGML_POOL_MAX_LIFETIME、PGML_POOL_IDLE_TIMEOUT等变量调优(见 pgml-sdks/pgml/src/lib.rs)。在 pgml-sdks/pgml/python/README.md 的快速开始中,第一步就是设置DATABASE_URL指向 PostgresML 数据库。
3. 流水线生命周期管理
除add_pipeline之外,Collection 还提供remove_pipeline(删除流水线并级联删除其 schema)、enable_pipeline/disable_pipeline(激活/停用流水线,停用期间 upsert 的文档不会生成 chunk 与向量,重新启用时会自动补齐),以及get_pipeline_status查看每个字段的 chunks / embeddings / tsvectors 的同步进度(synced、not_synced、total)。对应的测试disable_enable_pipeline与pipeline_sync_status位于 pgml-sdks/pgml/src/lib.rs。
4. 版本迁移
SDK 版本间的变化并不保证完全向后兼容,因此提供migrate()函数(Python 中为from pgml import migrate; await migrate(),见 pgml-sdks/pgml/src/lib.rs 与 pgml-sdks/pgml/python/README.md 的 Upgrading 一节),它会把所有 Collection 迁移到与当前 SDK 兼容的结构,升级 SDK 后应执行一次。
5. 目录与文件摄入
upsert_directory支持按file_types(如['md', 'txt'])、file_batch_size(默认 10)、follow_links、ignore_paths(正则)批量摄入目录下的文件,upsert_file则可摄入单个文件(pgml-sdks/pgml/src/collection.rs),非常适合把本地知识库一键倒入 Collection,作为 RAG 应用的数据底座。
总结
PostgresML 1.0 SDK 的意义不在于"又一个客户端库",而在于它把搜索应用的全部复杂度——表管理、文本切分、向量化、索引构建、混合排序——收敛为"Collection + Pipeline"两个高层抽象,并且每一层都保持 SQL 级透明:
- 上手快:Python 与 JavaScript 两套 API 语义一致,声明式 Pipeline 让建索引变成几行配置;
- 能力全:语义搜索、全文搜索、混合搜索、元数据过滤、HNSW 参数透传、批量并行 upsert 一应俱全;
- 可审计:DEBUG 日志输出全部 SQL,ER 图展示真实表结构,搜索事件落库,为调优与监控留足数据;
- 本地化:嵌入与检索都在 PostgreSQL 内部完成,不依赖外部网络调用。
如果你正准备在自有 PostgreSQL 上搭建向量搜索或 RAG 应用,可以按 pgml-sdks/pgml/python/README.md 的快速开始跑通第一个例子,并用本文提到的init_logger("DEBUG")与generate_er_diagram深入观察 SDK 在数据库里做了什么——你会发现,所谓"魔术",不过是一套组织良好的 SQL。
- 后端
- 人工智能
- 机器学习
- RAG
- 向量数据库
【免费下载链接】postgresml
Postgres with GPUs for ML/AI apps.
相关推荐
pgvector-python: 在Python中拥抱PostgreSQL向量搜索
pgvector python: 在Python中拥抱PostgreSQL向量搜索 项目介绍 pgvector python 是一个Python库,旨在简化在P
使用 LanceDB JavaScript SDK 构建向量检索应用:安装、连接、建表与向量搜索实战
使用 LanceDB JavaScript SDK 构建向量检索应用:安装、连接、建表与向量搜索实战 本篇文章以 LanceDB 官方 JavaScript S
数据库向量数据库全文检索人工智能Python Deep Dive上下文管理器:资源安全管理与高级应用
Python Deep Dive上下文管理器:资源安全管理与高级应用 Python Deep Dive课程提供了全面的Python编程知识,其中上下文管理器是确
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考