Pydantic AI 教学:用型别安全的方式打造 LLM Agent,从第一支程式到上线

做过 LLM 应用的人都知道,最痛的不是接 API,而是模型回传的东西每次都长得不一样。Pydantic AI 把 Pydantic 的型别验证搬进 Agent 开发,让输出有结构、工具能被静态检查。这篇带你从安装写到进阶,附上我自己踩过的坑。

前言:模型回传的东西,为什么每次都不一样

如果你接过 LLM 的 API,大概对这个画面不陌生:你叫模型「回传一个 JSON,里面要有 name 跟 score」,前九次都好好的,第十次它在 JSON 前面加了一句「好的,以下是结果:」,你的 json.loads() 当场炸掉。然后你开始写一堆正规表达式去清字串,再写一堆 if 去检查栏位在不在——最后你的「AI 应用」有七成程式码是在跟模型的不稳定输出搏斗。

我自己维护过一个内部分类服务,光是处理模型偶尔少给一个栏位这件事,就让我加了三天班。后来换成 Pydantic AI,那段防御性程式码几乎全删掉了,因为验证这件事框架直接帮你扛。这篇就讲清楚它怎么用。

Pydantic AI 是什么

Pydantic AI 是 Pydantic 团队出的 Python Agent 框架。Pydantic 这个名字你可能在别的地方看过——OpenAI、Anthropic、Google 的官方 SDK,还有 LangChain、LlamaIndex,底层的资料验证很多都靠它。换句话说,做验证这件事,他们是这个生态系里最有资格的人。

它的设计哲学很像 FastAPI:用 Python 原生的型别注记(type hints)把行为定义清楚,剩下的交给框架。核心只有几个概念——Agent(代理)、Tools(工具)、Dependencies(依赖注入)、Structured Output(结构化输出)。你不需要去背一堆它自己发明的抽象类别,写起来就是普通 Python,但 IDE 会给你自动补全,型别检查器(像 Pyright、mypy)能在你还没跑程式前就抓到错。

它是 model-agnostic 的,也就是不绑特定模型厂商。OpenAI、Anthropic、Google、Groq、Cohere、Mistral、Ollama 等十几家都支援,要换模型通常只改一个字串。想搞懂 Agent 跟一般 API 呼叫差在哪,可以先看什么是 AI Agent

能拿来做什么

讲白话,凡是「需要模型回传可信赖结果」的场景它都合适:

  • 结构化抽取:把一封客诉信丢进去,要它吐出 情绪类别急迫度 三个栏位,而且保证型别正确。
  • 分类与标注:大量文件要打标签,输出限定在你定义的 Enum 里,模型乱回答会被挡下来。
  • 工具型 Agent:让模型能呼叫你的函式——查资料库、打天气 API、算数学,框架负责把函式的型别转成模型看得懂的工具描述。
  • RAG 问答:搭配向量检索,做出有依据的问答系统,这部分可以参考我们的 RAG 实作指南

比起 LangChain 那种什么都包的大框架,Pydantic AI 刻意做得很薄。如果你只是要把模型输出变可靠,不想为了一个小功能背一整套生态系,它的学习曲线会友善很多。

怎么用:第一次上手

1. 安装

pip install pydantic-ai

建议开一个虚拟环境。Python 版本用 3.9 以上比较安全。

2. 设定 API 金钥

以 Anthropic 为例,设个环境变数:

export ANTHROPIC_API_KEY=你的金钥

用 OpenAI 就设 OPENAI_API_KEY,以此类推。

3. 写第一支 Agent

from pydantic_ai import Agent

agent = Agent('anthropic:claude-sonnet-4-6')
result = agent.run_sync('用一句话解释什么是向量资料库')
print(result.output)

第一个参数就是模型名称,格式是 厂商:模型。要换成 OpenAI 就改成 'openai:gpt-4o' 之类,其他程式不用动——这就是 model-agnostic 的好处。

4. 让输出有结构

这才是重点。你定义一个 Pydantic 模型当输出格式:

from pydantic import BaseModel
from pydantic_ai import Agent

class Review(BaseModel):
    sentiment: str   # positive / negative / neutral
    score: int       # 1 到 5
    summary: str

agent = Agent('anthropic:claude-sonnet-4-6', output_type=Review)
result = agent.run_sync('这家店东西好吃但等了快一小时,有点夸张')
print(result.output.score)     # 直接拿到整数,不用自己 parse
print(result.output.sentiment) # 直接拿到字串

模型回的东西如果不符合 Review 的型别,框架会自动把错误讯息丢回去叫模型重试。你拿到 result.output 时,它已经是一个验证过的 Python 物件,IDE 还会帮你补全栏位。

5. 给 Agent 一个工具

from pydantic_ai import Agent

agent = Agent('anthropic:claude-sonnet-4-6')

@agent.tool_plain
def get_weather(city: str) -> str:
    """查询指定城市目前的天气"""
    return f'{city} 现在 28 度,晴'

result = agent.run_sync('台北现在天气如何?')
print(result.output)

那个 docstring 不是写好看的——它会变成模型看到的工具说明。函式的型别注记(city: str)也会被转成模型理解的参数规格,参数同样经过 Pydantic 验证。

进阶技巧

依赖注入是它最被低估的功能。你可以透过 RunContext 把资料库连线、使用者身分、API client 这些东西型别安全地传进 Agent 跟工具里:

from dataclasses import dataclass
from pydantic_ai import Agent, RunContext

@dataclass
class Deps:
    user_id: int
    db: object  # 你的资料库连线

agent = Agent('anthropic:claude-sonnet-4-6', deps_type=Deps)

@agent.tool
def get_orders(ctx: RunContext[Deps]) -> str:
    return f'查询使用者 {ctx.deps.user_id} 的订单'

写测试时,把 db 换成假的物件就行,不用碰真资料库,这对写单元测试太重要了。

串流(streaming):要做即时打字效果,用 agent.run_stream(),它会边产生边验证结构化输出,使用者体验好很多。

观测性:Pydantic AI 跟同团队的 Logfire 整合得很顺。接上去之后,每一次模型呼叫、每一个工具触发、花了多少 token、跑了几秒,全都看得到。LLM 应用最难 debug 的就是「模型到底为什么这样回」,有了这个就不用瞎猜。想把 Agent 规划得更完整,搭配我们的 Agent 开发指南 一起看。

常见错误与注意事项

  • 以为加了 output_type 就 100% 安全:框架会在验证失败时叫模型重试,但重试是有上限的。一直失败它会丢例外,你还是得 try/except。型别验证减少的是「脏资料溜进系统」,不是「模型永远不出错」。
  • 工具的 docstring 随便写:模型完全靠 docstring 判断什么时候该呼叫工具。写得含糊,模型就乱呼叫或不呼叫。把它当成写给模型看的使用说明书。
  • 在工具里塞太多逻辑却不处理例外:工具里的程式码出错,讯息会被回传给模型,模型可能绕着错误打转烧掉一堆 token。该挡的例外自己挡好。
  • 忽略成本:结构化输出重试、工具多轮呼叫,token 消耗比你想的快。上线前一定要接观测,看清楚实际花费。
  • 把它当大框架用:它刻意做得薄。如果你需要复杂的多步骤编排、现成的一堆连接器,可能 LlamaIndex 或别的方案更省事,别硬凑。

TheAI学院 评语

老实说,市面上 Agent 框架多到让人选择障碍,但 Pydantic AI 解决的是一个非常具体、每个 LLM 开发者都中过招的痛:输出不可靠。它没有想做「全宇宙最强框架」,就是把「型别安全」这件 Python 圈本来就在乎的事,干净地搬进 AI 开发。对已经习惯 FastAPI、Pydantic 写法的人,几乎没有学习成本。

它不会让你的模型变聪明,但会让你的程式码变可靠——而后者才是上线后真正救你的东西。

如果你是要做 demo、玩玩看,可能感受不深;但只要你的东西要真的上线、要被人用、要长期维护,型别安全跟可观测这两件事的价值会一天比一天明显。

资料来源

常见问题

Pydantic AI 跟 LangChain 差在哪,该选哪个?

最大差别是「重量」。LangChain 是大型生态系,连接器、整合、抽象层都很多,适合需要复杂编排的大型专案,但学习曲线陡。Pydantic AI 刻意做得薄,核心只有 Agent、工具、依赖注入、结构化输出几个概念,主打型别安全。如果你的需求是「让模型输出变可靠、写法贴近原生 Python」,Pydantic AI 上手快很多;如果你需要大量现成整合,LangChain 比较省事。两者不冲突,看专案规模选。

一定要用 OpenAI 的模型吗?可以接本地模型吗?

不用。Pydantic AI 是 model-agnostic,支援 OpenAI、Anthropic、Google、Groq、Mistral、Cohere、Ollama 等十几家,换模型通常只改建立 Agent 时那一个字串。要跑本地模型可以透过 Ollama,把模型字串指向本地服务即可,程式其他部分不用动。

结构化输出真的能保证模型不乱回吗?

不能保证模型本身不出错,但能保证「不符合你定义型别的资料不会溜进系统」。当模型回的东西通不过 Pydantic 验证,框架会自动把错误讯息丢回去要它重试。不过重试有次数上限,持续失败会丢例外,所以你还是要用 try/except 处理最坏情况。它降低的是脏资料风险,不是模型的智商问题。

新手没写过 Pydantic,学这个会很难吗?

如果你会基本 Python 跟型别注记(type hints),门槛不高。Pydantic 的核心就是「用 class 定义资料长什么样」,写法很直觉。建议先花十分钟看一下 Pydantic 怎么定义 BaseModel,再回来写 Agent,会顺很多。真正的观念门槛反而是 Agent 跟工具的设计思路,可以搭配我们的 Agent 开发指南一起学。

繁體中文版 →