快速结论:这个报错通常出现在 CrewAI 使用 output_pydantic 定义任务输出、并在 prompt 内嵌 JSON Schema 时:prompt 里的 schema 把 Pydantic 模型中的 Optional 字段强行标成 required 且去掉 null,导致模型无法表达“不适用”,只能编造非空值。优先排查 prompt 内 schema 与 provider 侧响应 schema 是否针对 optional / nullable 字段给出了互相矛盾的约束。
适用环境:Issue 中已确认的环境为 CrewAI(复现基于 crewai==1.15.10,后续评论基于 crewai==1.15.16 轮子验证),使用 Pydantic 模型 + Task(output_pydantic=...);报告者所在路径为 LiteLLM(经 InternalInstructor 构建响应 schema)。Issue 未提供操作系统、Python、CUDA、显卡信息,故不补写。
最快修复方案:暂无确认的一步修复方案。Issue 中提出的候选修复是给 generate_model_description 传入已存在的 strip_null_types=False(代码树中 utilities/agent_utils.py 对 tool schema 已有先例),但这是提案,尚未在 Issue 中确认为已合并或已发布的修复。
注意事项:即使启用 strip_null_types=False,ensure_all_properties_required 仍会让 prompt 里的 required 比 Pydantic 原生 schema 更宽,“必填但可为 null”是预期编码,不要用 required 结构相等来判断是否修好。此外评论指出该缺陷至少存在于三种配置(LiteLLM 路径、原生 OpenAI 路径、agent 带 tools 时 provider 侧模型被丢弃的单契约路径),仅在 LiteLLM 路径上两个 schema 才真正互相矛盾;评论区还提到 force_additional_properties_false 会把 dict[str, str] 变成只允许 {} 的对象,属于同一家族但 nullability 修复无法覆盖。
问题场景
在使用 CrewAI 构建任务时,通过 Task(..., output_pydantic=Plan) 指定 Pydantic 输出模型,并由 build_task_prompt_with_schema 把 task.output_pydantic 的 JSON Schema 嵌入 prompt。当输出模型里含有 Optional[str] = Field(default=None) 这类可选字段时,模型收到的 prompt schema 与 provider 侧(例如 LiteLLM / instructor)按同一 Pydantic 模型生成的响应 schema 不一致,导致模型被逼着为每个可选字段填非空值。报告者的实际影响是:规划器把不相关的参数拼接进同一个必填字符串字段、丢掉了本该输出的步骤,个别运行甚至把 JSON 字符串一路写到输出 token 上限,并把困惑直接写进 payload。
报错原文
[BUG] Task output schema in the prompt marks Optional fields as required and strips null, so the model cannot express "not applicable"
pydantic required: ['name']
pydantic note : {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, ...}
in-prompt required: ['name', 'note']
in-prompt note : {"default": null, "type": "string", ...}
"value": "... Wait, the schema requires step_description, element_description, value, k"
原因分析
最可能的原因是 prompt 内嵌 schema 的生成链路对 Pydantic schema 做了面向 OpenAI strict function-calling 的“清洗”,但该调用点并不是 function-calling schema,而是 prompt 中的一段文字,strict 约束本不适用。具体地:generate_model_description 默认 strip_null_types=True,会把每个 anyOf 中的 null 去掉;同时 ensure_all_properties_required 会把所有属性加入 required。两者叠加后,Optional[str] = None 字段在 prompt 里被描述成“必填、非空字符串”,而字段自身的 "type": "string" 旁边又标注 "default": null,内部自相矛盾。
评论补充指出:在两个契约同时存在的 LiteLLM 路径上,instructor 按模型直接生成的 schema 仍允许 null 且不把 note 放进 required,于是同一请求里出现两种相反的约束;而在原生 OpenAI 路径上,provider 侧 schema 也由同一默认值生成并带 strict: True,等于强制了“必填非空字符串”,逃生通道被关闭。失败之所以静默,是因为两个契约的交集恰好是“非空字符串”,而编造出来的非空字符串本身就是合法的 Optional[str],校验无从拦截。
环境排查
- CrewAI 版本:Issue 复现基于
crewai==1.15.10,评论基于crewai==1.15.16验证缺陷仍存在;报告者指出crewai==1.8.1时 prompt 中仍为anyOf: [{"type": "string"}, {"type": "null"}],具体回归版本未二分定位。 - LLM 提供方路径:确认是 LiteLLM(经
InternalInstructor)还是原生 OpenAI(Responses / chat completions)、Gemini 等,不同路径下 provider 侧 schema 行为不同。 - agent 是否配置了 tools:评论指出
effective_response_model = None if self.original_tools else self.response_model,带 tools 时 provider 侧模型会被丢弃,只剩被清洗过的 prompt schema 这一个契约。 - Pydantic 模型定义:确认可选字段是否使用
Optional[...] = None或X | None = None,以便对比model_json_schema()与 prompt 内 schema 的required和anyOf。 - Issue 未提供操作系统、Python 版本、CUDA、显卡等信息,这些项目无法作为排查依据。
解决步骤
- 先用 Issue 提供的复现脚本确认现象:定义含
Optional[str]字段的嵌套模型,分别打印Plan.model_json_schema()与generate_model_description(Plan)["json_schema"]["schema"],对比Step的required及note属性。pip install crewai==1.15.10为本 Issue 复现所用版本。 - 若需要看到 prompt 实际下发内容,调用
build_task_prompt_with_schema(task, "")检查嵌入的 schema。 - 确认自身所处的 LLM 路径与是否带 tools,判断是“两个契约矛盾”还是“只剩一个被清洗后的契约”。
- 可优先尝试的候选修复:在 prompt 内嵌 schema 的生成调用点传入已有参数
strip_null_types=False,即schema_dict = generate_model_description(task.output_pydantic, strip_null_types=False)。该写法在同代码树的utilities/agent_utils.py对 tool schema 已有先例,但 Issue 未确认它已被官方采纳发布,属于提案,需自测。 - 注意不要同时改动
ensure_all_properties_required:评论指出它与_common_strict_pipeline共用,影响 OpenAI / Anthropic / Bedrock 的 strict sanitizer,改动面更大;“必填 + 可为 null”是 strict 模式下的标准写法,保留required更宽是预期行为。 - 若受
force_additional_properties_false影响,需额外检查dict[str, str]等字段:它会被改写成只接受{}的对象,这属于同一家族的另一问题,不在 nullability 修复范围内。
验证方法
用同一复现脚本重新对比:note 在 prompt 内 schema 中应恢复为 anyOf: [{"type": "string"}, {"type": "null"}]。同时不能只看 required 是否与 Pydantic 原生一致——评论明确给出 prompt, post-#6775 状态下 required 仍为 ['name','note'],因此验证应着眼于“该字段是否允许为 null、note 键是否必须出现”这一可接受值集合,而不是 required 结构相等。行为层面,观察原先被迫编造非空值的可选字段是否可以不填或填 null,以及输出是否不再出现把无关参数拼接、把 JSON 值写到 token 上限的情况。
参考来源
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。


