问题描述
Java 使用 AgentScope 1.0.11 + 非 OpenAI 模型(如 Qwen3-14b)开启流式响应(<font style="color:rgb(0, 0, 0);">stream=true</font>)时,ReAct Agent 执行 MCP 工具调用后,日志中持续输出以下警告且agent无法调用mcp且无法正常返回响应给客户端, 设置 <font style="color:rgb(0, 0, 0);">agent.stream=false</font>后问题消失。
WARN i.a.c.f.o.OpenAIMessageConverter - ToolUseBlock has null id or name, skipping
影响范围
- 功能影响:agent无法调用mcp且无法正常返回响应给客户端
- 日志噪音:每轮 ReAct 迭代重复打印 WARN
- 潜在风险:被丢弃的 ToolUseBlock 不会出现在发给模型的对话历史中,可能影响模型对上下文的理解
根因定位
缺陷位置:<font style="color:rgb(0, 0, 0);">io.agentscope.core.agent.accumulator.ToolCallsAccumulator.ToolCallBuilder#build()</font>
调用链路
- LLM(Qwen3, stream)返回流式 chunk,部分 chunk 不携带
<font style="color:rgb(0, 0, 0);">function.name</font>
<font style="color:rgb(0, 0, 0);">OpenAIResponseParser.parseChunkResponse()</font> 将 name 为 null 的 chunk 设为空串 <font style="color:rgb(0, 0, 0);">""</font>,创建 fragment ToolUseBlock(<font style="color:rgb(0, 0, 0);">name="__fragment__"</font>)
<font style="color:rgb(0, 0, 0);">ToolCallsAccumulator.ToolCallBuilder.merge()</font> 判断 <font style="color:rgb(0, 0, 0);">"__fragment__"</font> 为 placeholder,跳过赋值
<font style="color:rgb(0, 0, 0);">ToolCallBuilder.name</font> 始终为初始值 <font style="color:rgb(0, 0, 0);">null</font>
<font style="color:rgb(0, 0, 0);">ToolCallBuilder.build()</font> 生成 <font style="color:rgb(0, 0, 0);">ToolUseBlock(id=generated, name=null)</font>
<font style="color:rgb(0, 0, 0);">ReasoningContext.buildFinalMessage()</font> 将该 ToolUseBlock 存入 Memory 的 assistant Msg
- 下一轮迭代
<font style="color:rgb(0, 0, 0);">OpenAIMessageConverter.convertAssistantMessage()</font> 检测到 <font style="color:rgb(0, 0, 0);">name==null</font>,打印 WARN 并跳过
代码缺陷
<font style="color:rgb(0, 0, 0);">ToolCallBuilder.build()</font> 对 <font style="color:rgb(0, 0, 0);">toolId</font> 做了 null 兜底(生成合成 ID),但对 <font style="color:rgb(0, 0, 0);">name</font> 没有做任何兜底处理,直接透传了可能为 null 的字段:
// ToolCallBuilder.build() 反编译还原
ToolUseBlock.builder()
.id(toolId != null ? toolId : generateId()) // ✅ 有兜底
.name(name) // ❌ 无兜底,可为 null
.input(mergedArgs)
.build();
临时规避
问题描述
Java 使用 AgentScope 1.0.11 + 非 OpenAI 模型(如 Qwen3-14b)开启流式响应(
<font style="color:rgb(0, 0, 0);">stream=true</font>)时,ReAct Agent 执行 MCP 工具调用后,日志中持续输出以下警告且agent无法调用mcp且无法正常返回响应给客户端, 设置<font style="color:rgb(0, 0, 0);">agent.stream=false</font>后问题消失。影响范围
根因定位
缺陷位置:
<font style="color:rgb(0, 0, 0);">io.agentscope.core.agent.accumulator.ToolCallsAccumulator.ToolCallBuilder#build()</font>调用链路
<font style="color:rgb(0, 0, 0);">function.name</font><font style="color:rgb(0, 0, 0);">OpenAIResponseParser.parseChunkResponse()</font>将 name 为 null 的 chunk 设为空串<font style="color:rgb(0, 0, 0);">""</font>,创建 fragment ToolUseBlock(<font style="color:rgb(0, 0, 0);">name="__fragment__"</font>)<font style="color:rgb(0, 0, 0);">ToolCallsAccumulator.ToolCallBuilder.merge()</font>判断<font style="color:rgb(0, 0, 0);">"__fragment__"</font>为 placeholder,跳过赋值<font style="color:rgb(0, 0, 0);">ToolCallBuilder.name</font>始终为初始值<font style="color:rgb(0, 0, 0);">null</font><font style="color:rgb(0, 0, 0);">ToolCallBuilder.build()</font>生成<font style="color:rgb(0, 0, 0);">ToolUseBlock(id=generated, name=null)</font><font style="color:rgb(0, 0, 0);">ReasoningContext.buildFinalMessage()</font>将该 ToolUseBlock 存入 Memory 的 assistant Msg<font style="color:rgb(0, 0, 0);">OpenAIMessageConverter.convertAssistantMessage()</font>检测到<font style="color:rgb(0, 0, 0);">name==null</font>,打印 WARN 并跳过代码缺陷
<font style="color:rgb(0, 0, 0);">ToolCallBuilder.build()</font>对<font style="color:rgb(0, 0, 0);">toolId</font>做了 null 兜底(生成合成 ID),但对<font style="color:rgb(0, 0, 0);">name</font>没有做任何兜底处理,直接透传了可能为 null 的字段:临时规避