十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Pydantic Is Still All You Need:Instructor 一年结构化输出实践回顾与源码解析

Pydantic Is Still All You Need:Instructor 一年结构化输出实践回顾与源码解析 Pydantic Is Still All You NeedInstructor 一年结构化输出实践回顾与源码解析【免费下载链接】instructorstructured outputs for llms项目地址: https://gitcode.com/GitHub_Trending/in/instructor本文是 Instructor 作者在提出 Pydantic: All You Need 一年后的回顾性技术指南系统复盘了用 Pydantic 函数调用Function Calling驱动 LLM 结构化输出的核心方法论为什么非结构化输出不可靠、为什么response_model三行代码一年不变、以及 Streaming、Partials、Validators 三大关键特性如何让 LLM 应用具备传统软件工程的可靠性。读完本文你将掌握 Instructor 结构化输出的完整心智模型、可落地的代码示例以及这些能力在仓库源码层面的实现原理。非结构化输出的代价为什么要为 LLM 定义 Schema想象一下你雇佣一个实习生去写一个 API它返回一串字符串你不得不用json.loads把它解析成字典然后祈祷数据还在。遇到这种情况你大概率会立刻解雇他换 GPT 来做。然而很多人对待 LLM 的方式恰恰如此——让模型自由输出文本再事后去解析、猜测、容错。文章作者用一个直白的类比点出了核心问题不使用 Schema 和结构化响应我们在构建与外部系统交互的工具时就会丧失可组合性Composability、兼容性Compatibility与可靠性Reliability。不兼容输出的字段名、类型、嵌套结构不稳定无法与其他系统直接对接不可组合下游函数无法把模型的输出当作类型安全的参数直接使用不可靠字段缺失、格式漂移、幻觉数据无法在运行时被自动捕获。而这一切在 Instructor 中由 Pydantic 模型一劳永逸地解决——LLM 的输出在返回给业务代码之前就已经是一个经过校验的、类型安全的 Python 对象。核心 API一年不变的三行代码文章反复强调一个反直觉的事实一年过去了核心 API 几乎没有变化。这本身就是设计稳定性的最好证明from instructor import from_openai client from_openai(OpenAI()) response client.create(modelgpt-5.4-mini, response_modelUser, messages[...])整个模式就是用 Pydantic 定义 Schema把模型作为response_model传给create拿到一个类型安全的对象。response_model参数在官方概念文档 Response Model 中被明确为三个职责定义 Schema 与提示词为语言模型生成符合结构的响应提供指引校验 API 响应对返回内容做类型与约束检查返回 Pydantic 模型实例业务代码拿到的是可直接使用的对象而不是裸字符串。从当前仓库源码看这一设计在生态层面同样稳定instructor/__init__.py中导出Instructor、AsyncInstructor、from_openai、from_provider等入口并且__version__ 1.17.1所有 Provider 客户端from_anthropic、from_gemini、from_mistral、from_cohere、from_bedrock、from_vertexai等通过懒加载Lazy Import按需引入未安装对应 SDK 时不会阻塞核心导入。这意味着「定义模型 → 调用 create → 得到对象」这条核心链路从 v1.0 至今都没有发生过破坏性变化。提示即 SchemaDocstring 与字段描述response_model不仅能约束输出结构本身还承担了提示词工程的角色。在 Response Model 中模型类的 docstring 和字段的Field(description...)都会被拼接进发给 LLM 的提示中from pydantic import BaseModel, Field import instructor class User(BaseModel): This is the prompt that will be used to generate the response. Any instructions here will be passed to the language model. name: str Field(descriptionThe name of the user.) age: int Field(descriptionThe age of the user.) client instructor.from_provider(openai/gpt-4o-mini) user client.create( response_modelUser, messages[{role: user, content: Extract: John is 30 years old}], )Docstring、类型注解与字段描述共同生成了发给模型的提示create方法再据此生成响应并完成校验。这正是Pydantic is all you need的第一层含义你不需要另学一套提示工程语言Pydantic 模型本身既是数据契约又是提示模板。模型即 Schema嵌套、验证、动态创建与行为嵌套模型与模块化Pydantic 天然支持嵌套对象与列表这让复杂业务结构可以像普通 Python 类一样模块化组织。例如会议信息提取中一个MeetingInfo模型可以嵌套List[User]子模型见 Streaming Partial ResponsesLLM 返回的层级结构会被完整校验并实例化。验证器让系统更可靠Pydantic 的验证框架是 Instructor 可靠性的基石。它提供了类型检查、数据强制转换coercion、自定义验证器与字段约束见 Validation例如from pydantic import BaseModel, Field, field_validator class User(BaseModel): name: str Field(..., min_length2, descriptionUsers full name) age: int Field(..., ge0, le150, descriptionUsers age) field_validator(emails) classmethod def validate_emails(cls, v): if not all( in email for email in v): raise ValueError(Invalid email format) return v动态模型创建运行时按需生成 Schema模型不一定是静态写死的。借助 Pydantic 的create_model可以在运行时根据配置或数据库动态构建模型Response Modelfrom pydantic import BaseModel, create_model, Field from typing import List types { string: str, integer: int, boolean: bool, number: float, List[str]: List[str], } # 假设从数据库查询得到字段定义 cursor [ (name, string, The name of the user.), (age, integer, The age of the user.), (email, string, The email of the user.), ] User create_model( User, **{ property_name: (types[property_type], Field(descriptiondescription)) for property_name, property_type, description in cursor }, __base__BaseModel, ) print(User.model_json_schema())这种能力让同一套代码可以为不同用户、不同场景动态定制输出结构——例如数据库表按model_name存储属性定义查询后即时生成模型。给模型添加行为Pydantic 模型就是普通 Python 类可以自由添加方法把提取结果直接变成可执行的领域对象from pydantic import BaseModel from typing import Literal import instructor client instructor.from_provider(openai/gpt-4.1-mini) class SearchQuery(BaseModel): query: str query_type: Literal[web, image, video] def execute(self): print(fSearching for {self.query} of type {self.query_type}) return Results for cat query client.create( modelgpt-4.1-mini, messages[{role: user, content: Search for a picture of a cat}], response_modelSearchQuery, ) results query.execute()这也是 Instructor 设计哲学的一部分见 Philosophy不发明新抽象只把 LLM 的输出接回你已有的 Python 代码。生态演进一年里发生了什么回顾过去一年文章作者分享的进展均为原作者对自身生态的表述包括发布 1.0 版本、扩展为多语言生态Python、TypeScript、Ruby、Go、Elixir、构建 Rust 版本等。对当前仓库而言最可验证的变化是Provider 支持面的持续扩大。文档中提到只要语言模型支持函数调用Function Calling这套 API 就始终是标准。如今仓库的 integrations 文档 已覆盖Ollamadocs/integrations/ollama.md、llama-cpp-pythondocs/integrations/llama-cpp-python.md、Anthropicdocs/integrations/anthropic.md、Coheredocs/integrations/cohere.md、Googledocs/integrations/google.md、Vertex AIdocs/integrations/vertex.md以及 Bedrock、Mistral、Groq、Perplexity、xAI、DeepSeek、Fireworks、Cerebras、Writer 等数十家 Provider。从源码看instructor/__init__.py这些from_*工厂函数全部通过_add_optional_export做可选依赖探测只有对应 SDK如anthropic、google.generativeai、cohere、boto3等已安装时才注册导出。这意味着统一的核心 API 之上可以无痛接入任意兼容函数调用的模型后端——response_model这一层抽象正是跨 Provider 稳定性的来源。关键特性一流式输出与 PartialsStreaming with Structure边生成边用结构化输出的一个常见痛点是必须等完整 JSON 返回才能解析。Instructor 的流式能力解决了这个问题对象在生成过程中即可被消费在保持结构化输出的同时显著降低首字节延迟。Partials实时渲染的增量对象所谓 Partial是指利用create_partial动态生成一个新类把原模型的所有字段都变成Optional从而在流式过程中也能构造出半成品对象。以下面的User模型为例from pydantic import BaseModel class User(BaseModel): name: str age: int如果直接流式接收 OpenAI 的 JSON只能在整个对象返回完成后解析{name: Jo {name: John, ag {name: John, age: {name: John, age: 25} # Completed而使用create_partial并设置streamTrue后create的返回值变成Generator[T]每次迭代都能拿到当前状态的有效对象缺失字段为None最后一个值就是完整提取结果{name: Jo User(nameJo, ageNone) {name: John, ag User(nameJohn, ageNone) {name: John, age: User(nameJohn, ageNone) {name: John, age: 25} User(nameJohn, age25)这种能力特别适合生成式 UIGenerative UI可以在不依赖复杂 JSON 解析的前提下把增量结果实时渲染到前端组件中。完整示例见 Streaming Partial Responses其同步与异步用法如下import instructor from pydantic import BaseModel from typing import List from rich.console import Console client instructor.from_provider(openai/gpt-4.1-mini) class User(BaseModel): name: str email: str twitter: str class MeetingInfo(BaseModel): users: List[User] date: str location: str budget: int deadline: str extraction_stream client.create_partial( response_modelMeetingInfo, messages[{role: user, content: fGet the information about the meeting {text_block}}], streamTrue, ) console Console() for extraction in extraction_stream: obj extraction.model_dump() console.clear() console.print(obj) print(extraction.model_dump_json(indent2))异步场景只需使用async for迭代import instructor from pydantic import BaseModel client instructor.from_provider(openai/gpt-5-nano, async_clientTrue) class User(BaseModel): name: str age: int async def print_partial_results(): user client.create_partial( response_modelUser, max_retries2, streamTrue, messages[{role: user, content: Jason is 12 years old}], ) async for m in user: print(m) # nameNone ageNone → nameJason age12 import asyncio asyncio.run(print_partial_results())注意由于流式响应的性质Partial 流式模式不支持验证器——验证器无法应用于未完成的流式响应见 Streaming Partial Responses 的警告说明。源码级的实现原理Partial 的实现位于 instructor/v2/dsl/partial.py其核心是基于JSON 完整性completeness-based的校验策略process_potential_object借助jiter的partial_mode解析不完整 JSON通过JsonCompleteness跟踪器判断根对象是否闭合完整tracker.is_root_complete()JSON 完整且非空使用原始模型model_validate做完整校验JSON 不完整用model_construct直接构造 Partial 对象跳过校验缺失字段保持None。仓库中还保留了PartialLiteralMixin但已标记为 deprecated——基于完整性校验后Literal与Enum类型在流式过程中会被自动处理不再需要手动引入该 mixin源码中有对应的 DeprecationWarning。这解释了为什么官方文档的示例已不再需要它。关键特性二验证器与 Reasking重问机制验证错误即自纠错信号文章提出了一个很关键的观点与其把自我批判自我反思包装成新概念不如把它们看作带清晰错误信息的验证错误让系统据此自纠详见 Validation and Reasking。Instructor 把两类验证统一到同一套开发体验中代码验证Pydantic Validators基于规则、可编程表达的逻辑LLM 验证llm_validator难以用代码表达的语义规则交给模型判断。代码验证示例——强制姓名必须包含空格from pydantic import BaseModel, ValidationError from typing_extensions import Annotated from pydantic import AfterValidator def name_must_contain_space(v: str) - str: if not in v: raise ValueError(Name must contain a space.) return v.lower() class UserDetail(BaseModel): age: int name: Annotated[str, AfterValidator(name_must_contain_space)] try: person UserDetail(age29, nameJason) except ValidationError as e: print(e)LLM 验证示例——内容不得包含令人反感的内容import instructor from instructor import llm_validator from pydantic import BaseModel, ValidationError, BeforeValidator from typing import Annotated client instructor.from_provider(openai/gpt-4.1-mini) class QuestionAnswer(BaseModel): question: str answer: Annotated[ str, BeforeValidator(llm_validator(dont say objectionable things, clientclient)), ] try: qa QuestionAnswer( questionWhat is the meaning of life?, answerThe meaning of life is to be evil and steal, ) except ValidationError as e: print(e)关键点在于LLM 验证产生的错误信息是由模型生成的因而对重问非常有用——系统可以把这条错误信息回传给模型让它修正输出。Reasking用max_retries实现自动纠错当验证失败时Instructor 会自动把错误上下文回传给 LLM 重新生成。在 Validation and Reasking 中max_retries是对抗两类坏输出的防御层Pydantic 验证错误代码或 LLM 验证JSON 解码错误模型返回了格式错误的响应。import instructor from pydantic import BaseModel, field_validator client instructor.from_provider(openai/gpt-4.1-mini) class UserDetails(BaseModel): name: str age: int field_validator(name) classmethod def validate_name(cls, v): if v.upper() ! v: raise ValueError(Name must be in uppercase.) return v model client.create( response_modelUserDetails, max_retries2, messages[{role: user, content: Extract jason is 25 years old}], )在后台验证失败时等价于发生如下行为详见 Validation and Reaskingfrom pydantic import ValidationError try: ... except ValidationError as e: kwargs[messages].append(response.choices[0].message) kwargs[messages].append( { role: user, content: fPlease correct the function call; errors encountered:\n{e}, } )整个流程在 Validation 的流程图中被完整刻画定义 Pydantic 模型 → 发送请求 → LLM 生成响应 → 校验 → 通过则返回对象 / 失败则按max_retries回传错误上下文重问直至达到重试上限后抛出InstructorValidationError。进阶用context做运行时动态验证验证器还可以访问运行时上下文例如校验引用必须出自原文import instructor from pydantic import BaseModel, ValidationInfo, field_validator client instructor.from_provider(openai/gpt-4.1-mini) class QuoteExtraction(BaseModel): Extract a claim with a supporting quote from source text. claim: str supporting_quote: str field_validator(supporting_quote) classmethod def verify_quote_in_source(cls, v: str, info: ValidationInfo): import re context info.context if context: source_text context.get(source_text, ) normalized_source re.sub(r\s, , source_text.strip()) normalized_quote re.sub(r\s, , v.strip()) if normalized_quote not in normalized_source: raise ValueError( fThe quote must be an exact substring from the source text. fQuote {v} was not found in the source. ) return v extraction client.create( response_modelQuoteExtraction, max_retries2, messages[ {role: system, content: Extract a claim and find an exact quote from the text that supports it.}, {role: user, content: Source text: ...\n\nExtract a claim about Python.}, ], context{source_text: source_text}, )真实应用生成、提取与结构化搜索生成与提取结构化输出在以下任务中表现尤为突出详见 examples 索引在 RAG 应用中生成追问问题follow-up questions校验生成内容中的 URL从转录文本或图片中提取结构化数据。图片场景的完整实战可参考 Automatically Generate Advertising Copy from Product Images先用视觉模型把商品图提取为Product模型名称、关键特性、描述再把这些结构化信息作为提示输入文案模型生成带标题与正文的AdCopy广告文案。提取出的Product还带有generate_prompt()方法把对象直接序列化为下一阶段模型的提示——这正是结构化输出驱动复合流程的典型模式。结构化搜索查询对于复杂搜索场景纯嵌入向量embeddings难以处理诸如X 最近有什么新闻这类带时间、来源约束的查询。结构化输出让搜索参数变成可校验的模型class Search(BaseModel): query: str start_date: Optional[datetime] end_date: Optional[datetime] limit: Optional[int] source: Literal[news, social, blog]模型可以驱动更复杂的检索逻辑同时保证参数类型与枚举合法性——LLM 不再自由发挥而是按契约填表。经验教训文章作者在一年实践中沉淀出三条经验验证错误是提升系统性能的关键把失败转化为结构化反馈模型就能基于清晰错误信息自我修正并非所有语言模型都能有效支持重试逻辑max_retries依赖模型对错误信息的理解能力选择后端时需评估其函数调用与纠错能力结构化输出对视觉、文本、RAG 与 Agent 应用一视同仁地有益它统一了模型产出 → 程序消费的接口无论输入模态如何。走向 Software 3.0用数据结构重新学会编程文章提出了一个更具哲学意味的观点我们不是在改变编程语言而是在重新学习如何用数据结构编程。结构化输出让我们能够拥有我们定义的对象Own the objects控制我们实现的函数Control the functions管理控制流Manage the control flow掌握提示词Own the prompts这种路径让 Software 3.0 与既有软件体系保持向后兼容——语言模型不再是不可解释的黑盒而是重新回到经典的程序结构之中。这与 Philosophy 中Make structured LLM outputs as easy as defining a Pydantic model的定位一脉相承。总结一年过去Pydantic is all you need 依然成立。它不只是关于生成准确的响应更是关于以兼容既有编程范式与工具的方式完成生成用 Schema 约束输出、用验证器兜底、用重问自纠错、用流式提升体验。在继续迭代大语言模型的同时牢记这些原则将帮助我们构建更健壮、更可维护、更强大的应用。AI 的未来不仅取决于模型本身的能力更取决于我们能否将其无缝融入现有的软件生态——而 Pydantic Instructor 正是这条路上的关键一环。相关文档Instructor Philosophy — 为什么选择 Pydantic 作为核心抽象Response Model — 用 Pydantic 模型定义结构化输出Validation — 验证流程、字段约束与最佳实践Validation and Reasking — 验证器与自动重问机制Streaming Partial Responses — 流式部分响应与生成式 UIStructured Data Extraction from Images — 从图片提取商品并生成广告文案Validation Deep Dive — 进阶验证模式Best Framework Comparison — 框架对比视角下的 InstructorIntroduction to Instructor — 快速入门指南【免费下载链接】instructorstructured outputs for llms项目地址: https://gitcode.com/GitHub_Trending/in/instructor创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表