Type something to search...
使用pydanticai和rag掌握AI代理:轻松构建智能系统的5个步骤

使用pydanticai和rag掌握AI代理:轻松构建智能系统的5个步骤

Generative AI 和 PydanticAI 的崛起

生成式 AI 的兴起改变了我们构建智能系统的方式,而像 PydanticAI 这样的框架使创建生产级 AI 代理变得前所未有的简单。在本教程中,我们将探讨如何使用 PydanticAI 构建具有检索增强生成 (RAG) 功能的 AI 代理。我们将逐步介绍核心概念、特性和一个实践示例,以帮助您入门。

什么是 PydanticAI?

PydanticAI 是一个基于流行的 Pydantic 库构建的 Python 框架。它通过提供以下工具简化了 AI 驱动的应用程序的开发:

  • 结构化输出验证:确保 LLM 输出符合预定义的模式。
  • 依赖注入:动态地为代理提供外部数据和服务。
  • 工具集成:允许代理在运行时调用函数。
  • 与模型无关的支持:适用于 OpenAI、Anthropic、Ollama 等。
  • 类型安全:利用 Python 类型提示进行可靠的开发。

通过结合这些特性,PydanticAI 使得构建可靠、可扩展且可维护的 AI 系统变得更容易。

为什么将 RAG 与 PydanticAI 一起使用?

检索增强生成 (RAG) 通过集成外部知识源(如数据库或文档存储库)来增强 LLM。这种方法非常适合需要最新或特定领域信息的任务。使用 PydanticAI,您可以通过结合依赖注入、工具和结构化响应来无缝地实现 RAG。

设置您的环境

在深入研究代码之前,请确保您的环境已准备就绪:

  1. 安装 PydanticAI:

    pip install pydantic-ai
    
  2. 使用 pgvector 设置 PostgreSQL:

    RAG 通常需要基于向量的搜索功能。使用 pgvector 在 PostgreSQL 中启用此功能:

    mkdir postgres-data
    docker run --rm \
        -e POSTGRES_PASSWORD=postgres \
        -p 54320:5432 \
        -v `pwd`/postgres-data:/var/lib/postgresql/data \
        pgvector/pgvector:pg17
    
  3. 准备依赖项:

    确保您拥有用于数据库连接的 asyncpg 等工具。

构建 RAG 代理

让我们创建一个 PydanticAI 代理,该代理使用 RAG 根据存储在数据库中的文档来回答问题。

  1. 定义依赖项

    依赖项为代理提供外部数据或服务。对于我们的 RAG 示例,我们将使用 PostgreSQL 连接池。

    from dataclasses import dataclass
    from asyncpg import Pool
    
    @dataclass
    class Deps:
        pool: Pool
    
  2. 创建数据库模式

    数据库将文档部分存储为嵌入,以进行高效检索。

    CREATE EXTENSION IF NOT EXISTS vector;
    
    CREATE TABLE IF NOT EXISTS doc_sections (
        id SERIAL PRIMARY KEY,
        url TEXT NOT NULL UNIQUE,
        title TEXT NOT NULL,
        content TEXT NOT NULL,
        embedding VECTOR(1536)  
    );
    
  3. 为 RAG 搜索定义工具

    工具是代理在运行时可以调用的函数。以下是我们实现搜索工具的方式:

    from pydantic_ai import Agent, RunContext
    
    async def search_docs(ctx: RunContext[Deps], query_embedding: list[float]) -> str:
        """Searches the documentation database using vector similarity."""
        rows = await ctx.deps.pool.fetch(
            """
            SELECT url, title, content 
            FROM doc_sections 
            ORDER BY embedding <-> $1 
            LIMIT 5
            """,
            query_embedding
        )
        return "\n\n".join(
            f"# {row['title']}\nDocumentation URL: {row['url']}\n\n{row['content']}"
            for row in rows
        )
    
  4. 构建代理

    现在我们将所有内容组合成一个代理。

    from pydantic_ai import Agent
    
    agent = Agent(
        model="openai:gpt-4o",  
        deps_type=Deps,
        system_prompt="You are a helpful assistant answering questions using provided documentation.",
    )
    
    @agent.tool
    async def search(ctx: RunContext[Deps], query: str) -> str:
        """Searches the documentation database."""
    
    query_embedding = await ctx.deps.pool.fetchval("SELECT embedding_function($1)", query)
        return await search_docs(ctx, query_embedding)
    
    async def main():
    
    async with database_connect() as pool:
            deps = Deps(pool=pool)
    
    question = "How do I configure logfire with FastAPI?"
            result = await agent.run(question, deps=deps)
            print(result.data)
    

运行示例

  1. 使用 pgvector 启动您的 PostgreSQL 服务器。
  2. 使用您的文档的嵌入填充 doc_sections 表。
  3. 运行上面的 Python 脚本与您的启用 RAG 的代理进行交互。

PydanticAI 的主要功能

  • 结构化输出验证:确保响应符合使用 Pydantic 模型定义的模式。
  • 动态工具集成:LLM 可以在运行时动态调用 search_docs 等工具。
  • 依赖注入:简化将外部资源(如数据库连接)传递到代理的过程。

使用 PydanticAI 和 RAG 构建一个全功能的聊天应用程序

我们将从之前的教程中获取 PydanticAI 和检索增强生成 (RAG) 的概念,并将它们扩展到一个功能齐全的聊天应用程序中。此应用程序将允许用户与 AI 代理交互,该代理可以从数据库中检索相关信息,就像知识助手一样。我们将受到 PydanticAI 聊天应用程序示例 的启发,但会通过 RAG 功能增强它,使响应更具上下文感知和数据驱动。

应用程序概述

该应用程序将包括:

  1. 后端:一个由 PydanticAI 驱动的 FastAPI 服务器,用于管理 AI 交互和数据库操作。
  2. 前端:一个用于用户交互的简单 HTML/JS/TS 界面。
  3. 数据库:SQLite 与 pgvector,用于存储和检索文档的嵌入。

设置环境

先决条件

  • Python 3.9+
  • Node.js(可选,用于前端开发)
  • PostgreSQL 与 pgvector(用于 RAG 功能)

安装依赖项

pip install pydantic-ai fastapi uvicorn logfire asyncpg sqlite3

如果您使用 PostgreSQL 与 pgvector,请确保已安装并正在运行:

docker run --rm \
    -e POSTGRES_PASSWORD=postgres \
    -p 54320:5432 \
    -v `pwd`/postgres-data:/var/lib/postgresql/data \
    pgvector/pgvector:pg17

后端:FastAPI 服务器

对于 RAG,我们将把文档嵌入存储在 PostgreSQL 表中。

CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE IF NOT EXISTS doc_sections (
    id SERIAL PRIMARY KEY,
    url TEXT NOT NULL UNIQUE,
    title TEXT NOT NULL,
    content TEXT NOT NULL,
    embedding VECTOR(1536)  
);

后端代码

这是完整的后端实现,结合了聊天功能和 RAG。

from fastapi import FastAPI, Request, Depends, Form
from fastapi.responses import FileResponse, StreamingResponse
from pydantic_ai import Agent, RunContext
from dataclasses import dataclass
from typing import Annotated
import asyncio
import sqlite3
from contextlib import asynccontextmanager
from datetime import datetime, timezone
import json

app = FastAPI()

@dataclass
class Deps:
    db: sqlite3.Connection

agent = Agent(
    model="openai:gpt-4o",
    deps_type=Deps,
    system_prompt="You are an AI assistant that retrieves and answers questions based on provided documentation."
)

@asynccontextmanager
async def lifespan(app: FastAPI):
    con = sqlite3.connect("chat_app.db")
    yield {"db": con}
    con.close()

app = FastAPI(lifespan=lifespan)

@app.get("/")
async def index():
    return FileResponse("chat_app.html", media_type="text/html")

@app.get("/chat_app.ts")
async def main_ts():
    return FileResponse("chat_app.ts", media_type="text/plain")

async def get_db(request: Request):
    return request.state.db

@agent.tool
async def search_docs(ctx: RunContext[Deps], query_embedding: list[float]) -> str:
    """Searches the database for relevant documents."""
    rows = ctx.deps.db.execute("""
        SELECT url, title, content 
        FROM doc_sections 
        ORDER BY embedding <-> ? 
        LIMIT 5;
    """, (query_embedding,))

    results = "\n\n".join(
        f"# {row[1]}\nURL: {row[0]}\n\n{row[2]}" for row in rows.fetchall()
    )

    return results or "No relevant documents found."

@app.post("/chat/")
async def post_chat(
    prompt: Annotated[str, Form()], db=Depends(get_db)
) -> StreamingResponse:

    async def stream_messages():
        yield json.dumps({
            "role": "user",
            "timestamp": datetime.now(timezone.utc).isoformat(),
            "content": prompt,
        }).encode("utf-8") + b"\n"

        messages = db.execute("SELECT message_list FROM messages").fetchall()
        message_history = [json.loads(row[0]) for row in messages]

        async with agent.run_stream(prompt, message_history=message_history) as result:
            async for text in result.stream(debounce_by=0.01):
                yield json.dumps({
                    "role": "model",
                    "timestamp": datetime.now(timezone.utc).isoformat(),
                    "content": text,
                }).encode("utf-8") + b"\n"

            db.execute(
                "INSERT INTO messages (message_list) VALUES (?)",
                (result.new_messages_json(),)
            )
            db.commit()

    return StreamingResponse(stream_messages(), media_type="text/plain")

前端:HTML 和 TypeScript

前端将允许用户与聊天应用程序交互。

HTML 模板

将其保存为 chat_app.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Chat App</title>
</head>
<body>
  <main>
      <h1>Chat Application</h1>
      <div id="conversation"></div>
      <form method="post">
          <input id="prompt-input" name="prompt" placeholder="Type your question..." />
          <button type="submit">Send</button>
      </form>
  </main>
</body>
<script src="/chat_app.ts" type="module"></script>
</html>

TypeScript 逻辑

将其保存为 chat_app.ts

const convElement = document.getElementById('conversation')!;
const promptInput = document.getElementById('prompt-input') as HTMLInputElement;

document.querySelector('form')!.addEventListener('submit', async (e) => {
  e.preventDefault();

  const formData = new FormData(e.target as HTMLFormElement);
  const response = await fetch('/chat/', { method: 'POST', body: formData });

  const reader = response.body!.getReader();
  let textDecoder = new TextDecoder();

  while (true) {
      const { done, value } = await reader.read();
      if (done) break;

      const message = JSON.parse(textDecoder.decode(value));
      const div = document.createElement('div');
      div.textContent = `${message.role}: ${message.content}`;
      convElement.appendChild(div);
  }
});

运行应用程序

1- 启动后端服务器:

uvicorn app:app --reload --port 8000

2- 在浏览器中打开 http://localhost:8000

下一步

要扩展此示例:

  1. 添加更多工具以进行高级交互(例如,摘要或数据提取)。
  2. 使用多代理设置进行复杂的工作流程。
  3. 使用 Pydantic Logfire 等工具集成监控,以进行调试和性能跟踪。

有关更多详细信息,请查看 PydanticAI 的文档RAG 示例

Related Posts

结合chatgpt-o3-mini与perplexity Deep Research的3步提示:提升论文写作质量的终极指南

结合chatgpt-o3-mini与perplexity Deep Research的3步提示:提升论文写作质量的终极指南

AI 研究报告和论文写作 合并两个系统指令以获得两个模型的最佳效果 Perplexity AI 的 Deep Research 工具提供专家级的研究报告,而 OpenAI 的 ChatGPT-o3-mini-high 擅长推理。我发现你可以将它们结合起来生成令人难以置信的论文,这些论文比任何一个模型单独撰写的都要好。你只需要将这个一次性提示复制到 **

阅读更多
让 Excel 过时的 10 种 Ai 工具:实现数据分析自动化,节省手工作业时间

让 Excel 过时的 10 种 Ai 工具:实现数据分析自动化,节省手工作业时间

Non members click here作为一名软件开发人员,多年来的一个发现总是让我感到惊讶,那就是人们还在 Excel

阅读更多
使用 ChatGPT 搜索网络功能的 10 种创意方法

使用 ChatGPT 搜索网络功能的 10 种创意方法

例如,提示和输出 你知道可以使用 ChatGPT 的“搜索网络”功能来完成许多任务,而不仅仅是基本的网络搜索吗? 对于那些不知道的人,ChatGPT 新的“搜索网络”功能提供实时信息。 截至撰写此帖时,该功能仅对使用 ChatGPT 4o 和 4o-mini 的付费会员开放。 ![](https://images.weserv.nl/?url=https://cdn-im

阅读更多
掌握Ai代理:解密Google革命性白皮书的10个关键问题解答

掌握Ai代理:解密Google革命性白皮书的10个关键问题解答

10 个常见问题解答 本文是我推出的一个名为“10 个常见问题解答”的新系列的一部分。在本系列中,我旨在通过回答关于该主题的十个最常见问题来分解复杂的概念。我的目标是使用简单的语言和相关的类比,使这些想法易于理解。 图片来自 [Solen Feyissa](https://unsplash.com/@solenfeyissa?utm_source=medium&utm_medi

阅读更多
在人工智能和技术领域保持领先地位的 10 项必学技能 📚

在人工智能和技术领域保持领先地位的 10 项必学技能 📚

在人工智能和科技这样一个动态的行业中,保持领先意味着不断提升你的技能。无论你是希望深入了解人工智能模型性能、掌握数据分析,还是希望通过人工智能转变传统领域如法律,这些课程都是你成功的捷径。以下是一个精心策划的高价值课程列表,可以助力你的职业发展,并让你始终处于创新的前沿。 1. 生成性人工智能简介课程: [生成性人工智能简介](https://genai.works

阅读更多
揭开真相!深度探悉DeepSeek AI的十大误区,您被误导了吗?

揭开真相!深度探悉DeepSeek AI的十大误区,您被误导了吗?

在AI军备竞赛中分辨事实与虚构 DeepSeek AI真的是它所宣传的游戏规则改变者,还是仅仅聪明的营销和战略炒作?👀 虽然一些人将其视为AI效率的革命性飞跃,但另一些人则认为它的成功建立在借用(甚至窃取的)创新和可疑的做法之上。传言称,DeepSeek的首席执行官在疫情期间像囤积卫生纸一样囤积Nvidia芯片——这只是冰山一角。 从其声称的550万美元培训预算到使用Open

阅读更多
Type something to search...