What happened / 发生了什么
DeepSeek V4 以anthropic提供商格式接入时,并行调用多个工具会导致400报错。
当 Assistant 在一次响应中并行发起多个 tool_use 时,AstrBot 的 Anthropic 适配层将后续多个 role="tool" 的返回结果转换成了多条独立的 User 消息。这可能导致 Anthropic api 校验失败,抛出 400 错误。
AstrBot 的 anthropic_source.py 将多个 role="tool" 的返回结果映射成了多条独立的 User 消息,导致 Anthropic 看到的序列如下:
Assistant: [tool_use_00, tool_use_01] ← 两个并行工具调用
User: [tool_result_00] ← 被当成“下一条消息”
User: [tool_result_01] ← Anthropic api可能认为这条是“多余的用户消息”
因此 Anthropic 校验逻辑判定:tool_use_01(call_01_BhsvXomagk6BoekAmAaC5Gtp)之后没有紧跟的 tool_result,直接拒绝请求并返回 400。
修复方向:在 anthropic_source.py 的消息转换逻辑中,当遇到多个连续的 role="tool" 消息时,应将其合并为单条 User 消息,结构示例:
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "call_00_5EQ0V4NahGLAKFDZp8hAVwLT",
"content": "...SKILL.md 内容..."
},
{
"type": "tool_result",
"tool_use_id": "call_01_BhsvXomagk6BoekAmAaC5Gtp",
"content": "...pubmed.py 内容..."
}
]
}
Reproduce / 如何复现?
- 配置使用 Anthropic 兼容格式的 deepseek-v4-flash
- 触发一次需要并行读取两个文件(或并行调用两个工具)的请求。
- 观察 API 返回 400 错误。
AstrBot version, deployment method (e.g., Windows Docker Desktop deployment), provider used, and messaging platform used. / AstrBot 版本、部署方式(如 Windows Docker Desktop 部署)、使用的提供商、使用的消息平台适配器
Docker部署,宿主机 Ubuntu server 24.04
OS
Linux
Logs / 报错日志
触发报错的结构:
[
{
"role": "assistant",
"content": [
{
"type": "think",
"think": "主人问有没有PubMed这个Skill...",
"encrypted": "..."
},
{
"type": "text",
"text": "目前还没有正式发布的Skill呢~不过堇的本事可不少!..."
}
],
"tool_calls": [
{
"type": "function",
"id": "call_00_sJcXYPcbhpxQYDPCjuVgDmf4",
"function": {
"name": "astrbot_list_skill_releases",
"arguments": "{}"
}
}
]
},
{
"role": "tool",
"content": "{\"items\": [], \"total\": 0}",
"tool_call_id": "call_00_sJcXYPcbhpxQYDPCjuVgDmf4"
},
{
"role": "assistant",
"content": [
{
"type": "think",
"think": "目前没有已发布的技能。那我直接给主人列出我所有能调用的工具和能力吧!",
"encrypted": "..."
},
{
"type": "text",
"text": "目前还没有正式发布的Skill呢~不过堇的本事可不少!..."
}
],
"tool_calls": [
{
"type": "function",
"id": "call_00_5EQ0V4NahGLAKFDZp8hAVwLT",
"function": {
"name": "astrbot_file_read_tool",
"arguments": "{\"path\": \"/workspace/skills/pubmed/SKILL.md\"}"
}
},
{
"type": "function",
"id": "call_01_BhsvXomagk6BoekAmAaC5Gtp",
"function": {
"name": "astrbot_file_read_tool",
"arguments": "{\"path\": \"/workspace/skills/pubmed/scripts/pubmed.py\"}"
}
}
]
},
{
"role": "tool",
"content": "---\nname: pubmed\ndescription: 你是一位拥有医学背景的文献检索专家...\n...(SKILL.md 完整内容)...",
"tool_call_id": "call_00_5EQ0V4NahGLAKFDZp8hAVwLT"
},
{
"role": "tool",
"content": "#!/usr/bin/env python3\n...(pubmed.py 完整内容)...",
"tool_call_id": "call_01_BhsvXomagk6BoekAmAaC5Gtp"
},
{
"role": "assistant",
"content": [
{
"type": "think",
"think": "主人确实上传了PubMed的SKILL文件!...",
"encrypted": null
},
{
"type": "text",
"text": "对不起主人!堇刚才说没有PubMed Skill..."
}
]
}
]
注: 报错似乎发生在最后一条 role="assistant" 消息被提交给 LLM 时。API 校验发现 call_01_BhsvXomagk6BoekAmAaC5Gtp 没有紧跟的 tool_result 块。
2026-04-28 16:59:55.306] [Core] [WARN] [v4.23.6] [runners.tool_loop_agent_runner:555]: Chat Model deepseek-a/deepseek-v4-flash request error: Error code: 400 - {'error': {'message': 'messages.43:`tool_use` ids were found without `tool_result` blocks immediately after: call_01_BhsvXomagk6BoekAmAaC5Gtp. Each `tool_use` block must have a corresponding `tool_result` block in the next message.', 'type': 'invalid_request_error', 'param': None, 'code': 'invalid_request_error'}}
Traceback (most recent call last):
File "/AstrBot/astrbot/core/agent/runners/tool_loop_agent_runner.py", line 510, in _iter_llm_responses_with_fallback
async for attempt in retrying:
File "/usr/local/lib/python3.12/site-packages/tenacity/asyncio/__init__.py", line 170, in __anext__
do = await self.iter(retry_state=self._retry_state)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/tenacity/asyncio/__init__.py", line 157, in iter
result = await action(retry_state)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/tenacity/_utils.py", line 111, in inner
return call(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/tenacity/__init__.py", line 393, in <lambda>
self._add_action_func(lambda rs: rs.outcome.result())
^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/concurrent/futures/_base.py", line 449, in result
return self.__get_result()
^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/concurrent/futures/_base.py", line 401, in __get_result
raise self._exception
File "/AstrBot/astrbot/core/agent/runners/tool_loop_agent_runner.py", line 514, in _iter_llm_responses_with_fallback
async for resp in self._iter_llm_responses(
File "/AstrBot/astrbot/core/agent/runners/tool_loop_agent_runner.py", line 477, in _iter_llm_responses
yield await self.provider.text_chat(**payload)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/AstrBot/astrbot/core/provider/sources/anthropic_source.py", line 581, in text_chat
raise e
File "/AstrBot/astrbot/core/provider/sources/anthropic_source.py", line 579, in text_chat
llm_response = await self._query(payloads, func_tool)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/AstrBot/astrbot/core/provider/sources/anthropic_source.py", line 299, in _query
completion = await self.client.messages.create(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/anthropic/resources/messages/messages.py", line 2447, in create
return await self._post(
^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/anthropic/_base_client.py", line 1996, in post
return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/anthropic/_base_client.py", line 1781, in request
raise self._make_status_error_from_response(err.response) from None
anthropic.BadRequestError: Error code: 400 - {'error': {'message': 'messages.43:`tool_use` ids were found without `tool_result` blocks immediately after: call_01_BhsvXomagk6BoekAmAaC5Gtp. Each `tool_use` block must have a corresponding `tool_result` block in the next message.', 'type': 'invalid_request_error', 'param': None, 'code': 'invalid_request_error'}}
[2026-04-28 16:59:55.327] [Core] [WARN] [v4.23.6] [runners.tool_loop_agent_runner:492]: Switched from deepseek-a/deepseek-v4-flash to fallback chat provider: 阿里百炼/deepseek-v4-flash
Are you willing to submit a PR? / 你愿意提交 PR 吗?
Code of Conduct
What happened / 发生了什么
DeepSeek V4 以anthropic提供商格式接入时,并行调用多个工具会导致400报错。
当 Assistant 在一次响应中并行发起多个 tool_use 时,AstrBot 的 Anthropic 适配层将后续多个 role="tool" 的返回结果转换成了多条独立的 User 消息。这可能导致 Anthropic api 校验失败,抛出 400 错误。
AstrBot 的 anthropic_source.py 将多个 role="tool" 的返回结果映射成了多条独立的 User 消息,导致 Anthropic 看到的序列如下:
因此 Anthropic 校验逻辑判定:tool_use_01(call_01_BhsvXomagk6BoekAmAaC5Gtp)之后没有紧跟的 tool_result,直接拒绝请求并返回 400。
修复方向:在 anthropic_source.py 的消息转换逻辑中,当遇到多个连续的 role="tool" 消息时,应将其合并为单条 User 消息,结构示例:
Reproduce / 如何复现?
AstrBot version, deployment method (e.g., Windows Docker Desktop deployment), provider used, and messaging platform used. / AstrBot 版本、部署方式(如 Windows Docker Desktop 部署)、使用的提供商、使用的消息平台适配器
Docker部署,宿主机 Ubuntu server 24.04
OS
Linux
Logs / 报错日志
触发报错的结构:
注: 报错似乎发生在最后一条 role="assistant" 消息被提交给 LLM 时。API 校验发现 call_01_BhsvXomagk6BoekAmAaC5Gtp 没有紧跟的 tool_result 块。
Are you willing to submit a PR? / 你愿意提交 PR 吗?
Code of Conduct