---
date: 2025-11-19T18:45:00+08:00
researcher: Claude (Sonnet 4.5)
git_commit: 7bdb6b004668efce3a7880939345c31b45122a9b
branch: main
repository: FWAuto
topic: "FWAuto 中 Claude Code SDK 的使用模式與架構分析"
tags: [research, codebase, ai, claude-code-sdk, architecture, prompt-management, workflow]
status: complete
last_updated: 2025-11-20
last_updated_by: Claude (Sonnet 4.5)
last_updated_note: "Finalized Open Questions decisions: system prompt strategy, chat behavior, dialog history, and AI nodes architecture (keeping current 3-node structure)"
---

# Research: FWAuto 中 Claude Code SDK 的使用模式與架構分析

**Date**: 2025-11-19T18:45:00+08:00
**Researcher**: Claude (Sonnet 4.5)
**Git Commit**: 7bdb6b004668efce3a7880939345c31b45122a9b
**Branch**: main
**Repository**: FWAuto

## Research Question

如何系統性地理解 FWAuto 使用 Claude Code SDK 的現況，以便在未來開發相關功能時保持一致性？具體包括：

1. 三個 AI nodes（ai_brain、log、chat）如何使用 SDK？
2. Prompt 管理機制如何運作？
3. SDK 配置模式有哪些差異？
4. System prompt 覆寫策略是什麼？
5. CLI 到 AI Node 的完整調用路徑為何？

## Summary

FWAuto 建立了一套成熟的 Claude Code SDK 集成架構，使用 **三種不同的 AI 使用模式** 來處理不同場景：

1. **單次執行模式**（ai_brain_node）- 使用 `query()` API，適合自動修復編譯錯誤和功能實作
2. **批次分析模式**（log_node）- 使用 `query()` + system prompt 覆寫，強制自動執行日誌分析
3. **互動對話模式**（chat_node）- 使用 `ClaudeSDKClient`，支援多輪對話和完整工具存取

這些模式透過 **LangGraph workflow** 協調執行，使用 **Jinja2 prompt templates** 管理 AI 行為，並透過 **platform-aware 配置** 確保安全性（限制可修改的目錄範圍）。

**核心設計原則**：
- **關注點分離**：SDK 使用、Prompt 管理、Workflow routing 各自獨立
- **可擴展性**：新增 AI 功能只需新增 prompt template 和對應的 task_type
- **安全性優先**：透過平台配置嚴格限制 AI 可修改的檔案範圍

## Detailed Findings

### 1. SDK API 使用模式

FWAuto 使用兩種不同的 Claude Agent SDK API：

#### 模式 A: `query()` 函數（單次執行）

**使用於**: ai_brain_node, log_node

```python
# 位置: src/fwauto/nodes/ai_brain.py:36-62
async def run_ai_brain():
    responses = []
    async for message in query(prompt=prompt, options=options):
        logger.debug(f"🧠 AI Brain: {message}")
        _print_message(message)
        responses.append(str(message))
    return {"success": True, "responses": responses}

# 執行方式
result = asyncio.run(run_ai_brain())
```

**特點**：
- 返回異步生成器（AsyncGenerator[Message]）
- 自動管理連線生命週期
- 適合一次性任務（程式碼修復、日誌分析）
- 使用 `asyncio.run()` 執行

#### 模式 B: `ClaudeSDKClient` 類（多輪對話）

**使用於**: chat_node

```python
# 位置: src/fwauto/nodes/chat.py:117-188
async with ClaudeSDKClient(options=options) as client:
    await client.connect()

    while True:
        user_input = await session.prompt_async()
        await client.query(user_input)

        async for msg in client.receive_response():
            _print_message(msg)
```

**特點**：
- 上下文管理器（`async with`）
- 顯式連線管理（`connect()`/`disconnect()`）
- 支援多輪對話，SDK 自動維護對話歷史
- 適合互動式場景（REPL-like）

**差異對照表**：

| 特性 | ai_brain_node | log_node | chat_node |
|------|---------------|----------|-----------|
| SDK API | `query()` | `query()` | `ClaudeSDKClient` |
| 執行環境 | `asyncio.run()` | `asyncio.run()` | Thread + `new_event_loop()` |
| 連線管理 | 自動 | 自動 | 顯式（connect/disconnect） |
| 對話狀態 | 無（單次） | 無（單次） | 有（跨輪次） |
| 消息流向 | 單向：AI→User | 單向：AI→User | 雙向：User↔AI |

### 2. Async/Await 處理模式

#### 模式 A: 直接 `asyncio.run()` 包裝

**位置**: src/fwauto/nodes/ai_brain.py:36-62

```python
def ai_brain_node(state: FirmwareState) -> dict[str, Any]:
    async def run_ai_brain():
        # 異步邏輯
        async for message in query(...):
            responses.append(str(message))
        return {"success": True, "responses": responses}

    result = asyncio.run(run_ai_brain())  # 同步調用異步函數
```

**優點**: 簡潔，適合單次執行
**限制**: 無法從已有 event loop 中呼叫

#### 模式 B: Thread 隔離 + `asyncio.new_event_loop()`

**位置**: src/fwauto/nodes/chat.py:78-114

```python
def run_in_thread():
    new_loop = asyncio.new_event_loop()
    asyncio.set_event_loop(new_loop)
    try:
        new_loop.run_until_complete(_run_chat_loop(...))
    finally:
        new_loop.close()

thread = threading.Thread(target=run_in_thread, daemon=False)
thread.start()
thread.join()
```

**設計考量**（程式碼註解）:
> Always use threading to avoid event loop conflicts. This is safer than trying to detect running loops, as some libraries may create event loops unexpectedly.

**優點**: 完全隔離，避免 event loop 衝突
**缺點**: Thread 開銷，無法與父層共享上下文

### 3. Response Streaming 處理

所有 AI nodes 使用統一的流式處理模式：

```python
# 位置: src/fwauto/nodes/ai_brain.py:49-54
responses = []
async for message in query(prompt=prompt, options=options):
    logger.debug(f"🧠 AI Brain: {message}")
    _print_message(message)       # 實時打印
    responses.append(str(message))  # 累積收集
```

**Message 處理流程**：

```
接收 Message 物件
  ↓
類型檢查 (isinstance(message, AssistantMessage))
  ↓
提取 TextBlock
  ↓
┌─ 實時列印到控制台
└─ 轉換為字串並累積
  ↓
返回完整 responses 列表
```

### 4. System Prompt 覆寫策略

#### 場景 1: Log Node 的強制執行模式

**位置**: src/fwauto/nodes/log.py:145-158

```python
system_override = (
    "CRITICAL: You are executing a specific automated task via SDK. "
    "This is NOT an interactive consultation. "
    "Execute the user's prompt instructions IMMEDIATELY and EXACTLY. "
    "DO NOT ask clarifying questions. DO NOT provide help menus. "
    "JUST DO THE TASK."
)

options = ClaudeAgentOptions(
    allowed_tools=["Read", "Grep"],
    permission_mode="bypassPermissions",
    add_dirs=[log_dir],
    system_prompt=system_override  # 直接覆寫
)
```

**設計理念**：
- 日誌分析是非互動式自動任務
- 禁止 AI 詢問澄清問題，確保流程確定性
- 工具限制為只讀（Read, Grep）

#### 場景 2: Chat Node 的 Template-Based Prompt

**位置**: src/fwauto/nodes/chat.py:50-63

```python
system_prompt = prompt_manager.get_prompt("chat", dict(state))

options = ClaudeAgentOptions(
    allowed_tools=["Read", "Write", "Edit", "Glob", "Grep", "Bash"],
    permission_mode="bypassPermissions",
    add_dirs=[project_root],
    cwd=project_root,
    system_prompt=system_prompt  # 使用 template，無覆寫
)
```

**設計理念**：
- 互動式設計，允許用戶對話
- 完整權限（所有 6 個工具）
- Prompt 內容來自 `prompts/chat.md`

#### 場景 3: AI Brain 的預設模式

**位置**: src/fwauto/nodes/ai_brain.py:109-133

```python
options = ClaudeAgentOptions(
    allowed_tools=["Read", "Write", "Edit", "MultiEdit", "Glob", "Grep", "Bash"],
    permission_mode="bypassPermissions",
    add_dirs=[project_root],
    cwd=project_root
    # 無 system_prompt 參數 = 使用 SDK 預設行為
)
```

**對照表**：

| Node | System Prompt 來源 | 覆寫類型 | 用途 |
|------|--------------------|----------|------|
| log_node | 硬編碼字串 | 強制執行覆寫 | 禁止互動諮詢 |
| chat_node | prompts/chat.md | Template 驅動 | 專案上下文感知 |
| ai_brain_node | （無） | 使用預設 | 依賴 prompt template 內容 |

### 5. Prompt Template 系統架構

#### Prompt Manager 架構

**位置**: src/fwauto/prompt_manager.py:14-106

```python
class PromptManager:
    def __init__(self):
        self.prompts_dir = Path(__file__).parent.parent.parent / "prompts"
        self.env = Environment(
            loader=FileSystemLoader(self.prompts_dir),
            undefined=StrictUndefined  # 嚴格模式
        )
```

**StrictUndefined 設定**：確保所有 template 變數必須在 state 中存在，提高可靠性。

#### Template 清單與用途

| 檔案 | 用途 | 主要變數 | System Prompt 覆寫 |
|------|------|---------|-------------------|
| `log.md` | UART 日誌分析 | log_file_path, user_prompt | ✅ 強制執行 |
| `chat.md` | 互動式對話 | project_root, platform | ❌ Template 驅動 |
| `fix_build_errors.md` | 自動修復編譯錯誤 | build_errors, build_log, platform_config | ❌ Template 驅動 |
| `implement_feature.md` | 實作新功能 | user_prompt, platform_config | ❌ Template 驅動 |

#### Jinja2 變數注入系統

**位置**: src/fwauto/prompt_manager.py:63-86

**變數來源**：

1. **系統變數**：
   - `cwd`: 當前工作目錄
   - `username`: 系統用戶名
   - `os_name`: 作業系統（Windows/Linux/Darwin）
   - `timestamp`: 當前時間戳

2. **平台配置變數**：
   ```python
   "platform_config": {
       "name": "STM32 + Keil",
       "forbidden_dirs": ["SYSTEM", "CORE", "HALLIB", "HARDWARE"],
       "allowed_dirs": ["USER"],
       "stop_conditions": [...]
   }
   ```

3. **Workflow State 變數**（直接傳遞）：
   - `project_root`, `build_errors`, `build_log`, `iteration`, `max_retries`
   - `user_prompt`, `log_file_path`, 等

#### Platform-Aware Template 範例

**位置**: prompts/fix_build_errors.md:24-42

```jinja2
## 🚫 嚴格禁止區域
{% for dir in platform_config.forbidden_dirs -%}
- **絕對不可修改**：`{{dir}}/` 目錄下任何檔案
{% endfor %}

## ✅ 唯一允許操作
{% for dir in platform_config.allowed_dirs -%}
- **只能修改**：`{{dir}}/` 目錄下的檔案
{% endfor %}

## ⚠️ 遇到以下情況立即停止修復
{% for condition in platform_config.stop_conditions -%}
- {{condition}}
{% endfor %}
```

這確保 AI 只能修改用戶程式碼目錄（如 `USER/`），不會誤觸系統核心檔案。

### 6. CLI 到 AI Node 的完整調用路徑

#### 路徑 A: `fwauto feat`（AI 輔助功能實作）

```
CLI: feat(prompt)
  [src/fwauto/cli.py:352-392]
  ↓
FirmwareState 初始化:
  - execution_mode = "feat"
  - user_prompt = 功能描述
  - mode_metadata = {deploy_after_build: True}
  ↓
run_workflow(state)
  ↓
route_from_start() → init_project_node
  [src/fwauto/graph.py:32-65]
  ↓
route_after_init() → ai_brain_node
  ↓
ai_brain_node:
  1. analyze_task_type(state) → "implement_feature"
  2. prompt_manager.get_prompt("implement_feature", state)
  3. query(prompt, options) → AI 實作程式碼
  4. 返回 build_status="success"
  [src/fwauto/nodes/ai_brain.py:24-73]
  ↓
route_after_ai_brain() → build_node
  ↓
build_node: make build
  [src/fwauto/nodes/build.py]
  ↓
route_after_build():
  - Success → deploy_node → END
  - Error + iteration < 3 → ai_brain_node (auto-fix)
  [src/fwauto/graph.py:89-109]
```

#### 路徑 B: `fwauto log`（日誌分析）

```
CLI: log_command(prompt, log_path)
  [src/fwauto/cli.py:301-349]
  ↓
FirmwareState:
  - execution_mode = "log"
  - user_prompt = 分析問題
  - log_file_path = 日誌路徑
  ↓
route_from_start() → init_project_node → log_node
  ↓
log_node:
  1. 路徑驗證（遠端/本地）
  2. 遠端檔案 → SCP 下載到 .fwauto/logs/
  3. prompt_manager.get_prompt("log", state)
  4. system_override = "CRITICAL: Execute immediately..."
  5. query(prompt, options) → 日誌分析結果
  [src/fwauto/nodes/log.py:22-175]
  ↓
END (log 總是直接結束)
```

#### 路徑 C: `fwauto chat`（互動對話）

```
CLI: chat()
  [src/fwauto/cli.py:395-436]
  ↓
FirmwareState:
  - execution_mode = "chat"
  - command = "chat"
  ↓
route_from_start() → init_project_node → chat_node
  ↓
chat_node:
  1. system_prompt = prompt_manager.get_prompt("chat", state)
  2. 初始化 ChatLogger
  3. Thread 隔離執行:
     async with ClaudeSDKClient(options) as client:
       - await client.connect()
       - while True:
           user_input = await session.prompt_async()
           await client.query(user_input)
           async for msg in client.receive_response():
               _print_message(msg)
  4. 對話記錄保存到 .fwauto/logs/chat_*.json
  [src/fwauto/nodes/chat.py:26-114]
  ↓
END
```

### 7. AI Auto-Fix Loop 機制

**觸發條件**: Build 失敗時自動啟動

**完整流程圖**：

```mermaid
graph TD
    A["fwauto feat '實作摩斯密碼'"] --> B["ai_brain_node<br/>task_type=implement_feature"]
    B --> C["build_node<br/>make build"]
    C --> D{"build_status?"}

    D -->|success| E["deploy_node"]
    E --> F["END ✓"]

    D -->|error| G{"iteration < max_retries<br/>(3次)?"}
    G -->|yes| H["ai_brain_node<br/>task_type=fix_build_errors<br/>iteration++"]
    G -->|no| I["END ✗<br/>Max retries reached"]

    H --> J["分析 build_errors"]
    J --> K["修改程式碼<br/>(只能修改 USER/ 目錄)"]
    K --> C

    style B fill:#fff3e0
    style H fill:#ffebee
    style E fill:#e8f5e9
```

**Task Type 選擇邏輯**:

**位置**: src/fwauto/nodes/ai_brain.py:83-106

```python
def analyze_task_type(state: FirmwareState) -> str:
    # 優先級順序
    if state.get("build_errors") and state.get("build_status") == "error":
        return "fix_build_errors"          # Auto-fix 觸發
    elif state.get("execution_mode") == "feat":
        return "implement_feature"
    elif state.get("execution_mode") == "fix":
        return "fix_bug"                   # (prompt 未創建)
    elif state.get("execution_mode") == "natural":
        return "natural_language"          # (prompt 未創建)
    else:
        return "general_codegen"           # (prompt 未創建)
```

**重試限制**：

**位置**: src/fwauto/graph.py:99-109

```python
current_iteration = state.get("iteration", 0)
max_retries = state.get("max_retries", 3)

if current_iteration < max_retries:
    return "ai_brain"  # 最多重試 3 次 (iteration=0,1,2)
else:
    return "end"       # iteration=3 時終止
```

### 8. ClaudeAgentOptions 配置模式

**三種配置策略對照**：

| Node | allowed_tools | permission_mode | add_dirs | system_prompt |
|------|---------------|-----------------|----------|---------------|
| ai_brain | Read, Write, Edit, MultiEdit, Glob, Grep, Bash | bypassPermissions | [project_root] | （無） |
| log | Read, Grep | bypassPermissions | [log_dir] | 強制執行覆寫 |
| chat | Read, Write, Edit, Glob, Grep, Bash | bypassPermissions | [project_root] | prompts/chat.md |

**配置原則**：
- **最小權限原則**：log_node 只需讀取，故只給 Read/Grep
- **安全範圍**：add_dirs 限制 AI 只能存取特定目錄
- **工具選擇**：根據任務需求選擇工具集（MultiEdit 只在 ai_brain 使用）

## Code References

| 檔案路徑 | 行號 | 說明 |
|---------|------|------|
| `src/fwauto/nodes/ai_brain.py` | 24-161 | AI Brain Node 完整實作 |
| `src/fwauto/nodes/ai_brain.py` | 83-106 | Task type 分析邏輯 |
| `src/fwauto/nodes/ai_brain.py` | 109-133 | AI options 配置 |
| `src/fwauto/nodes/log.py` | 22-176 | Log Analysis Node |
| `src/fwauto/nodes/log.py` | 145-158 | System prompt 覆寫 |
| `src/fwauto/nodes/log.py` | 265-344 | 遠端日誌下載（SCP） |
| `src/fwauto/nodes/chat.py` | 26-114 | Chat Node 主邏輯 |
| `src/fwauto/nodes/chat.py` | 78-114 | Thread 隔離執行 |
| `src/fwauto/nodes/chat.py` | 117-188 | 互動式聊天迴圈 |
| `src/fwauto/prompt_manager.py` | 14-106 | Prompt Manager 完整實作 |
| `src/fwauto/prompt_manager.py` | 63-86 | 變數注入邏輯 |
| `src/fwauto/cli.py` | 82-436 | CLI 命令定義 |
| `src/fwauto/graph.py` | 16-190 | LangGraph workflow 定義 |
| `src/fwauto/graph.py` | 89-109 | Build 後的路由邏輯 |
| `src/fwauto/graph.py` | 111-115 | AI Brain 後的路由邏輯 |
| `src/fwauto/state.py` | 6-76 | FirmwareState schema |
| `prompts/fix_build_errors.md` | 1-118 | 編譯錯誤修復 prompt |
| `prompts/implement_feature.md` | 1-118 | 功能實作 prompt |
| `prompts/log.md` | 1-18 | 日誌分析 prompt |
| `prompts/chat.md` | 1-25 | 互動對話 prompt |

## Architecture Insights

### 設計模式發現

#### 1. Supervisor Pattern（監督者模式）

**位置**: ai_brain_node

ai_brain_node 作為 supervisor，根據當前狀態分析任務類型並路由到對應的 prompt template：

```
ai_brain_node
  ↓
analyze_task_type(state)
  ├─ build_errors → fix_build_errors.md
  ├─ execution_mode=feat → implement_feature.md
  ├─ execution_mode=fix → fix_bug.md
  └─ default → general_codegen.md
```

這種模式讓新增 AI 功能變得簡單：只需新增 prompt template 和對應的條件判斷。

#### 2. Template Method Pattern（模板方法模式）

所有 AI nodes 遵循相同的執行模式：

```python
def ai_node(state: FirmwareState) -> dict[str, Any]:
    # 1. 載入 prompt
    prompt = prompt_manager.get_prompt(task_type, state)

    # 2. 配置 SDK options
    options = get_ai_options(state)

    # 3. 執行 AI query
    result = asyncio.run(execute_ai(prompt, options))

    # 4. 更新 state
    return update_state_from_result(state, result)
```

這確保所有 AI 操作的一致性。

#### 3. Strategy Pattern（策略模式）

**System prompt 覆寫策略**：

- **策略 A**（log_node）：硬編碼強制執行模式
- **策略 B**（chat_node）：Template-based 專案感知
- **策略 C**（ai_brain_node）：預設行為（依賴 prompt template）

每個策略根據使用場景選擇（自動化 vs 互動式）。

#### 4. Anchor-Based Project Discovery

**位置**: src/fwauto/nodes/init.py, src/fwauto/config.py

類似 Git 的 `.git/` 機制，FWAuto 從當前目錄向上搜尋 `.fwauto/`：

```python
def find_project_root(start_path: Path | None = None) -> Path | None:
    current = start_path or Path.cwd()

    while current != current.parent:
        fwauto_dir = current / ".fwauto"
        if fwauto_dir.is_dir():
            return current
        current = current.parent

    return None
```

**優點**：使用者可在任何子目錄執行命令，無需記憶專案路徑。

### 擴展性分析

#### 如何新增新的 AI 功能？

**Step 1**: 建立新的 prompt template

```bash
# prompts/refactor_code.md
你是 {{platform_config.name}} 韌體開發專家，負責重構程式碼。

## 重構目標
{{user_prompt}}

## 允許修改的目錄
{% for dir in platform_config.allowed_dirs -%}
- ✅ `{{dir}}/`
{% endfor %}
```

**Step 2**: 更新 task type 分析邏輯

```python
# src/fwauto/nodes/ai_brain.py
def analyze_task_type(state: FirmwareState) -> str:
    # 新增條件
    if state.get("execution_mode") == "refactor":
        return "refactor_code"
    # ... 其他條件
```

**Step 3**: 新增 CLI 命令

```python
# src/fwauto/cli.py
@app.command()
def refactor(prompt: str):
    state: FirmwareState = {
        "execution_mode": "refactor",
        "user_prompt": prompt,
        # ... 其他欄位
    }
    run_workflow(state)
```

**Step 4**: 更新 execution_mode 定義

```python
# src/fwauto/state.py
execution_mode: Literal[
    "build", "deploy", "run", "log", "feat", "chat", "refactor", "error"
]
```

**無需修改**：
- Workflow graph（自動路由）
- Prompt manager（自動載入）
- SDK 配置邏輯（繼承 ai_brain 模式）

#### Platform 擴展方向

目前只支援 `stm32_keil` 平台，未來可擴展：

```python
# src/fwauto/platforms.py
PLATFORM_CONFIGS = {
    "stm32_keil": {...},
    "esp32_idf": {
        "name": "ESP32 + ESP-IDF",
        "forbidden_dirs": ["components/esp32", "components/freertos"],
        "allowed_dirs": ["main", "components/custom"],
        "stop_conditions": [...]
    },
    "nordic_nrf": {...}
}
```

所有 prompt templates 會自動適配新平台配置（透過 Jinja2 迭代）。

### 技術債務與限制

#### 1. 缺失的 Prompt Templates

**位置**: src/fwauto/nodes/ai_brain.py:98-105

```python
elif state.get("execution_mode") == "fix":
    return "fix_bug"              # ❌ prompts/fix_bug.md 不存在
elif state.get("execution_mode") == "natural":
    return "natural_language"     # ❌ prompts/natural_language.md 不存在
else:
    return "general_codegen"      # ❌ prompts/general_codegen.md 不存在
```

這些 execution_mode 也未在 `src/fwauto/state.py` 的 Literal 定義中。

#### 2. Execution Mode 不一致

**State 定義**（src/fwauto/state.py:53-56）:
```python
execution_mode: Literal["build", "deploy", "run", "log", "feat", "chat", "error"]
```

**AI Brain 檢查**（src/fwauto/nodes/ai_brain.py:98-102）:
```python
elif state.get("execution_mode") == "fix":      # 未定義在 state.py
elif state.get("execution_mode") == "natural":  # 未定義在 state.py
```

這可能導致未預期的行為。

#### 3. System Prompt 覆寫策略不統一

只有 log_node 使用硬編碼覆寫，其他 nodes 依賴 template 或預設行為。這可能導致：

- 新手開發者不清楚何時應該覆寫 system prompt
- 缺少明確的決策指南

**建議方向**（非具體實施步驟）：
- 建立 system prompt 覆寫的使用場景文件
- 定義覆寫的最佳實踐（何時用硬編碼 vs template）

## Open Questions

### 1. Prompt Template 版本控制策略

**問題**：當 prompt templates 需要調整時（例如修改安全限制、新增指示），如何確保變更不會破壞現有功能？

**決策**：✅ 暫時不處理
- 目前 prompt 變更頻率低，手動測試即可
- 未來若變更頻繁，再考慮引入自動化測試機制

### 2. 多平台支援的優先順序

**問題**：目前只支援 `stm32_keil`，擴展到其他平台（ESP32, nRF52, Raspberry Pi）的優先順序為何？

**決策**：🎯 平台無關設計優先
- **目前主要平台**：AM62X (TI Sitara)
- **設計原則**：AI 應支援多種平台，但不綁定特定平台
- **架構方向**：
  - 保持 platform_config 的抽象性
  - 避免在 prompt templates 中硬編碼平台特定資訊
  - 讓 AI 能夠根據專案結構自適應不同平台

**技術可行性**：
- 現有的 `platform_config` 機制已支援多平台擴展
- Jinja2 template 的迭代語法可自動適配不同平台配置
- 只需為新平台定義 `allowed_dirs`、`forbidden_dirs`、`stop_conditions`

### 3. AI Auto-Fix 成功率監控

**問題**：目前沒有追蹤 AI auto-fix 的成功率（3 次重試內修復成功的比例）。

**決策**：✅ 暫時不需要
- 手動觀察即可，不需要自動化監控
- 未來若需要優化 auto-fix 邏輯，再考慮建立統計系統

### 4. System Prompt 覆寫的使用時機

**問題**：何時應該使用硬編碼 system prompt 覆寫？何時應該依賴 template？

**當前狀況**：
- log_node 使用硬編碼覆寫（強制執行）
- chat_node 使用 template（專案感知）
- ai_brain_node 使用預設（無覆寫）

**決策結果**：✅ 已確定策略（2025-11-20）
- **核心原則**：所有 AI 操作都應與 FWAuto 功能深度綁定
- **具體要求**：
  - 提到「編譯」→ 建議使用者執行 `fwauto build`
  - 提到「部署/燒錄」→ 建議使用者執行 `fwauto deploy`
  - 提到「分析錯誤/系統行為」→ 建議使用 log_node 相關功能

**實施策略**：
1. **Chat 模式行為**：
   - ✅ **建議使用者執行**：AI 告知使用者「請執行 `fwauto build`」，但不自動執行
   - 理由：使用者保有控制權，避免非預期的編譯/部署

2. **超出範圍處理**：
   - ✅ **提供指引與教學**：當功能超出 FWAuto 支援範圍時，AI 提供手動操作的詳細指引
   - 理由：教育性，幫助使用者學習如何自行處理

**實施方式**：
- 使用 **Template 方向**：在 `prompts/chat.md` 中加入明確指示
- 範例：「當使用者需要編譯時，建議執行 `fwauto build`，不要直接使用 Bash 執行編譯命令」
- 優點：保持彈性，易於調整和維護

### 5. 對話歷史的長期儲存與檢索

**問題**：chat_node 的對話記錄目前保存在 `.fwauto/logs/chat_*.json`，但沒有檢索或重播機制。

**決策**：✅ 暫時不需要（2025-11-20）
- 目前僅需要保留記錄作為備份
- 未來再考慮進階功能（RAG 檢索、對話摘要、問題模式識別等）

**技術可行性**（備忘）：
- 目前 ChatLogger 已儲存完整對話（JSON 格式）
- 若未來需要，可整合向量資料庫（如 ChromaDB）進行語義搜尋
- 可利用 LangChain 的 ConversationBufferMemory 機制

## Follow-up Research 2025-11-20T16:30:00+08:00

### AI Nodes 整合可行性分析

**研究問題**：評估將 log_node 和 chat_node 的 AI 功能整合到 ai_brain_node 的可行性。

**研究者**: Claude (Sonnet 4.5)
**Git Commit**: 6847735c09db7fce8d521aa9d9c54da3b2faba08
**Branch**: ai

#### 整合提議：統一 AI 處理層

**核心概念**：
- 將所有 AI 驅動的任務路由到單一 `ai_brain_node`
- log 和 chat 作為 task types（類似現有的 `implement_feature` 和 `fix_build_errors`）
- 保持統一的 SDK 使用模式和 prompt 管理機制

#### 架構對照圖

**現況**（三個獨立 AI nodes）：
```mermaid
graph TD
    CLI["CLI Commands"] --> Route["Workflow Routing"]
    Route -->|feat| AIBrain["ai_brain_node<br/>query() API"]
    Route -->|log| LogNode["log_node<br/>query() API + system override"]
    Route -->|chat| ChatNode["chat_node<br/>ClaudeSDKClient"]

    AIBrain --> PromptMgr["PromptManager"]
    LogNode --> PromptMgr
    ChatNode --> PromptMgr

    style AIBrain fill:#fff3e0
    style LogNode fill:#e3f2fd
    style ChatNode fill:#f3e5f5
```

**提議**（統一到 ai_brain_node）：
```mermaid
graph TD
    CLI2["CLI Commands"] --> Route2["Workflow Routing"]
    Route2 -->|feat| AIBrain2["ai_brain_node"]
    Route2 -->|log| AIBrain2
    Route2 -->|chat| AIBrain2

    AIBrain2 --> Analyze["analyze_task_type()"]
    Analyze -->|feat| Feat["implement_feature.md"]
    Analyze -->|fix| Fix["fix_build_errors.md"]
    Analyze -->|log| Log["log.md"]
    Analyze -->|chat| Chat2["chat.md"]

    style AIBrain2 fill:#fff3e0
    style Analyze fill:#ffe0b2
```

#### 技術差異分析

| 特性 | log_node | chat_node | 整合到 ai_brain 的挑戰 |
|------|---------|-----------|---------------------|
| **SDK API** | `query()` | `ClaudeSDKClient` | ⚠️ 需支援兩種 API 模式 |
| **執行模式** | 單次執行 | 多輪對話 | ⚠️ ai_brain 目前只支援單次執行 |
| **連線管理** | 自動 | 顯式（connect/disconnect） | ⚠️ 需處理長連線生命週期 |
| **對話狀態** | 無狀態 | 有狀態（SDK 維護） | ⚠️ 需引入狀態管理邏輯 |
| **System Prompt** | 硬編碼強制執行 | Template 驅動 | ✅ 可透過 task_type 選擇策略 |
| **工具權限** | Read/Grep（限制） | Read/Write/Edit/Glob/Grep/Bash（完整） | ✅ 可透過 task_type 配置 |
| **Thread 隔離** | ❌ | ✅（避免 event loop 衝突） | ⚠️ 需處理 chat 模式的 thread 執行 |
| **Workflow 路由** | log → END | chat → END | ✅ 可統一為 ai_brain → END/build |

#### Pros: 整合到 ai_brain_node 的優勢

##### ✅ 架構簡化
- **統一入口**：所有 AI 任務都經過 `ai_brain_node`，邏輯集中
- **一致的模式**：統一的 prompt 管理、SDK 配置、錯誤處理
- **減少重複程式碼**：
  - 目前三個 nodes 都有類似的 `_print_message()` 函數（log.py:199-209, chat.py:191-201, ai_brain.py:76-80）
  - 都使用 `prompt_manager.get_prompt()` 載入 templates
  - 都需要配置 `ClaudeAgentOptions`

##### ✅ 可維護性提升
- **單一責任**：ai_brain 成為唯一的 AI 協調者（Supervisor Pattern）
- **擴展性**：新增 AI 功能只需新增 task_type，無需新增 node
- **測試簡化**：只需測試一個 node 的多種模式，而非三個獨立 nodes

##### ✅ 一致的使用者體驗
- 所有 AI 驅動的功能都使用相同的錯誤處理邏輯
- 統一的 logging 格式和訊息呈現
- 一致的重試機制（如果需要）

#### Cons: 整合的挑戰與代價

##### ❌ 技術複雜度增加

**問題 1：雙模式 SDK 使用**
- 目前 ai_brain 只使用 `query()` API（單次執行）
- chat 需要 `ClaudeSDKClient`（長連線、多輪對話）
- **衝突**：兩種 API 模式在同一個 node 中並存會增加邏輯複雜度

**問題 2：Async/Thread 執行模式差異**
- ai_brain + log：`asyncio.run()` 簡單包裝
- chat：`threading.Thread` + `new_event_loop()`（避免 event loop 衝突）
- **衝突**：需要在 ai_brain 中判斷何時使用 thread 隔離

**問題 3：狀態管理**
- ai_brain + log：無狀態（每次執行獨立）
- chat：有狀態（SDK 自動維護對話歷史）
- **衝突**：`FirmwareState` 目前不支援對話歷史，需擴展 state schema

##### ❌ 單一 Node 責任過重

**違反關注點分離原則**：
- ai_brain 需要處理：
  - 單次程式碼生成（implement_feature）
  - 編譯錯誤修復（fix_build_errors）
  - 日誌分析（log）
  - 互動式對話（chat）
- 每個任務有不同的：
  - 執行模式（單次 vs 多輪）
  - 工具權限（限制 vs 完整）
  - 錯誤處理策略
  - 成功標準

**可讀性下降**：
- `ai_brain_node()` 函數會變得非常長且複雜
- 需要大量的條件判斷來區分不同模式

##### ❌ 測試複雜度爆炸

**測試案例數量**：
- 目前：每個 node 獨立測試（3 個測試套件）
- 整合後：需要測試所有 task_type 的組合
  - `implement_feature` + build success/fail
  - `fix_build_errors` + iteration loop
  - `log` + 遠端/本地檔案
  - `chat` + 多輪對話、exit 條件

**Mock 複雜度**：
- 需要 mock 兩種不同的 SDK API（`query()` vs `ClaudeSDKClient`）
- 需要 mock thread 執行環境

##### ❌ 錯誤隔離降低

**現況**：
- log_node 失敗不影響 ai_brain
- chat_node 異常不影響 build workflow

**整合後**：
- 所有 AI 功能共用同一個 node
- 一個 task_type 的 bug 可能影響其他功能
- 錯誤處理邏輯更難隔離

#### 技術可行性評估

##### ✅ 可行的整合點

1. **Prompt 管理**：已統一使用 `PromptManager`，無需變更
2. **System Prompt 策略**：可透過 task_type 選擇不同策略
3. **工具權限配置**：可透過 task_type 動態配置 `allowed_tools`

##### ⚠️ 需要重大重構的部分

1. **SDK API 模式切換**：
   ```python
   # ai_brain_node 需要根據 task_type 選擇 API
   if task_type == "chat":
       # 使用 ClaudeSDKClient + thread 隔離
       result = run_chat_mode(state)
   else:
       # 使用 query() + asyncio.run()
       result = run_query_mode(state)
   ```

2. **State Schema 擴展**：
   ```python
   # FirmwareState 需要新增對話歷史支援
   chat_history: list[Message] | None
   chat_session_active: bool
   ```

3. **Workflow Routing 調整**：
   ```python
   # graph.py 需要調整路由邏輯
   # 目前：log → END, chat → END
   # 整合後：ai_brain → (根據 task_type 決定下一步)
   ```

#### 架構權衡：關注點分離 vs 統一入口

| 層面 | 現況（三個獨立 nodes） | 整合（單一 ai_brain） |
|------|---------------------|---------------------|
| **關注點分離** | ✅ 每個 node 職責明確 | ❌ ai_brain 責任過重 |
| **程式碼重複** | ❌ 重複的 SDK 配置邏輯 | ✅ 統一的 SDK 配置 |
| **可讀性** | ✅ 簡單直觀 | ❌ 複雜的條件分支 |
| **測試性** | ✅ 獨立測試 | ❌ 組合測試複雜 |
| **錯誤隔離** | ✅ 獨立失敗 | ❌ 共享風險 |
| **擴展性** | ❌ 新增功能需新增 node | ✅ 新增 task_type 即可 |

#### 建議方向

基於 KISS 原則和架構權衡，建議採用 **折衷方案**：

##### 🎯 方案 A：保持現狀（推薦）

**理由**：
- log 和 chat 的執行模式與 ai_brain 根本不同（單次 vs 多輪）
- 整合會引入不必要的複雜度
- 目前的重複程式碼（`_print_message()`）可透過共用 utility 函數解決

**改進方向**：
- 提取共用邏輯到 `ai_utils.py`（print_message, configure_sdk_options）
- 保持三個 nodes 獨立，各司其職

##### 🎯 方案 B：部分整合（考慮）

**做法**：
- 只整合 log_node 到 ai_brain（因為都是 `query()` API）
- chat_node 保持獨立（因為使用不同的 SDK API 和執行模式）

**優點**：
- 減少一個 node
- log 和 ai_brain 使用相同的執行模式，整合較簡單

**缺點**：
- log 的「強制執行」system prompt 覆寫邏輯會進入 ai_brain
- 仍然需要處理 log 的特殊路由（log → END）

##### ❌ 方案 C：完全整合（不推薦）

**理由**：
- 技術複雜度遠超過收益
- 違反單一職責原則
- 測試和維護成本大幅增加

#### 決策結果

**✅ 選擇方案 A：保持現狀**（2025-11-20）

- **決策理由**：
  - log 和 chat 的執行模式與 ai_brain 根本不同（單次 vs 多輪）
  - 整合會引入不必要的複雜度，違反 KISS 原則
  - 遵循關注點分離原則，每個 node 職責明確

- **改進方向**：
  - 已提取共用邏輯到 `ai_utils.py`（print_message, configure_sdk_options）
  - 保持三個 nodes 獨立，各司其職
  - 共用函數避免過度抽象，只提取明確重複的邏輯

- **未來擴展原則**：
  - 新增 AI 功能優先考慮加入 ai_brain 作為新的 task_type
  - 只有在執行模式根本不同時（如需要長連線、多輪對話），才建立新的 node
  - 範例：`debug`、`refactor` 等功能應加入 ai_brain，而非建立新 node

## Related Research

（目前尚無相關研究文件）

未來可能的研究方向：
- LangGraph workflow optimization
- Prompt engineering best practices for firmware development
- AI-assisted debugging strategies
- AI nodes consolidation architecture patterns
