fix(aiops-p1): Playbook 學習閉環 5斷點全修 + DB Migration(ADR-092 B4)
【P0.4 補丁】pre_decision_investigator Prometheus query 欄位缺失 - _build_tool_params() 補 "query" 欄位(prometheus_query tool 必要參數) - 新增 _build_prometheus_query() — 依告警類型生成 PromQL(CPU/Memory/Crash/Disk/HTTP/Pod/fallback) - 修復後 D3_METRICS 感官維度實際取得資料(原本 100% 回 missing_query_parameter) 【P1 Playbook 學習閉環 B1-B5 全修】 - B2 db/models.py: ApprovalRecord 新增 matched_playbook_id 欄位 + ix_approval_matched_playbook index - B2 db/models.py: TimelineEvent 新增 incident_id 欄位(MCP 稽核用)+ index - B3 approval_db.py: record→ApprovalRequest 補回 incident_id + matched_playbook_id - B4 approval_repository.py: 同 B3(兩個轉換函式必須同步) - B5 approval_db.py: approval_request_to_record_data 補 matched_playbook_id → DB 才能存值 【P1.5 KM 寫入】approval_execution.py: fire-and-forget → await wait_for(30s) - 根因:asyncio.create_task 在 Pod recycle 時被殺,KM 寫入靜默遺失 - 修復:await asyncio.wait_for(..., timeout=30.0) + TimeoutError log 【Migration 文件】adr092_p1_learning_chain_fix.sql - ALTER TABLE approval_records ADD COLUMN matched_playbook_id VARCHAR(36) - ALTER TABLE timeline_events ADD COLUMN incident_id VARCHAR(64) - 執行:psql $DATABASE_URL -f apps/api/migrations/adr092_p1_learning_chain_fix.sql 【附帶 Agent 改動】 - decision_manager: Phase 2 YAML NO_ACTION 優先門(主機層/外部服務跳過 Agent Debate) - alert_rules.yaml: Sentry/ClickHouse + HostDiskUsageHigh/Critical 新規則 - solver_agent: action_title 語意合成兜底(取代靜默丟棄) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1140,6 +1140,7 @@ def _package_to_proposal_data(package: Any) -> dict[str, Any]:
|
||||
"confidence": confidence,
|
||||
"risk_level": risk_level,
|
||||
"source": "phase2_agent_debate",
|
||||
"provider": "phase2_agents", # 2026-04-24 ogt: 讓 Telegram 顯示 "Phase2 Agents" 而非通用 "AI 仲裁"
|
||||
"requires_human_review": package.requires_human_approval,
|
||||
# Phase 2 診斷摘要(供 Audit Trail / 學習閉環,不直接顯示給用戶)
|
||||
"debate_summary": package.debate_summary or "",
|
||||
@@ -2283,6 +2284,45 @@ class DecisionManager:
|
||||
# 需要 EvidenceSnapshot;若 P1 未開啟則自行收集
|
||||
# 2026-04-15 ogt + Claude Sonnet 4.6(亞太)
|
||||
if aiops_flags.is_phase_enabled(2): # Gate 2: 用 is_phase_enabled 統一父 Phase 守衛
|
||||
# 2026-04-24 ogt + Claude Sonnet 4.6: YAML NO_ACTION 優先門
|
||||
# 根因:Phase 2 五 agent 對主機層/外部服務告警(HostDiskUsageHigh /
|
||||
# SentryClickHouseMemoryPressure)做 kubectl 分析 → Solver 永遠降級
|
||||
# (無 K8s target) → confidence=20% + kubectl get pods(無意義)
|
||||
# 修復:YAML 匹配到 NO_ACTION 且 kubectl_command="" → 跳過 Agent Debate,
|
||||
# 直接返回 YAML 規則響應,讓 Telegram 推送人工排查通知
|
||||
try:
|
||||
from src.services.alert_rule_engine import match_rule as _p2_match_rule
|
||||
_p2_labels = incident.signals[0].labels if incident.signals else {}
|
||||
_p2_alertname = _p2_labels.get("alertname", "")
|
||||
_p2_yaml = _p2_match_rule({
|
||||
"labels": _p2_labels,
|
||||
"alert_type": _p2_alertname,
|
||||
"message": (
|
||||
incident.signals[0].annotations.get("summary", "")
|
||||
if incident.signals else ""
|
||||
),
|
||||
"target_resource": incident.affected_services[0] if incident.affected_services else "unknown",
|
||||
"namespace": _p2_labels.get("namespace", "awoooi-prod"),
|
||||
"severity": incident.severity.value if hasattr(incident.severity, "value") else "medium",
|
||||
})
|
||||
_is_no_action_yaml = (
|
||||
_p2_yaml is not None
|
||||
and _p2_yaml.get("suggested_action") == "NO_ACTION"
|
||||
and not _p2_yaml.get("kubectl_command", "").strip()
|
||||
and _p2_yaml.get("rule_id", "") not in ("generic_fallback", "")
|
||||
)
|
||||
if _is_no_action_yaml:
|
||||
logger.info(
|
||||
"p2_yaml_no_action_bypass",
|
||||
incident_id=incident.incident_id,
|
||||
alertname=_p2_alertname,
|
||||
rule_id=_p2_yaml.get("rule_id", ""),
|
||||
reason="YAML NO_ACTION 規則命中,跳過 Agent Debate",
|
||||
)
|
||||
return _p2_yaml
|
||||
except Exception as _p2_yaml_err:
|
||||
logger.debug("p2_yaml_precheck_error", error=str(_p2_yaml_err))
|
||||
|
||||
p2_snapshot = evidence_snapshot
|
||||
if p2_snapshot is None:
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user