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:
OG T
2026-03-23 23:17:52 +08:00
parent bd1f94dd72
commit 29ceb786ca

View File

@@ -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"