agentscope-ai / agentscope-ai/agentscope

[Bug]: DeveloperOrientedException can emit an empty success and leave tool calls unfinished

Đang mở
#2,618 1 bình luận 0 reaction 0 người được giao Xem trên GitHub
Ngôn ngữ chính
Python
Star
31.6k
Fork
3.5k
Merge trung bình
1 ngày 16 giờ
Pull request đã merge (30 ngày)
103

Mô tả

### Prerequisites

- [x] I have searched the existing [issues](https://github.com/agentscope-ai/agentscope/issues) and [discussions](https://github.com/agentscope-ai/agentscope/discussions), and this is not a duplicate.
- [x] This is a bug, not a usage question. (For questions, please use [Discussions](https://github.com/agentscope-ai/agentscope/discussions/new?category=general) instead.)

### Background / Description

DeveloperOrientedException is intentionally fatal: Toolkit.call_tool re-raises it so programming and framework-contract errors reach the developer instead of being converted into agent-facing tool errors.

The fatal path currently violates the tool-call lifecycle in two steps:

1. Toolkit.call_tool yields the accumulated ToolResponse from finally. When the tool raises DeveloperOrientedException, the generator yields an empty ToolResponse(state=SUCCESS) before the exception continues propagating. A middleware that treats ToolResponse as terminal can stop consuming the generator and hide the fatal error.
2. Once the empty-success emission is removed and the exception reaches the Agent, the reply terminates while its ToolCallBlock can remain unfinished. In Agent Service this leaves the session in awaiting_permission / running state even though the reply has failed.

Concurrent tool execution wraps the fatal exception in ExceptionGroup. The service error classifier checks only the outer exception for DeveloperOrientedException, so the Web UI reports an unknown failure instead of a sanitized internal error.

Expected lifecycle:

```text
DeveloperOrientedException
-> no SUCCESS tool response
-> close every unfinished tool call as FINISHED
-> append a matching ToolResultBlock(INTERRUPTED)
-> terminate the reply with ReplyEnd(ERROR, INTERNAL)
-> keep the raw traceback in server logs with session/reply/tool identifiers
```

This does not change recoverable tool-error behavior. Ordinary exceptions must still become ToolResultState.ERROR results that the agent can observe and reason about.

Related work:

- #1888 / #1905 recover orphan tool calls after mid-stream failures. This report adds the concrete DeveloperOrientedException path and the premature empty-success emission in Toolkit.call_tool.
- #1985 discussed recoverable transport/tool failures. This report preserves the fatal semantics of DeveloperOrientedException.

## Proposed contribution

Because the proposed cleanup changes the core Agent._reply_impl failure path, I would appreciate the maintainers' review of the intended lifecycle and scope before opening a pull request. I already have a working local patch and focused regression tests for the Toolkit response boundary, unfinished tool-call cleanup, and ExceptionGroup classification. If this is considered a valid issue and the approach is acceptable, I would be glad to contribute the fix and adjust it based on your feedback.

### Error Messages

```shell
Before removing the finally emission:

ToolResponse(state=success, content=[])

After allowing the fatal exception to propagate, but before lifecycle cleanup:

ExceptionGroup: One or more tool calls raised an exception
+- DeveloperOrientedException: boom: fatal tool failure

Observed service state after the failed reply:

session status: awaiting_permission
tool call state: asking
Web UI: Reply failed - unknown error
```

### Steps to Reproduce

1. Register a deterministic fatal test tool in Agent Service:

```python
from agentscope.exception import DeveloperOrientedException
from agentscope.tool import FunctionTool, ToolChunk

async def boom_tool() -> ToolChunk:
raise DeveloperOrientedException("boom: fatal tool failure")

async def extra_agent_tools(user_id, agent_id, session_id):
del user_id, agent_id, session_id
return [FunctionTool(boom_tool, description="Fatal error reproduction")]
```

2. Pass `extra_agent_tools=extra_agent_tools` to `create_app` and start the bundled Agent Service and Web UI.
3. In a fresh session, ask the agent to call `boom_tool` exactly once.
4. Approve the tool call when the HITL confirmation card appears.
5. Observe the empty successful result on the original code, or the unfinished tool call / `awaiting_permission` state after only moving the final response out of `finally`.

### Environment

- AgentScope Version: 2.0.8, source checkout at b82253ba1b680a67091c4de32f074477c30ba416
- Python Version: 3.11.15
- OS: macOS 26.6.2 arm64

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

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

Hướng nghiên cứu

Start at Toolkit.call_tool and Agent._reply_impl, then inspect the Agent Service error classifier and the focused regression tests mentioned in the report. Trace fatal and ordinary exceptions separately; done means no empty SUCCESS response, unfinished calls close with matching interruption results, ReplyEnd is INTERNAL, and ExceptionGroup classification is sanitized while raw tracebacks remain logged.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
python
Lĩnh vực
api, backend, testing
Loại issue
Lỗi
Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức độ hoạt động
Sôi nổi
Độ rõ ràng
Đặc tả rõ ràng
Mức phù hợp với người mới
35/100

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.