ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Pydantic 对接大模型:强制结构化输出,实现工业数据采集零解析错误

Pydantic 对接大模型:强制结构化输出,实现工业数据采集零解析错误

上个月接了个行业竞品数据的采集需求,前同事留下的近四百行XPath与正则表达式,赶上对方官网一次前端重构,直接全线报错。通宵改了半宿解析逻辑,没过一周对方又换了个页面布局,人直接麻了。

做过网页数据提取的开发者应该都懂这种痛点:传统解析方案高度依赖页面结构,前端改个类名、调个标签顺序,之前写的逻辑基本就要重写。尤其是面对几十个不同站点的采集需求时,维护成本高到离谱,一半的开发时间都耗在适配页面变化上。

后来尝试用大模型来做内容提取,不用关心页面结构,直接把HTML丢进去就能拿到想要的字段。本以为找到了终极方案,结果踩了新的坑:大模型输出格式完全看“心情”,有时候是Markdown表格,有时候是纯文本分段,偶尔还带两句“以上是提取结果”的客套话,数据入库的时候频繁报错,清洗逻辑写得比解析代码还长。

折腾了两周之后,最终落地了一套Pydantic + 大模型的结构化输出方案,从数据模型定义到输出校验形成完整闭环,彻底解决了输出格式不稳定的问题,上线三个月解析错误率为零。今天就把这套方案的完整实现思路和踩坑经验分享出来。

一、传统数据采集方案的三大顽疾

传统基于规则的解析方案,在页面结构稳定的场景下效率很高,但面对如今频繁迭代的前端页面,短板越来越明显。

1. 强耦合页面结构,维护成本极高

XPath、CSS选择器、正则表达式,本质都是和页面DOM结构强绑定的。前端哪怕只是改了个class名、嵌套了一层div、调整了标签顺序,解析逻辑就会直接失效。

面对业务迭代频繁的站点,基本是改一次页面就要重写一次解析,长期维护成本远高于初始开发成本。很多采集项目最后不是做不出来,而是维护不动了。

2. 容错能力极差,异常数据难拦截

传统解析是“按图索骥”的逻辑,页面少个字段、多个嵌套层级,直接返回空值或者抛出异常。想要做柔性处理,就要写大量的分支判断和兜底逻辑。

尤其是遇到一些排版不规则的详情页,很容易出现字段缺失、数据错位的问题,脏数据流入下游后排查成本极高。

3. 跨场景复用性低,拓展成本高

换一个站点、换一种页面类型,之前的解析逻辑基本就用不了了。哪怕是同类型的资讯详情页,不同站点的结构天差地别,每接一个新站点都要从零开始写选择器、调正则,效率极低。

二、纯大模型解析为什么没法直接落地

大模型的语义理解能力确实降维打击了传统规则解析,但直接拿来做生产级采集,还有三个绕不开的问题。

1. 输出格式不稳定,没有统一结构

大模型的自然语言属性决定了它的输出是灵活的。同一个提示词,两次调用返回的格式可能都不一样:有时候是JSON,有时候是列表,有时候是分段纯文本,偶尔还带多余的说明文字。

这种非结构化的输出,根本没法直接落库或者交给下游程序处理,必须加一层数据清洗,而清洗逻辑的复杂度并不比传统解析低。

2. 数据类型无约束,脏数据防不胜防

比如你要的是数字类型的价格,大模型可能返回“199元”、“¥199”甚至“一百九十九元”;你要的是日期格式,它可能返回“2026年11月”、“11/3/2026”各种格式。

如果没有强制的类型约束,后续就要写大量的格式转换逻辑,而且永远不知道下一次大模型会输出什么奇葩格式。

3. 异常输出无法提前拦截,程序易崩溃

就算提示词里反复强调“只输出JSON”,偶尔还是会出现不完整的JSON、多余的Markdown代码块标记,甚至模型幻觉出来的字段。

没有校验层的话,整个采集链路很容易因为个别异常输出直接中断,生产环境的稳定性完全没法保障。

三、Pydantic + 大模型的核心原理

这套方案的核心,是把大模型的语义理解能力和Pydantic的工程化校验能力结合起来,从机制上保证输出的结构化和正确性。

现在主流大模型都支持工具调用(Function Calling)能力,本质上就是大模型可以按照你提供的JSON Schema格式,严格输出结构化数据。而Pydantic恰好是Python生态里最成熟的数据建模与校验库,可以用极简的Python类定义数据结构,自动生成标准JSON Schema,同时还能做类型转换、数据校验、默认值填充。

整个执行流程形成了完整的闭环:

  1. 用Pydantic定义目标数据模型,明确字段名、数据类型、业务规则
  2. 将Pydantic模型自动转换为JSON Schema,作为大模型的输出格式约束
  3. 大模型基于输入内容和Schema约束,输出严格符合格式的结构化数据
  4. 用Pydantic对大模型的输出做二次校验,自动完成类型转换
  5. 校验不通过的自动触发重试或降级处理,保证链路稳定

简单来说,大模型负责“理解语义提取内容”,Pydantic负责“管住格式保证规范”,两者配合刚好解决了各自的短板。

四、从零实现工业级结构化采集方案

下面直接上核心实现代码,整套方案逻辑不复杂,十几行核心代码就能跑通。

1. 环境依赖

pipinstallpydantic openai tenacity

2. 定义Pydantic数据模型

这是整个方案的核心,所有的字段约束都在这里定义。Field中的description是给大模型看的字段说明,描述越清晰,提取准确率越高。

frompydanticimportBaseModel,FieldfromtypingimportList,OptionalfromdatetimeimportdateclassArticleInfo(BaseModel):"""文章详情信息模型"""title:str=Field(description="文章的完整标题,不要截断")publish_date:Optional[date]=Field(description="文章发布日期,统一为YYYY-MM-DD格式,不存在则返回null")author:Optional[str]=Field(description="文章作者名称,不存在则返回null")content:str=Field(description="文章正文纯文本内容,去除所有HTML标签和广告内容")tags:List[str]=Field(description="文章标签列表,不存在则返回空数组")

3. 模型转Function定义

将Pydantic模型转换为大模型兼容的工具调用格式,这里以OpenAI接口格式为例,国产大模型的接口格式基本通用。

defpydantic_to_function(model:type[BaseModel])->dict:"""将Pydantic模型转换为标准函数调用定义"""schema=model.model_json_schema()return{"name":schema["title"],"description":schema.get("description",""),"parameters":{"type":"object","properties":schema["properties"],"required":schema.get("required",[])}}

4. 结构化提取核心逻辑

调用大模型时强制指定工具调用,保证输出严格符合Schema,最后用Pydantic做最终校验。

fromopenaiimportOpenAIimportre client=OpenAI(api_key="你的API_KEY",base_url="你的接口地址")defextract_structured_data(html_content:str,model:type[BaseModel])->BaseModel:"""从HTML内容中提取结构化数据"""function_def=pydantic_to_function(model)response=client.chat.completions.create(model="gpt-4o-mini",messages=[{"role":"system","content":"你是专业的数据提取助手,严格按照指定格式提取信息,不要添加任何额外说明文字。"},{"role":"user","content":f"从以下HTML内容中提取指定信息:\n\n{html_content}"}],tools=[{"type":"function","function":function_def}],tool_choice={"type":"function","function":{"name":function_def["name"]}},temperature=0)# 提取返回的JSON内容,兼容带代码块的情况raw_args=response.choices[0].message.tool_calls[0].function.arguments json_str=re.sub(r"^```json\s*|\s*```$","",raw_args.strip())# Pydantic校验并自动转换类型returnmodel.model_validate_json(json_str)

5. 增加重试兜底机制

生产环境一定要加重试,遇到校验失败的情况,把错误信息返回给大模型让它修正,大部分问题一次重试就能解决。

五、三种采集方案横向对比

我在二十个不同站点的资讯详情页上做了对比测试,结果如下:

对比维度传统XPath/正则解析纯大模型自由输出Pydantic + 大模型
单站点开发周期2-4小时15分钟20分钟
页面变更维护成本极高(基本重写)极低极低
解析准确率(结构不变)99%+95%99%+
解析准确率(结构变化)几乎为090%+90%+
输出格式合规率100%60%左右99.9%+
跨站点复用性极差极好极好

从测试结果可以看出:

  • 页面结构稳定的场景,传统方案依然是性价比最高的选择
  • 页面结构多变、多站点异构的场景,Pydantic+大模型的方案优势碾压级明显
  • 相比纯大模型方案,Pydantic只多了几行代码,但把格式合规率从60%拉到了接近100%,生产可用性直接拉满

六、踩坑实录与优化技巧

这套方案我们线上跑了三个多月,踩了不少细节的坑,这里分享几个最实用的优化点。

1. 字段描述一定要写清楚

Field里的description不是摆设,是给大模型的提示指令。比如价格字段,不要只写“商品价格”,要写“商品当前售价,纯数字,单位为元,不要包含货币符号和千分位分隔符”,能大幅减少格式错误。

2. 非必填字段一律设为Optional

页面上可能不存在的字段,一定要用Optional类型,不要设为必填。否则大模型没找到该字段时,输出的JSON会缺少字段,导致Pydantic校验失败。宁愿多做一层非空判断,也不要随便把字段设为必填。

3. 预处理HTML,降本提效

不要把完整HTML直接丢给大模型。先去掉脚本、样式、广告、导航栏、页脚这些无关内容,只保留正文区域的HTML,能减少70%以上的Token消耗,同时减少干扰信息,提升提取准确率。

4. 嵌套模型尽量扁平化

如果数据结构特别复杂,嵌套层级很深,生成的JSON Schema会很长,占用大量上下文Token。非必要的情况下尽量把模型做扁平化,或者拆分成分步提取,控制单次调用的Token成本。

5. 小模型完全够用

很多人觉得结构化输出必须用GPT-4,实际上现在主流国产模型的Function Calling能力已经非常成熟,处理常规的网页提取完全够用,成本只有GPT-4的几十分之一,批量采集的时候成本差距非常大。

6. 校验失败的智能重试

遇到校验不通过的情况,不要直接重试原请求。把Pydantic的ValidationError错误信息拼到提示词里,告诉大模型哪里错了、怎么改,再发起第二次调用,修复成功率远高于盲目重试。

七、写在最后

Pydantic + 大模型的组合,本质上是给灵活的大模型套上了工程化的“紧箍咒”,既保留了语义理解的优势,又解决了输出不可控的核心问题。

对于需要处理大量异构页面、页面结构频繁变化的采集场景,这套方案能大幅提升开发效率,降低长期维护成本。当然它也不是银弹,对于结构高度稳定的内部系统采集,传统的规则解析依然是性价比最高的选择。

技术方案没有绝对的好坏,适合场景的才是最好的。

返回列表