Instructor Patching 机制深度解析:如何为 LLM 客户端注入结构化输出能力
2026/9/15 12:04:41 网站建设 项目流程

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_modelmax_retriescontext等参数;
  • 校验:自动将 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方法执行流程如下:

  1. 包装完成方法patch()拦截客户端的chat.completions.create(或传入的任意create函数),用functools.wraps包装成新函数并回写到客户端对象上;
  2. 解析响应模型:调用prepare_response_model处理response_modelresponse_model is not None and mode not in Mode.parallel_modes()时执行,见 patch.py L227-L228);
  3. 转换为提供商格式:通过注册表mode_registry.get_handlers(provider, mode)拿到该提供商对应 Mode 的request_handler,将 Pydantic 模型转换为 JSON Schema、工具定义等提供商可理解的格式(见 registry.py L159);
  4. 发送并校验重试:将加工后的请求交给retry_sync_v2/retry_async_v2(instructor/v2/core/retry.py),其中内置了校验失败后的重试(reask)逻辑;
  5. 返回类型化对象:校验通过后把 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.TOOLSprovider=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_modelcontextmax_retriesstricthookstoken_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的核心差异:

维度手动 Patchingfrom_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),包含TOOLSPARALLEL_TOOLSANTHROPIC_TOOLSGEMINI_TOOLSMISTRAL_TOOLSVERTEXAI_TOOLSCEREBRAS_TOOLSWRITER_TOOLSBEDROCK_TOOLSRESPONSES_TOOLS等。

JSON Mode(Mode.JSON

在提示词中直接要求模型返回 JSON,然后从响应中解析并校验。对大部分提供商可用,且通常更省 token。

支持方:OpenAI、Anthropic、Google、Ollama 及大多数提供商。Mode.json_modes()(mode.py L108-L127)列出了全部 JSON 类 Mode,包括JSONJSON_O1JSON_SCHEMAANTHROPIC_JSONGEMINI_JSONBEDROCK_JSONPERPLEXITY_JSONXAI_JSON等。

Markdown JSON(Mode.MD_JSON

要求模型返回包裹在 markdown 代码块中的 JSON,之后从文本/代码块中提取。

  • 支持方:Databricks、部分视觉模型;
  • 适用场景:工具调用不可用或输出结构简单时的回退方案。

值得注意的是,当前仓库中许多提供商的旧模式(如BEDROCK_JSONFIREWORKS_JSONWRITER_JSONPERPLEXITY_JSON)在 v2 中都会映射到MD_JSON,见DEPRECATED_TO_CORE映射表(mode.py L203-L250)。

各提供商的默认 Mode

文档与from_provider文档(docs/concepts/from_provider.md)共同给出的默认模式如下:

提供商默认 Mode说明
OpenAIMode.TOOLS函数调用,支持流式结构化输出
AnthropicMode.TOOLSClaude 原生 tool use API
Google GeminiMode.TOOLS函数调用;需要jsonref包;Union 类型(除Optional外)不受支持
OllamaMode.TOOLSMode.JSONllama3.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 → TOOLS
  • ANTHROPIC_JSON → MD_JSON
  • BEDROCK_JSON → MD_JSON
  • VERTEXAI_PARALLEL_TOOLS → PARALLEL_TOOLS

因此,无论你传的是新旧哪种 Mode,最终都会收敛到TOOLSJSONJSON_SCHEMAMD_JSONPARALLEL_TOOLSRESPONSES_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。仅在以下情况才考虑:

  1. 需要对 patch 过程做细粒度控制(例如自定义create函数、精确指定providermode);
  2. 正在使用自定义客户端实现(可借助patch(create=...)传入任意函数);
  3. 调试 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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询