mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
perf(agent): implement Lane D performance fixes
This commit is contained in:
parent
1a00b9276c
commit
23133ea45d
@ -256,9 +256,23 @@ public class NodeStreamingChatHelper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast a lightweight progress event so the frontend shows activity
|
||||
* during silent LLM calls (e.g. triage). Sent as a "progress" SSE event.
|
||||
*/
|
||||
public void broadcastProgress(String conversationId, String message) {
|
||||
if (streamTracker == null || conversationId == null || conversationId.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
streamTracker.broadcastObject(conversationId, "progress",
|
||||
Map.of("message", message != null ? message : ""));
|
||||
}
|
||||
|
||||
// ==================== 重试配置 ====================
|
||||
|
||||
private static final int MAX_RETRIES = 5;
|
||||
// For rate-limit and server-error: fail fast to failover chain.
|
||||
private static final int MAX_RETRIES_RATE_LIMIT = 2;
|
||||
private static final long BACKOFF_BASE_MS = 3000;
|
||||
private static final long BACKOFF_CAP_MS = 60_000;
|
||||
|
||||
@ -540,11 +554,22 @@ public class NodeStreamingChatHelper {
|
||||
broadcastDelta(conversationId, "warning",
|
||||
buildDeltaJson("⏱️ 请求频率受限,等待 " + (delay / 1000) + " 秒后重试(第 " + attempt + "/" + MAX_RETRIES + " 次)..."));
|
||||
}
|
||||
try {
|
||||
Thread.sleep(delay);
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
return buildErrorResult("LLM 调用被中断", conversationId, phase);
|
||||
// Poll stop flag every 100ms so user Stop is honored mid-backoff.
|
||||
long remaining = delay;
|
||||
while (remaining > 0) {
|
||||
if (streamTracker != null && streamTracker.isStopRequested(conversationId)) {
|
||||
log.info("[{}] Stop requested during backoff — aborting retry: conversationId={}",
|
||||
phase, conversationId);
|
||||
throw new CancellationException("Stream stopped by user");
|
||||
}
|
||||
long slice = Math.min(100, remaining);
|
||||
try {
|
||||
Thread.sleep(slice);
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
return buildErrorResult("LLM 调用被中断", conversationId, phase);
|
||||
}
|
||||
remaining -= slice;
|
||||
}
|
||||
}
|
||||
|
||||
@ -731,10 +756,12 @@ public class NodeStreamingChatHelper {
|
||||
conversationId, phase, errorType);
|
||||
}
|
||||
|
||||
// Rate limit / Server error: 重试
|
||||
if (attempt < MAX_RETRIES && (errorType == ErrorType.RATE_LIMIT || errorType == ErrorType.SERVER_ERROR)) {
|
||||
// Rate limit / Server error: retry with reduced limit for rate-limit/server-error
|
||||
boolean isRateLimitOrServerError = errorType == ErrorType.RATE_LIMIT || errorType == ErrorType.SERVER_ERROR;
|
||||
int effectiveMaxRetries = isRateLimitOrServerError ? MAX_RETRIES_RATE_LIMIT : MAX_RETRIES;
|
||||
if (attempt < effectiveMaxRetries && isRateLimitOrServerError) {
|
||||
log.warn("[{}] Retryable error (attempt {}/{}, type={}): {}",
|
||||
phase, attempt, MAX_RETRIES, errorType, error.getMessage());
|
||||
phase, attempt, effectiveMaxRetries, errorType, error.getMessage());
|
||||
return null; // 返回 null 触发重试
|
||||
}
|
||||
|
||||
|
||||
@ -38,13 +38,10 @@ import java.util.concurrent.*;
|
||||
public class ToolExecutionExecutor {
|
||||
|
||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
private static final ExecutorService TOOL_EXECUTOR = Executors.newFixedThreadPool(
|
||||
Math.max(4, Runtime.getRuntime().availableProcessors()),
|
||||
r -> {
|
||||
Thread t = new Thread(r, "tool-executor");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
});
|
||||
// JDK 21 virtual threads: no blocking stall for I/O-bound tools.
|
||||
// Each tool invocation gets its own lightweight carrier thread.
|
||||
private static final ExecutorService TOOL_EXECUTOR =
|
||||
Executors.newVirtualThreadPerTaskExecutor();
|
||||
|
||||
/**
|
||||
* Legacy hardcoded unsafe set, kept as a fallback when no
|
||||
|
||||
@ -26,8 +26,8 @@ import java.util.Set;
|
||||
* agent:
|
||||
* tool-result:
|
||||
* enabled: true
|
||||
* per-result-threshold-chars: 4000
|
||||
* per-turn-budget-chars: 16000
|
||||
* per-result-threshold-chars: 16000
|
||||
* per-turn-budget-chars: 32000
|
||||
* preview-head-chars: 800
|
||||
* storage-base-dir:
|
||||
* </pre>
|
||||
@ -39,10 +39,10 @@ public class ToolResultProperties {
|
||||
private boolean enabled = true;
|
||||
|
||||
/** A single tool result larger than this is spilled to disk. */
|
||||
private int perResultThresholdChars = 4000;
|
||||
private int perResultThresholdChars = 16000; // was 4000 — prevents WebSearch spill-to-disk
|
||||
|
||||
/** Aggregate cap on combined response size in one tool turn. */
|
||||
private int perTurnBudgetChars = 16000;
|
||||
private int perTurnBudgetChars = 32000; // was 16000 — headroom for multi-tool turns
|
||||
|
||||
/** Number of leading characters kept inline as a preview after spilling. */
|
||||
private int previewHeadChars = 800;
|
||||
|
||||
@ -165,6 +165,12 @@ public class PlanGenerationNode implements NodeAction {
|
||||
|
||||
Prompt prompt = new Prompt(promptMessages);
|
||||
|
||||
// Broadcast a lightweight progress token so the frontend shows activity
|
||||
// during the silent triage call (typically 1-3 s).
|
||||
if (streamingHelper != null) {
|
||||
streamingHelper.broadcastProgress(conversationId, "分析中...");
|
||||
}
|
||||
|
||||
// Silent streaming call — structured JSON is parsed below; tokens are not forwarded to the client.
|
||||
NodeStreamingChatHelper.StreamResult result = streamingHelper.streamCallSilent(
|
||||
chatModel, prompt, conversationId, "plan_generation");
|
||||
|
||||
@ -167,8 +167,8 @@ mate:
|
||||
# the in-context preview points the agent at the spill file (read_file tool).
|
||||
tool-result:
|
||||
enabled: true
|
||||
per-result-threshold-chars: 4000
|
||||
per-turn-budget-chars: 16000
|
||||
per-result-threshold-chars: 16000 # was 4000 — prevents WebSearch spill-to-disk
|
||||
per-turn-budget-chars: 32000 # was 16000 — headroom for multi-tool turns
|
||||
preview-head-chars: 800
|
||||
storage-base-dir: ""
|
||||
# Retrieval-style tools that must NEVER be spilled. Spilling read_file's
|
||||
|
||||
Loading…
Reference in New Issue
Block a user