feat(phase6-9): Complete modular architecture and Agent Teams
Phase 6.4 - Modular Architecture: - Add lewooogo-brain adapters for LLM providers - Add lewooogo-data dual memory (Redis + PostgreSQL) - Implement consensus engine for multi-agent decisions - Add incident memory service for historical context Phase 9 - Agent Teams (Claude Agent SDK): - Add base agent class with Claude Sonnet 4 integration - Implement action planner, blast radius, and security agents - Add agent API endpoints and proposal workflow - Integrate ADR-009 OpenClaw Agent Teams architecture DevOps & CI/CD: - Add GitHub Actions CI/CD workflows (ci.yaml, cd.yaml) - Add pre-commit hooks and secrets baseline - Add docker-compose for local development - Update Kubernetes network policies Frontend Improvements: - Add auto-healing error boundary component - Update i18n messages for agent features - Enhance dual-state incident card with execution feedback Documentation: - Add 7 ADRs covering MCP, design system, architecture decisions - Update ARCHITECTURE_MEMORY.md with modular design - Add GLOBAL_RULES.md and SOUL.md for project identity Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -22,13 +22,13 @@ Decision Manager - Phase 6.5 非同步決策狀態機
|
||||
import asyncio
|
||||
from datetime import datetime, timezone
|
||||
from enum import Enum
|
||||
from typing import Any, Literal
|
||||
from typing import Any
|
||||
from uuid import uuid4
|
||||
|
||||
import structlog
|
||||
|
||||
from src.core.redis_client import get_redis
|
||||
from src.models.incident import Incident, IncidentStatus, Severity
|
||||
from src.models.incident import Incident
|
||||
from src.services.openclaw import get_openclaw
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
@@ -425,6 +425,124 @@ class DecisionManager:
|
||||
await self._save_token(token)
|
||||
return token
|
||||
|
||||
async def get_or_create_decision_with_consensus(
|
||||
self,
|
||||
incident: Incident,
|
||||
timeout_sec: float = 30.0,
|
||||
use_consensus: bool = True,
|
||||
) -> DecisionToken:
|
||||
"""
|
||||
取得或建立決策令牌 (含 Agent Teams 共識)
|
||||
|
||||
Phase 9.4 升級版本:
|
||||
- 對於 P0/P1 事件,自動啟用 ConsensusEngine
|
||||
- 整合多專家意見
|
||||
- 共識分數影響風險評估
|
||||
|
||||
Args:
|
||||
incident: 事件
|
||||
timeout_sec: 超時秒數
|
||||
use_consensus: 是否使用共識引擎 (預設 True)
|
||||
|
||||
Returns:
|
||||
DecisionToken
|
||||
"""
|
||||
# 判斷是否需要共識 (P0/P1 或明確要求)
|
||||
should_use_consensus = use_consensus and incident.severity.value in ["P0", "P1"]
|
||||
|
||||
if not should_use_consensus:
|
||||
# 使用原有的雙軌決策
|
||||
return await self.get_or_create_decision(incident, timeout_sec)
|
||||
|
||||
# Phase 9.4: 使用 ConsensusEngine
|
||||
from src.services.consensus_engine import get_consensus_engine
|
||||
|
||||
consensus_engine = get_consensus_engine()
|
||||
|
||||
# 檢查現有 token
|
||||
existing_token = await self._find_existing_token(incident.incident_id)
|
||||
if existing_token and existing_token.state in (
|
||||
DecisionState.READY,
|
||||
DecisionState.EXECUTING,
|
||||
DecisionState.COMPLETED,
|
||||
):
|
||||
return existing_token
|
||||
|
||||
# 建立新 token
|
||||
token = DecisionToken(
|
||||
token=f"DEC-{uuid4().hex[:12].upper()}",
|
||||
incident_id=incident.incident_id,
|
||||
state=DecisionState.ANALYZING,
|
||||
)
|
||||
await self._save_token(token)
|
||||
|
||||
logger.info(
|
||||
"decision_analyzing_with_consensus",
|
||||
token=token.token,
|
||||
incident_id=incident.incident_id,
|
||||
)
|
||||
|
||||
try:
|
||||
# 執行共識分析
|
||||
consensus_result = await asyncio.wait_for(
|
||||
consensus_engine.run_consensus(incident, timeout_sec),
|
||||
timeout=timeout_sec,
|
||||
)
|
||||
|
||||
# 轉換為 proposal_data 格式
|
||||
proposal_data = {
|
||||
"source": "consensus_engine",
|
||||
"consensus_id": consensus_result.consensus_id,
|
||||
"consensus_score": consensus_result.consensus_score,
|
||||
"action": consensus_result.recommended_action,
|
||||
"description": consensus_result.final_reasoning,
|
||||
"risk_level": consensus_result.risk_level,
|
||||
"kubectl_command": consensus_result.recommended_kubectl,
|
||||
"reasoning": consensus_result.final_reasoning,
|
||||
"confidence": consensus_result.consensus_score,
|
||||
"agent_count": len(consensus_result.opinions),
|
||||
"dissenting_opinions": consensus_result.dissenting_opinions,
|
||||
"from_cache": False,
|
||||
}
|
||||
|
||||
token.state = DecisionState.READY
|
||||
token.proposal_data = proposal_data
|
||||
token.updated_at = datetime.now(timezone.utc)
|
||||
|
||||
logger.info(
|
||||
"decision_ready_with_consensus",
|
||||
token=token.token,
|
||||
consensus_id=consensus_result.consensus_id,
|
||||
consensus_score=consensus_result.consensus_score,
|
||||
)
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
logger.warning(
|
||||
"consensus_timeout_using_expert",
|
||||
token=token.token,
|
||||
timeout_sec=timeout_sec,
|
||||
)
|
||||
# Fallback 到 Expert System
|
||||
expert_result = expert_analyze(incident)
|
||||
token.state = DecisionState.READY
|
||||
token.proposal_data = expert_result
|
||||
token.updated_at = datetime.now(timezone.utc)
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(
|
||||
"consensus_error_using_expert",
|
||||
token=token.token,
|
||||
error=str(e),
|
||||
)
|
||||
expert_result = expert_analyze(incident)
|
||||
token.state = DecisionState.READY
|
||||
token.proposal_data = expert_result
|
||||
token.error = str(e)
|
||||
token.updated_at = datetime.now(timezone.utc)
|
||||
|
||||
await self._save_token(token)
|
||||
return token
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Singleton
|
||||
|
||||
Reference in New Issue
Block a user