mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-15 11:58:34 +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;
|
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_BASE_MS = 3000;
|
||||||
private static final long BACKOFF_CAP_MS = 60_000;
|
private static final long BACKOFF_CAP_MS = 60_000;
|
||||||
|
|
||||||
@ -540,11 +554,22 @@ public class NodeStreamingChatHelper {
|
|||||||
broadcastDelta(conversationId, "warning",
|
broadcastDelta(conversationId, "warning",
|
||||||
buildDeltaJson("⏱️ 请求频率受限,等待 " + (delay / 1000) + " 秒后重试(第 " + attempt + "/" + MAX_RETRIES + " 次)..."));
|
buildDeltaJson("⏱️ 请求频率受限,等待 " + (delay / 1000) + " 秒后重试(第 " + attempt + "/" + MAX_RETRIES + " 次)..."));
|
||||||
}
|
}
|
||||||
try {
|
// Poll stop flag every 100ms so user Stop is honored mid-backoff.
|
||||||
Thread.sleep(delay);
|
long remaining = delay;
|
||||||
} catch (InterruptedException ie) {
|
while (remaining > 0) {
|
||||||
Thread.currentThread().interrupt();
|
if (streamTracker != null && streamTracker.isStopRequested(conversationId)) {
|
||||||
return buildErrorResult("LLM 调用被中断", conversationId, phase);
|
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);
|
conversationId, phase, errorType);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rate limit / Server error: 重试
|
// Rate limit / Server error: retry with reduced limit for rate-limit/server-error
|
||||||
if (attempt < MAX_RETRIES && (errorType == ErrorType.RATE_LIMIT || errorType == ErrorType.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={}): {}",
|
log.warn("[{}] Retryable error (attempt {}/{}, type={}): {}",
|
||||||
phase, attempt, MAX_RETRIES, errorType, error.getMessage());
|
phase, attempt, effectiveMaxRetries, errorType, error.getMessage());
|
||||||
return null; // 返回 null 触发重试
|
return null; // 返回 null 触发重试
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -38,13 +38,10 @@ import java.util.concurrent.*;
|
|||||||
public class ToolExecutionExecutor {
|
public class ToolExecutionExecutor {
|
||||||
|
|
||||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||||
private static final ExecutorService TOOL_EXECUTOR = Executors.newFixedThreadPool(
|
// JDK 21 virtual threads: no blocking stall for I/O-bound tools.
|
||||||
Math.max(4, Runtime.getRuntime().availableProcessors()),
|
// Each tool invocation gets its own lightweight carrier thread.
|
||||||
r -> {
|
private static final ExecutorService TOOL_EXECUTOR =
|
||||||
Thread t = new Thread(r, "tool-executor");
|
Executors.newVirtualThreadPerTaskExecutor();
|
||||||
t.setDaemon(true);
|
|
||||||
return t;
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Legacy hardcoded unsafe set, kept as a fallback when no
|
* Legacy hardcoded unsafe set, kept as a fallback when no
|
||||||
|
|||||||
@ -26,8 +26,8 @@ import java.util.Set;
|
|||||||
* agent:
|
* agent:
|
||||||
* tool-result:
|
* tool-result:
|
||||||
* enabled: true
|
* enabled: true
|
||||||
* per-result-threshold-chars: 4000
|
* per-result-threshold-chars: 16000
|
||||||
* per-turn-budget-chars: 16000
|
* per-turn-budget-chars: 32000
|
||||||
* preview-head-chars: 800
|
* preview-head-chars: 800
|
||||||
* storage-base-dir:
|
* storage-base-dir:
|
||||||
* </pre>
|
* </pre>
|
||||||
@ -39,10 +39,10 @@ public class ToolResultProperties {
|
|||||||
private boolean enabled = true;
|
private boolean enabled = true;
|
||||||
|
|
||||||
/** A single tool result larger than this is spilled to disk. */
|
/** 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. */
|
/** 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. */
|
/** Number of leading characters kept inline as a preview after spilling. */
|
||||||
private int previewHeadChars = 800;
|
private int previewHeadChars = 800;
|
||||||
|
|||||||
@ -165,6 +165,12 @@ public class PlanGenerationNode implements NodeAction {
|
|||||||
|
|
||||||
Prompt prompt = new Prompt(promptMessages);
|
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.
|
// Silent streaming call — structured JSON is parsed below; tokens are not forwarded to the client.
|
||||||
NodeStreamingChatHelper.StreamResult result = streamingHelper.streamCallSilent(
|
NodeStreamingChatHelper.StreamResult result = streamingHelper.streamCallSilent(
|
||||||
chatModel, prompt, conversationId, "plan_generation");
|
chatModel, prompt, conversationId, "plan_generation");
|
||||||
|
|||||||
@ -167,8 +167,8 @@ mate:
|
|||||||
# the in-context preview points the agent at the spill file (read_file tool).
|
# the in-context preview points the agent at the spill file (read_file tool).
|
||||||
tool-result:
|
tool-result:
|
||||||
enabled: true
|
enabled: true
|
||||||
per-result-threshold-chars: 4000
|
per-result-threshold-chars: 16000 # was 4000 — prevents WebSearch spill-to-disk
|
||||||
per-turn-budget-chars: 16000
|
per-turn-budget-chars: 32000 # was 16000 — headroom for multi-tool turns
|
||||||
preview-head-chars: 800
|
preview-head-chars: 800
|
||||||
storage-base-dir: ""
|
storage-base-dir: ""
|
||||||
# Retrieval-style tools that must NEVER be spilled. Spilling read_file's
|
# Retrieval-style tools that must NEVER be spilled. Spilling read_file's
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user