使用 Instructor 与 Writer 实现结构化输出:Palmyra 模型完整接入指南
【免费下载链接】instructorstructured outputs for llms项目地址: https://gitcode.com/GitHub_Trending/in/instructor
本指南聚焦于当前开源项目 instructor 对 Writer 平台的集成方案,完整演示如何通过instructor.from_provider("writer/palmyra-x5")一行代码获得 Writer 的结构化输出能力,覆盖同步/异步调用、嵌套对象抽取与流式输出三大实战场景,并深入底层源码揭示其工具调用(TOOLS)、JSON Schema 与 Markdown JSON(MD_JSON)三种工作模式的实现原理,帮助读者在真实业务中稳定地让 LLM 返回符合 Pydantic 模型约束的数据。
准备工作:账号、API Key 与安装
使用 Writer 结构化输出前,需要先完成两项准备:
- 注册 Writer 账号并获取 API Key:前往 Writer 官网注册账户,创建 API Key 后通过环境变量注入:
export WRITER_API_KEY=<your-api-key-here>- 安装 instructor 的 Writer 扩展:项目通过可选依赖的方式提供 Writer 支持,安装命令如下:
pip install "instructor[writer]"安装后,instructor 会在运行时按需导入 Writer 官方 SDK(writerai)。从 v2 Writer 客户端工厂源码 可以看到,SDK 采用惰性导入策略——只有当writerai真正被使用时才导入;如果未安装,from_writer会抛出ClientError,并提示你执行pip install writer-sdk。这保证了 instructor 核心包不会因为缺少某个厂商 SDK 而无法导入。
Palmyra-X 模型:自带工具调用能力
Writer 的 Palmyra-X 系列模型原生支持工具调用(tool calling)功能,这意味着可以将 Pydantic 模型直接转换为函数 schema 交给模型填充,从而获得稳定、符合结构约束的输出。在 instructor 的 provider 规格定义中,Writer 的默认模型标识为writer/palmyra-x5,并注册了writer别名(见 provider_specs.py),因此可以直接用from_provider("writer/palmyra-x5")快速初始化客户端。
说明:原版指南在描述中提及的模型为 Palmyra-X-004,而当前仓库源码中注册的默认模型字符串为
writer/palmyra-x5,实际使用时以源码中确认可用的writer/palmyra-x5为准。
快速上手:同步结构化抽取
最基础的用法是定义一个 PydanticBaseModel,把抽取任务交给client.create:
import instructor from pydantic import BaseModel # 初始化 Writer 客户端 client = instructor.from_provider("writer/palmyra-x5") class User(BaseModel): name: str age: int # 抽取结构化数据 user = client.create( messages=[{"role": "user", "content": "Extract: John is 30 years old"}], response_model=User, ) print(user) #> name='John' age=30instructor 会自动完成「Pydantic 模型 → 工具 schema → 模型工具调用 → JSON 解析 → Pydantic 校验」的完整链路,返回给你的是已经通过类型校验的User实例,而不是一串需要手工解析的文本。
异步调用:async 版本
在高并发、IO 密集的场景下,可以使用异步客户端。只需要在from_provider中传入async_client=True:
import instructor from pydantic import BaseModel import asyncio client = instructor.from_provider( "writer/palmyra-x5", async_client=True, ) class User(BaseModel): name: str age: int async def extract_user(): # 抽取结构化数据 user = await client.create( messages=[{"role": "user", "content": "Extract: John is 30 years old"}], response_model=User, ) print(user) #> name='John' age=30 if __name__ == "__main__": import asyncio asyncio.run(extract_user())从底层实现看,from_writer工厂会根据传入客户端的类型自动分派:writerai.Writer(同步)返回Instructor,writerai.AsyncWriter(异步)返回AsyncInstructor,两者的创建函数都统一取自client.chat.chat(见 client.py)——这是 Writer SDK 与 OpenAI 风格 API 的一个关键差异点:Writer 使用chat.chat而非chat.completions.create。
嵌套对象:复杂结构的抽取
真实业务中的实体往往包含层级关系。Writer 同样支持嵌套对象,instructor 会自动将嵌套的 Pydantic 模型展开为完整的 JSON Schema 交给模型:
import instructor from pydantic import BaseModel # 初始化 Writer 客户端 client = instructor.from_provider("writer/palmyra-x5") class Address(BaseModel): street: str city: str country: str class User(BaseModel): name: str age: int addresses: list[Address] # 创建带嵌套对象的结构化输出 user = client.create( messages=[ { "role": "user", "content": """ Extract: Jason is 25 years old. He lives at 123 Main St, New York, USA and has a summer house at 456 Beach Rd, Miami, USA """, }, ], response_model=User, ) print(user) #> { #> 'name': 'Jason', #> 'age': 25, #> 'addresses': [ #> { #> 'street': '123 Main St', #> 'city': 'New York', #> 'country': 'USA' #> }, #> { #> 'street': '456 Beach Rd', #> 'city': 'Miami', #> 'country': 'USA' #> } #> ] #> }这里list[Address]类型的字段会被转换为数组形式的 JSON Schema,模型返回的多个地址将被逐一校验并组装为Address对象列表,适合从一段自由文本中同时抽取多个同类实体。
流式输出:两类场景
当输出内容较长或希望尽早开始处理结果时,instructor 提供两种流式方案,Writer 均已通过原生工具调用方式支持:
- Iterables(列表流):用于流式返回同一类型的多个对象,例如一次抽取多个用户;
- Partial Streaming(部分流):用于流式返回单个对象,在响应逐步到达时立即开始处理。
部分流式(Partial Streaming)示例
import instructor from pydantic import BaseModel client = instructor.from_provider("writer/palmyra-x5") class Person(BaseModel): name: str age: int resp = client.create_partial( messages=[ { "role": "user", "content": "Ivan is 27 and lives in Singapore", } ], response_model=Person, ) for person in resp: print(person) #> name=None age=None #> name='Ivan' age=None #> name='Ivan' age=27可以看到,随着模型逐步生成 token,Person对象的字段会从None逐渐被填充,这正是部分流式输出的典型特征——下游可以在完整响应到达前就对已确定字段(如name)提前处理。
三种工作模式与底层实现
从源码看,Writer 集成在 v2 模式注册表中注册了三种 handler(见 handlers.py),这也是Mode枚举中WRITER_TOOLS/WRITER_JSON归一化后对应的能力集(映射关系见 mode.py):
| 模式 | Handler 类 | 请求构造方式 | 响应解析方式 |
|---|---|---|---|
TOOLS(默认) | WriterToolsHandler | 将response_model通过generate_openai_schema转为 function schema,设置tools=[{"type": "function", "function": schema}]与tool_choice="auto" | 读取choices[0].message.tool_calls[0].function.arguments,用model_validate_json校验 |
JSON_SCHEMA | WriterJSONSchemaHandler | 设置response_format={"type": "json_schema", "json_schema": {"schema": model_json_schema()}},走 Writer 原生 JSON Schema 通道 | 直接解析choices[0].message.content文本为 JSON |
MD_JSON | WriterMDJSONHandler | 将 schema 注入 system 消息,并要求模型在 ```json 代码块中返回 JSON | 通过extract_json_from_codeblock从代码块中提取 JSON 再校验 |
几点值得注意的实现细节:
- 默认模式为
TOOLS:from_writer的mode参数默认取Mode.TOOLS,即优先利用 Palmyra 系列模型的原生工具调用能力(client.py);而WRITER_TOOLS、WRITER_JSON这类厂商私有模式会在工厂内部被normalize_mode归一化为通用模式,保证与其他 provider 的行为一致。 - reask 自动重试:当校验失败时,三种模式都注册了对应的 reask 处理函数(
reask_writer_tools/reask_writer_json),会把上一次响应和校验错误信息拼回对话历史,要求模型修正输出,这正是 instructor 提高结构化输出成功率的机制之一。 - 截断检测:
TOOLS模式解析响应时,如果检测到finish_reason == "length",会抛出IncompleteOutputException,避免静默返回不完整数据(见 handlers.py)。 - mode 未注册时的保护:如果传入了 Writer 不支持的 mode,
from_writer会抛出ModeError,并列出该 provider 实际可用的模式列表,便于排查。
仓库中的测试对以上行为做了直接验证(见 tests/v2/test_writer_handlers.py):例如断言 TOOLS 模式下tool_choice会被设置为"auto"、工具调用返回的 JSON 能正确解析为User(name="Alice", age=30)、MD_JSON 模式下嵌套 ```json 代码块能正确提取解析;此外 tests/v2/test_writer_client.py 还保证了三个 handler 均可正常导入。
兼容层说明
如果你在旧代码中看到from instructor.providers.writer import from_writer这样的导入路径,无需担心——instructor/providers/writer/ 下的__init__.py、client.py、utils.py现在都是指向 v2 实现的兼容门面(facade),from_writer实际转发到instructor.v2.providers.writer.client.from_writer。因此无论走from_provider("writer/palmyra-x5")还是显式from_writer(writer_client),底层都是同一套 v2 注册表与 handler 体系。
小结
通过 instructor 接入 Writer 的结构化输出,核心路径只有三步:安装instructor[writer]→ 设置WRITER_API_KEY→instructor.from_provider("writer/palmyra-x5")。之后无论是同步、异步、嵌套对象还是流式输出,都可以用与 OpenAI 等其他 provider 完全一致的client.create/client.create_partial接口完成,Pydantic 模型的校验与自动重试则由底层WriterToolsHandler等模式处理器透明承接。若想深入源码,可从 v2 Writer 客户端工厂 与 v2 Writer 模式处理器 两个文件入手。
【免费下载链接】instructorstructured outputs for llms项目地址: https://gitcode.com/GitHub_Trending/in/instructor
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考