agentscope-ai / agentscope-ai/agentscope-java

[Bug]:ReActAgent 调用不存在的工具时直接退出 ReAct 循环,未给模型重试机会

Đang mở
#3,102 2 bình luận 0 reaction 0 người được giao Xem trên GitHub
area/core/agent area/core/model bug
Ngôn ngữ chính
Java
Star
5.6k
Fork
1.3k
Merge trung bình
4 ngày 12 giờ
Pull request đã merge (30 ngày)
77

Mô tả

# ReActAgent 调用不存在的工具时直接退出 ReAct 循环,未给模型重试机会

## 问题描述

当 LLM 在流式响应中尝试调用一个不在 tools schema 里的工具(如把 Skill 名 `metric_condition_generation` 当工具调用),LLM 服务端会返回 error。agentscope 的 `OpenAIClient` 把这个 error 包装为 `OpenAIException` 抛出,`ReActAgent.reasoning()` 只捕获 `InterruptedException`,其他异常直接传播终止整个 `streamEvents` 流。

模型没有机会纠正错误、换一个工具或改用纯文本回复——整个对话直接以 error 结束。

## 复现场景

1. 配置了一个 Agent,有 tools 但没有 `metric_condition_generation` 这个工具
2. 模型在 reasoning 阶段幻觉式调用了 `metric_condition_generation`(这是一个 Skill 名,不是工具名)
3. LLM 服务端在流式响应中返回 error:`LLM is trying to invoke a non-exist tool: "metric_condition_generation"`
4. `OpenAIClient` 把 error 包装为 `OpenAIException` 并抛出
5. `ReActAgent.reasoning()` 的 `onErrorResume` 只捕获 `InterruptedException`,`OpenAIException` 直接传播
6. 整个 `streamEvents` 流以 error 终止,对话直接结束

## 实际结果

```
OpenAI API error in streaming response: LLM is trying to invoke a non-exist tool: "metric_condition_generation", you can add some few shots examples or adjust the prompt.
```

对话直接终止,用户看到错误,没有结果。

## 期望结果

当 LLM 尝试调用不存在的工具时,框架应该:
1. 捕获这个 error,不终止整个 ReAct 循环
2. 构造一个 `ToolResultBlock.error("工具 X 不存在,请使用已注册的工具或直接回复用户")` 作为该 tool_call 的结果
3. 把这个 error 结果加入上下文,继续下一轮 reasoning——让模型看到错误后自行纠正
4. 如果模型多次(如 2~3 次)仍然调用不存在的工具,才终止循环

## 根因分析

### 错误产生路径

```
LLM 返回 tool_call(name="metric_condition_generation")
→ LLM 服务端发现该工具不在 tools schema 里
→ 流式响应返回 error
→ OpenAIClient/OpenAIResponseParser 解析 error
→ 抛出 OpenAIException("OpenAI API error in streaming response: LLM is trying to invoke a non-exist tool: ...")
→ ReActAgent.reasoning() 的 model.stream() 报错
→ onErrorResume 只捕获 InterruptedException
→ OpenAIException 直接传播
→ streamEvents 流终止
```

### 涉及的代码

**OpenAIClient**(agentscope-extensions-model-openai):
- `OpenAIClient.stream()` 解析流式响应时,发现 `OpenAIResponse.isError()` 为 true
- 构造 `OpenAIException` 并通过 `SynchronousSink.error()` 抛出

**OpenAIResponseParser**(agentscope-extensions-model-openai):
- 同样有 "OpenAI API error in streaming response" 字符串

**ReActAgent.CallExecution.reasoning()**(agentscope-core):
- `model.stream().doOnNext(this::publishEvent).then(...)` 的 stream 因 error 终止
- `onErrorResume(InterruptedException.class, ...)` 只捕获 `InterruptedException`
- 其他异常(如 `OpenAIException`)直接传播,终止整个 reasoning 循环

### 对比 ToolExecutor 的处理

`ToolExecutor.invokeTool()` 在工具不存在时(`toolRegistry.getTool(name)` 返回 null),会构造 `ToolResultBlock.error("Tool not found: " + name)` 并正常返回,不抛异常。这给了模型重试的机会。

但 LLM 服务端在流式阶段就拦截了——工具名不在 tools schema 里,服务端直接返回 error,不走 agentscope 的 `ToolExecutor`。所以 `ToolExecutor` 的兜底逻辑在这个场景下不生效。

## 建议修复

### 方案 A:在 reasoning 的 onErrorResume 中捕获 OpenAIException

```java
// ReActAgent.CallExecution.reasoning() 里
.onErrorResume(OpenAIException.class, error -> {
// 构造一个 error ToolResultBlock,让模型在下一轮看到错误后自行纠正
Msg errorMsg = Msg.builder()
.role(MsgRole.ASSISTANT)
.content(TextBlock.builder().text("工具调用失败: " + error.getMessage()).build())
.build();
state.contextMutable().add(errorMsg);
// 继续下一轮 reasoning,给模型纠正的机会
return reasoning(iter + 1, true); // ignoreMaxIters=true 保证至少重试一次
})
```

### 方案 B:在 OpenAIClient 里不抛异常,改为返回 error ChatResponse

```java
// OpenAIClient.stream() 里解析到 error response 时
if (response.isError()) {
// 不抛异常,改为构造一个携带错误信息的 ChatResponse
return Flux.just(ChatResponse.error(response.getEffectiveErrorMessage()));
}
```

这样 `ReActAgent.modelCallStream` 的 `concatMap` 会正常处理这个 error ChatResponse,可以转换为 error event 发给下游,不会终止流。

### 推荐方案 B

方案 A 在 `reasoning` 层兜底,但 error 的语义不明确(是工具不存在还是网络错误?);方案 B 在源头处理,让 error 成为流中的一个正常事件,框架可以在 `concatMap` 里把它转成 error `ToolResultBlock`,和 `ToolExecutor` 的 "Tool not found" 处理对齐。

## 环境信息

- agentscope-core: 2.0.3
- agentscope-extensions-model-openai: 2.0.3
- JDK: 17
- 模型服务: 百炼 qwen-plus / antchat Qwen3.5-35B-A3B

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.