项目: instructor-ai/instructor (567-labs/instructor)
定位: 为 LLM 提供结构化输出的 Python 库,基于 Pydantic 实现类型安全的 JSON 提取
版本: v1.17.1 | Stars: 13,000+ | 语言: Python 100% | 许可证: MIT
主页: https://python.useinstructor.com/
1. 项目定位与核心价值
Instructor 解决的是一个极其具体的问题:从 LLM 的非结构化文本输出中可靠地提取结构化数据。传统方式需要手动编写 JSON Schema、处理验证错误、实现重试逻辑、解析不同提供商的 API 差异——Instructor 将这一切封装为一个极简接口:
client = instructor.from_provider("openai/gpt-4o-mini")
user = client.chat.completions.create(
response_model=User, # Pydantic 模型即 Schema
messages=[{"role": "user", "content": "John is 25 years old"}],
)
# 直接得到 User(name='John', age=25)
核心理念是 Schema-First:开发者只需定义 Pydantic BaseModel,Instructor 自动完成 JSON Schema 生成、API 适配、响应解析、验证重试的全链路。与 PydanticAI(Pydantic 官方的 Agent 运行时)形成互补——Instructor 聚焦于快速提取场景,PydanticAI 面向更复杂的 Agent 编排。
2. 整体架构:v1/v2 双层结构
Instructor 的代码库经历了从 v1 到 v2 的重大重构,当前采用 兼容层 + 核心层 的双层架构:
instructor/
├── __init__.py # 顶层入口,懒加载所有导出
├── core/ # 兼容层(v1 入口),实际全部 re-export 自 v2
│ ├── client.py → instructor.v2.core.client
│ ├── patch.py → instructor.v2.core.patch
│ ├── retry.py → instructor.v2.core.retry
│ ├── hooks.py → instructor.v2.core.hooks
│ └── exceptions.py → instructor.v2.core.errors
├── mode.py → instructor.v2.core.mode
├── auto_client.py → instructor.v2.auto_client
├── v2/ # 真正的核心实现
│ ├── core/
│ │ ├── client.py # Instructor / AsyncInstructor 主类
│ │ ├── patch.py # patch 机制(猴子补丁 API 客户端)
│ │ ├── retry.py # 重试引擎(基于 tenacity)
│ │ ├── mode.py # Mode 枚举(40+ 种模式)
│ │ ├── providers.py # Provider 枚举与检测
│ │ ├── registry.py # 模式处理器注册表
│ │ ├── hooks.py # 事件钩子系统
│ │ ├── templating.py # Jinja2 消息模板
│ │ ├── messages.py # 消息处理工具
│ │ ├── schema.py # Schema 生成入口
│ │ └── function_calls.py # OpenAISchema / ResponseSchema
│ ├── providers/ # 各 LLM 提供商适配器
│ │ ├── openai/
│ │ ├── anthropic/
│ │ ├── gemini/
│ │ ├── mistral/
│ │ ├── cohere/
│ │ ├── groq/
│ │ ├── bedrock/
│ │ └── ...(20+ 提供商)
│ ├── dsl/ # 高级 DSL 组件
│ │ ├── partial.py # Partial 流式解析
│ │ ├── iterable.py # IterableModel 流式列表
│ │ └── citation.py # 引用追踪
│ └── validation.py # LLM 验证器
├── batch/ # 批处理 API
├── distil/ # 微调数据蒸馏
├── cache/ # 缓存层
└── cli/ # 命令行工具
关键设计决策:v1 的 instructor.client、instructor.patch 等模块已变为纯兼容 shim,通过 __getattr__ 实现懒加载重定向到 v2。这意味着现有用户的代码无需改动,但新功能全部在 v2 中开发。
3. Mode 枚举:40+ 种 API 交互模式
Mode 是 Instructor 最核心的枚举,定义了与 LLM API 交互的每种方式:
class Mode(enum.Enum):
# OpenAI 系列
FUNCTIONS = "function_call" # 已废弃
PARALLEL_TOOLS = "parallel_tool_call"
TOOLS = "tool_call"
TOOLS_STRICT = "tools_strict"
JSON = "json_mode"
JSON_SCHEMA = "json_schema_mode"
MD_JSON = "markdown_json_mode"
RESPONSES_TOOLS = "responses_tools" # Responses API
# Anthropic 系列
ANTHROPIC_TOOLS = "anthropic_tools"
ANTHROPIC_JSON = "anthropic_json"
ANTHROPIC_PARALLEL_TOOLS = "anthropic_parallel_tools"
ANTHROPIC_REASONING_TOOLS = "anthropic_reasoning_tools"
# Google/Vertex 系列
GEMINI_TOOLS / GEMINI_JSON / GENAI_TOOLS / GENAI_JSON
VERTEXAI_TOOLS / VERTEXAI_JSON / VERTEXAI_PARALLEL_TOOLS
# 其他提供商
MISTRAL_TOOLS / MISTRAL_STRUCTURED_OUTPUTS
COHERE_TOOLS / COHERE_JSON_SCHEMA
XAI_TOOLS / XAI_JSON
BEDROCK_TOOLS / BEDROCK_JSON
FIREWORKS_TOOLS / FIREWORKS_JSON
CEREBRAS_TOOLS / CEREBRAS_JSON
WRITER_TOOLS / WRITER_JSON
PERPLEXITY_JSON / OPENROUTER_STRUCTURED_OUTPUTS
Mode 提供了 tool_modes() 和 json_modes() 两个分类方法,将所有模式分为工具调用类和 JSON 类两大阵营。这种分类直接决定了请求构建和响应解析的逻辑分支。
4. 注册表机制:模式处理器的中心调度
ModeRegistry 是 v2 架构的核心枢纽,采用 (Provider, Mode) → ModeHandlers 的映射模式:
@dataclass
class ModeHandlers:
request_handler: RequestHandler # 构建 API 请求参数
reask_handler: ReaskHandler # 验证失败时的重试消息构建
response_parser: ResponseParser # 从 API 响应中提取结构化数据
stream_extractor: StreamExtractor | None = None # 同步流式提取
stream_extractor_async: AsyncStreamExtractor | None = None # 异步流式提取
message_converter: MessageConverter | None = None # 多模态消息转换
template_handler: TemplateHandler | None = None # Jinja2 模板处理
注册表支持两种注册方式:
- 即时注册:
register()直接绑定处理器 - 懒加载注册:
register_lazy()延迟到首次访问时才 import 提供商模块
懒加载通过 _lazy_load_lock(threading.Lock)保证线程安全——首次访问时只有一个线程执行 loader,其他线程等待后直接读取已加载的结果。这避免了 20+ 提供商模块在 import 时全部加载的开销。
# 启动时注册所有懒加载处理器
def _register_default_lazy_handlers():
for provider, (module_path, modes) in HANDLER_SPECS.items():
for mode in modes:
mode_registry.register_lazy(provider, mode, lambda: ...)
5. Patch 机制:猴子补丁的艺术
Instructor 的核心 API 设计是通过 patch() 函数对原始 API 客户端进行猴子补丁,使其 create() 方法支持 response_model 参数:
def patch_v2(func, provider, mode, default_model=None):
"""Patch a function to use v2 registry for structured outputs."""
RegistryValidationMixin.validate_mode_registration(provider, mode)
if is_async(func):
return _create_async_wrapper(func, provider, mode, default_model)
else:
return _create_sync_wrapper(func, provider, mode, default_model)
补丁后的函数在调用时:
- 注入 response_model:将 Pydantic 模型转为 JSON Schema,注入到 API 请求的 tools/function 参数中
- 模板处理:通过 Jinja2 SandboxedEnvironment 渲染消息中的模板变量
- 调用原始 API:使用修改后的 kwargs 调用底层 API
- 解析响应:通过注册表的 response_parser 从 API 响应中提取 JSON
- 验证重试:使用 Pydantic 验证提取的数据,失败则通过 reask_handler 重试
6. 重试引擎:智能容错
重试机制基于 tenacity 库,但做了深度定制:
_RETRYABLE_PARSE_ERRORS = (
ValidationError, # Pydantic 验证失败
json.JSONDecodeError, # JSON 解析错误
AsyncValidationError, # 异步验证错误
ResponseParsingError, # 响应解析错误
)
重试策略的核心逻辑:
- token_budget:可设置 token 预算上限,累积 usage 超过预算时终止重试
- max_retries:支持 int 或 tenacity 的 Retrying/AsyncRetrying 对象
- reask 机制:验证失败时,通过 reask_handler 将错误信息注入对话历史,引导 LLM 修正输出
- usage 追踪:每次重试后累加 token 使用量,支持 Anthropic 的 cache_creation_input_tokens 等特殊字段
def _validate_token_budget(token_budget, *, response_model, kwargs):
if token_budget is None:
return
# 检查累积 token 是否超过预算
7. DSL 层:Partial 与 Iterable 流式处理
Partial(流式部分解析)
Partial 是 Instructor 最精巧的设计之一,支持从流式 JSON 中实时构建 Pydantic 模型:
- 通过
JsonCompleteness追踪器判断 JSON 字段是否完整 - 完整的嵌套 BaseModel 字段使用
model_validate()严格验证 - 不完整的字段使用
model_construct()跳过验证,保留原始值 - 采用
trailing-strings模式处理流式中被截断的字符串
# 流式使用
async for partial_user in client.chat.completions.create(
response_model=Partial[User],
stream=True,
messages=[...],
):
print(partial_user) # 逐步填充的 User 对象
IterableModel(流式列表提取)
IterableModel 支持从流式响应中逐个提取列表项,适用于处理大量数据时不想等待完整响应的场景。内部维护 tasks 列表的流式 JSON 解析器,每完成一个 item 就 yield 一个 Pydantic 模型实例。
8. 多提供商适配:20+ Provider 统一接口
Instructor 通过 Provider 枚举和提供商目录结构实现统一适配:
class Provider(Enum):
OPENAI = "openai"
AZURE_OPENAI = "azure_openai"
ANTHROPIC = "anthropic"
GEMINI = "gemini"
GENAI = "genai"
MISTRAL = "mistral"
COHERE = "cohere"
GROQ = "groq"
CEREBRAS = "cerebras"
FIREWORKS = "fireworks"
BEDROCK = "bedrock"
WRITER = "writer"
XAI = "xai"
DEEPSEEK = "deepseek"
PERPLEXITY = "perplexity"
OPENROUTER = "openrouter"
VERTEXAI = "vertexai"
OLLAMA = "ollama"
LITELLM = "litellm"
每个提供商模块包含:
client.py:from_()工厂函数schema.py:提供商特定的 JSON Schema 生成逻辑templating.py:提供商特定的消息模板处理- 处理器注册:通过
@mode_registry.register()装饰器注册 ModeHandlers
auto_client.py 提供 from_provider("openai/gpt-4o") 风格的自动路由,通过 URL 模式匹配自动选择提供商和模式。
9. Hooks 事件系统:可观测性
Instructor 提供了完整的事件钩子系统,用于监控和调试:
class HookName(Enum):
COMPLETION_KWARGS = "completion:kwargs" # API 调用参数
COMPLETION_RESPONSE = "completion:response" # API 原始响应
COMPLETION_ERROR = "completion:error" # 调用错误
COMPLETION_LAST_ATTEMPT = "completion:last_attempt" # 最后一次尝试
COMPLETION_USAGE = "completion:usage" # Token 用量
PARSE_ERROR = "parse:error" # 解析错误
Hooks 支持:
on()/off()注册和移除处理器clear()清除指定或所有处理器__add__/__iadd__合并多个 Hooks 实例combine()类方法批量合并copy()深拷贝- 处理器错误被
warnings.warn()捕获,不会中断主流程 - 通过
inspect.signature()自动适配不同签名的处理器
10. 工程实践与设计哲学
懒加载架构
顶层 __init__.py 通过 __getattr__ 实现完全懒加载,所有导出(40+ 个符号)都通过 _LAZY_IMPORTS 字典延迟解析。_add_optional_export() 动态检测可选依赖(anthropic、google、mistral 等)是否安装,只导出可用的提供商。
消息隔离
messages.py 中的 copy_messages_for_mutation() 解决了一个微妙的 bug——reask handler 会直接 .insert()/.append() 修改 messages 列表,如果不深拷贝会污染调用者的对话历史。
Schema 生成
通过 generate_openai_schema()、generate_anthropic_schema()、generate_gemini_schema() 三个入口适配不同提供商的 JSON Schema 格式差异。每个提供商的 schema 生成逻辑独立维护在各自的 schema.py 中。
模板引擎
使用 Jinja2 的 SandboxedEnvironment 处理消息模板,自动检测消息格式(OpenAI string content / Anthropic list content / Cohere message field / Gemini parts)并路由到对应的处理函数。
微调数据蒸馏
distil.py 提供 FinetuneFormat 和 Instructions,支持将 Instructor 的结构化提取逻辑蒸馏为微调训练数据,实现”用小模型替代大模型的结构化输出能力”。
批处理
batch.py 提供 BatchProcessor、BatchRequest、BatchJob,支持大规模批量提取任务的异步处理。
架构总结
Instructor 的架构可以用三个关键词概括:
- Schema-First:Pydantic 模型即 JSON Schema,即 API 约束,即类型安全保障。开发者只需定义数据结构,所有 API 适配逻辑自动完成。
- Registry-Driven:(Provider, Mode) → ModeHandlers 的中心注册表是整个系统的调度枢纽。每个提供商的每种交互模式都有独立的处理器,通过懒加载实现按需初始化。
- Retry-Resilient:基于 tenacity 的智能重试引擎,结合 reask 机制(将验证错误反馈给 LLM 引导修正)和 token budget 控制,在可靠性与成本之间取得平衡。
这个架构使 Instructor 成为 LLM 结构化输出的事实标准——13,000+ Stars、250+ 贡献者、20+ 提供商支持、40+ 种交互模式,证明了”一个 Pydantic 模型搞定一切”的设计理念的成功。
发表回复