• 欢迎访问少将全栈,学会感恩,乐于付出,珍惜缘份,成就彼此、推荐使用最新版火狐浏览器和Chrome浏览器访问本网站。
  • 吐槽,投稿,删稿,交个朋友
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏少将全栈吧

Docker Compose搭建RAG系统:一份可直接用的PostgreSQL+pgvector模板

可复用技术资产 admin 1小时前 5次浏览 已收录 扫描二维码

引言

你试过从头搭一个RAG系统吗?选向量数据库、装embedding模型、写检索逻辑、接LLM——每个环节单独看都不难,但串起来就处处是坑。上个月我给自己的AI应用加了语义搜索功能,花了两天时间才把Docker环境调通。事后整理了一份可以直接用的Docker Compose模板,想着应该有不少人需要这个。

这份模板用了PostgreSQL + pgvector,原因很简单:大多数项目已经用PostgreSQL了,加个扩展就行,不用额外维护一套MongoDB或者Pinecone集群。根据PostgreSQL官方文档,pgvector自2021年发布以来已成为最流行的PostgreSQL向量检索扩展,在GitHub上已获得超过1.3万颗星。对于中小规模项目(百万级向量以内),完全够用。

模板结构总览

整个配置包含三个服务:PostgreSQL数据库(含pgvector)、一个Python API服务(FastAPI + LangChain)、以及一个简单的Web前端。全部通过Docker Compose编排,一条命令启动。

rag-demo/
├── docker-compose.yml
├── db/
│   └── init.sql
├── api/
│   ├── Dockerfile
│   ├── requirements.txt
│   ├── app.py
│   └── .env.example
└── frontend/
    ├── Dockerfile
    ├── index.html
    └── nginx.conf

第一步:数据库配置

PostgreSQL官方提供的pgvector镜像可以直接用,不需要自己编译扩展。以下是docker-compose.yml的关键部分:

version: '3.8'
services:
  db:
    image: pgvector/pgvector:pg16
    environment:
      POSTGRES_DB: ragdb
      POSTGRES_USER: raguser
      POSTGRES_PASSWORD: ${DB_PASSWORD:-changeme}
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U raguser -d ragdb"]
      interval: 5s
      timeout: 5s
      retries: 5

初始化SQL脚本创建向量表和索引。关键配置是IVFFlat索引的lists参数——lists值设为表行数的平方根(例如10万行设sqrt(100000)≈316),这是pgvector官方文档推荐的基准值。

CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE documents (
    id BIGSERIAL PRIMARY KEY,
    content TEXT NOT NULL,
    embedding vector(1536),
    metadata JSONB DEFAULT '{}',
    created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_documents_embedding
    ON documents
    USING ivfflat (embedding vector_cosine_ops)
    WITH (lists = 100);

第二步:API服务

API层使用FastAPI + LangChain + OpenAI Embeddings。LangChain的PGVector集成类封装了连接管理和检索逻辑,大幅减少了样板代码。据LangChain官方文档,PGVector集成支持三种距离度量:余弦相似度(cosine)、欧几里得距离(l2)、内积(ip)。对于文本检索,余弦相似度通常是默认选择。

from langchain_community.vectorstores import PGVector
from langchain_openai import OpenAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
import os

CONNECTION_STRING = PGVector.connection_string_from_db_params(
    driver="psycopg2",
    host=os.getenv("DB_HOST", "db"),
    port=5432,
    database=os.getenv("DB_NAME", "ragdb"),
    user=os.getenv("DB_USER", "raguser"),
    password=os.getenv("DB_PASSWORD", "changeme"),
)

embeddings = OpenAIEmbeddings(model="text-embedding-3-small")

vector_store = PGVector(
    collection_name="documents",
    connection_string=CONNECTION_STRING,
    embedding_function=embeddings,
)

检索函数的关键参数是k(返回结果数)和score_threshold(相似度阈值)。实测经验:k=5, score_threshold=0.7对于大多数问答场景效果最佳,低于0.7的结果噪音太大。

def search_documents(query: str, k: int = 5):
    results = vector_store.similarity_search_with_relevance_scores(
        query,
        k=k,
        score_threshold=0.7
    )
    return [{"content": doc.page_content,
             "score": score,
             "metadata": doc.metadata}
            for doc, score in results]

第三步:文档分块策略

RAG系统中最容易被忽视的环节是文档分块(chunking)。块太小会丢失上下文,块太大会包含过多噪音,降低检索精度。RecursiveCharacterTextSplitter是LangChain官方推荐的通用分块器,采用分层分隔符策略。

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=200,
    separators=["\n\n", "\n", "。", ".", " ", ""]
)

chunk_size=1000, chunk_overlap=200是一个不错的起点。对于中文内容,建议把句号(。)加到separators列表里,比按换行符切分效果更好。Anthropic在2024年的RAG最佳实践报告中也提到,重叠200字符可以显著减少上下文被截断导致的检索遗漏。

第四步:完整的RAG查询链

将检索结果传给LLM生成答案。以下是使用LangChain的完整链:

from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

prompt = PromptTemplate(
    template="""基于以下参考内容回答问题。
如果参考内容中没有相关信息,直接说不知道,不要编造。

参考内容:
{context}

问题:{question}

回答:""",
    input_variables=["context", "question"]
)

qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",
    retriever=vector_store.as_retriever(
        search_type="similarity_score_threshold",
        search_kwargs={"k": 5, "score_threshold": 0.7}
    ),
    chain_type_kwargs={"prompt": prompt},
    return_source_documents=True
)

chain_type选”stuff”是因为它把检索结果全部塞进一个prompt里——简单直接。对于上下文超过LLM窗口大小的情况,可以改用”map_reduce”或”refine”,但会增加一次LLM调用和延迟。

启动与测试

# 1. 克隆或创建项目目录
mkdir rag-demo && cd rag-demo
# 2. 复制上述文件
# 3. 启动
docker compose up -d
# 4. 验证
curl http://localhost:8000/health
# 返回 {"status": "ok", "vector_count": 0}

全部服务启动后,通过API写入文档并测试检索:

curl -X POST http://localhost:8000/ingest \
  -H "Content-Type: application/json" \
  -d '{"content": "pgvector is a PostgreSQL extension for vector similarity search"}'

curl "http://localhost:8000/search?q=what+is+pgvector"

性能调优建议

这套模板在以下条件下表现最佳:

  • 向量数量 < 100万:超过这个量级建议升级到HNSW索引(pgvector 0.7.0+支持)或迁移到专用向量数据库
  • embedding维度 ≤ 1536:OpenAI text-embedding-3-small是1536维,text-embedding-3-large是3072维。更高维度意味着更多存储和更慢的检索
  • 并发查询 < 50/s:单节点PostgreSQL的向量检索吞吐量有限。Pinecone官方对比数据显示,在百万级向量场景下,专用向量数据库的查询延迟比PostgreSQL+pgvector低3-5倍

如果项目规模超出这个范围,可以考虑用Pinecone或Weaviate替代——但那就不是”一个docker-compose搞定”的事情了。

完整模板获取

上述所有代码整合成了一个可直接运行的GitHub仓库。配置文件、环境变量模板、Dockerfile一应俱全。复制下来改改.env就能用。

这就是用PostgreSQL+pgvector搭RAG系统的一套可复用的Docker Compose模板。从项目创建到第一条检索结果返回,大概需要10分钟。如果你的项目正好需要语义搜索功能,这个模板应该能帮你省下不少时间。

喜欢 (0)
[🍬谢谢你请我吃糖果🍬🍬~]
分享 (0)
关于作者:
少将,关注Web全栈开发、项目管理,持续不断的学习、努力成为一个更棒的开发,做最好的自己,让世界因你不同。