别再手动拼接Prompt了!Pydantic-AI让AI应用开发像写API一样优雅

别再手动拼接Prompt了!Pydantic-AI让AI应用开发像写API一样优雅

别再手动拼接Prompt了!Pydantic-AI让AI应用开发像写API一样优雅

为什么Pydantic-AI正在重新定义AI Agent开发范式


引言:AI开发的新范式

当你第一次尝试用Python构建AI应用时,是否经历过这样的痛苦?

Prompt字符串像意大利面一样缠绕,每次改需求都要小心翼翼地在某个角落里修改文字,LLM返回的数据格式完全不可控,一个不小心整个流程就崩溃了。更让人头疼的是,代码写完后自己都看不懂,更别说让同事维护了。

这些问题,Pydantic团队在构建了全球最流行的数据验证库Pydantic之后,给出了他们的答案:Pydantic-AI

如果你还不知道Pydantic是什么,简而言之,它是一个Python库,通过Python类型注解来定义数据模型,自动进行数据验证和转换。在FastAPI、Django Ninja等现代Web框架中,你几乎都能看到Pydantic的身影。

而Pydantic-AI,则是将这种“类型驱动开发”的理念,带入了AI Agent开发领域。


为什么值得关注

解决的核心痛点

痛点一:Prompt管理的混乱

传统做法是把所有指令都塞进一个巨大的字符串:

# 传统做法 - 维护困难
prompt = f"""
你是一个客服助手。
用户说: {user_message}
请用专业、友好的语气回复。
如果用户抱怨,要道歉。
如果用户询问产品,提供详细信息。
...
"""

当项目变大时,这种做法会迅速失控。而Pydantic-AI允许你用结构化的方式定义Agent的行为:

from pydantic_ai import Agent

agent = Agent(
    'openai:gpt-4',
    system_prompt='你是一个专业的客服助手...',
    result_type=SupportResult  # 结果类型自动验证
)

痛点二:输出格式的不确定性

让LLM返回结构化数据一直是难题。JSON模式有时候不work,Parser经常返回格式错误的数据。Pydantic-AI通过Result Type机制,强制要求模型输出符合预期格式的数据,并且自动验证。

痛点三:工具调用的复杂性

要让AI调用外部工具,需要处理一堆JSON Schema、参数校验、结果解析的逻辑。Pydantic-AI简化了这个过程,直接用Python函数定义工具,自动处理所有底层细节。

与竞品的对比

特性 LangChain LlamaIndex Pydantic-AI
类型安全
学习曲线 陡峭 中等 平缓
数据验证 需额外集成 需额外集成 内置
代码可读性 中等 中等
维护成本

环境搭建

前置要求

在开始之前,请确保你的开发环境满足以下条件:

  • Python 3.10 或更高版本
  • 已安装pip或poetry包管理器
  • 拥有一个支持的LLM服务API密钥(OpenAI、Anthropic、Google等)

安装步骤

方式一:使用pip安装

pip install pydantic-ai

方式二:使用poetry安装

poetry add pydantic-ai

方式三:安装特定版本

pip install pydantic-ai==0.0.1b1

安装额外依赖

根据你使用的LLM提供商,可能需要安装额外的依赖:

# OpenAI支持
pip install 'pydantic-ai[openai]'

# Anthropic支持
pip install 'pydantic-ai[anthropic]'

# Google Gemini支持
pip install 'pydantic-ai[google-gemini]'

# 同时安装多个
pip install 'pydantic-ai[openai,anthropic]'

环境变量配置

创建一个.env文件来管理你的API密钥:

# .env文件内容
OPENAI_API_KEY=sk-your-openai-key-here
ANTHROPIC_API_KEY=sk-ant-your-anthropic-key-here
GOOGLE_API_KEY=your-google-api-key

在Python代码中加载环境变量:

from dotenv import load_dotenv

load_dotenv()  # 加载.env文件中的环境变量

验证安装

创建一个简单的测试脚本来验证安装是否成功:

import pydantic_ai

print(f"Pydantic-AI版本: {pydantic_ai.__version__}")

from pydantic_ai import Agent
print("Agent导入成功!")

核心概念详解

Agent:AI Agent的基本单元

在Pydantic-AI中,Agent是核心概念。你可以把它理解为一个“智能助手”,它具备以下特性:

  • 有一个特定的角色和目标(通过system_prompt定义)
  • 能够处理用户输入并返回结构化结果
  • 可以调用外部工具来扩展能力
  • 返回的结果会被自动验证

创建一个最基本的Agent:

from pydantic_ai import Agent

agent = Agent(
    model='openai:gpt-4',
    system_prompt='你是一个乐于助人的AI助手'
)

Model:支持多种LLM后端

Pydantic-AI支持多种LLM提供商,模型名称遵循统一的格式:provider:model-name

支持的模型示例:

# OpenAI系列
agent = Agent('openai:gpt-4')
agent = Agent('openai:gpt-4-turbo')
agent = Agent('openai:gpt-3.5-turbo')

# Anthropic系列
agent = Agent('anthropic:claude-3-5-sonnet-20241022')
agent = Agent('anthropic:claude-3-haiku-20240307')

# Google系列
agent = Agent('google-gemini:gemini-pro')
agent = Agent('google-gemini:gemini-1.5-flash')

# Ollama本地模型
agent = Agent('ollama:llama3')
agent = Agent('ollama:qwen2')

Result Type:结构化输出的保证

这是Pydantic-AI最强大的特性之一。你可以通过定义Pydantic模型来指定Agent的输出格式:

from pydantic import BaseModel
from pydantic_ai import Agent

class UserProfile(BaseModel):
    name: str
    age: int
    email: str

agent = Agent(
    'openai:gpt-4',
    result_type=UserProfile
)

当Agent执行完成后,返回的结果会自动转换为UserProfile类型。如果格式不匹配,会抛出明确的错误。

System Prompt:定义Agent角色

System Prompt定义了Agent的身份和行为规则。Pydantic-AI支持多种方式定义system prompt:

方式一:直接字符串

agent = Agent(
    'openai:gpt-4',
    system_prompt='你是一个专业的Python程序员,擅长编写高质量、易读的代码。'
)

方式二:多段落Prompt

system_prompt = """
你是一个数据分析助手。

你的职责:
1. 理解用户的数据分析需求
2. 提供清晰的数据解读
3. 生成可执行的建议

沟通风格:
- 使用简单易懂的语言
- 适当使用数据可视化
- 给出具体的行动建议
"""

agent = Agent('openai:gpt-4', system_prompt=system_prompt)

方式三:动态System Prompt

有时你需要在运行时动态调整system prompt:

from pydantic_ai import Agent

def get_system_prompt(context: dict) -> str:
    base = "你是一个数据分析助手。"
    if context.get('expert_mode'):
        base += " 以专业分析师的标准回答。"
    else:
        base += " 用通俗易懂的语言解释。"
    return base

agent = Agent(
    'openai:gpt-4',
    system_prompt=get_system_prompt({'expert_mode': True})
)

实战教程:从入门到精通

案例一:构建一个智能客服Agent

让我们从头开始,构建一个能够回答产品相关问题的智能客服。

第一步:定义数据结构

from pydantic import BaseModel, Field
from typing import Optional

class SupportResult(BaseModel):
    """客服回复的结果模型"""
    response: str = Field(description="回复用户的文本内容")
    category: str = Field(description="问题分类:product, shipping, refund, other")
    needs_escalation: bool = Field(description="是否需要人工介入")
    confidence: float = Field(description="回答的置信度,0-1之间", ge=0, le=1)

class Product(BaseModel):
    name: str
    price: float
    description: str
    in_stock: bool

第二步:创建Agent

from pydantic_ai import Agent

system_prompt = """
你是一个专业的产品客服助手。

你的职责:
1. 回答用户关于产品的问题
2. 提供准确的库存和价格信息
3. 判断是否需要人工客服介入

回答规则:
- 保持友好、专业的语气
- 如果信息不确定,请明确说明
- 当用户要求退款或投诉时,设置needs_escalation为true
"""

agent = Agent(
    'openai:gpt-4',
    result_type=SupportResult,
    system_prompt=system_prompt
)

第三步:执行查询

async def main():
    result = await agent.run("这件T恤有红色可选吗?多少钱?")

    print(f"回复: {result.data.response}")
    print(f"分类: {result.data.category}")
    print(f"需要人工: {result.data.needs_escalation}")
    print(f"置信度: {result.data.confidence}")

import asyncio
asyncio.run(main())

完整示例代码:

from pydantic import BaseModel, Field
from pydantic_ai import Agent
import asyncio

# ==================== 定义数据结构 ====================

class SupportResult(BaseModel):
    response: str = Field(description="回复用户的文本内容")
    category: str = Field(description="问题分类")
    needs_escalation: bool = Field(description="是否需要人工介入")
    confidence: float = Field(description="置信度", ge=0, le=1)

# ==================== 创建Agent ====================

agent = Agent(
    'openai:gpt-4',
    result_type=SupportResult,
    system_prompt=(
        "你是一个专业的产品客服助手。"
        "回答用户关于产品的问题,并判断是否需要人工介入。"
        "始终保持友好、专业的语气。"
    )
)

# ==================== 定义产品数据 ====================

products = {
    "T恤": {
        "price": 99.0,
        "colors": ["黑色", "白色", "蓝色", "红色"],
        "in_stock": True
    },
    "牛仔裤": {
        "price": 199.0,
        "sizes": ["S", "M", "L", "XL"],
        "in_stock": True
    }
}

# ==================== 执行查询 ====================

async def main():
    # 查询产品信息
    result = await agent.run("T恤有红色的吗?多少钱?")

    print("=" * 50)
    print(f"回复内容: {result.data.response}")
    print(f"问题分类: {result.data.category}")
    print(f"需要人工介入: {result.data.needs_escalation}")
    print(f"置信度: {result.data.confidence:.2f}")
    print("=" * 50)

    # 投诉场景
    complaint_result = await agent.run(
        "我上周买的商品质量很差,要求全额退款!"
    )

    print(f"投诉回复: {complaint_result.data.response}")
    print(f"需要人工: {complaint_result.data.needs_escalation}")

if __name__ == "__main__":
    asyncio.run(main())

案例二:构建带工具调用的Agent

让Agent能够调用外部函数是构建实用AI应用的关键。Pydantic-AI提供了简洁的方式来定义和使用工具。

第一步:定义工具函数

from pydantic_ai import Agent, Tool
from datetime import datetime

# ==================== 定义各种工具函数 ====================

def get_weather(city: str) -> str:
    """
    获取城市天气信息

    Args:
        city: 城市名称
    Returns:
        天气描述字符串
    """
    # 这里是模拟数据,实际应用中会调用天气API
    weather_data = {
        "北京": "多云,25°C,适宜出行",
        "上海": "晴天,28°C,注意防晒",
        "广州": "雷阵雨,26°C,记得带伞",
        "深圳": "阴天,27°C,空气湿润"
    }
    return weather_data.get(city, "抱歉,暂不支持该城市")

def get_current_time() -> str:
    """获取当前时间"""
    return datetime.now().strftime("%Y年%m月%d日 %H:%M:%S")

def calculate(expression: str) -> float:
    """
    执行数学计算

    Args:
        expression: 数学表达式,如 "2+3*4"
    Returns:
        计算结果
    """
    # 注意:实际应用中应该使用安全的计算器,避免eval
    allowed_chars = set("0123456789+-*/.() ")
    if all(c in allowed_chars for c in expression):
        return eval(expression)
    return "表达式包含非法字符"

第二步:创建带工具的Agent

# 创建Agent并注册工具
agent = Agent(
    'openai:gpt-4',
    tools=[
        Tool(get_weather, description="获取指定城市的天气信息"),
        Tool(get_current_time, description="获取当前日期和时间"),
        Tool(calculate, description="执行数学计算")
    ]
)

第三步:使用工具执行任务

async def tool_demo():
    # 示例1:查询天气
    result1 = await agent.run("北京今天天气怎么样?适合出门吗?")
    print("天气查询结果:")
    print(result1.data)
    print("-" * 40)

    # 示例2:获取时间并计算
    result2 = await agent.run("现在是几点?如果加上6小时是几点?")
    print("时间计算结果:")
    print(result2.data)
    print("-" * 40)

    # 示例3:纯计算
    result3 = await agent.run("帮我计算 (15 + 25) * 3 / 4")
    print("计算结果:")
    print(result3.data)

asyncio.run(tool_demo())

完整示例代码:

from pydantic_ai import Agent, Tool
from pydantic import BaseModel
from datetime import datetime
import asyncio

# ==================== 定义数据结构 ====================

class WeatherInfo(BaseModel):
    city: str
    weather: str
    temperature: str
    suggestion: str

class TimeInfo(BaseModel):
    current_time: str
    calculated_time: str
    calculation_expression: str
    result: str

# ==================== 定义工具函数 ====================

def get_weather(city: str) -> str:
    """获取城市天气信息"""
    weather_data = {
        "北京": "多云,25°C",
        "上海": "晴天,28°C",
        "广州": "雷阵雨,26°C",
        "深圳": "阴天,27°C"
    }
    return weather_data.get(city, f"暂不支持{city}的天气查询")

def get_current_time() -> str:
    """获取当前时间"""
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

def add_hours(hours: int) -> str:
    """添加小时数到当前时间"""
    from datetime import timedelta
    new_time = datetime.now() + timedelta(hours=hours)
    return new_time.strftime("%Y-%m-%d %H:%M:%S")

def calculate(expression: str) -> str:
    """执行数学计算"""
    try:
        result = eval(expression)
        return str(result)
    except Exception as e:
        return f"计算错误: {str(e)}"

# ==================== 创建Agent ====================

agent = Agent(
    'openai:gpt-4',
    tools=[
        Tool(get_weather, description="获取城市天气,如'北京天气'"),
        Tool(get_current_time, description="获取当前时间"),
        Tool(add_hours, description="计算几小时后的时间"),
        Tool(calculate, description="计算数学表达式")
    ],
    system_prompt=(
        "你是一个智能助手,可以查询天气、时间和执行计算。"
        "根据用户需求选择合适的工具来回答问题。"
        "如果有多个工具相关,先调用一个再看结果。"
    )
)

# ==================== 执行演示 ====================

async def main():
    print("=" * 60)
    print("示例1:查询天气")
    print("=" * 60)
    result1 = await agent.run("帮我查一下上海的天气")
    print(f"AI回复: {result1.data}")

    print("\n" + "=" * 60)
    print("示例2:时间计算")
    print("=" * 60)
    result2 = await agent.run("现在几点了?如果过8小时是几点?")
    print(f"AI回复: {result2.data}")

    print("\n" + "=" * 60)
    print("示例3:数学计算")
    print("=" * 60)
    result3 = await agent.run("计算 125 * 8 + 300 等于多少?")
    print(f"AI回复: {result3.data}")

if __name__ == "__main__":
    asyncio.run(main())

案例三:构建多轮对话Agent

在实际应用中,AI通常需要记住之前的对话内容。Pydantic-AI提供了简洁的对话管理机制。

第一步:理解Message历史

from pydantic_ai import Agent
from pydantic_ai.messages import UserMessage, AssistantMessage

# 创建Agent
agent = Agent('openai:gpt-4')

async def conversation_demo():
    # 第一次对话
    result1 = await agent.run("我叫张三,是一名软件工程师")
    print(f"AI: {result1.data}")

    # 第二次对话(带历史)
    result2 = await agent.run(
        "根据我之前说的,我适合什么类型的工作?",
        message_history=agent.messages  # 传递历史消息
    )
    print(f"AI: {result2.data}")

第二步:更复杂的多轮对话

from pydantic_ai import Agent
from pydantic_ai.messages import UserMessage, AssistantMessage, SystemMessage
from dataclasses import dataclass

@dataclass
class ConversationContext:
    user_name: str = ""
    preferences: list = None

    def __post_init__(self):
        if self.preferences is None:
            self.preferences = []

# 创建带上下文的Agent
agent = Agent(
    'openai:gpt-4',
    system_prompt="""
    你是一个旅游规划助手。
    记住用户的基本信息和偏好,为他们推荐合适的旅游目的地。
    """
)

async def travel_planner():
    context = ConversationContext()

    # 第一轮:收集信息
    result1 = await agent.run(
        "我叫李明,我喜欢海边和美食,不喜欢太冷的地方"
    )
    print(f"助手: {result1.data}")

    # 更新上下文
    context.user_name = "李明"
    context.preferences = ["海边", "美食"]

    # 第二轮:基于偏好推荐
    result2 = await agent.run(
        f"你好{context.user_name},根据我的偏好推荐一个目的地吧",
        message_history=agent.messages
    )
    print(f"助手: {result2.data}")

    # 第三轮:进一步规划
    result3 = await agent.run(
        "好,那就去那里吧。帮我规划3天的行程",
        message_history=agent.messages
    )
    print(f"助手: {result3.data}")

asyncio.run(travel_planner())

案例四:流式输出处理

对于需要实时显示AI回复的应用,流式输出是必须的。

from pydantic_ai import Agent
import asyncio

agent = Agent('openai:gpt-4')

async def streaming_demo():
    print("开始流式输出:\n")

    # 使用stream方法获取流式响应
    async with agent.run_stream("写一首关于春天的诗") as response:
        # 处理流式输出
        async for chunk in response.stream():
            print(chunk, end="", flush=True)

    print("\n\n流式输出完成!")

asyncio.run(streaming_demo())

带有进度指示的流式输出:

import asyncio
from pydantic_ai import Agent

agent = Agent('openai:gpt-4')

async def streaming_with_progress():
    print("正在生成代码,请稍候...\n")

    async with agent.run_stream(
        "写一个Python函数,实现快速排序算法"
    ) as response:
        full_response = ""
        async for chunk in response.stream():
            full_response += chunk
            # 可以在这里添加进度指示
            print(f"\r生成中... {len(full_response)} 字符", end="")

    print(f"\n\n完整代码:\n{full_response}")

asyncio.run(streaming_with_progress())

常见使用场景

场景一:数据提取与转换

从非结构化文本中提取结构化数据是AI的强项:

from pydantic import BaseModel, Field
from pydantic_ai import Agent
import asyncio

class InvoiceData(BaseModel):
    invoice_number: str = Field(description="发票号码")
    date: str = Field(description="开票日期")
    amount: float = Field(description="金额")
    items: list[str] = Field(description="购买物品列表")
    vendor: str = Field(description="供应商名称")

agent = Agent(
    'openai:gpt-4',
    result_type=InvoiceData,
    system_prompt=(
        "你是一个数据提取专家。"
        "从发票文本中提取结构化信息。"
        "所有金额必须是数字格式。"
    )
)

async def extract_invoice():
    invoice_text = """
    增值税专用发票
    发票号码:FP202401150001
    开票日期:2024年1月15日
    供应商:北京科技有限公司
    购买物品:
    1. 笔记本电脑 x1台 - 5999元
    2. 无线鼠标 x2个 - 198元
    3. 机械键盘 x1个 - 399元
    合计金额:6596元(含税)
    """

    result = await agent.run(invoice_text)

    print("提取的发票数据:")
    print(f"发票号: {result.data.invoice_number}")
    print(f"日期: {result.data.date}")
    print(f"供应商: {result.data.vendor}")
    print(f"总金额: ¥{result.data.amount}")
    print("购买物品:")
    for item in result.data.items:
        print(f"  - {item}")

asyncio.run(extract_invoice())

场景二:智能问答系统

构建一个基于知识库的问答系统:

from pydantic import BaseModel
from pydantic_ai import Agent
import asyncio

class QAResult(BaseModel):
    answer: str = Field(description="回答内容")
    source: str = Field(description="答案来源")
    confidence: float = Field(description="置信度", ge=0, le=1)

class QAKnowledge:
    """知识库模拟"""

    def __init__(self):
        self.knowledge = {
            "退款政策": "我们的退货政策是收到商品后30天内可申请退货,"
                      "商品需保持原包装完好。退款将在收到退货后3-5个工作日内处理。",
            "会员权益": "成为会员后可享受:1. 全场9折优惠 2. 积分翻倍 3. 专属客服服务 4. 优先发货",
            "配送时间": "标准配送3-5个工作日,加急配送次日达,部分地区支持当日达"
        }

    def search(self, query: str) -> str:
        """搜索相关知识"""
        for key, value in self.knowledge.items():
            if key in query:
                return f"[{key}]\n{value}"
        return "抱歉,我没有找到相关的答案。"

# 初始化知识库
knowledge_base = QAKnowledge()

agent = Agent(
    'openai:gpt-4',
    result_type=QAResult,
    system_prompt=f"""
    你是一个智能客服助手,使用以下知识库回答用户问题。

    知识库内容:
    {knowledge_base.search('退款')}
    {knowledge_base.search('会员')}
    {knowledge_base.search('配送')}

    回答规则:
    1. 优先使用知识库内容回答
    2. 如果知识库没有相关信息,说明无法回答
    3. 回答要清晰、有条理
    """
)

async def qa_demo():
    questions = [
        "退货要多久能拿到钱?",
        "成为会员有什么好处?",
        "一般几天能收到货?"
    ]

    for question in questions:
        result = await agent.run(question)
        print(f"问题: {question}")
        print(f"回答: {result.data.answer}")
        print(f"来源: {result.data.source}")
        print(f"置信度: {result.data.confidence}")
        print("-" * 50)

asyncio.run(qa_demo())

场景三:代码审查助手

构建一个自动化代码审查工具:

from pydantic import BaseModel, Field
from pydantic_ai import Agent
from typing import list
import asyncio

class CodeReviewResult(BaseModel):
    issues: list[dict] = Field(description="发现的问题列表")
    suggestions: list[str] = Field(description="改进建议")
    overall_rating: str = Field(description="整体评价:good/needs_work/poor")
    score: int = Field(description="评分1-10分", ge=1, le=10)

agent = Agent(
    'openai:gpt-4',
    result_type=CodeReviewResult,
    system_prompt="""
    你是一个资深的Python代码审查专家。
    审查以下代码,关注:
    1. 代码质量和可读性
    2. 潜在的bug和安全问题
    3. 性能优化建议
    4. Python最佳实践

    请给出具体的改进建议和代码示例。
    """
)

async def code_review_demo():
    code_to_review = '''
def process_user_data(data):
    # 处理用户数据
    result = eval(data)  # 危险!
    exec(result)
    return result
    '''

    result = await agent.run(f"请审查以下Python代码:\n{code_to_review}")

    print("=" * 60)
    print("代码审查报告")
    print("=" * 60)
    print(f"\n整体评分: {result.data.score}/10")
    print(f"评价: {result.data.overall_rating}")

    print("\n发现的问题:")
    for i, issue in enumerate(result.data.issues, 1):
        print(f"  {i}. [{issue.get('severity', 'N/A')}] {issue.get('message', '')}")
        if issue.get('line'):
            print(f"     行号: {issue['line']}")

    print("\n改进建议:")
    for i, suggestion in enumerate(result.data.suggestions, 1):
        print(f"  {i}. {suggestion}")

asyncio.run(code_review_demo())

进阶技巧与最佳实践

技巧一:自定义输出验证

有时候需要更复杂的输出验证逻辑:

from pydantic import BaseModel, field_validator, Field
from pydantic_ai import Agent
import asyncio

class ValidatedEmailResult(BaseModel):
    email: str = Field(description="邮箱地址")
    is_valid: bool = Field(description="是否有效")
    suggestion: str = Field(description="如果是无效邮箱,给出更正建议")

    @field_validator('email')
    @classmethod
    def validate_email_format(cls, v):
        """验证邮箱格式"""
        import re
        pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
        if not re.match(pattern, v):
            raise ValueError(f"邮箱格式不正确: {v}")
        return v.lower()

agent = Agent(
    'openai:gpt-4',
    result_type=ValidatedEmailResult,
    system_prompt=(
        "你是一个邮箱验证助手。"
        "检查用户提供的邮箱地址是否有效。"
        "如果无效,给出合理的更正建议。"
    )
)

async def email_validation():
    test_emails = [
        "John@Example.com",
        "user+tag@gmail.com",
        "invalid-email",
        "test@company.co.uk"
    ]

    for email in test_emails:
        try:
            result = await agent.run(f"验证邮箱: {email}")
            print(f"✓ {email}")
            print(f"  有效: {result.data.is_valid}")
            if result.data.suggestion:
                print(f"  建议: {result.data.suggestion}")
        except Exception as e:
            print(f"✗ {email}: {e}")
        print()

asyncio.run(email_validation())

技巧二:并行工具调用

当有多个独立的工具调用时,可以让它们并行执行:

from pydantic_ai import Agent, Tool
import asyncio

def get_stock_price(symbol: str) -> float:
    """获取股票价格"""
    prices = {"AAPL": 175.50, "GOOGL": 140.25, "MSFT": 378.90}
    return prices.get(symbol, 0.0)

def get_company_info(symbol: str) -> str:
    """获取公司信息"""
    info = {
        "AAPL": "Apple Inc. - 全球领先的科技公司",
        "GOOGL": "Alphabet Inc. - 全球最大的搜索引擎公司",
        "MSFT": "Microsoft Corporation - 全球领先的软件公司"
    }
    return info.get(symbol, "未知公司")

agent = Agent(
    'openai:gpt-4',
    tools=[
        Tool(get_stock_price),
        Tool(get_company_info)
    ],
    system_prompt="你可以查询股票信息,请尽可能一次性获取所有需要的信息。"
)

async def parallel_demo():
    # 虽然这里表面上是顺序调用
    # 但Agent内部会根据需要决定是否并行
    result = await agent.run(
        "告诉我苹果(AAPL)、谷歌(GOOGL)和微软(MSFT)的股价和公司信息"
    )
    print(result.data)

asyncio.run(parallel_demo())

技巧三:错误处理与重试机制

from pydantic_ai import Agent
from pydantic_ai.exceptions import ModelRetry
import asyncio

agent = Agent(
    'openai:gpt-4',
    retries=3  # 设置重试次数
)

async def error_handling_demo():
    try:
        result = await agent.run("你的问题")
        print(result.data)
    except ModelRetry as e:
        print(f"模型需要重试: {e}")
    except Exception as e:
        print(f"发生错误: {e}")

技巧四:使用本地模型

对于有隐私要求或想要节省成本的场景,可以使用Ollama运行本地模型:

# 确保已安装并启动Ollama
# ollama serve
# ollama pull llama3

agent = Agent(
    'ollama:llama3',  # 使用本地模型
    system_prompt="你是一个友好的AI助手",
    # 可以指定Ollama服务的地址
    # infer_params={"base_url": "http://localhost:11434"}
)

async def local_model_demo():
    result = await agent.run("你好,请介绍一下你自己")
    print(result.data)

最佳实践总结

实践一:保持System Prompt简洁明确

# 不好的做法
agent = Agent(
    'openai:gpt-4',
    system_prompt="""
    你是一个助手。你要帮助用户。用户可能会问各种问题。
    你需要尽可能准确地回答问题。如果你不确定,就说不知道。
    不要编造信息。保持礼貌。回答要简洁但完整。
    ... 还有100行类似的废话 ...
    """
)

# 好的做法
agent = Agent(
    'openai:gpt-4',
    system_prompt="""
    你是一个数据分析助手。
    职责:分析数据,提供洞察,给出建议。
    规则:1. 只陈述有数据支撑的结论 2. 不确定时明确说明
    """
)

实践二:充分利用类型提示

from pydantic import BaseModel, Field
from typing import Optional, Literal

class SearchResult(BaseModel):
    title: str
    url: str
    snippet: str
    relevance_score: float = Field(ge=0, le=1, description="相关性评分")

class AdvancedSearchResult(SearchResult):
    published_date: Optional[str] = None
    author: Optional[str] = None

# 使用Literal限制可选值
class StatusResponse(BaseModel):
    status: Literal["success", "pending", "failed"]
    message: str

实践三:合理设置超时和重试

from pydantic_ai import Agent
import asyncio

agent = Agent(
    'openai:gpt-4',
    retries=2,
    timeout=30  # 30秒超时
)

async def with_timeout():
    try:
        result = await asyncio.wait_for(
            agent.run("复杂问题"),
            timeout=30
        )
        return result
    except asyncio.TimeoutError:
        print("请求超时,请重试")

与其他工具的集成

集成FastAPI

from fastapi import FastAPI
from pydantic_ai import Agent
from pydantic import BaseModel
import asyncio

app = FastAPI()
agent = Agent('openai:gpt-4')

class Question(BaseModel):
    text: str

class Answer(BaseModel):
    response: str

@app.post("/ask", response_model=Answer)
async def ask_question(question: Question):
    result = await agent.run(question.text)
    return Answer(response=result.data)

# 运行方式:uvicorn main:app --reload

集成Streamlit

import streamlit as st
from pydantic_ai import Agent
import asyncio

st.title("AI 助手")

agent = Agent('openai:gpt-4')

if "messages" not in st.session_state:
    st.session_state.messages = []

for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

if prompt := st.chat_input("请输入您的问题"):
    st.session_state.messages.append({"role": "user", "content": prompt})
    with st.chat_message("user"):
        st.markdown(prompt)

    with st.chat_message("assistant"):
        result = asyncio.run(agent.run(prompt))
        st.markdown(result.data)

    st.session_state.messages.append(
        {"role": "assistant", "content": result.data}
    )

集成Django

# views.py
from django.http import JsonResponse
from pydantic_ai import Agent
from pydantic import BaseModel
import asyncio

agent = Agent('openai:gpt-4')

class ChatRequest(BaseModel):
    message: str

class ChatResponse(BaseModel):
    response: str

async def chat_view(request):
    data = ChatRequest.model_validate_json(request.body)
    result = await agent.run(data.message)
    return JsonResponse(ChatResponse(response=result.data).model_dump())

常见问题与解决方案

问题一:API密钥配置

问题描述: 代码运行时报错提示找不到API密钥

解决方案:

# 方式一:使用环境变量
import os
os.environ["OPENAI_API_KEY"] = "your-key-here"

# 方式二:使用python-dotenv
from dotenv import load_dotenv
load_dotenv()  # 加载.env文件

# 方式三:直接传入
agent = Agent('openai:gpt-4', api_key="your-key-here")

问题二:模型不支持工具调用

问题描述: 某些模型返回不支持工具的错误

解决方案: 确保使用支持工具调用的模型版本

# 确认模型支持工具调用
# OpenAI: gpt-4-turbo, gpt-4, gpt-3.5-turbo (需最新版本)
# Anthropic: claude-3系列
# Google: gemini-pro (需检查版本)

# 如果必须使用不支持的模型,可以禁用工具
agent = Agent(
    'openai:gpt-3.5-turbo',
    tools=[]  # 空列表,不使用工具
)

问题三:输出格式不匹配

问题描述: Agent返回的数据无法转换为指定的Result Type

解决方案:

from pydantic import BaseModel
from pydantic_ai import Agent

class FlexibleResult(BaseModel):
    text: str
    extra_data: dict = {}  # 接收额外未定义字段

agent = Agent(
    'openai:gpt-4',
    result_type=FlexibleResult,
    system_prompt="你的回复应该是一个包含text字段的JSON对象"
)

# 或者添加验证器处理特殊情况
class RobustResult(BaseModel):
    content: str

    @classmethod
    def validate_response(cls, data):
        # 自定义验证逻辑
        if isinstance(data, str):
            return cls(content=data)
        return cls(**data)

问题四:Token使用超限

问题描述: 对话历史太长导致Token超限

解决方案:

# 限制消息历史长度
async def limited_chat(user_input: str):
    MAX_MESSAGES = 10  # 保留最近10条消息

    # 截取最近的消息
    recent_messages = agent.messages[-MAX_MESSAGES:]

    result = await agent.run(
        user_input,
        message_history=recent_messages
    )
    return result

总结与展望

核心价值回顾

Pydantic-AI为Python开发者带来了几个关键价值:

  1. 类型安全:通过Python类型注解实现Prompt、数据验证和输出的全链路类型安全
  2. 简洁API:相比LangChain等框架,API更加直观易懂
  3. 结构化输出:内置的Result Type机制解决了LLM输出格式不确定的问题
  4. 工具集成:简洁的Tool装饰器让外部函数调用变得轻而易举

适用场景

  • 构建需要返回结构化数据的AI应用
  • 开发需要调用多个外部API的Agent
  • 需要类型安全和代码自动补全的团队项目
  • 从其他框架迁移希望降低维护成本的团队

不适用场景

  • 极度简单的单次调用场景(直接调用API可能更简单)
  • 需要复杂图结构的复杂工作流
  • 对延迟极度敏感的生产环境(需要更多优化)

展望未来

Pydantic-AI作为一个新兴项目,正在快速发展。建议关注:

  • 官方GitHub仓库的更新和版本发布
  • 社区贡献的工具和插件
  • 与更多LLM提供商的集成
  • 性能优化和新特性

相关资源链接

官方资源

  • GitHub仓库:https://github.com/pydantic/pydantic-ai
  • 官方文档:https://ai.pydantic.dev/
  • PyPI页面:https://pypi.org/project/pydantic-ai/
  • Pydantic官网:https://docs.pydantic.dev/

学习资源

  • Pydantic基础:如果你不熟悉Pydantic,建议先学习其基础用法
  • Python类型提示:熟练使用typing模块能让开发更顺畅

相关项目

  • Pydantic:数据验证库,Pydantic-AI的基础
  • FastAPI:现代Web框架,与Pydantic深度集成
  • Instructor:另一个用于结构化输出的库
  • DSPy:斯坦福的编程模型框架

写在最后

Pydantic-AI代表了AI应用开发的一种新思路:将我们在传统软件开发中积累的最佳实践(如类型安全、代码验证)引入到AI领域。这不仅仅是一个技术框架,更是一种开发范式的转变。

无论你是正在构建AI应用的开发者,还是希望了解AI工程化的学习者,Pydantic-AI都值得一试。它的设计理念简单而强大:让AI开发变得像写普通Python代码一样自然。

开始动手试试吧,也许你会发现,构建AI应用从未如此简单。

祝你开发愉快!

如果内容对您有帮助,欢迎打赏

您的支持是我继续创作的动力

前往打赏页面

评论区

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注