@@ -233,6 +233,9 @@ class AgentLoop(
233233
234234 // Timeout compaction counter (aligned with OpenClaw timeoutCompactionAttempts)
235235 private var timeoutCompactionAttempts = 0
236+ // Empty/suspicious response retry counter
237+ private var emptyResponseRetryAttempts = 0
238+ private val MAX_EMPTY_RESPONSE_RETRY_ATTEMPTS = 2
236239
237240 // Transient HTTP retry guard (aligned with OpenClaw didRetryTransientHttpError)
238241 private var didRetryTransientHttpError = false
@@ -983,12 +986,47 @@ class AgentLoop(
983986 rawContent = response.thinkingContent
984987 }
985988
986- // Warn if LLM returned suspicious default text
987- if (rawContent == " 无响应" || rawContent == " 无响应。" || rawContent == " 没有响应" ) {
988- writeLog(" ⚠️ LLM returned suspicious default text: '$rawContent '" )
989- writeLog(" This usually indicates: context too large, model confusion, or corrupted history" )
989+ // Detect suspicious default text from LLM (e.g. "无响应")
990+ // Instead of silently accepting, try to compact context and retry
991+ val isSuspiciousResponse = rawContent == " 无响应" || rawContent == " 无响应。" || rawContent == " 没有响应"
992+ if (isSuspiciousResponse && emptyResponseRetryAttempts < MAX_EMPTY_RESPONSE_RETRY_ATTEMPTS ) {
993+ emptyResponseRetryAttempts++
994+ writeLog(" ⚠️ LLM returned suspicious default text: '$rawContent ' (retry $emptyResponseRetryAttempts /$MAX_EMPTY_RESPONSE_RETRY_ATTEMPTS )" )
990995 writeLog(" Messages count: ${messages.size} , Total context chars: ${ToolResultContextGuard .estimateContextChars(messages)} " )
991- Log .w(TAG , " ⚠️ LLM returned suspicious default text: '$rawContent ' (context may be too large)" )
996+ Log .w(TAG , " ⚠️ Suspicious response '$rawContent ', retry $emptyResponseRetryAttempts /$MAX_EMPTY_RESPONSE_RETRY_ATTEMPTS " )
997+
998+ if (contextManager != null ) {
999+ writeLog(" 🔄 正在压缩上下文后重试..." )
1000+ val recoveryResult = contextManager.handleContextOverflow(
1001+ error = Exception (" Suspicious default response: $rawContent " ),
1002+ messages = messages
1003+ )
1004+ when (recoveryResult) {
1005+ is ContextRecoveryResult .Recovered -> {
1006+ writeLog(" ✅ 上下文压缩成功: ${recoveryResult.strategy} " )
1007+ messages.clear()
1008+ messages.addAll(recoveryResult.messages)
1009+ _progressFlow .emit(ProgressUpdate .ContextRecovered (
1010+ strategy = recoveryResult.strategy,
1011+ attempt = recoveryResult.attempt
1012+ ))
1013+ }
1014+ is ContextRecoveryResult .CannotRecover -> {
1015+ writeLog(" ⚠️ 上下文压缩失败,尝试工具结果截断..." )
1016+ val ctxTokens = resolveContextWindowTokens()
1017+ pruneOldToolResults(messages, ctxTokens)
1018+ ToolResultContextGuard .enforceContextBudget(messages, ctxTokens)
1019+ }
1020+ }
1021+ } else {
1022+ writeLog(" 🔄 无 contextManager,直接重试..." )
1023+ }
1024+ continue
1025+ }
1026+
1027+ if (isSuspiciousResponse) {
1028+ writeLog(" ❌ LLM 多次返回可疑默认文本 '$rawContent ',放弃重试" )
1029+ Log .e(TAG , " ❌ LLM returned suspicious response after $emptyResponseRetryAttempts retries" )
9921030 }
9931031
9941032 finalContent = if (SubagentPromptBuilder .isSilentReplyText(rawContent)) null else rawContent
@@ -1448,6 +1486,7 @@ class AgentLoop(
14481486 fun reset () {
14491487 shouldStop = false
14501488 timeoutCompactionAttempts = 0
1489+ emptyResponseRetryAttempts = 0
14511490 didRetryTransientHttpError = false
14521491 loopDetectionState.toolCallHistory.clear()
14531492 // Drain steer channel to remove stale messages
0 commit comments