feat(web): 全局戰情室顯示真實 AI 決策鏈
問題: - ThinkingTerminal 使用 DEMO_DECISION_CHAIN 假數據 - 用戶無法看到 OpenClaw AI 的真實推理過程 修復: - 新增 convertToDecisionChain() 轉換 API 格式 - 從 incident.decision.proposal_data 提取真實 AI 資料 - 顯示: 決策引擎來源、推理過程、建議動作、信心分數 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -31,12 +31,95 @@ import {
|
||||
IncidentCardGrid,
|
||||
IncidentEmptyState,
|
||||
ThinkingTerminal,
|
||||
DEMO_DECISION_CHAIN,
|
||||
DualStateIncidentCard,
|
||||
} from '@/components/incident'
|
||||
import { AlertTriangle } from 'lucide-react'
|
||||
import type { IncidentResponse } from '@/lib/api-client'
|
||||
|
||||
// =============================================================================
|
||||
// Types for AI Decision Chain Display
|
||||
// =============================================================================
|
||||
|
||||
interface ReasoningStep {
|
||||
step: string
|
||||
reasoning: string
|
||||
timestamp?: string
|
||||
}
|
||||
|
||||
interface DecisionChainDisplay {
|
||||
analysis_type: 'blast_radius' | 'root_cause' | 'action_suggestion'
|
||||
target_service: string
|
||||
reasoning_steps: ReasoningStep[]
|
||||
conclusion: string
|
||||
confidence: number
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Utility: Convert API proposal_data to DecisionChain format
|
||||
// =============================================================================
|
||||
|
||||
function convertToDecisionChain(
|
||||
incident: IncidentResponse | null | undefined
|
||||
): DecisionChainDisplay | null {
|
||||
if (!incident?.decision?.proposal_data) {
|
||||
return null
|
||||
}
|
||||
|
||||
const data = incident.decision.proposal_data
|
||||
const target = incident.affected_services?.[0] || 'unknown-service'
|
||||
const source = data.source || 'unknown'
|
||||
const now = new Date().toISOString()
|
||||
|
||||
// 建構推理步驟 (從 API 資料)
|
||||
const steps: ReasoningStep[] = [
|
||||
{
|
||||
step: 'SIGNAL_RECEIVED',
|
||||
reasoning: `收到 ${incident.signal_count || 1} 筆告警,影響服務: ${target}`,
|
||||
timestamp: incident.created_at,
|
||||
},
|
||||
{
|
||||
step: 'SEVERITY_EVALUATION',
|
||||
reasoning: `告警等級: ${incident.severity} | 狀態: ${incident.status}`,
|
||||
},
|
||||
{
|
||||
step: 'AI_ENGINE',
|
||||
reasoning: `決策引擎: ${source === 'expert_system' ? 'Expert System (規則引擎)' : 'OpenClaw LLM (智能分析)'}`,
|
||||
},
|
||||
]
|
||||
|
||||
// 加入 AI 推理 (如果有)
|
||||
if (data.reasoning) {
|
||||
steps.push({
|
||||
step: 'ROOT_CAUSE_ANALYSIS',
|
||||
reasoning: data.reasoning,
|
||||
})
|
||||
}
|
||||
|
||||
// 加入描述 (如果有)
|
||||
if (data.description) {
|
||||
steps.push({
|
||||
step: 'ANALYSIS_RESULT',
|
||||
reasoning: data.description,
|
||||
})
|
||||
}
|
||||
|
||||
// 加入建議動作
|
||||
if (data.action || data.kubectl_command) {
|
||||
steps.push({
|
||||
step: 'ACTION_RECOMMENDATION',
|
||||
reasoning: `建議動作: ${data.action || data.kubectl_command}\n風險等級: ${data.risk_level || 'medium'}`,
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
analysis_type: 'action_suggestion',
|
||||
target_service: target,
|
||||
reasoning_steps: steps,
|
||||
conclusion: data.action || '等待 AI 分析完成...',
|
||||
confidence: data.confidence ?? 0.75,
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Utility: Map IncidentResponse to DualStateIncidentCard props
|
||||
// =============================================================================
|
||||
@@ -236,13 +319,17 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
)}
|
||||
</DataPincerPanel>
|
||||
|
||||
{/* OpenClaw Thinking Terminal (Phase 7: 決策鏈視覺化) */}
|
||||
{/* OpenClaw Thinking Terminal (Phase 7: 決策鏈視覺化 - 真實 AI 資料) */}
|
||||
<DataPincerPanel
|
||||
title="OpenClaw Terminal"
|
||||
status="thinking"
|
||||
status={(incidents?.length || 0) > 0 ? "thinking" : "healthy"}
|
||||
>
|
||||
<ThinkingTerminal
|
||||
decisionChain={(incidents?.length || 0) > 0 ? DEMO_DECISION_CHAIN : null}
|
||||
decisionChain={
|
||||
(incidents?.length || 0) > 0
|
||||
? convertToDecisionChain(incidents?.[0])
|
||||
: null
|
||||
}
|
||||
incidentId={(incidents?.length || 0) > 0 ? incidents?.[0]?.incident_id : undefined}
|
||||
autoPlay={(incidents?.length || 0) > 0}
|
||||
maxHeight="300px"
|
||||
|
||||
Reference in New Issue
Block a user