Skip to content

fix(core): 用户中断 reasoning 时丢弃未执行的工具调用 - #2471

Open
hanydd wants to merge 2 commits into
agentscope-ai:mainfrom
hanydd:feat/interrupt-tool-call
Open

fix(core): 用户中断 reasoning 时丢弃未执行的工具调用#2471
hanydd wants to merge 2 commits into
agentscope-ai:mainfrom
hanydd:feat/interrupt-tool-call

Conversation

@hanydd

@hanydd hanydd commented Jul 28, 2026

Copy link
Copy Markdown

fix: #2470

AgentScope-Java Version

2.0.1-SNAPSHOT

Description

修改概述

修复用户中断 ReActAgent 的 reasoning 阶段时,尚未执行的工具调用被写入 memory,导致后续请求携带孤立 tool_use、会话上下文无法恢复的问题。

问题原因

reasoning 产生的消息会在工具执行前写入 memory。用户中断恰好发生在工具调用流式生成期间,或 reasoning 已完成但 acting 尚未开始时,当前实现仍会保留其中的 ToolUseBlock。由于对应工具没有执行,memory 中不会产生配对的工具结果;重建 Agent 或发起下一轮模型请求时,严格校验消息历史的模型会拒绝这段上下文。

修复方案

  • 用户中断 reasoning 时,仅持久化已经生成的文本和 thinking 内容,丢弃所有尚未执行的 ToolUseBlock
  • 将正常 reasoning 消息的完整持久化延后到 acting 前的中断检查之后,覆盖“工具调用已生成、但 acting 尚未开始”的窗口。
  • 保持系统中断以及 PartialReasoningPolicy 的既有语义不变。

测试覆盖

  • reasoning 流式生成部分工具参数时发生用户中断。
  • reasoning 已生成完整工具调用、acting 开始前发生用户中断。
  • 用户中断纯文本 reasoning 时保留已生成文本。
  • 用户中断仅包含工具调用的 reasoning 时不持久化空消息。
  • 系统中断分别使用 PartialReasoningPolicy.SAVEDISCARD
  • gotoReasoning 时分别处理非空和空 reasoning 消息。
  • 基于中断后的 memory 重建 Agent,并在严格历史校验下继续对话。
  • 本地 JaCoCo 报告中,本 PR 修改的所有可执行行和条件分支均已覆盖。
  • 完整执行 agentscope-core 测试:2228 tests,0 failures,0 errors,8 skipped。

Checklist

Please check the following items before code is ready to be reviewed.

  • Code has been formatted with mvn spotless:apply
  • All tests are passing (mvn -pl agentscope-core test)
  • Javadoc comments are complete and follow project conventions
  • Related documentation has been updated (not applicable; this fix does not change the public API)
  • Code is ready for review

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@codecov

codecov Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@hanydd
hanydd marked this pull request as ready for review July 29, 2026 00:07
@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@oss-maintainer oss-maintainer left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: PR #2471

fix(core): 用户中断 reasoning 时丢弃未执行的工具调用

Summary

Fixes a correctness issue where user-interrupted reasoning could persist orphaned ToolUseBlock entries into the conversation history, making subsequent model calls fail due to unresolvable tool-use references.

Analysis

Root cause: The reasoning message (including any in-flight ToolUseBlock) was persisted to memory before the acting phase. If a user interrupt arrived during the window between tool-call generation and acting start, the tool call was written to state without a corresponding ToolResultBlock, leaving the conversation in an unrecoverable state.

Fix approach — correct and minimal:

  1. persistInterruptedReasoningMessage() — new method that strips ToolUseBlock entries for USER interrupts while preserving text/thinking content. SYSTEM interrupts retain existing PartialReasoningPolicy semantics (unchanged).
  2. Deferred persistence — moves the contextMutable().add(eventMsg) call to after checkInterrupted() in the pre-acting path, closing the race window.
  3. withoutToolUseBlocks() — clean static helper that filters out tool-use blocks; returns null if nothing remains (avoiding empty message persistence).

Code quality:

  • Clean separation between user and system interrupt handling
  • No changes to public API or PartialReasoningPolicy contract
  • Proper null handling throughout
  • toList() (immutable) used correctly

Test coverage (420 lines, 7 scenarios):

  • Partial streaming tool call during user interrupt
  • Completed tool call before acting during user interrupt
  • Text-only reasoning preserved on user interrupt
  • Tool-call-only reasoning not persisted as empty message
  • System interrupt with SAVE and DISCARD policies
  • gotoReasoning with non-null and null reasoning messages
  • Agent rebuild from interrupted state with strict history validation

All 2228 core tests pass. CI is fully green.

Verdict: Approved ✅ — well-scoped fix with thorough test coverage.

@hanydd
hanydd force-pushed the feat/interrupt-tool-call branch from 7aefec4 to 02b5e71 Compare July 29, 2026 10:22
@hanydd
hanydd force-pushed the feat/interrupt-tool-call branch from 02b5e71 to c00e9f4 Compare July 30, 2026 16:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: 用户中断 reasoning 时会持久化未执行的工具调用,导致会话上下文无法恢复

2 participants