diff --git a/.gitea/workflows/ai-technology-watch.yaml b/.gitea/workflows/ai-technology-watch.yaml new file mode 100644 index 00000000..bb327a90 --- /dev/null +++ b/.gitea/workflows/ai-technology-watch.yaml @@ -0,0 +1,110 @@ +# ============================================================================= +# AWOOOI AI Technology Watch (Gitea Actions) +# ============================================================================= +# 每 6 小時只讀監控主流 AI 技術 primary sources。此 workflow 只產生 +# Gitea step summary;不安裝 SDK、不呼叫 LLM API、不 commit report、不發 +# Telegram、不切換 provider route、不修改 production。 + +name: AI 技術雷達監控 + +on: + workflow_dispatch: + schedule: + - cron: '0 */6 * * *' + +jobs: + ai-technology-watch: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + + - name: 執行只讀 AI 技術雷達監控 + id: watch + run: | + set -euo pipefail + REPORT="/tmp/ai_technology_watch_report.json" + PREVIOUS_REPORT="$(find docs/evaluations -maxdepth 1 -type f -name 'ai_technology_watch_report_*.json' | sort | tail -n 1 || true)" + PREVIOUS_ARGS=() + if [ -n "$PREVIOUS_REPORT" ]; then + PREVIOUS_ARGS=(--previous-report "$PREVIOUS_REPORT") + echo "使用已提交的上一份 AI 技術雷達 baseline: $PREVIOUS_REPORT" + else + echo "找不到已提交的 AI 技術雷達 baseline,執行第一次 live baseline。" + fi + + python3 scripts/agents/ai-technology-watch.py \ + --registry docs/ai/ai-technology-watch-sources.v1.json \ + --output "$REPORT" \ + --mode live \ + --timeout-seconds 12 \ + "${PREVIOUS_ARGS[@]}" + + python3 -m json.tool "$REPORT" >/dev/null + python3 - "$REPORT" <<'PY' + import json + import os + import sys + + report_path = sys.argv[1] + with open(report_path, encoding="utf-8") as handle: + data = json.load(handle) + + if data.get("schema_version") != "ai_technology_watch_report_v1": + raise SystemExit("AI 技術雷達 schema_version 不正確") + if data.get("mode") != "live": + raise SystemExit("AI 技術雷達 workflow 必須以 live mode 執行") + + policy = data.get("policy") or {} + forbidden = [ + "sdk_installation_approved", + "paid_api_calls_approved", + "production_routing_approved", + "telegram_send_approved", + "model_provider_switch_approved", + "host_write_approved", + ] + unsafe = [key for key in forbidden if policy.get(key) is not False] + if unsafe: + raise SystemExit(f"AI 技術雷達 policy 必須維持 false: {unsafe}") + if policy.get("read_only") is not True: + raise SystemExit("AI 技術雷達必須維持 read_only") + + summary = data.get("summary") + if not isinstance(summary, dict): + raise SystemExit("缺少 AI 技術雷達 summary") + required = [ + "technology_count", + "technology_area_count", + "source_count", + "changed_technologies", + "watch_only_technologies", + "review_queue_count", + "source_failure_count", + "high_priority_count", + ] + missing = [key for key in required if key not in summary] + if missing: + raise SystemExit(f"缺少 AI 技術雷達 summary keys: {missing}") + + output_path = os.environ.get("GITHUB_OUTPUT") + if output_path: + with open(output_path, "a", encoding="utf-8") as handle: + for key in required: + handle.write(f"{key}={summary.get(key, 0)}\n") + + step_summary_path = os.environ.get("GITHUB_STEP_SUMMARY") + if step_summary_path: + with open(step_summary_path, "a", encoding="utf-8") as handle: + handle.write("## AI 技術雷達監控\n\n") + handle.write(f"- 技術項目:{summary['technology_count']}\n") + handle.write(f"- 技術領域:{summary['technology_area_count']}\n") + handle.write(f"- 來源數:{summary['source_count']}\n") + handle.write(f"- 變更技術:{summary['changed_technologies']}\n") + handle.write(f"- 審核佇列:{summary['review_queue_count']}\n") + handle.write(f"- 來源失敗:{summary['source_failure_count']}\n") + handle.write(f"- 高優先級技術:{summary['high_priority_count']}\n") + handle.write("\nPolicy: 只讀監控;此 workflow 不批准 SDK/API/provider/Telegram/host/production 變更。\n") + + print(json.dumps(summary, ensure_ascii=False, sort_keys=True)) + PY diff --git a/apps/api/src/api/v1/agents.py b/apps/api/src/api/v1/agents.py index 4c95919f..58a42b1d 100644 --- a/apps/api/src/api/v1/agents.py +++ b/apps/api/src/api/v1/agents.py @@ -41,6 +41,9 @@ from src.services.agent_market_governance_snapshot import ( from src.services.ai_agent_market_radar_readback import ( load_latest_ai_agent_market_radar_readback, ) +from src.services.ai_technology_radar_readback import ( + load_latest_ai_technology_radar_readback, +) from src.services.agent_service import ( AgentService, TaskState, @@ -718,6 +721,35 @@ async def get_ai_agent_market_radar_readback() -> dict[str, Any]: ) from exc +@router.get( + "/ai-technology-radar-readback", + response_model=dict[str, Any], + summary="取得 AI 技術雷達與滾動更新讀回", + description=( + "讀取最新已提交的 AI 技術雷達 readback;" + "此端點只呈現 AI 技術 primary sources、技術領域、審核佇列、Agent 分工與滾動更新 Gate。" + "它不呼叫外部來源、不安裝 SDK、不呼叫付費 API、不切換模型、不送 Telegram、" + "不改主機、不修改 production routing、不替換 OpenClaw。" + ), +) +async def get_ai_technology_radar_readback() -> dict[str, Any]: + """回傳最新 AI 技術雷達與滾動更新只讀快照。""" + try: + payload = await asyncio.to_thread(load_latest_ai_technology_radar_readback) + return redact_public_lan_topology(payload) + 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_technology_radar_readback_invalid", error=str(exc)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="AI 技術雷達 readback 無效", + ) from exc + + @router.get( "/automation-inventory-snapshot", response_model=dict[str, Any], diff --git a/apps/api/src/services/agent_market_watch.py b/apps/api/src/services/agent_market_watch.py index d0f95f91..ec94e16c 100644 --- a/apps/api/src/services/agent_market_watch.py +++ b/apps/api/src/services/agent_market_watch.py @@ -291,6 +291,16 @@ def _parse_source(source_type: str, body: bytes) -> dict[str, str | None]: "version": str(version) if version else None, "published_at": str(published_at) if published_at else None, } + if source_type == "github_tags": + payload = _loads_json(body) + if isinstance(payload, list) and payload: + first = payload[0] + if isinstance(first, dict): + version = first.get("name") + return { + "version": str(version) if version else None, + "published_at": None, + } return {"version": None, "published_at": None} diff --git a/apps/api/src/services/ai_technology_radar_readback.py b/apps/api/src/services/ai_technology_radar_readback.py new file mode 100644 index 00000000..d9e99e12 --- /dev/null +++ b/apps/api/src/services/ai_technology_radar_readback.py @@ -0,0 +1,68 @@ +""" +AI technology radar readback. + +Loads the committed read-only AI technology radar artifact. The radar is an +operator decision surface only; it does not approve SDK installs, paid API +calls, production routing, Telegram sends, host writes, or OpenClaw replacement. +""" + +from __future__ import annotations + +import json +from pathlib import Path +from typing import Any + +from src.services.snapshot_paths import default_operations_dir + +_DEFAULT_OPERATIONS_DIR = default_operations_dir(Path(__file__)) +_SNAPSHOT_NAME = "ai-technology-radar-readback.snapshot.json" + + +def load_latest_ai_technology_radar_readback( + operations_dir: Path | None = None, +) -> dict[str, Any]: + """Load the committed AI technology radar readback snapshot.""" + directory = operations_dir or _DEFAULT_OPERATIONS_DIR + snapshot_path = directory / _SNAPSHOT_NAME + with snapshot_path.open(encoding="utf-8") as handle: + payload = json.load(handle) + + if not isinstance(payload, dict): + raise ValueError(f"{snapshot_path}: expected JSON object") + if payload.get("schema_version") != "ai_technology_radar_readback_v1": + raise ValueError(f"{snapshot_path}: unexpected schema_version") + + policy = payload.get("policy") or {} + forbidden_true = [ + key + for key in [ + "sdk_installation_approved", + "paid_api_calls_approved", + "production_routing_approved", + "telegram_send_approved", + "model_provider_switch_approved", + "host_write_approved", + "openclaw_replacement_approved", + ] + if policy.get(key) is not False + ] + if forbidden_true: + raise ValueError(f"{snapshot_path}: unsafe policy flags: {forbidden_true}") + if policy.get("read_only") is not True: + raise ValueError(f"{snapshot_path}: read_only policy must be true") + + serialized = json.dumps(payload, ensure_ascii=False) + forbidden_fragments = [ + "/Users/", + ".claude/projects", + ".codex", + "192.168.", + "auth.json", + "conversations", + "sessions", + ] + leaked = [fragment for fragment in forbidden_fragments if fragment in serialized] + if leaked: + raise ValueError(f"{snapshot_path}: forbidden local or raw-history fragment: {leaked}") + + return payload diff --git a/apps/api/src/services/ai_technology_watch.py b/apps/api/src/services/ai_technology_watch.py new file mode 100644 index 00000000..efb143cd --- /dev/null +++ b/apps/api/src/services/ai_technology_watch.py @@ -0,0 +1,183 @@ +""" +AI technology watch service. + +Builds a read-only cross-domain AI technology radar from primary sources. This +wraps the existing market-watch fetcher so the broader radar uses the same +no-SDK, no-paid-API, no-production-change boundary as the Agent market watch. +""" + +from __future__ import annotations + +import importlib.util +import sys +from collections import Counter +from collections.abc import Callable +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + +FetchSource = Callable[[str, int], Any] + +_AGENT_MARKET_WATCH_MODULE = None + + +def run_ai_technology_watch( + registry: dict[str, Any], + *, + registry_path: str, + mode: str = "live", + previous_report: dict[str, Any] | None = None, + timeout_seconds: int = 12, + fetcher: FetchSource | None = None, + generated_at: str | None = None, +) -> dict[str, Any]: + """Build a broad read-only AI technology watch report.""" + run_agent_market_watch = _load_agent_market_watch_runner() + agent_previous = _to_agent_previous_report(previous_report or {}) + raw_report = run_agent_market_watch( + registry, + registry_path=registry_path, + mode=mode, + previous_report=agent_previous, + timeout_seconds=timeout_seconds, + fetcher=fetcher, + generated_at=generated_at, + ) + registry_by_id = { + str(item.get("candidate_id", "")).strip(): item + for item in registry.get("candidates") or [] + if str(item.get("candidate_id", "")).strip() + } + technologies = [ + _technology_row(raw_candidate, registry_by_id.get(raw_candidate["candidate_id"], {})) + for raw_candidate in raw_report.get("candidates") or [] + ] + area_counts = Counter(row["technology_area"] for row in technologies) + priority_counts = Counter(row["evaluation_priority"] for row in technologies) + changed = [row for row in technologies if row["changed"]] + + return { + "schema_version": "ai_technology_watch_report_v1", + "generated_at": generated_at or datetime.now(timezone.utc).isoformat(), + "mode": mode, + "registry": { + "path": registry_path, + "schema_version": str(registry.get("schema_version", "")), + "updated_at": str(registry.get("updated_at", "")), + }, + "cadence": dict(registry.get("cadence") or {}), + "policy": _policy(registry), + "summary": { + "technology_count": len(technologies), + "technology_area_count": len(area_counts), + "source_count": raw_report["summary"]["source_count"], + "changed_technologies": len(changed), + "watch_only_technologies": len(technologies) - len(changed), + "review_queue_count": len(changed), + "source_failure_count": raw_report["summary"]["failure_count"], + "high_priority_count": priority_counts.get("p0", 0) + + priority_counts.get("p1", 0), + }, + "technology_area_counts": dict(sorted(area_counts.items())), + "technologies": technologies, + "review_queue": [_review_queue_item(row) for row in changed], + "new_technology_discovery": raw_report.get("new_candidate_discovery") or [], + "failures": raw_report.get("failures") or [], + } + + +def _load_agent_market_watch_runner() -> Any: + global _AGENT_MARKET_WATCH_MODULE + if _AGENT_MARKET_WATCH_MODULE is not None: + return _AGENT_MARKET_WATCH_MODULE.run_agent_market_watch + + module_name = "awoooi_agent_market_watch_service_for_ai_technology" + service_path = Path(__file__).with_name("agent_market_watch.py") + spec = importlib.util.spec_from_file_location(module_name, service_path) + if spec is None or spec.loader is None: + raise RuntimeError(f"cannot load agent market watch service from {service_path}") + module = importlib.util.module_from_spec(spec) + sys.modules[module_name] = module + spec.loader.exec_module(module) + _AGENT_MARKET_WATCH_MODULE = module + return module.run_agent_market_watch + + +def _policy(registry: dict[str, Any]) -> dict[str, Any]: + policy = dict(registry.get("policy") or {}) + policy.setdefault("read_only", True) + policy.setdefault("sdk_installation_approved", False) + policy.setdefault("paid_api_calls_approved", False) + policy.setdefault("production_routing_approved", False) + policy.setdefault("workflow_modification_approved", False) + policy.setdefault("telegram_send_approved", False) + policy.setdefault("model_provider_switch_approved", False) + policy.setdefault("host_write_approved", False) + return policy + + +def _technology_row(raw_candidate: dict[str, Any], registry_candidate: dict[str, Any]) -> dict[str, Any]: + return { + "technology_id": raw_candidate["candidate_id"], + "display_name": raw_candidate["display_name"], + "technology_area": str(registry_candidate.get("technology_area") or "uncategorized"), + "integration_surface": str(registry_candidate.get("integration_surface") or "watch_only"), + "awoooi_role": str(registry_candidate.get("awoooi_role") or raw_candidate.get("recommended_role") or ""), + "evaluation_priority": raw_candidate.get("evaluation_priority") or "watch", + "requires_cost_approval": bool(raw_candidate.get("requires_cost_approval")), + "requires_dependency_approval": bool(raw_candidate.get("requires_dependency_approval")), + "requires_security_review": bool(registry_candidate.get("requires_security_review", True)), + "sources": raw_candidate.get("sources") or [], + "changed": bool(raw_candidate.get("changed")), + "decision": raw_candidate.get("decision"), + "recommended_actions": _recommended_actions(raw_candidate, registry_candidate), + } + + +def _recommended_actions( + raw_candidate: dict[str, Any], + registry_candidate: dict[str, Any], +) -> list[str]: + if raw_candidate.get("changed"): + return [ + "refresh_ai_technology_scorecard", + "classify_business_applicability", + "prepare_no_install_integration_note", + "route_high_risk_items_to_human_review", + ] + if any(source.get("error") for source in raw_candidate.get("sources") or []): + return ["retry_primary_source_fetch", "keep_current_runtime_status"] + actions = list(registry_candidate.get("steady_state_actions") or []) + return actions or ["keep_watch_only_status"] + + +def _review_queue_item(row: dict[str, Any]) -> dict[str, Any]: + return { + "technology_id": row["technology_id"], + "technology_area": row["technology_area"], + "reason": "primary_source_version_or_content_changed", + "required_next_gate": "scorecard_then_sandbox_or_replay_plan", + "requires_cost_approval": row["requires_cost_approval"], + "requires_dependency_approval": row["requires_dependency_approval"], + "requires_security_review": row["requires_security_review"], + } + + +def _to_agent_previous_report(report: dict[str, Any]) -> dict[str, Any] | None: + if not report: + return None + if report.get("schema_version") == "agent_market_watch_report_v1": + return report + if report.get("schema_version") != "ai_technology_watch_report_v1": + return None + return { + "schema_version": "agent_market_watch_report_v1", + "candidates": [ + { + "candidate_id": row.get("technology_id"), + "sources": row.get("sources") or [], + } + for row in report.get("technologies") or [] + if row.get("technology_id") + ], + } diff --git a/apps/api/tests/test_agent_market_watch.py b/apps/api/tests/test_agent_market_watch.py index d1265ac7..2b9330a8 100644 --- a/apps/api/tests/test_agent_market_watch.py +++ b/apps/api/tests/test_agent_market_watch.py @@ -225,6 +225,48 @@ def test_docs_hash_ignores_dynamic_script_noise(): assert second_report["candidates"][0]["sources"][0]["changed_since_reference"] is False +def test_market_watch_parses_github_tags_for_projects_without_releases(): + registry = { + "schema_version": "agent_market_watch_sources_v1", + "policy": {"replacement_decision_allowed": False}, + "candidates": [ + { + "candidate_id": "pgvector", + "display_name": "pgvector", + "evaluation_priority": "watch", + "recommended_role": "vector store", + "sources": [ + { + "source_id": "pgvector_tags", + "type": "github_tags", + "url": "https://api.github.com/repos/pgvector/pgvector/tags", + } + ], + } + ], + } + + def fetcher(_url: str, _timeout: int) -> FetchedSource: + return FetchedSource( + status="ok", + http_status=200, + body=json.dumps([{"name": "v0.8.0"}]).encode(), + ) + + report = run_agent_market_watch( + registry, + registry_path="registry.json", + mode="live", + fetcher=fetcher, + generated_at="2026-06-25T00:00:00+00:00", + ) + + source = report["candidates"][0]["sources"][0] + assert source["status"] == "ok" + assert source["version"] == "v0.8.0" + assert report["summary"]["failure_count"] == 0 + + def test_versioned_source_ignores_metadata_hash_noise_when_version_is_unchanged(): registry = { "schema_version": "agent_market_watch_sources_v1", diff --git a/apps/api/tests/test_ai_technology_radar_readback.py b/apps/api/tests/test_ai_technology_radar_readback.py new file mode 100644 index 00000000..145eb1f5 --- /dev/null +++ b/apps/api/tests/test_ai_technology_radar_readback.py @@ -0,0 +1,61 @@ +from __future__ import annotations + +import json + +from src.services.ai_technology_radar_readback import ( + load_latest_ai_technology_radar_readback, +) + + +def test_ai_technology_radar_readback_committed_snapshot_is_safe(): + payload = load_latest_ai_technology_radar_readback() + + assert payload["schema_version"] == "ai_technology_radar_readback_v1" + assert payload["summary"]["overall_completion_percent"] == 42.2 + assert payload["summary"]["ai_technology_radar_completion_percent"] == 100.0 + assert payload["summary"]["technology_count"] == 20 + assert payload["summary"]["technology_area_count"] == 6 + assert payload["summary"]["source_count"] == 47 + assert payload["summary"]["source_failures"] == 0 + assert payload["summary"]["high_priority_count"] == 14 + assert payload["summary"]["rolling_update_status"] == ( + "near_real_time_watch_ready_integration_gated" + ) + assert payload["source_scope"]["gitea_main_evidence_basis_commit"] == "683428bd" + + policy = payload["policy"] + assert policy["read_only"] is True + assert policy["raw_chat_history_synced"] is False + assert policy["sdk_installation_approved"] is False + assert policy["paid_api_calls_approved"] is False + assert policy["production_routing_approved"] is False + assert policy["telegram_send_approved"] is False + assert policy["model_provider_switch_approved"] is False + assert policy["host_write_approved"] is False + assert policy["openclaw_replacement_approved"] is False + + serialized = json.dumps(payload, ensure_ascii=False) + assert "/Users/" not in serialized + assert ".claude/projects" not in serialized + assert ".codex" not in serialized + assert "192.168." not in serialized + assert "auth.json" not in serialized + + +def test_ai_technology_radar_readback_contains_roles_and_cadence(): + payload = load_latest_ai_technology_radar_readback() + + roles = {row["agent"]: row for row in payload["professional_agent_roles"]} + assert "OpenClaw" in roles + assert "NemoTron" in roles + assert "Hermes" in roles + assert "MarketRadar" in roles + assert "生產路由" in roles["OpenClaw"]["review_boundary"] + assert "離線回放評估者" in roles["NemoTron"]["professional_role"] + + controls = {row["cadence"]: row for row in payload["rolling_update_controls"]} + assert "每 6 小時" in controls + assert controls["每 6 小時"]["gate"] == "read_only_only" + assert payload["report_contract"]["api_endpoint"] == ( + "/api/v1/agents/ai-technology-radar-readback" + ) diff --git a/apps/api/tests/test_ai_technology_radar_readback_api.py b/apps/api/tests/test_ai_technology_radar_readback_api.py new file mode 100644 index 00000000..ddc343b5 --- /dev/null +++ b/apps/api/tests/test_ai_technology_radar_readback_api.py @@ -0,0 +1,35 @@ +from __future__ import annotations + +import json + +from fastapi import FastAPI +from fastapi.testclient import TestClient + +from src.api.v1.agents import router + + +def test_ai_technology_radar_readback_endpoint_returns_committed_snapshot(): + app = FastAPI() + app.include_router(router, prefix="/api/v1") + client = TestClient(app) + + response = client.get("/api/v1/agents/ai-technology-radar-readback") + + assert response.status_code == 200 + data = response.json() + assert data["schema_version"] == "ai_technology_radar_readback_v1" + assert data["summary"]["overall_completion_percent"] == 42.2 + assert data["summary"]["ai_technology_radar_completion_percent"] == 100.0 + assert data["summary"]["technology_count"] == 20 + assert data["summary"]["source_count"] == 47 + assert data["summary"]["source_failures"] == 0 + assert data["policy"]["read_only"] is True + assert data["policy"]["sdk_installation_approved"] is False + assert data["policy"]["telegram_send_approved"] is False + assert data["policy"]["openclaw_replacement_approved"] is False + + serialized = json.dumps(data, ensure_ascii=False) + assert "/Users/" not in serialized + assert ".claude/projects" not in serialized + assert ".codex" not in serialized + assert "192.168." not in serialized diff --git a/apps/api/tests/test_ai_technology_watch.py b/apps/api/tests/test_ai_technology_watch.py new file mode 100644 index 00000000..1af1daa5 --- /dev/null +++ b/apps/api/tests/test_ai_technology_watch.py @@ -0,0 +1,148 @@ +from __future__ import annotations + +import json + +from src.services.agent_market_watch import FetchedSource +from src.services.ai_technology_watch import run_ai_technology_watch + + +def test_ai_technology_watch_groups_areas_and_blocks_integration(): + registry = { + "schema_version": "ai_technology_watch_sources_v1", + "updated_at": "2026-06-25", + "cadence": { + "near_real_time_watch": "每 6 小時", + "daily_triage": "每日", + "weekly_scorecard": "每週", + "monthly_strategy_review": "每月", + }, + "policy": { + "read_only": True, + "sdk_installation_approved": False, + "paid_api_calls_approved": False, + "production_routing_approved": False, + "telegram_send_approved": False, + "model_provider_switch_approved": False, + "host_write_approved": False, + }, + "candidates": [ + { + "candidate_id": "langgraph_runtime", + "display_name": "LangGraph", + "technology_area": "agent_frameworks", + "integration_surface": "durable_workflow_human_in_loop", + "awoooi_role": "事件工作流候選", + "evaluation_priority": "p0", + "requires_cost_approval": False, + "requires_dependency_approval": True, + "requires_security_review": True, + "sources": [ + { + "source_id": "langgraph_pypi", + "type": "pypi", + "url": "https://pypi.org/pypi/langgraph/json", + "reference_version": "1.0.0", + } + ], + }, + { + "candidate_id": "ragas_eval", + "display_name": "Ragas", + "technology_area": "evaluation_and_observability", + "integration_surface": "rag_evaluation", + "awoooi_role": "RAG 品質評測候選", + "evaluation_priority": "p1", + "requires_cost_approval": False, + "requires_dependency_approval": True, + "requires_security_review": True, + "sources": [ + { + "source_id": "ragas_pypi", + "type": "pypi", + "url": "https://pypi.org/pypi/ragas/json", + "reference_version": "0.1.0", + } + ], + }, + ], + } + + def fetcher(url: str, _timeout: int) -> FetchedSource: + version = "1.1.0" if "langgraph" in url else "0.1.0" + payload = { + "info": {"version": version}, + "releases": { + version: [{"upload_time_iso_8601": "2026-06-25T01:02:03Z"}] + }, + } + return FetchedSource(status="ok", http_status=200, body=json.dumps(payload).encode()) + + report = run_ai_technology_watch( + registry, + registry_path="docs/ai/ai-technology-watch-sources.v1.json", + mode="live", + fetcher=fetcher, + generated_at="2026-06-25T00:00:00+00:00", + ) + + assert report["schema_version"] == "ai_technology_watch_report_v1" + assert report["summary"]["technology_count"] == 2 + assert report["summary"]["technology_area_count"] == 2 + assert report["summary"]["changed_technologies"] == 1 + assert report["summary"]["review_queue_count"] == 1 + assert report["summary"]["high_priority_count"] == 2 + assert report["technology_area_counts"]["agent_frameworks"] == 1 + assert report["technology_area_counts"]["evaluation_and_observability"] == 1 + assert report["policy"]["read_only"] is True + assert report["policy"]["sdk_installation_approved"] is False + assert report["policy"]["paid_api_calls_approved"] is False + assert report["policy"]["production_routing_approved"] is False + assert report["policy"]["telegram_send_approved"] is False + assert report["review_queue"][0]["technology_id"] == "langgraph_runtime" + + +def test_ai_technology_watch_reuses_previous_report_for_change_detection(): + registry = { + "schema_version": "ai_technology_watch_sources_v1", + "updated_at": "2026-06-25", + "policy": {"read_only": True}, + "candidates": [ + { + "candidate_id": "mcp_sdk", + "display_name": "MCP SDK", + "technology_area": "mcp_and_a2a", + "evaluation_priority": "p0", + "sources": [ + { + "source_id": "mcp_npm", + "type": "npm", + "url": "https://registry.npmjs.org/%40modelcontextprotocol%2Fsdk", + } + ], + } + ], + } + + def fetcher(_url: str, _timeout: int) -> FetchedSource: + payload = {"dist-tags": {"latest": "1.2.3"}, "time": {"1.2.3": "2026-06-25"}} + return FetchedSource(status="ok", http_status=200, body=json.dumps(payload).encode()) + + first_report = run_ai_technology_watch( + registry, + registry_path="registry.json", + mode="live", + fetcher=fetcher, + generated_at="2026-06-25T00:00:00+00:00", + ) + second_report = run_ai_technology_watch( + registry, + registry_path="registry.json", + mode="live", + previous_report=first_report, + fetcher=fetcher, + generated_at="2026-06-25T01:00:00+00:00", + ) + + assert first_report["summary"]["changed_technologies"] == 0 + assert second_report["summary"]["changed_technologies"] == 0 + assert second_report["technologies"][0]["sources"][0]["version"] == "1.2.3" diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 477b33bf..20f8b3e6 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,32 @@ +## 2026-06-25|AI 技術雷達近即時監控與產品讀回 + +**背景**:使用者要求 AWOOOI 不能只評估 AI Agent,整個產品與網站必須持續、即時監控市場上所有主流 AI 技術,包含新技術、新版本、新整合與新應用,並能滾動更新到專案評估流程中。 + +**完成**: +- 新增 `docs/ai/ai-technology-watch-sources.v1.json`,監控範圍擴到 `agent_frameworks`、`model_providers`、`rag_and_vector`、`mcp_and_a2a`、`evaluation_and_observability`、`model_serving` 六大領域。 +- 新增 `apps/api/src/services/ai_technology_watch.py` 與 `scripts/agents/ai-technology-watch.py`,沿用 AI Agent market watch 的只讀 primary-source fetch / change detection / review queue 邏輯。 +- `agent_market_watch.py` 新增 `github_tags` source type,支援 pgvector 這類沒有 GitHub Release 但有 tags 的主流專案,避免誤報來源失敗。 +- 新增 `docs/evaluations/ai_technology_watch_report_2026-06-25.json`:本輪 live 讀取 `20` 個技術項、`47` 個 primary sources、`6` 個技術領域,`source_failure_count=0`、`changed_technologies=0`、高優先級項目 `14`。 +- 新增 `scripts/dev/ai-technology-radar-readback.py`、`docs/operations/ai-technology-radar-readback.snapshot.json`、`docs/operations/AI-TECHNOLOGY-RADAR-READBACK-2026-06-25.md` 與 `docs/operations/AI-TECHNOLOGY-RADAR-READBACK.md`。 +- 新增 read-only API:`GET /api/v1/agents/ai-technology-radar-readback`,前端可讀 AI 技術雷達、Agent 專業分工、滾動更新節奏、審核佇列與 blocked gates。 +- 新增 `.gitea/workflows/ai-technology-watch.yaml`,每 6 小時執行只讀 AI 技術監控並寫入 Gitea step summary;workflow 不 commit、不發 Telegram、不安裝 SDK、不呼叫 LLM API、不切 provider route、不改 production。 +- 新增 schema 與測試:`ai_technology_watch_report_v1`、`ai_technology_radar_readback_v1`、watch service tests、readback loader tests、FastAPI endpoint tests。 + +**目前真相**: +- AI 技術雷達來源成功率:`100%`。 +- AI 技術監控項目:`20`。 +- primary sources:`47`。 +- 技術領域:`6`。 +- 需要審核變更:`0`。 +- 來源失敗:`0`。 +- OpenClaw 替換批准:`false`。 +- SDK 安裝、付費 API、production routing、Telegram send、model provider switch、host write:全部仍為 `false`。 +- OpenClaw / Hermes / NemoTron / MarketRadar / Critic-Reviewer 的專業分工已在 readback 中可讀;NemoTron 仍定位為 replay evaluator / smoke gate,不得直接進 production routing。 + +**邊界**: +- 本輪沒有安裝新 SDK、沒有呼叫付費 AI API、沒有發 Telegram、沒有修改 AI provider route、沒有主機寫入、沒有 Nginx / K8s / secret / runtime config 變更、沒有替換 OpenClaw。 +- `.gitea/workflows/ai-technology-watch.yaml` 只啟用 read-only source monitoring;任何後續自動整合、套件升級、模型切換、Telegram live delivery 或 production change 仍需獨立 owner gate。 + ## 2026-06-25|Wazuh release / route readback 狀態收斂與 agent registry 未完成邊界 **背景**:Wazuh 用戶端消失事故的關鍵狀態已從「IwoooS production route 仍 404」前進到「route 已部署但 Wazuh live metadata / agent registry 尚未驗收」。本輪同步前台、release gate、live metadata env gate、handoff 與 LOGBOOK,避免 operator 或平行工作視窗把 route 200、agent transport、service active 或 UI 可見誤判成所有主機已納回 Wazuh。 diff --git a/docs/ai/ai-technology-watch-sources.v1.json b/docs/ai/ai-technology-watch-sources.v1.json new file mode 100644 index 00000000..16ffb1c8 --- /dev/null +++ b/docs/ai/ai-technology-watch-sources.v1.json @@ -0,0 +1,563 @@ +{ + "schema_version": "ai_technology_watch_sources_v1", + "updated_at": "2026-06-25", + "cadence": { + "near_real_time_watch": "每 6 小時執行一次只讀 primary-source 檢查,偵測主流 AI 技術版本、文件與 release 變更。", + "daily_triage": "每日彙整變更技術,依商業適用性、依賴風險、成本風險與資安風險分組。", + "weekly_scorecard": "每週刷新技術 scorecard,判斷是否值得進入 sandbox、offline replay 或 adapter design。", + "monthly_strategy_review": "每月策略檢討,決定技術應納入 roadmap、維持 watch-only 或從監控清單移除。" + }, + "policy": { + "read_only": true, + "raw_chat_history_synced": false, + "sdk_installation_approved": false, + "paid_api_calls_approved": false, + "production_routing_approved": false, + "workflow_modification_approved": false, + "telegram_send_approved": false, + "model_provider_switch_approved": false, + "host_write_approved": false + }, + "coverage_contract": { + "scope": [ + "agent_frameworks", + "model_providers", + "rag_and_vector", + "mcp_and_a2a", + "evaluation_and_observability", + "coding_agents", + "multimodal_generation" + ], + "must_not_do": [ + "安裝新 SDK", + "呼叫付費模型 API", + "修改 provider routing", + "修改 production prompts", + "發送 Telegram 指令", + "寫入主機", + "在缺少 replay / shadow / canary 證據時替換 OpenClaw" + ] + }, + "candidates": [ + { + "candidate_id": "openai_agents_sdk", + "display_name": "OpenAI Agents SDK", + "technology_area": "agent_frameworks", + "integration_surface": "agent_handoff_tracing_guardrails", + "awoooi_role": "協調者、handoff、tool tracing、guardrail 候選", + "evaluation_priority": "p0", + "requires_cost_approval": true, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "source_id": "openai_agents_python_pypi", + "type": "pypi", + "url": "https://pypi.org/pypi/openai-agents/json", + "reference_version": "0.17.7" + }, + { + "source_id": "openai_agents_typescript_npm", + "type": "npm", + "url": "https://registry.npmjs.org/%40openai%2Fagents", + "reference_version": "0.12.0" + }, + { + "source_id": "openai_agents_docs", + "type": "docs", + "url": "https://developers.openai.com/api/docs/guides/agents" + } + ] + }, + { + "candidate_id": "nvidia_nemotron_nemo", + "display_name": "NVIDIA Nemotron + NeMo Agent Toolkit", + "technology_area": "agent_frameworks", + "integration_surface": "offline_replay_evaluator_smoke_gate", + "awoooi_role": "NemoTron replay / evaluator / synthetic data gate", + "evaluation_priority": "p0", + "requires_cost_approval": true, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "source_id": "nvidia_nemotron_developer_page", + "type": "docs", + "url": "https://developer.nvidia.com/topics/ai/nemotron" + }, + { + "source_id": "nvidia_nemo_agent_toolkit_docs", + "type": "docs", + "url": "https://docs.nvidia.com/nemo/agent-toolkit/latest/index.html" + } + ] + }, + { + "candidate_id": "langgraph_runtime", + "display_name": "LangGraph", + "technology_area": "agent_frameworks", + "integration_surface": "durable_workflow_human_in_loop", + "awoooi_role": "事件處理與可恢復 workflow kernel 候選", + "evaluation_priority": "p0", + "requires_cost_approval": false, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "source_id": "langgraph_pypi", + "type": "pypi", + "url": "https://pypi.org/pypi/langgraph/json", + "reference_version": "1.2.6" + }, + { + "source_id": "langgraph_github_release", + "type": "github_release", + "url": "https://api.github.com/repos/langchain-ai/langgraph/releases/latest", + "reference_version": "1.2.6" + }, + { + "source_id": "langgraph_docs", + "type": "docs", + "url": "https://docs.langchain.com/oss/python/langgraph/overview" + } + ] + }, + { + "candidate_id": "google_adk_stack", + "display_name": "Google Agent Development Kit", + "technology_area": "agent_frameworks", + "integration_surface": "gemini_enterprise_agent_stack", + "awoooi_role": "Gemini/Vertex agent stack watch-only 候選", + "evaluation_priority": "p1", + "requires_cost_approval": true, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "source_id": "google_adk_pypi", + "type": "pypi", + "url": "https://pypi.org/pypi/google-adk/json", + "reference_version": "2.3.0" + }, + { + "source_id": "google_adk_github_release", + "type": "github_release", + "url": "https://api.github.com/repos/google/adk-python/releases/latest", + "reference_version": "v2.3.0" + }, + { + "source_id": "google_adk_docs", + "type": "docs", + "url": "https://docs.cloud.google.com/gemini-enterprise-agent-platform/build/adk" + } + ] + }, + { + "candidate_id": "microsoft_agent_framework", + "display_name": "Microsoft Agent Framework", + "technology_area": "agent_frameworks", + "integration_surface": "enterprise_mcp_a2a_workflow", + "awoooi_role": "MCP/A2A enterprise workflow watch-only 候選", + "evaluation_priority": "p1", + "requires_cost_approval": true, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "source_id": "microsoft_agent_framework_github_release", + "type": "github_release", + "url": "https://api.github.com/repos/microsoft/agent-framework/releases/latest", + "reference_version": "dotnet-1.11.0" + }, + { + "source_id": "microsoft_agent_framework_docs", + "type": "docs", + "url": "https://learn.microsoft.com/en-us/agent-framework/overview/" + } + ] + }, + { + "candidate_id": "crewai_flows", + "display_name": "CrewAI Flows + Crews", + "technology_area": "agent_frameworks", + "integration_surface": "multi_agent_prototype", + "awoooi_role": "快速 prototype / non-production 評估候選", + "evaluation_priority": "p2", + "requires_cost_approval": false, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "source_id": "crewai_pypi", + "type": "pypi", + "url": "https://pypi.org/pypi/crewai/json", + "reference_version": "1.14.7" + }, + { + "source_id": "crewai_docs", + "type": "docs", + "url": "https://docs.crewai.com/" + } + ] + }, + { + "candidate_id": "modelcontextprotocol_sdk", + "display_name": "Model Context Protocol SDK", + "technology_area": "mcp_and_a2a", + "integration_surface": "tool_registry_interoperability", + "awoooi_role": "read-only tool registry / MCP adapter 候選", + "evaluation_priority": "p0", + "requires_cost_approval": false, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "source_id": "mcp_typescript_sdk_npm", + "type": "npm", + "url": "https://registry.npmjs.org/%40modelcontextprotocol%2Fsdk" + }, + { + "source_id": "mcp_typescript_sdk_github_release", + "type": "github_release", + "url": "https://api.github.com/repos/modelcontextprotocol/typescript-sdk/releases/latest" + }, + { + "source_id": "mcp_typescript_sdk_docs", + "type": "docs", + "url": "https://github.com/modelcontextprotocol/typescript-sdk" + } + ] + }, + { + "candidate_id": "a2a_protocol", + "display_name": "Agent2Agent Protocol", + "technology_area": "mcp_and_a2a", + "integration_surface": "agent_to_agent_interop", + "awoooi_role": "跨 Agent 溝通協定 watch-only 候選", + "evaluation_priority": "p1", + "requires_cost_approval": false, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "source_id": "a2a_protocol_github_release", + "type": "github_release", + "url": "https://api.github.com/repos/a2aproject/A2A/releases/latest" + }, + { + "source_id": "a2a_python_github_release", + "type": "github_release", + "url": "https://api.github.com/repos/a2aproject/a2a-python/releases/latest" + }, + { + "source_id": "a2a_protocol_docs", + "type": "docs", + "url": "https://github.com/a2aproject/A2A" + } + ] + }, + { + "candidate_id": "openai_model_platform", + "display_name": "OpenAI Model Platform", + "technology_area": "model_providers", + "integration_surface": "model_capability_cost_routing", + "awoooi_role": "模型能力、成本與 routing scorecard 來源", + "evaluation_priority": "p0", + "requires_cost_approval": true, + "requires_dependency_approval": false, + "requires_security_review": true, + "sources": [ + { + "source_id": "openai_models_docs", + "type": "docs", + "url": "https://platform.openai.com/docs/models" + }, + { + "source_id": "openai_python_pypi", + "type": "pypi", + "url": "https://pypi.org/pypi/openai/json" + }, + { + "source_id": "openai_node_npm", + "type": "npm", + "url": "https://registry.npmjs.org/openai" + } + ] + }, + { + "candidate_id": "anthropic_claude_platform", + "display_name": "Anthropic Claude Platform", + "technology_area": "model_providers", + "integration_surface": "model_capability_cost_routing", + "awoooi_role": "Claude model / coding agent / remediation watch source", + "evaluation_priority": "p0", + "requires_cost_approval": true, + "requires_dependency_approval": false, + "requires_security_review": true, + "sources": [ + { + "source_id": "anthropic_models_docs", + "type": "docs", + "url": "https://docs.anthropic.com/en/docs/about-claude/models/overview" + }, + { + "source_id": "anthropic_sdk_npm", + "type": "npm", + "url": "https://registry.npmjs.org/%40anthropic-ai%2Fsdk" + }, + { + "source_id": "claude_agent_sdk_docs", + "type": "docs", + "url": "https://code.claude.com/docs/en/agent-sdk/overview" + } + ] + }, + { + "candidate_id": "google_gemini_platform", + "display_name": "Google Gemini Platform", + "technology_area": "model_providers", + "integration_surface": "model_capability_cost_routing", + "awoooi_role": "Gemini model capability / cost watch source", + "evaluation_priority": "p1", + "requires_cost_approval": true, + "requires_dependency_approval": false, + "requires_security_review": true, + "sources": [ + { + "source_id": "gemini_models_docs", + "type": "docs", + "url": "https://ai.google.dev/gemini-api/docs/models" + }, + { + "source_id": "google_genai_pypi", + "type": "pypi", + "url": "https://pypi.org/pypi/google-genai/json" + } + ] + }, + { + "candidate_id": "llamaindex_rag", + "display_name": "LlamaIndex", + "technology_area": "rag_and_vector", + "integration_surface": "rag_indexing_connectors", + "awoooi_role": "RAG ingestion / indexing / connector watch source", + "evaluation_priority": "p1", + "requires_cost_approval": false, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "source_id": "llama_index_core_pypi", + "type": "pypi", + "url": "https://pypi.org/pypi/llama-index-core/json" + }, + { + "source_id": "llama_index_docs", + "type": "docs", + "url": "https://developers.llamaindex.ai/python/framework/" + } + ] + }, + { + "candidate_id": "langchain_runtime", + "display_name": "LangChain", + "technology_area": "rag_and_vector", + "integration_surface": "llm_app_runtime_connectors", + "awoooi_role": "LLM app integration connector watch source", + "evaluation_priority": "p2", + "requires_cost_approval": false, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "source_id": "langchain_pypi", + "type": "pypi", + "url": "https://pypi.org/pypi/langchain/json" + }, + { + "source_id": "langchain_docs", + "type": "docs", + "url": "https://docs.langchain.com/" + } + ] + }, + { + "candidate_id": "pgvector_vector_store", + "display_name": "pgvector", + "technology_area": "rag_and_vector", + "integration_surface": "postgres_vector_index", + "awoooi_role": "現有 Postgres/pgvector 能力與版本 freshness 來源", + "evaluation_priority": "p1", + "requires_cost_approval": false, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "source_id": "pgvector_github_tags", + "type": "github_tags", + "url": "https://api.github.com/repos/pgvector/pgvector/tags" + }, + { + "source_id": "pgvector_docs", + "type": "docs", + "url": "https://github.com/pgvector/pgvector" + } + ] + }, + { + "candidate_id": "qdrant_vector_store", + "display_name": "Qdrant", + "technology_area": "rag_and_vector", + "integration_surface": "dedicated_vector_database", + "awoooi_role": "專用 vector DB 候選,只能 sandbox 評估", + "evaluation_priority": "p2", + "requires_cost_approval": false, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "source_id": "qdrant_client_pypi", + "type": "pypi", + "url": "https://pypi.org/pypi/qdrant-client/json" + }, + { + "source_id": "qdrant_github_release", + "type": "github_release", + "url": "https://api.github.com/repos/qdrant/qdrant/releases/latest" + } + ] + }, + { + "candidate_id": "chromadb_vector_store", + "display_name": "ChromaDB", + "technology_area": "rag_and_vector", + "integration_surface": "local_vector_database", + "awoooi_role": "本機 / sandbox vector store 候選", + "evaluation_priority": "p3", + "requires_cost_approval": false, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "source_id": "chromadb_pypi", + "type": "pypi", + "url": "https://pypi.org/pypi/chromadb/json" + }, + { + "source_id": "chromadb_github_release", + "type": "github_release", + "url": "https://api.github.com/repos/chroma-core/chroma/releases/latest" + } + ] + }, + { + "candidate_id": "ragas_eval", + "display_name": "Ragas", + "technology_area": "evaluation_and_observability", + "integration_surface": "rag_eval_metrics", + "awoooi_role": "RAG / LLM app evaluation metrics 候選", + "evaluation_priority": "p1", + "requires_cost_approval": false, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "source_id": "ragas_pypi", + "type": "pypi", + "url": "https://pypi.org/pypi/ragas/json" + }, + { + "source_id": "ragas_docs", + "type": "docs", + "url": "https://docs.ragas.io/en/stable/" + } + ] + }, + { + "candidate_id": "langfuse_observability", + "display_name": "Langfuse", + "technology_area": "evaluation_and_observability", + "integration_surface": "llm_observability_tracing", + "awoooi_role": "LLM trace / prompt / eval observability 候選", + "evaluation_priority": "p1", + "requires_cost_approval": true, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "source_id": "langfuse_pypi", + "type": "pypi", + "url": "https://pypi.org/pypi/langfuse/json" + }, + { + "source_id": "langfuse_docs", + "type": "docs", + "url": "https://langfuse.com/docs" + } + ] + }, + { + "candidate_id": "huggingface_tgi", + "display_name": "Hugging Face Text Generation Inference", + "technology_area": "model_serving", + "integration_surface": "self_hosted_model_serving", + "awoooi_role": "自託管模型 serving 能力與版本 freshness 來源", + "evaluation_priority": "p2", + "requires_cost_approval": true, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "source_id": "tgi_github_release", + "type": "github_release", + "url": "https://api.github.com/repos/huggingface/text-generation-inference/releases/latest" + }, + { + "source_id": "tgi_docs", + "type": "docs", + "url": "https://huggingface.co/docs/text-generation-inference/index" + } + ] + }, + { + "candidate_id": "vllm_serving", + "display_name": "vLLM", + "technology_area": "model_serving", + "integration_surface": "self_hosted_llm_inference", + "awoooi_role": "自託管 LLM inference 候選,需 GPU/成本/安全 gate", + "evaluation_priority": "p2", + "requires_cost_approval": true, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "source_id": "vllm_pypi", + "type": "pypi", + "url": "https://pypi.org/pypi/vllm/json" + }, + { + "source_id": "vllm_github_release", + "type": "github_release", + "url": "https://api.github.com/repos/vllm-project/vllm/releases/latest" + } + ] + } + ], + "discovery_sources": [ + { + "source_id": "github_ai_agent_discovery", + "type": "github_search", + "url": "https://api.github.com/search/repositories?q=topic:ai-agent+stars:%3E5000&sort=updated&order=desc" + }, + { + "source_id": "github_mcp_discovery", + "type": "github_search", + "url": "https://api.github.com/search/repositories?q=topic:mcp+stars:%3E1000&sort=updated&order=desc" + }, + { + "source_id": "github_rag_discovery", + "type": "github_search", + "url": "https://api.github.com/search/repositories?q=topic:rag+stars:%3E3000&sort=updated&order=desc" + } + ] +} diff --git a/docs/evaluations/ai_technology_watch_report_2026-06-25.json b/docs/evaluations/ai_technology_watch_report_2026-06-25.json new file mode 100644 index 00000000..7745325f --- /dev/null +++ b/docs/evaluations/ai_technology_watch_report_2026-06-25.json @@ -0,0 +1,1142 @@ +{ + "cadence": { + "daily_triage": "每日彙整變更技術,依商業適用性、依賴風險、成本風險與資安風險分組。", + "monthly_strategy_review": "每月策略檢討,決定技術應納入 roadmap、維持 watch-only 或從監控清單移除。", + "near_real_time_watch": "每 6 小時執行一次只讀 primary-source 檢查,偵測主流 AI 技術版本、文件與 release 變更。", + "weekly_scorecard": "每週刷新技術 scorecard,判斷是否值得進入 sandbox、offline replay 或 adapter design。" + }, + "failures": [], + "generated_at": "2026-06-25T03:56:43.486761+00:00", + "mode": "live", + "new_technology_discovery": [ + { + "error": null, + "http_status": 200, + "items": [ + { + "full_name": "esengine/DeepSeek-Reasonix", + "html_url": "https://github.com/esengine/DeepSeek-Reasonix", + "stargazers_count": 24465, + "updated_at": "2026-06-25T03:56:39Z" + }, + { + "full_name": "zhayujie/CowAgent", + "html_url": "https://github.com/zhayujie/CowAgent", + "stargazers_count": 45598, + "updated_at": "2026-06-25T03:54:00Z" + }, + { + "full_name": "EKKOLearnAI/hermes-studio", + "html_url": "https://github.com/EKKOLearnAI/hermes-studio", + "stargazers_count": 8439, + "updated_at": "2026-06-25T03:53:22Z" + }, + { + "full_name": "NousResearch/hermes-agent", + "html_url": "https://github.com/NousResearch/hermes-agent", + "stargazers_count": 202213, + "updated_at": "2026-06-25T03:51:37Z" + }, + { + "full_name": "can1357/oh-my-pi", + "html_url": "https://github.com/can1357/oh-my-pi", + "stargazers_count": 14522, + "updated_at": "2026-06-25T03:54:23Z" + } + ], + "source_id": "github_ai_agent_discovery", + "status": "ok", + "type": "github_search", + "url": "https://api.github.com/search/repositories?q=topic:ai-agent+stars:%3E5000&sort=updated&order=desc" + }, + { + "error": null, + "http_status": 200, + "items": [ + { + "full_name": "lobehub/lobehub", + "html_url": "https://github.com/lobehub/lobehub", + "stargazers_count": 79055, + "updated_at": "2026-06-25T03:56:02Z" + }, + { + "full_name": "dyoshikawa/rulesync", + "html_url": "https://github.com/dyoshikawa/rulesync", + "stargazers_count": 1190, + "updated_at": "2026-06-25T03:54:56Z" + }, + { + "full_name": "zhayujie/CowAgent", + "html_url": "https://github.com/zhayujie/CowAgent", + "stargazers_count": 45598, + "updated_at": "2026-06-25T03:54:00Z" + }, + { + "full_name": "heygen-com/hyperframes", + "html_url": "https://github.com/heygen-com/hyperframes", + "stargazers_count": 31088, + "updated_at": "2026-06-25T03:52:56Z" + }, + { + "full_name": "can1357/oh-my-pi", + "html_url": "https://github.com/can1357/oh-my-pi", + "stargazers_count": 14522, + "updated_at": "2026-06-25T03:54:23Z" + } + ], + "source_id": "github_mcp_discovery", + "status": "ok", + "type": "github_search", + "url": "https://api.github.com/search/repositories?q=topic:mcp+stars:%3E1000&sort=updated&order=desc" + }, + { + "error": null, + "http_status": 200, + "items": [ + { + "full_name": "langgenius/dify", + "html_url": "https://github.com/langgenius/dify", + "stargazers_count": 146489, + "updated_at": "2026-06-25T03:49:11Z" + }, + { + "full_name": "volcengine/OpenViking", + "html_url": "https://github.com/volcengine/OpenViking", + "stargazers_count": 26015, + "updated_at": "2026-06-25T03:47:36Z" + }, + { + "full_name": "headroomlabs-ai/headroom", + "html_url": "https://github.com/headroomlabs-ai/headroom", + "stargazers_count": 50063, + "updated_at": "2026-06-25T03:54:33Z" + }, + { + "full_name": "elizaOS/eliza", + "html_url": "https://github.com/elizaOS/eliza", + "stargazers_count": 18641, + "updated_at": "2026-06-25T03:39:41Z" + }, + { + "full_name": "mem0ai/mem0", + "html_url": "https://github.com/mem0ai/mem0", + "stargazers_count": 59389, + "updated_at": "2026-06-25T03:46:08Z" + } + ], + "source_id": "github_rag_discovery", + "status": "ok", + "type": "github_search", + "url": "https://api.github.com/search/repositories?q=topic:rag+stars:%3E3000&sort=updated&order=desc" + } + ], + "policy": { + "host_write_approved": false, + "model_provider_switch_approved": false, + "paid_api_calls_approved": false, + "production_routing_approved": false, + "raw_chat_history_synced": false, + "read_only": true, + "sdk_installation_approved": false, + "telegram_send_approved": false, + "workflow_modification_approved": false + }, + "registry": { + "path": "docs/ai/ai-technology-watch-sources.v1.json", + "schema_version": "ai_technology_watch_sources_v1", + "updated_at": "2026-06-25" + }, + "review_queue": [], + "schema_version": "ai_technology_watch_report_v1", + "summary": { + "changed_technologies": 0, + "high_priority_count": 14, + "review_queue_count": 0, + "source_count": 47, + "source_failure_count": 0, + "technology_area_count": 6, + "technology_count": 20, + "watch_only_technologies": 20 + }, + "technologies": [ + { + "awoooi_role": "協調者、handoff、tool tracing、guardrail 候選", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "OpenAI Agents SDK", + "evaluation_priority": "p0", + "integration_surface": "agent_handoff_tracing_guardrails", + "recommended_actions": [ + "keep_watch_only_status" + ], + "requires_cost_approval": true, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "changed_since_reference": false, + "content_hash": "f3f2446f64e51e5a0dfa398a", + "error": null, + "http_status": 200, + "published_at": "2026-06-24T05:15:31.741499Z", + "reference_version": "0.17.7", + "source_id": "openai_agents_python_pypi", + "status": "ok", + "type": "pypi", + "url": "https://pypi.org/pypi/openai-agents/json", + "version": "0.17.7" + }, + { + "changed_since_reference": false, + "content_hash": "d13d7176d0b123dc1d6a7b08", + "error": null, + "http_status": 200, + "published_at": "2026-06-24T04:02:12.610Z", + "reference_version": "0.12.0", + "source_id": "openai_agents_typescript_npm", + "status": "ok", + "type": "npm", + "url": "https://registry.npmjs.org/%40openai%2Fagents", + "version": "0.12.0" + }, + { + "changed_since_reference": false, + "content_hash": "043ec42b0cc899a72448614c", + "error": null, + "http_status": 200, + "published_at": null, + "reference_version": null, + "source_id": "openai_agents_docs", + "status": "ok", + "type": "docs", + "url": "https://developers.openai.com/api/docs/guides/agents", + "version": null + } + ], + "technology_area": "agent_frameworks", + "technology_id": "openai_agents_sdk" + }, + { + "awoooi_role": "NemoTron replay / evaluator / synthetic data gate", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "NVIDIA Nemotron + NeMo Agent Toolkit", + "evaluation_priority": "p0", + "integration_surface": "offline_replay_evaluator_smoke_gate", + "recommended_actions": [ + "keep_watch_only_status" + ], + "requires_cost_approval": true, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "changed_since_reference": false, + "content_hash": "06028073c740b559b76a4715", + "error": null, + "http_status": 200, + "published_at": null, + "reference_version": null, + "source_id": "nvidia_nemotron_developer_page", + "status": "ok", + "type": "docs", + "url": "https://developer.nvidia.com/topics/ai/nemotron", + "version": null + }, + { + "changed_since_reference": false, + "content_hash": "da7400a5ae03d8de4dc4ef16", + "error": null, + "http_status": 200, + "published_at": null, + "reference_version": null, + "source_id": "nvidia_nemo_agent_toolkit_docs", + "status": "ok", + "type": "docs", + "url": "https://docs.nvidia.com/nemo/agent-toolkit/latest/index.html", + "version": null + } + ], + "technology_area": "agent_frameworks", + "technology_id": "nvidia_nemotron_nemo" + }, + { + "awoooi_role": "事件處理與可恢復 workflow kernel 候選", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "LangGraph", + "evaluation_priority": "p0", + "integration_surface": "durable_workflow_human_in_loop", + "recommended_actions": [ + "keep_watch_only_status" + ], + "requires_cost_approval": false, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "changed_since_reference": false, + "content_hash": "0700375668bf4a039be45c4c", + "error": null, + "http_status": 200, + "published_at": "2026-06-18T20:58:20.335564Z", + "reference_version": "1.2.6", + "source_id": "langgraph_pypi", + "status": "ok", + "type": "pypi", + "url": "https://pypi.org/pypi/langgraph/json", + "version": "1.2.6" + }, + { + "changed_since_reference": false, + "content_hash": "ceb3d51c1e67fc6e2e9fda21", + "error": null, + "http_status": 200, + "published_at": "2026-06-18T20:58:32Z", + "reference_version": "1.2.6", + "source_id": "langgraph_github_release", + "status": "ok", + "type": "github_release", + "url": "https://api.github.com/repos/langchain-ai/langgraph/releases/latest", + "version": "1.2.6" + }, + { + "changed_since_reference": false, + "content_hash": "c8100f72af1cb84426b57ac3", + "error": null, + "http_status": 200, + "published_at": null, + "reference_version": null, + "source_id": "langgraph_docs", + "status": "ok", + "type": "docs", + "url": "https://docs.langchain.com/oss/python/langgraph/overview", + "version": null + } + ], + "technology_area": "agent_frameworks", + "technology_id": "langgraph_runtime" + }, + { + "awoooi_role": "Gemini/Vertex agent stack watch-only 候選", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "Google Agent Development Kit", + "evaluation_priority": "p1", + "integration_surface": "gemini_enterprise_agent_stack", + "recommended_actions": [ + "keep_watch_only_status" + ], + "requires_cost_approval": true, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "changed_since_reference": false, + "content_hash": "e2d0102cb37d90e01d9e4fc3", + "error": null, + "http_status": 200, + "published_at": "2026-06-18T18:47:06.323661Z", + "reference_version": "2.3.0", + "source_id": "google_adk_pypi", + "status": "ok", + "type": "pypi", + "url": "https://pypi.org/pypi/google-adk/json", + "version": "2.3.0" + }, + { + "changed_since_reference": false, + "content_hash": "88aec475a8cfd83f8e67e35b", + "error": null, + "http_status": 200, + "published_at": "2026-06-18T18:45:04Z", + "reference_version": "v2.3.0", + "source_id": "google_adk_github_release", + "status": "ok", + "type": "github_release", + "url": "https://api.github.com/repos/google/adk-python/releases/latest", + "version": "v2.3.0" + }, + { + "changed_since_reference": false, + "content_hash": "f8ebe9e670bf59fdb44d7133", + "error": null, + "http_status": 200, + "published_at": null, + "reference_version": null, + "source_id": "google_adk_docs", + "status": "ok", + "type": "docs", + "url": "https://docs.cloud.google.com/gemini-enterprise-agent-platform/build/adk", + "version": null + } + ], + "technology_area": "agent_frameworks", + "technology_id": "google_adk_stack" + }, + { + "awoooi_role": "MCP/A2A enterprise workflow watch-only 候選", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "Microsoft Agent Framework", + "evaluation_priority": "p1", + "integration_surface": "enterprise_mcp_a2a_workflow", + "recommended_actions": [ + "keep_watch_only_status" + ], + "requires_cost_approval": true, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "changed_since_reference": false, + "content_hash": "268e86906524bb431c109f4d", + "error": null, + "http_status": 200, + "published_at": "2026-06-23T21:18:26Z", + "reference_version": "dotnet-1.11.0", + "source_id": "microsoft_agent_framework_github_release", + "status": "ok", + "type": "github_release", + "url": "https://api.github.com/repos/microsoft/agent-framework/releases/latest", + "version": "dotnet-1.11.0" + }, + { + "changed_since_reference": false, + "content_hash": "97e807de8517641d1c3d1a77", + "error": null, + "http_status": 200, + "published_at": null, + "reference_version": null, + "source_id": "microsoft_agent_framework_docs", + "status": "ok", + "type": "docs", + "url": "https://learn.microsoft.com/en-us/agent-framework/overview/", + "version": null + } + ], + "technology_area": "agent_frameworks", + "technology_id": "microsoft_agent_framework" + }, + { + "awoooi_role": "快速 prototype / non-production 評估候選", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "CrewAI Flows + Crews", + "evaluation_priority": "p2", + "integration_surface": "multi_agent_prototype", + "recommended_actions": [ + "keep_watch_only_status" + ], + "requires_cost_approval": false, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "changed_since_reference": false, + "content_hash": "b3955563d45132bdd2471889", + "error": null, + "http_status": 200, + "published_at": "2026-06-11T17:14:39.912300Z", + "reference_version": "1.14.7", + "source_id": "crewai_pypi", + "status": "ok", + "type": "pypi", + "url": "https://pypi.org/pypi/crewai/json", + "version": "1.14.7" + }, + { + "changed_since_reference": false, + "content_hash": "cf3b3465165c450510e0fd61", + "error": null, + "http_status": 200, + "published_at": null, + "reference_version": null, + "source_id": "crewai_docs", + "status": "ok", + "type": "docs", + "url": "https://docs.crewai.com/", + "version": null + } + ], + "technology_area": "agent_frameworks", + "technology_id": "crewai_flows" + }, + { + "awoooi_role": "read-only tool registry / MCP adapter 候選", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "Model Context Protocol SDK", + "evaluation_priority": "p0", + "integration_surface": "tool_registry_interoperability", + "recommended_actions": [ + "keep_watch_only_status" + ], + "requires_cost_approval": false, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "changed_since_reference": false, + "content_hash": "ca0fb3725519e6517209f0f0", + "error": null, + "http_status": 200, + "published_at": "2026-03-30T16:50:42.718Z", + "reference_version": null, + "source_id": "mcp_typescript_sdk_npm", + "status": "ok", + "type": "npm", + "url": "https://registry.npmjs.org/%40modelcontextprotocol%2Fsdk", + "version": "1.29.0" + }, + { + "changed_since_reference": false, + "content_hash": "910d982e612e4ea69f6dddb6", + "error": null, + "http_status": 200, + "published_at": "2026-03-30T16:49:30Z", + "reference_version": null, + "source_id": "mcp_typescript_sdk_github_release", + "status": "ok", + "type": "github_release", + "url": "https://api.github.com/repos/modelcontextprotocol/typescript-sdk/releases/latest", + "version": "v1.29.0" + }, + { + "changed_since_reference": false, + "content_hash": "9b938636c879b0d2628843f9", + "error": null, + "http_status": 200, + "published_at": null, + "reference_version": null, + "source_id": "mcp_typescript_sdk_docs", + "status": "ok", + "type": "docs", + "url": "https://github.com/modelcontextprotocol/typescript-sdk", + "version": null + } + ], + "technology_area": "mcp_and_a2a", + "technology_id": "modelcontextprotocol_sdk" + }, + { + "awoooi_role": "跨 Agent 溝通協定 watch-only 候選", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "Agent2Agent Protocol", + "evaluation_priority": "p1", + "integration_surface": "agent_to_agent_interop", + "recommended_actions": [ + "keep_watch_only_status" + ], + "requires_cost_approval": false, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "changed_since_reference": false, + "content_hash": "fe0f870c8c568c6597f38079", + "error": null, + "http_status": 200, + "published_at": "2026-05-28T11:34:36Z", + "reference_version": null, + "source_id": "a2a_protocol_github_release", + "status": "ok", + "type": "github_release", + "url": "https://api.github.com/repos/a2aproject/A2A/releases/latest", + "version": "v1.0.1" + }, + { + "changed_since_reference": false, + "content_hash": "39cd1b8a6283966240e0a778", + "error": null, + "http_status": 200, + "published_at": "2026-05-29T09:34:03Z", + "reference_version": null, + "source_id": "a2a_python_github_release", + "status": "ok", + "type": "github_release", + "url": "https://api.github.com/repos/a2aproject/a2a-python/releases/latest", + "version": "v1.1.0" + }, + { + "changed_since_reference": false, + "content_hash": "e0bc99f2e81d9faa595f3dc2", + "error": null, + "http_status": 200, + "published_at": null, + "reference_version": null, + "source_id": "a2a_protocol_docs", + "status": "ok", + "type": "docs", + "url": "https://github.com/a2aproject/A2A", + "version": null + } + ], + "technology_area": "mcp_and_a2a", + "technology_id": "a2a_protocol" + }, + { + "awoooi_role": "模型能力、成本與 routing scorecard 來源", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "OpenAI Model Platform", + "evaluation_priority": "p0", + "integration_surface": "model_capability_cost_routing", + "recommended_actions": [ + "keep_watch_only_status" + ], + "requires_cost_approval": true, + "requires_dependency_approval": false, + "requires_security_review": true, + "sources": [ + { + "changed_since_reference": false, + "content_hash": "a0e7044dcc1a3c9bdde9b3c4", + "error": null, + "http_status": 200, + "published_at": null, + "reference_version": null, + "source_id": "openai_models_docs", + "status": "ok", + "type": "docs", + "url": "https://platform.openai.com/docs/models", + "version": null + }, + { + "changed_since_reference": false, + "content_hash": "3a739b3b7cff0338db14af8d", + "error": null, + "http_status": 200, + "published_at": "2026-06-24T20:55:58.882276Z", + "reference_version": null, + "source_id": "openai_python_pypi", + "status": "ok", + "type": "pypi", + "url": "https://pypi.org/pypi/openai/json", + "version": "2.44.0" + }, + { + "changed_since_reference": false, + "content_hash": "de4f7bc0f17aebae70e92acc", + "error": null, + "http_status": 200, + "published_at": "2026-06-24T20:36:37.856Z", + "reference_version": null, + "source_id": "openai_node_npm", + "status": "ok", + "type": "npm", + "url": "https://registry.npmjs.org/openai", + "version": "6.45.0" + } + ], + "technology_area": "model_providers", + "technology_id": "openai_model_platform" + }, + { + "awoooi_role": "Claude model / coding agent / remediation watch source", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "Anthropic Claude Platform", + "evaluation_priority": "p0", + "integration_surface": "model_capability_cost_routing", + "recommended_actions": [ + "keep_watch_only_status" + ], + "requires_cost_approval": true, + "requires_dependency_approval": false, + "requires_security_review": true, + "sources": [ + { + "changed_since_reference": false, + "content_hash": "a2ead84a42eee92c3b36c5ee", + "error": null, + "http_status": 200, + "published_at": null, + "reference_version": null, + "source_id": "anthropic_models_docs", + "status": "ok", + "type": "docs", + "url": "https://docs.anthropic.com/en/docs/about-claude/models/overview", + "version": null + }, + { + "changed_since_reference": false, + "content_hash": "22069549b95a4e6e9b3efcd2", + "error": null, + "http_status": 200, + "published_at": "2026-06-24T18:50:15.985Z", + "reference_version": null, + "source_id": "anthropic_sdk_npm", + "status": "ok", + "type": "npm", + "url": "https://registry.npmjs.org/%40anthropic-ai%2Fsdk", + "version": "0.106.0" + }, + { + "changed_since_reference": false, + "content_hash": "5622132c0dc32c13c0f62568", + "error": null, + "http_status": 200, + "published_at": null, + "reference_version": null, + "source_id": "claude_agent_sdk_docs", + "status": "ok", + "type": "docs", + "url": "https://code.claude.com/docs/en/agent-sdk/overview", + "version": null + } + ], + "technology_area": "model_providers", + "technology_id": "anthropic_claude_platform" + }, + { + "awoooi_role": "Gemini model capability / cost watch source", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "Google Gemini Platform", + "evaluation_priority": "p1", + "integration_surface": "model_capability_cost_routing", + "recommended_actions": [ + "keep_watch_only_status" + ], + "requires_cost_approval": true, + "requires_dependency_approval": false, + "requires_security_review": true, + "sources": [ + { + "changed_since_reference": false, + "content_hash": "60db43f0591d1789c13ba419", + "error": null, + "http_status": 200, + "published_at": null, + "reference_version": null, + "source_id": "gemini_models_docs", + "status": "ok", + "type": "docs", + "url": "https://ai.google.dev/gemini-api/docs/models", + "version": null + }, + { + "changed_since_reference": false, + "content_hash": "2e5896a3f7ebb9c5e5d6b168", + "error": null, + "http_status": 200, + "published_at": "2026-06-24T01:33:16.296219Z", + "reference_version": null, + "source_id": "google_genai_pypi", + "status": "ok", + "type": "pypi", + "url": "https://pypi.org/pypi/google-genai/json", + "version": "2.10.0" + } + ], + "technology_area": "model_providers", + "technology_id": "google_gemini_platform" + }, + { + "awoooi_role": "RAG ingestion / indexing / connector watch source", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "LlamaIndex", + "evaluation_priority": "p1", + "integration_surface": "rag_indexing_connectors", + "recommended_actions": [ + "keep_watch_only_status" + ], + "requires_cost_approval": false, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "changed_since_reference": false, + "content_hash": "d23e23bd7fd9440b5b11f8b1", + "error": null, + "http_status": 200, + "published_at": "2026-06-24T19:35:52.833783Z", + "reference_version": null, + "source_id": "llama_index_core_pypi", + "status": "ok", + "type": "pypi", + "url": "https://pypi.org/pypi/llama-index-core/json", + "version": "0.14.23" + }, + { + "changed_since_reference": false, + "content_hash": "07a4311e0c56bef5eff90470", + "error": null, + "http_status": 200, + "published_at": null, + "reference_version": null, + "source_id": "llama_index_docs", + "status": "ok", + "type": "docs", + "url": "https://developers.llamaindex.ai/python/framework/", + "version": null + } + ], + "technology_area": "rag_and_vector", + "technology_id": "llamaindex_rag" + }, + { + "awoooi_role": "LLM app integration connector watch source", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "LangChain", + "evaluation_priority": "p2", + "integration_surface": "llm_app_runtime_connectors", + "recommended_actions": [ + "keep_watch_only_status" + ], + "requires_cost_approval": false, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "changed_since_reference": false, + "content_hash": "a48c3b206d034c7b0b90786e", + "error": null, + "http_status": 200, + "published_at": "2026-06-22T23:00:31.619946Z", + "reference_version": null, + "source_id": "langchain_pypi", + "status": "ok", + "type": "pypi", + "url": "https://pypi.org/pypi/langchain/json", + "version": "1.3.11" + }, + { + "changed_since_reference": false, + "content_hash": "57dbf4a9e69eafc6d490c181", + "error": null, + "http_status": 200, + "published_at": null, + "reference_version": null, + "source_id": "langchain_docs", + "status": "ok", + "type": "docs", + "url": "https://docs.langchain.com/", + "version": null + } + ], + "technology_area": "rag_and_vector", + "technology_id": "langchain_runtime" + }, + { + "awoooi_role": "現有 Postgres/pgvector 能力與版本 freshness 來源", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "pgvector", + "evaluation_priority": "p1", + "integration_surface": "postgres_vector_index", + "recommended_actions": [ + "keep_watch_only_status" + ], + "requires_cost_approval": false, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "changed_since_reference": false, + "content_hash": "36549870c2aa94c481c5f376", + "error": null, + "http_status": 200, + "published_at": null, + "reference_version": null, + "source_id": "pgvector_github_tags", + "status": "ok", + "type": "github_tags", + "url": "https://api.github.com/repos/pgvector/pgvector/tags", + "version": "v0.8.3" + }, + { + "changed_since_reference": false, + "content_hash": "61b9903cc26a5fc7111cbc99", + "error": null, + "http_status": 200, + "published_at": null, + "reference_version": null, + "source_id": "pgvector_docs", + "status": "ok", + "type": "docs", + "url": "https://github.com/pgvector/pgvector", + "version": null + } + ], + "technology_area": "rag_and_vector", + "technology_id": "pgvector_vector_store" + }, + { + "awoooi_role": "專用 vector DB 候選,只能 sandbox 評估", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "Qdrant", + "evaluation_priority": "p2", + "integration_surface": "dedicated_vector_database", + "recommended_actions": [ + "keep_watch_only_status" + ], + "requires_cost_approval": false, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "changed_since_reference": false, + "content_hash": "fbb71fdce3f7457bd10eb978", + "error": null, + "http_status": 200, + "published_at": "2026-05-11T14:12:36.998394Z", + "reference_version": null, + "source_id": "qdrant_client_pypi", + "status": "ok", + "type": "pypi", + "url": "https://pypi.org/pypi/qdrant-client/json", + "version": "1.18.0" + }, + { + "changed_since_reference": false, + "content_hash": "0f0682ba8a040a64057c03c1", + "error": null, + "http_status": 200, + "published_at": "2026-06-04T06:50:36Z", + "reference_version": null, + "source_id": "qdrant_github_release", + "status": "ok", + "type": "github_release", + "url": "https://api.github.com/repos/qdrant/qdrant/releases/latest", + "version": "v1.18.2" + } + ], + "technology_area": "rag_and_vector", + "technology_id": "qdrant_vector_store" + }, + { + "awoooi_role": "本機 / sandbox vector store 候選", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "ChromaDB", + "evaluation_priority": "p3", + "integration_surface": "local_vector_database", + "recommended_actions": [ + "keep_watch_only_status" + ], + "requires_cost_approval": false, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "changed_since_reference": false, + "content_hash": "06e0ac7ae5aa6f22125c0dcb", + "error": null, + "http_status": 200, + "published_at": "2026-05-05T05:54:48.906852Z", + "reference_version": null, + "source_id": "chromadb_pypi", + "status": "ok", + "type": "pypi", + "url": "https://pypi.org/pypi/chromadb/json", + "version": "1.5.9" + }, + { + "changed_since_reference": false, + "content_hash": "dbd2cd85ad1f7c822a080e46", + "error": null, + "http_status": 200, + "published_at": "2026-05-05T05:55:40Z", + "reference_version": null, + "source_id": "chromadb_github_release", + "status": "ok", + "type": "github_release", + "url": "https://api.github.com/repos/chroma-core/chroma/releases/latest", + "version": "1.5.9" + } + ], + "technology_area": "rag_and_vector", + "technology_id": "chromadb_vector_store" + }, + { + "awoooi_role": "RAG / LLM app evaluation metrics 候選", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "Ragas", + "evaluation_priority": "p1", + "integration_surface": "rag_eval_metrics", + "recommended_actions": [ + "keep_watch_only_status" + ], + "requires_cost_approval": false, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "changed_since_reference": false, + "content_hash": "8c0a0fea2e6b423ae05b5d40", + "error": null, + "http_status": 200, + "published_at": "2026-01-13T17:47:59.200116Z", + "reference_version": null, + "source_id": "ragas_pypi", + "status": "ok", + "type": "pypi", + "url": "https://pypi.org/pypi/ragas/json", + "version": "0.4.3" + }, + { + "changed_since_reference": false, + "content_hash": "adc0098fc150daf81a599005", + "error": null, + "http_status": 200, + "published_at": null, + "reference_version": null, + "source_id": "ragas_docs", + "status": "ok", + "type": "docs", + "url": "https://docs.ragas.io/en/stable/", + "version": null + } + ], + "technology_area": "evaluation_and_observability", + "technology_id": "ragas_eval" + }, + { + "awoooi_role": "LLM trace / prompt / eval observability 候選", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "Langfuse", + "evaluation_priority": "p1", + "integration_surface": "llm_observability_tracing", + "recommended_actions": [ + "keep_watch_only_status" + ], + "requires_cost_approval": true, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "changed_since_reference": false, + "content_hash": "9ab6655947d61e70a876c353", + "error": null, + "http_status": 200, + "published_at": "2026-06-24T07:58:15.529940Z", + "reference_version": null, + "source_id": "langfuse_pypi", + "status": "ok", + "type": "pypi", + "url": "https://pypi.org/pypi/langfuse/json", + "version": "4.11.0" + }, + { + "changed_since_reference": false, + "content_hash": "d90e1c8e226fa4ce2b45c22b", + "error": null, + "http_status": 200, + "published_at": null, + "reference_version": null, + "source_id": "langfuse_docs", + "status": "ok", + "type": "docs", + "url": "https://langfuse.com/docs", + "version": null + } + ], + "technology_area": "evaluation_and_observability", + "technology_id": "langfuse_observability" + }, + { + "awoooi_role": "自託管模型 serving 能力與版本 freshness 來源", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "Hugging Face Text Generation Inference", + "evaluation_priority": "p2", + "integration_surface": "self_hosted_model_serving", + "recommended_actions": [ + "keep_watch_only_status" + ], + "requires_cost_approval": true, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "changed_since_reference": false, + "content_hash": "d6ea0096e24f7e441961e35a", + "error": null, + "http_status": 200, + "published_at": "2025-12-19T14:35:25Z", + "reference_version": null, + "source_id": "tgi_github_release", + "status": "ok", + "type": "github_release", + "url": "https://api.github.com/repos/huggingface/text-generation-inference/releases/latest", + "version": "v3.3.7" + }, + { + "changed_since_reference": false, + "content_hash": "c42a7871f12bcff3648aba61", + "error": null, + "http_status": 200, + "published_at": null, + "reference_version": null, + "source_id": "tgi_docs", + "status": "ok", + "type": "docs", + "url": "https://huggingface.co/docs/text-generation-inference/index", + "version": null + } + ], + "technology_area": "model_serving", + "technology_id": "huggingface_tgi" + }, + { + "awoooi_role": "自託管 LLM inference 候選,需 GPU/成本/安全 gate", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "vLLM", + "evaluation_priority": "p2", + "integration_surface": "self_hosted_llm_inference", + "recommended_actions": [ + "keep_watch_only_status" + ], + "requires_cost_approval": true, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "changed_since_reference": false, + "content_hash": "df159e475abea07f118301ee", + "error": null, + "http_status": 200, + "published_at": "2026-06-15T05:11:49.394364Z", + "reference_version": null, + "source_id": "vllm_pypi", + "status": "ok", + "type": "pypi", + "url": "https://pypi.org/pypi/vllm/json", + "version": "0.23.0" + }, + { + "changed_since_reference": false, + "content_hash": "1fee977f40c7bef04a3bb2f9", + "error": null, + "http_status": 200, + "published_at": "2026-06-15T05:27:20Z", + "reference_version": null, + "source_id": "vllm_github_release", + "status": "ok", + "type": "github_release", + "url": "https://api.github.com/repos/vllm-project/vllm/releases/latest", + "version": "v0.23.0" + } + ], + "technology_area": "model_serving", + "technology_id": "vllm_serving" + } + ], + "technology_area_counts": { + "agent_frameworks": 6, + "evaluation_and_observability": 2, + "mcp_and_a2a": 2, + "model_providers": 3, + "model_serving": 2, + "rag_and_vector": 5 + } +} diff --git a/docs/operations/AI-TECHNOLOGY-RADAR-READBACK-2026-06-25.md b/docs/operations/AI-TECHNOLOGY-RADAR-READBACK-2026-06-25.md new file mode 100644 index 00000000..f81e6ac7 --- /dev/null +++ b/docs/operations/AI-TECHNOLOGY-RADAR-READBACK-2026-06-25.md @@ -0,0 +1,70 @@ +# AI 技術雷達與滾動更新讀回 + +- 產生時間:`2026-06-25T03:56:51.751955+00:00` +- 整體治理完成度:`42.2%` +- AI 技術雷達來源成功率:`100.0%` +- 監控技術項目:`20` +- 技術領域:`6` +- 官方 / primary sources:`47` +- 來源失敗:`0` +- 需要審核變更:`0` +- 高優先級項目:`14` +- 滾動更新狀態:`near_real_time_watch_ready_integration_gated` + +## 技術領域覆蓋 + +| 技術領域 | 技術數 | 高優先級 | 需要審核 | 代表技術 | +|---|---:|---:|---:|---| +| `agent_frameworks` | `6` | `5` | `0` | OpenAI Agents SDK, NVIDIA Nemotron + NeMo Agent Toolkit, LangGraph, Google Agent Development Kit | +| `evaluation_and_observability` | `2` | `2` | `0` | Ragas, Langfuse | +| `mcp_and_a2a` | `2` | `2` | `0` | Model Context Protocol SDK, Agent2Agent Protocol | +| `model_providers` | `3` | `3` | `0` | OpenAI Model Platform, Anthropic Claude Platform, Google Gemini Platform | +| `model_serving` | `2` | `0` | `0` | Hugging Face Text Generation Inference, vLLM | +| `rag_and_vector` | `5` | `2` | `0` | LlamaIndex, LangChain, pgvector, Qdrant | + +## 高優先級審核佇列 + +| 技術 | 領域 | 優先級 | Gate | 下一步 | +|---|---|---|---|---| + +## Agent 專業分工 + +| Agent | 專業角色 | 自動化範圍 | 需要審核的邊界 | +|---|---|---|---| +| OpenClaw | 生產決策仲裁者、風險分級與最後 policy guard | 維持現有 production baseline、讀取 replay / shadow 評分、拒絕無證據替換 | 任何取代、降級、生產路由切換都必須通過 replay / shadow / canary 與人工批准。 | +| NemoTron | 離線回放評估者、模型能力比較、合約輸出 smoke gate | 只讀 request pack、比對候選輸出、產生 replay scorecard 草稿 | 不得自行呼叫外部 NIM/API、不得讀 labels 作答、不得進生產路由。 | +| Hermes | 知識管理、RAG 整理、報告草稿與長期技能庫維護 | 整理 primary source 摘要、建立 no-send 日週月報、準備人審包 | 不得同步 raw chat history、不得保存 secret、不得直接發 Telegram live report。 | +| MarketRadar | AI 技術市場雷達、版本監控、來源失敗偵測 | 每 6 小時只讀 primary sources、產生 freshness / review queue | 不得自動新增 SDK、不得自動修改 provider route 或 workflow 行為。 | +| Critic / Reviewer | 獨立審核、反例檢查、整合風險評分 | 檢查政策旗標、來源可靠性、成本與資安風險 | 只能輸出 blocked / candidate / owner_review,不得直接執行寫入。 | + +## 滾動更新控制 + +| 節奏 | Agent 可自動做什麼 | 輸出 | Gate | +|---|---|---|---| +| 每 6 小時 | 讀取官方文件、PyPI、npm、GitHub release、primary source hash。 | AI 技術 watch report、來源失敗清單、review queue。 | `read_only_only` | +| 每日 | 依 business applicability、成本、依賴、資安、AWOOOI fit 分類。 | 日報摘要與中低風險自動處理建議。 | `no_send_report_until_delivery_gate` | +| 每週 | 刷新 scorecard,決定 sandbox / replay / adapter design 優先級。 | 週報、優先序、候選整合審查包。 | `scorecard_required_before_replay` | +| 每月 | 彙整趨勢,提出 roadmap / watch-only / retire 建議。 | 月報與策略審核包。 | `human_review_for_strategy_or_production_change` | + +## 優先工作清單 + +| 順序 | 工作 | 優先級 | 自動化模式 | 完成定義 | +|---:|---|---|---|---| +| 1 | AI 技術雷達 primary source 監控產品化 | `P0` | `agent_auto_read_only` | API、snapshot、Markdown、schema、測試與 production readback 都能顯示技術領域、來源與 Gate。 | +| 2 | 近即時版本 / release / docs 變更偵測 | `P0` | `agent_auto_schedule_read_only` | 每 6 小時可跑 watch;失敗來源會進日報,不會自動整合。 | +| 3 | OpenClaw / Hermes / NemoTron / MarketRadar 專業分工與成長紀錄 | `P0` | `agent_auto_read_model_human_review_for_write` | 每個 Agent 的角色、輸出、學習寫回與限制都能被前端讀回。 | +| 4 | AI 技術 scorecard 與 sandbox / replay 優先級 | `P1` | `agent_propose_owner_review` | 高優先級變更先進 scorecard,再進 no-cost/no-write sandbox 或 replay 計畫。 | +| 5 | Telegram Bot 報告與高風險審核橋接 | `P1` | `blocked_until_telegram_send_gate` | 低中風險只告警回報;高風險需 owner approval 後才可發送或執行。 | +| 6 | 新 AI 技術探索與 watchlist 擴充 | `P2` | `agent_auto_discover_human_classify` | GitHub topic / package registry / 官方 blog 可提出候選,但加入正式 watchlist 前需審核。 | + +## 仍被 Gate 擋下 + +- `sdk_installation_approved=false` +- `paid_api_calls_approved=false` +- `production_routing_approved=false` +- `telegram_send_approved=false` +- `model_provider_switch_approved=false` +- `host_write_approved=false` +- `openclaw_replacement_approved=false` +- `replay_shadow_canary_gate_required=true` +- `cost_and_data_boundary_review_required=true` diff --git a/docs/operations/AI-TECHNOLOGY-RADAR-READBACK.md b/docs/operations/AI-TECHNOLOGY-RADAR-READBACK.md new file mode 100644 index 00000000..f81e6ac7 --- /dev/null +++ b/docs/operations/AI-TECHNOLOGY-RADAR-READBACK.md @@ -0,0 +1,70 @@ +# AI 技術雷達與滾動更新讀回 + +- 產生時間:`2026-06-25T03:56:51.751955+00:00` +- 整體治理完成度:`42.2%` +- AI 技術雷達來源成功率:`100.0%` +- 監控技術項目:`20` +- 技術領域:`6` +- 官方 / primary sources:`47` +- 來源失敗:`0` +- 需要審核變更:`0` +- 高優先級項目:`14` +- 滾動更新狀態:`near_real_time_watch_ready_integration_gated` + +## 技術領域覆蓋 + +| 技術領域 | 技術數 | 高優先級 | 需要審核 | 代表技術 | +|---|---:|---:|---:|---| +| `agent_frameworks` | `6` | `5` | `0` | OpenAI Agents SDK, NVIDIA Nemotron + NeMo Agent Toolkit, LangGraph, Google Agent Development Kit | +| `evaluation_and_observability` | `2` | `2` | `0` | Ragas, Langfuse | +| `mcp_and_a2a` | `2` | `2` | `0` | Model Context Protocol SDK, Agent2Agent Protocol | +| `model_providers` | `3` | `3` | `0` | OpenAI Model Platform, Anthropic Claude Platform, Google Gemini Platform | +| `model_serving` | `2` | `0` | `0` | Hugging Face Text Generation Inference, vLLM | +| `rag_and_vector` | `5` | `2` | `0` | LlamaIndex, LangChain, pgvector, Qdrant | + +## 高優先級審核佇列 + +| 技術 | 領域 | 優先級 | Gate | 下一步 | +|---|---|---|---|---| + +## Agent 專業分工 + +| Agent | 專業角色 | 自動化範圍 | 需要審核的邊界 | +|---|---|---|---| +| OpenClaw | 生產決策仲裁者、風險分級與最後 policy guard | 維持現有 production baseline、讀取 replay / shadow 評分、拒絕無證據替換 | 任何取代、降級、生產路由切換都必須通過 replay / shadow / canary 與人工批准。 | +| NemoTron | 離線回放評估者、模型能力比較、合約輸出 smoke gate | 只讀 request pack、比對候選輸出、產生 replay scorecard 草稿 | 不得自行呼叫外部 NIM/API、不得讀 labels 作答、不得進生產路由。 | +| Hermes | 知識管理、RAG 整理、報告草稿與長期技能庫維護 | 整理 primary source 摘要、建立 no-send 日週月報、準備人審包 | 不得同步 raw chat history、不得保存 secret、不得直接發 Telegram live report。 | +| MarketRadar | AI 技術市場雷達、版本監控、來源失敗偵測 | 每 6 小時只讀 primary sources、產生 freshness / review queue | 不得自動新增 SDK、不得自動修改 provider route 或 workflow 行為。 | +| Critic / Reviewer | 獨立審核、反例檢查、整合風險評分 | 檢查政策旗標、來源可靠性、成本與資安風險 | 只能輸出 blocked / candidate / owner_review,不得直接執行寫入。 | + +## 滾動更新控制 + +| 節奏 | Agent 可自動做什麼 | 輸出 | Gate | +|---|---|---|---| +| 每 6 小時 | 讀取官方文件、PyPI、npm、GitHub release、primary source hash。 | AI 技術 watch report、來源失敗清單、review queue。 | `read_only_only` | +| 每日 | 依 business applicability、成本、依賴、資安、AWOOOI fit 分類。 | 日報摘要與中低風險自動處理建議。 | `no_send_report_until_delivery_gate` | +| 每週 | 刷新 scorecard,決定 sandbox / replay / adapter design 優先級。 | 週報、優先序、候選整合審查包。 | `scorecard_required_before_replay` | +| 每月 | 彙整趨勢,提出 roadmap / watch-only / retire 建議。 | 月報與策略審核包。 | `human_review_for_strategy_or_production_change` | + +## 優先工作清單 + +| 順序 | 工作 | 優先級 | 自動化模式 | 完成定義 | +|---:|---|---|---|---| +| 1 | AI 技術雷達 primary source 監控產品化 | `P0` | `agent_auto_read_only` | API、snapshot、Markdown、schema、測試與 production readback 都能顯示技術領域、來源與 Gate。 | +| 2 | 近即時版本 / release / docs 變更偵測 | `P0` | `agent_auto_schedule_read_only` | 每 6 小時可跑 watch;失敗來源會進日報,不會自動整合。 | +| 3 | OpenClaw / Hermes / NemoTron / MarketRadar 專業分工與成長紀錄 | `P0` | `agent_auto_read_model_human_review_for_write` | 每個 Agent 的角色、輸出、學習寫回與限制都能被前端讀回。 | +| 4 | AI 技術 scorecard 與 sandbox / replay 優先級 | `P1` | `agent_propose_owner_review` | 高優先級變更先進 scorecard,再進 no-cost/no-write sandbox 或 replay 計畫。 | +| 5 | Telegram Bot 報告與高風險審核橋接 | `P1` | `blocked_until_telegram_send_gate` | 低中風險只告警回報;高風險需 owner approval 後才可發送或執行。 | +| 6 | 新 AI 技術探索與 watchlist 擴充 | `P2` | `agent_auto_discover_human_classify` | GitHub topic / package registry / 官方 blog 可提出候選,但加入正式 watchlist 前需審核。 | + +## 仍被 Gate 擋下 + +- `sdk_installation_approved=false` +- `paid_api_calls_approved=false` +- `production_routing_approved=false` +- `telegram_send_approved=false` +- `model_provider_switch_approved=false` +- `host_write_approved=false` +- `openclaw_replacement_approved=false` +- `replay_shadow_canary_gate_required=true` +- `cost_and_data_boundary_review_required=true` diff --git a/docs/operations/ai-technology-radar-readback.snapshot.json b/docs/operations/ai-technology-radar-readback.snapshot.json new file mode 100644 index 00000000..78ea7bda --- /dev/null +++ b/docs/operations/ai-technology-radar-readback.snapshot.json @@ -0,0 +1,496 @@ +{ + "blocked_gates": [ + "sdk_installation_approved=false", + "paid_api_calls_approved=false", + "production_routing_approved=false", + "telegram_send_approved=false", + "model_provider_switch_approved=false", + "host_write_approved=false", + "openclaw_replacement_approved=false", + "replay_shadow_canary_gate_required=true", + "cost_and_data_boundary_review_required=true" + ], + "generated_at": "2026-06-25T03:56:51.751955+00:00", + "high_priority_review_queue": [], + "integration_candidates": [ + { + "awoooi_role": "協調者、handoff、tool tracing、guardrail 候選", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "OpenAI Agents SDK", + "integration_surface": "agent_handoff_tracing_guardrails", + "recommended_actions": [ + "keep_watch_only_status" + ], + "technology_area": "agent_frameworks", + "technology_id": "openai_agents_sdk" + }, + { + "awoooi_role": "NemoTron replay / evaluator / synthetic data gate", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "NVIDIA Nemotron + NeMo Agent Toolkit", + "integration_surface": "offline_replay_evaluator_smoke_gate", + "recommended_actions": [ + "keep_watch_only_status" + ], + "technology_area": "agent_frameworks", + "technology_id": "nvidia_nemotron_nemo" + }, + { + "awoooi_role": "事件處理與可恢復 workflow kernel 候選", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "LangGraph", + "integration_surface": "durable_workflow_human_in_loop", + "recommended_actions": [ + "keep_watch_only_status" + ], + "technology_area": "agent_frameworks", + "technology_id": "langgraph_runtime" + }, + { + "awoooi_role": "Gemini/Vertex agent stack watch-only 候選", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "Google Agent Development Kit", + "integration_surface": "gemini_enterprise_agent_stack", + "recommended_actions": [ + "keep_watch_only_status" + ], + "technology_area": "agent_frameworks", + "technology_id": "google_adk_stack" + }, + { + "awoooi_role": "MCP/A2A enterprise workflow watch-only 候選", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "Microsoft Agent Framework", + "integration_surface": "enterprise_mcp_a2a_workflow", + "recommended_actions": [ + "keep_watch_only_status" + ], + "technology_area": "agent_frameworks", + "technology_id": "microsoft_agent_framework" + }, + { + "awoooi_role": "快速 prototype / non-production 評估候選", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "CrewAI Flows + Crews", + "integration_surface": "multi_agent_prototype", + "recommended_actions": [ + "keep_watch_only_status" + ], + "technology_area": "agent_frameworks", + "technology_id": "crewai_flows" + }, + { + "awoooi_role": "read-only tool registry / MCP adapter 候選", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "Model Context Protocol SDK", + "integration_surface": "tool_registry_interoperability", + "recommended_actions": [ + "keep_watch_only_status" + ], + "technology_area": "mcp_and_a2a", + "technology_id": "modelcontextprotocol_sdk" + }, + { + "awoooi_role": "跨 Agent 溝通協定 watch-only 候選", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "Agent2Agent Protocol", + "integration_surface": "agent_to_agent_interop", + "recommended_actions": [ + "keep_watch_only_status" + ], + "technology_area": "mcp_and_a2a", + "technology_id": "a2a_protocol" + }, + { + "awoooi_role": "模型能力、成本與 routing scorecard 來源", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "OpenAI Model Platform", + "integration_surface": "model_capability_cost_routing", + "recommended_actions": [ + "keep_watch_only_status" + ], + "technology_area": "model_providers", + "technology_id": "openai_model_platform" + }, + { + "awoooi_role": "Claude model / coding agent / remediation watch source", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "Anthropic Claude Platform", + "integration_surface": "model_capability_cost_routing", + "recommended_actions": [ + "keep_watch_only_status" + ], + "technology_area": "model_providers", + "technology_id": "anthropic_claude_platform" + }, + { + "awoooi_role": "Gemini model capability / cost watch source", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "Google Gemini Platform", + "integration_surface": "model_capability_cost_routing", + "recommended_actions": [ + "keep_watch_only_status" + ], + "technology_area": "model_providers", + "technology_id": "google_gemini_platform" + }, + { + "awoooi_role": "RAG ingestion / indexing / connector watch source", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "LlamaIndex", + "integration_surface": "rag_indexing_connectors", + "recommended_actions": [ + "keep_watch_only_status" + ], + "technology_area": "rag_and_vector", + "technology_id": "llamaindex_rag" + }, + { + "awoooi_role": "LLM app integration connector watch source", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "LangChain", + "integration_surface": "llm_app_runtime_connectors", + "recommended_actions": [ + "keep_watch_only_status" + ], + "technology_area": "rag_and_vector", + "technology_id": "langchain_runtime" + }, + { + "awoooi_role": "現有 Postgres/pgvector 能力與版本 freshness 來源", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "pgvector", + "integration_surface": "postgres_vector_index", + "recommended_actions": [ + "keep_watch_only_status" + ], + "technology_area": "rag_and_vector", + "technology_id": "pgvector_vector_store" + }, + { + "awoooi_role": "專用 vector DB 候選,只能 sandbox 評估", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "Qdrant", + "integration_surface": "dedicated_vector_database", + "recommended_actions": [ + "keep_watch_only_status" + ], + "technology_area": "rag_and_vector", + "technology_id": "qdrant_vector_store" + }, + { + "awoooi_role": "本機 / sandbox vector store 候選", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "ChromaDB", + "integration_surface": "local_vector_database", + "recommended_actions": [ + "keep_watch_only_status" + ], + "technology_area": "rag_and_vector", + "technology_id": "chromadb_vector_store" + }, + { + "awoooi_role": "RAG / LLM app evaluation metrics 候選", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "Ragas", + "integration_surface": "rag_eval_metrics", + "recommended_actions": [ + "keep_watch_only_status" + ], + "technology_area": "evaluation_and_observability", + "technology_id": "ragas_eval" + }, + { + "awoooi_role": "LLM trace / prompt / eval observability 候選", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "Langfuse", + "integration_surface": "llm_observability_tracing", + "recommended_actions": [ + "keep_watch_only_status" + ], + "technology_area": "evaluation_and_observability", + "technology_id": "langfuse_observability" + }, + { + "awoooi_role": "自託管模型 serving 能力與版本 freshness 來源", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "Hugging Face Text Generation Inference", + "integration_surface": "self_hosted_model_serving", + "recommended_actions": [ + "keep_watch_only_status" + ], + "technology_area": "model_serving", + "technology_id": "huggingface_tgi" + }, + { + "awoooi_role": "自託管 LLM inference 候選,需 GPU/成本/安全 gate", + "changed": false, + "decision": "watch_only_no_change", + "display_name": "vLLM", + "integration_surface": "self_hosted_llm_inference", + "recommended_actions": [ + "keep_watch_only_status" + ], + "technology_area": "model_serving", + "technology_id": "vllm_serving" + } + ], + "policy": { + "host_write_approved": false, + "model_provider_switch_approved": false, + "openclaw_replacement_approved": false, + "paid_api_calls_approved": false, + "production_routing_approved": false, + "raw_chat_history_synced": false, + "read_only": true, + "sdk_installation_approved": false, + "telegram_send_approved": false + }, + "priority_workplan": [ + { + "automation_mode": "agent_auto_read_only", + "done_definition": "API、snapshot、Markdown、schema、測試與 production readback 都能顯示技術領域、來源與 Gate。", + "order": 1, + "priority": "P0", + "work_item": "AI 技術雷達 primary source 監控產品化" + }, + { + "automation_mode": "agent_auto_schedule_read_only", + "done_definition": "每 6 小時可跑 watch;失敗來源會進日報,不會自動整合。", + "order": 2, + "priority": "P0", + "work_item": "近即時版本 / release / docs 變更偵測" + }, + { + "automation_mode": "agent_auto_read_model_human_review_for_write", + "done_definition": "每個 Agent 的角色、輸出、學習寫回與限制都能被前端讀回。", + "order": 3, + "priority": "P0", + "work_item": "OpenClaw / Hermes / NemoTron / MarketRadar 專業分工與成長紀錄" + }, + { + "automation_mode": "agent_propose_owner_review", + "done_definition": "高優先級變更先進 scorecard,再進 no-cost/no-write sandbox 或 replay 計畫。", + "order": 4, + "priority": "P1", + "work_item": "AI 技術 scorecard 與 sandbox / replay 優先級" + }, + { + "automation_mode": "blocked_until_telegram_send_gate", + "done_definition": "低中風險只告警回報;高風險需 owner approval 後才可發送或執行。", + "order": 5, + "priority": "P1", + "work_item": "Telegram Bot 報告與高風險審核橋接" + }, + { + "automation_mode": "agent_auto_discover_human_classify", + "done_definition": "GitHub topic / package registry / 官方 blog 可提出候選,但加入正式 watchlist 前需審核。", + "order": 6, + "priority": "P2", + "work_item": "新 AI 技術探索與 watchlist 擴充" + } + ], + "professional_agent_roles": [ + { + "agent": "OpenClaw", + "auto_scope": "維持現有 production baseline、讀取 replay / shadow 評分、拒絕無證據替換", + "professional_role": "生產決策仲裁者、風險分級與最後 policy guard", + "review_boundary": "任何取代、降級、生產路由切換都必須通過 replay / shadow / canary 與人工批准。" + }, + { + "agent": "NemoTron", + "auto_scope": "只讀 request pack、比對候選輸出、產生 replay scorecard 草稿", + "professional_role": "離線回放評估者、模型能力比較、合約輸出 smoke gate", + "review_boundary": "不得自行呼叫外部 NIM/API、不得讀 labels 作答、不得進生產路由。" + }, + { + "agent": "Hermes", + "auto_scope": "整理 primary source 摘要、建立 no-send 日週月報、準備人審包", + "professional_role": "知識管理、RAG 整理、報告草稿與長期技能庫維護", + "review_boundary": "不得同步 raw chat history、不得保存 secret、不得直接發 Telegram live report。" + }, + { + "agent": "MarketRadar", + "auto_scope": "每 6 小時只讀 primary sources、產生 freshness / review queue", + "professional_role": "AI 技術市場雷達、版本監控、來源失敗偵測", + "review_boundary": "不得自動新增 SDK、不得自動修改 provider route 或 workflow 行為。" + }, + { + "agent": "Critic / Reviewer", + "auto_scope": "檢查政策旗標、來源可靠性、成本與資安風險", + "professional_role": "獨立審核、反例檢查、整合風險評分", + "review_boundary": "只能輸出 blocked / candidate / owner_review,不得直接執行寫入。" + } + ], + "report_contract": { + "agent_auto_allowed_for": [ + "官方來源只讀監控", + "版本與文件 hash 比對", + "審核佇列分類", + "繁中 no-send 報告草稿", + "離線 replay fixture 準備", + "低風險文件與讀回 snapshot 更新提案" + ], + "api_endpoint": "/api/v1/agents/ai-technology-radar-readback", + "daily": "每日彙整變更、來源失敗、審核佇列與可自動處理項目。", + "frontend_target": "/zh-TW/governance?tab=agent-market", + "human_review_required_for": [ + "新 SDK / package / MCP server 安裝", + "付費 API 或 token 上限變更", + "模型 provider / 生產路由切換", + "Telegram Bot 即時發送或審批按鈕策略變更", + "主機、K8s、workflow、Nginx、secret 或資料層寫入", + "OpenClaw 生產決策核心替換、拆分或降級" + ], + "monthly": "每月進行策略 review,決定納入 roadmap、維持 watch-only 或移出監控。", + "near_real_time": "每 6 小時讀取 primary sources,偵測主流 AI 技術版本、文件與 release 變更。", + "schedule_cron_utc": "0 */6 * * *", + "schedule_enabled": true, + "schedule_workflow": ".gitea/workflows/ai-technology-watch.yaml", + "weekly": "每週做技術 scorecard,決定 sandbox / replay / adapter design 優先級。" + }, + "rolling_update_controls": [ + { + "agent_auto_action": "讀取官方文件、PyPI、npm、GitHub release、primary source hash。", + "cadence": "每 6 小時", + "cadence_source": "每 6 小時執行一次只讀 primary-source 檢查,偵測主流 AI 技術版本、文件與 release 變更。", + "gate": "read_only_only", + "output": "AI 技術 watch report、來源失敗清單、review queue。" + }, + { + "agent_auto_action": "依 business applicability、成本、依賴、資安、AWOOOI fit 分類。", + "cadence": "每日", + "cadence_source": "每日彙整變更技術,依商業適用性、依賴風險、成本風險與資安風險分組。", + "gate": "no_send_report_until_delivery_gate", + "output": "日報摘要與中低風險自動處理建議。" + }, + { + "agent_auto_action": "刷新 scorecard,決定 sandbox / replay / adapter design 優先級。", + "cadence": "每週", + "cadence_source": "每週刷新技術 scorecard,判斷是否值得進入 sandbox、offline replay 或 adapter design。", + "gate": "scorecard_required_before_replay", + "output": "週報、優先序、候選整合審查包。" + }, + { + "agent_auto_action": "彙整趨勢,提出 roadmap / watch-only / retire 建議。", + "cadence": "每月", + "cadence_source": "每月策略檢討,決定技術應納入 roadmap、維持 watch-only 或從監控清單移除。", + "gate": "human_review_for_strategy_or_production_change", + "output": "月報與策略審核包。" + } + ], + "schema_version": "ai_technology_radar_readback_v1", + "source_scope": { + "agent_market_radar_readback": "docs/operations/ai-agent-market-radar-readback.snapshot.json", + "gitea_main_evidence_basis_commit": "683428bd", + "scope_note": "本讀回只整合已提交的只讀來源監控、AI Agent 市場雷達與治理 gate;不包含 raw chat history、secret、session 或本機工作視窗內容。", + "technology_source_registry": "docs/ai/ai-technology-watch-sources.v1.json", + "technology_watch_report": "docs/evaluations/ai_technology_watch_report_2026-06-25.json" + }, + "summary": { + "ai_technology_radar_completion_percent": 100.0, + "changed_technologies": 0, + "high_priority_count": 14, + "overall_completion_percent": 42.2, + "review_queue_count": 0, + "rolling_update_status": "near_real_time_watch_ready_integration_gated", + "source_count": 47, + "source_failures": 0, + "technology_area_count": 6, + "technology_count": 20 + }, + "technology_area_counts": { + "agent_frameworks": 6, + "evaluation_and_observability": 2, + "mcp_and_a2a": 2, + "model_providers": 3, + "model_serving": 2, + "rag_and_vector": 5 + }, + "technology_domains": [ + { + "changed_count": 0, + "high_priority_count": 5, + "representative_technologies": [ + "OpenAI Agents SDK", + "NVIDIA Nemotron + NeMo Agent Toolkit", + "LangGraph", + "Google Agent Development Kit" + ], + "technology_area": "agent_frameworks", + "technology_count": 6 + }, + { + "changed_count": 0, + "high_priority_count": 2, + "representative_technologies": [ + "Ragas", + "Langfuse" + ], + "technology_area": "evaluation_and_observability", + "technology_count": 2 + }, + { + "changed_count": 0, + "high_priority_count": 2, + "representative_technologies": [ + "Model Context Protocol SDK", + "Agent2Agent Protocol" + ], + "technology_area": "mcp_and_a2a", + "technology_count": 2 + }, + { + "changed_count": 0, + "high_priority_count": 3, + "representative_technologies": [ + "OpenAI Model Platform", + "Anthropic Claude Platform", + "Google Gemini Platform" + ], + "technology_area": "model_providers", + "technology_count": 3 + }, + { + "changed_count": 0, + "high_priority_count": 0, + "representative_technologies": [ + "Hugging Face Text Generation Inference", + "vLLM" + ], + "technology_area": "model_serving", + "technology_count": 2 + }, + { + "changed_count": 0, + "high_priority_count": 2, + "representative_technologies": [ + "LlamaIndex", + "LangChain", + "pgvector", + "Qdrant" + ], + "technology_area": "rag_and_vector", + "technology_count": 5 + } + ] +} diff --git a/docs/schemas/ai_technology_radar_readback_v1.schema.json b/docs/schemas/ai_technology_radar_readback_v1.schema.json new file mode 100644 index 00000000..77e6f570 --- /dev/null +++ b/docs/schemas/ai_technology_radar_readback_v1.schema.json @@ -0,0 +1,127 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "urn:awoooi:ai-technology-radar-readback-v1", + "title": "AWOOOI AI 技術雷達產品讀回快照 (v1)", + "type": "object", + "required": [ + "schema_version", + "generated_at", + "source_scope", + "summary", + "policy", + "technology_area_counts", + "technology_domains", + "high_priority_review_queue", + "professional_agent_roles", + "rolling_update_controls", + "integration_candidates", + "priority_workplan", + "blocked_gates", + "report_contract" + ], + "properties": { + "schema_version": { + "type": "string", + "const": "ai_technology_radar_readback_v1" + }, + "generated_at": { + "type": "string", + "minLength": 1 + }, + "source_scope": { + "type": "object", + "additionalProperties": true + }, + "summary": { + "type": "object", + "required": [ + "overall_completion_percent", + "ai_technology_radar_completion_percent", + "technology_count", + "technology_area_count", + "source_count", + "changed_technologies", + "review_queue_count", + "source_failures", + "high_priority_count", + "rolling_update_status" + ], + "properties": { + "overall_completion_percent": {"type": "number"}, + "ai_technology_radar_completion_percent": {"type": "number"}, + "technology_count": {"type": "integer", "minimum": 0}, + "technology_area_count": {"type": "integer", "minimum": 0}, + "source_count": {"type": "integer", "minimum": 0}, + "changed_technologies": {"type": "integer", "minimum": 0}, + "review_queue_count": {"type": "integer", "minimum": 0}, + "source_failures": {"type": "integer", "minimum": 0}, + "high_priority_count": {"type": "integer", "minimum": 0}, + "rolling_update_status": {"type": "string", "minLength": 1} + }, + "additionalProperties": true + }, + "policy": { + "type": "object", + "required": [ + "read_only", + "raw_chat_history_synced", + "sdk_installation_approved", + "paid_api_calls_approved", + "production_routing_approved", + "telegram_send_approved", + "model_provider_switch_approved", + "host_write_approved", + "openclaw_replacement_approved" + ], + "properties": { + "read_only": {"type": "boolean", "const": true}, + "raw_chat_history_synced": {"type": "boolean", "const": false}, + "sdk_installation_approved": {"type": "boolean", "const": false}, + "paid_api_calls_approved": {"type": "boolean", "const": false}, + "production_routing_approved": {"type": "boolean", "const": false}, + "telegram_send_approved": {"type": "boolean", "const": false}, + "model_provider_switch_approved": {"type": "boolean", "const": false}, + "host_write_approved": {"type": "boolean", "const": false}, + "openclaw_replacement_approved": {"type": "boolean", "const": false} + }, + "additionalProperties": true + }, + "technology_area_counts": { + "type": "object", + "additionalProperties": {"type": "integer", "minimum": 0} + }, + "technology_domains": { + "type": "array", + "items": {"type": "object", "additionalProperties": true} + }, + "high_priority_review_queue": { + "type": "array", + "items": {"type": "object", "additionalProperties": true} + }, + "professional_agent_roles": { + "type": "array", + "items": {"type": "object", "additionalProperties": true} + }, + "rolling_update_controls": { + "type": "array", + "items": {"type": "object", "additionalProperties": true} + }, + "integration_candidates": { + "type": "array", + "items": {"type": "object", "additionalProperties": true} + }, + "priority_workplan": { + "type": "array", + "items": {"type": "object", "additionalProperties": true} + }, + "blocked_gates": { + "type": "array", + "items": {"type": "string", "minLength": 1} + }, + "report_contract": { + "type": "object", + "additionalProperties": true + } + }, + "additionalProperties": false +} diff --git a/docs/schemas/ai_technology_watch_report_v1.schema.json b/docs/schemas/ai_technology_watch_report_v1.schema.json new file mode 100644 index 00000000..990befa4 --- /dev/null +++ b/docs/schemas/ai_technology_watch_report_v1.schema.json @@ -0,0 +1,157 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "urn:awoooi:ai-technology-watch-report-v1", + "title": "AWOOOI AI 技術雷達來源監控報告 (v1)", + "type": "object", + "required": [ + "schema_version", + "generated_at", + "mode", + "registry", + "cadence", + "policy", + "summary", + "technology_area_counts", + "technologies", + "review_queue", + "new_technology_discovery", + "failures" + ], + "properties": { + "schema_version": { + "type": "string", + "const": "ai_technology_watch_report_v1" + }, + "generated_at": { + "type": "string", + "minLength": 1 + }, + "mode": { + "type": "string", + "enum": ["offline", "live"] + }, + "registry": { + "type": "object", + "required": ["path", "schema_version", "updated_at"], + "properties": { + "path": {"type": "string"}, + "schema_version": {"type": "string"}, + "updated_at": {"type": "string"} + }, + "additionalProperties": true + }, + "cadence": { + "type": "object", + "additionalProperties": true + }, + "policy": { + "type": "object", + "required": [ + "read_only", + "sdk_installation_approved", + "paid_api_calls_approved", + "production_routing_approved", + "telegram_send_approved", + "model_provider_switch_approved", + "host_write_approved" + ], + "properties": { + "read_only": {"type": "boolean", "const": true}, + "sdk_installation_approved": {"type": "boolean", "const": false}, + "paid_api_calls_approved": {"type": "boolean", "const": false}, + "production_routing_approved": {"type": "boolean", "const": false}, + "telegram_send_approved": {"type": "boolean", "const": false}, + "model_provider_switch_approved": {"type": "boolean", "const": false}, + "host_write_approved": {"type": "boolean", "const": false} + }, + "additionalProperties": true + }, + "summary": { + "type": "object", + "required": [ + "technology_count", + "technology_area_count", + "source_count", + "changed_technologies", + "watch_only_technologies", + "review_queue_count", + "source_failure_count", + "high_priority_count" + ], + "properties": { + "technology_count": {"type": "integer", "minimum": 0}, + "technology_area_count": {"type": "integer", "minimum": 0}, + "source_count": {"type": "integer", "minimum": 0}, + "changed_technologies": {"type": "integer", "minimum": 0}, + "watch_only_technologies": {"type": "integer", "minimum": 0}, + "review_queue_count": {"type": "integer", "minimum": 0}, + "source_failure_count": {"type": "integer", "minimum": 0}, + "high_priority_count": {"type": "integer", "minimum": 0} + }, + "additionalProperties": true + }, + "technology_area_counts": { + "type": "object", + "additionalProperties": {"type": "integer", "minimum": 0} + }, + "technologies": { + "type": "array", + "items": {"$ref": "#/$defs/technology"} + }, + "review_queue": { + "type": "array", + "items": {"type": "object", "additionalProperties": true} + }, + "new_technology_discovery": { + "type": "array", + "items": {"type": "object", "additionalProperties": true} + }, + "failures": { + "type": "array", + "items": {"type": "string"} + } + }, + "$defs": { + "technology": { + "type": "object", + "required": [ + "technology_id", + "display_name", + "technology_area", + "integration_surface", + "awoooi_role", + "evaluation_priority", + "requires_cost_approval", + "requires_dependency_approval", + "requires_security_review", + "sources", + "changed", + "decision", + "recommended_actions" + ], + "properties": { + "technology_id": {"type": "string", "minLength": 1}, + "display_name": {"type": "string", "minLength": 1}, + "technology_area": {"type": "string", "minLength": 1}, + "integration_surface": {"type": "string"}, + "awoooi_role": {"type": "string"}, + "evaluation_priority": {"type": "string"}, + "requires_cost_approval": {"type": "boolean"}, + "requires_dependency_approval": {"type": "boolean"}, + "requires_security_review": {"type": "boolean"}, + "sources": { + "type": "array", + "items": {"type": "object", "additionalProperties": true} + }, + "changed": {"type": "boolean"}, + "decision": {"type": "string"}, + "recommended_actions": { + "type": "array", + "items": {"type": "string"} + } + }, + "additionalProperties": true + } + }, + "additionalProperties": false +} diff --git a/scripts/agents/ai-technology-watch.py b/scripts/agents/ai-technology-watch.py new file mode 100644 index 00000000..d05a19db --- /dev/null +++ b/scripts/agents/ai-technology-watch.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +"""Run the AWOOOI read-only AI technology watch.""" + +from __future__ import annotations + +import argparse +import importlib.util +import json +import sys +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[2] +API_ROOT = ROOT / "apps" / "api" +SERVICE_PATH = ROOT / "apps" / "api" / "src" / "services" / "ai_technology_watch.py" +run_ai_technology_watch = None + + +def main() -> int: + global run_ai_technology_watch + if run_ai_technology_watch is None: + run_ai_technology_watch = _load_service() + + parser = argparse.ArgumentParser(description="Run AWOOOI AI technology watch.") + parser.add_argument( + "--registry", + default="docs/ai/ai-technology-watch-sources.v1.json", + help="AI technology watch registry JSON", + ) + parser.add_argument("--output", required=True, help="report output JSON") + parser.add_argument( + "--mode", + choices=("offline", "live"), + default="live", + help="offline validates registry only; live fetches primary sources", + ) + parser.add_argument("--previous-report") + parser.add_argument("--timeout-seconds", type=int, default=12) + args = parser.parse_args() + + registry = _read_json(Path(args.registry)) + previous = _read_json(Path(args.previous_report)) if args.previous_report else None + report = run_ai_technology_watch( + registry, + registry_path=args.registry, + mode=args.mode, + previous_report=previous, + timeout_seconds=args.timeout_seconds, + ) + Path(args.output).write_text( + json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True) + "\n", + encoding="utf-8", + ) + print(json.dumps(report["summary"], ensure_ascii=False, sort_keys=True)) + return 0 + + +def _read_json(path: Path) -> dict[str, Any]: + with path.open(encoding="utf-8") as handle: + payload = json.load(handle) + if not isinstance(payload, dict): + raise SystemExit(f"{path}: expected JSON object") + return payload + + +def _load_service() -> Any: + api_root = str(API_ROOT) + if api_root not in sys.path: + sys.path.insert(0, api_root) + module_name = "awoooi_ai_technology_watch_service" + spec = importlib.util.spec_from_file_location(module_name, SERVICE_PATH) + if spec is None or spec.loader is None: + raise SystemExit(f"cannot load AI technology watch service from {SERVICE_PATH}") + module = importlib.util.module_from_spec(spec) + sys.modules[module_name] = module + spec.loader.exec_module(module) + return module.run_ai_technology_watch + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/dev/ai-technology-radar-readback.py b/scripts/dev/ai-technology-radar-readback.py new file mode 100644 index 00000000..d2361b1b --- /dev/null +++ b/scripts/dev/ai-technology-radar-readback.py @@ -0,0 +1,434 @@ +#!/usr/bin/env python3 +"""Build the AWOOOI AI technology radar readback artifact.""" + +from __future__ import annotations + +import argparse +import json +from collections import defaultdict +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + + +def build_radar( + *, + technology_watch: dict[str, Any], + agent_market_radar: dict[str, Any], + evidence_commit: str, + generated_at: str | None = None, +) -> dict[str, Any]: + """Build a read-only AI technology radar readback from committed evidence.""" + _require_schema(technology_watch, "ai_technology_watch_report_v1", "technology_watch") + _require_schema(agent_market_radar, "ai_agent_market_radar_readback_v1", "agent_market_radar") + + watch_summary = technology_watch.get("summary") or {} + agent_summary = agent_market_radar.get("summary") or {} + source_count = int(watch_summary.get("source_count", 0)) + failure_count = int(watch_summary.get("source_failure_count", 0)) + success_rate = _success_rate(source_count, failure_count) + + return { + "schema_version": "ai_technology_radar_readback_v1", + "generated_at": generated_at or datetime.now(timezone.utc).isoformat(), + "source_scope": { + "technology_watch_report": "docs/evaluations/ai_technology_watch_report_2026-06-25.json", + "technology_source_registry": "docs/ai/ai-technology-watch-sources.v1.json", + "agent_market_radar_readback": "docs/operations/ai-agent-market-radar-readback.snapshot.json", + "gitea_main_evidence_basis_commit": evidence_commit, + "scope_note": "本讀回只整合已提交的只讀來源監控、AI Agent 市場雷達與治理 gate;不包含 raw chat history、secret、session 或本機工作視窗內容。", + }, + "summary": { + "overall_completion_percent": float(agent_summary.get("overall_completion_percent", 42.2)), + "ai_technology_radar_completion_percent": success_rate, + "technology_count": int(watch_summary.get("technology_count", 0)), + "technology_area_count": int(watch_summary.get("technology_area_count", 0)), + "source_count": source_count, + "changed_technologies": int(watch_summary.get("changed_technologies", 0)), + "review_queue_count": int(watch_summary.get("review_queue_count", 0)), + "source_failures": failure_count, + "high_priority_count": int(watch_summary.get("high_priority_count", 0)), + "rolling_update_status": "near_real_time_watch_ready_integration_gated", + }, + "policy": { + "read_only": True, + "raw_chat_history_synced": False, + "sdk_installation_approved": False, + "paid_api_calls_approved": False, + "production_routing_approved": False, + "telegram_send_approved": False, + "model_provider_switch_approved": False, + "host_write_approved": False, + "openclaw_replacement_approved": False, + }, + "technology_area_counts": dict(technology_watch.get("technology_area_counts") or {}), + "technology_domains": _technology_domains(technology_watch), + "high_priority_review_queue": _high_priority_review_queue(technology_watch), + "professional_agent_roles": _professional_agent_roles(), + "rolling_update_controls": _rolling_update_controls(technology_watch), + "integration_candidates": _integration_candidates(technology_watch), + "priority_workplan": _priority_workplan(), + "blocked_gates": [ + "sdk_installation_approved=false", + "paid_api_calls_approved=false", + "production_routing_approved=false", + "telegram_send_approved=false", + "model_provider_switch_approved=false", + "host_write_approved=false", + "openclaw_replacement_approved=false", + "replay_shadow_canary_gate_required=true", + "cost_and_data_boundary_review_required=true", + ], + "report_contract": { + "api_endpoint": "/api/v1/agents/ai-technology-radar-readback", + "frontend_target": "/zh-TW/governance?tab=agent-market", + "schedule_workflow": ".gitea/workflows/ai-technology-watch.yaml", + "schedule_enabled": True, + "schedule_cron_utc": "0 */6 * * *", + "near_real_time": "每 6 小時讀取 primary sources,偵測主流 AI 技術版本、文件與 release 變更。", + "daily": "每日彙整變更、來源失敗、審核佇列與可自動處理項目。", + "weekly": "每週做技術 scorecard,決定 sandbox / replay / adapter design 優先級。", + "monthly": "每月進行策略 review,決定納入 roadmap、維持 watch-only 或移出監控。", + "human_review_required_for": [ + "新 SDK / package / MCP server 安裝", + "付費 API 或 token 上限變更", + "模型 provider / 生產路由切換", + "Telegram Bot 即時發送或審批按鈕策略變更", + "主機、K8s、workflow、Nginx、secret 或資料層寫入", + "OpenClaw 生產決策核心替換、拆分或降級", + ], + "agent_auto_allowed_for": [ + "官方來源只讀監控", + "版本與文件 hash 比對", + "審核佇列分類", + "繁中 no-send 報告草稿", + "離線 replay fixture 準備", + "低風險文件與讀回 snapshot 更新提案", + ], + }, + } + + +def render_markdown(payload: dict[str, Any]) -> str: + """Render a Traditional Chinese operator report.""" + summary = payload["summary"] + lines = [ + "# AI 技術雷達與滾動更新讀回", + "", + f"- 產生時間:`{payload['generated_at']}`", + f"- 整體治理完成度:`{summary['overall_completion_percent']}%`", + f"- AI 技術雷達來源成功率:`{summary['ai_technology_radar_completion_percent']}%`", + f"- 監控技術項目:`{summary['technology_count']}`", + f"- 技術領域:`{summary['technology_area_count']}`", + f"- 官方 / primary sources:`{summary['source_count']}`", + f"- 來源失敗:`{summary['source_failures']}`", + f"- 需要審核變更:`{summary['changed_technologies']}`", + f"- 高優先級項目:`{summary['high_priority_count']}`", + f"- 滾動更新狀態:`{summary['rolling_update_status']}`", + "", + "## 技術領域覆蓋", + "", + "| 技術領域 | 技術數 | 高優先級 | 需要審核 | 代表技術 |", + "|---|---:|---:|---:|---|", + ] + for domain in payload["technology_domains"]: + lines.append( + f"| `{domain['technology_area']}` | `{domain['technology_count']}` | " + f"`{domain['high_priority_count']}` | `{domain['changed_count']}` | " + f"{', '.join(domain['representative_technologies'])} |" + ) + + lines.extend([ + "", + "## 高優先級審核佇列", + "", + "| 技術 | 領域 | 優先級 | Gate | 下一步 |", + "|---|---|---|---|---|", + ]) + for item in payload["high_priority_review_queue"]: + lines.append( + f"| {item['display_name']} | `{item['technology_area']}` | " + f"`{item['evaluation_priority']}` | `{item['gate_status']}` | {item['next_gate']} |" + ) + + lines.extend([ + "", + "## Agent 專業分工", + "", + "| Agent | 專業角色 | 自動化範圍 | 需要審核的邊界 |", + "|---|---|---|---|", + ]) + for role in payload["professional_agent_roles"]: + lines.append( + f"| {role['agent']} | {role['professional_role']} | " + f"{role['auto_scope']} | {role['review_boundary']} |" + ) + + lines.extend([ + "", + "## 滾動更新控制", + "", + "| 節奏 | Agent 可自動做什麼 | 輸出 | Gate |", + "|---|---|---|---|", + ]) + for control in payload["rolling_update_controls"]: + lines.append( + f"| {control['cadence']} | {control['agent_auto_action']} | " + f"{control['output']} | `{control['gate']}` |" + ) + + lines.extend([ + "", + "## 優先工作清單", + "", + "| 順序 | 工作 | 優先級 | 自動化模式 | 完成定義 |", + "|---:|---|---|---|---|", + ]) + for item in payload["priority_workplan"]: + lines.append( + f"| {item['order']} | {item['work_item']} | `{item['priority']}` | " + f"`{item['automation_mode']}` | {item['done_definition']} |" + ) + + lines.extend([ + "", + "## 仍被 Gate 擋下", + "", + ]) + 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 _success_rate(source_count: int, failure_count: int) -> float: + if source_count <= 0: + return 0.0 + return round(((source_count - failure_count) / source_count) * 100, 1) + + +def _technology_domains(report: dict[str, Any]) -> list[dict[str, Any]]: + grouped: dict[str, list[dict[str, Any]]] = defaultdict(list) + for row in report.get("technologies") or []: + grouped[str(row.get("technology_area") or "uncategorized")].append(row) + + domains = [] + for area, rows in sorted(grouped.items()): + high_priority = [row for row in rows if row.get("evaluation_priority") in {"p0", "p1"}] + changed = [row for row in rows if row.get("changed")] + domains.append({ + "technology_area": area, + "technology_count": len(rows), + "high_priority_count": len(high_priority), + "changed_count": len(changed), + "representative_technologies": [ + str(row.get("display_name") or row.get("technology_id")) + for row in rows[:4] + ], + }) + return domains + + +def _high_priority_review_queue(report: dict[str, Any]) -> list[dict[str, Any]]: + rows = [ + row + for row in report.get("technologies") or [] + if row.get("evaluation_priority") in {"p0", "p1"} and row.get("changed") + ] + return [ + { + "technology_id": row.get("technology_id"), + "display_name": row.get("display_name"), + "technology_area": row.get("technology_area"), + "evaluation_priority": row.get("evaluation_priority"), + "gate_status": "scorecard_required_before_integration", + "next_gate": "刷新 scorecard,若涉及 SDK/API/route/Telegram/host write 則送人工審核。", + "requires_cost_approval": bool(row.get("requires_cost_approval")), + "requires_dependency_approval": bool(row.get("requires_dependency_approval")), + "requires_security_review": bool(row.get("requires_security_review")), + } + for row in rows[:12] + ] + + +def _professional_agent_roles() -> list[dict[str, Any]]: + return [ + { + "agent": "OpenClaw", + "professional_role": "生產決策仲裁者、風險分級與最後 policy guard", + "auto_scope": "維持現有 production baseline、讀取 replay / shadow 評分、拒絕無證據替換", + "review_boundary": "任何取代、降級、生產路由切換都必須通過 replay / shadow / canary 與人工批准。", + }, + { + "agent": "NemoTron", + "professional_role": "離線回放評估者、模型能力比較、合約輸出 smoke gate", + "auto_scope": "只讀 request pack、比對候選輸出、產生 replay scorecard 草稿", + "review_boundary": "不得自行呼叫外部 NIM/API、不得讀 labels 作答、不得進生產路由。", + }, + { + "agent": "Hermes", + "professional_role": "知識管理、RAG 整理、報告草稿與長期技能庫維護", + "auto_scope": "整理 primary source 摘要、建立 no-send 日週月報、準備人審包", + "review_boundary": "不得同步 raw chat history、不得保存 secret、不得直接發 Telegram live report。", + }, + { + "agent": "MarketRadar", + "professional_role": "AI 技術市場雷達、版本監控、來源失敗偵測", + "auto_scope": "每 6 小時只讀 primary sources、產生 freshness / review queue", + "review_boundary": "不得自動新增 SDK、不得自動修改 provider route 或 workflow 行為。", + }, + { + "agent": "Critic / Reviewer", + "professional_role": "獨立審核、反例檢查、整合風險評分", + "auto_scope": "檢查政策旗標、來源可靠性、成本與資安風險", + "review_boundary": "只能輸出 blocked / candidate / owner_review,不得直接執行寫入。", + }, + ] + + +def _rolling_update_controls(report: dict[str, Any]) -> list[dict[str, Any]]: + cadence = report.get("cadence") or {} + return [ + { + "cadence": "每 6 小時", + "agent_auto_action": "讀取官方文件、PyPI、npm、GitHub release、primary source hash。", + "output": "AI 技術 watch report、來源失敗清單、review queue。", + "gate": "read_only_only", + "cadence_source": cadence.get("near_real_time_watch", ""), + }, + { + "cadence": "每日", + "agent_auto_action": "依 business applicability、成本、依賴、資安、AWOOOI fit 分類。", + "output": "日報摘要與中低風險自動處理建議。", + "gate": "no_send_report_until_delivery_gate", + "cadence_source": cadence.get("daily_triage", ""), + }, + { + "cadence": "每週", + "agent_auto_action": "刷新 scorecard,決定 sandbox / replay / adapter design 優先級。", + "output": "週報、優先序、候選整合審查包。", + "gate": "scorecard_required_before_replay", + "cadence_source": cadence.get("weekly_scorecard", ""), + }, + { + "cadence": "每月", + "agent_auto_action": "彙整趨勢,提出 roadmap / watch-only / retire 建議。", + "output": "月報與策略審核包。", + "gate": "human_review_for_strategy_or_production_change", + "cadence_source": cadence.get("monthly_strategy_review", ""), + }, + ] + + +def _integration_candidates(report: dict[str, Any]) -> list[dict[str, Any]]: + return [ + { + "technology_id": row.get("technology_id"), + "display_name": row.get("display_name"), + "technology_area": row.get("technology_area"), + "integration_surface": row.get("integration_surface"), + "awoooi_role": row.get("awoooi_role"), + "changed": bool(row.get("changed")), + "decision": row.get("decision"), + "recommended_actions": row.get("recommended_actions") or [], + } + for row in report.get("technologies") or [] + ] + + +def _priority_workplan() -> list[dict[str, Any]]: + return [ + { + "order": 1, + "priority": "P0", + "work_item": "AI 技術雷達 primary source 監控產品化", + "automation_mode": "agent_auto_read_only", + "done_definition": "API、snapshot、Markdown、schema、測試與 production readback 都能顯示技術領域、來源與 Gate。", + }, + { + "order": 2, + "priority": "P0", + "work_item": "近即時版本 / release / docs 變更偵測", + "automation_mode": "agent_auto_schedule_read_only", + "done_definition": "每 6 小時可跑 watch;失敗來源會進日報,不會自動整合。", + }, + { + "order": 3, + "priority": "P0", + "work_item": "OpenClaw / Hermes / NemoTron / MarketRadar 專業分工與成長紀錄", + "automation_mode": "agent_auto_read_model_human_review_for_write", + "done_definition": "每個 Agent 的角色、輸出、學習寫回與限制都能被前端讀回。", + }, + { + "order": 4, + "priority": "P1", + "work_item": "AI 技術 scorecard 與 sandbox / replay 優先級", + "automation_mode": "agent_propose_owner_review", + "done_definition": "高優先級變更先進 scorecard,再進 no-cost/no-write sandbox 或 replay 計畫。", + }, + { + "order": 5, + "priority": "P1", + "work_item": "Telegram Bot 報告與高風險審核橋接", + "automation_mode": "blocked_until_telegram_send_gate", + "done_definition": "低中風險只告警回報;高風險需 owner approval 後才可發送或執行。", + }, + { + "order": 6, + "priority": "P2", + "work_item": "新 AI 技術探索與 watchlist 擴充", + "automation_mode": "agent_auto_discover_human_classify", + "done_definition": "GitHub topic / package registry / 官方 blog 可提出候選,但加入正式 watchlist 前需審核。", + }, + ] + + +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 technology radar readback.") + parser.add_argument("--technology-watch", required=True) + parser.add_argument("--agent-market-radar", required=True) + parser.add_argument("--evidence-commit", required=True) + parser.add_argument("--output", required=True) + parser.add_argument("--markdown-output", required=True) + parser.add_argument("--markdown-alias-output") + args = parser.parse_args() + + payload = build_radar( + technology_watch=load_json(Path(args.technology_watch)), + agent_market_radar=load_json(Path(args.agent_market_radar)), + 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") + if args.markdown_alias_output: + Path(args.markdown_alias_output).write_text(markdown, encoding="utf-8") + print( + "AI_TECHNOLOGY_RADAR_READBACK_OK " + f"overall={payload['summary']['overall_completion_percent']}% " + f"technology={payload['summary']['technology_count']} " + f"sources={payload['summary']['source_count']} " + f"changed={payload['summary']['changed_technologies']} " + f"failures={payload['summary']['source_failures']}" + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())