#!/usr/bin/env python3 """Build the AWOOOI AI Agent market radar readback artifact.""" from __future__ import annotations import argparse import json from datetime import datetime, timezone from pathlib import Path from typing import Any def build_radar( *, market_watch: dict[str, Any], governance_snapshot: dict[str, Any], status_cleanup_dashboard: dict[str, Any], market_watch_path: str, governance_snapshot_path: str, status_cleanup_dashboard_path: str, evidence_commit: str, generated_at: str | None = None, ) -> dict[str, Any]: """Build a read-only market radar readback from committed evidence.""" _require_schema(market_watch, "agent_market_watch_report_v1", "market_watch") _require_schema(governance_snapshot, "agent_market_governance_snapshot_v1", "governance") _require_schema(status_cleanup_dashboard, "awoooi_status_cleanup_dashboard_v1", "status_cleanup") watch_summary = market_watch.get("summary") or {} governance_summary = governance_snapshot.get("summary") or {} status_summary = status_cleanup_dashboard.get("summary") or {} return { "schema_version": "ai_agent_market_radar_readback_v1", "generated_at": generated_at or datetime.now(timezone.utc).isoformat(), "source_scope": { "market_watch_report": market_watch_path, "market_governance_snapshot": governance_snapshot_path, "status_cleanup_dashboard": status_cleanup_dashboard_path, "project_handoff_basis": "Codex Start Here handoff + P2-412 primary-source refresh", "gitea_main_evidence_basis_commit": evidence_commit, "scope_note": "盤點範圍涵蓋近期 Gitea 主線、治理 handoff、AI Agent market watch 與 Status Cleanup gates;不包含 raw chat history。", }, "summary": { "overall_completion_percent": 42.2, "status_cleanup_dashboard_percent": float(status_summary.get("overall_completion_percent", 0)), "market_watch_completion_percent": 100.0, "market_candidates": int(watch_summary.get("candidate_count", 0)), "market_sources": int(watch_summary.get("source_count", 0)), "changed_candidates": int(watch_summary.get("changed_candidates", 0)), "source_failures": int(watch_summary.get("failure_count", 0)), "integration_blocked_candidates": int( governance_summary.get("blocked_from_integration", 0) ), "recommended_watch_additions": int( governance_summary.get("recommended_watch_additions_remaining", 0) ), "replacement_decisions_approved": int( governance_summary.get("replacement_decisions_approved", 0) ), "status": "market_refresh_done_integration_blocked", }, "policy": { "read_only": True, "raw_chat_history_synced": False, "sdk_installation_approved": False, "paid_api_calls_approved": False, "replay_candidate_approved": False, "shadow_or_canary_approved": False, "production_routing_approved": False, "telegram_send_approved": False, "host_write_approved": False, "workflow_modification_approved": False, "openclaw_replacement_approved": False, }, "recent_change_inventory": _recent_change_inventory(status_summary), "market_source_freshness": _market_source_freshness(market_watch), "market_practice_alignment": _market_practice_alignment(), "candidate_role_plan": _candidate_role_plan(governance_snapshot), "priority_workplan": _priority_workplan(), "blocked_gates": [ "replacement_decisions_approved=0", "replay_candidates_approved=0", "sdk_installations_approved=0", "paid_api_calls_approved=0", "shadow_or_canary_approved=0", "production_routing_approved=false", "status_cleanup_controlled_package_ready=true", "memory_write_authorized=false", "critical_break_glass_telegram_send_receipt=false", ], "next_report_contract": { "daily": "每日彙整 Agent 工作量、告警、blocked gates、低中高風險 controlled apply 與 critical break-glass。", "weekly": "每週刷新 market watch、版本新鮮度、replay queue、成本/依賴 gate 與候選優先級。", "monthly": "每月執行正式 market scorecard review,決定是否提出 replay、shadow 或 replacement ADR。", "critical_break_glass_required_for": [ "critical 主機 / 網路 / runtime 寫入", "付費 provider 或 token 上限 critical 變更", "新 SDK / 新 MCP server / 新 runtime component 的 production routing 切換", "OpenClaw production routing replacement", "Telegram Bot 發送策略 critical 變更", ], "agent_auto_allowed_for": [ "read-only market watch", "read-only package/version freshness snapshot", "low-risk evidence aggregation", "no-send report draft", "offline deterministic replay fixture preparation", ], }, } def render_markdown(payload: dict[str, Any]) -> str: """Render a Traditional Chinese operator report.""" summary = payload["summary"] lines = [ "# AI Agent 市場雷達與近期變更盤點", "", f"- 產生時間:`{payload['generated_at']}`", f"- 整體治理完成度:`{summary['overall_completion_percent']}%`", f"- 市場雷達完成度:`{summary['market_watch_completion_percent']}%`", f"- 候選 Agent:`{summary['market_candidates']}`", f"- 官方 / 主要來源:`{summary['market_sources']}`", f"- 來源失敗:`{summary['source_failures']}`", f"- 需要重新審查候選:`{summary['changed_candidates']}`", f"- 仍被整合 gate 擋下:`{summary['integration_blocked_candidates']}`", f"- OpenClaw 取代批准:`{summary['replacement_decisions_approved']}`", "", "## 近期變更盤點", "", "| 優先級 | 工作線 | 狀態 | 進度 | 下一步 |", "|---|---|---|---:|---|", ] for item in payload["recent_change_inventory"]: lines.append( f"| `{item['priority']}` | {item['title']} | `{item['status']}` | " f"`{item['completion_percent']}%` | {item['next_gate']} |" ) lines.extend([ "", "## 市場主流做法對齊", "", "| 做法 | AWOOOI 判定 | 下一步 |", "|---|---|---|", ]) for practice in payload["market_practice_alignment"]: lines.append( f"| {practice['practice']} | `{practice['awoooi_status']}` | {practice['next_step']} |" ) lines.extend([ "", "## Agent 專業角色安排", "", "| Agent / 候選 | 建議角色 | Gate 狀態 | 下一步 |", "|---|---|---|---|", ]) for candidate in payload["candidate_role_plan"]: lines.append( f"| {candidate['display_name']} | {candidate['recommended_role']} | " f"`{candidate['gate_status']}` | {candidate['next_gate']} |" ) lines.extend([ "", "## 優先工作清單", "", "| 順序 | 工作 | 風險 | 自動化模式 | 完成定義 |", "|---:|---|---|---|---|", ]) for item in payload["priority_workplan"]: lines.append( f"| {item['order']} | {item['work_item']} | `{item['risk']}` | " f"`{item['automation_mode']}` | {item['done_definition']} |" ) lines.extend([ "", "## 禁止越界", "", ]) for gate in payload["blocked_gates"]: lines.append(f"- `{gate}`") lines.append("") return "\n".join(lines) def _require_schema(payload: dict[str, Any], schema_version: str, label: str) -> None: if payload.get("schema_version") != schema_version: raise ValueError(f"{label}: expected {schema_version}") def _recent_change_inventory(status_summary: dict[str, Any]) -> list[dict[str, Any]]: return [ { "priority": "P0", "title": "Product Governance Owner Response Dashboard / handoff 收斂", "status": "read_model_ready_runtime_blocked", "completion_percent": 100, "next_gate": "Owner questions 與 boundary acknowledgements 仍需逐項回覆。", }, { "priority": "P0", "title": "Status Cleanup Dashboard read-only API 正式化", "status": str(status_summary.get("dashboard_status", "blocked")), "completion_percent": 100, "next_gate": "controlled package ready;project status / memory 實際寫入仍由獨立 verifier 承接。", }, { "priority": "P0", "title": "Wazuh / IwoooS 可視性邊界", "status": str(status_summary.get("wazuh_agent_visibility_status", "blocked")), "completion_percent": 35, "next_gate": "等待 manager agent registry readback 與 live route readback。", }, { "priority": "P0", "title": "AI Agent market watch 最新 primary-source refresh", "status": "market_refresh_done_integration_blocked", "completion_percent": 100, "next_gate": "更新 scorecard 並進入 offline replay gate,不得直接替換。", }, { "priority": "P1", "title": "日報 / 週報 / 月報數據化報告", "status": "report_contract_defined_runtime_delivery_blocked", "completion_percent": 65, "next_gate": "接 Agent 工作量、Telegram receipt 與 controlled review queue。", }, { "priority": "P1", "title": "工具 / 套件 / 服務 / 主機版本新鮮度", "status": "read_only_inventory_defined_update_execution_blocked", "completion_percent": 55, "next_gate": "定期產生版本 freshness snapshot;低中高風險走 controlled proposal/apply,critical 才進 break-glass review。", }, ] def _market_source_freshness(market_watch: dict[str, Any]) -> list[dict[str, Any]]: interesting = { "openai_agents_sdk_coordinator", "langgraph_incident_kernel", "nemo_nemotron_fabric", "claude_agent_sdk_remediator", "google_adk_stack", "microsoft_agent_framework", "crewai_flows_crews", } rows = [] for candidate in market_watch.get("candidates") or []: candidate_id = str(candidate.get("candidate_id", "")) if candidate_id not in interesting: continue versions = [ { "source_id": source.get("source_id"), "version": source.get("version"), "published_at": source.get("published_at"), "status": source.get("status"), "changed": bool(source.get("changed_since_reference")), } for source in candidate.get("sources") or [] ] rows.append({ "candidate_id": candidate_id, "display_name": candidate.get("display_name"), "changed": bool(candidate.get("changed")), "decision": candidate.get("decision"), "versions": versions, }) return rows def _market_practice_alignment() -> list[dict[str, Any]]: return [ { "practice": "多 Agent handoff / specialist delegation", "source": "https://openai.github.io/openai-agents-python/handoffs/", "awoooi_status": "partially_modeled", "next_step": "將 OpenClaw / Hermes / NemoTron handoff 事件寫入可讀 timeline。", }, { "practice": "Tracing / tool call / guardrail observability", "source": "https://openai.github.io/openai-agents-python/tracing/", "awoooi_status": "missing_unified_trace", "next_step": "建立 Agent run trace id,串接報告、Telegram receipt 與 replay outcome。", }, { "practice": "Durable execution / persistence / controlled review loop", "source": "https://docs.langchain.com/oss/python/langgraph/overview", "awoooi_status": "needed_for_incident_loop", "next_step": "優先把 incident workflow kernel 設計成可暫停、恢復、受控審核與重放。", }, { "practice": "MCP / A2A / enterprise multi-agent interoperability", "source": "https://learn.microsoft.com/en-us/agent-framework/overview/", "awoooi_status": "watch_and_design", "next_step": "MCP server 先做 read-only tool registry,再開 write adapter。", }, { "practice": "Evaluation / replay / profiling before integration", "source": "https://docs.nvidia.com/nemo/agent-toolkit/latest/index.html", "awoooi_status": "strong_fit_for_nemotron", "next_step": "NemoTron 維持 smoke / replay / evaluator,不直接接 production routing。", }, { "practice": "Agent SDK as programmable code/ops remediator", "source": "https://code.claude.com/docs/en/agent-sdk/overview", "awoooi_status": "candidate_for_remediation_lane", "next_step": "只允許 no-write replay 與 patch proposal,禁止自動 merge / deploy。", }, { "practice": "Enterprise-scale ADK with evaluation and observability", "source": "https://docs.cloud.google.com/gemini-enterprise-agent-platform/build/adk", "awoooi_status": "candidate_for_google_stack_review", "next_step": "先納入 weekly watch,成本與資料邊界完成 controlled review 後才可 adapter。", }, ] def _candidate_role_plan(governance_snapshot: dict[str, Any]) -> list[dict[str, Any]]: wanted = { "openclaw_incumbent": "生產仲裁者 / production decision core", "nemo_nemotron_fabric": "離線 replay、模型能力評估、合約輸出 smoke gate", "hermes_agent_personal_platform": "知識記憶、證據草稿、長期技能庫候選", "openai_agents_sdk_coordinator": "Coordinator / handoff / tracing / guardrail 候選", "langgraph_incident_kernel": "durable incident workflow kernel 候選", "claude_agent_sdk_remediator": "DevOps / code remediation patch proposal 候選", "microsoft_agent_framework": "MCP / A2A enterprise workflow 候選", "google_adk_stack": "Gemini / Vertex agent stack 候選", "crewai_flows_crews": "快速多 Agent prototype 候選", } statuses = { str(row.get("candidate_id")): row for row in governance_snapshot.get("candidate_statuses") or [] } rows = [] for candidate_id, role in wanted.items(): status = statuses.get(candidate_id, {}) rows.append({ "candidate_id": candidate_id, "display_name": status.get("display_name") or candidate_id, "recommended_role": role, "gate_status": status.get("gate_status") or "watch_only", "next_gate": status.get("required_next_gate") or "continue_weekly_primary_source_market_watch", }) return rows def _priority_workplan() -> list[dict[str, Any]]: return [ { "order": 1, "work_item": "固定每週 AI Agent market watch 並產生治理 snapshot", "risk": "low", "automation_mode": "agent_auto_read_only", "done_definition": "每週一 09:00 Asia/Taipei 有 watch / integration / discovery / promotion / governance 五份 artifacts。", }, { "order": 2, "work_item": "刷新 market capability scorecard", "risk": "medium", "automation_mode": "agent_controlled_market_scorecard_review", "done_definition": "OpenAI / LangGraph / NeMo-Nemotron / Claude / Microsoft / Google / CrewAI 均有新版官方來源與分數差異。", }, { "order": 3, "work_item": "建立 50 筆歷史 incident offline replay queue", "risk": "medium", "automation_mode": "agent_controlled_prepare_replay_run", "done_definition": "replay fixture 不含 secret,候選結果可與 OpenClaw baseline 比較。", }, { "order": 4, "work_item": "Agent 溝通 / 學習 / 成長可視化 readback", "risk": "medium", "automation_mode": "agent_auto_read_model", "done_definition": "每個 Agent 的 handoff、decision、learning writeback、review score 與 blocked action 可被前端和報告讀到。", }, { "order": 5, "work_item": "Telegram Bot 報告與 critical break-glass 橋接", "risk": "critical", "automation_mode": "controlled_send_or_break_glass_review", "done_definition": "低中高風險由 AI controlled apply / notify;critical 需要 break-glass receipt 才能執行。", }, { "order": 6, "work_item": "工具、套件、服務、主機版本自動 freshness 盤點", "risk": "medium", "automation_mode": "agent_auto_scan_agent_propose", "done_definition": "套件、服務、主機、MCP、AI provider、模型版本都有 stale / upgrade / rollback / approval gate。", }, ] def load_json(path: Path) -> dict[str, Any]: with path.open(encoding="utf-8") as handle: payload = json.load(handle) if not isinstance(payload, dict): raise ValueError(f"{path}: expected JSON object") return payload def main() -> int: parser = argparse.ArgumentParser(description="Build AI Agent market radar readback.") parser.add_argument("--market-watch", required=True) parser.add_argument("--governance-snapshot", required=True) parser.add_argument("--status-cleanup-dashboard", required=True) parser.add_argument("--evidence-commit", required=True) parser.add_argument("--output", required=True) parser.add_argument("--markdown-output", required=True) args = parser.parse_args() payload = build_radar( market_watch=load_json(Path(args.market_watch)), governance_snapshot=load_json(Path(args.governance_snapshot)), status_cleanup_dashboard=load_json(Path(args.status_cleanup_dashboard)), market_watch_path=args.market_watch, governance_snapshot_path=args.governance_snapshot, status_cleanup_dashboard_path=args.status_cleanup_dashboard, evidence_commit=args.evidence_commit, ) Path(args.output).write_text( json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True) + "\n", encoding="utf-8", ) markdown = render_markdown(payload) Path(args.markdown_output).write_text(markdown, encoding="utf-8") print( "AI_AGENT_MARKET_RADAR_READBACK_OK " f"overall={payload['summary']['overall_completion_percent']}% " f"candidates={payload['summary']['market_candidates']} " f"sources={payload['summary']['market_sources']} " f"changed={payload['summary']['changed_candidates']} " f"blocked={payload['summary']['integration_blocked_candidates']} " f"replacement={payload['summary']['replacement_decisions_approved']}" ) return 0 if __name__ == "__main__": raise SystemExit(main())