[BUG] Gemini native provider never appends trailing user turn -> 400 ‘Requests ending with a model turn are not supported’

该报错发生在 CrewAI 使用原生 Gemini provider(模型字符串以 gemini/ 或 google/ 开头)且启用了 guardrail 重试或触发 handle_max_iterations_exceeded 时,消息历史以模型回合结束导致 Gemini API 返回 400。优

快速结论:该报错发生在 CrewAI 使用原生 Gemini provider(模型字符串以 gemini/google/ 开头)且启用了 guardrail 重试或触发 handle_max_iterations_exceeded 时,消息历史以模型回合结束导致 Gemini API 返回 400。优先排查消息格式化逻辑是否在历史末尾补上用户回合,临时规避可设置 guardrail_max_retries=0 并调高 max_iterations

适用环境:CrewAI 1.6.0(含 crewAI Tools 1.6.0)、Python 3.11、Venv 虚拟环境、Google Gemini API(AI Studio 和 Vertex 均受影响)、已安装 google-genai 扩展以启用原生 provider。

最快修复方案:暂无确认的一步修复方案(Issue 关闭时 PR 尚未合入)。可优先尝试以下两个已验证的规避方法:① 提高 max_iterations 并设置 guardrail_max_retries=0 以避开重入路径;② 卸载 google-genai,让 gemini/* 回退到 LiteLLM 路径(该路径已有尾随用户回合防护)。

注意事项:上述规避方法仅绕过问题,不解决根因;卸载 google-genai 后原生 provider 不可用,模型请求将走 LiteLLM 替代路径,可能影响其他依赖原生 provider 的功能。Issue 中提出的正式修复(在 _format_messages_for_gemini 末尾补齐用户回合)尚在 PR 审查阶段,未发布到正式版本。

问题场景

用户在 CrewAI 中使用 LLM(model="gemini/gemini-flash-latest") 或任何 google/.../gemini/... 模型字符串,并安装了 google-genai 扩展(此时 CrewAI 会走原生 Google Gen AI provider 而非 LiteLLM 回退路径)。当任务配置了 guardrail 且设置 guardrail_max_retries 大于 0,或代理循环触发了 handle_max_iterations_exceeded 时,执行器会在已经追加了 assistant/tool-call 回合且没有插入用户回合的情况下重新调用 LLM,导致发送给 Gemini generateContent API 的消息历史以模型回合结尾而被拒绝。

Issue 作者在单代理 Crew 中配置 Task 的 guardrail=<completeness_check>guardrail_max_retries=2 时稳定复现该问题;短任务、无 guardrail 的 Crew(如 Google 官方 CrewAI+Gemini 示例)不会触发。

报错原文

[BUG] Gemini native provider never appends trailing user turn -> 400 'Requests ending with a model turn are not supported'

400 INVALID_ARGUMENT. {'error': {'code': 400, 'message': 'Requests ending with a model turn are not supported.', 'status': 'INVALID_ARGUMENT'}}

原因分析

可能原因:CrewAI 的原生 Gemini/Google provider(crewai/llms/providers/gemini/completion.py)中的 GeminiCompletion._format_messages_for_gemini 方法在将 assistant 消息映射为 Gemini 的 model 角色时(包括纯文本分支和工具调用分支),没有检查格式化后的 contents 列表是否以 model 回合结尾。CrewAI 自己的代理循环可以产生这种历史——例如 handle_max_iterations_exceeded 追加一条 assistant 消息后重新调用 LLM,或任务 guardrail/guardrail 重试次数耗尽后的再次调用——这种畸形历史被原样发送到 Gemini API,被其拒绝。

值得注意的是,CrewAI 的 LLM 回退路径(LLM._format_messages_for_provider)中已经有针对 Mistral 和 Ollama 的同类防护(在最后一条消息是 assistant 时追加合成用户消息),但原生 Gemini provider 没有这一防护,导致两条路径行为不一致。

环境排查

  • CrewAI 版本:1.6.0(issue 作者确认在 release 版本和当前 main 分支均存在此问题)
  • crewAI Tools 版本:1.6.0
  • Python 版本:3.11
  • 虚拟环境:Venv
  • 依赖:确认已安装 google-genai 扩展(未安装时走 LiteLLM 回退路径,不会触发此问题)
  • 操作系统:其他(issue 未指明具体系统)
  • Gemini API 端点:AI Studio 和 Vertex 均执行相同的 contents 契约

解决步骤

  1. 临时规避(已验证):提高 max_iterations 并设置 guardrail_max_retries=0,避免代理循环在 assistant/tool-call 回合后重新调用 LLM。
  2. 替代规避(已验证):卸载 google-genai 扩展,使 gemini/* 模型字符串回退到 LiteLLM 路径——该路径的 _format_messages_for_provider 已包含尾随用户回合防护。
  3. 正式修复(待合入):Issue 作者已提交 PR,计划在 _format_messages_for_gemini 末尾镜像 Mistral 的防护逻辑:当最后一条 content 是 model 回合时,追加一条合成用户消息(文本为 “Please continue.”,与 Mistral 路径一致),使代理循环在 guardrail 重试或 handle_max_iterations_exceeded 后可以安全地重新调用 LLM。回归测试覆盖尾随 assistant 场景以及无需补丁的场景(已用户回合结尾、工具响应结尾、仅系统消息)。
  4. 直接复现脚本(用于验证):直接调用 GeminiCompletion._format_messages_for_gemini,传入以 assistant 回合结束的历史,观察返回的 contents 角色列表是否以 model 结尾。

验证方法

修复后,直接调用 _format_messages_for_gemini 并传入以 assistant 回合结束的历史(例如 [{"role": "user", "content": "hi"}, {"role": "assistant", "content": "partial"}]),确认返回的 contents 列表最后一个角色是 user 而不是 model。在真实代理运行中,配置 guardrail 且 guardrail_max_retries 大于 0,确认触发重试后不再收到 400 INVALID_ARGUMENT 且任务能正常完成。当前 Issue 关闭时 PR 尚未合入,需自行应用补丁或等待后续版本。

参考来源

crewAIInc/crewAI #6984

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 21616

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注