feat(phase6-9): Complete modular architecture and Agent Teams

Phase 6.4 - Modular Architecture:
- Add lewooogo-brain adapters for LLM providers
- Add lewooogo-data dual memory (Redis + PostgreSQL)
- Implement consensus engine for multi-agent decisions
- Add incident memory service for historical context

Phase 9 - Agent Teams (Claude Agent SDK):
- Add base agent class with Claude Sonnet 4 integration
- Implement action planner, blast radius, and security agents
- Add agent API endpoints and proposal workflow
- Integrate ADR-009 OpenClaw Agent Teams architecture

DevOps & CI/CD:
- Add GitHub Actions CI/CD workflows (ci.yaml, cd.yaml)
- Add pre-commit hooks and secrets baseline
- Add docker-compose for local development
- Update Kubernetes network policies

Frontend Improvements:
- Add auto-healing error boundary component
- Update i18n messages for agent features
- Enhance dual-state incident card with execution feedback

Documentation:
- Add 7 ADRs covering MCP, design system, architecture decisions
- Update ARCHITECTURE_MEMORY.md with modular design
- Add GLOBAL_RULES.md and SOUL.md for project identity

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-03-23 18:40:36 +08:00
parent 6eccb45757
commit 7478dc0254
169 changed files with 24613 additions and 247 deletions

View File

@@ -0,0 +1,130 @@
# ADR-001: MCP Protocol 採用
> **狀態**: Accepted
> **日期**: 2026-03-19
> **決策者**: CTO + CEO
## 背景
AWOOOI 的 leWOOOgo Engine 需要與大量外部工具整合 (K8s, SSH, AWS/GCP, Database, Notification 等)。傳統做法是針對每個服務寫專屬 Adapter耗時且難以維護。
Anthropic 的 **Model Context Protocol (MCP)** 提供標準化的 AI-Tool 溝通協議,已有數百個社群 MCP Server 可直接使用。
## 決策
**採用 MCP 作為 leWOOOgo BRAIN ↔ ACTION 的標準通訊協議**
```
┌─────────────────────────────────────────────────────────────┐
│ leWOOOgo Engine │
├─────────────────────────────────────────────────────────────┤
│ │
│ 🧱 INPUT ──→ 🧠 BRAIN ──→ 📢 OUTPUT │
│ │ │
│ ↓ (MCP Protocol) │
│ 🔧 ACTION ←→ [MCP Servers] │
│ │ │
│ ↓ │
│ 📊 DATA │
│ │
└─────────────────────────────────────────────────────────────┘
```
### MCP Server 分類
| 類別 | 範例 MCP Server | 用途 |
|------|----------------|------|
| **Infrastructure** | kubernetes, docker, ssh | 基礎設施操作 |
| **Cloud** | aws, gcp, azure | 雲端資源管理 |
| **Database** | postgres, redis, mongodb | 資料存取 |
| **Notification** | slack, telegram, email | 訊息發送 |
| **Monitoring** | prometheus, grafana | 監控查詢 |
| **Security** | vault, trivy | 安全掃描 |
### leWOOOgo 整合方式
```typescript
// packages/lewooogo-brain/src/mcp-bridge.ts
interface MCPBridge {
// 動態載入 MCP Server
loadServer(serverName: string): Promise<MCPServer>
// 執行 MCP Tool
callTool(server: string, tool: string, params: object): Promise<MCPResult>
// 列出可用工具
listTools(server: string): Promise<MCPToolDefinition[]>
}
```
## 理由
### 1. 生態系統成熟
| 指標 | 數值 |
|------|------|
| 社群 MCP Server | 300+ |
| 官方維護 Server | 20+ |
| 協議版本 | Stable (2024-11) |
### 2. 與 Claude 深度整合
AWOOOI 使用 Claude 作為主要 LLMMCP 是 Anthropic 原生協議,整合最順暢。
### 3. 節省開發時間
| 方案 | 預估工時 |
|------|---------|
| 自建 50 個 Adapter | 500+ 小時 |
| 採用 MCP + 自訂 5 個 | 50 小時 |
### 4. 標準化介面
所有工具使用相同的 JSON-RPC 介面,簡化 BRAIN 邏輯。
## 後果
### 優點
- **即時獲得** 數百種工具能力
- **社群維護** 減輕維護負擔
- **標準協議** 簡化架構設計
- **Claude 原生** 最佳 LLM 整合體驗
### 缺點
- **依賴外部** 需信任社群 MCP Server 品質
- **協議鎖定** 若 MCP 標準改變需跟進
### 風險
| 風險 | 緩解措施 |
|------|---------|
| MCP Server 品質不一 | 建立內部審核清單,只允許白名單 Server |
| 安全漏洞 | 所有 MCP 調用經過 Privacy Shield 脫敏 |
| 效能瓶頸 | 關鍵路徑自建 Adapter非關鍵走 MCP |
### 例外情況
以下場景**不使用**社群 MCP Server改自建 leWOOOgo Adapter
1. **核心業務邏輯** - 如 ClawBot Triage Engine
2. **高頻調用** - 如 Redis Cache (效能考量)
3. **機敏操作** - 如 K8s Delete (需額外授權)
## 實施計畫
| Phase | 任務 | 時程 |
|-------|------|------|
| 0 | 定義 MCPBridge 介面 | Week 1 |
| 1 | 整合 5 個核心 MCP Server | Week 2-3 |
| 2 | 建立 MCP Server 白名單機制 | Week 3 |
| 3 | Privacy Shield 整合 | Week 4 |
## 參考
- [MCP Official Spec](https://spec.modelcontextprotocol.io/)
- [MCP Server Registry](https://github.com/modelcontextprotocol/servers)
- [Anthropic MCP Announcement](https://www.anthropic.com/news/model-context-protocol)
- 會議記錄: `docs/meetings/2026-03-19_FRONTEND_RESTRUCTURE_STRATEGY.md`

View File

@@ -0,0 +1,191 @@
# ADR-002: Nothing.tech 設計系統採用
> **狀態**: Accepted
> **日期**: 2026-03-19
> **決策者**: CPO + CTO
## 背景
AWOOOI 需要統一的視覺語言,區隔於傳統 Dashboard 風格。CEO 在戰略會議中指定採用 **Nothing.tech** 風格:點陣字體 + 毛玻璃效果 + 極簡黑白。
此風格強調「科技感」與「未來感」,符合 AI-First 運維平台定位。
## 決策
**採用 Nothing.tech 風格作為 AWOOOI 設計系統基礎**
### 色彩系統
```css
:root {
/* 主色 */
--nothing-black: #000000;
--nothing-white: #FFFFFF;
--nothing-red: #D71921; /* 告警、錯誤、Critical */
/* 灰階 */
--nothing-gray-50: #FAFAFA;
--nothing-gray-100: #F5F5F5;
--nothing-gray-200: #E5E5E5;
--nothing-gray-300: #D4D4D4;
--nothing-gray-400: #A3A3A3;
--nothing-gray-500: #737373;
--nothing-gray-600: #525252;
--nothing-gray-700: #404040;
--nothing-gray-800: #1A1A1A;
--nothing-gray-900: #0A0A0A;
/* 語意色 */
--status-healthy: #22C55E; /* Green - 正常 */
--status-warning: #F59E0B; /* Amber - 警告 */
--status-critical: #D71921; /* Nothing Red - 嚴重 */
--status-unknown: #6B7280; /* Gray - 未知 */
}
```
### 字體系統
| 用途 | 字體 | Fallback |
|------|------|----------|
| **AI 介面** | NDot 57 | JetBrains Mono, monospace |
| **標題** | NDot 47 | Inter, system-ui |
| **內文** | Inter | system-ui, sans-serif |
| **程式碼** | JetBrains Mono | Fira Code, monospace |
```css
:root {
--font-display: "NDot", "JetBrains Mono", monospace;
--font-heading: "NDot", "Inter", system-ui;
--font-body: "Inter", system-ui, sans-serif;
--font-mono: "JetBrains Mono", "Fira Code", monospace;
}
```
### 毛玻璃效果 (Glassmorphism)
```css
.glass-panel {
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 16px;
}
.glass-panel-dark {
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.05);
}
```
### 動效規範
| 效果 | 用途 | Duration |
|------|------|----------|
| **呼吸燈** | AI 狀態指示 | 2s ease-in-out |
| **打字機** | ClawBot 回應 | 30ms/字元 |
| **淡入** | 卡片載入 | 200ms ease-out |
| **滑入** | 側邊欄 | 300ms cubic-bezier |
```css
@keyframes breathe {
0%, 100% { opacity: 0.4; }
50% { opacity: 1; }
}
.ai-status-indicator {
animation: breathe 2s ease-in-out infinite;
}
```
## 理由
### 1. 品牌差異化
傳統運維 Dashboard 使用 Material/Ant Design視覺同質化嚴重。Nothing.tech 風格能立即建立品牌辨識度。
### 2. AI-First 視覺語言
點陣字體與極簡風格傳達「精準」與「科技感」,符合 AI 運維平台定位。
### 3. 技術可行性
| 需求 | 實現方式 |
|------|---------|
| 點陣字體 | NDot (需購買) 或 Dot Matrix (免費替代) |
| 毛玻璃 | CSS backdrop-filter (現代瀏覽器支援) |
| 深色主題 | Tailwind dark mode |
## 後果
### 優點
- **品牌辨識度** 強烈視覺風格
- **AI 定位** 符合 Agent-Centric 理念
- **現代感** 吸引科技用戶
### 缺點
- **字體成本** NDot 需商業授權
- **相容性** 舊瀏覽器不支援 backdrop-filter
### 風險
| 風險 | 緩解措施 |
|------|---------|
| NDot 授權費用 | 初期用 JetBrains Mono 替代,驗證後再購買 |
| Safari 毛玻璃問題 | 加入 `-webkit-backdrop-filter` prefix |
| 可讀性 | 限制點陣字體於標題,內文用 Inter |
## Tailwind 配置
```javascript
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
nothing: {
black: '#000000',
white: '#FFFFFF',
red: '#D71921',
gray: {
50: '#FAFAFA',
100: '#F5F5F5',
200: '#E5E5E5',
300: '#D4D4D4',
400: '#A3A3A3',
500: '#737373',
600: '#525252',
700: '#404040',
800: '#1A1A1A',
900: '#0A0A0A',
}
},
status: {
healthy: '#22C55E',
warning: '#F59E0B',
critical: '#D71921',
unknown: '#6B7280',
}
},
fontFamily: {
display: ['NDot', 'JetBrains Mono', 'monospace'],
heading: ['NDot', 'Inter', 'system-ui'],
body: ['Inter', 'system-ui', 'sans-serif'],
mono: ['JetBrains Mono', 'Fira Code', 'monospace'],
},
backdropBlur: {
glass: '20px',
}
}
}
}
```
## 參考
- [Nothing.tech Official](https://nothing.tech/)
- [NDot Font](https://pangrampangram.com/products/ndot)
- 會議記錄: `docs/meetings/2026-03-19_FRONTEND_RESTRUCTURE_STRATEGY.md`

View File

@@ -0,0 +1,244 @@
# ADR-003: leWOOOgo 模組化架構
> **狀態**: Accepted
> **日期**: 2026-03-19
> **決策者**: CTO + CEO
## 背景
AWOOOI 需要高度模組化的架構讓開發者能像組樂高一樣快速組合功能。CEO 命名此引擎為 **leWOOOgo** (樂高 + WOOO)。
傳統 monolithic 架構難以擴展plugin 架構則能支援生態系統發展。
## 決策
**採用六大積木類別的 Plugin 架構**
```
┌─────────────────────────────────────────────────────────────────┐
│ leWOOOgo Engine │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 🧱 INPUT ──────→ 🧠 BRAIN ──────→ 📢 OUTPUT │
│ (觸發器) (AI 處理) (通知) │
│ │ │ │ │
│ │ ↓ │ │
│ │ 🔧 ACTION │ │
│ │ (執行器) │ │
│ │ │ │ │
│ └───────→ 📊 DATA ←───────────────┘ │
│ (儲存) │
│ │
│ 🎨 UI │
│ (介面元件) │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### 六大積木類別
| 類別 | 介面 | 用途 | 範例 |
|------|------|------|------|
| **INPUT** | `TriggerPlugin` | 觸發工作流 | Webhook, Cron, Alert, Email |
| **BRAIN** | `AgentProvider` | AI 處理決策 | LLM Router, RAG, Triage, MCP |
| **OUTPUT** | `NotificationChannel` | 發送通知 | Telegram, Slack, LINE, Email |
| **ACTION** | `ActionExecutor` | 執行操作 | K8s, SSH, Docker, API Call |
| **DATA** | `DataAdapter` | 資料存取 | PostgreSQL, Redis, S3, Vector |
| **UI** | `WidgetComponent` | 介面元件 | Card, Chart, Timeline, Status |
### 核心介面定義
```typescript
// packages/lewooogo-core/src/interfaces/plugin.ts
/** 所有 Plugin 的基礎介面 */
interface LeWOOOgoPlugin {
readonly id: string
readonly name: string
readonly version: string
readonly category: 'INPUT' | 'BRAIN' | 'OUTPUT' | 'ACTION' | 'DATA' | 'UI'
initialize(): Promise<void>
healthCheck(): Promise<HealthStatus>
shutdown(): Promise<void>
}
/** INPUT 觸發器 */
interface TriggerPlugin extends LeWOOOgoPlugin {
category: 'INPUT'
subscribe(handler: TriggerHandler): Unsubscribe
getSchema(): TriggerSchema
}
/** BRAIN AI 處理器 */
interface AgentProvider extends LeWOOOgoPlugin {
category: 'BRAIN'
process(input: AgentInput): Promise<AgentOutput>
getCapabilities(): AgentCapability[]
}
/** OUTPUT 通知頻道 */
interface NotificationChannel extends LeWOOOgoPlugin {
category: 'OUTPUT'
send(message: NotificationMessage): Promise<SendResult>
getTemplates(): NotificationTemplate[]
}
/** ACTION 執行器 */
interface ActionExecutor extends LeWOOOgoPlugin {
category: 'ACTION'
execute(action: ActionRequest): Promise<ActionResult>
dryRun(action: ActionRequest): Promise<DryRunResult>
rollback(executionId: string): Promise<RollbackResult>
}
/** DATA 資料適配器 */
interface DataAdapter extends LeWOOOgoPlugin {
category: 'DATA'
connect(): Promise<void>
query<T>(request: QueryRequest): Promise<T>
disconnect(): Promise<void>
}
/** UI 介面元件 */
interface WidgetComponent extends LeWOOOgoPlugin {
category: 'UI'
render(props: WidgetProps): ReactNode
getConfigSchema(): JSONSchema
}
```
### 資料夾結構
```
packages/
├── lewooogo-core/ # 核心引擎
│ ├── src/
│ │ ├── interfaces/ # 六大介面定義
│ │ ├── registry/ # Plugin 註冊中心
│ │ ├── pipeline/ # 工作流引擎
│ │ └── utils/ # 共用工具
│ └── package.json
├── lewooogo-input/ # INPUT 積木
│ ├── src/
│ │ ├── webhook/
│ │ ├── cron/
│ │ ├── prometheus-alert/
│ │ └── email-trigger/
│ └── package.json
├── lewooogo-brain/ # BRAIN 積木
│ ├── src/
│ │ ├── llm-router/ # LLM 路由器
│ │ ├── mcp-bridge/ # MCP 整合 (ADR-001)
│ │ ├── triage-engine/ # 告警分級
│ │ └── rag-provider/ # RAG 檢索
│ └── package.json
├── lewooogo-output/ # OUTPUT 積木
│ ├── src/
│ │ ├── telegram/
│ │ ├── slack/
│ │ ├── line/
│ │ └── email/
│ └── package.json
├── lewooogo-action/ # ACTION 積木
│ ├── src/
│ │ ├── kubernetes/
│ │ ├── ssh/
│ │ ├── docker/
│ │ └── http-api/
│ └── package.json
├── lewooogo-data/ # DATA 積木
│ ├── src/
│ │ ├── postgres/
│ │ ├── redis/
│ │ ├── s3/
│ │ └── vector-db/
│ └── package.json
└── lewooogo-ui/ # UI 積木
├── src/
│ ├── cards/
│ ├── charts/
│ ├── timeline/
│ └── status-indicators/
└── package.json
```
## 理由
### 1. 開發者體驗 (DX)
| 傳統方式 | leWOOOgo 方式 |
|---------|--------------|
| 修改核心程式碼 | npm install + 註冊 |
| 重新部署整體 | 熱插拔 Plugin |
| 閱讀大量文檔 | 統一介面 + TypeScript |
### 2. 生態系統潛力
標準介面允許第三方開發 Plugin形成市場。
### 3. 測試隔離
每個 Plugin 獨立測試,不影響核心引擎。
## 後果
### 優點
- **模組化** 功能獨立開發部署
- **可擴展** 第三方生態系統
- **可測試** 單元測試隔離
- **可維護** 責任分離清晰
### 缺點
- **初期成本** 需建立完整介面規範
- **效能開銷** Plugin 動態載入有成本
- **版本管理** 多 package 需 monorepo 工具
### 風險
| 風險 | 緩解措施 |
|------|---------|
| 介面設計錯誤 | Phase 0 充分討論 + 早期 POC 驗證 |
| Plugin 衝突 | Plugin Registry 管理 + 命名空間隔離 |
| 效能問題 | 關鍵路徑避免過度抽象,效能測試 |
## Monorepo 工具
採用 **pnpm workspace** + **Turborepo**
```yaml
# pnpm-workspace.yaml
packages:
- 'apps/*'
- 'packages/*'
```
```json
// turbo.json
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["build"]
}
}
}
```
## 參考
- [Turborepo](https://turbo.build/)
- [pnpm Workspaces](https://pnpm.io/workspaces)
- ADR-001: MCP Protocol 採用
- 會議記錄: `docs/meetings/2026-03-19_FRONTEND_RESTRUCTURE_STRATEGY.md`

View File

@@ -0,0 +1,268 @@
# ADR-004: 前端狀態管理統一採用 Zustand
> **狀態**: Accepted
> **日期**: 2026-03-19
> **更新日期**: 2026-03-20 (Gate 0 驗證完成)
> **決策者**: CTO + CPO
---
## Gate 0 里程碑驗證 (2026-03-20)
**Tracer Bullet 測試通過!** 以下實作已驗證:
| 元件 | Store | 狀態 |
|------|-------|------|
| Dashboard SSE | `dashboard.store.ts` | ✅ 即時同步 |
| Approval Multi-Sig | `approval.store.ts` | ✅ 狀態機運作正常 |
| HITL 簽核流程 | 整合 API `/approvals/{id}/approve` | ✅ TOCTOU 防護驗證 |
---
## 背景
AWOOOI 的前端 (Agent Hub) 需要處理高度頻繁的狀態更新,包括:
- ClawBot 的 SSE 思考串流 (`/agent/thinking`)
- 即時狀態燈 (Data Pincer 呼吸動畫)
- 待授權卡片的佇列管理 (`/approvals`)
- Plugin 健康狀態即時更新
我們需要一個輕量、無需過度樣板代碼 (Boilerplate),且能與 React 18 完美協作的狀態管理庫。
## 決策
**全面採用 Zustand 作為全域狀態管理工具**
```typescript
// stores/agent.store.ts
import { create } from 'zustand'
import { subscribeWithSelector } from 'zustand/middleware'
interface AgentState {
status: 'idle' | 'thinking' | 'executing' | 'waiting_approval'
thinkingStream: string[]
pendingApprovals: Approval[]
// Actions
setStatus: (status: AgentState['status']) => void
appendThinking: (chunk: string) => void
addApproval: (approval: Approval) => void
}
export const useAgentStore = create<AgentState>()(
subscribeWithSelector((set) => ({
status: 'idle',
thinkingStream: [],
pendingApprovals: [],
setStatus: (status) => set({ status }),
appendThinking: (chunk) => set((s) => ({
thinkingStream: [...s.thinkingStream, chunk]
})),
addApproval: (approval) => set((s) => ({
pendingApprovals: [...s.pendingApprovals, approval]
})),
}))
)
```
### 狀態分層策略
| 層級 | 工具 | 用途 |
|------|------|------|
| **全域 UI 狀態** | Zustand | Agent 狀態、Sidebar 開關、Theme |
| **伺服器資料快取** | TanStack Query | API 回應快取、自動重新驗證 |
| **表單狀態** | React Hook Form | 表單驗證、欄位狀態 |
| **元件局部狀態** | useState | 簡單 UI 切換 |
### 禁止事項
```typescript
// ❌ 禁止Redux
import { createStore } from 'redux'
// ❌ 禁止Context API 做複雜狀態管理
const GlobalContext = createContext<ComplexState>(...)
// ❌ 禁止:單一巨大 Store
const useGodStore = create(() => ({
agent: ...,
plugins: ...,
pipelines: ..., // 太多!
}))
// ✅ 正確Slice Pattern 分拆
const useAgentStore = create(...)
const usePluginStore = create(...)
const usePipelineStore = create(...)
```
## 理由
### 1. 效能優勢
| 特性 | Redux | Zustand |
|------|-------|---------|
| Bundle Size | ~7KB | ~1KB |
| Boilerplate | 高 | 極低 |
| Re-render 控制 | 需 memo/selector | 內建 selector |
| SSE/WebSocket | 需 middleware | 原生支援 |
### 2. SSE 整合範例
```typescript
// hooks/useAgentThinking.ts
export function useAgentThinking() {
const appendThinking = useAgentStore((s) => s.appendThinking)
useEffect(() => {
const eventSource = new EventSource('/v1/agent/thinking')
eventSource.onmessage = (event) => {
appendThinking(event.data) // 直接更新 Zustand
}
return () => eventSource.close()
}, [appendThinking])
}
```
### 3. TanStack Query 協作
```typescript
// hooks/useApprovals.ts
export function useApprovals() {
return useQuery({
queryKey: ['approvals', 'pending'],
queryFn: () => api.listApprovals({ status: 'pending' }),
refetchInterval: 5000, // 每 5 秒輪詢
})
}
```
## 後果
### 優點
- **極度輕量** 不增加 bundle 負擔
- **高頻更新** 完美處理 SSE/WebSocket 串流
- **簡單 API** 降低學習曲線
- **TypeScript 友善** 完整型別推導
### 缺點
- **生態較小** 相比 Redux 社群資源較少
- **DevTools** 功能不如 Redux DevTools 強大
### 風險
| 風險 | 緩解措施 |
|------|---------|
| Store 肥大化 | 強制執行 Slice PatternCode Review 把關 |
| 狀態同步錯誤 | 搭配 TanStack Query 管理伺服器狀態 |
---
## Gate 0 實作細節
### 1. Dashboard SSE Store
```typescript
// stores/dashboard.store.ts
interface DashboardState {
hosts: HostStatus[]
connectionStatus: 'connecting' | 'connected' | 'disconnected' | 'error'
lastUpdate: Date | null
// SSE 控制
connect: (apiUrl: string) => void
disconnect: () => void
}
export const useDashboardStore = create<DashboardState>((set, get) => ({
hosts: [],
connectionStatus: 'disconnected',
lastUpdate: null,
connect: (apiUrl) => {
const eventSource = new EventSource(`${apiUrl}/api/v1/dashboard/stream`)
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data)
set({ hosts: data.hosts, lastUpdate: new Date() })
}
eventSource.onerror = () => set({ connectionStatus: 'error' })
eventSource.onopen = () => set({ connectionStatus: 'connected' })
},
disconnect: () => {
// AbortController cleanup
}
}))
```
### 2. Approval Multi-Sig 狀態機
```typescript
// stores/approval.store.ts
interface ApprovalState {
pendingApprovals: Approval[]
selectedApproval: Approval | null
signingStatus: 'idle' | 'signing' | 'success' | 'error'
// Actions
signApproval: (id: string, userId: string, role: string) => Promise<void>
refreshApprovals: () => Promise<void>
}
// 狀態機轉換圖
// pending → (簽核) → pending (需更多簽章)
// pending → (簽核) → approved (達到閾值)
// pending → (拒絕) → rejected
// pending → (TOCTOU) → voided (資源狀態改變)
```
### 3. SSE + Zustand 整合模式
**企業級 SSE 最佳實踐:**
| 特性 | 實作 |
|------|------|
| **Buffer** | 累積 5 秒內的更新,批次 setState |
| **AbortController** | 元件 unmount 時正確關閉連線 |
| **Reconnection** | 指數退避重連 (1s → 2s → 4s → max 30s) |
| **Heartbeat** | 每 30 秒 ping超時則重連 |
```typescript
// 企業級 SSE Hook 範例
function useSSE(url: string) {
const abortControllerRef = useRef<AbortController>()
const bufferRef = useRef<HostStatus[]>([])
useEffect(() => {
abortControllerRef.current = new AbortController()
const flushBuffer = setInterval(() => {
if (bufferRef.current.length > 0) {
useDashboardStore.setState({ hosts: bufferRef.current })
bufferRef.current = []
}
}, 5000)
return () => {
abortControllerRef.current?.abort()
clearInterval(flushBuffer)
}
}, [url])
}
```
---
## 參考
- [Zustand](https://zustand-demo.pmnd.rs/)
- [TanStack Query](https://tanstack.com/query)
- ADR-002: Nothing.tech 設計系統 (動畫需求)
- [approvals-contract.yaml](../api/approvals-contract.yaml) - API 契約定義

View File

@@ -0,0 +1,178 @@
# ADR-005: 導入 BFF (Backend-For-Frontend) API 閘道模式
> **狀態**: Accepted
> **日期**: 2026-03-19
> **決策者**: CTO + CIO
## 背景
AWOOOI 的底層是由 leWOOOgo Engine 驅動的微服務/Plugin 架構。如果讓 Next.js 前端直接呼叫:
- 多個分散的 Plugin API
- Ollama / Claude API
- PostgreSQL / Redis
- K8s API
會導致:
1. 前端邏輯過於肥大
2. 極高的資安外洩風險
3. 難以實施統一的身分驗證與權限控制
## 決策
**強制實施 BFF (Backend-For-Frontend) 架構**
```
┌─────────────────────────────────────────────────────────────────┐
│ AWOOOI 架構 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌─────────────────┐ │
│ │ Next.js │ ──────→ │ FastAPI BFF │ │
│ │ 前端 │ HTTPS │ Gateway │ │
│ └─────────┘ └────────┬────────┘ │
│ │ │
│ ┌───────────────────┼───────────────────┐ │
│ ↓ ↓ ↓ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ leWOOOgo │ │ ClawBot │ │ PostgreSQL │ │
│ │ Plugins │ │ (Ollama) │ │ Redis │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ════════════════════════════════════════════════════════════ │
│ DMZ (前端無法直達) │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### 核心規則
| 規則 | 說明 |
|------|------|
| **單一入口** | 前端只能打 `https://api.awoooi.wooo.work/v1/*` |
| **禁止直連** | 前端禁止直連 PostgreSQL、Redis、K8s、Ollama |
| **身分驗證** | 所有請求經 BFF JWT 驗證 |
| **資料脫敏** | Privacy Shield 在 BFF 層攔截機敏資料 |
### BFF 層職責
```python
# apps/api/src/routes/agent.py
from fastapi import APIRouter, Depends
from src.auth import require_auth
from src.privacy import PrivacyShield
from src.services import clawbot_client, approval_service
router = APIRouter(prefix="/agent", tags=["Agent"])
@router.post("/chat")
async def chat_with_agent(
request: ChatRequest,
user: User = Depends(require_auth), # 1. 身分驗證
):
# 2. 資料脫敏
sanitized = PrivacyShield.sanitize(request.message)
# 3. 聚合多個後端服務
response = await clawbot_client.chat(sanitized, user_id=user.id)
# 4. 判斷是否需要 Approval
if response.requires_action:
approval = await approval_service.create(
action=response.suggested_action,
user_id=user.id,
)
response.approval_id = approval.id
return response
```
### 禁止事項
```typescript
// ❌ 禁止:前端直連資料庫
const client = new Client({ connectionString: 'postgresql://...' })
// ❌ 禁止:前端直接呼叫 Ollama
const response = await fetch('http://192.168.0.188:11434/api/generate')
// ❌ 禁止:前端直接操作 K8s
const k8s = new KubeConfig()
// ✅ 正確:透過 BFF API
const response = await fetch('https://api.awoooi.wooo.work/v1/agent/chat', {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}` },
body: JSON.stringify({ message: '...' }),
})
```
## 理由
### 1. Zero Trust 網路隔離
| 元件 | 網路可達性 |
|------|-----------|
| Next.js (前端) | Public Internet |
| FastAPI BFF | DMZ (僅接受前端) |
| PostgreSQL | Internal Only |
| Redis | Internal Only |
| Ollama | Internal Only |
| K8s API | Internal Only |
### 2. 統一關注點
| 關注點 | 處理位置 |
|--------|---------|
| 身分驗證 | BFF Middleware |
| 權限檢查 | BFF Dependency |
| 請求限流 | BFF / Nginx |
| 資料脫敏 | BFF Privacy Shield |
| 審計日誌 | BFF Logger |
### 3. 資料聚合
```python
# 一個 API 呼叫 = 多個後端服務聚合
@router.get("/dashboard")
async def get_dashboard(user: User = Depends(require_auth)):
# 平行取得多個資料源
agent_status, pending_approvals, recent_alerts = await asyncio.gather(
clawbot_client.get_status(),
approval_service.list_pending(user.id),
alert_service.list_recent(limit=10),
)
return DashboardResponse(
agent=agent_status,
approvals=pending_approvals,
alerts=recent_alerts,
)
```
## 後果
### 優點
- **Zero Trust** 真正的網路隔離
- **前端精簡** 只負責渲染 UI
- **統一治理** 所有安全策略集中管理
- **可觀測性** 單一入口易於監控
### 缺點
- **開發成本** 新功能需在 BFF 層多寫一層
- **延遲增加** 多一層網路跳躍 (~1-5ms)
### 風險
| 風險 | 緩解措施 |
|------|---------|
| BFF 成為瓶頸 | 水平擴展 + Redis 快取 |
| 開發速度下降 | OpenAPI 自動生成 Client SDK |
## 參考
- [BFF Pattern](https://samnewman.io/patterns/architectural/bff/)
- api-contract.yaml (BFF 對外契約)
- ADR-001: MCP Protocol (內部服務整合)

View File

@@ -0,0 +1,297 @@
# ADR-006: AI 降級備援策略
> **狀態**: 已接受
> **日期**: 2026-03-20
> **決策者**: CTO, CEO
---
## 背景
AWOOOI 系統高度依賴 AI 功能,包括 AI Copilot、異常偵測、智能摘要等。
當本地 Ollama 服務不可用時,需要有完善的降級備援機制,同時嚴格控制雲端 API 成本。
### CEO 指示 #2
> 雲端備援的順序採 **Gemini API 然後才是 Claude API**,並且要有效控管、監控,
> API Token 使用的數量,要搭配告警機制,避免費用暴增!
---
## 決策
### 1. AI 服務優先順序
```
┌─────────────────────────────────────────────────────┐
│ 優先級 1: Ollama (本地) │
│ 192.168.0.188:11434 │
│ 成本: $0 / 延遲: ~200ms │
└─────────────────────────────────────────────────────┘
│ 失敗
┌─────────────────────────────────────────────────────┐
│ 優先級 2: Gemini API (雲端備援 - 優先) │
│ 成本: ~$0.001/1K tokens │
└─────────────────────────────────────────────────────┘
│ 失敗
┌─────────────────────────────────────────────────────┐
│ 優先級 3: Claude API (雲端備援 - 次選) │
│ 成本: ~$0.008/1K tokens │
└─────────────────────────────────────────────────────┘
│ 失敗
┌─────────────────────────────────────────────────────┐
│ 優先級 4: 靜態回應 (完全降級) │
│ 返回預設訊息,不調用任何 AI │
└─────────────────────────────────────────────────────┘
```
### 2. Circuit Breaker 機制
```python
# apps/api/app/services/ai/circuit_breaker.py
from enum import Enum
from datetime import datetime, timedelta
import asyncio
class CircuitState(Enum):
CLOSED = "closed" # 正常
OPEN = "open" # 熔斷
HALF_OPEN = "half_open" # 試探
class CircuitBreaker:
def __init__(
self,
failure_threshold: int = 5, # 連續失敗 5 次觸發熔斷
recovery_timeout: int = 60, # 熔斷後 60 秒嘗試恢復
half_open_max_calls: int = 3 # 半開狀態最多 3 次試探
):
self.state = CircuitState.CLOSED
self.failure_count = 0
self.last_failure_time = None
self.half_open_calls = 0
# ...
async def call(self, func, *args, **kwargs):
if self.state == CircuitState.OPEN:
if self._should_try_recovery():
self.state = CircuitState.HALF_OPEN
self.half_open_calls = 0
else:
raise CircuitOpenError("Circuit is open")
try:
result = await func(*args, **kwargs)
self._on_success()
return result
except Exception as e:
self._on_failure()
raise
```
### 3. Token 使用量監控與告警
#### 每日/每月配額
| API | 每日上限 | 每月上限 | 告警閾值 |
|-----|---------|---------|---------|
| Gemini | 100K tokens | 2M tokens | 70% |
| Claude | 50K tokens | 500K tokens | 70% |
#### 監控 Schema
```python
# apps/api/app/models/ai_usage.py
class AIUsageLog(Base):
__tablename__ = "ai_usage_logs"
id = Column(UUID, primary_key=True)
provider = Column(String) # ollama, gemini, claude
model = Column(String)
input_tokens = Column(Integer)
output_tokens = Column(Integer)
latency_ms = Column(Integer)
success = Column(Boolean)
error_message = Column(String, nullable=True)
user_id = Column(UUID, ForeignKey("users.id"))
created_at = Column(DateTime, default=func.now())
```
#### 告警規則
```yaml
# k8s/monitoring/prometheus/ai-usage-alerts.yaml
groups:
- name: ai-usage-alerts
rules:
# Gemini 每日用量 70% 告警
- alert: GeminiDailyUsageWarning
expr: |
sum(increase(ai_tokens_total{provider="gemini"}[24h])) > 70000
labels:
severity: warning
annotations:
summary: "Gemini API 每日用量已達 70%"
description: "今日 Gemini 已使用 {{ $value | humanize }} tokens"
# Gemini 每日用量 90% 嚴重告警
- alert: GeminiDailyUsageCritical
expr: |
sum(increase(ai_tokens_total{provider="gemini"}[24h])) > 90000
labels:
severity: critical
annotations:
summary: "Gemini API 每日用量已達 90%,即將觸發限流"
# Claude 每日用量 70% 告警
- alert: ClaudeDailyUsageWarning
expr: |
sum(increase(ai_tokens_total{provider="claude"}[24h])) > 35000
labels:
severity: warning
annotations:
summary: "Claude API 每日用量已達 70%"
# Ollama 連續失敗告警
- alert: OllamaConsecutiveFailures
expr: |
increase(ai_requests_failed_total{provider="ollama"}[5m]) > 5
labels:
severity: critical
annotations:
summary: "Ollama 服務可能已離線"
description: "過去 5 分鐘 Ollama 請求失敗超過 5 次,已啟動雲端備援"
# 月度預算 50% 提醒
- alert: MonthlyAIBudgetWarning
expr: |
(
sum(increase(ai_tokens_total{provider="gemini"}[30d])) * 0.000001 +
sum(increase(ai_tokens_total{provider="claude"}[30d])) * 0.000008
) > 5
labels:
severity: warning
annotations:
summary: "AI 月度成本已達 $5 (預算 50%)"
```
### 4. 成本預估
| 場景 | Gemini | Claude | 月成本 |
|------|--------|--------|--------|
| **正常** (Ollama 100%) | 0 | 0 | $0 |
| **輕度降級** (Ollama 90%, Gemini 10%) | ~200K | 0 | ~$0.20 |
| **中度降級** (Gemini 80%, Claude 20%) | ~1.6M | ~400K | ~$5 |
| **完全降級** (雲端 100%) | ~2M | ~500K | ~$10 |
### 5. 實作範例
```python
# apps/api/app/services/ai/router.py
from app.services.ai.providers import OllamaProvider, GeminiProvider, ClaudeProvider
from app.services.ai.circuit_breaker import CircuitBreaker
from app.services.ai.usage_tracker import UsageTracker
class AIRouter:
def __init__(self):
self.ollama = OllamaProvider()
self.gemini = GeminiProvider()
self.claude = ClaudeProvider()
self.ollama_circuit = CircuitBreaker(failure_threshold=3, recovery_timeout=30)
self.gemini_circuit = CircuitBreaker(failure_threshold=5, recovery_timeout=60)
self.claude_circuit = CircuitBreaker(failure_threshold=5, recovery_timeout=60)
self.usage_tracker = UsageTracker()
async def generate(self, prompt: str, user_id: str) -> AIResponse:
providers = [
("ollama", self.ollama, self.ollama_circuit),
("gemini", self.gemini, self.gemini_circuit),
("claude", self.claude, self.claude_circuit),
]
for name, provider, circuit in providers:
# 檢查配額
if name in ["gemini", "claude"]:
if await self.usage_tracker.is_quota_exceeded(name):
logger.warning(f"{name} daily quota exceeded, skipping")
continue
try:
result = await circuit.call(provider.generate, prompt)
# 記錄使用量
await self.usage_tracker.log(
provider=name,
input_tokens=result.input_tokens,
output_tokens=result.output_tokens,
user_id=user_id,
success=True
)
return result
except CircuitOpenError:
logger.info(f"{name} circuit is open, trying next provider")
continue
except Exception as e:
logger.error(f"{name} failed: {e}, trying next provider")
await self.usage_tracker.log(
provider=name,
error_message=str(e),
user_id=user_id,
success=False
)
continue
# 所有 AI 都失敗,返回靜態回應
return AIResponse(
content="抱歉AI 服務暫時不可用。請稍後再試,或聯繫管理員。",
provider="fallback",
tokens=0
)
```
### 6. Dashboard 展示
AI 用量監控面板應顯示:
- 今日各 Provider 使用量 (tokens)
- 本月累計成本 (USD)
- 各 Provider 健康狀態 (綠/黃/紅)
- 平均延遲 (ms)
- 成功率 (%)
---
## 影響
### 正面
- 確保 AI 功能高可用性
- 成本可控、可預測
- 即時告警避免帳單爆炸
### 需要注意
- 需維護多個 API Key
- 不同 Provider 回應品質可能有差異
- 需要處理 API 格式轉換
---
## 變更記錄
| 日期 | 版本 | 變更 | 作者 |
|------|------|------|------|
| 2026-03-20 | v1.0 | 初版建立 | CTO |
---
*此 ADR 記錄 AI 降級備援策略的決策過程與實作規範。*

View File

@@ -0,0 +1,234 @@
# ADR-007: 資料保留策略
> **狀態**: 已接受
> **日期**: 2026-03-20
> **決策者**: CEO, CTO, CIO
---
## 背景
需要定義各類型資料的保留時間 (TTL),確保:
1. 系統效能不因資料累積而下降
2. 重要資料有足夠的回溯時間
3. 儲存成本可控
### CEO 指示 #7
> 熱資料 (Redis/即時查詢) TTL 7 天 => 初期是否也保留 6 個月?要確認數據量有多大?
---
## 決策
### 資料分層策略
```
┌─────────────────────────────────────────────────────────┐
│ Layer 1: 熱資料 (Redis) │
│ TTL: 7-30 天 │
│ 用途: 即時查詢、快取、Session │
│ 預估容量: ~500MB │
└─────────────────────────────────────────────────────────┘
│ 過期後
┌─────────────────────────────────────────────────────────┐
│ Layer 2: 溫資料 (PostgreSQL) │
│ TTL: 6 個月 (CEO 指示) │
│ 用途: 歷史查詢、報表、分析 │
│ 預估容量: ~5GB/月 │
└─────────────────────────────────────────────────────────┘
│ 過期後
┌─────────────────────────────────────────────────────────┐
│ Layer 3: 冷資料 (歸檔) │
│ TTL: 永久 (審計日誌) / 1 年 (一般) │
│ 用途: 合規、稽核、法律要求 │
│ 預估容量: ~10GB/年 │
└─────────────────────────────────────────────────────────┘
```
### 各資料類型 TTL 定義
#### Redis 熱資料 (Layer 1)
| 資料類型 | TTL | 說明 | 預估大小 |
|---------|-----|------|---------|
| Session Token | 7 天 | 用戶登入狀態 | ~1KB/session |
| Dashboard 快取 | 5 分鐘 | 即時指標聚合 | ~10KB |
| 主機狀態快取 | 30 秒 | 即時健康狀態 | ~2KB/host |
| AI 回應快取 | 1 小時 | 相同問題快取 | ~5KB/entry |
| 限流計數器 | 1 分鐘 | Rate Limiting | ~100B/user |
**Redis 容量評估**:
- 4 台主機 × 2KB = 8KB (即時狀態)
- 100 用戶 × 1KB = 100KB (Session)
- Dashboard 快取 = 50KB
- AI 快取 (1000 條) = 5MB
- **總計: ~10MB (遠低於 Redis 16GB 容量)**
> ✅ **結論**: Redis 熱資料保持短 TTL (7-30 天) 是合理的,不需要延長至 6 個月。
> Redis 用於快取和即時查詢,歷史資料應存放在 PostgreSQL。
#### PostgreSQL 溫資料 (Layer 2)
| 資料類型 | TTL | 說明 | 預估大小 |
|---------|-----|------|---------|
| 監控指標 | 6 個月 | CPU/Memory/Disk 歷史 | ~1GB/月 |
| 告警記錄 | 6 個月 | 歷史告警 | ~100MB/月 |
| 部署記錄 | 6 個月 | Pipeline 執行歷史 | ~50MB/月 |
| 工單記錄 | 6 個月 | 處理歷史 | ~20MB/月 |
| AI 對話記錄 | 6 個月 | Copilot 歷史 | ~500MB/月 |
| 用戶操作記錄 | 6 個月 | 行為追蹤 | ~200MB/月 |
**PostgreSQL 容量評估**:
- 每月增量: ~2GB
- 6 個月累計: ~12GB
- **總計 (含索引): ~20GB**
> ✅ **結論**: PostgreSQL 溫資料保留 6 個月,符合 CEO 指示,容量可控。
#### 冷資料歸檔 (Layer 3)
| 資料類型 | TTL | 說明 |
|---------|-----|------|
| 審計日誌 | 永久 | 合規要求,不可刪除 |
| 財務記錄 | 7 年 | 法律要求 |
| 安全事件 | 3 年 | 資安稽核 |
| 系統設定變更 | 1 年 | 變更追蹤 |
---
### 資料清理機制
#### 自動清理 Job
```python
# apps/api/app/jobs/data_cleanup.py
from datetime import datetime, timedelta
from app.database import get_db
from app.models import Metric, Alert, Deployment, AIConversation
async def cleanup_expired_data():
"""每日凌晨 3:00 執行"""
six_months_ago = datetime.utcnow() - timedelta(days=180)
async with get_db() as db:
# 清理過期監控指標
deleted_metrics = await db.execute(
delete(Metric).where(Metric.created_at < six_months_ago)
)
logger.info(f"Deleted {deleted_metrics.rowcount} expired metrics")
# 清理過期告警 (保留 acknowledged 狀態)
deleted_alerts = await db.execute(
delete(Alert).where(
Alert.created_at < six_months_ago,
Alert.status != 'archived' # 保留歸檔的告警
)
)
logger.info(f"Deleted {deleted_alerts.rowcount} expired alerts")
# ... 其他資料類型
await db.commit()
# 更新 Prometheus 指標
cleanup_records_deleted.labels(type="metrics").inc(deleted_metrics.rowcount)
cleanup_records_deleted.labels(type="alerts").inc(deleted_alerts.rowcount)
```
#### K8s CronJob
```yaml
# k8s/jobs/data-cleanup-cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: awoooi-data-cleanup
namespace: awoooi-prod
spec:
schedule: "0 3 * * *" # 每日凌晨 3:00
jobTemplate:
spec:
template:
spec:
containers:
- name: cleanup
image: awoooi-api:latest
command: ["python", "-m", "app.jobs.data_cleanup"]
restartPolicy: OnFailure
```
---
### 資料量監控
#### Prometheus 指標
```yaml
# k8s/monitoring/prometheus/data-alerts.yaml
groups:
- name: data-storage-alerts
rules:
# PostgreSQL 容量警告
- alert: PostgreSQLHighUsage
expr: |
pg_database_size_bytes{datname="awoooi"} > 15 * 1024 * 1024 * 1024
labels:
severity: warning
annotations:
summary: "PostgreSQL 容量已達 15GB"
description: "目前使用 {{ $value | humanize1024 }},建議檢查資料清理 Job"
# Redis 容量警告
- alert: RedisHighMemory
expr: |
redis_memory_used_bytes{db="10"} > 1 * 1024 * 1024 * 1024
labels:
severity: warning
annotations:
summary: "Redis DB 10 記憶體使用超過 1GB"
```
---
### 儲存成本評估
| 層級 | 6 個月容量 | 儲存類型 | 成本 |
|------|-----------|---------|------|
| Redis (熱) | ~10MB | 內存 | 包含在伺服器 |
| PostgreSQL (溫) | ~20GB | SSD | 包含在伺服器 |
| 歸檔 (冷) | ~10GB/年 | HDD/S3 | ~$0.5/月 |
**結論**: 採用自建伺服器,儲存成本基本為 $0 (已攤提)。
---
## 影響
### 正面
- 資料保留策略明確,符合 CEO 6 個月要求
- Redis 維持高效能 (短 TTL)
- 歷史資料可追溯
- 儲存成本可控
### 需要注意
- 清理 Job 需要監控,確保正常執行
- 歸檔資料需要定期備份
- 審計日誌不可刪除,需永久保留
---
## 變更記錄
| 日期 | 版本 | 變更 | 作者 |
|------|------|------|------|
| 2026-03-20 | v1.0 | 初版建立 | CTO |
---
*此 ADR 記錄資料保留策略的決策過程與實作規範。*

View File

@@ -0,0 +1,583 @@
# ADR-009: OpenClaw Agent Teams 架構
**狀態**: 提議中 → 研究完成
**日期**: 2026-03-23
**決策者**: 統帥 + AI 架構師
**Phase**: 9.1-9.2 (SDK 研究 + 架構設計)
## 背景
AWOOOI 的核心價值是 "AI Sees. AI Acts. You Approve."
目前 OpenClaw 是單一 AI 大腦,面對複雜告警時:
- 單一視角可能遺漏問題
- 無法並行分析多個面向
- 決策品質依賴單一模型
Claude 推出了 **Claude Agent SDK** (原 Claude Code SDK2026-03-20 發布 v0.1.50),支援多 Agent 協調。我們評估將此概念整合進 AWOOOI 產品。
### SDK 研究結論 (2026-03-23)
| 項目 | 研究結果 |
|------|---------|
| **SDK 名稱** | `claude-agent-sdk` (PyPI) |
| **最新版本** | v0.1.50 (2026-03-20) |
| **Python 版本** | ≥ 3.10 |
| **核心 API** | `query()`, `ClaudeSDKClient` |
| **Subagent 支援** | ✅ 原生支援 (`AgentDefinition`) |
| **自訂 Tools** | ✅ `@tool` 裝飾器 + MCP 整合 |
## 決策
**採用 Claude Agent SDK 實作 OpenClaw Agent Teams升級為多專家共識決策架構。**
### 為何選擇 Claude Agent SDK (而非自建)
| 考量 | 自建方案 | Claude Agent SDK |
|------|---------|------------------|
| 開發時間 | 2-3 週 | 2-3 天 |
| Tool 執行 | 需自行實作 | 內建 (Read, Edit, Bash...) |
| Subagent | 需自行設計 | 原生支援 |
| Session 管理 | 需自行實作 | 內建 (resume, fork) |
| MCP 整合 | 需橋接 | 原生支援 |
| 維護成本 | 高 | 低 (跟隨 Anthropic 更新) |
### 架構設計
```
┌─────────────────────────────────────────────────────────────┐
│ OpenClaw Coordinator │
│ (Team Lead Agent) │
├─────────────────────────────────────────────────────────────┤
│ ↓ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Security │ │ BlastRadius │ │ Action │ │
│ │ Agent │ │ Agent │ │ Planner │ │
│ │ (資安評估) │ │ (影響範圍) │ │ (行動方案) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ ↓ ↓ ↓ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Consensus Engine │ │
│ │ (共識引擎 - 加權投票) │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Final Proposal │ │
│ │ (統一提案 → 人類審批) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
### Agent 職責
| Agent | 職責 | 輸出 |
|-------|------|------|
| **Coordinator** | 分配任務、彙整共識 | Final Proposal |
| **SecurityAgent** | 評估安全風險、權限影響 | Risk Score (0-10) |
| **BlastRadiusAgent** | 分析影響範圍、相依服務 | Affected Services List |
| **ActionPlannerAgent** | 規劃修復步驟、回滾方案 | Action Steps + Rollback |
### 共識機制
```python
class ConsensusEngine:
weights = {
"security": 0.4, # 資安權重最高
"blast_radius": 0.3, # 影響範圍次之
"action_plan": 0.3, # 行動方案
}
def calculate_confidence(self, results: dict) -> float:
"""加權計算整體信心分數"""
score = 0
for agent, weight in self.weights.items():
score += results[agent].confidence * weight
return score
def should_auto_approve(self, confidence: float) -> bool:
"""信心分數 > 0.9 且無高風險 → 可自動執行"""
return confidence > 0.9 and not self.has_high_risk()
```
## 技術實作
### 依賴 (Phase 9.2 研究結果)
```toml
# apps/api/pyproject.toml
[project.dependencies]
# Phase 9: OpenClaw Agent Teams
claude-agent-sdk = ">=0.1.50" # Claude Agent SDK (原 Claude Code SDK)
# Note: SDK 自動包含 Claude Code CLI無需額外安裝
```
#### 安裝指令
```bash
# 使用 uv (推薦)
uv add claude-agent-sdk
# 使用 pip
pip install claude-agent-sdk
# 驗證安裝
python -c "from claude_agent_sdk import query; print('OK')"
```
#### 環境變數
```bash
# 必須
export ANTHROPIC_API_KEY=sk-ant-...
# 可選 (雲端備援,參考 ADR-006)
export CLAUDE_CODE_USE_BEDROCK=1 # AWS Bedrock
export CLAUDE_CODE_USE_VERTEX=1 # Google Vertex AI
```
### 核心類別 (使用 Claude Agent SDK)
```python
# apps/api/src/services/openclaw_team.py
import asyncio
from claude_agent_sdk import (
query,
ClaudeAgentOptions,
AgentDefinition,
ClaudeSDKClient,
AssistantMessage,
ResultMessage,
)
from dataclasses import dataclass
from typing import AsyncIterator
@dataclass
class AgentResult:
agent: str
analysis: str
confidence: float
risk_score: float | None = None
affected_services: list[str] | None = None
action_steps: list[str] | None = None
@dataclass
class Proposal:
incident_id: str
summary: str
agent_results: list[AgentResult]
consensus_score: float
recommended_action: str
auto_approvable: bool
class OpenClawTeam:
"""
使用 Claude Agent SDK 實作多專家協調分析
符合 leWOOOgo BRAIN 積木介面
"""
def __init__(self):
# 定義專家 Subagents
self.agents = {
"security-expert": AgentDefinition(
description="資安專家,評估安全風險與權限影響",
prompt="""你是 AWOOOI 的資安專家。
分析告警的安全風險,評估:
1. 是否涉及敏感資料
2. 是否可能被利用
3. 權限邊界是否被突破
輸出 JSON: {"risk_score": 0-10, "analysis": "...", "confidence": 0-1}""",
tools=["Read", "Grep"], # 只讀權限
),
"blast-radius": AgentDefinition(
description="影響範圍分析師,評估相依服務與影響範圍",
prompt="""你是 AWOOOI 的影響範圍分析師。
分析告警的影響範圍:
1. 直接影響的服務
2. 間接相依的服務
3. 使用者影響人數估計
輸出 JSON: {"affected_services": [...], "blast_radius": "low|medium|high", "confidence": 0-1}""",
tools=["Read", "Glob", "Grep"],
),
"action-planner": AgentDefinition(
description="行動規劃師,制定修復步驟與回滾方案",
prompt="""你是 AWOOOI 的行動規劃師。
根據告警制定修復計畫:
1. 立即修復步驟 (kubectl 指令)
2. 驗證步驟
3. 回滾方案
注意: 所有 kubectl 必須帶 -n awoooi-prod
輸出 JSON: {"action_steps": [...], "rollback_steps": [...], "confidence": 0-1}""",
tools=["Read", "Glob"],
),
}
self.options = ClaudeAgentOptions(
allowed_tools=["Read", "Glob", "Grep", "Agent"], # Agent 用於調用 Subagent
agents=self.agents,
system_prompt="""你是 OpenClaw CoordinatorAWOOOI 的 AI 決策引擎。
你的任務是協調多個專家 Agent 分析告警,彙整共識並產出最終提案。
呼叫順序: security-expert → blast-radius → action-planner
最終輸出統一提案供人類審批。""",
)
async def analyze_incident(self, incident: dict) -> Proposal:
"""
並行呼叫多個 Subagent 分析告警
"""
prompt = f"""
分析以下告警並產出修復提案:
```json
{json.dumps(incident, ensure_ascii=False, indent=2)}
```
請依序呼叫以下 Agent:
1. security-expert - 評估安全風險
2. blast-radius - 分析影響範圍
3. action-planner - 規劃修復步驟
收集所有分析結果後,使用 ConsensusEngine 邏輯 (security 40%, blast_radius 30%, action 30%)
計算整體信心分數,並產出最終提案。
輸出格式:
```json
{{
"summary": "一句話摘要",
"agent_results": [...],
"consensus_score": 0-1,
"recommended_action": "建議的 kubectl 指令",
"auto_approvable": true/false (>0.9 且無高風險)
}}
```
"""
result_json = None
async for message in query(prompt=prompt, options=self.options):
if isinstance(message, ResultMessage):
# 解析最終結果
result_json = self._extract_json(message.result)
if not result_json:
raise ValueError("Agent Team 未能產出有效提案")
return Proposal(
incident_id=incident.get("id", "unknown"),
summary=result_json.get("summary", ""),
agent_results=self._parse_agent_results(result_json.get("agent_results", [])),
consensus_score=result_json.get("consensus_score", 0),
recommended_action=result_json.get("recommended_action", ""),
auto_approvable=result_json.get("auto_approvable", False),
)
def _extract_json(self, text: str) -> dict:
"""從回應中提取 JSON"""
import json
import re
match = re.search(r'```json\s*(.*?)\s*```', text, re.DOTALL)
if match:
return json.loads(match.group(1))
return json.loads(text)
def _parse_agent_results(self, results: list) -> list[AgentResult]:
"""解析各 Agent 結果"""
return [
AgentResult(
agent=r.get("agent", "unknown"),
analysis=r.get("analysis", ""),
confidence=r.get("confidence", 0),
risk_score=r.get("risk_score"),
affected_services=r.get("affected_services"),
action_steps=r.get("action_steps"),
)
for r in results
]
```
### 替代方案: ClaudeSDKClient (互動式)
```python
# 適用於需要人機互動的場景
async def interactive_analysis(incident: dict):
async with ClaudeSDKClient(options=options) as client:
# 第一輪: 安全分析
await client.query(f"使用 security-expert 分析: {json.dumps(incident)}")
security_result = await collect_response(client)
# 人類可在此介入調整
# 第二輪: 影響範圍
await client.query("繼續使用 blast-radius 分析影響範圍")
blast_result = await collect_response(client)
# ...
```
### API 端點
```python
# apps/api/src/routes/incidents.py
@router.post("/api/v1/incidents/{incident_id}/analyze")
async def analyze_with_team(incident_id: str):
"""使用 Agent Team 分析告警"""
incident = await get_incident(incident_id)
team = OpenClawTeam()
proposal = await team.analyze_incident(incident)
return {
"proposal": proposal,
"agent_results": proposal.agent_results,
"consensus_score": proposal.consensus_score,
"auto_approvable": proposal.auto_approvable
}
```
### UI 呈現
```tsx
// apps/web/src/components/incident/agent-team-analysis.tsx
export function AgentTeamAnalysis({ proposal }: Props) {
return (
<GlassCard>
<h3>{t('incident.teamAnalysis')}</h3>
{/* 各 Agent 分析結果 */}
<div className="grid grid-cols-3 gap-4">
{proposal.agentResults.map(result => (
<AgentResultCard
key={result.agent}
agent={result.agent}
confidence={result.confidence}
summary={result.summary}
/>
))}
</div>
{/* 共識分數 */}
<ConsensusScore score={proposal.consensusScore} />
{/* 最終提案 */}
<ProposalCard proposal={proposal} />
</GlassCard>
)
}
```
## 對應 leWOOOgo 積木
| 積木類別 | 新增模組 |
|---------|---------|
| **BRAIN** | `SecurityAgent` |
| **BRAIN** | `BlastRadiusAgent` |
| **BRAIN** | `ActionPlannerAgent` |
| **BRAIN** | `CoordinatorAgent` |
| **BRAIN** | `ConsensusEngine` |
## 後果
### 優點
1. **多視角分析** - 不同專家 Agent 各司其職
2. **共識決策** - 加權投票提高決策品質
3. **可解釋性** - 每個 Agent 的分析過程透明
4. **彈性擴展** - 可新增更多專家 Agent
5. **差異化** - 競品無此功能
### 缺點
1. **成本增加** - 多 Agent 呼叫增加 API 費用
2. **延遲增加** - 並行分析仍需等待最慢的 Agent
3. **複雜度** - 共識機制需要調優
### 風險
| 風險 | 緩解措施 |
|------|---------|
| API 成本爆炸 | 設定 Token 上限、快取策略 |
| Agent 意見衝突 | 共識引擎加權投票 |
| SDK 不穩定 | 先用 Anthropic SDK 模擬 |
## 與 leWOOOgo 整合 (ADR-003)
OpenClaw Agent Teams 作為 **BRAIN 積木** 整合進 leWOOOgo 架構:
```
┌─────────────────────────────────────────────────────────────────┐
│ leWOOOgo Engine │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 🧱 INPUT ──────→ 🧠 BRAIN ──────────────→ 📢 OUTPUT │
│ (Prometheus) │ (Telegram) │
│ │ │
│ ┌──────┴──────┐ │
│ │ OpenClawTeam │ ← NEW: Agent Teams │
│ │ (SDK-based) │ │
│ └──────┬──────┘ │
│ │ │
│ ┌──────┴──────┐ │
│ │ 🔧 ACTION │ │
│ │ K8sExecutor │ │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### BRAIN 積木介面實作
```python
# packages/lewooogo-brain/src/openclaw_team_plugin.py
from lewooogo_core.interfaces import AgentProvider, AgentInput, AgentOutput
class OpenClawTeamPlugin(AgentProvider):
"""
leWOOOgo BRAIN 積木: OpenClaw Agent Teams
符合 ADR-003 定義的 AgentProvider 介面
"""
id = "openclaw-agent-team"
name = "OpenClaw Agent Team"
version = "0.1.0"
category = "BRAIN"
def __init__(self):
self.team = OpenClawTeam()
async def initialize(self) -> None:
# 驗證 API Key
assert os.environ.get("ANTHROPIC_API_KEY"), "Missing ANTHROPIC_API_KEY"
async def process(self, input: AgentInput) -> AgentOutput:
proposal = await self.team.analyze_incident(input.payload)
return AgentOutput(
result=proposal,
confidence=proposal.consensus_score,
metadata={"agent_count": 3, "sdk_version": "0.1.50"},
)
def get_capabilities(self) -> list[str]:
return [
"security-analysis",
"blast-radius-analysis",
"action-planning",
"consensus-decision",
]
async def health_check(self) -> dict:
return {"status": "healthy", "sdk": "claude-agent-sdk"}
async def shutdown(self) -> None:
pass
```
## 與 ADR-006 整合 (AI 備援)
Agent Teams 整合現有 AI Fallback 策略:
```
優先級 1: Ollama (本地) → 簡單告警走 Ollama
優先級 2: Claude Agent SDK → 複雜告警走 Agent Teams
優先級 3: Gemini API → SDK 失敗時備援
優先級 4: 靜態回應
```
### 路由邏輯
```python
class OpenClawRouter:
async def route(self, incident: dict) -> Proposal:
# 根據告警複雜度選擇處理器
if self._is_simple_alert(incident):
# 簡單告警: Ollama 足夠
return await self.ollama_handler.analyze(incident)
else:
# 複雜告警: 使用 Agent Teams
try:
return await self.agent_team.analyze_incident(incident)
except ClaudeSDKError:
# SDK 失敗,降級到 Gemini
return await self.gemini_fallback.analyze(incident)
def _is_simple_alert(self, incident: dict) -> bool:
# 判斷邏輯: P3/P4 且影響單一服務 → 簡單
severity = incident.get("severity", "P3")
affected = incident.get("affected_services", [])
return severity in ["P3", "P4"] and len(affected) <= 1
```
## 實作計劃 (更新版)
| Phase | 內容 | 狀態 | 預估 |
|-------|------|------|------|
| 9.1 | ADR 審核 + SDK 研究 | ✅ 完成 | 0.5 天 |
| 9.2 | SDK 整合 + POC | 🔜 下一步 | 1 天 |
| 9.3 | 3 專家 Agent 實作 | | 2 天 |
| 9.4 | ConsensusEngine + leWOOOgo 整合 | | 1.5 天 |
| 9.5 | API 端點 + UI 呈現 | | 1.5 天 |
| 9.6 | 測試 + 文檔 + ADR-006 整合 | | 1 天 |
**總計: 7.5 天** (原估 10 天,因 SDK 簡化減少)
### Phase 9.2 POC 驗證項目
```bash
# 1. 安裝 SDK
cd apps/api && uv add claude-agent-sdk
# 2. 建立測試腳本
cat > scripts/test-agent-team.py << 'EOF'
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition
async def main():
# 簡單 Subagent 測試
options = ClaudeAgentOptions(
allowed_tools=["Agent"],
agents={
"test-agent": AgentDefinition(
description="測試 Agent",
prompt="回答問題並回傳 JSON",
tools=[],
)
},
)
async for msg in query(
prompt="使用 test-agent 回答: 2+2=?",
options=options,
):
print(msg)
asyncio.run(main())
EOF
# 3. 執行測試
python scripts/test-agent-team.py
```
## 相關 ADR
- ADR-003: leWOOOgo 模組架構 (BRAIN 積木)
- ADR-006: AI 備援策略 (Fallback 整合)
- ADR-001: MCP Protocol 採用 (SDK 支援 MCP)
## 參考資料
- [Claude Agent SDK Overview](https://platform.claude.com/docs/en/agent-sdk/overview)
- [Claude Agent SDK Quickstart](https://platform.claude.com/docs/en/agent-sdk/quickstart)
- [Claude Agent SDK Python GitHub](https://github.com/anthropics/claude-agent-sdk-python)
- [Claude Agent SDK Demos](https://github.com/anthropics/claude-agent-sdk-demos)
- [LangGraph + Claude Agent SDK 整合](https://www.mager.co/blog/2026-03-07-langgraph-claude-agent-sdk-ultimate-guide/)
## 變更記錄
| 日期 | 版本 | 變更 | 作者 |
|------|------|------|------|
| 2026-03-23 | v0.1 | 初稿提議 | AI 架構師 |
| 2026-03-23 | v0.2 | SDK 研究完成,加入具體整合方案 | AI 架構師 |