Instructor错误
新手教程:解决 Instructor 常见错误(多工具调用、字段校验、API 兼容性等)
当你在使用 instructor 库(一个让大模型输出结构化数据的 Python 库)配合大模型时,可能会遇到各种错误。本教程会用大白话告诉你:为什么会出这些错,以及 每种情况对应的解决方案。
目录
- 错误 1:Instructor does not support multiple tool calls
- 错误 2:Pydantic 对象没有某个字段
- 错误 3:API 拒绝 messages 内容
- 错误 4:多模型额度切换时 Pydantic 字段污染
- 总结:避坑清单
错误 1:Instructor does not support multiple tool calls
1.1 错误出现的场景
典型的代码长这样:
import instructor
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:11434/v1", # 你的本地模型服务地址
api_key="ollama",
)
client = instructor.from_openai(client) # 没有指定模式
response = client.chat.completions.create(
model="llama3.2",
response_model=YourPydanticModel, # 期望得到结构化输出
messages=[...],
)
运行后,控制台就报出了那个错误。
1.2 为什么会发生这个错误?
instructor 默认使用 Tool Calling(函数调用)模式。这个模式要求模型严格按照 OpenAI 的函数调用规范返回结果。但是:
- 许多本地模型(如 Llama 3、Yi-Coder、Qwen 等)对函数调用的支持不完善。
- 即使模型支持,有时模型会尝试返回多个工具调用,而
instructor默认无法处理多个调用,所以就会报错并提示你改用List[Model]。
实际上,你完全不需要改用 List[Model]。更简单的做法是:让 instructor 换一种工作方式,不再依赖函数调用,而是直接让模型输出纯 JSON。
1.3 解决方案总览
| 方案 | 核心思路 | 适用场景 |
|---|---|---|
使用 Mode.MD_JSON |
从 Markdown 代码块中提取 JSON | 首选,几乎兼容所有模型 |
使用 Mode.JSON |
要求模型直接输出纯 JSON | API 支持原生 JSON 模式时更规范 |
1.4 方案一:使用 MD_JSON 模式(推荐新手首选)
什么是 MD_JSON?
很多模型喜欢把输出的 JSON 放在 Markdown 代码块里,例如:
```json
{"name": "张三", "age": 25}
```
Mode.MD_JSON 就是让 instructor 自动识别并提取这个代码块里的 JSON,而不管外面有没有额外文字。
代码修改(只需改一行)
修改前:
client = instructor.from_openai(client)
修改后:
client = instructor.from_openai(client, mode=instructor.Mode.MD_JSON)
完整示例:
import instructor
from openai import OpenAI
from pydantic import BaseModel
class Person(BaseModel):
name: str
age: int
client = OpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama",
)
client = instructor.from_openai(client, mode=instructor.Mode.MD_JSON)
response = client.chat.completions.create(
model="llama3.2",
response_model=Person,
messages=[{"role": "user", "content": "张三,25岁"}],
)
print(response.name) # 张三
print(response.age) # 25
4.3 为什么 MD_JSON 最稳定?
- 即使模型在 JSON 前后加了说明文字,也能正常工作。
- 不依赖模型的函数调用能力,兼容性最好。
- 因此它是新手遇到错误时的第一选择。
1.5 方案二:使用 JSON 模式(更严格,适合高级用户)
什么是 JSON 模式?
Mode.JSON 强制模型必须输出一个纯 JSON 字符串,不能有任何额外文本(包括代码块标记)。它通常需要 API 提供原生支持(如 OpenAI 的 response_format={"type": "json_object"})。
代码修改(也是改一行)
client = instructor.from_openai(client, mode=instructor.Mode.JSON)
完整示例:
client = instructor.from_openai(client, mode=instructor.Mode.JSON)
注意:使用 Mode.JSON 时,建议在系统提示词或用户消息中明确要求只输出 JSON,例如:
messages=[
{"role": "system", "content": "You are a helpful assistant. Always output valid JSON without any extra text."},
{"role": "user", "content": "张三,25岁"}
]
什么时候用 JSON 模式?
- 你使用的是官方 OpenAI、Gemini 等云端 API,并且它们支持
response_format。 - 你对输出格式有严格的要求,不希望有任何额外的自然语言。
- 如果本地模型也能稳定输出纯 JSON(经过测试),也可以使用。
1.6 两种模式对比一览表
| 特性 | Mode.MD_JSON | Mode.JSON |
|---|---|---|
| 提取方式 | 正则提取代码块内的 JSON | 直接解析 API 返回的纯文本 |
| 对额外文字 | 容错,会忽略代码块外的内容 | 不兼容,模型必须只输出 JSON |
| API 要求 | 无特殊要求,所有模型通用 | 需 API 支持 response_format 或能稳定输出纯 JSON |
| 推荐指数 | ⭐⭐⭐⭐⭐(新手首选) | ⭐⭐⭐(进阶使用) |
1.7 额外优化建议:让模型乖乖输出 JSON
即使你使用了上述模式,有时模型仍然会“跑偏”,比如在 JSON 前加上“Sure, here is the JSON:”。你可以通过以下方式进一步提高成功率:
在用户消息中明确要求:
user_content = """
请根据以下信息输出 JSON:
姓名:张三,年龄:25
重要:你的回复必须是一个合法的 JSON 对象,不要包含任何其他文字。
"""
使用 output_instructions 参数(如果模型 API 支持):
response = client.chat.completions.create(
...,
extra_body={
"output_instructions": ["Your response must be a valid JSON object."]
}
)
1.8 常见问题 FAQ
Q1:修改模式后,我的 response_model 还能自动验证吗?
A:可以。instructor 的 JSON 和 MD_JSON 模式都会自动解析并验证 Pydantic 模型,你不需要修改其他代码。
Q2:我用了 MD_JSON 还是报错怎么办?
A:极少数情况模型连 JSON 都不输出。你可以打印 response.choices[0].message.content 查看原始输出,然后根据实际内容调整提示词。通常添加“只输出 JSON,不要有任何解释”就能解决。
Q3:我需要把项目中所有 instructor.from_openai 都改一遍吗?
A:是的,建议统一修改,以保持行为一致。
1.9 小结
当你看到 Instructor does not support multiple tool calls 这个错误时:
- 不要慌,这不是你代码逻辑的错误,而是
instructor默认模式与本地模型不太合拍。 - 立即尝试:将
instructor.from_openai(client)改为instructor.from_openai(client, mode=instructor.Mode.MD_JSON)。 - 如果还想更严格,可以尝试
Mode.JSON,但记得在提示词中强调"只输出 JSON"。 - 最后,享受
instructor带来的结构化数据提取便利吧!
现在你可以放心地让本地模型输出 Pydantic 对象,而不再被那个烦人的错误困扰了。
错误 2:Pydantic 对象没有某个字段(object has no field “xxx”)
2.1 错误信息
ValidationError: "TrafficAccidentAnalysis" object has no field "used_model"
或者在响应时出现:
"TrafficAccidentAnalysis" object has no field "used_model"
2.2 错误场景
你希望给 instructor 返回的 Pydantic 对象"附加"一些额外信息(比如记录用了哪个模型),于是写了类似这样的代码:
response = client.chat.completions.create(
model=current_model,
response_model=TrafficAccidentAnalysis, # 严格按这个模型校验
messages=[...],
)
response.analysis_id = analysis_id # 这两个字段 Pydantic 里没有!
response.used_model = current_model # 报错就出在这里
2.3 为什么会发生这个错误?
Pydantic 模型在 model_config = ConfigDict(extra="forbid")(或默认行为)下,禁止给实例添加未声明的字段。一旦你赋值一个未定义的字段,Pydantic 就会抛 ValidationError。
2.4 解决方案:把"额外信息"作为返回值的一部分
错误示例:
response = client.chat.completions.create(...)
response.used_model = current_model # ❌ 报错
正确示例 1:把额外信息放到 Pydantic 模型里
class TrafficAccidentAnalysis(BaseModel):
analysis_id: str
used_model: str = "unknown" # 显式声明
# ... 其他字段
response = client.chat.completions.create(
response_model=TrafficAccidentAnalysis,
...
)
response.used_model = current_model # ✅ OK
正确示例 2:用元组返回(推荐)
def analyze(video_path, current_model):
response = client.chat.completions.create(...)
return response, current_model # ✅ 模型名作为局部变量传递
result, used_model = analyze(video_path, "qwen")
正确示例 3:放到外部 dict
result = {
"analysis": response,
"used_model": current_model,
}
2.5 避坑口诀
Pydantic 实例不能"加字段",要么声明,要么用元组/dict 携带。
错误 3:API 拒绝 messages 内容(Unexpected item type in content)
3.1 错误信息
openai.BadRequestError: Error code: 400 - {'error': {'message':
'<400> InternalError.Algo.InvalidParameter: The provided messages input is invalid.
The error info is [Unexpected item type in content.].', 'type': 'invalid_request_error'}}
3.2 错误场景
你用 instructor 调用了某些云端 API(例如阿里百炼 DashScope 的 OpenAI 兼容模式),模型明明支持函数调用,但请求却报 400 错。
3.3 为什么会发生这个错误?
instructor 默认的 Tool Calling 模式会在 messages 中插入 tool_calls 类型的内容项。但某些 API(特别是第三方 OpenAI 兼容 API)对 content 字段类型校验非常严格,会拒绝 tool_calls 类型,于是返回 400。
3.4 解决方案
方案 A:切换到 Mode.MD_JSON(推荐)
import instructor
client = instructor.from_openai(client, mode=instructor.Mode.MD_JSON)
这种方式完全不走 tool_calls,content 全部是普通文本,兼容性最好。
方案 B:使用 instructor.Mode.JSON + 强提示词
client = instructor.from_openai(client, mode=instructor.Mode.JSON)
# 同时在 system prompt 中加:
# "请只输出 JSON,不要包含任何其他文字。"
方案 C:自定义重试,跳过这种错误
try:
response = client.chat.completions.create(...)
except BadRequestError as e:
if "Unexpected item type" in str(e):
# 切换到下一个模型
...
3.5 避坑口诀
遇到 “Unexpected item type” 99% 是 instructor 走 tool_calls 模式的问题,换成 MD_JSON 即可。
错误 4:多模型额度切换时 Pydantic 字段污染
4.1 错误信息
当你在做"多模型自动 fallback"(A 模型额度用完切 B 模型)时,可能会出现以下两类问题:
object has no field "xxx"(Pydantic 字段污染)- 模型切换时,递归调用导致
time.sleep失败或KeyboardInterrupt
4.2 错误场景
def analyze_with_fallback(video_path, model_index=0):
current_model = MODELS[model_index]
try:
response = client.chat.completions.create(
model=current_model,
response_model=TrafficAccidentAnalysis,
...
)
response.used_model = current_model # ❌ 字段污染
return response
except Exception as e:
time.sleep(1)
return analyze_with_fallback(video_path, model_index + 1) # 递归
4.3 为什么会发生这个错误?
- 字段污染:参考错误 2,给 Pydantic 实例加未声明字段。
- 递归陷阱:如果
time.sleep在 except 块里,每次重试都会等;但如果用户在等待时按 Ctrl+C,会因为递归太深导致 KeyboardInterrupt 难以处理。
4.4 解决方案
def analyze_with_fallback(video_path, model_index=0):
"""返回 (response, used_model) 元组"""
if model_index >= len(MODELS):
return None, None
current_model = MODELS[model_index]
try:
response = client.chat.completions.create(
model=current_model,
response_model=TrafficAccidentAnalysis,
...
)
return response, current_model # ✅ 用元组返回
except Exception as e:
time.sleep(2)
return analyze_with_fallback(video_path, model_index + 1)
# 调用
result, used_model = analyze_with_fallback(video_path)
4.5 进阶优化:循环代替递归
如果模型特别多,递归可能太深,可以改成循环:
def analyze_with_fallback(video_path):
for model_index, current_model in enumerate(MODELS):
try:
response = client.chat.completions.create(
model=current_model,
response_model=TrafficAccidentAnalysis,
...
)
return response, current_model
except Exception as e:
print(f"模型 {current_model} 失败: {e}")
time.sleep(2)
continue
return None, None
4.6 避坑口诀
多模型 fallback 用元组返回模型名;模型很多时用循环代替递归。
总结:避坑清单
| 错误类型 | 错误信息关键词 | 一句话修复 |
|---|---|---|
| 错误 1 | multiple tool calls |
改为 mode=instructor.Mode.MD_JSON |
| 错误 2 | object has no field |
Pydantic 不能加字段,用元组/dict 携带 |
| 错误 3 | Unexpected item type |
切换到 MD_JSON 模式 |
| 错误 4 | 多模型 fallback 字段污染 | 用元组返回,或循环代替递归 |
终极口诀
- 遇到 instructor 报错,先看错误信息的关键词,对应到上面 4 类问题。
- MD_JSON 是万能兜底模式,80% 的兼容性问题用它就能解决。
- Pydantic 模型字段必须显式声明,不能"加塞"。
- 多模型 fallback 用元组或循环,别让 Pydantic 实例承担额外数据。
- 不要忘了捕获异常后
time.sleep(1~2),避免被限流。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)