pgai × OpenAI:在自托管 PostgreSQL 中快速构建语义搜索与 RAG 嵌入流水线 📅 发布时间:2026/9/17 3:06:41 👁 浏览次数: pgai × OpenAI在自托管 PostgreSQL 中快速构建语义搜索与 RAG 嵌入流水线【免费下载链接】pgaiA suite of tools to develop RAG, semantic search, and other AI applications more easily with PostgreSQL项目地址: https://gitcode.com/GitHub_Trending/pg/pgai本文基于 pgai 仓库中的 OpenAI 向量器快速上手指南docs/vectorizer/quick-start-openai.md完整演示在自托管 Postgres 实例中从零搭建 pgai 向量器vectorizer的全过程用 Docker Compose 拉起数据库与向量器 worker、用一条 SQL 创建基于 OpenAI 的向量器、自动生成本地嵌入表并在一条查询中完成语义搜索。读完本文你将掌握 pgai 向量器的标准接入流程并能读懂ai.create_vectorizer、ai.embedding_openai背后的配置结构与 worker 的批量嵌入实现细节。一、搭建本地开发环境pgai 的本地开发环境是一个 Docker 配置包含两个关键组件一个预装 TimescaleDB 与 pgai 扩展的 Postgres 部署镜像一个 pgai 向量器 worker 镜像timescale/pgai-vectorizer-worker:latest负责真正调用 OpenAI API 生成嵌入。1. 创建 Docker 配置新建一个目录将以下配置保存为compose.yamlyour-api-key替换为你的 OpenAI API Keyname: pgai services: db: image: timescale/timescaledb-ha:pg16 environment: POSTGRES_PASSWORD: postgres OPENAI_API_KEY: your-api-key ports: - 5432:5432 volumes: - data:/home/postgres/pgdata/data vectorizer-worker: image: timescale/pgai-vectorizer-worker:latest environment: PGAI_VECTORIZER_WORKER_DB_URL: postgres://postgres:postgresdb:5432/postgres OPENAI_API_KEY: your-api-key volumes: data:几个值得注意的配置点db服务基于timescale/timescaledb-ha:pg16镜像自带 TimescaleDB 与 pgai 所需扩展因此安装 pgai 时可以直接CREATE EXTENSION。pgai 安装代码要求 PostgreSQL 15 及以上版本见projects/pgai/pgai/_install/install.py中的版本检查逻辑指南选用 pg16 满足该前提OPENAI_API_KEY同时注入db和vectorizer-worker两个服务worker 在进程环境中读取密钥发起 OpenAI 请求数据库侧则可在 SQL 中调用ai.openai_embed等函数时通过密钥机制使用PGAI_VECTORIZER_WORKER_DB_URL告诉 worker 要连接并处理哪个数据库。2. 启动数据库并安装 pgaidocker compose up -d db然后执行安装命令它会复用vectorizer-worker镜像用python -m pgai install把 pgai 库安装进数据库docker compose run --rm --entrypoint python -m pgai install -d postgres://postgres:postgresdb:5432/postgres vectorizer-worker从安装源码projects/pgai/pgai/_install/install.py的install()函数可以看到这一步实际做了四件事检查server_version_num低于 15 直接抛错CREATE EXTENSION IF NOT EXISTS vectorpgvector并确定 vector 扩展所在的 schema加载打包好的projects/pgai/pgai/data/ai.sql将其中extschema:vector占位符替换为实际 schema 后执行创建ai模式下的全部向量器函数、表和视图若数据库中已装有旧版ai扩展低于 0.10.0会提示需要升级。二、创建并运行向量器连接数据库有两条路Docker 内docker compose exec -it db psql本地 psqlpsql postgres://postgres:postgreslocalhost:5432/postgres1. 启用 ai 扩展CREATE EXTENSION IF NOT EXISTS ai CASCADE;2. 创建源表blogCREATE TABLE blog ( id SERIAL PRIMARY KEY, title TEXT, authors TEXT, contents TEXT, metadata JSONB );注意blog必须有主键——ai.create_vectorizer会读取源表主键定义用于队列表的行对齐没有主键约束会直接报错见projects/pgai/db/sql/idempotent/012-vectorizer-api.sql中source table must have a primary key constraint的校验。3. 插入示例数据INSERT INTO blog (title, authors, contents, metadata) VALUES (Getting Started with PostgreSQL, John Doe, PostgreSQL is a powerful, open source object-relational database system..., {tags: [database, postgresql, beginner], read_time: 5, published_date: 2024-03-15}), (10 Tips for Effective Blogging, Jane Smith, Mike Johnson, Blogging can be a great way to share your thoughts and expertise..., {tags: [blogging, writing, tips], read_time: 8, published_date: 2024-03-20}), (The Future of Artificial Intelligence, Dr. Alan Turing, As we look towards the future, artificial intelligence continues to evolve..., {tags: [AI, technology, future], read_time: 12, published_date: 2024-04-01}), (Healthy Eating Habits for Busy Professionals, Samantha Lee, Maintaining a healthy diet can be challenging for busy professionals..., {tags: [health, nutrition, lifestyle], read_time: 6, published_date: 2024-04-05}), (Introduction to Cloud Computing, Chris Anderson, Cloud computing has revolutionized the way businesses operate..., {tags: [cloud, technology, business], read_time: 10, published_date: 2024-04-10});4. 创建向量器SELECT ai.create_vectorizer( blog::regclass, loading ai.loading_column(contents), embedding ai.embedding_openai(text-embedding-3-small, 768), destination ai.destination_table(blog_contents_embeddings) );对照projects/pgai/db/sql/idempotent/012-vectorizer-api.sql中ai.create_vectorizer的完整签名这条语句显式给了 3 个参数其余全部走默认值理解默认值有助于按需定制参数指南中的取值默认值作用sourceblog::regclass必填源表loadingai.loading_column(contents)null必填从哪个列读取文本parsing缺省ai.parsing_auto()文本解析二进制/富文本等embeddingai.embedding_openai(text-embedding-3-small, 768)null必填嵌入实现与模型chunking缺省ai.chunking_recursive_character_text_splitter()长文本切块策略indexing缺省ai.indexing_default()是否在目标表自动建向量索引formatting缺省ai.formatting_python_template()源列拼接到 chunk 的模板scheduling缺省ai.scheduling_default()定时调度依赖 TimescaleDB 调度器时生效processing缺省ai.processing_default()处理参数enqueue_existing缺省true把源表已有行加入处理队列——指南里先插数据、后建向量器5 条数据正是靠它被全量处理的创建时函数还会完成一系列实质工作同样在012-vectorizer-api.sql中实现校验embedding/loading非空且dimensions存在、创建目标嵌入表本例为blog_contents_embeddings、创建队列表ai._vectorizer_q_id与失败队列表、在blog上挂触发器_vectorizer_src_trg_id把后续 INSERT/UPDATE/DELETE 同步进队列最后把完整配置写入ai.vectorizer元数据表。其中ai.embedding_openai(text-embedding-3-small, 768)并不是直接发起请求而是在projects/pgai/db/sql/idempotent/004-embedding.sql中定义的配置构造器它返回一个 jsonb 配置对象-- 简化自 projects/pgai/db/sql/idempotent/004-embedding.sql (L4-L23) create or replace function ai.embedding_openai ( model pg_catalog.text , dimensions pg_catalog.int4 , chat_user pg_catalog.text default null , api_key_name pg_catalog.text default OPENAI_API_KEY , base_url text default null ) returns pg_catalog.jsonb; -- 返回 {implementation:openai,config_type:embedding,model:...,dimensions:...,api_key_name:OPENAI_API_KEY}关键参数api_key_name默认就是OPENAI_API_KEY即 worker 与 SQL 侧ai.openai_embed都会优先找这个环境变量/密钥名base_url可用于指向兼容 OpenAI 协议的网关。该配置随后会被ai._validate_embedding校验仅接受openai、ollama、voyageai、litellm四种实现。5. 启动向量器 worker 并观察日志自托管场景下向量器由外部 worker 驱动Timescale Cloud 上则由 TimescaleDB 调度自动运行。在新终端启动docker compose up -d vectorizer-workerdocker compose logs -f vectorizer-worker看到如下日志说明 worker 已拾取到向量器并开始处理blogvectorizer-worker-1 | 2024-10-23 12:56:36 [info ] running vectorizer vectorizer_id1worker 的行为在projects/pgai/pgai/vectorizer/worker.py中主循环轮询ai.vectorizer表拿到全部向量器 id逐个加载配置_get_vectorizerL112-L155。对 OpenAI 实现它会按embedding.api_key_name本例OPENAI_API_KEY先从进程环境变量取密钥取不到再尝试ai.reveal_secret从数据库密钥表读取两者都没有则抛ApiKeyNotFoundError——这就是compose.yaml中给 worker 注入OPENAI_API_KEY的原因。6. 语义搜索一条查询命中嵌入结果SELECT chunk, embedding ai.openai_embed(text-embedding-3-small, good food, dimensions768) as distance FROM blog_contents_embeddings ORDER BY distance;结果按余弦距离从近到远排列来自原指南chunkdistanceMaintaining a healthy diet can be challenging for busy professionals...0.6720892190933228Blogging can be a great way to share your thoughts and expertise...0.7744888961315155PostgreSQL is a powerful, open source object-relational database system...0.815629243850708Cloud computing has revolutionized the way businesses operate...0.8913049921393394As we look towards the future, artificial intelligence continues to evolve...0.9215681301612775good food 的最近邻正是健康饮食那篇博客语义搜索生效。这里用到两个机制是 pgvector 提供的余弦距离算子ORDER BY distance即按相似度排序ai.openai_embed是数据库内的嵌入函数定义于projects/extension/sql/idempotent/001-openai.sql签名为openai_embed(model, input_text, api_key, api_key_name, dimensions, openai_user, extra_headers, extra_query, extra_body, verbose, client_config)密钥解析走ai.secrets.get_secret与向量器侧用的是同一套密钥体系。查询嵌入务必与建向量器时用相同的模型和维度本例都是text-embedding-3-small 768否则向量空间不一致、距离无意义。更稳妥的做法是直接复用向量器自己的嵌入配置ai.vectorizer_embed(vectorizer_id_or_name, good food)定义于projects/pgai/db/sql/idempotent/012-vectorizer-api.sql的vectorizer_embed函数内部按implementation分派到openai_embed等实现天然保证查询端与文档端配置一致。三、嵌入批处理与截断worker 侧的源码细节worker 拿到队列数据后并不是一个 chunk 一次 API 调用。从projects/pgai/pgai/vectorizer/embedders/openai.py可以看到 OpenAI 嵌入器的三个关键实现批量打包_max_chunks_per_batch返回 2048、_max_tokens_per_batch返回 300000配合projects/pgai/pgai/vectorizer/embeddings.py中的batch_indices()L35-L73按单批 chunk 数不超过 2048 且估算 token 数不超过 30 万的规则把 chunk 切成若干请求批次。若单个 chunk 超过批 token 上限会直接抛BatchingError上下文截断内置模型上下文长度表EMBEDDING_MODEL_CONTEXT_LENGTHL28-L32标明text-embedding-3-small上限为 8191 tokenembed()L166-L198会用 tiktoken 编码每个 chunk超长的会截断到 8191 token 并打印chunk truncated警告——这意味着过长的contents不会让流水线失败但超出部分的信息会丢失必要时应在chunking配置中调小分块尺寸流式解析响应请求带encoding_formatfloat响应用ijson.parse_async流式解析 embedding 数组与usagetoken 统计避免超大 JSON 一次性驻留内存并自动带 3 次重试AsyncOpenAI(..., max_retries3)。另外注意一个实现约束对text-embedding-ada-002dimensions必须固定为 1536_openai_dimensions属性会强制校验而text-embedding-3-small/large支持自定义维度指南选用 768 维以节省存储与检索开销。四、运维常用操作向量器跑起来后日常运维可以直接查库以下对象均由projects/pgai/db/sql/idempotent/012-vectorizer-api.sql提供-- 查看向量器状态、源表、目标表、待处理队列深度 SELECT * FROM ai.vectorizer_status; -- 精确查看某个向量器的队列积压默认采样上限 10001 行 SELECT ai.vectorizer_queue_pending(blog_blog_contents_embeddings);其他常用能力ai.enable_vectorizer_schedule(id_or_name)/ai.disable_vectorizer_schedule(...)暂停/恢复调度依赖 TimescaleDB job 的部署会同步alter_job(scheduled...)ai.drop_vectorizer(id_or_name, drop_all)删除调度任务、源表触发器、队列表与元数据行drop_all true时才会连目标嵌入表一起删除失败行的重试排查每个向量器有独立的失败队列表ai._vectorizer_q_failed_idworker 处理失败如 API 429的行会落到这里配合docker compose logs -f vectorizer-worker定位原因。五、小结与延伸走完上述步骤你就得到了一个源表即向量库的 PostgreSQLblog表上的触发器会自动把增删改推入队列worker 持续消费并把嵌入写入blog_contents_embeddings一条查询即可完成语义搜索——这正是 pgai 面向 RAG、语义检索类应用的核心价值。若想继续深入建议按以下路径均为仓库内文档向量器概念总览overview全部 API 参考chunking、formatting、scheduling 等配置api-reference不想依赖 OpenAI 时的本地模型方案Ollama 快速上手 与 Voyage 快速上手文档PDF/HTML/S3 文件嵌入场景s3-documents、document-embeddings后台 worker 的部署方式worker。【免费下载链接】pgaiA suite of tools to develop RAG, semantic search, and other AI applications more easily with PostgreSQL项目地址: https://gitcode.com/GitHub_Trending/pg/pgai创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考