page contents

FastAPI 背后的 Pydantic 团队,把 AI Agent 做顺手了:PydanticAI 是真的夯!

PydanticAI 最让我舒服的地方,就在这里:它没把 Agent 包装成什么玄学工作流,而是继续用 Python 工程师熟悉的类型、函数和依赖注入来管模型。

attachments-2026-08-asaebRc86a73e3b99d4d3.png模型明明答对了,程序还是崩了。

日志往下一翻:

KeyError: 'severity'

提示词里要求返回 severity,模型偏偏给了个 level。再跑一次,字段又变成 risk_level。这种代码我一般不敢直接接业务,模型回答得再像人,到了程序里也只是一段不太稳定的字符串。

PydanticAI 最让我舒服的地方,就在这里:它没把 Agent 包装成什么玄学工作流,而是继续用 Python 工程师熟悉的类型、函数和依赖注入来管模型。

官方对它的定位很直接:用于构建生产级生成式 AI 应用和工作流的 Python Agent 框架。现在的 API 已经围绕类型安全、结构化输出、工具调用、依赖注入和可观测性展开。

装起来倒没什么花活:

uv add pydantic-ai

我拿一个线上告警分析场景写段代码。不是让模型自由发挥,而是让它查 trace、看执行计划,最后老老实实返回一个固定结构。

import os
from dataclasses import dataclass
from typing import Literal

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


@dataclass
class OpsContext:
    service_name: str
    traces: dict[str, list[str]]
    sql_plans: dict[str, str]


class TriageReport(BaseModel):
    level: Literal["P1", "P2", "P3"]
    suspect: str = Field(min_length=3)
    evidence: list[str] = Field(min_length=1, max_length=5)
    next_check: str


triage_agent = Agent(
    os.getenv("AI_MODEL", "openai:gpt-5.2"),
    deps_type=OpsContext,
    output_type=TriageReport,
    instructions=(
        "你负责分析线上接口超时。"
        "只能根据工具返回的数据判断,禁止补造日志。"
        "证据不足时直接说明缺什么。"
        "不要给出更新、删除等写操作。"
    ),
)


@triage_agent.tool
def load_trace(ctx: RunContext[OpsContext], trace_id: str) -> str:
    """读取指定 trace 的关键调用片段。"""
    rows = ctx.deps.traces.get(trace_id)
    if not rows:
        return "TRACE_NOT_FOUND"

    return "\n".join(rows[:20])


@triage_agent.tool
def load_sql_plan(ctx: RunContext[OpsContext], fingerprint: str) -> str:
    """读取慢 SQL 对应的只读执行计划。"""
    return ctx.deps.sql_plans.get(fingerprint, "PLAN_NOT_FOUND")


deps = OpsContext(
    service_name="order-api",
    traces={
        "tr-82af": [
            "POST /orders cost=2184ms status=200",
            "inventory.check cost=41ms status=200",
            "order_repo.query cost=2076ms status=200",
            "sql_fingerprint=order_list_v3",
        ]
    },
    sql_plans={
        "order_list_v3": (
            "Seq Scan on orders "
            "rows=184320 filter=(tenant_id=42 and status='PAID')"
        )
    },
)

result = triage_agent.run_sync(
    "分析 trace_id=tr-82af,给出下一步排查动作。",
    deps=deps,
)

print(result.output.model_dump_json(indent=2))

这段代码里,真正值钱的不是 Agent(),而是那几个看起来很普通的类型。

OpsContext 管依赖。数据库连接、HTTP Client、租户信息、配置中心客户端,都可以从这里传进去,不用塞全局变量,也不用把密钥拼进提示词。

load_trace 和 load_sql_plan 是工具。模型只能决定什么时候调用,真正查数据的代码仍然握在我们手里。工具参数会根据 Python 类型生成 schema,传错参数也不会悄悄混过去。

TriageReport 则卡住最终输出。业务代码拿到的不再是一坨 Markdown,而是确定的 level、suspect、evidence 和 next_check。result.output 的类型也能被 IDE 和类型检查器识别。

这才是我觉得它夯的地方。

不少 Agent 框架写到后面,代码会变成提示词套提示词,链路绕得像低代码平台导出的 JSON。PydanticAI 没急着发明另一套编程语言。普通函数还是普通函数,数据模型还是 Pydantic,异步调用还是 await,依赖也没有藏到什么神秘容器里。

换模型也比较克制。官方已经内置支持 OpenAI、Anthropic、Gemini、Bedrock、Groq、Mistral、OpenRouter 等多种提供方,很多兼容 OpenAI API 的服务也可以接入。模型名称抽成环境变量以后,业务代码通常不用跟着重写。

另外两个点也挺实在。

一个是测试。PydanticAI 自带 TestModel 和 FunctionModel,单元测试没必要每次真烧 Token。另一个是可观测性,它可以结合 Logfire,也会输出符合语义约定的 OpenTelemetry 数据。Agent 到底调用了几次模型、工具卡在哪里、Token 花到哪一步,不至于全靠猜。

不过别把框架当保险箱。

工具的权限、超时、幂等、重试和审计,还是得自己做。尤其是退款、发券、改库存这类写操作,我不会因为用了 PydanticAI 就直接放给模型。模型负责判断,程序负责边界,这条线不能混。

PydanticAI 没有解决模型不稳定的问题,它只是把这种不稳定关进了类型、工具和验证规则里。

对 Python 项目来说,这比再造一套花里胡哨的 Agent DSL 靠谱多了。

更多相关技术内容咨询欢迎前往并持续关注好学星城论坛了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

 

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

2307 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. Pack 2307 文章
  3. 小柒 2228 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章