Instructor Patching 机制深度解析:如何为 LLM 客户端注入结构化输出能力
【免费下载链接】instructorstructured outputs for llms项目地址: https://gitcode.com/GitHub_Trending/in/instructor
Patching 是 Instructor 的核心机制之一:它不修改 LLM SDK 的原始代码,而是通过包装完成方法,为客户端"注入"结构化输出能力——新增response_model等参数、基于 Pydantic 的校验、失败重试与类型化返回。本文将围绕 docs/concepts/patching.md 展开,结合当前仓库的 v2 源码实现(instructor/v2/core/patch.py、instructor/v2/core/mode.py、instructor/v2/core/registry.py),讲清 Patching 的工作原理、各种 Mode 的适用场景、各提供商的默认模式,以及手动 Patching 与from_provider的取舍,读完即可在自己的项目中安全地使用或调试 Patching。
Patching 是什么:给客户端"加装"结构化输出
Patching 的英文原意是"打补丁"。在 Instructor 的语境中,它指的是在不动 LLM 客户端原始代码的前提下,为其对象附加新功能。当你对 OpenAI、Anthropic、Google 等任意 SDK 客户端执行 patch 之后,该客户端会获得一套新的能力:
- 新参数:
create()(或chat.completions.create())方法新增response_model、max_retries、context等参数; - 校验:自动将 LLM 返回内容与 Pydantic 模型比对,不合格则拦截;
- 重试:校验失败时携带错误反馈自动重试;
- 兼容性:被 patch 的客户端原有方法全部保留,你依然可以使用 SDK 的全部原生功能。
当前仓库中,instructor.core.patch仅是兼容导出层(见 instructor/core/patch.py),真正的实现位于 v2 体系:instructor/v2/core/patch.py。这意味着 Patching 机制已统一收敛到 v2 的 handler 注册表架构中,这也解释了为什么文档推荐所有用户优先使用from_provider——它会自动完成 patch 并选择正确的 Mode。
Patching 的工作流程:五步走
从 instructor/v2/core/patch.py 的同步包装器_create_sync_wrapper(异步版本_create_async_wrapper与之对称,见同文件 L309-L428)可以看出,patch 后的create方法执行流程如下:
- 包装完成方法:
patch()拦截客户端的chat.completions.create(或传入的任意create函数),用functools.wraps包装成新函数并回写到客户端对象上; - 解析响应模型:调用
prepare_response_model处理response_model(response_model is not None and mode not in Mode.parallel_modes()时执行,见 patch.py L227-L228); - 转换为提供商格式:通过注册表
mode_registry.get_handlers(provider, mode)拿到该提供商对应 Mode 的request_handler,将 Pydantic 模型转换为 JSON Schema、工具定义等提供商可理解的格式(见 registry.py L159); - 发送并校验重试:将加工后的请求交给
retry_sync_v2/retry_async_v2(instructor/v2/core/retry.py),其中内置了校验失败后的重试(reask)逻辑; - 返回类型化对象:校验通过后把 JSON 反序列化为 Pydantic 模型实例返回,而不是原始响应。
包装器还额外处理了几件"隐藏事项":autodetect_images图像自动检测、cache/cache_namespace/cache_ttl响应缓存、handle_templating模板注入(instructor/v2/core/templating.py),以及token_budget预算校验(instructor/v2/core/budget.py)。这些能力都是"锦上添花"——即使你不显式使用它们,patch 依旧正常工作。
从测试也能佐证这套流程的稳定性:tests/llm/test_openai/test_patch.py 同时覆盖了同步与异步客户端的多 Mode 场景,tests/core/test_patch.py 验证了最基本的instructor.patch(client)用法。
手动 Patching:最小可用示例
文档提供了最直接的用法——先创建原生客户端,再手动 patch:
import openai import instructor from pydantic import BaseModel class YourModel(BaseModel): message: str # 创建基础客户端 openai_client = openai.OpenAI() # 手动 patch 它 client = instructor.patch(openai_client, mode=instructor.Mode.TOOLS) # 现在可以用了 response = client.chat.completions.create( response_model=YourModel, messages=[{"role": "user", "content": "Say hello"}], )从源码看,instructor.patch的完整签名是(instructor/v2/core/patch.py L147-L168):
def patch( client: OpenAI | AsyncOpenAI | None = None, create: Callable[..., T_Retval] | None = None, mode: Mode = Mode.TOOLS, provider: Provider = Provider.OPENAI, ) -> OpenAI | AsyncOpenAI | InstructorChatCompletionCreate:值得注意的细节:
- 默认
mode=Mode.TOOLS、provider=Provider.OPENAI:如果你用的是非 OpenAI 客户端,必须显式传入正确的provider,否则注册表会因 Mode 未注册而抛出RegistryError(patch 前会调用RegistryValidationMixin.validate_mode_registration校验,见 patch.py L106)。 - 支持
create参数:除了传客户端,你也可以直接传一个函数instructor.patch(create=my_create_fn, ...),这为自定义客户端实现提供了入口。 apatch已弃用:异步场景直接使用同一个patch即可(patch.py L171-L182 会抛出DeprecationWarning)。- 包装器签名(patch.py L195-L204):patch 后的
create接受response_model、context、max_retries、strict、hooks、token_budget。其中max_retries的类型是int | Retrying,既可以是次数,也可以是 tenacity 的Retrying实例——当前 v2 实现中默认值为1(旧版文档记载为0,以当前仓库源码为准)。
为什么推荐from_provider而非手动 Patching
文档反复强调:99% 的场景下,from_provider是更优选择。对比两种写法:
import instructor from pydantic import BaseModel # 更简单的方式 class YourModel(BaseModel): message: str client = instructor.from_provider("openai/gpt-4o-mini") _response = client.create( response_model=YourModel, messages=[{"role": "user", "content": "Say hello"}], )手动 Patching 与from_provider的核心差异:
| 维度 | 手动 Patching | from_provider |
|---|---|---|
| 提供商识别 | 需要自己传provider枚举 | 从"provider/model"字符串自动识别 |
| Mode 选择 | 需要自己传mode | 按提供商应用推荐默认 Mode |
| 客户端创建 | 自己 new SDK 客户端再 patch | 一行代码创建并完成全部配置 |
| 切换提供商 | 需重写初始化代码 | 改一行字符串即可 |
| 适用场景 | 自定义客户端、调试 patch 行为 | 绝大多数生产场景 |
from_provider的完整用法(API Key、Mode 覆盖、缓存、异步客户端、错误处理、提供商切换)参见 docs/concepts/from_provider.md;从旧的手动 Patching 模式迁移的详细步骤参见 docs/concepts/migration.md。
在底层,from_provider也复用了同样的注册表分发逻辑,并通过get_provider(base_url)(instructor/v2/core/providers.py L84-L126)根据 base URL 的关键字(如"azure"、"anthropic"、"ollama"、"localhost:11434"、"x.ai"等)自动推断Provider枚举,随后再确定合适的 Mode。
Patching 的三种核心 Mode 与适用场景
文档将 Mode 划分为三大类,核心差异在于"如何让模型返回结构化内容":
Tool Calling(Mode.TOOLS)
利用提供商原生的函数/工具调用 API,把 Pydantic 模型转换成工具定义,让模型以工具参数的形式输出结构化 JSON。
- OpenAI 的默认模式(函数调用);
- 支持方:OpenAI、Anthropic、Google、Ollama(针对支持工具调用的模型)。
在源码中,Mode.tool_modes()返回全部工具类 Mode 的集合(instructor/v2/core/mode.py L80-L105),包含TOOLS、PARALLEL_TOOLS、ANTHROPIC_TOOLS、GEMINI_TOOLS、MISTRAL_TOOLS、VERTEXAI_TOOLS、CEREBRAS_TOOLS、WRITER_TOOLS、BEDROCK_TOOLS、RESPONSES_TOOLS等。
JSON Mode(Mode.JSON)
在提示词中直接要求模型返回 JSON,然后从响应中解析并校验。对大部分提供商可用,且通常更省 token。
支持方:OpenAI、Anthropic、Google、Ollama 及大多数提供商。Mode.json_modes()(mode.py L108-L127)列出了全部 JSON 类 Mode,包括JSON、JSON_O1、JSON_SCHEMA、ANTHROPIC_JSON、GEMINI_JSON、BEDROCK_JSON、PERPLEXITY_JSON、XAI_JSON等。
Markdown JSON(Mode.MD_JSON)
要求模型返回包裹在 markdown 代码块中的 JSON,之后从文本/代码块中提取。
- 支持方:Databricks、部分视觉模型;
- 适用场景:工具调用不可用或输出结构简单时的回退方案。
值得注意的是,当前仓库中许多提供商的旧模式(如BEDROCK_JSON、FIREWORKS_JSON、WRITER_JSON、PERPLEXITY_JSON)在 v2 中都会映射到MD_JSON,见DEPRECATED_TO_CORE映射表(mode.py L203-L250)。
各提供商的默认 Mode
文档与from_provider文档(docs/concepts/from_provider.md)共同给出的默认模式如下:
| 提供商 | 默认 Mode | 说明 |
|---|---|---|
| OpenAI | Mode.TOOLS | 函数调用,支持流式结构化输出 |
| Anthropic | Mode.TOOLS | Claude 原生 tool use API |
| Google Gemini | Mode.TOOLS | 函数调用;需要jsonref包;Union 类型(除Optional外)不受支持 |
| Ollama | Mode.TOOLS或Mode.JSON | llama3.1、llama3.2、mistral-nemo 等支持工具;旧模型回退到 JSON |
从源码角度,v2 对"过时提供商专属 Mode"的处理是自动降级并警告:normalize_mode_for_provider(instructor/v2/core/providers.py L76-L81)会在 Mode 出现在DEPRECATED_TO_CORE中时,先通过Mode.warn_deprecated_mode发出DeprecationWarning,再将其替换为核心 Mode。例如:
ANTHROPIC_TOOLS → TOOLSANTHROPIC_JSON → MD_JSONBEDROCK_JSON → MD_JSONVERTEXAI_PARALLEL_TOOLS → PARALLEL_TOOLS
因此,无论你传的是新旧哪种 Mode,最终都会收敛到TOOLS、JSON、JSON_SCHEMA、MD_JSON、PARALLEL_TOOLS、RESPONSES_TOOLS这几个核心 Mode 之一。完整的 Mode 对比、选型建议与各提供商兼容性清单,参见 docs/modes-comparison.md 和 docs/concepts/mode-migration.md。
使用from_provider时,这些默认值会被自动应用;如需覆盖,可通过mode参数指定:
import instructor client = instructor.from_provider( "openai/gpt-4o-mini", mode=instructor.Mode.JSON, # 覆盖默认的 TOOLS 模式 )Patching 究竟给客户端加了什么?
文档明确了 patch 注入的功能清单,我们逐一对照源码确认:
新增参数
response_model:Pydantic 模型或类型,定义期望的输出结构。包装器接收后由prepare_response_model规范化,再交给 handler 转成 schema。max_retries:校验失败时的重试次数。当前 v2 包装器签名默认1,且支持传入 tenacity 的Retrying对象进行精细控制(patch.py L198)。context:校验钩子(hooks)使用的附加上下文,会随请求一起传递给handle_templating与重试逻辑。- 此外还有
strict(默认True,用于 schema 严格模式)、hooks(instructor/v2/core/hooks.py)、token_budget等参数。
增强的方法行为
patch 后的create()方法:
- 接受
response_model参数; - 自动校验响应是否符合 Pydantic 模型;
- 校验失败时自动重试并携带错误信息(reask);
- 返回类型化的 Pydantic 对象而非原始响应;
- 原有全部参数照常透传,SDK 原生功能不受影响。
提供商特定的注意事项
OpenAI
- 默认
Mode.TOOLS(函数调用),流式场景同样支持结构化输出(相关测试见 tests/v2/test_openai_streaming.py); - 通过
Mode.RESPONSES_TOOLS支持新的 Responses API 工具调用(见 mode.py L29-L30)。
Anthropic
- 默认
Mode.TOOLS(tool use),使用 Claude 原生工具调用 API; ANTHROPIC_REASONING_TOOLS已弃用,现在建议用Mode.ANTHROPIC_TOOLS配合thinking={'type': 'enabled', 'budget_tokens': ...}参数实现扩展思考(见 mode.py L156-L175 的弃用说明)。
Google Gemini
- 默认
Mode.TOOLS(函数调用); - 工具调用依赖
jsonref包(需额外安装); - Union 类型(除
Optional外)不受支持,设计 schema 时需注意。
Ollama(本地模型)
- 默认
Mode.TOOLS(模型支持时)或Mode.JSON; - llama3.1、llama3.2、mistral-nemo 等模型支持工具调用,旧模型自动回退到 JSON 模式。
什么时候才需要手动 Patching?
文档给出的结论非常明确:绝大多数场景都不需要手动 Patching。仅在以下情况才考虑:
- 需要对 patch 过程做细粒度控制(例如自定义
create函数、精确指定provider与mode); - 正在使用自定义客户端实现(可借助
patch(create=...)传入任意函数); - 调试 Patching 行为本身。
此外,如果你的需求是"多工具并行"(一个响应中多次工具调用),可关注Mode.PARALLEL_TOOLS及其别名(parallel_modes(),见 mode.py L129-L136)。
深入阅读
- docs/concepts/from_provider.md —— 推荐方式:一行代码创建已 patch 的客户端
- docs/concepts/migration.md —— 从手动 Patching 迁移到
from_provider - docs/modes-comparison.md —— 各 Mode 的详细对比与选型建议
- docs/concepts/mode-migration.md —— 旧 Mode 到核心 Mode 的映射说明
- docs/integrations/index.md —— 各提供商的专属文档
- 核心实现:instructor/v2/core/patch.py、instructor/v2/core/mode.py、instructor/v2/core/registry.py、instructor/v2/core/providers.py
- 测试参考:tests/core/test_patch.py、tests/llm/test_openai/test_patch.py、tests/v2/test_issue_2374.py(演示了
Mode.MD_JSON + Provider.GEMINI的手动组合)
【免费下载链接】instructorstructured outputs for llms项目地址: https://gitcode.com/GitHub_Trending/in/instructor
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考