使用Timescale/pgai构建基于FastAPI的语义搜索与RAG应用教程

使用Timescale/pgai构建基于FastAPI的语义搜索与RAG应用教程

pgai Helper functions for AI workflows pgai 项目地址: https://gitcode.com/gh_mirrors/pg/pgai

项目概述

Timescale/pgai是一个强大的PostgreSQL扩展,它集成了AI功能,使开发者能够直接在数据库中执行向量搜索和检索增强生成(RAG)等AI任务。本教程将详细介绍如何使用pgai结合FastAPI框架构建一个完整的语义搜索和RAG应用。

环境准备

在开始之前,需要准备以下环境:

  1. 安装PostgreSQL数据库并配置好连接
  2. 获取OpenAI API密钥
  3. 安装Python 3.7+环境
  4. 安装FastAPI和相关依赖

应用架构

这个FastAPI应用主要包含以下几个核心功能模块:

  1. 数据库初始化与pgai扩展安装
  2. 数据加载与向量化处理
  3. 语义搜索功能
  4. 检索增强生成(RAG)功能

详细实现步骤

1. 初始化pgai扩展

在应用启动时,我们需要安装pgai扩展并启动向量化工作器:

# 安装pgai扩展
pgai.install(DB_URL)

# 启动向量化工作器
worker = Worker(DB_URL)
task = asyncio.create_task(worker.run())

这段代码会在PostgreSQL中创建ai模式,并安装所有必要的数据库对象。向量化工作器负责在后台异步处理向量嵌入的创建和更新。

2. 创建数据表并加载示例数据

我们创建了一个articles表来存储文章数据:

async def create_articles_table():
    async with pool.connection() as conn:
        async with conn.cursor() as cur:
            await cur.execute("""
                CREATE TABLE IF NOT EXISTS articles (
                    id SERIAL PRIMARY KEY,
                    url TEXT NOT NULL,
                    title TEXT NOT NULL,
                    text TEXT NOT NULL
                )
            """)
        await conn.commit()

然后从公开数据集加载了10篇英文文章:

async def load_articles():
    num_articles = 10
    max_text_length = 1000
    
    dataset = load_dataset("public_dataset", split=f"train", streaming=True)
    
    async with pool.connection() as conn:
        async with conn.cursor() as cur:
            for article in dataset.take(num_articles):
                await cur.execute(
                    "INSERT INTO articles (url, title, text) VALUES (%s, %s, %s)",
                    (article['url'], article['title'], article['text'][:max_text_length])
                )
            await conn.commit()

3. 配置向量化器

为了使文本数据支持语义搜索,我们需要为text列创建向量嵌入:

async def create_vectorizer():
    vectorizer_statement = CreateVectorizer(
        source="articles",
        target_table='articles_embedding_storage',
        loading=LoadingColumnConfig(column_name='text'),
        embedding=EmbeddingOllamaConfig(model='all-minilm', dimensions=384, base_url="http://localhost:11434")
    ).to_sql()
    
    try:
        async with pool.connection() as conn:
            async with conn.cursor() as cur:
                await cur.execute(vectorizer_statement)
            await conn.commit()
    except Exception as e:
        if "already exists" not in str(e):
            raise e

这段代码创建了一个向量化器,它会自动为articles表的text列生成向量嵌入,并将结果存储在articles_embedding_storage表中。我们使用了Ollama的all-minilm模型来生成384维的向量。

4. 检查向量化进度

向量化是一个异步过程,我们可以通过查询vectorizer_status视图来检查进度:

@app.get("/vectorizer_status")
async def vectorizer_status():
    async with pool.connection() as conn:
        async with conn.cursor(row_factory=dict_row) as cur:
            await cur.execute("SELECT * FROM ai.vectorizer_status")
            return await cur.fetchall()

pending_items列显示为0时,表示所有嵌入向量都已创建完成。

5. 实现语义搜索

语义搜索的核心是_find_relevant_chunks函数:

async def _find_relevant_chunks(client: ollama.AsyncClient, query: str, limit: int = 2) -> List[ArticleSearchResult]:
    response = await client.embed(model="all-minilm", input=query)
    embedding = np.array(response.embeddings[0])
    
    async with pool.connection() as conn:
        async with conn.cursor(row_factory=class_row(ArticleSearchResult)) as cur:
            await cur.execute("""
                SELECT a.id, a.url, a.title, a.text, a.chunk, a.embedding <=> %s as distance
                FROM articles_embedding a
                ORDER BY distance
                LIMIT %s
            """, (embedding, limit))
            
            return await cur.fetchall()

这个函数首先将查询文本转换为向量,然后使用pgvector的<=>操作符计算余弦距离,找出与查询最相关的文本块。

6. 实现RAG功能

RAG功能结合了语义搜索和LLM生成能力:

@app.get("/rag")
async def rag(query: str) -> Optional[str]:
    client = ollama.AsyncClient(host="http://localhost:11434")
    
    chunks = await _find_relevant_chunks(client, query)
    context = "\n\n".join(f"{article.title}:\n{article.text}" for article, _ in chunks)
    
    prompt = f"""Question: {query}

Please use the following context to provide an accurate response:

{context}

Answer:"""
            
    response = await client.generate(
        model='tinyllama',
        prompt=prompt,
        stream=False
    )
    
    return response['response']

这个端点首先通过语义搜索找到相关上下文,然后将上下文和问题一起发送给LLM生成回答。

应用测试

启动应用后,可以通过以下方式测试功能:

  1. 访问/docs查看API文档并测试端点
  2. 使用/search端点进行语义搜索
  3. 使用/rag端点进行问答
  4. 使用/insert_pgai_article端点添加新文章

性能优化建议

  1. 对于生产环境,建议将向量化工作器运行在独立进程中
  2. 可以考虑使用更大的嵌入模型提高搜索质量
  3. 对于大量数据,可以调整向量化器的批处理大小
  4. 可以添加缓存机制减少重复计算

总结

通过本教程,我们展示了如何使用Timescale/pgai快速构建一个功能完整的语义搜索和RAG应用。pgai的强大之处在于它将AI功能深度集成到PostgreSQL中,使开发者能够用熟悉的SQL语法实现复杂的AI应用场景。这种架构既简化了开发流程,又保证了系统的可扩展性和性能。

pgai Helper functions for AI workflows pgai 项目地址: https://gitcode.com/gh_mirrors/pg/pgai

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

霍美予Mabel

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值