From e427af3cb2d505ac659df9ae4c8f79aca7d1eecb Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 11 Jun 2026 11:27:50 +0800 Subject: [PATCH] =?UTF-8?q?feat(governance):=20=E6=8E=A5=E5=85=A5=E4=B8=89?= =?UTF-8?q?=20Agent=20=E4=BD=88=E5=BB=BA=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/api/v1/agents.py | 110 +- .../services/ai_agent_deployment_layout.py | 135 +++ .../tests/test_ai_agent_deployment_layout.py | 186 ++++ .../test_ai_agent_deployment_layout_api.py | 31 + apps/web/messages/en.json | 66 ++ apps/web/messages/zh-TW.json | 66 ++ .../tabs/automation-inventory-tab.tsx | 172 ++- apps/web/src/lib/api-client.ts | 108 ++ docs/LOGBOOK.md | 24 + ...AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md | 16 + .../AI_AGENT_DEPLOYMENT_LAYOUT_2026-06-11.md | 116 +++ ...ai_agent_deployment_layout_2026-06-11.json | 976 ++++++++++++++++++ .../ai_agent_deployment_layout_v1.schema.json | 343 ++++++ 13 files changed, 2308 insertions(+), 41 deletions(-) create mode 100644 apps/api/src/services/ai_agent_deployment_layout.py create mode 100644 apps/api/tests/test_ai_agent_deployment_layout.py create mode 100644 apps/api/tests/test_ai_agent_deployment_layout_api.py create mode 100644 docs/ai/AI_AGENT_DEPLOYMENT_LAYOUT_2026-06-11.md create mode 100644 docs/evaluations/ai_agent_deployment_layout_2026-06-11.json create mode 100644 docs/schemas/ai_agent_deployment_layout_v1.schema.json diff --git a/apps/api/src/api/v1/agents.py b/apps/api/src/api/v1/agents.py index db695d12..a0a5d208 100644 --- a/apps/api/src/api/v1/agents.py +++ b/apps/api/src/api/v1/agents.py @@ -35,70 +35,73 @@ from pydantic import BaseModel, Field from src.core.logging import get_logger from src.core.sse import get_publisher +from src.services.agent_market_governance_snapshot import ( + load_latest_agent_market_governance_snapshot, +) +from src.services.agent_service import ( + AgentService, + TaskState, + get_agent_service, +) from src.services.ai_agent_automation_backlog_snapshot import ( load_latest_ai_agent_automation_backlog_snapshot, ) from src.services.ai_agent_automation_inventory_snapshot import ( load_latest_ai_agent_automation_inventory_snapshot, ) -from src.services.agent_market_governance_snapshot import ( - load_latest_agent_market_governance_snapshot, +from src.services.ai_agent_deployment_layout import ( + load_latest_ai_agent_deployment_layout, ) -from src.services.backup_dr_target_inventory import ( - load_latest_backup_dr_target_inventory, +from src.services.ai_provider_route_matrix import ( + load_latest_ai_provider_route_matrix, ) from src.services.backup_dr_readiness_matrix import ( load_latest_backup_dr_readiness_matrix, ) +from src.services.backup_dr_target_inventory import ( + load_latest_backup_dr_target_inventory, +) from src.services.backup_notification_policy import ( load_latest_backup_notification_policy, ) from src.services.backup_restore_drill_approval_package_template import ( load_latest_backup_restore_drill_approval_package_template, ) -from src.services.offsite_escrow_readiness_status import ( - load_latest_offsite_escrow_readiness_status, -) -from src.services.runtime_surface_inventory import ( - load_latest_runtime_surface_inventory, -) -from src.services.gitea_workflow_runner_health import ( - load_latest_gitea_workflow_runner_health, -) -from src.services.observability_contract_matrix import ( - load_latest_observability_contract_matrix, -) -from src.services.ai_provider_route_matrix import ( - load_latest_ai_provider_route_matrix, -) -from src.services.service_health_gap_matrix import ( - load_latest_service_health_gap_matrix, -) -from src.services.service_health_failure_notification_policy import ( - load_latest_service_health_failure_notification_policy, -) -from src.services.package_supply_chain_inventory import ( - load_latest_package_supply_chain_inventory, -) -from src.services.javascript_package_inventory import ( - load_latest_javascript_package_inventory, -) -from src.services.docker_build_surface_inventory import ( - load_latest_docker_build_surface_inventory, +from src.services.dependency_drift_check_plan import ( + load_latest_dependency_drift_check_plan, ) from src.services.dependency_risk_policy import ( load_latest_dependency_risk_policy, ) -from src.services.dependency_drift_check_plan import ( - load_latest_dependency_drift_check_plan, -) from src.services.dependency_upgrade_approval_package_template import ( load_latest_dependency_upgrade_approval_package_template, ) -from src.services.agent_service import ( - AgentService, - TaskState, - get_agent_service, +from src.services.docker_build_surface_inventory import ( + load_latest_docker_build_surface_inventory, +) +from src.services.gitea_workflow_runner_health import ( + load_latest_gitea_workflow_runner_health, +) +from src.services.javascript_package_inventory import ( + load_latest_javascript_package_inventory, +) +from src.services.observability_contract_matrix import ( + load_latest_observability_contract_matrix, +) +from src.services.offsite_escrow_readiness_status import ( + load_latest_offsite_escrow_readiness_status, +) +from src.services.package_supply_chain_inventory import ( + load_latest_package_supply_chain_inventory, +) +from src.services.runtime_surface_inventory import ( + load_latest_runtime_surface_inventory, +) +from src.services.service_health_failure_notification_policy import ( + load_latest_service_health_failure_notification_policy, +) +from src.services.service_health_gap_matrix import ( + load_latest_service_health_gap_matrix, ) router = APIRouter(prefix="/agents", tags=["Agent Teams"]) @@ -494,6 +497,33 @@ async def get_automation_backlog_snapshot() -> dict[str, Any]: ) from exc +@router.get( + "/agent-deployment-layout", + response_model=dict[str, Any], + summary="取得 AI Agent 佈建布局快照", + description=( + "讀取最新已提交的 OpenClaw / Hermes / NemoTron 佈建布局快照;" + "此端點不部署 Agent、不呼叫外部模型、不送 Telegram、不碰 DB/Redis、不讀 Secret payload、" + "不批准 SDK/API/shadow/canary/生產路由或主機變更。" + ), +) +async def get_agent_deployment_layout() -> dict[str, Any]: + """Return the latest read-only AI Agent deployment layout snapshot.""" + try: + return await asyncio.to_thread(load_latest_ai_agent_deployment_layout) + except FileNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=str(exc), + ) from exc + except (json.JSONDecodeError, ValueError) as exc: + logger.error("ai_agent_deployment_layout_invalid", error=str(exc)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="AI Agent 佈建布局快照無效", + ) from exc + + @router.get( "/runtime-surface-inventory", response_model=dict[str, Any], diff --git a/apps/api/src/services/ai_agent_deployment_layout.py b/apps/api/src/services/ai_agent_deployment_layout.py new file mode 100644 index 00000000..665e8ea2 --- /dev/null +++ b/apps/api/src/services/ai_agent_deployment_layout.py @@ -0,0 +1,135 @@ +""" +AI Agent deployment layout snapshot. + +Loads the latest committed, read-only layout for OpenClaw, Hermes, and +NemoTron across hosts, packages, tools, services, projects, web surfaces, +learning loops, and Telegram notification boundaries. This module never +deploys agents, sends Telegram messages, calls providers, or approves writes. +""" + +from __future__ import annotations + +import json +from pathlib import Path +from typing import Any + +from src.services.snapshot_paths import default_evaluations_dir + +_DEFAULT_EVALUATIONS_DIR = default_evaluations_dir(Path(__file__)) +_SNAPSHOT_PATTERN = "ai_agent_deployment_layout_*.json" +_SCHEMA_VERSION = "ai_agent_deployment_layout_v1" + + +def load_latest_ai_agent_deployment_layout( + evaluations_dir: Path | None = None, +) -> dict[str, Any]: + """Load the newest committed AI Agent deployment layout snapshot.""" + directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR + candidates = sorted(directory.glob(_SNAPSHOT_PATTERN)) + if not candidates: + raise FileNotFoundError(f"no AI Agent deployment layout snapshots found in {directory}") + + latest = candidates[-1] + with latest.open(encoding="utf-8") as handle: + payload = json.load(handle) + + if not isinstance(payload, dict): + raise ValueError(f"{latest}: expected JSON object") + _require_schema(payload, _SCHEMA_VERSION, str(latest)) + _require_read_only_layout(payload, str(latest)) + _require_rollup_consistency(payload, str(latest)) + _require_frontend_redaction(payload, str(latest)) + _require_target_boundaries(payload, str(latest)) + return payload + + +def _require_schema(payload: dict[str, Any], expected: str, label: str) -> None: + actual = payload.get("schema_version") + if actual != expected: + raise ValueError(f"{label}: expected schema_version={expected}, got {actual!r}") + + +def _require_read_only_layout(payload: dict[str, Any], label: str) -> None: + program_status = payload.get("program_status") or {} + if program_status.get("read_only_mode") is not True: + raise ValueError(f"{label}: program_status.read_only_mode must be true") + if program_status.get("deployment_authority") != "layout_only_no_runtime_deploy": + raise ValueError(f"{label}: deployment_authority must stay layout_only_no_runtime_deploy") + + boundaries = payload.get("approval_boundaries") or {} + blocked_flags = { + "sdk_installation_allowed", + "paid_api_call_allowed", + "shadow_or_canary_allowed", + "production_routing_allowed", + "destructive_operation_allowed", + "secret_plaintext_allowed", + "autonomous_host_mutation_allowed", + "telegram_direct_send_allowed", + } + allowed = sorted(flag for flag in blocked_flags if boundaries.get(flag) is not False) + if allowed: + raise ValueError(f"{label}: approval boundaries must remain false: {allowed}") + + +def _require_rollup_consistency(payload: dict[str, Any], label: str) -> None: + targets = payload.get("deployment_targets") or [] + rollups = payload.get("rollups") or {} + + if rollups.get("total_targets") != len(targets): + raise ValueError(f"{label}: rollups.total_targets must match deployment_targets") + if rollups.get("by_domain") != _count_by(targets, "domain_id"): + raise ValueError(f"{label}: rollups.by_domain must match deployment_targets") + if rollups.get("by_primary_agent") != _count_by(targets, "primary_agent"): + raise ValueError(f"{label}: rollups.by_primary_agent must match deployment_targets") + if rollups.get("by_deployment_state") != _count_by(targets, "deployment_state"): + raise ValueError(f"{label}: rollups.by_deployment_state must match deployment_targets") + if rollups.get("by_telegram_policy") != _count_by(targets, "telegram_policy"): + raise ValueError(f"{label}: rollups.by_telegram_policy must match deployment_targets") + + blocked_target_ids = sorted( + target.get("target_id") + for target in targets + if target.get("deployment_state") == "blocked_by_gate" + or target.get("automation_level") == "blocked" + ) + if sorted(rollups.get("blocked_target_ids") or []) != blocked_target_ids: + raise ValueError(f"{label}: rollups.blocked_target_ids must match blocked targets") + + +def _require_frontend_redaction(payload: dict[str, Any], label: str) -> None: + redaction = ((payload.get("collaboration_contract") or {}).get("frontend_redaction") or {}) + if redaction.get("operator_conversation_display_allowed") is not False: + raise ValueError(f"{label}: operator conversation display must stay false") + if redaction.get("agent_private_reasoning_display_allowed") is not False: + raise ValueError(f"{label}: agent private reasoning display must stay false") + + +def _require_target_boundaries(payload: dict[str, Any], label: str) -> None: + targets = payload.get("deployment_targets") or [] + missing = [ + target.get("target_id") + for target in targets + if not target.get("approval_gate") + or not target.get("telegram_policy") + or not target.get("communication_channels") + ] + if missing: + raise ValueError(f"{label}: deployment targets missing boundary fields: {sorted(missing)}") + + invalid_nemotron_runtime = [ + target.get("target_id") + for target in targets + if target.get("primary_agent") == "nemotron" + and target.get("automation_level") not in {"observe_only", "blocked"} + ] + if invalid_nemotron_runtime: + raise ValueError(f"{label}: Nemotron targets must stay observe_only or blocked") + + +def _count_by(items: list[dict[str, Any]], key: str) -> dict[str, int]: + counts: dict[str, int] = {} + for item in items: + value = item.get(key) + counts[value] = counts.get(value, 0) + 1 + return counts diff --git a/apps/api/tests/test_ai_agent_deployment_layout.py b/apps/api/tests/test_ai_agent_deployment_layout.py new file mode 100644 index 00000000..b5413fa3 --- /dev/null +++ b/apps/api/tests/test_ai_agent_deployment_layout.py @@ -0,0 +1,186 @@ +from __future__ import annotations + +import json + +import pytest + +from src.services.ai_agent_deployment_layout import ( + load_latest_ai_agent_deployment_layout, +) + + +def test_load_latest_ai_agent_deployment_layout_reads_committed_snapshot(): + data = load_latest_ai_agent_deployment_layout() + + assert data["schema_version"] == "ai_agent_deployment_layout_v1" + assert data["program_status"]["read_only_mode"] is True + assert data["program_status"]["deployment_authority"] == "layout_only_no_runtime_deploy" + assert data["rollups"]["total_targets"] == len(data["deployment_targets"]) == 42 + assert data["rollups"]["by_primary_agent"]["openclaw"] == 17 + assert data["rollups"]["by_primary_agent"]["hermes"] == 23 + assert data["rollups"]["by_primary_agent"]["nemotron"] == 2 + assert data["rollups"]["blocked_target_ids"] == [ + "host_120", + "nemotron_replay_pipeline", + ] + assert data["approval_boundaries"]["telegram_direct_send_allowed"] is False + assert data["approval_boundaries"]["production_routing_allowed"] is False + assert data["collaboration_contract"]["frontend_redaction"]["operator_conversation_display_allowed"] is False + + nemotron_targets = [ + target for target in data["deployment_targets"] if target["primary_agent"] == "nemotron" + ] + assert {target["automation_level"] for target in nemotron_targets} <= {"observe_only", "blocked"} + + +def test_load_latest_ai_agent_deployment_layout_rejects_runtime_authority(tmp_path): + snapshot = _snapshot() + snapshot["approval_boundaries"]["production_routing_allowed"] = True + (tmp_path / "ai_agent_deployment_layout_2026-06-11.json").write_text( + json.dumps(snapshot), + encoding="utf-8", + ) + + with pytest.raises(ValueError, match="approval boundaries"): + load_latest_ai_agent_deployment_layout(tmp_path) + + +def test_load_latest_ai_agent_deployment_layout_rejects_rollup_mismatch(tmp_path): + snapshot = _snapshot() + snapshot["rollups"]["by_primary_agent"]["openclaw"] = 99 + (tmp_path / "ai_agent_deployment_layout_2026-06-11.json").write_text( + json.dumps(snapshot), + encoding="utf-8", + ) + + with pytest.raises(ValueError, match="by_primary_agent"): + load_latest_ai_agent_deployment_layout(tmp_path) + + +def test_load_latest_ai_agent_deployment_layout_rejects_nemotron_runtime(tmp_path): + snapshot = _snapshot() + snapshot["deployment_targets"][0]["primary_agent"] = "nemotron" + snapshot["deployment_targets"][0]["automation_level"] = "hitl_execute_after_approval" + snapshot["rollups"]["by_primary_agent"] = {"nemotron": 1} + (tmp_path / "ai_agent_deployment_layout_2026-06-11.json").write_text( + json.dumps(snapshot), + encoding="utf-8", + ) + + with pytest.raises(ValueError, match="Nemotron targets"): + load_latest_ai_agent_deployment_layout(tmp_path) + + +def _snapshot() -> dict: + return { + "schema_version": "ai_agent_deployment_layout_v1", + "generated_at": "2026-06-11T18:20:00+08:00", + "program_status": { + "overall_completion_percent": 10, + "current_priority": "P1", + "current_task_id": "P1-401", + "next_task_id": "P1-402", + "read_only_mode": True, + "deployment_authority": "layout_only_no_runtime_deploy", + }, + "agent_contracts": [ + { + "agent_id": "openclaw", + "display_name": "OpenClaw", + "primary_specialty": "生產仲裁", + "deployment_lane": "production_decision_core", + "allowed_autonomy": ["只讀診斷"], + "must_delegate_to": ["Hermes"], + "blocked_actions": ["production_write"], + "learning_scope": ["incident"], + }, + { + "agent_id": "hermes", + "display_name": "Hermes", + "primary_specialty": "治理", + "deployment_lane": "governance", + "allowed_autonomy": ["只讀盤點"], + "must_delegate_to": ["OpenClaw"], + "blocked_actions": ["production_write"], + "learning_scope": ["docs"], + }, + { + "agent_id": "nemotron", + "display_name": "NemoTron", + "primary_specialty": "離線評估", + "deployment_lane": "offline_evaluator", + "allowed_autonomy": ["smoke"], + "must_delegate_to": ["OpenClaw"], + "blocked_actions": ["production_route"], + "learning_scope": ["replay"], + }, + ], + "domains": [ + { + "domain_id": "hosts", + "display_name": "主機", + "description": "測試主機", + } + ], + "deployment_targets": [ + { + "target_id": "host_110", + "domain_id": "hosts", + "display_name": "110", + "target_type": "host", + "primary_agent": "openclaw", + "supporting_agents": ["hermes"], + "deployment_state": "active_governed", + "automation_level": "observe_only", + "capabilities": ["monitor"], + "telegram_policy": "failure_only", + "learning_inputs": ["metrics"], + "communication_channels": ["Prometheus"], + "approval_gate": "read_only_allowed", + "evidence_refs": ["infra/ansible/inventory/hosts.yml"], + "next_action": "觀察。", + } + ], + "collaboration_contract": { + "message_bus": "Redis Streams", + "audit_trail": "AgentSession", + "handoff_rules": ["OpenClaw 仲裁"], + "frontend_redaction": { + "operator_conversation_display_allowed": False, + "agent_private_reasoning_display_allowed": False, + "display_policy": "只顯示摘要。", + }, + }, + "learning_contract": { + "event_sources": ["incident"], + "feedback_loops": ["trust"], + "growth_metrics": ["precision"], + "retention_policy": "redacted evidence only", + }, + "telegram_contract": { + "primary_gateway": "telegram_gateway.py", + "bot_roles": ["OpenClaw"], + "notification_classes": ["failure"], + "redaction_policy": "redacted", + "e2e_validation": "ADR-035", + }, + "rollups": { + "total_targets": 1, + "by_domain": {"hosts": 1}, + "by_primary_agent": {"openclaw": 1}, + "by_deployment_state": {"active_governed": 1}, + "by_telegram_policy": {"failure_only": 1}, + "blocked_target_ids": [], + "approval_required_target_ids": [], + }, + "approval_boundaries": { + "sdk_installation_allowed": False, + "paid_api_call_allowed": False, + "shadow_or_canary_allowed": False, + "production_routing_allowed": False, + "destructive_operation_allowed": False, + "secret_plaintext_allowed": False, + "autonomous_host_mutation_allowed": False, + "telegram_direct_send_allowed": False, + }, + } diff --git a/apps/api/tests/test_ai_agent_deployment_layout_api.py b/apps/api/tests/test_ai_agent_deployment_layout_api.py new file mode 100644 index 00000000..3d2f3f6e --- /dev/null +++ b/apps/api/tests/test_ai_agent_deployment_layout_api.py @@ -0,0 +1,31 @@ +from __future__ import annotations + +from fastapi import FastAPI +from fastapi.testclient import TestClient + +from src.api.v1.agents import router + + +def test_ai_agent_deployment_layout_endpoint_returns_committed_snapshot(): + app = FastAPI() + app.include_router(router, prefix="/api/v1") + client = TestClient(app) + + response = client.get("/api/v1/agents/agent-deployment-layout") + + assert response.status_code == 200 + data = response.json() + assert data["schema_version"] == "ai_agent_deployment_layout_v1" + assert data["program_status"]["overall_completion_percent"] == 45 + assert data["program_status"]["current_task_id"] == "P1-402" + assert data["program_status"]["next_task_id"] == "P1-403" + assert data["program_status"]["read_only_mode"] is True + assert data["rollups"]["total_targets"] == 42 + assert data["rollups"]["by_domain"]["hosts"] == 6 + assert data["rollups"]["by_domain"]["websites"] == 5 + assert data["rollups"]["by_primary_agent"]["nemotron"] == 2 + assert data["approval_boundaries"]["telegram_direct_send_allowed"] is False + assert data["approval_boundaries"]["autonomous_host_mutation_allowed"] is False + assert data["collaboration_contract"]["frontend_redaction"]["operator_conversation_display_allowed"] is False + assert any(target["target_id"] == "telegram_gateway" for target in data["deployment_targets"]) + assert any(target["target_id"] == "nemotron_replay_pipeline" for target in data["deployment_targets"]) diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 1b34fd8f..c0f1f453 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -2965,6 +2965,72 @@ "taskBoundaries": "任務邊界", "explicitApprovalTasks": "需明確批准" }, + "deploymentLayout": { + "title": "OpenClaw / Hermes / NemoTron 佈建布局", + "source": "{generated} · {current} → {next}", + "targetsTitle": "優先佈建目標", + "targetsShown": "顯示 {count}/{total}", + "agentContractTitle": "Agent 專長分工", + "telegramTitle": "Telegram Bot 告警合約", + "telegramDetail": "統一入口 {gateway},通知類別 {classes} 種;Agent 不直接持有 token,也不直接發送。", + "learningTitle": "主動學習與協作", + "learningDetail": "事件來源 {sources}、回饋迴圈 {loops}、成長指標 {metrics};先落只讀證據,再走批准關卡。", + "redactionLocked": "前端不顯示對話內容", + "directSendBlocked": "Telegram 直送禁止", + "frontendSafe": "只顯示狀態、證據與邊界", + "metrics": { + "targets": "佈建目標", + "openclaw": "OpenClaw", + "hermes": "Hermes", + "nemotron": "NemoTron", + "approval": "需批准", + "blocked": "阻擋" + }, + "labels": { + "domain": "領域", + "primary": "主責", + "support": "協作", + "telegram": "告警", + "automation": "自動化", + "evidence": "證據", + "bus": "協作匯流" + }, + "agents": { + "openclaw": "OpenClaw", + "hermes": "Hermes", + "nemotron": "NemoTron" + }, + "domains": { + "hosts": "主機", + "packages": "套件", + "tools": "工具", + "services": "服務", + "projects": "專案", + "websites": "網站前後台", + "learning": "學習協作" + }, + "deploymentStates": { + "active_governed": "治理中", + "read_only_layout": "只讀布局", + "blocked_by_gate": "關卡阻擋", + "planned": "規劃中", + "candidate_only": "候選" + }, + "automationLevels": { + "observe_only": "只讀觀察", + "prepare_only": "只準備提案", + "dry_run_only": "僅乾跑", + "hitl_execute_after_approval": "批准後 HITL 執行", + "blocked": "阻擋" + }, + "telegramPolicies": { + "failure_only": "僅失敗", + "action_required": "需處置", + "approval_required": "需批准", + "daily_summary_only": "僅日報", + "no_direct_notify": "不直送" + } + }, "overview": { "title": "決策指揮摘要", "mode": "只讀決策支援", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 1b34fd8f..c0f1f453 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -2965,6 +2965,72 @@ "taskBoundaries": "任務邊界", "explicitApprovalTasks": "需明確批准" }, + "deploymentLayout": { + "title": "OpenClaw / Hermes / NemoTron 佈建布局", + "source": "{generated} · {current} → {next}", + "targetsTitle": "優先佈建目標", + "targetsShown": "顯示 {count}/{total}", + "agentContractTitle": "Agent 專長分工", + "telegramTitle": "Telegram Bot 告警合約", + "telegramDetail": "統一入口 {gateway},通知類別 {classes} 種;Agent 不直接持有 token,也不直接發送。", + "learningTitle": "主動學習與協作", + "learningDetail": "事件來源 {sources}、回饋迴圈 {loops}、成長指標 {metrics};先落只讀證據,再走批准關卡。", + "redactionLocked": "前端不顯示對話內容", + "directSendBlocked": "Telegram 直送禁止", + "frontendSafe": "只顯示狀態、證據與邊界", + "metrics": { + "targets": "佈建目標", + "openclaw": "OpenClaw", + "hermes": "Hermes", + "nemotron": "NemoTron", + "approval": "需批准", + "blocked": "阻擋" + }, + "labels": { + "domain": "領域", + "primary": "主責", + "support": "協作", + "telegram": "告警", + "automation": "自動化", + "evidence": "證據", + "bus": "協作匯流" + }, + "agents": { + "openclaw": "OpenClaw", + "hermes": "Hermes", + "nemotron": "NemoTron" + }, + "domains": { + "hosts": "主機", + "packages": "套件", + "tools": "工具", + "services": "服務", + "projects": "專案", + "websites": "網站前後台", + "learning": "學習協作" + }, + "deploymentStates": { + "active_governed": "治理中", + "read_only_layout": "只讀布局", + "blocked_by_gate": "關卡阻擋", + "planned": "規劃中", + "candidate_only": "候選" + }, + "automationLevels": { + "observe_only": "只讀觀察", + "prepare_only": "只準備提案", + "dry_run_only": "僅乾跑", + "hitl_execute_after_approval": "批准後 HITL 執行", + "blocked": "阻擋" + }, + "telegramPolicies": { + "failure_only": "僅失敗", + "action_required": "需處置", + "approval_required": "需批准", + "daily_summary_only": "僅日報", + "no_direct_notify": "不直送" + } + }, "overview": { "title": "決策指揮摘要", "mode": "只讀決策支援", diff --git a/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx b/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx index e6996857..af07a6c2 100644 --- a/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx +++ b/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx @@ -35,6 +35,7 @@ import { GlassCard } from '@/components/ui/glass-card' import { StatusOrb } from '@/components/ui/status-orb' import { apiClient, + type AiAgentDeploymentLayoutSnapshot, type AiProviderRouteMatrixSnapshot, type AiAgentAutomationBacklogSnapshot, type AiAgentAutomationInventorySnapshot, @@ -311,6 +312,7 @@ export function AutomationInventoryTab() { const [giteaHealth, setGiteaHealth] = useState(null) const [observabilityMatrix, setObservabilityMatrix] = useState(null) const [providerRouteMatrix, setProviderRouteMatrix] = useState(null) + const [deploymentLayout, setDeploymentLayout] = useState(null) const [serviceHealthGapMatrix, setServiceHealthGapMatrix] = useState(null) const [serviceHealthNotificationPolicy, setServiceHealthNotificationPolicy] = useState(null) const [loading, setLoading] = useState(true) @@ -329,6 +331,7 @@ export function AutomationInventoryTab() { apiClient.getGiteaWorkflowRunnerHealth(), apiClient.getObservabilityContractMatrix(), apiClient.getAiProviderRouteMatrix(), + apiClient.getAiAgentDeploymentLayout(), apiClient.getServiceHealthGapMatrix(), apiClient.getServiceHealthFailureNotificationPolicy(), ] as const @@ -346,6 +349,7 @@ export function AutomationInventoryTab() { giteaHealthResult, observabilityMatrixResult, providerRouteMatrixResult, + deploymentLayoutResult, serviceHealthGapMatrixResult, serviceHealthNotificationPolicyResult, ] = results @@ -360,6 +364,7 @@ export function AutomationInventoryTab() { setGiteaHealth(giteaHealthResult.status === 'fulfilled' ? giteaHealthResult.value : null) setObservabilityMatrix(observabilityMatrixResult.status === 'fulfilled' ? observabilityMatrixResult.value : null) setProviderRouteMatrix(providerRouteMatrixResult.status === 'fulfilled' ? providerRouteMatrixResult.value : null) + setDeploymentLayout(deploymentLayoutResult.status === 'fulfilled' ? deploymentLayoutResult.value : null) setServiceHealthGapMatrix(serviceHealthGapMatrixResult.status === 'fulfilled' ? serviceHealthGapMatrixResult.value : null) setServiceHealthNotificationPolicy(serviceHealthNotificationPolicyResult.status === 'fulfilled' ? serviceHealthNotificationPolicyResult.value : null) setError([ @@ -372,6 +377,7 @@ export function AutomationInventoryTab() { giteaHealthResult, observabilityMatrixResult, providerRouteMatrixResult, + deploymentLayoutResult, serviceHealthGapMatrixResult, serviceHealthNotificationPolicyResult, ].some(result => result.status === 'rejected')) @@ -409,6 +415,35 @@ export function AutomationInventoryTab() { .filter(group => group.items.length > 0) }, [backlog]) + const visibleDeploymentTargets = useMemo(() => { + if (!deploymentLayout) return [] + const statePriority = { + blocked_by_gate: 0, + active_governed: 1, + read_only_layout: 2, + planned: 3, + candidate_only: 4, + } as Record + const telegramPriority = { + approval_required: 0, + action_required: 1, + failure_only: 2, + daily_summary_only: 3, + no_direct_notify: 4, + } as Record + return [...deploymentLayout.deployment_targets] + .sort((a, b) => { + const stateLeft = statePriority[a.deployment_state] ?? 5 + const stateRight = statePriority[b.deployment_state] ?? 5 + if (stateLeft !== stateRight) return stateLeft - stateRight + const telegramLeft = telegramPriority[a.telegram_policy] ?? 5 + const telegramRight = telegramPriority[b.telegram_policy] ?? 5 + if (telegramLeft !== telegramRight) return telegramLeft - telegramRight + return a.target_id.localeCompare(b.target_id) + }) + .slice(0, 12) + }, [deploymentLayout]) + const visibleReadinessRows = useMemo(() => { if (!backupReadiness) return [] const priority = { blocked: 0, action_required: 1, deferred: 2, ready: 3 } as Record @@ -552,7 +587,7 @@ export function AutomationInventoryTab() { ) } - if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { + if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { return (
@@ -619,6 +654,12 @@ export function AutomationInventoryTab() { + providerRouteMatrix.rollups.shadow_or_canary_allowed_count + providerRouteMatrix.rollups.runtime_route_change_allowed_count ) + const deploymentLayoutTargets = deploymentLayout.rollups.total_targets + const deploymentLayoutOpenClawTargets = deploymentLayout.rollups.by_primary_agent.openclaw ?? 0 + const deploymentLayoutHermesTargets = deploymentLayout.rollups.by_primary_agent.hermes ?? 0 + const deploymentLayoutNemotronTargets = deploymentLayout.rollups.by_primary_agent.nemotron ?? 0 + const deploymentLayoutBlockedTargets = deploymentLayout.rollups.blocked_target_ids.length + const deploymentLayoutApprovalTargets = deploymentLayout.rollups.approval_required_target_ids.length const serviceHealthActions = serviceHealthGapMatrix.rollups.target_ids_requiring_action.length const serviceHealthStaleGaps = serviceHealthGapMatrix.rollups.stale_endpoint_ids.length const serviceHealthOperatorReviews = serviceHealthGapMatrix.rollups.health_gap_ids.length @@ -771,6 +812,14 @@ export function AutomationInventoryTab() { } } + const deploymentLayoutValueLabel = (group: string, value: string) => { + try { + return t(`deploymentLayout.${group}.${value}` as never) + } catch { + return value + } + } + const serviceHealthValueLabel = (value: string) => { try { return t(`serviceHealth.values.${value}` as never) @@ -942,6 +991,124 @@ export function AutomationInventoryTab() { 0 ? 'warn' : 'ok'} icon={} />
+ +
+
+
+ + + {t('deploymentLayout.title')} + +
+ +
+ +
+ } /> + } /> + } /> + } /> + 0 ? 'warn' : 'ok'} icon={} /> + 0 ? 'danger' : 'ok'} icon={} /> +
+ +
+
+
+ + {t('deploymentLayout.targetsTitle')} + + +
+
+ {visibleDeploymentTargets.map(target => ( +
+
+ + {target.target_id} + + +
+
+ + + + +
+
+ {target.next_action} +
+
+ {target.supporting_agents.length > 0 ? ( + deploymentLayoutValueLabel('agents', agent)).join(' / ')}`} muted /> + ) : null} + +
+
+ ))} +
+
+ +
+
+ {t('deploymentLayout.agentContractTitle')} + {deploymentLayout.agent_contracts.map(contract => ( +
+ + {deploymentLayoutValueLabel('agents', contract.agent_id)} + + + {contract.primary_specialty} + +
+ {contract.allowed_autonomy.slice(0, 2).map(item => ( + + ))} +
+
+ ))} +
+ +
+ {t('deploymentLayout.telegramTitle')} +
+ {t('deploymentLayout.telegramDetail', { + gateway: deploymentLayout.telegram_contract.primary_gateway, + classes: deploymentLayout.telegram_contract.notification_classes.length, + })} +
+
+ + +
+
+ +
+ {t('deploymentLayout.learningTitle')} +
+ {t('deploymentLayout.learningDetail', { + sources: deploymentLayout.learning_contract.event_sources.length, + loops: deploymentLayout.learning_contract.feedback_loops.length, + metrics: deploymentLayout.learning_contract.growth_metrics.length, + })} +
+
+ + +
+
+
+
+
+
+
@@ -2389,6 +2556,9 @@ export function AutomationInventoryTab() { .automation-inventory-provider-map-grid, .automation-inventory-provider-grid, .automation-inventory-provider-route-grid, + .automation-inventory-deployment-layout-kpi-grid, + .automation-inventory-deployment-layout-grid, + .automation-inventory-deployment-layout-target-grid, .automation-inventory-service-health-kpi-grid, .automation-inventory-service-health-map-grid, .automation-inventory-service-health-evidence-grid, diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts index 0d2a8bbe..b721ec46 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -262,6 +262,11 @@ export const apiClient = { return handleResponse(res) }, + async getAiAgentDeploymentLayout() { + const res = await fetch(`${API_BASE_URL}/agents/agent-deployment-layout`) + return handleResponse(res) + }, + async getRuntimeSurfaceInventory() { const res = await fetch(`${API_BASE_URL}/agents/runtime-surface-inventory`) return handleResponse(res) @@ -819,6 +824,109 @@ export interface AiAgentAutomationBacklogSnapshot { > } +export interface AiAgentDeploymentLayoutSnapshot { + schema_version: 'ai_agent_deployment_layout_v1' + generated_at: string + program_status: { + overall_completion_percent: number + current_priority: 'P0' | 'P1' | 'P2' | 'P3' + current_task_id: string + next_task_id: string + read_only_mode: true + deployment_authority: 'layout_only_no_runtime_deploy' + } + agent_contracts: Array<{ + agent_id: string + display_name: string + primary_specialty: string + deployment_lane: string + allowed_autonomy: string[] + must_delegate_to: string[] + blocked_actions: string[] + learning_scope: string[] + }> + domains: Array<{ + domain_id: string + display_name: string + description: string + }> + deployment_targets: Array<{ + target_id: string + domain_id: string + display_name: string + target_type: string + primary_agent: string + supporting_agents: string[] + deployment_state: + | 'active_governed' + | 'read_only_layout' + | 'blocked_by_gate' + | 'planned' + | 'candidate_only' + automation_level: + | 'observe_only' + | 'prepare_only' + | 'dry_run_only' + | 'hitl_execute_after_approval' + | 'blocked' + capabilities: string[] + telegram_policy: + | 'failure_only' + | 'action_required' + | 'approval_required' + | 'daily_summary_only' + | 'no_direct_notify' + learning_inputs: string[] + communication_channels: string[] + approval_gate: string + evidence_refs: string[] + next_action: string + }> + collaboration_contract: { + message_bus: string + audit_trail: string + handoff_rules: string[] + frontend_redaction: { + operator_conversation_display_allowed: false + agent_private_reasoning_display_allowed: false + display_policy: string + } + } + learning_contract: { + event_sources: string[] + feedback_loops: string[] + growth_metrics: string[] + retention_policy: string + } + telegram_contract: { + primary_gateway: string + bot_roles: string[] + notification_classes: string[] + redaction_policy: string + e2e_validation: string + } + rollups: { + total_targets: number + by_domain: Record + by_primary_agent: Record + by_deployment_state: Record + by_telegram_policy: Record + blocked_target_ids: string[] + approval_required_target_ids: string[] + } + approval_boundaries: Record< + | 'sdk_installation_allowed' + | 'paid_api_call_allowed' + | 'shadow_or_canary_allowed' + | 'production_routing_allowed' + | 'destructive_operation_allowed' + | 'secret_plaintext_allowed' + | 'autonomous_host_mutation_allowed' + | 'telegram_direct_send_allowed', + false + > +} + export interface RuntimeSurfaceInventorySnapshot { schema_version: 'runtime_surface_inventory_v1' generated_at: string diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index a03a6695..ba93d1de 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,27 @@ +## 2026-06-11|OpenClaw / Hermes / NemoTron 佈建布局第一波 + +**背景**:統帥要求將 OpenClaw、Hermes、NemoTron 依各自專長安排到所有主機、套件、工具、服務、專案、網站前後台,並納入主動學習、互相溝通、持續成長與 Telegram Bot 告警鏈路。本波先建立可驗證的只讀佈建布局,避免把尚未批准的 runtime deploy、SDK/API、Telegram 發送或主機操作誤當已授權。 + +**完成內容:** +- 新增 `docs/schemas/ai_agent_deployment_layout_v1.schema.json`,定義三 Agent 佈建布局、target、Telegram policy、learning contract、collaboration contract 與 approval boundaries。 +- 新增 `docs/evaluations/ai_agent_deployment_layout_2026-06-11.json`,覆蓋 42 個 target:6 台主機、3 組套件、8 個工具、8 個服務、8 個專案、5 個網站前後台、4 個學習/協作面。 +- 新增 `apps/api/src/services/ai_agent_deployment_layout.py`,只讀載入最新 committed layout,並驗證 `production_routing_allowed`、`telegram_direct_send_allowed`、`autonomous_host_mutation_allowed` 等旗標必須維持 false。 +- 新增 `GET /api/v1/agents/agent-deployment-layout`,只讀回傳佈建布局;端點不部署 Agent、不呼叫外部模型、不送 Telegram、不碰 DB/Redis、不讀 Secret payload。 +- 新增 `docs/ai/AI_AGENT_DEPLOYMENT_LAYOUT_2026-06-11.md`,記錄 OpenClaw / Hermes / NemoTron 分工、Telegram 策略、主動學習與優先順序。 +- 更新 `docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md`,新增 P1-401 完成狀態與 P1-402 / P1-403 / P2-401 / P3-401 下一步。 +- 更新 `apps/web/src/lib/api-client.ts`,加入 `getAiAgentDeploymentLayout()` 與 `AiAgentDeploymentLayoutSnapshot` 型別,供治理後台後續接 UI 使用。 +- 更新 `apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx` 與 i18n,將三 Agent 佈建布局接入治理頁自動化盤點,只顯示狀態、證據、Agent 主責、Telegram policy 與批准邊界,不顯示工作對話內容。 +- 新增 `apps/api/tests/test_ai_agent_deployment_layout.py` 與 `apps/api/tests/test_ai_agent_deployment_layout_api.py`。 + +**完成度與邊界:** +- 三 Agent 佈建布局合約:`100%`。 +- 三 Agent 佈建布局整體:`45%`,因目前已完成 schema / snapshot / API / 測試 / 報告 / 治理頁 UI,尚未做 runtime deploy、Telegram E2E 發送或 AgentSession worker。 +- NemoTron 仍維持離線評估 / smoke / replay 候選,不得直接進 shadow/canary/production route。 +- 所有 Telegram 通知必須走 Gateway / ADR-035;本波不直接送 Telegram、不讀 token、不新增 Bot secret。 +- 120 host 仍維持 blocked,不安排自動修復。 +- 前端只能顯示任務狀態、證據摘要、批准邊界與產物連結,不得顯示操作對話原文或 Agent 私有推理。 +- 本地驗證:JSON parse、API pytest、ruff、web typecheck 通過;in-app Browser 對本機 URL 被安全 policy 阻擋,未取得畫面截圖。 + ## 2026-06-11|IwoooS 即時資安危害優先序與 Wave 0 修補 **背景**:使用者調整 IwoooS 推進方向:不再把「完全不影響現有服務」放在所有工作之前,而是優先處理有即時性資訊安全危害的部分;處理過程仍需主動同步其他專案並避免未協調的服務中斷。本階段先確認全域資安範圍,並處理 source-control 層級可立即收斂的 P0。 diff --git a/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md b/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md index 8bb06a79..7691e240 100644 --- a/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md +++ b/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md @@ -11,10 +11,13 @@ | Agent 市場治理 | 72% | 進行中 | `agent_market_governance_snapshot_v1`、API、UI 分頁、每週觀察流程 | | Nemotron 實際整合應用 | 30% | 完整回放前仍被關卡擋下 | `blocked_needs_evidence`,下一關是 `refresh_source_evidence_then_5_record_smoke_only` | | 工具 / 服務 / 套件 AI 自動化 | 92% | P0 已完成;P1 服務 / runtime / 監控 / provider / service health / 備份 / DR / 套件與供應鏈只讀基線已完成;P1-007 失敗限定通知合約與前端 redaction 合約已完成;下一主線是 P2-004 依賴 / 供應鏈漂移監控 | 狀態分類、盤點 schema、權限矩陣、靜態盤點種子、只讀 API、UI 骨架、驗證、自動化待辦 schema / 快照 / API / 分組 UI、Backup / DR 目標盤點、準備度矩陣、備份通知政策、Backup / DR 證據 UI、復原演練批准包模板、異地 / escrow 準備度狀態、任務批准邊界、確定性進度彙總、Python 套件 / 供應鏈只讀基線、JS pnpm/npm 只讀基線、Docker build surface 只讀基線、CVE / license / drift 嚴重度政策、定期依賴漂移與外部資料來源檢查設計、依賴升級批准包模板、runtime_surface_inventory_v1 schema / snapshot / API / UI、gitea_workflow_runner_health_v1 schema / snapshot / API / UI、observability_contract_matrix_v1 schema / snapshot / API / UI、ai_provider_route_matrix_v1 schema / snapshot / API / UI、service_health_gap_matrix_v1 schema / snapshot / API / UI、service health evidence cards UI、service_health_failure_notification_policy_v1 schema / snapshot / API / UI 已完成 | +| OpenClaw / Hermes / NemoTron 佈建布局 | 45% | P1-401 / P1-402 已完成;仍是只讀 layout 與治理頁顯示,不是 runtime deploy | `ai_agent_deployment_layout_v1` schema、`ai_agent_deployment_layout_2026-06-11.json`、`GET /api/v1/agents/agent-deployment-layout`、治理頁自動化盤點 UI、`AI_AGENT_DEPLOYMENT_LAYOUT_2026-06-11.md` | | 本工作清單與分析報告 | 100% | 已完成 | 本 MD 文件 | AI Agent 自動化工作包目前完成度:**92%**。本工作清單文件本身完成度:**100%**。 +三 Agent 佈建布局目前完成度:**45%**。第一波已完成只讀 schema / snapshot / API / 測試 / 報告,第二波已接入治理頁自動化盤點 UI;正式 runtime 佈署、Telegram E2E 發送與 AgentSession 工作流仍需逐項 gate。 + 完成度計算模型: ```text @@ -54,6 +57,19 @@ AI Agent 自動化工作包目前完成度:**92%**。本工作清單文件本 | Claude Agent SDK 候選 | DevOps / 程式修復專家 | 離線修復評分、patch plan 批判 | SDK/API 使用、未經 OpenClaw/HITL 的執行 | | CrewAI / ADK / Microsoft 候選 | 次級或平台候選 | 觀察 / 回放準備度、能力評分表 | 生產執行 | +### 3.1 OpenClaw / Hermes / NemoTron 佈建布局(P1-401) + +| 項目 | 狀態 | +|---|---| +| 佈建布局 schema | 已完成:`docs/schemas/ai_agent_deployment_layout_v1.schema.json` | +| 佈建布局 snapshot | 已完成:`docs/evaluations/ai_agent_deployment_layout_2026-06-11.json` | +| 只讀 API | 已完成:`GET /api/v1/agents/agent-deployment-layout` | +| 細化報告 | 已完成:`docs/ai/AI_AGENT_DEPLOYMENT_LAYOUT_2026-06-11.md` | +| 前端治理 UI | 已完成:P1-402,治理頁自動化盤點 tab 已顯示佈建布局、主責 Agent、Telegram policy、批准/阻擋狀態 | +| Telegram 三 Agent lane E2E | 待辦:P1-403,必須沿用 Gateway / ADR-035,不允許 Agent 直接發送 | +| AgentSession / Redis Streams runtime loop | 待辦:P2-401,需 migration / worker gate | +| NemoTron 3 Ultra smoke | 待辦:P3-401,需 source refresh + cost/data approval | + ## 4. 工作流總覽 | ID | 工作流 | 目標 | 目前狀態 | 目標狀態 | diff --git a/docs/ai/AI_AGENT_DEPLOYMENT_LAYOUT_2026-06-11.md b/docs/ai/AI_AGENT_DEPLOYMENT_LAYOUT_2026-06-11.md new file mode 100644 index 00000000..034d18a5 --- /dev/null +++ b/docs/ai/AI_AGENT_DEPLOYMENT_LAYOUT_2026-06-11.md @@ -0,0 +1,116 @@ +# OpenClaw / Hermes / NemoTron 佈建布局與協作報告 + +> 日期:2026-06-11(台北時間) +> 狀態:只讀布局與治理頁接線完成,runtime 佈署尚未授權。 +> 權威資料:`docs/evaluations/ai_agent_deployment_layout_2026-06-11.json` +> Schema:`docs/schemas/ai_agent_deployment_layout_v1.schema.json` +> API:`GET /api/v1/agents/agent-deployment-layout` + +## 1. 結論 + +目前已把 OpenClaw、Hermes、NemoTron 依各自專長安排到主機、套件、工具、服務、專案、網站前後台、學習協作與 Telegram 告警布局中。 + +本波完成的是「可驗證佈建布局」,不是直接把 Agent 遠端安裝到所有主機;因此不會碰 Secret、不送 Telegram、不改主機、不裝 SDK、不呼叫付費 API、不進 shadow/canary、不改 production route。 + +## 2. 完成度 + +| 範圍 | 完成度 | 狀態 | +|---|---:|---| +| 三 Agent 佈建布局合約 | 100% | Schema、snapshot、loader、API、測試已建立 | +| 佈建布局資料覆蓋 | 100% | 42 個 target 已納入 | +| 治理頁只讀顯示 | 100% | 自動化盤點 tab 已接 API 與繁中顯示 | +| Runtime 實際部署 | 0% | 需要逐項 gate / approval / deploy | +| Telegram Gateway 整合布局 | 70% | 既有 Gateway / ADR-035 可承接;本波不直接送訊息 | +| 主動學習與協作布局 | 45% | 已定義 AgentSession / Redis Streams / KM / replay 回饋;runtime loop 需後續落地 | + +## 3. Agent 分工 + +| Agent | 專長 | 主要佈建 lane | 可主動做 | 必須交給誰 | +|---|---|---|---|---| +| OpenClaw | 生產仲裁、HITL、執行風險、Telegram action-required | production decision core | 只讀診斷、風險分級、批准包審查、批准後執行仲裁 | Hermes 彙整治理;NemoTron 做離線模型評估 | +| Hermes | 治理、知識、文件、套件/供應鏈、降噪、跨專案盤點 | governance / knowledge / reporting | 盤點、報告、runbook、依賴漂移、降噪提案、批准包草稿 | OpenClaw 仲裁生產與 Telegram;NemoTron 評估模型能力 | +| NemoTron | 離線專家、模型比較、工具呼叫、NIM / NVIDIA replay | offline evaluator | sanitized smoke、replay 評分、輸出合約檢查、模型能力矩陣 | OpenClaw 仲裁 shadow/canary/production;Hermes 彙整來源與報告 | + +## 4. 覆蓋範圍 + +| Domain | Target 數 | 主要 owner | +|---|---:|---| +| 主機 | 6 | OpenClaw | +| 套件與建置 | 3 | Hermes | +| 工具 | 8 | Hermes / OpenClaw | +| 服務 | 8 | OpenClaw / Hermes | +| 專案 | 8 | Hermes / OpenClaw | +| 網站前後台 | 5 | Hermes / OpenClaw | +| 學習與協作 | 4 | OpenClaw / Hermes / NemoTron | + +合計:42 個 target。 + +## 5. 主機佈建策略 + +| 主機 | 角色 | 主 Agent | 狀態 | Telegram | +|---|---|---|---|---| +| 110 | DevOps / runner / monitoring | OpenClaw | active_governed | action_required | +| 188 | AI / Web / DB / Redis / SigNoz | OpenClaw | active_governed | action_required | +| 111 | Ollama fallback | OpenClaw | read_only_layout | failure_only | +| 120 | K3s blocked recovery | OpenClaw | blocked_by_gate | action_required | +| 121 | K3s peer | OpenClaw | active_governed | failure_only | +| 112 | Kali / security evidence | Hermes | read_only_layout | no_direct_notify | + +120 仍維持 blocked,不安排自動修復;必須等 console / SSH / K3s evidence 回來。 + +## 6. Telegram 策略 + +所有 Agent 都不得直接持有 token 或直接送訊息;統一經 `apps/api/src/services/telegram_gateway.py` 與 ADR-035 的部署後 E2E 驗證鏈路。 + +通知等級: + +| 類型 | 策略 | +|---|---| +| critical failure | 立即告警 | +| operator action required | 立即告警 | +| approval required | Telegram approval card | +| success / healthy | 壓低或每日摘要 | +| watch-only no-op | 靜默 | + +訊息不得包含 Secret、token、cookie、private key、原始 payload、操作對話原文或 Agent 私有推理。 + +## 7. 主動學習與互相溝通 + +協作路徑: + +1. Prometheus / Sentry / SigNoz / backup / runtime snapshot 產生事件。 +2. Hermes 先彙整只讀證據與 runbook / dependency / market context。 +3. OpenClaw 仲裁風險、HITL、Telegram action-required 與是否能進 dry-run。 +4. NemoTron 只處理 sanitized smoke / replay / model comparison。 +5. 結果回寫 AgentSession、Timeline、KM、Playbook trust 與 market scorecard。 + +學習來源: + +- incident timeline +- approval outcome +- alert operation log +- service health snapshot +- backup / DR evidence +- dependency drift snapshot +- agent market watch +- NemoTron smoke / replay result + +## 8. 優先順序 + +| 優先級 | 任務 | 狀態 | 下一步 | +|---|---|---|---| +| P1-401 | 建立三 Agent 佈建布局 schema / snapshot / API | 完成 | 進入 Telegram / 協作 runtime gate | +| P1-402 | 治理後台顯示佈建布局 | 完成 | 已顯示 target、owner、Telegram policy、批准/阻擋狀態 | +| P1-403 | Telegram Gateway 對三 Agent lane 做 E2E smoke 包 | 待辦 | 不直接發送,先做批准包 | +| P2-401 | AgentSession / Redis Streams 協作資料面落地 | 待辦 | 需要 migration / worker gate | +| P2-402 | Playbook trust / KM / operator feedback loop 補強 | 待辦 | 先做只讀報表 | +| P3-401 | NemoTron 3 Ultra source refresh + 5-record smoke | 待辦 | 需要 cost/data approval 後才能跑 | + +## 9. 紅線 + +- 不把布局當成 runtime 授權。 +- 不把 AwoooP work item 當成生產批准。 +- 不把 IwoooS UI 可見當成安全授權。 +- 不讓 NemoTron 在 smoke/replay gate 前進 shadow/canary。 +- 不讓 Hermes 直接發 Telegram 或修改生產。 +- 不讓任何 Agent 顯示操作對話原文或私有推理到前端。 diff --git a/docs/evaluations/ai_agent_deployment_layout_2026-06-11.json b/docs/evaluations/ai_agent_deployment_layout_2026-06-11.json new file mode 100644 index 00000000..e35e7b38 --- /dev/null +++ b/docs/evaluations/ai_agent_deployment_layout_2026-06-11.json @@ -0,0 +1,976 @@ +{ + "schema_version": "ai_agent_deployment_layout_v1", + "generated_at": "2026-06-11T18:20:00+08:00", + "program_status": { + "overall_completion_percent": 45, + "current_priority": "P1", + "current_task_id": "P1-402", + "next_task_id": "P1-403", + "read_only_mode": true, + "deployment_authority": "layout_only_no_runtime_deploy" + }, + "agent_contracts": [ + { + "agent_id": "openclaw", + "display_name": "OpenClaw", + "primary_specialty": "生產仲裁、風險判斷、HITL 關卡與執行前後驗證", + "deployment_lane": "production_decision_core", + "allowed_autonomy": [ + "只讀診斷", + "風險分級", + "批准包審查", + "Telegram action-required 分流", + "批准後的 dry-run / 執行仲裁" + ], + "must_delegate_to": [ + "Hermes 負責治理、文件、降噪與盤點彙整", + "NemoTron 負責離線模型能力比較與 replay 評分" + ], + "blocked_actions": [ + "未批准的生產寫入", + "未批准的 destructive operation", + "未批准的 provider route 切換", + "未批准的 Telegram 直接發送", + "未通過 replacement gate 前降級或取代自身生產角色" + ], + "learning_scope": [ + "incident lifecycle", + "approval outcome", + "post-execution verification", + "playbook trust score", + "alert grouping quality" + ] + }, + { + "agent_id": "hermes", + "display_name": "Hermes", + "primary_specialty": "治理、知識管理、文件、套件/供應鏈、降噪與跨專案盤點", + "deployment_lane": "governance_knowledge_and_reporting", + "allowed_autonomy": [ + "只讀盤點", + "Runbook / KM 草稿", + "市場與依賴漂移摘要", + "告警降噪提案", + "批准包起草" + ], + "must_delegate_to": [ + "OpenClaw 仲裁任何生產、Telegram、host mutation 或 rollback", + "NemoTron 評估模型/工具能力與 replay 結果" + ], + "blocked_actions": [ + "直接改生產環境", + "直接發送 Telegram 通知", + "直接修改 Secret", + "自行升級套件或 SDK", + "自行切換 AI provider" + ], + "learning_scope": [ + "runbook freshness", + "docs drift", + "dependency drift", + "service health evidence gap", + "operator review feedback" + ] + }, + { + "agent_id": "nemotron", + "display_name": "NemoTron / Nemotron", + "primary_specialty": "離線專家評估、模型工具能力比較、NIM/NVIDIA replay 與長任務 Agent 能力驗證", + "deployment_lane": "offline_evaluator_and_specialist_candidate", + "allowed_autonomy": [ + "sanitized request pack 分析", + "5-record smoke 評分", + "50-record replay 結果比較", + "工具呼叫輸出合約檢查", + "候選模型能力矩陣更新" + ], + "must_delegate_to": [ + "OpenClaw 仲裁生產風險與是否可進 shadow/canary", + "Hermes 彙整市場來源、文件與 operator 報告" + ], + "blocked_actions": [ + "直接讀取 production secret", + "未批准的 paid API / NIM 呼叫", + "未批准的 SDK 安裝", + "未通過 smoke gate 前進 full replay", + "自行進 shadow/canary 或生產路由" + ], + "learning_scope": [ + "smoke gate failures", + "output contract completeness", + "latency budget", + "tool calling reliability", + "OpenClaw same-run baseline delta" + ] + } + ], + "domains": [ + { + "domain_id": "hosts", + "display_name": "主機", + "description": "110 / 111 / 112 / 120 / 121 / 188 的只讀監控、診斷、備份與批准後修復佈局。" + }, + { + "domain_id": "packages", + "display_name": "套件與建置", + "description": "Python、pnpm/npm、Docker base image、CVE、license、digest 與 drift。" + }, + { + "domain_id": "tools", + "display_name": "工具", + "description": "Gitea、Harbor、Prometheus、Alertmanager、SigNoz、Sentry、Open-WebUI、Telegram、Ansible。" + }, + { + "domain_id": "services", + "display_name": "服務", + "description": "API、Web、AwoooP、IwoooS、PostgreSQL、Redis、K8s workload 與內部控制面。" + }, + { + "domain_id": "projects", + "display_name": "專案", + "description": "AWOOOI 及已納入治理視野的外部/相鄰專案。" + }, + { + "domain_id": "websites", + "display_name": "網站前後台", + "description": "公開站、治理後台、告警後台、AwoooP 後台、IwoooS 後台。" + }, + { + "domain_id": "learning", + "display_name": "學習與協作", + "description": "AgentSession、KM、Playbook trust、market watch、replay harness。" + } + ], + "deployment_targets": [ + { + "target_id": "host_110", + "domain_id": "hosts", + "display_name": "110 DevOps / Gitea runner / monitoring host", + "target_type": "host", + "primary_agent": "openclaw", + "supporting_agents": ["hermes"], + "deployment_state": "active_governed", + "automation_level": "prepare_only", + "capabilities": ["host health diagnosis", "runner health review", "backup freshness review", "approval package"], + "telegram_policy": "action_required", + "learning_inputs": ["Gitea workflow result", "runner evidence", "backup status", "Alertmanager route"], + "communication_channels": ["AwoooP approval", "Telegram failure/action-required", "Prometheus metrics"], + "approval_gate": "host mutation requires human approval", + "evidence_refs": ["infra/ansible/inventory/hosts.yml", "docs/evaluations/gitea_workflow_runner_health_2026-06-05.json"], + "next_action": "把 runner / backup / monitoring 狀態納入 Agent 協作節點,但只允許準備修復提案。" + }, + { + "target_id": "host_188", + "domain_id": "hosts", + "display_name": "188 AI / Web / PostgreSQL / Redis / SigNoz host", + "target_type": "host", + "primary_agent": "openclaw", + "supporting_agents": ["hermes", "nemotron"], + "deployment_state": "active_governed", + "automation_level": "prepare_only", + "capabilities": ["AI provider health review", "database/cache health review", "observability review", "NemoTron candidate evidence"], + "telegram_policy": "action_required", + "learning_inputs": ["AI route matrix", "DB backup status", "SigNoz/ClickHouse evidence", "NemoTron smoke result"], + "communication_channels": ["AwoooP approval", "Telegram failure/action-required", "Prometheus metrics"], + "approval_gate": "host mutation, service restart, provider route change require explicit approval", + "evidence_refs": ["infra/ansible/inventory/hosts.yml", "docs/evaluations/ai_provider_route_matrix_2026-06-05.json"], + "next_action": "建立 OpenClaw 主仲裁、Hermes 彙整、NemoTron 離線評估的 host-188 協作節點。" + }, + { + "target_id": "host_111", + "domain_id": "hosts", + "display_name": "111 Ollama fallback host", + "target_type": "host", + "primary_agent": "openclaw", + "supporting_agents": ["hermes"], + "deployment_state": "read_only_layout", + "automation_level": "observe_only", + "capabilities": ["fallback health observation", "route readiness review"], + "telegram_policy": "failure_only", + "learning_inputs": ["Ollama health", "provider failover evidence"], + "communication_channels": ["Prometheus metrics", "Telegram failure-only"], + "approval_gate": "SSH / service operation requires approval", + "evidence_refs": ["infra/ansible/inventory/hosts.yml", "docs/runbooks/RUNBOOK-OLLAMA-FAILOVER.md"], + "next_action": "保持只讀觀察,避免因 fallback 健康誤判而切換 provider。" + }, + { + "target_id": "host_120", + "domain_id": "hosts", + "display_name": "120 K3s master / blocked recovery host", + "target_type": "host", + "primary_agent": "openclaw", + "supporting_agents": ["hermes"], + "deployment_state": "blocked_by_gate", + "automation_level": "blocked", + "capabilities": ["blocked-state tracking", "recovery checklist", "backup config capture blocker"], + "telegram_policy": "action_required", + "learning_inputs": ["cold-start scorecard", "backup config capture", "host reachability"], + "communication_channels": ["Telegram action-required", "LOGBOOK", "cold-start runbook"], + "approval_gate": "console/SSH recovery evidence required before any automation", + "evidence_refs": ["docs/runbooks/BACKUP-STATUS.md", "docs/runbooks/FULL-STACK-COLD-START-SOP.md"], + "next_action": "維持 blocked,不安排自動修復,等待 console / SSH 復原證據。" + }, + { + "target_id": "host_121", + "domain_id": "hosts", + "display_name": "121 K3s peer host", + "target_type": "host", + "primary_agent": "openclaw", + "supporting_agents": ["hermes"], + "deployment_state": "active_governed", + "automation_level": "observe_only", + "capabilities": ["K3s readiness observation", "failover context", "backup/cold-start evidence"], + "telegram_policy": "failure_only", + "learning_inputs": ["K3s node readiness", "host connectivity", "cold-start scorecard"], + "communication_channels": ["Prometheus metrics", "Telegram failure-only"], + "approval_gate": "K3s mutation requires maintenance window approval", + "evidence_refs": ["infra/ansible/inventory/hosts.yml", "docs/runbooks/K3S-OPTIMIZATION-RUNBOOK.md"], + "next_action": "將 121 視為 K3s readiness 證據節點,不做未批准操作。" + }, + { + "target_id": "host_112", + "domain_id": "hosts", + "display_name": "112 Kali / security evidence host", + "target_type": "host", + "primary_agent": "hermes", + "supporting_agents": ["openclaw"], + "deployment_state": "read_only_layout", + "automation_level": "observe_only", + "capabilities": ["security evidence catalog", "owner response package", "read-only posture projection"], + "telegram_policy": "no_direct_notify", + "learning_inputs": ["IwoooS posture projection", "owner response evidence"], + "communication_channels": ["IwoooS read-only handoff", "operator review"], + "approval_gate": "active scan / host update / credentialed scan require independent approval", + "evidence_refs": ["docs/security/iwooos-posture-projection.snapshot.json", "infra/ansible/inventory/hosts.yml"], + "next_action": "維持只讀安全證據,不啟用 active scan。" + }, + { + "target_id": "pkg_python_api", + "domain_id": "packages", + "display_name": "API Python dependencies", + "target_type": "package_set", + "primary_agent": "hermes", + "supporting_agents": ["openclaw"], + "deployment_state": "active_governed", + "automation_level": "observe_only", + "capabilities": ["dependency inventory", "CVE/license drift review", "upgrade approval package"], + "telegram_policy": "daily_summary_only", + "learning_inputs": ["package inventory", "CVE policy", "dependency drift plan"], + "communication_channels": ["governance UI", "daily summary only"], + "approval_gate": "dependency upgrade requires approval package", + "evidence_refs": ["docs/evaluations/package_supply_chain_inventory_2026-06-04.json"], + "next_action": "Hermes 週期性彙整漂移,OpenClaw 只仲裁高風險升級。" + }, + { + "target_id": "pkg_web_pnpm", + "domain_id": "packages", + "display_name": "Web pnpm/npm dependencies", + "target_type": "package_set", + "primary_agent": "hermes", + "supporting_agents": ["openclaw"], + "deployment_state": "active_governed", + "automation_level": "observe_only", + "capabilities": ["package inventory", "lockfile drift review", "upgrade proposal"], + "telegram_policy": "daily_summary_only", + "learning_inputs": ["javascript package inventory", "dependency drift plan"], + "communication_channels": ["governance UI", "daily summary only"], + "approval_gate": "package upgrade / install requires approval", + "evidence_refs": ["docs/evaluations/javascript_package_inventory_2026-06-04.json"], + "next_action": "只產生升級批准包,不自動 npm/pnpm install。" + }, + { + "target_id": "docker_build_surface", + "domain_id": "packages", + "display_name": "Docker base image and build surface", + "target_type": "container_image", + "primary_agent": "hermes", + "supporting_agents": ["openclaw"], + "deployment_state": "active_governed", + "automation_level": "prepare_only", + "capabilities": ["digest drift review", "base image risk review", "build-surface proposal"], + "telegram_policy": "action_required", + "learning_inputs": ["docker build surface inventory", "CVE policy"], + "communication_channels": ["governance UI", "Telegram action-required for critical drift"], + "approval_gate": "image digest/base image change requires review and deploy gate", + "evidence_refs": ["docs/evaluations/docker_build_surface_inventory_2026-06-04.json"], + "next_action": "把 critical image drift 送 OpenClaw 仲裁,其他由 Hermes 月報。" + }, + { + "target_id": "gitea_actions", + "domain_id": "tools", + "display_name": "Gitea Actions and host runner", + "target_type": "workflow_tool", + "primary_agent": "hermes", + "supporting_agents": ["openclaw"], + "deployment_state": "active_governed", + "automation_level": "observe_only", + "capabilities": ["workflow health matrix", "runner attestation", "notification hygiene"], + "telegram_policy": "action_required", + "learning_inputs": ["workflow results", "runner evidence", "CI failure classes"], + "communication_channels": ["Gitea Actions summary", "Telegram actionable/failure only"], + "approval_gate": "workflow modification requires review", + "evidence_refs": ["docs/evaluations/gitea_workflow_runner_health_2026-06-05.json", ".gitea/workflows/"], + "next_action": "Hermes 彙整 runner / workflow gaps;OpenClaw 只仲裁部署或 workflow 修改。" + }, + { + "target_id": "harbor_registry", + "domain_id": "tools", + "display_name": "Harbor registry", + "target_type": "registry", + "primary_agent": "hermes", + "supporting_agents": ["openclaw"], + "deployment_state": "read_only_layout", + "automation_level": "observe_only", + "capabilities": ["backup freshness", "image inventory", "registry health summary"], + "telegram_policy": "failure_only", + "learning_inputs": ["backup target inventory", "registry backup evidence"], + "communication_channels": ["backup policy", "Telegram failure-only"], + "approval_gate": "registry cleanup / retention change requires approval", + "evidence_refs": ["scripts/backup/backup-harbor.sh", "docs/evaluations/backup_dr_target_inventory_2026-06-04.json"], + "next_action": "只監控 backup / freshness,不自動刪 image。" + }, + { + "target_id": "prometheus_alertmanager", + "domain_id": "tools", + "display_name": "Prometheus / Alertmanager", + "target_type": "observability", + "primary_agent": "hermes", + "supporting_agents": ["openclaw"], + "deployment_state": "active_governed", + "automation_level": "prepare_only", + "capabilities": ["alert contract review", "noise reduction proposal", "E2E chain evidence"], + "telegram_policy": "action_required", + "learning_inputs": ["alert rules", "alert chain metrics", "notification outcome"], + "communication_channels": ["Alertmanager webhook", "Telegram action-required"], + "approval_gate": "alert rule deploy / silence requires approval", + "evidence_refs": ["docs/evaluations/observability_contract_matrix_2026-06-05.json", "docs/adr/ADR-035-telegram-alert-chain-enforcement.md"], + "next_action": "保持告警必到,成功訊息降噪,規則部署另走 deploy-alerts gate。" + }, + { + "target_id": "signoz_clickhouse", + "domain_id": "tools", + "display_name": "SigNoz / ClickHouse", + "target_type": "observability", + "primary_agent": "hermes", + "supporting_agents": ["openclaw"], + "deployment_state": "active_governed", + "automation_level": "observe_only", + "capabilities": ["trace evidence", "log evidence", "storage health review"], + "telegram_policy": "action_required", + "learning_inputs": ["SigNoz alert", "ClickHouse health", "trace/log evidence"], + "communication_channels": ["governance UI", "Telegram action-required"], + "approval_gate": "query-heavy or retention change requires approval", + "evidence_refs": ["docs/evaluations/observability_contract_matrix_2026-06-05.json", "ops/signoz"], + "next_action": "Hermes 做證據摘要,OpenClaw 仲裁重大 storage / retention 風險。" + }, + { + "target_id": "sentry", + "domain_id": "tools", + "display_name": "Sentry", + "target_type": "observability", + "primary_agent": "hermes", + "supporting_agents": ["openclaw"], + "deployment_state": "active_governed", + "automation_level": "observe_only", + "capabilities": ["issue summary", "frontend/backend error drift", "release evidence"], + "telegram_policy": "action_required", + "learning_inputs": ["Sentry issue class", "release marker", "frontend/backend route"], + "communication_channels": ["governance UI", "Telegram action-required"], + "approval_gate": "Sentry DSN / project setting change requires approval", + "evidence_refs": ["apps/web/src/instrumentation.ts", "scripts/backup/backup-sentry.sh"], + "next_action": "只讀錯誤分類,不讀 secret,不直接建立/修改 Sentry 設定。" + }, + { + "target_id": "open_webui", + "domain_id": "tools", + "display_name": "Open-WebUI / AI workspace", + "target_type": "ai_tool", + "primary_agent": "nemotron", + "supporting_agents": ["hermes", "openclaw"], + "deployment_state": "candidate_only", + "automation_level": "observe_only", + "capabilities": ["model evaluation evidence", "AI artifact inventory", "offline specialist review"], + "telegram_policy": "no_direct_notify", + "learning_inputs": ["AI artifact backup", "model evaluation", "operator review"], + "communication_channels": ["offline report", "governance UI"], + "approval_gate": "external model call / NIM route requires cost and data-boundary approval", + "evidence_refs": ["scripts/backup/backup-open-webui.sh", "docs/evaluations/agent_nemotron_contract_tuned_smoke_matrix_2026-06-02.json"], + "next_action": "NemoTron 只做離線評估,不接 production route。" + }, + { + "target_id": "telegram_gateway", + "domain_id": "tools", + "display_name": "Telegram Gateway / Bot alert chain", + "target_type": "notification_gateway", + "primary_agent": "openclaw", + "supporting_agents": ["hermes"], + "deployment_state": "active_governed", + "automation_level": "hitl_execute_after_approval", + "capabilities": ["failure-only routing", "approval card", "dedup", "E2E validation"], + "telegram_policy": "approval_required", + "learning_inputs": ["notification outcome", "dedup result", "operator button action"], + "communication_channels": ["Telegram Bot", "AwoooP approval", "alert operation log"], + "approval_gate": "direct send / bot token change / chat target change requires approval and E2E smoke", + "evidence_refs": ["apps/api/src/services/telegram_gateway.py", "docs/adr/ADR-035-telegram-alert-chain-enforcement.md"], + "next_action": "把三 Agent 的通知都收斂到 Gateway,不讓 Agent 直接持有 token 或直接發送。" + }, + { + "target_id": "ansible_control", + "domain_id": "tools", + "display_name": "Ansible host control plane", + "target_type": "host_iac", + "primary_agent": "openclaw", + "supporting_agents": ["hermes"], + "deployment_state": "read_only_layout", + "automation_level": "hitl_execute_after_approval", + "capabilities": ["check-mode proposal", "host-state drift", "rollback plan"], + "telegram_policy": "approval_required", + "learning_inputs": ["Ansible check-mode", "host textfile exporter", "operator approval outcome"], + "communication_channels": ["AwoooP approval", "Telegram approval-required"], + "approval_gate": "any ansible apply requires independent human approval", + "evidence_refs": ["docs/runbooks/ANSIBLE-OPERATING-MODEL.md", "infra/ansible/inventory/hosts.yml"], + "next_action": "先建立 check-mode 證據;apply 仍必須人工批准。" + }, + { + "target_id": "awoooi_api", + "domain_id": "services", + "display_name": "AWOOOI API backend", + "target_type": "api", + "primary_agent": "openclaw", + "supporting_agents": ["hermes", "nemotron"], + "deployment_state": "active_governed", + "automation_level": "prepare_only", + "capabilities": ["incident decision", "MCP context", "agent API snapshots", "post-execution verification"], + "telegram_policy": "failure_only", + "learning_inputs": ["incident", "timeline event", "approval record", "agent snapshot"], + "communication_channels": ["API", "Redis stream", "Telegram failure-only"], + "approval_gate": "runtime deploy / DB migration / provider route change requires CD gate", + "evidence_refs": ["apps/api/src/api/v1/agents.py", "docs/evaluations/runtime_surface_inventory_2026-06-05.json"], + "next_action": "把新佈建布局暴露為只讀 API,不新增執行端點。" + }, + { + "target_id": "awoooi_web", + "domain_id": "services", + "display_name": "AWOOOI public web frontend", + "target_type": "web", + "primary_agent": "hermes", + "supporting_agents": ["openclaw"], + "deployment_state": "active_governed", + "automation_level": "observe_only", + "capabilities": ["frontend evidence display", "i18n review", "route health summary"], + "telegram_policy": "failure_only", + "learning_inputs": ["browser smoke", "Sentry issue", "i18n validation"], + "communication_channels": ["governance UI", "Sentry", "Telegram failure-only"], + "approval_gate": "frontend deploy requires CD validation and production smoke", + "evidence_refs": ["apps/web/src/app/[locale]/governance/page.tsx", "docs/evaluations/runtime_surface_inventory_2026-06-05.json"], + "next_action": "Hermes 維護 UI/文案與證據卡;OpenClaw 仲裁發布風險。" + }, + { + "target_id": "governance_backoffice", + "domain_id": "services", + "display_name": "Governance backoffice", + "target_type": "backoffice", + "primary_agent": "hermes", + "supporting_agents": ["openclaw"], + "deployment_state": "active_governed", + "automation_level": "observe_only", + "capabilities": ["automation inventory", "agent market", "service health", "deployment layout display"], + "telegram_policy": "action_required", + "learning_inputs": ["snapshot freshness", "operator review", "blocked gate count"], + "communication_channels": ["governance UI", "AwoooP work item"], + "approval_gate": "UI display is not runtime authorization", + "evidence_refs": ["apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx"], + "next_action": "將本 layout 先接進只讀 API,再接治理 UI。" + }, + { + "target_id": "awooop_control_plane", + "domain_id": "services", + "display_name": "AwoooP control plane", + "target_type": "control_plane", + "primary_agent": "openclaw", + "supporting_agents": ["hermes"], + "deployment_state": "active_governed", + "automation_level": "hitl_execute_after_approval", + "capabilities": ["work item", "approval", "truth chain", "run state"], + "telegram_policy": "approval_required", + "learning_inputs": ["approval decision", "run state", "operator timeline"], + "communication_channels": ["AwoooP approvals", "Telegram approval card"], + "approval_gate": "AwoooP work item is not security or production approval by itself", + "evidence_refs": ["apps/web/src/app/[locale]/awooop/page.tsx", "docs/awooop/MASTER-WORKPLAN.md"], + "next_action": "OpenClaw 只把 AwoooP 當批准/證據控制面,不讓候選 Agent 直接執行。" + }, + { + "target_id": "iwooos_security_surface", + "domain_id": "services", + "display_name": "IwoooS security surface", + "target_type": "security_surface", + "primary_agent": "hermes", + "supporting_agents": ["openclaw"], + "deployment_state": "active_governed", + "automation_level": "observe_only", + "capabilities": ["read-only evidence", "owner response packet", "security posture"], + "telegram_policy": "action_required", + "learning_inputs": ["owner response gate", "redacted evidence refs", "scope handoff"], + "communication_channels": ["IwoooS page", "operator review", "Telegram action-required only"], + "approval_gate": "active runtime / scan / host update remains false until independent approval", + "evidence_refs": ["apps/web/src/app/[locale]/iwooos/page.tsx", "docs/security/iwooos-posture-projection.snapshot.json"], + "next_action": "Hermes 整理資安證據,OpenClaw 守住 active runtime gate。" + }, + { + "target_id": "postgresql_primary", + "domain_id": "services", + "display_name": "PostgreSQL primary data layer", + "target_type": "database", + "primary_agent": "openclaw", + "supporting_agents": ["hermes"], + "deployment_state": "active_governed", + "automation_level": "prepare_only", + "capabilities": ["connection health", "backup freshness", "migration risk", "slow query evidence"], + "telegram_policy": "failure_only", + "learning_inputs": ["DB alert", "backup status", "migration outcome"], + "communication_channels": ["Prometheus", "backup status", "Telegram failure-only"], + "approval_gate": "migration / restore / schema change requires approval and backup evidence", + "evidence_refs": ["apps/api/migrations/", "docs/evaluations/backup_dr_readiness_matrix_2026-06-04.json"], + "next_action": "OpenClaw 仲裁 DB 風險;Hermes 產出備份與 migration 證據摘要。" + }, + { + "target_id": "redis_cache", + "domain_id": "services", + "display_name": "Redis / Stream / cache layer", + "target_type": "cache", + "primary_agent": "openclaw", + "supporting_agents": ["hermes"], + "deployment_state": "active_governed", + "automation_level": "prepare_only", + "capabilities": ["stream backlog review", "cache health", "agent bus readiness"], + "telegram_policy": "failure_only", + "learning_inputs": ["Redis alert", "agent stream backlog", "approval outcome"], + "communication_channels": ["Prometheus", "Agent stream", "Telegram failure-only"], + "approval_gate": "flush / restart / data mutation requires approval", + "evidence_refs": ["docs/evaluations/service_health_gap_matrix_2026-06-05.json", "docs/adr/ADR-082-multi-agent-collaboration.md"], + "next_action": "用 Redis stream 作為 Agent 協作匯流,但不允許無批准清除資料。" + }, + { + "target_id": "k8s_workloads", + "domain_id": "services", + "display_name": "K8s workloads / manifests", + "target_type": "k8s", + "primary_agent": "openclaw", + "supporting_agents": ["hermes"], + "deployment_state": "active_governed", + "automation_level": "dry_run_only", + "capabilities": ["manifest mapping", "rollout evidence", "drift interpretation", "dry-run proposal"], + "telegram_policy": "action_required", + "learning_inputs": ["runtime surface inventory", "drift report", "post-execution verifier"], + "communication_channels": ["AwoooP approval", "Telegram action-required", "Prometheus"], + "approval_gate": "kubectl apply / rollout / scale / delete requires approval", + "evidence_refs": ["k8s/awoooi-prod/", "docs/evaluations/runtime_surface_inventory_2026-06-05.json"], + "next_action": "OpenClaw 做乾跑仲裁,Hermes 彙整 manifest 與 runbook。" + }, + { + "target_id": "project_awoooi", + "domain_id": "projects", + "display_name": "AWOOOI core project", + "target_type": "project", + "primary_agent": "openclaw", + "supporting_agents": ["hermes", "nemotron"], + "deployment_state": "active_governed", + "automation_level": "prepare_only", + "capabilities": ["incident governance", "agent market governance", "runtime truth", "deployment evidence"], + "telegram_policy": "action_required", + "learning_inputs": ["LOGBOOK", "HARD_RULES", "market watch", "runtime smoke"], + "communication_channels": ["Gitea", "AwoooP", "Telegram", "governance UI"], + "approval_gate": "production deploy and provider changes require existing CD/HITL gates", + "evidence_refs": ["docs/HARD_RULES.md", "docs/LOGBOOK.md"], + "next_action": "把三 Agent 佈局做成 AWOOOI 的正式只讀控制面資料。" + }, + { + "target_id": "project_awooop", + "domain_id": "projects", + "display_name": "AwoooP operations project surface", + "target_type": "project", + "primary_agent": "openclaw", + "supporting_agents": ["hermes"], + "deployment_state": "active_governed", + "automation_level": "hitl_execute_after_approval", + "capabilities": ["work item", "approval queue", "run monitor", "contract governance"], + "telegram_policy": "approval_required", + "learning_inputs": ["work item status", "approval outcome", "truth chain"], + "communication_channels": ["AwoooP UI", "Telegram approval card"], + "approval_gate": "AwoooP approval is necessary but not sufficient for security or host mutation", + "evidence_refs": ["docs/awooop/MASTER-WORKPLAN.md", "apps/web/src/app/[locale]/awooop/"], + "next_action": "將 Agent 佈局轉為 AwoooP work item templates,但不自動簽核。" + }, + { + "target_id": "project_iwooos", + "domain_id": "projects", + "display_name": "IwoooS security governance", + "target_type": "project", + "primary_agent": "hermes", + "supporting_agents": ["openclaw"], + "deployment_state": "active_governed", + "automation_level": "observe_only", + "capabilities": ["scope handoff", "owner response gate", "redacted evidence"], + "telegram_policy": "action_required", + "learning_inputs": ["IwoooS posture projection", "owner response state"], + "communication_channels": ["IwoooS UI", "operator review"], + "approval_gate": "UI-visible state must not be treated as runtime authorization", + "evidence_refs": ["docs/security/iwooos-posture-projection.snapshot.json"], + "next_action": "Hermes 維持只讀證據卡,OpenClaw 防止 runtime gate 被誤開。" + }, + { + "target_id": "project_vibework", + "domain_id": "projects", + "display_name": "VibeWork adjacent product", + "target_type": "adjacent_project", + "primary_agent": "hermes", + "supporting_agents": ["openclaw"], + "deployment_state": "read_only_layout", + "automation_level": "observe_only", + "capabilities": ["boundary inventory", "release evidence summary", "alert routing expectation"], + "telegram_policy": "no_direct_notify", + "learning_inputs": ["cross-project handoff", "runtime evidence if explicitly supplied"], + "communication_channels": ["operator report only"], + "approval_gate": "separate repo/runtime approval required", + "evidence_refs": ["docs/security/vibework-iwooos-onboarding-handoff.snapshot.json"], + "next_action": "只保留治理視野,不跨 repo 自動部署。" + }, + { + "target_id": "project_stockplatform", + "domain_id": "projects", + "display_name": "StockPlatform adjacent product", + "target_type": "adjacent_project", + "primary_agent": "hermes", + "supporting_agents": ["nemotron", "openclaw"], + "deployment_state": "read_only_layout", + "automation_level": "observe_only", + "capabilities": ["AI research governance", "dry-run review", "human-review boundary"], + "telegram_policy": "no_direct_notify", + "learning_inputs": ["research governance note", "operator-approved evidence"], + "communication_channels": ["operator report only"], + "approval_gate": "separate project approval required before any runtime work", + "evidence_refs": ["docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md"], + "next_action": "NemoTron 只可作研究乾跑,不作投資建議或生產推薦。" + }, + { + "target_id": "project_tsenyang", + "domain_id": "projects", + "display_name": "TSENYANG website adjacent project", + "target_type": "adjacent_project", + "primary_agent": "hermes", + "supporting_agents": ["openclaw"], + "deployment_state": "read_only_layout", + "automation_level": "observe_only", + "capabilities": ["SEO/launch evidence summary", "analytics boundary review"], + "telegram_policy": "no_direct_notify", + "learning_inputs": ["operator-approved launch evidence"], + "communication_channels": ["operator report only"], + "approval_gate": "separate repo/runtime approval required", + "evidence_refs": ["docs/security/iwooos-posture-projection.snapshot.json"], + "next_action": "只保留跨產品治理視角,不直接改網站。" + }, + { + "target_id": "project_bitan", + "domain_id": "projects", + "display_name": "Bitan Pharmacy adjacent project", + "target_type": "adjacent_project", + "primary_agent": "hermes", + "supporting_agents": ["openclaw"], + "deployment_state": "read_only_layout", + "automation_level": "observe_only", + "capabilities": ["production recovery evidence summary", "AI ops loop review"], + "telegram_policy": "no_direct_notify", + "learning_inputs": ["operator-approved recovery evidence"], + "communication_channels": ["operator report only"], + "approval_gate": "separate repo/runtime approval required", + "evidence_refs": ["docs/security/iwooos-posture-projection.snapshot.json"], + "next_action": "只列入相鄰產品治理,不跨專案部署。" + }, + { + "target_id": "project_agent_bounty", + "domain_id": "projects", + "display_name": "agent-bounty-protocol onboarding surface", + "target_type": "adjacent_project", + "primary_agent": "hermes", + "supporting_agents": ["openclaw"], + "deployment_state": "read_only_layout", + "automation_level": "observe_only", + "capabilities": ["read-only security onboarding", "owner gate", "external agent boundary"], + "telegram_policy": "no_direct_notify", + "learning_inputs": ["agent bounty onboarding handoff", "owner response status"], + "communication_channels": ["IwoooS read-only handoff", "operator report"], + "approval_gate": "no runtime, repo, scan, cron, payout or contract action without explicit approval", + "evidence_refs": ["docs/security/agent-bounty-iwooos-onboarding-handoff.snapshot.json"], + "next_action": "維持 IwoooS 只讀收件,不跨到 agent-bounty runtime。" + }, + { + "target_id": "public_frontend", + "domain_id": "websites", + "display_name": "Public frontend routes", + "target_type": "website_frontend", + "primary_agent": "hermes", + "supporting_agents": ["openclaw"], + "deployment_state": "active_governed", + "automation_level": "observe_only", + "capabilities": ["browser smoke evidence", "i18n coverage", "route health"], + "telegram_policy": "failure_only", + "learning_inputs": ["browser smoke", "Sentry error", "route uptime"], + "communication_channels": ["Sentry", "Prometheus", "Telegram failure-only"], + "approval_gate": "frontend deploy requires build/typecheck/browser smoke", + "evidence_refs": ["apps/web/src/app/[locale]/page.tsx"], + "next_action": "Hermes 負責內容/顯示品質;OpenClaw 仲裁發布風險。" + }, + { + "target_id": "governance_ui", + "domain_id": "websites", + "display_name": "Governance UI backoffice", + "target_type": "website_backoffice", + "primary_agent": "hermes", + "supporting_agents": ["openclaw"], + "deployment_state": "active_governed", + "automation_level": "observe_only", + "capabilities": ["agent market display", "automation inventory display", "deployment layout display"], + "telegram_policy": "action_required", + "learning_inputs": ["operator review", "snapshot freshness", "blocked gate count"], + "communication_channels": ["governance UI", "AwoooP work item"], + "approval_gate": "UI display does not authorize runtime action", + "evidence_refs": ["apps/web/src/app/[locale]/governance/page.tsx"], + "next_action": "新增只讀 layout 資料源後再接 UI 呈現。" + }, + { + "target_id": "alerts_backoffice", + "domain_id": "websites", + "display_name": "Alerts and operation-log backoffice", + "target_type": "website_backoffice", + "primary_agent": "openclaw", + "supporting_agents": ["hermes"], + "deployment_state": "active_governed", + "automation_level": "prepare_only", + "capabilities": ["incident triage", "alert operation log", "operator action request"], + "telegram_policy": "action_required", + "learning_inputs": ["alert operation log", "incident timeline", "notification outcome"], + "communication_channels": ["alert-operation-logs UI", "Telegram action-required"], + "approval_gate": "alert action execution requires approval", + "evidence_refs": ["apps/web/src/app/[locale]/alerts/page.tsx", "apps/web/src/app/[locale]/alert-operation-logs/page.tsx"], + "next_action": "OpenClaw 產生 action proposal;Hermes 彙整趨勢與降噪。" + }, + { + "target_id": "awooop_admin", + "domain_id": "websites", + "display_name": "AwoooP admin routes", + "target_type": "website_backoffice", + "primary_agent": "openclaw", + "supporting_agents": ["hermes"], + "deployment_state": "active_governed", + "automation_level": "hitl_execute_after_approval", + "capabilities": ["approval queue", "runs", "contracts", "tenants"], + "telegram_policy": "approval_required", + "learning_inputs": ["approval decision", "run evidence", "contract state"], + "communication_channels": ["AwoooP UI", "Telegram approval card"], + "approval_gate": "AwoooP UI action requires separate policy and human approval", + "evidence_refs": ["apps/web/src/app/[locale]/awooop/"], + "next_action": "將三 Agent 提案映射成 AwoooP work item,不讓候選 Agent 直接執行。" + }, + { + "target_id": "iwooos_admin", + "domain_id": "websites", + "display_name": "IwoooS read-only security routes", + "target_type": "website_backoffice", + "primary_agent": "hermes", + "supporting_agents": ["openclaw"], + "deployment_state": "active_governed", + "automation_level": "observe_only", + "capabilities": ["security mirror", "scope card", "owner response gate"], + "telegram_policy": "action_required", + "learning_inputs": ["owner response", "security posture", "redacted refs"], + "communication_channels": ["IwoooS UI", "operator review"], + "approval_gate": "IwoooS visibility is not active runtime authorization", + "evidence_refs": ["apps/web/src/app/[locale]/iwooos/page.tsx"], + "next_action": "繼續維持低摩擦只讀資安治理。" + }, + { + "target_id": "agent_session_bus", + "domain_id": "learning", + "display_name": "AgentSession / Redis Streams collaboration bus", + "target_type": "learning_bus", + "primary_agent": "openclaw", + "supporting_agents": ["hermes", "nemotron"], + "deployment_state": "planned", + "automation_level": "observe_only", + "capabilities": ["agent handoff", "turn audit", "replayable collaboration"], + "telegram_policy": "no_direct_notify", + "learning_inputs": ["agent session turn", "critic challenge", "coordinator decision"], + "communication_channels": ["Redis stream", "agent_sessions audit"], + "approval_gate": "new runtime worker or schema migration requires approval", + "evidence_refs": ["docs/adr/ADR-082-multi-agent-collaboration.md"], + "next_action": "先補只讀 layout;下一波才評估是否需要 migration / worker。" + }, + { + "target_id": "km_playbook_learning", + "domain_id": "learning", + "display_name": "KM / Playbook trust learning loop", + "target_type": "learning_loop", + "primary_agent": "hermes", + "supporting_agents": ["openclaw"], + "deployment_state": "planned", + "automation_level": "observe_only", + "capabilities": ["runbook freshness", "playbook trust evidence", "negative reinforcement"], + "telegram_policy": "daily_summary_only", + "learning_inputs": ["execution result", "post verification", "operator review"], + "communication_channels": ["KM", "LOGBOOK", "daily summary only"], + "approval_gate": "playbook promotion requires review", + "evidence_refs": ["docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md"], + "next_action": "Hermes 整理學習素材,OpenClaw 仲裁是否可升為 approved playbook。" + }, + { + "target_id": "nemotron_replay_pipeline", + "domain_id": "learning", + "display_name": "NemoTron smoke / replay pipeline", + "target_type": "model_replay", + "primary_agent": "nemotron", + "supporting_agents": ["openclaw", "hermes"], + "deployment_state": "blocked_by_gate", + "automation_level": "blocked", + "capabilities": ["5-record smoke", "output contract review", "latency budget", "same-run baseline delta"], + "telegram_policy": "approval_required", + "learning_inputs": ["smoke gate", "external runner report", "OpenClaw baseline"], + "communication_channels": ["offline report", "operator review", "Telegram approval-required after gate"], + "approval_gate": "refresh source evidence, cost/data approval, then 5-record smoke only", + "evidence_refs": ["docs/evaluations/agent_nemotron_contract_tuned_smoke_matrix_2026-06-02.json", "docs/runbooks/OPENCLAW-REPLACEMENT-EVALUATION.md"], + "next_action": "新增 Nemotron 3 Ultra source evidence 後,先重跑 5-record smoke。" + }, + { + "target_id": "agent_market_watch", + "domain_id": "learning", + "display_name": "AI Agent market watch", + "target_type": "market_watch", + "primary_agent": "hermes", + "supporting_agents": ["openclaw", "nemotron"], + "deployment_state": "active_governed", + "automation_level": "observe_only", + "capabilities": ["primary-source scan", "integration review", "discovery intake", "candidate scoring"], + "telegram_policy": "action_required", + "learning_inputs": ["official docs", "release metadata", "scorecard", "operator review"], + "communication_channels": ["Gitea Actions", "governance UI", "Telegram actionable only"], + "approval_gate": "market watch cannot approve SDK/API/replay/shadow/canary/production", + "evidence_refs": [".gitea/workflows/agent-market-watch.yaml", "docs/evaluations/agent_market_governance_snapshot_2026-06-04.json"], + "next_action": "把 Nemotron 3 Ultra 納入下一次 source refresh 和 scorecard review。" + } + ], + "collaboration_contract": { + "message_bus": "以 Redis Streams / AgentSession / timeline event 作為可回放協作總線;本快照只描述布局,不啟動新 worker。", + "audit_trail": "所有 Agent 建議必須落到可查詢事件、批准包或 committed snapshot;不得只存在聊天視窗。", + "handoff_rules": [ + "OpenClaw 收斂生產風險、HITL 與 Telegram action-required。", + "Hermes 收斂治理、文件、盤點、降噪與跨專案報告。", + "NemoTron 收斂 sanitized 離線評估、模型比較與 replay score。", + "任何 Agent 發現風險升級時必須轉交 OpenClaw 仲裁。", + "任何 Agent 發現市場/依賴漂移時必須轉交 Hermes 彙整。", + "任何候選模型能力聲稱必須先經 NemoTron 或 replay harness 產出同題證據。" + ], + "frontend_redaction": { + "operator_conversation_display_allowed": false, + "agent_private_reasoning_display_allowed": false, + "display_policy": "前端只能顯示任務狀態、證據摘要、批准邊界與產物連結;不得顯示操作對話原文或 Agent 私有推理。" + } + }, + "learning_contract": { + "event_sources": [ + "incident timeline", + "approval outcome", + "alert operation log", + "service health snapshot", + "backup / DR evidence", + "dependency drift snapshot", + "agent market watch", + "NemoTron smoke / replay result" + ], + "feedback_loops": [ + "成功/失敗/中性結果回寫 Playbook trust", + "Critic / Reviewer 挑戰結果回寫 AgentSession", + "Telegram 按鈕與 operator review 回寫 notification outcome", + "Market watch source delta 進入 scorecard / replay gate", + "NemoTron replay 失敗模式回寫 prompt / contract 改善清單" + ], + "growth_metrics": [ + "playbook trust_score 有效更新數", + "general fallback alert ratio", + "agent handoff completion rate", + "notification failure rate", + "smoke gate pass rate", + "operator-approved proposal precision" + ], + "retention_policy": "學習資料只保留 redacted evidence refs、hash、分類與結果;Secret、token、原始對話與私有推理不得進前端或告警。" + }, + "telegram_contract": { + "primary_gateway": "apps/api/src/services/telegram_gateway.py", + "bot_roles": [ + "OpenClaw Bot:生產告警、批准、HITL 與 action-required。", + "Hermes Bot lane:治理摘要、週期性報告與降噪候選,但仍經 Gateway。", + "NemoTron lane:只在 smoke/replay gate 需要 operator review 時產生摘要,不能直接發送。" + ], + "notification_classes": [ + "critical failure: immediate", + "operator action required: immediate", + "approval required: approval card", + "success / healthy: suppressed or daily summary", + "watch-only no-op: quiet" + ], + "redaction_policy": "不得在 Telegram 顯示 Secret、token、cookie、private key、原始 payload、操作對話原文或 Agent 私有推理;只顯示 evidence ref、狀態、影響範圍與下一步。", + "e2e_validation": "沿用 ADR-035:部署前檢查 Secret、部署時注入 K8s Secret、部署後做 E2E 告警鏈路驗證;本布局不直接送測試通知。" + }, + "rollups": { + "total_targets": 42, + "by_domain": { + "hosts": 6, + "packages": 3, + "tools": 8, + "services": 8, + "projects": 8, + "websites": 5, + "learning": 4 + }, + "by_primary_agent": { + "openclaw": 17, + "hermes": 23, + "nemotron": 2 + }, + "by_deployment_state": { + "active_governed": 28, + "read_only_layout": 9, + "blocked_by_gate": 2, + "planned": 2, + "candidate_only": 1 + }, + "by_telegram_policy": { + "failure_only": 8, + "action_required": 17, + "approval_required": 6, + "daily_summary_only": 3, + "no_direct_notify": 8 + }, + "blocked_target_ids": [ + "host_120", + "nemotron_replay_pipeline" + ], + "approval_required_target_ids": [ + "host_120", + "telegram_gateway", + "ansible_control", + "awooop_control_plane", + "project_awooop", + "awooop_admin", + "nemotron_replay_pipeline" + ] + }, + "approval_boundaries": { + "sdk_installation_allowed": false, + "paid_api_call_allowed": false, + "shadow_or_canary_allowed": false, + "production_routing_allowed": false, + "destructive_operation_allowed": false, + "secret_plaintext_allowed": false, + "autonomous_host_mutation_allowed": false, + "telegram_direct_send_allowed": false + } +} diff --git a/docs/schemas/ai_agent_deployment_layout_v1.schema.json b/docs/schemas/ai_agent_deployment_layout_v1.schema.json new file mode 100644 index 00000000..3fef90a9 --- /dev/null +++ b/docs/schemas/ai_agent_deployment_layout_v1.schema.json @@ -0,0 +1,343 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "urn:awoooi:ai-agent-deployment-layout-v1", + "title": "AWOOOI AI Agent 佈建布局快照 v1", + "description": "OpenClaw、Hermes、NemoTron 在主機、套件、工具、服務、專案與網站前後台的只讀佈建布局合約。此 schema 不授權任何生產寫入、Telegram 發送、SDK 安裝、付費 API、shadow/canary 或生產路由切換。", + "type": "object", + "required": [ + "schema_version", + "generated_at", + "program_status", + "agent_contracts", + "domains", + "deployment_targets", + "collaboration_contract", + "learning_contract", + "telegram_contract", + "rollups", + "approval_boundaries" + ], + "properties": { + "schema_version": { + "type": "string", + "const": "ai_agent_deployment_layout_v1" + }, + "generated_at": { + "type": "string", + "minLength": 1 + }, + "program_status": { + "type": "object", + "required": [ + "overall_completion_percent", + "current_priority", + "current_task_id", + "next_task_id", + "read_only_mode", + "deployment_authority" + ], + "properties": { + "overall_completion_percent": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "current_priority": { + "type": "string", + "enum": ["P0", "P1", "P2", "P3"] + }, + "current_task_id": { + "type": "string", + "minLength": 1 + }, + "next_task_id": { + "type": "string", + "minLength": 1 + }, + "read_only_mode": { + "type": "boolean", + "const": true + }, + "deployment_authority": { + "type": "string", + "const": "layout_only_no_runtime_deploy" + } + }, + "additionalProperties": false + }, + "agent_contracts": { + "type": "array", + "minItems": 3, + "items": { + "type": "object", + "required": [ + "agent_id", + "display_name", + "primary_specialty", + "deployment_lane", + "allowed_autonomy", + "must_delegate_to", + "blocked_actions", + "learning_scope" + ], + "properties": { + "agent_id": {"type": "string", "minLength": 1}, + "display_name": {"type": "string", "minLength": 1}, + "primary_specialty": {"type": "string", "minLength": 1}, + "deployment_lane": {"type": "string", "minLength": 1}, + "allowed_autonomy": { + "type": "array", + "items": {"type": "string", "minLength": 1} + }, + "must_delegate_to": { + "type": "array", + "items": {"type": "string", "minLength": 1} + }, + "blocked_actions": { + "type": "array", + "minItems": 1, + "items": {"type": "string", "minLength": 1} + }, + "learning_scope": { + "type": "array", + "items": {"type": "string", "minLength": 1} + } + }, + "additionalProperties": false + } + }, + "domains": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": ["domain_id", "display_name", "description"], + "properties": { + "domain_id": {"type": "string", "minLength": 1}, + "display_name": {"type": "string", "minLength": 1}, + "description": {"type": "string", "minLength": 1} + }, + "additionalProperties": false + } + }, + "deployment_targets": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": [ + "target_id", + "domain_id", + "display_name", + "target_type", + "primary_agent", + "supporting_agents", + "deployment_state", + "automation_level", + "capabilities", + "telegram_policy", + "learning_inputs", + "communication_channels", + "approval_gate", + "evidence_refs", + "next_action" + ], + "properties": { + "target_id": {"type": "string", "minLength": 1}, + "domain_id": {"type": "string", "minLength": 1}, + "display_name": {"type": "string", "minLength": 1}, + "target_type": {"type": "string", "minLength": 1}, + "primary_agent": {"type": "string", "minLength": 1}, + "supporting_agents": { + "type": "array", + "items": {"type": "string", "minLength": 1} + }, + "deployment_state": { + "type": "string", + "enum": [ + "active_governed", + "read_only_layout", + "blocked_by_gate", + "planned", + "candidate_only" + ] + }, + "automation_level": { + "type": "string", + "enum": [ + "observe_only", + "prepare_only", + "dry_run_only", + "hitl_execute_after_approval", + "blocked" + ] + }, + "capabilities": { + "type": "array", + "items": {"type": "string", "minLength": 1} + }, + "telegram_policy": { + "type": "string", + "enum": [ + "failure_only", + "action_required", + "approval_required", + "daily_summary_only", + "no_direct_notify" + ] + }, + "learning_inputs": { + "type": "array", + "items": {"type": "string", "minLength": 1} + }, + "communication_channels": { + "type": "array", + "items": {"type": "string", "minLength": 1} + }, + "approval_gate": {"type": "string", "minLength": 1}, + "evidence_refs": { + "type": "array", + "items": {"type": "string", "minLength": 1} + }, + "next_action": {"type": "string", "minLength": 1} + }, + "additionalProperties": false + } + }, + "collaboration_contract": { + "type": "object", + "required": [ + "message_bus", + "audit_trail", + "handoff_rules", + "frontend_redaction" + ], + "properties": { + "message_bus": {"type": "string", "minLength": 1}, + "audit_trail": {"type": "string", "minLength": 1}, + "handoff_rules": { + "type": "array", + "minItems": 1, + "items": {"type": "string", "minLength": 1} + }, + "frontend_redaction": { + "type": "object", + "required": [ + "operator_conversation_display_allowed", + "agent_private_reasoning_display_allowed", + "display_policy" + ], + "properties": { + "operator_conversation_display_allowed": {"type": "boolean", "const": false}, + "agent_private_reasoning_display_allowed": {"type": "boolean", "const": false}, + "display_policy": {"type": "string", "minLength": 1} + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "learning_contract": { + "type": "object", + "required": [ + "event_sources", + "feedback_loops", + "growth_metrics", + "retention_policy" + ], + "properties": { + "event_sources": { + "type": "array", + "items": {"type": "string", "minLength": 1} + }, + "feedback_loops": { + "type": "array", + "items": {"type": "string", "minLength": 1} + }, + "growth_metrics": { + "type": "array", + "items": {"type": "string", "minLength": 1} + }, + "retention_policy": {"type": "string", "minLength": 1} + }, + "additionalProperties": false + }, + "telegram_contract": { + "type": "object", + "required": [ + "primary_gateway", + "bot_roles", + "notification_classes", + "redaction_policy", + "e2e_validation" + ], + "properties": { + "primary_gateway": {"type": "string", "minLength": 1}, + "bot_roles": { + "type": "array", + "items": {"type": "string", "minLength": 1} + }, + "notification_classes": { + "type": "array", + "items": {"type": "string", "minLength": 1} + }, + "redaction_policy": {"type": "string", "minLength": 1}, + "e2e_validation": {"type": "string", "minLength": 1} + }, + "additionalProperties": false + }, + "rollups": { + "type": "object", + "required": [ + "total_targets", + "by_domain", + "by_primary_agent", + "by_deployment_state", + "by_telegram_policy", + "blocked_target_ids", + "approval_required_target_ids" + ], + "properties": { + "total_targets": {"type": "integer", "minimum": 1}, + "by_domain": {"type": "object", "additionalProperties": {"type": "integer"}}, + "by_primary_agent": {"type": "object", "additionalProperties": {"type": "integer"}}, + "by_deployment_state": {"type": "object", "additionalProperties": {"type": "integer"}}, + "by_telegram_policy": {"type": "object", "additionalProperties": {"type": "integer"}}, + "blocked_target_ids": { + "type": "array", + "items": {"type": "string", "minLength": 1} + }, + "approval_required_target_ids": { + "type": "array", + "items": {"type": "string", "minLength": 1} + } + }, + "additionalProperties": false + }, + "approval_boundaries": { + "type": "object", + "required": [ + "sdk_installation_allowed", + "paid_api_call_allowed", + "shadow_or_canary_allowed", + "production_routing_allowed", + "destructive_operation_allowed", + "secret_plaintext_allowed", + "autonomous_host_mutation_allowed", + "telegram_direct_send_allowed" + ], + "properties": { + "sdk_installation_allowed": {"type": "boolean", "const": false}, + "paid_api_call_allowed": {"type": "boolean", "const": false}, + "shadow_or_canary_allowed": {"type": "boolean", "const": false}, + "production_routing_allowed": {"type": "boolean", "const": false}, + "destructive_operation_allowed": {"type": "boolean", "const": false}, + "secret_plaintext_allowed": {"type": "boolean", "const": false}, + "autonomous_host_mutation_allowed": {"type": "boolean", "const": false}, + "telegram_direct_send_allowed": {"type": "boolean", "const": false} + }, + "additionalProperties": false + } + }, + "additionalProperties": false +}