Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[已修复,附修复文件] AstrBot 在长工具调用链场景下(1 条 user 消息 + 大量 assistant/tool 循环),调用智谱 (Zhipu) 大模型 API 时报错(openai.BadRequestError: Error code: 400 - {'error': {'code': '1214', 'message': 'messages 参数非法。请检查文档。'}} #6196

Copy link
Copy link

Description

@leonforcode
Issue body actions

What happened / 发生了什么

1. 问题现象

AstrBot 在长工具调用链场景下(1 条 user 消息 + 大量 assistant/tool 循环),调用智谱 (Zhipu) 大模型 API 时报错:

Error code: 400 - {'error': {'code': '1214', 'message': 'messages 参数非法。请检查文档。'}}

主模型 glm-4.7 和备用模型 glm-5 均返回同样错误,导致本轮对话彻底失败。


2. 问题分析

2.1 日志关键线索

[BefCompact] RunCtx.messages -> [44] system,user,assistant,tool,...,assistant,tool
[AftCompact] RunCtx.messages -> [41] system,assistant,tool,...,assistant,tool

压缩前有 44 条消息,压缩后变成 41 条。关键变化:user 消息在压缩后消失了,消息序列从 system,user,... 变成了 system,assistant,...

2.2 根本原因

问题出在 truncator.py 的截断逻辑中。调用链如下:

manager.py: process()
  → 检查 token 超限 → TruncateByTurnsCompressor.__call__()
    → truncator.truncate_by_dropping_oldest_turns()
      → 如果还超限 → truncator.truncate_by_halving()

三个截断方法(truncate_by_turnstruncate_by_dropping_oldest_turnstruncate_by_halving)都存在同一缺陷:

  1. 将消息分为 system_messages + non_system_messages
  2. non_system_messages 前端或中间截断
  3. 在截断后的列表中查找第一个 user 消息,如果找不到就清空或以 assistant 开头

在纯工具调用对话中(用户只发了 1 条消息,后面全是 assistant/tool 循环),唯一的 user 消息位于 non_system_messages 最前端。截断时被删除后,剩余消息全是 assistant/tool,没有任何 user 消息。

这导致发送给 API 的消息格式变成 system → assistant → tool → ...,违反了智谱 API 的格式要求(system 后必须紧跟 user),触发 1214 错误。

2.3 影响范围

  • 所有使用智谱(及其他对消息格式有严格要求的)模型的场景
  • 长工具调用链对话(单条 user 消息 + 多轮 tool 调用)最容易触发
  • truncate_by_dropping_oldest_turns()truncate_by_halving()truncate_by_turns() 三个方法均存在此问题

3. 修复方案

3.1 修改策略

truncator.py 中新增两个方法,并修改三个截断方法的返回逻辑:

变更 说明
新增 _split_system_and_rest() 提取重复的 system/非system 拆分逻辑为公共方法,消除三处重复代码
新增 _ensure_first_user_message() 核心修复方法。在截断结果中检查是否仍保留 user 消息;如果被截掉,则从原始消息列表中找回第一条 user 消息并插入到 system 之后
修改 truncate_by_turns() 末尾改为调用 _ensure_first_user_message()
修改 truncate_by_dropping_oldest_turns() 末尾改为调用 _ensure_first_user_message()(报错的直接触发点)
修改 truncate_by_halving() 末尾改为调用 _ensure_first_user_message()

3.2 _ensure_first_user_message() 核心逻辑

def _ensure_first_user_message(self, system_messages, non_system_messages, original_messages):
    # 1. 快速路径:截断后第一条非system消息已是 user → 直接返回
    # 2. 否则从原始消息中找到第一条 user 消息
    # 3. 用 identity check (is) 避免重复插入
    # 4. 将 user 消息插入到 system 之后、其余消息之前
    # 5. 最后经过 fix_messages() 修复 tool_call 配对

4. 测试验证

# 测试场景 结果
1 1 条 user + 20 轮 tool 调用,drop oldest 1 轮 ✅ system→user 保持
2 1 条 user + 21 轮 tool 调用,对半砍 ✅ system→user 保持
3 1 条 user + 20 轮 tool 调用,keep=5 轮 ✅ system→user 保持
4 极端:drop 19 轮(几乎全删) ✅ system→user 保持
5 正常多轮对话(多个 user 消息) ✅ 无回归影响
6 TruncateByTurnsCompressor 端到端完整流程 ✅ 消息格式合法

5. 文件清单

文件 说明
truncator.py 修复后的完整文件副本
修复报告.md 本文档

6. 注意事项

  • 修改后需要重启 AstrBot 实例才能生效

Reproduce / 如何复现?

在长工具调用的情况下调用智谱API

AstrBot version, deployment method (e.g., Windows Docker Desktop deployment), provider used, and messaging platform used. / AstrBot 版本、部署方式(如 Windows Docker Desktop 部署)、使用的提供商、使用的消息平台适配器

Mac 系统本地部署 4.19.5/4.19.4 均出现,更老版本应该也会出现

OS

macOS

Logs / 报错日志

[2026-03-13 11:58:12.239] [Core] [DBUG] [runners.tool_loop_agent_runner:297]: [BefCompact] RunCtx.messages -> [42] system,user,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool
[2026-03-13 11:58:12.240] [Core] [DBUG] [runners.tool_loop_agent_runner:297]: [AftCompact] RunCtx.messages -> [42] system,user,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool
[2026-03-13 11:58:14.988] [Core] [DBUG] [sources.openai_source:262]: completion: ChatCompletion(id='20260313115812556f6395fb694aba', choices=[Choice(finish_reason='tool_calls', index=0, logprobs=None, message=ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageFunctionToolCall(id='call_-7807195287843241881', function=Function(arguments='{"command":"cd /Users/macbook/TrendRadar_Local && git branch -a"}', name='astrbot_execute_shell'), type='function', index=0)], reasoning_content='好的,这是一个从 GitHub 克隆的仓库。让我检查一下当前分支和状态。'))], created=1773374294, model='glm-4.7', object='chat.completion', service_tier=None, system_fingerprint=None, usage=CompletionUsage(completion_tokens=46, prompt_tokens=44993, total_tokens=45039, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=18, rejected_prediction_tokens=None), prompt_tokens_details=PromptTokensDetails(audio_tokens=None, cached_tokens=44929)), request_id='20260313115812556f6395fb694aba')
[2026-03-13 11:58:14.993] [Core] [INFO] [respond.stage:184]: Prepare to send - ou_0f7d1/ou_0f7d1ef17612aa1936cd24700d6531ec:
[2026-03-13 11:58:14.993] [Core] [INFO] [respond.stage:215]: 消息为空,跳过发送阶段
[2026-03-13 11:58:14.993] [Core] [INFO] [runners.tool_loop_agent_runner:657]: Agent 使用工具: ['astrbot_execute_shell']
[2026-03-13 11:58:14.994] [Core] [INFO] [runners.tool_loop_agent_runner:703]: 使用工具:astrbot_execute_shell,参数:{'command': 'cd /Users/macbook/TrendRadar_Local && git branch -a'}
[2026-03-13 11:58:15.054] [Core] [INFO] [runners.tool_loop_agent_runner:881]: Tool astrbot_execute_shell Result: {"stdout": " remotes/origin/master\n", "stderr": "", "exit_code": 0}
[2026-03-13 11:58:15.056] [Core] [DBUG] [runners.tool_loop_agent_runner:297]: [BefCompact] RunCtx.messages -> [44] system,user,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool
[2026-03-13 11:58:15.056] [Core] [DBUG] [runners.tool_loop_agent_runner:297]: [AftCompact] RunCtx.messages -> [41] system,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,tool
[2026-03-13 11:58:15.265] [Core] [WARN] [v4.19.4] [runners.tool_loop_agent_runner:268]: Chat Model zhipu/glm-4.7 request error: Error code: 400 - {'error': {'code': '1214', 'message': 'messages 参数非法。请检查文档。'}}
Traceback (most recent call last):
File "/Users/macbook/.astrbot_launcher/instances/57240f40-0c61-44bd-86a8-224bb783c51f/core/astrbot/core/agent/runners/tool_loop_agent_runner.py", line 243, in _iter_llm_responses_with_fallback
async for resp in self._iter_llm_responses(include_model=idx == 0):
File "/Users/macbook/.astrbot_launcher/instances/57240f40-0c61-44bd-86a8-224bb783c51f/core/astrbot/core/agent/runners/tool_loop_agent_runner.py", line 220, in _iter_llm_responses
yield await self.provider.text_chat(**payload)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/macbook/.astrbot_launcher/instances/57240f40-0c61-44bd-86a8-224bb783c51f/core/astrbot/core/provider/sources/openai_source.py", line 763, in text_chat
) = await self._handle_api_error(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/macbook/.astrbot_launcher/instances/57240f40-0c61-44bd-86a8-224bb783c51f/core/astrbot/core/provider/sources/openai_source.py", line 714, in _handle_api_error
raise e
File "/Users/macbook/.astrbot_launcher/instances/57240f40-0c61-44bd-86a8-224bb783c51f/core/astrbot/core/provider/sources/openai_source.py", line 751, in text_chat
llm_response = await self._query(payloads, func_tool)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/macbook/.astrbot_launcher/instances/57240f40-0c61-44bd-86a8-224bb783c51f/core/astrbot/core/provider/sources/openai_source.py", line 251, in _query
completion = await self.client.chat.completions.create(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/macbook/.astrbot_launcher/instances/57240f40-0c61-44bd-86a8-224bb783c51f/venv/lib/python3.12/site-packages/openai/resources/chat/completions/completions.py", line 2714, in create
return await self._post(
^^^^^^^^^^^^^^^^^
File "/Users/macbook/.astrbot_launcher/instances/57240f40-0c61-44bd-86a8-224bb783c51f/venv/lib/python3.12/site-packages/openai/_base_client.py", line 1884, in post
return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/macbook/.astrbot_launcher/instances/57240f40-0c61-44bd-86a8-224bb783c51f/venv/lib/python3.12/site-packages/openai/_base_client.py", line 1669, in request
raise self._make_status_error_from_response(err.response) from None
openai.BadRequestError: Error code: 400 - {'error': {'code': '1214', 'message': 'messages 参数非法。请检查文档。'}}
[2026-03-13 11:58:15.292] [Core] [WARN] [v4.19.4] [runners.tool_loop_agent_runner:235]: Switched from zhipu/glm-4.7 to fallback chat provider: zhipu/glm-5
[2026-03-13 11:58:15.724] [Core] [WARN] [v4.19.4] [runners.tool_loop_agent_runner:268]: Chat Model zhipu/glm-5 request error: Error code: 400 - {'error': {'code': '1214', 'message': 'messages 参数非法。请检查文档。'}}
Traceback (most recent call last):
File "/Users/macbook/.astrbot_launcher/instances/57240f40-0c61-44bd-86a8-224bb783c51f/core/astrbot/core/agent/runners/tool_loop_agent_runner.py", line 243, in _iter_llm_responses_with_fallback
async for resp in self._iter_llm_responses(include_model=idx == 0):
File "/Users/macbook/.astrbot_launcher/instances/57240f40-0c61-44bd-86a8-224bb783c51f/core/astrbot/core/agent/runners/tool_loop_agent_runner.py", line 220, in _iter_llm_responses
yield await self.provider.text_chat(**payload)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/macbook/.astrbot_launcher/instances/57240f40-0c61-44bd-86a8-224bb783c51f/core/astrbot/core/provider/sources/openai_source.py", line 763, in text_chat
) = await self._handle_api_error(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/macbook/.astrbot_launcher/instances/57240f40-0c61-44bd-86a8-224bb783c51f/core/astrbot/core/provider/sources/openai_source.py", line 714, in _handle_api_error
raise e
File "/Users/macbook/.astrbot_launcher/instances/57240f40-0c61-44bd-86a8-224bb783c51f/core/astrbot/core/provider/sources/openai_source.py", line 751, in text_chat
llm_response = await self._query(payloads, func_tool)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/macbook/.astrbot_launcher/instances/57240f40-0c61-44bd-86a8-224bb783c51f/core/astrbot/core/provider/sources/openai_source.py", line 251, in _query
completion = await self.client.chat.completions.create(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/macbook/.astrbot_launcher/instances/57240f40-0c61-44bd-86a8-224bb783c51f/venv/lib/python3.12/site-packages/openai/resources/chat/completions/completions.py", line 2714, in create
return await self._post(
^^^^^^^^^^^^^^^^^
File "/Users/macbook/.astrbot_launcher/instances/57240f40-0c61-44bd-86a8-224bb783c51f/venv/lib/python3.12/site-packages/openai/_base_client.py", line 1884, in post
return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/macbook/.astrbot_launcher/instances/57240f40-0c61-44bd-86a8-224bb783c51f/venv/lib/python3.12/site-packages/openai/_base_client.py", line 1669, in request
raise self._make_status_error_from_response(err.response) from None
openai.BadRequestError: Error code: 400 - {'error': {'code': '1214', 'message': 'messages 参数非法。请检查文档。'}}

Are you willing to submit a PR? / 你愿意提交 PR 吗?

  • Yes!

Code of Conduct

Reactions are currently unavailable

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:coreThe bug / feature is about astrbot's core, backendThe bug / feature is about astrbot's core, backendbugSomething isn't workingSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    Morty Proxy This is a proxified and sanitized view of the page, visit original site.