fix(agent): tolerate non-json analyzer replies and cap graph step latency
This commit is contained in:
parent
794ab44fe8
commit
ee2026a973
@ -85,6 +85,24 @@ function compactText(text: string, max = 48) {
|
||||
return `${normalized.slice(0, max)}...`;
|
||||
}
|
||||
|
||||
async function withTimeoutFallback<T>(
|
||||
promise: Promise<T>,
|
||||
timeoutMs: number,
|
||||
fallbackValue: T,
|
||||
): Promise<T> {
|
||||
let timeoutId: NodeJS.Timeout | undefined;
|
||||
try {
|
||||
return await Promise.race([
|
||||
promise,
|
||||
new Promise<T>((resolve) => {
|
||||
timeoutId = setTimeout(() => resolve(fallbackValue), timeoutMs);
|
||||
}),
|
||||
]);
|
||||
} finally {
|
||||
if (timeoutId) clearTimeout(timeoutId);
|
||||
}
|
||||
}
|
||||
|
||||
const AGENT_VISION_MAX_IMAGES = 4;
|
||||
const AGENT_VISION_MAX_SIDE = 500;
|
||||
const AGENT_VISION_FETCH_TIMEOUT_MS = 10_000;
|
||||
@ -484,6 +502,7 @@ function buildStrategicFriendlyMessage(
|
||||
state: AutoAgentStateType,
|
||||
): Promise<string | null> {
|
||||
const fallbackQuestion = analysis.clarifyingQuestions?.[0];
|
||||
const existingReply = analysis.conversationalReply?.trim();
|
||||
|
||||
if (analysis.needsClarification) {
|
||||
return Promise.resolve(
|
||||
@ -491,7 +510,21 @@ function buildStrategicFriendlyMessage(
|
||||
);
|
||||
}
|
||||
|
||||
return generateAgentConversation(analysis, state);
|
||||
if (existingReply) {
|
||||
const isStructuredReply =
|
||||
/###\s*需求理解/.test(existingReply) ||
|
||||
/###\s*图片分析/.test(existingReply) ||
|
||||
/###\s*执行计划/.test(existingReply);
|
||||
if (isStructuredReply || existingReply.length >= 24) {
|
||||
return Promise.resolve(existingReply);
|
||||
}
|
||||
}
|
||||
|
||||
return withTimeoutFallback(
|
||||
generateAgentConversation(analysis, state),
|
||||
12000,
|
||||
existingReply || null,
|
||||
);
|
||||
}
|
||||
|
||||
type EnabledModelWithProvider = Awaited<
|
||||
@ -694,6 +727,7 @@ async function generateAgentConversation(
|
||||
],
|
||||
temperature: 0.2,
|
||||
maxTokens: 360,
|
||||
abortSignal: AbortSignal.timeout(12000),
|
||||
} as any);
|
||||
|
||||
visualAnalysis = visionText.trim();
|
||||
@ -746,6 +780,7 @@ async function generateAgentConversation(
|
||||
prompt,
|
||||
temperature: 0.35,
|
||||
maxTokens: 520,
|
||||
abortSignal: AbortSignal.timeout(12000),
|
||||
} as any);
|
||||
|
||||
const cleaned = text.trim();
|
||||
@ -819,6 +854,7 @@ async function recommendImageParamsWithAgent(
|
||||
prompt: requestText,
|
||||
temperature: 0.15,
|
||||
maxTokens: 220,
|
||||
abortSignal: AbortSignal.timeout(10000),
|
||||
} as any);
|
||||
|
||||
const parsed = parseJsonBlock<AgentRecommendedImageParams>(text);
|
||||
@ -903,6 +939,7 @@ async function recommendVideoPlanWithAgent(
|
||||
prompt,
|
||||
temperature: 0.2,
|
||||
maxTokens: 720,
|
||||
abortSignal: AbortSignal.timeout(12000),
|
||||
} as any);
|
||||
|
||||
const parsed = parseJsonBlock<Record<string, unknown>>(text);
|
||||
@ -974,6 +1011,7 @@ async function refineRouteWithAgent(
|
||||
${candidatesText}`,
|
||||
temperature: 0.1,
|
||||
maxTokens: 120,
|
||||
abortSignal: AbortSignal.timeout(8000),
|
||||
} as any);
|
||||
|
||||
const parsed = parseJsonBlock<{ modelId?: string; reason?: string }>(text);
|
||||
|
||||
@ -373,7 +373,21 @@ function parseAnalysisResultFromModelText(
|
||||
const jsonMatch = responseText.match(/\{[\s\S]*\}/);
|
||||
if (!jsonMatch) {
|
||||
console.warn("[Smart Analyzer] Model response missing JSON:", responseText.slice(0, 240));
|
||||
return null;
|
||||
const fallback = fallbackKeywordAnalysis(userText, hasImage);
|
||||
const conversationalReply = responseText.trim();
|
||||
if (!conversationalReply) {
|
||||
return null;
|
||||
}
|
||||
return normalizeIntentForImageContext(
|
||||
{
|
||||
...fallback,
|
||||
confidence: Math.max(fallback.confidence, 0.82),
|
||||
needsClarification: false,
|
||||
conversationalReply,
|
||||
},
|
||||
userText,
|
||||
hasImage,
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
@ -386,7 +400,21 @@ function parseAnalysisResultFromModelText(
|
||||
"raw=",
|
||||
jsonMatch[0].slice(0, 240),
|
||||
);
|
||||
return null;
|
||||
const fallback = fallbackKeywordAnalysis(userText, hasImage);
|
||||
const conversationalReply = responseText.trim();
|
||||
if (!conversationalReply) {
|
||||
return null;
|
||||
}
|
||||
return normalizeIntentForImageContext(
|
||||
{
|
||||
...fallback,
|
||||
confidence: Math.max(fallback.confidence, 0.8),
|
||||
needsClarification: false,
|
||||
conversationalReply,
|
||||
},
|
||||
userText,
|
||||
hasImage,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -902,6 +930,7 @@ ${historyText}
|
||||
],
|
||||
temperature: 0.1,
|
||||
maxTokens: 360,
|
||||
abortSignal: AbortSignal.timeout(12000),
|
||||
}
|
||||
: {
|
||||
model,
|
||||
@ -909,6 +938,7 @@ ${historyText}
|
||||
prompt: userMessage,
|
||||
temperature: 0.1,
|
||||
maxTokens: 300,
|
||||
abortSignal: AbortSignal.timeout(10000),
|
||||
};
|
||||
|
||||
const { text: responseText } = await generateText(requestPayload as any);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user