feat: ReActAgent流式降级优化——可配置重试、非流式回退包装为流式、周期性SSE心跳 - #121
feat: ReActAgent流式降级优化——可配置重试、非流式回退包装为流式、周期性SSE心跳#121openjiuwen-sync-bot[bot] wants to merge 5 commits into
Conversation
- 移除流式降级逻辑,改为可配置重试(streamMaxRetries/streamRetryDelayMs) - 新增 callModelStreamWithRetry/writeNonStreamAsStreamChunks - 新增SSE心跳机制,修复SystemPromptBuilder NPE - 保留origin/730的shouldFailTaskOnToolError等既有改动
- 非流式回退callModel阻塞期间按5s周期发送SSE心跳(原仅发一次) - 使用ScheduledThreadPoolExecutor显式构造+命名线程(遵循G.CON.12) - writeStreamHeartbeat增加异常防护(emitter关闭时静默忽略) - 修复重试等待段InterruptedException未恢复中断标志问题
- 提取writeToolCallsAndUsage方法消除tool_calls/usage_metadata重复构建逻辑(DRY) - ScheduledExecutorService改为ScheduledThreadPoolExecutor声明消除多余强转 - 心跳消息字符串提取为HEARTBEAT_NON_STREAM_MSG常量消除硬编码重复 - 移除不再使用的ScheduledExecutorService import - 新增heartbeatSentPeriodicallyDuringLongCallModel测试验证5s周期心跳
|
guoxiuyan1 seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
1 similar comment
|
guoxiuyan1 seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
|
head_sha: 变更摘要本次 PR 主要针对 主要改动
|
|
head_sha: 代码审查我已经完成了对所有 6 个文件的逐一审查。以下是审查总结: 审查总结发现的问题
各文件审查结果
整体风险评估中等风险。P2 的
💬 仅评论 |
|
head_sha:
|
|
head_sha:
|
- 修复toText(null)返回字面量"null"导致SSE误发送问题:在调用toText前检查getContent()是否为null - G.ERR.02: catch RuntimeException改为catch BaseError精确捕获writeStream异常 - G.MET.01: 提取fallbackToNonStreamWithHeartbeat方法,callModelStreamWithRetry从65行降至35行 - G.CON.12: ThreadFactory改为块lambda设置daemon和UncaughtExceptionHandler - 新增nullContentWithToolCallsDoesNotSendLiteralNull测试覆盖content=null+tool_calls场景
|
head_sha:
|
- G.CON.10: 用shutdownNow()替代shutdown()+awaitTermination(),消除Thread.currentThread().interrupt()调用 - G.CON.12: 用Executors.defaultThreadFactory().newThread()替代new Thread(),线程创建由受管控的ThreadFactory完成
|
head_sha:
|
Paired: GitHub #121 ↔ GitCode !244
背景
ReActAgent 流式模式下,当模型返回空响应时直接回退非流式调用,存在以下问题:
callModel阻塞期间无 SSE 事件,客户端长时间无数据收发后超时断开流程图
流式降级整体流程
周期性心跳时序
sequenceDiagram participant Client as 客户端 SSE participant Agent as callModelStreamWithRetry participant HB as ScheduledThreadPoolExecutor participant Model as callModel Agent->>Model: callModelStream attempt 0..N Model-->>Agent: 空响应 Agent->>Client: 心跳 - 流式响应为空,正在重试 Note over Agent: 全部重试失败,回退非流式 Agent->>Client: 心跳同步 - 正在以非流式模式获取响应 Agent->>HB: scheduleAtFixedRate 5s间隔 Agent->>Model: callModel 阻塞 loop 每5秒 HB->>Client: SSE progress 心跳事件 end Model-->>Agent: AssistantMessage Agent->>HB: cancel and shutdown finally Agent->>Client: writeNonStreamAsStreamChunks 分块发送 Agent-->>Agent: 返回 Optional.of aiMessage改动内容
1. 流式可配置重试 + 非流式回退包装(
ReActAgent.java)callModelStreamWithRetry:流式返回空时按streamMaxRetries(默认 2 次)重试,间隔streamRetryDelayMs(默认 1000ms)IllegalStateException)不重试,避免部分 chunk 已发送导致重复callModel,将响应包装为流式 chunk 逐块发送(writeNonStreamAsStreamChunks,分块大小 200 字符)writeStreamResult不会发送此轮 content);无 tool_call 时由writeStreamResult统一发送避免重复2. 周期性 SSE 心跳(
ReActAgent.java)callModel前,先同步发送一次心跳,确保客户端立即收到切换通知callModel阻塞期间用ScheduledThreadPoolExecutor按HEARTBEAT_INTERVAL_MS(5s)周期发送 SSEprogress事件,防止客户端超时断开callModel返回后finally取消心跳并关闭执行器,避免线程泄漏writeStreamHeartbeat包 try-catch,emitter 关闭时静默忽略,避免中断定时器线程3. SystemPromptBuilder NPE 修复(
SystemPromptBuilder.java)build()排序前removeIf(section -> section == null),防止Comparator.comparingIntNPE4. 新增配置项(
ReActAgentConfig.java)streamMaxRetriesstreamRetryDelayMs5. Clean Code 重构
writeToolCallsAndUsage方法消除 tool_calls/usage_metadata 重复构建逻辑(DRY)ScheduledExecutorService改为ScheduledThreadPoolExecutor声明消除多余强转HEARTBEAT_NON_STREAM_MSG常量消除硬编码重复测试
新增
ReActAgentStreamDegradationTest(9 个测试):heartbeatSentPeriodicallyDuringLongCallModel):验证 ≥2 个心跳且间隔 [3s, 8s]新增
SystemPromptBuilderTest(NPE 场景覆盖)。全部 9 + 9 = 18 个测试通过。
验证
mvn compile+mvn test-compile通过(1882 源文件)ReActAgentStreamDegradationTest9/9 通过SystemPromptBuilderTest9/9 通过git diff --check无 trailing whitespace