Skip to content

Commit 117b726

Browse files
committed
feat: LLM返回无响应时自动压缩上下文重试(最多2次)
1 parent b2f940e commit 117b726

File tree

1 file changed

+44
-5
lines changed
  • app/src/main/java/com/xiaomo/androidforclaw/agent/loop

1 file changed

+44
-5
lines changed

app/src/main/java/com/xiaomo/androidforclaw/agent/loop/AgentLoop.kt

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)