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

This commit is contained in:
vinland100 2026-01-08 14:53:34 +08:00
parent 7da7cf7fa9
commit 0e2a7dfa87
3 changed files with 70 additions and 5 deletions

View File

@ -664,7 +664,7 @@ Final Answer: {{"findings": [...], "summary": "..."}}"""
await self.emit_llm_decision("继续分析", "LLM 需要更多分析")
self._conversation_history.append({
"role": "user",
"content": "请继续分析。你输出了 Thought 但没有输出 Action。请**立即**选择一个工具执行,或者如果分析完成,输出 Final Answer 汇总所有发现",
"content": "如果分析已经完成了,请立即输出 Final Answer 汇总所有发现。如果分析未完成,请继续分析:你输出了 Thought 但没有输出 Action。请**立即**选择一个工具执行。",
})
# 🔥 如果循环结束但没有发现,强制 LLM 总结

View File

@ -244,9 +244,74 @@ export const LogEntry = memo(function LogEntry({ item, isExpanded, onToggle }: L
)}
</div>
{/* 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}
</pre>
{(() => {
// Try to parse JSON content
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>
)}

View File

@ -128,7 +128,7 @@ export function cleanThinkingContent(content: string): 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;
return output.slice(0, maxLength) + '\n... (truncated)';
}