Modify the Agent audit logic to prevent infinite loops until the loop limit is triggered.
Build and Push CodeReview / build (push) Waiting to run
Details
Build and Push CodeReview / build (push) Waiting to run
Details
This commit is contained in:
parent
7da7cf7fa9
commit
0e2a7dfa87
|
|
@ -664,7 +664,7 @@ Final Answer: {{"findings": [...], "summary": "..."}}"""
|
||||||
await self.emit_llm_decision("继续分析", "LLM 需要更多分析")
|
await self.emit_llm_decision("继续分析", "LLM 需要更多分析")
|
||||||
self._conversation_history.append({
|
self._conversation_history.append({
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"content": "请继续分析。你输出了 Thought 但没有输出 Action。请**立即**选择一个工具执行,或者如果分析完成,输出 Final Answer 汇总所有发现。",
|
"content": "如果分析已经完成了,请立即输出 Final Answer 汇总所有发现。如果分析未完成,请继续分析:你输出了 Thought 但没有输出 Action。请**立即**选择一个工具执行。",
|
||||||
})
|
})
|
||||||
|
|
||||||
# 🔥 如果循环结束但没有发现,强制 LLM 总结
|
# 🔥 如果循环结束但没有发现,强制 LLM 总结
|
||||||
|
|
|
||||||
|
|
@ -244,9 +244,74 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<pre className="p-4 text-sm font-mono text-foreground/85 max-h-64 overflow-y-auto custom-scrollbar whitespace-pre-wrap break-words">
|
{(() => {
|
||||||
{item.content}
|
// Try to parse JSON content
|
||||||
</pre>
|
let parsedContent: any = null;
|
||||||
|
let isJson = false;
|
||||||
|
try {
|
||||||
|
if (item.content && (item.content.trim().startsWith('{') || item.content.trim().startsWith('['))) {
|
||||||
|
// Remove any "Output:" prefix if present for parsing
|
||||||
|
const cleanContent = item.content.replace(/^Output:\s*/, '').trim();
|
||||||
|
parsedContent = JSON.parse(cleanContent);
|
||||||
|
isJson = true;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Not JSON
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Findings View (if it looks like a findings list or object)
|
||||||
|
if (isJson && (
|
||||||
|
(Array.isArray(parsedContent) && parsedContent.length > 0 && parsedContent[0].title && parsedContent[0].severity) ||
|
||||||
|
(parsedContent.findings && Array.isArray(parsedContent.findings))
|
||||||
|
)) {
|
||||||
|
const findingsList = Array.isArray(parsedContent) ? parsedContent : parsedContent.findings;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-2 p-3 bg-muted/30">
|
||||||
|
<div className="flex items-center justify-between mb-1">
|
||||||
|
<span className="text-xs font-bold uppercase text-muted-foreground tracking-wider">Findings Summary ({findingsList.length})</span>
|
||||||
|
</div>
|
||||||
|
{findingsList.map((finding: any, idx: number) => (
|
||||||
|
<div key={idx} className="bg-card border border-border rounded p-3 flex flex-col gap-1.5 shadow-sm">
|
||||||
|
<div className="flex items-start justify-between gap-3">
|
||||||
|
<span className="text-sm font-semibold text-foreground">{finding.title || 'Unknown Vulnerability'}</span>
|
||||||
|
<Badge className={`uppercase text-[10px] px-1.5 py-0 h-5 ${SEVERITY_COLORS[finding.severity as keyof typeof SEVERITY_COLORS] || SEVERITY_COLORS.info}`}>
|
||||||
|
{finding.severity || 'unknown'}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-muted-foreground font-mono truncate">
|
||||||
|
{finding.file_path}:{finding.line_start || '?'}
|
||||||
|
</div>
|
||||||
|
{finding.description && (
|
||||||
|
<div className="text-sm text-foreground/85 mt-1 border-t border-border/50 pt-2">
|
||||||
|
{finding.description}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="mt-2 text-xs text-center text-muted-foreground font-mono opacity-50">
|
||||||
|
Raw Details Hidden (Toggle not impl yet)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Generic JSON View (Pretty Print)
|
||||||
|
if (isJson) {
|
||||||
|
return (
|
||||||
|
<pre className="p-4 text-sm font-mono text-blue-600 dark:text-blue-400 max-h-96 overflow-y-auto custom-scrollbar whitespace-pre-wrap break-words bg-slate-50 dark:bg-slate-950/50">
|
||||||
|
{JSON.stringify(parsedContent, null, 2)}
|
||||||
|
</pre>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Fallback: Raw Text
|
||||||
|
return (
|
||||||
|
<pre className="p-4 text-sm font-mono text-foreground/85 max-h-96 overflow-y-auto custom-scrollbar whitespace-pre-wrap break-words">
|
||||||
|
{item.content}
|
||||||
|
</pre>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ export function cleanThinkingContent(content: string): string {
|
||||||
/**
|
/**
|
||||||
* Truncate output string
|
* Truncate output string
|
||||||
*/
|
*/
|
||||||
export function truncateOutput(output: string, maxLength: number = 1000): string {
|
export function truncateOutput(output: string, maxLength: number = 50000): string {
|
||||||
if (output.length <= maxLength) return output;
|
if (output.length <= maxLength) return output;
|
||||||
return output.slice(0, maxLength) + '\n... (truncated)';
|
return output.slice(0, maxLength) + '\n... (truncated)';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue