feat(wave6-8): P2.1 fusion + P2.2 governance + P2.4 consensus + Wave 7/8 BLOCKER 修復
承接 Wave 6/7/8 多 engineer 在 agent 限額前完成的代碼,補 commit 解 production HEAD 隱性 import error(decision_fusion 已被 decision_manager 引用但檔案 untracked)。 新增(後端核心): - decision_fusion.py (562 行) — P2.1 方法 III(OpenClaw + Hermes + Elephant 三 LLM 融合) - aiops_timeline.py + aiops_timeline_service.py — critic B4 修復 /api/v1/aiops/timeline endpoint,DB 存取抽到 service 層遵守 leWOOOgo 積木化 - migrations/p2_decision_fusion_columns.sql + rollback — approval_records fusion 欄位 修改(後端整合): - decision_manager.py — fusion 三斷鏈修補(critic B1+B2+B3): · B1: 寫 _evidence_snapshot_ref 到 token.proposal_data · B2: fusion 前計算 complexity_score 並寫 token · B3: fusion composite 寫 token.proposal_data["decision_fusion"] - auto_approve.py — fusion + consensus 認識(critic B3+B5): · composite > 0.7 → auto_execute_eligible bypass min_confidence · source=consensus_engine + score>=0.6 → 規則可信路徑 - consensus_engine.py — db-fix _save_consensus 重用 agent_sessions - governance_agent.py — db-fix _alert PG 寫入 ai_governance_events - approval_db.py — fusion 3 欄位 + 2 partial index + CheckConstraint - db/models.py — schema 對齊 migration - core/config.py — vuln #1 修復:OLLAMA_URL/_FALLBACK_URL field_validator 拒絕公網 IP + 外部域名,僅允許私網/loopback/K8s SVC 白名單 - core/feature_flags.py — P2 fusion + consensus flags - main.py — governance_agent lifespan 啟動 - failover_alerter.py — Wave8-X2: in-memory dedup fallback(Redis 拒絕後不 fail-open) - ollama_*.py — metrics 整合 + recovery 改善 - auto_repair_service.py — verifier 接線 新增(測試 2438 行): - test_decision_fusion.py / test_governance_agent.py / test_consensus_integration.py - test_p2_db_fixes.py / test_wave8_fusion_fixes.py - test_config_url_validation.py(vuln #1 12 tests) - test_failover_alerter.py +Wave8-X2 in-memory dedup 補測 驗收: 116 tests pass (decision_fusion + wave8_fusion + config_url + consensus + governance + p2_db_fixes + failover_alerter) Conflict resolution: - 3 檔(config.py + auto_approve.py + decision_manager.py)git stash pop 衝突 保留 stashed (engineer 最終版),補回 ValueError 「公網 IP」字樣對齊 test Note: 此 commit 解 production HEAD 隱性 import error 仍未修: vuln #4 prompt injection / debugger B14 quota fail-closed / B25-B26 drain_pending_tasks / B8 governance fail alert Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-Authored-By: Multiple Engineers (Wave 6/7/8) <noreply@anthropic.com>
This commit is contained in:
@@ -622,16 +622,75 @@ class ConsensusEngine:
|
||||
return result
|
||||
|
||||
async def _save_consensus(self, result: ConsensusResult) -> None:
|
||||
"""儲存共識結果到 Redis"""
|
||||
"""儲存共識結果到 Redis(熱快取)+ PG(永久記錄)
|
||||
|
||||
2026-04-26 P2-DB-Fix by Claude — db-expert P0 三修(P0.2):
|
||||
補 PG 寫入 agent_sessions,符合 ADR-085 鐵律
|
||||
Redis TTL 到期不再造成共識記憶消失
|
||||
"""
|
||||
# 1. 既有 Redis 寫(熱快取,保留)
|
||||
redis_client = get_redis()
|
||||
key = f"{CONSENSUS_PREFIX}{result.consensus_id}"
|
||||
|
||||
await redis_client.set(
|
||||
key,
|
||||
json.dumps(result.to_dict()),
|
||||
ex=CONSENSUS_TTL,
|
||||
)
|
||||
|
||||
# 2. 補 PG 永久寫入(ADR-085 鐵律 — 失敗不阻斷主流程)
|
||||
try:
|
||||
from src.db.base import get_db_context
|
||||
from src.db.models import AgentSession
|
||||
from sqlalchemy import insert as _sa_insert
|
||||
from hashlib import sha256 as _sha256
|
||||
|
||||
rows = []
|
||||
# 每個 AgentOpinion 寫一行(CISO 可稽核性要求)
|
||||
for opinion in result.opinions:
|
||||
_input_hash = _sha256(
|
||||
json.dumps(opinion.to_dict(), sort_keys=True).encode()
|
||||
).hexdigest()[:16]
|
||||
rows.append({
|
||||
"session_id": result.consensus_id,
|
||||
"incident_id": result.incident_id,
|
||||
"agent_role": opinion.agent_type.value, # sre/security/cost/performance ≤20 chars
|
||||
"vote": "abstain", # AgentOpinion 無標準投票欄;coordinator 行再覆蓋
|
||||
"output_json": opinion.to_dict(),
|
||||
"latency_ms": 0, # Phase 9.4 AgentOpinion 未計 latency
|
||||
"degraded": False,
|
||||
"input_hash": _input_hash,
|
||||
})
|
||||
# coordinator 行:整合決策結果
|
||||
_coord_hash = _sha256(
|
||||
json.dumps({"consensus_id": result.consensus_id}, sort_keys=True).encode()
|
||||
).hexdigest()[:16]
|
||||
rows.append({
|
||||
"session_id": result.consensus_id,
|
||||
"incident_id": result.incident_id,
|
||||
"agent_role": "coordinator",
|
||||
"vote": "approve" if result.consensus_score >= 0.6 else "abstain",
|
||||
"output_json": result.to_dict(),
|
||||
"latency_ms": 0,
|
||||
"degraded": False,
|
||||
"input_hash": _coord_hash,
|
||||
})
|
||||
|
||||
async with get_db_context() as db:
|
||||
await db.execute(_sa_insert(AgentSession), rows)
|
||||
await db.commit()
|
||||
|
||||
logger.info(
|
||||
"consensus_pg_write_ok",
|
||||
consensus_id=result.consensus_id,
|
||||
rows=len(rows),
|
||||
)
|
||||
except Exception as _pg_err:
|
||||
logger.warning(
|
||||
"consensus_pg_write_failed",
|
||||
error=str(_pg_err),
|
||||
consensus_id=result.consensus_id,
|
||||
)
|
||||
|
||||
async def get_consensus(self, consensus_id: str) -> ConsensusResult | None:
|
||||
"""取得共識結果"""
|
||||
redis_client = get_redis()
|
||||
|
||||
Reference in New Issue
Block a user