
先看清"能跑"和"能干活"的差距
很多人第一次写 Agent 都有点失望:代码能跑,模型在说话,但往下两步就露馅——返回一段字符串,自己 json.loads(),还得写一堆兜底防它今天给 JSON 明天给 Markdown;想让它查业务数据,就手写一段 prompt 解释"我有个函数叫 xxx",prompt 越写越长;想写单元测试,发现全局变量和写死路径根本没法测。
问题不在模型不聪明,在中间缺一层工程约束。Pydantic AI 干的就是这层活:把类型注解、Pydantic 模型、依赖注入直接接到大模型上,让"输出必须是这个结构""这个函数可以被调用"在代码层面被约束住,而不是靠 prompt 恳求。

概念一句话分清:调一次接口问一句答一句叫用大模型;Agent 多出来的是自己决定下一步——调函数、拿结果、继续想、交出校验过的对象。少这个循环,它就是个会说话的字符串生成器。
五行跑通,先确认模型能说话
pip install "pydantic-ai" pandas numpy
export OPENAI_API_KEY="sk-xxxx"
from pydantic_ai import Agent
from pydantic_ai.models.openai import OpenAIChatModel
from pydantic_ai.providers.openai import OpenAIProvider
MODEL = OpenAIChatModel(
"deepseek-ai/DeepSeek-V3.2",
provider=OpenAIProvider(base_url="https://api.siliconflow.cn/v1",
api_key=os.getenv("OPENAI_API_KEY")),
)
agent = Agent(MODEL, instructions="你是一名个人财务分析助手。")
print(agent.run_sync("什么是大语言模型?").output)

run_sync 是同步版;塞进 FastAPI 换 await agent.run(...),API 面完全一样。
收益最大的一步:让模型交出对象
给 Agent 指定 output_type,用 Pydantic 模型描述结构,框架全包后续:生成 JSON Schema、随请求发出、回包校验、不过就带报错让模型重答。
class SpendingInsight(BaseModel):
period: str = Field(description="分析区间,形如 2026-09-01 ~ 2026-09-30")
headline: str = Field(description="一句话结论,把数字带上")
top_categories: list[str] = Field(description="花钱最多的前三个类别,金额降序")
anomalies: list[str] = Field(default_factory=list, description="异常消费,没有就空列表")
confidence: float = Field(description="可信度 0-1", ge=0.0, le=1.0)
ledger_agent = Agent(MODEL, instructions="你是一名个人财务分析助手。",
output_type=SpendingInsight)

每个字段的 Field(description=...) 不是注释——它会进发给模型的 Schema,描述写得越准,重试次数越少。这份模型就是接口契约:下游写 Excel 也好、推送消息也好,拿到的永远是同一套字段。重试上限用 retries 参数控制,默认值下模型反复答错会抛 UnexpectedModelBehavior,生产上要显式设并接住。
给它一双手:工具注册的两条纪律
@ledger_agent.tool
def get_metrics(ctx: RunContext[LedgerRepo], start: str, end: str,
categories: list[str] | None = None) -> str:
"""查询一段日期内的支出总额、笔数、单笔均值,自动与上一等长周期环比。
Args:
start: 开始日期,YYYY-MM-DD。
categories: 消费类别列表,不传表示全部。
"""
return json.dumps(ctx.deps.query_metrics(start, end, categories), ensure_ascii=False)
纪律一:docstring 是给模型看的调用说明书,不是给人看的注释,写得含糊它就在错误的时机乱调。纪律二:所有计算放进工具里——环比、占比、均值必须 Python 算好再返回,模型只负责读数字、说人话。LLM 做算术是不可靠的,这一点永远不要赌。

还有一笔成本账:每次工具调用都是一次模型请求,工具和模型之间可能来回好几轮。工具切得太碎,钱和时间按轮数往上翻。设计原则是单次调用给足信息,宁可一个工具返回多字段,不要让模型为了凑齐上下文连调四五次。
依赖注入:为了测试,不只是为了优雅
数据源收进一个数据类,Agent 声明 deps_type=LedgerRepo,运行时塞实例:
repo = LedgerRepo(frame=pd.read_csv("bill.csv", parse_dates=["date"]))
result = ledger_agent.run_sync("上个月我花超了吗?", deps=repo)
insight = result.output # SpendingInsight 实例,直接 .headline .top_categories
回报在测试时立刻兑现——不改 Agent 任何定义,with ledger_agent.override(deps=build_mock_repo()) 换 mock 跑离线用例;想连模型调用都省掉,官方的 TestModel / FunctionModel 同样 override 掉 model,单测稳定、零 token 成本。这一条是 Pydantic AI 相对裸写 Agent 最大的工程价值。
能力开关与验收
新版框架把可复用行为做成 capabilities 组合挂载:capabilities=[Thinking(effort="high"), WebSearch()],前者适合"消费为什么涨"这类归因,后者给外部视角。日常建议都关——算数已交给 Python,开思考多花钱,联网还可能被无关内容带偏。注意这套 API 是较新版本才有的,旧版代码里找不到 capabilities 参数就先升级包再对照。
整条链路验收清单:output_type 校验失败能自动重试且可设上限;工具 docstring 修改后模型调用行为可感知变化;override mock 下单测零联网通过;真实账单跑一轮,输出的每个字段类型合法。四条全绿,这个 Agent 才算"能干活"。

评论0