首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >从结构化输出到 Tool,把大模型变成可调用零件

从结构化输出到 Tool,把大模型变成可调用零件

原创
作者头像
dsy
发布2026-08-11 15:46:22
发布2026-08-11 15:46:22
1160
举报

上篇我解决了第一道坎:让大模型的输出从一段自然语言,变成能直接 .name.skills 的 Python 对象。

但上线跑了几条真实简历后,第二道坎立刻出现了。

模型返回了这么一个 skills:

代码语言:python
复制
["Python", " ", "Flink"]   # 中间混了个空格字符串

还能返回更离谱的——简历里压根没写姓名,它却给你编一个。

那一刻我意识到:光让输出"长得像对象"不够,还得让这个对象"干净、可信、能干活"。今天这篇就是顺着这道坎,从结构化输出一路走到 Tool,把大模型真正变成一个可约束、可校验、可复用的工程零件。

一、为什么是 PydanticAI,而不是别的框架

一开始我也有疑问:Agent 框架这么多,凭啥先学这个?

今天补了一个系统视角。我把几个主流框架摆在一起比,差别一下就清楚了:

框架

更关注什么

PydanticAI

类型安全、结构化输出、Pythonic 工程化

LangGraph

多步骤流程、状态机、复杂 Agent 编排

CrewAI

多角色协作,像一个虚拟团队

LangChain

生态组件多,适合 RAG 和各种集成

OpenAI Agents SDK

OpenAI 生态内的 Agent、工具、追踪

这张表治好了我的选择困难:它们不是谁比谁强,是关注点不同。

我当下最需要的,是把 LLM 输出变成稳定的 Python 对象,而不是一上来就搞多 Agent 流程编排。这就好比学开车,我现在该先把手动挡的离合油门踩明白,而不是急着学车队调度。

所以先 PydanticAI,等哪天真要做复杂编排,再去碰 LangGraph。

二、field_validator:模型的"事后验收员"

上篇的 ResumeSummary 只定义了字段,今天我加了一道清洗关:

代码语言:python
复制
from pydantic import BaseModel, Field, field_validator

class ResumeSummary(BaseModel):
    name: str = Field(description="候选人姓名")
    summary: str = Field(description="一句话总结候选人的核心背景")
    skills: list[str] = Field(min_length=1, description="从简历中抽取出的技能")

    @field_validator("skills")
    @classmethod
    def clean_skills(cls, skills: list[str]) -> list[str]:
        return [skill.strip() for skill in skills if skill.strip()]

关键点:clean_skills 不是你手动调用的,而是 Pydantic 在创建对象时自动触发。

当我写下:

代码语言:python
复制
ResumeSummary(
    name="李明",
    summary="资深数据工程师,具备 AI 和数据平台经验。",
    skills=["Python", " ", "Flink"]
)

Pydantic 会自动跑校验,把 ["Python", " ", "Flink"] 清洗成 ["Python", "Flink"]

它干两件事:

  • 清洗数据:去掉空格、空串,normalize。
  • 拒绝错误数据:如果进来的是 [" ", ""],清洗完变空列表,而 min_length=1 会直接报错拒绝。

这正好是上一篇说的"业务契约"的执行层——契约不光要写,还要有人查

三、@classmethod 到底是啥:厨房还在备料时,先让厨房处理

@classmethod@field_validator 绑在一起,第一次看容易懵。今天我想通了一个记法:

  • 普通方法用 self —— 对象已经存在,你在对做好的菜下指令。
  • 类方法用 cls —— 对象还没完全生成,厨房还在备料,先让"厨房"(类)来处理。

Pydantic 的校验就发生在"对象正在被创建"的过程里,对象本身还没出生,所以只能用 cls,不能用 self

一句话:self 是成品,cls 是产线。

四、三层约束:事前、结构、事后

今天最值钱的一个认知,是把三种约束摆成了时间线:

层级

角色

作用

instructions

事前约束

规定模型"该做什么、边界在哪"

output_type

结构约束

规定输出"长什么样"

field_validator

事后验收

规定数据"干不干净、合不合规"

模糊的写法长这样:

代码语言:python
复制
instructions="帮我分析一下简历"

工程化的写法要包含角色 + 任务 + 规则 + 边界:

代码语言:python
复制
instructions=(
    "你是一个简历结构化助手。"
    "任务是从简历原文抽取姓名、职业背景总结和技能清单。"
    "必须严格基于原文,不允许编造原文没有的信息。"
    "skills 必须是技能词列表,不要写成一整句话。"
)

instructions 管住模型的嘴,output_type 管住输出的形,validator 管住数据的质。三层叠起来,模型的自由发挥就被框在了一个可信区间里。

五、让 AI 学会说"我不知道"

真实简历经常缺字段。以前我会担心:模型会不会硬编一个名字填进去?

今天我把"不确定性"也结构化了:

代码语言:python
复制
class ResumeSummary(BaseModel):
    name: str
    confidence: float
    missing_fields: list[str]

当简历里没有姓名时,它不该编造,而是输出:

代码语言:json
复制
{
  "name": "unknown",
  "missing_fields": ["name"],
  "confidence": 0.65
}

这背后是一条很重要的工程原则:

AI 系统不应该假装自己什么都知道,它应该能结构化地表达"不知道"。

confidencemissing_fields 就是把"不确定"从模型的脑子里,搬到了你的程序能读到的字段里。下游代码看到 missing_fields 非空,就知道该走人工补录,而不是拿假数据入库。

六、工程化第一步:把脚本变成函数

今天我自己总结了最关键的一句话:

工程化的第一步,是把脚本逻辑提炼成函数。

能跑的脚本长这样:

代码语言:python
复制
result = agent.run_sync(resume_text)
print(result.output)

这只是 Demo——一次性、不可复用。把它提成函数,性质就变了:

代码语言:python
复制
def analyze_resume(resume_text: str) -> ResumeSummary:
    result = agent.run_sync(resume_text)
    return result.output

这个函数之后可以被四样东西复用:

  • 命令行调用
  • pytest 写单测
  • FastAPI 暴露成接口
  • 前端或其他模块直接 import

能跑起来只是 Demo,能被函数调用才开始接近工程。 这句话我打算贴在显示器上。

七、Tool:让 Agent 调用确定性的 Python 函数

最后到了今天的高潮——Tool。

核心思想一句话:把普通 Python 函数注册给 Agent,让它在需要时自己调用。

代码语言:python
复制
@agent.tool_plain
def classify_skill(skill: str) -> str:
    ...

有了它,Agent 不用自己猜"Python 算后端还是数据技能",而是直接调用这个确定性函数去判。

我一口气挂了三个:

  • classify_skill(skill) —— 技能分类
  • score_project(project_text) —— 项目质量评分
  • detect_resume_risk(resume_text) —— 简历风险识别

这里的关键分工是:

Agent 负责理解和组织任务,Python 函数负责确定性判断。

需要想通一个容易踩的坑:你挂了 score_project,但最终 JSON 里却看不到评分。为什么?因为 Tool 不是输出字段,它是 Agent 可以调用的能力。只有当三件事同时满足,评分才会进结果:

  1. Agent 真的调用了它;
  2. 输出模型里有字段接收它的结果;
  3. instructions 明确要求把评分写回结果。

所以 Tool 像工具箱里的扳手——你带了不等于在用,用不用、用完放不放进收纳盒,是两码事。

总结

今天的核心闭环串起来是这样一条链:

BaseModel 定义结构 → Field 定义字段约束 → field_validator 清洗校验 → instructions 约束行为 → missing_fields 表达不确定 → 函数封装让代码可复用 → Tool 让 Agent 调用 Python 函数。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、为什么是 PydanticAI,而不是别的框架
  • 二、field_validator:模型的"事后验收员"
  • 三、@classmethod 到底是啥:厨房还在备料时,先让厨房处理
  • 四、三层约束:事前、结构、事后
  • 五、让 AI 学会说"我不知道"
  • 六、工程化第一步:把脚本变成函数
  • 七、Tool:让 Agent 调用确定性的 Python 函数
  • 总结
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档