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:
@@ -1,6 +1,11 @@
|
||||
"""
|
||||
Incident Engine v1.1 - Phase 6.3 認知覺醒核心 (效能強化版)
|
||||
============================================================
|
||||
Incident Engine v1.2 - Phase 6.4e DualMemory 整合版
|
||||
====================================================
|
||||
|
||||
v1.2 重構內容 (Phase 6.4e):
|
||||
- 整合 DualIncidentMemory 進行 DB 持久化
|
||||
- 保持 Lua 原子操作進行 Redis Working Memory 更新
|
||||
- 支援從 Episodic Memory (PostgreSQL) 回載 Incident
|
||||
|
||||
v1.1 重構內容 (2026-03-22 架構師審查後修正):
|
||||
1. O(1) 反向索引: 廢除 SCAN,改用 namespace/target 索引直查
|
||||
@@ -30,15 +35,13 @@ from typing import Any
|
||||
import structlog
|
||||
|
||||
from src.core.redis_client import get_redis
|
||||
from src.db.base import get_db_context
|
||||
from src.db.models import IncidentRecord
|
||||
from src.models.incident import (
|
||||
Incident,
|
||||
IncidentStatus,
|
||||
Severity,
|
||||
Signal,
|
||||
)
|
||||
from src.services.graph_rag import topology_graph, BlastRadiusResult
|
||||
from src.services.incident_memory import DualIncidentMemory, get_incident_memory
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
@@ -254,8 +257,15 @@ class IncidentEngine:
|
||||
incident = await engine.process_signal(signal_data)
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
def __init__(self, memory: DualIncidentMemory | None = None) -> None:
|
||||
"""
|
||||
初始化 IncidentEngine
|
||||
|
||||
Args:
|
||||
memory: DualIncidentMemory 實例 (可選,預設使用 Singleton)
|
||||
"""
|
||||
self._graph = topology_graph
|
||||
self._memory = memory or get_incident_memory()
|
||||
self._lua_aggregate_sha: str | None = None
|
||||
self._lua_create_sha: str | None = None
|
||||
|
||||
@@ -519,75 +529,53 @@ class IncidentEngine:
|
||||
incident.affected_services.append(target)
|
||||
|
||||
# =========================================================================
|
||||
# 持久化 (DB 層)
|
||||
# 持久化 (DB 層) - Phase 6.4e: 委託給 DualIncidentMemory
|
||||
# =========================================================================
|
||||
|
||||
async def _persist_to_db(self, incident: Incident) -> None:
|
||||
"""
|
||||
持久化到 SQLite/PostgreSQL (Episodic Memory)
|
||||
持久化到 PostgreSQL (Episodic Memory)
|
||||
|
||||
Phase 6.4e: 委託給 DualIncidentMemory.persist_incident()
|
||||
Redis 已在 Lua Script 中更新,這裡只處理 DB
|
||||
"""
|
||||
try:
|
||||
async with get_db_context() as db:
|
||||
from sqlalchemy import select
|
||||
success = await self._memory.persist_incident(incident)
|
||||
incident.persisted_to_pg = success
|
||||
|
||||
# 檢查是否已存在
|
||||
stmt = select(IncidentRecord).where(
|
||||
IncidentRecord.incident_id == incident.incident_id
|
||||
if success:
|
||||
logger.debug(
|
||||
"db_persisted_via_dual_memory",
|
||||
incident_id=incident.incident_id,
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
"db_persist_failed_via_dual_memory",
|
||||
incident_id=incident.incident_id,
|
||||
)
|
||||
result = await db.execute(stmt)
|
||||
existing = result.scalar_one_or_none()
|
||||
|
||||
if existing:
|
||||
# 更新現有記錄
|
||||
existing.status = incident.status.value
|
||||
existing.severity = incident.severity.value
|
||||
existing.signals = [
|
||||
s.model_dump(mode="json") for s in incident.signals
|
||||
]
|
||||
existing.affected_services = incident.affected_services
|
||||
existing.updated_at = incident.updated_at
|
||||
else:
|
||||
# 建立新記錄
|
||||
record = IncidentRecord(
|
||||
incident_id=incident.incident_id,
|
||||
status=incident.status.value,
|
||||
severity=incident.severity.value,
|
||||
signals=[
|
||||
s.model_dump(mode="json") for s in incident.signals
|
||||
],
|
||||
affected_services=incident.affected_services,
|
||||
decision_chain=(
|
||||
incident.decision_chain.model_dump(mode="json")
|
||||
if incident.decision_chain
|
||||
else None
|
||||
),
|
||||
proposal_ids=[str(pid) for pid in incident.proposal_ids],
|
||||
outcome=(
|
||||
incident.outcome.model_dump(mode="json")
|
||||
if incident.outcome
|
||||
else None
|
||||
),
|
||||
created_at=incident.created_at,
|
||||
updated_at=incident.updated_at,
|
||||
resolved_at=incident.resolved_at,
|
||||
closed_at=incident.closed_at,
|
||||
ttl_days=incident.ttl_days,
|
||||
vectorized=incident.vectorized,
|
||||
)
|
||||
db.add(record)
|
||||
|
||||
incident.persisted_to_pg = True
|
||||
|
||||
logger.debug(
|
||||
"db_persisted",
|
||||
incident_id=incident.incident_id,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.exception("db_save_error", error=str(e))
|
||||
|
||||
# =========================================================================
|
||||
# 從 Episodic Memory 載入 (Phase 6.4e 新增)
|
||||
# =========================================================================
|
||||
|
||||
async def get_incident(self, incident_id: str) -> Incident | None:
|
||||
"""
|
||||
取得 Incident
|
||||
|
||||
Phase 6.4e: 委託給 DualIncidentMemory.load_incident()
|
||||
優先從 Working Memory (Redis) 讀取,miss 時從 Episodic (PostgreSQL) 讀取
|
||||
|
||||
Args:
|
||||
incident_id: Incident ID
|
||||
|
||||
Returns:
|
||||
Incident 或 None
|
||||
"""
|
||||
return await self._memory.load_incident(incident_id)
|
||||
|
||||
# =========================================================================
|
||||
# 輔助方法
|
||||
# =========================================================================
|
||||
|
||||
Reference in New Issue
Block a user