開發日誌 #2 - 整合設計與實施
整合目標
在前一篇日誌中,我們看到了兩個工具的誕生和各自的優勢。現在,讓我們看看如何將它們整合在一起。
整合的核心目標:
- ✅ 讓 jira_poller 獲得 automation_tool 的深度分析能力
- ✅ 讓 automation_tool 的 AI 能力融入實際工作流
- ✅ 保持兩個系統的獨立性(可單獨使用)
- ✅ 實現優雅降級(automation_tool 不可用時仍可運行)
- ✅ 最小化代碼改動
整合方案設計
方案對比
方案 A:直接引用
# 在 jira_poller 中
automation_tool_path = Path(__file__).parent.parent.parent / "automation_tool"
sys.path.insert(0, str(automation_tool_path))
from requirement_analyzer import RequirementAnalyzer
from system_designer import SystemDesigner優點:
- ✅ 快速實現
- ✅ 靈活修改
- ✅ 保持獨立性
- ✅ 開發期間方便調試
缺點:
- ❌ 路徑依賴
- ❌ 不夠優雅
方案 B:打包成 pip package
cd automation_tool
pip install -e .
# 在 jira_poller 中
from automation_tool import RequirementAnalyzer, SystemDesigner優點:
- ✅ 標準化
- ✅ 版本管理
- ✅ 依賴聲明
缺點:
- ❌ 開發期間修改麻煩
- ❌ 需要維護 setup.py
- ❌ 過度工程化
最終選擇
選擇方案 A,原因:
- 兩個專案仍在快速迭代
- 經常需要同步修改
- 暫不需要對外發布
- 降級機制可處理路徑問題
未來計劃:穩定後考慮方案 B
整合架構設計
系統架構圖
┌─────────────────────────────────────────────────────────┐
│ Jira Cloud │
│ (任務管理、評論、審批指令) │
└───────────────────────┬─────────────────────────────────┘
│
│ Jira REST API
│
┌───────────────────────▼─────────────────────────────────┐
│ jira_poller │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ task_poller.py │ │
│ │ (主輪詢器 - 30 秒輪詢) │ │
│ └──────────────────┬──────────────────────────────┘ │
│ │ │
│ ┌──────────────────▼──────────────────────────────┐ │
│ │ orchestrator.py │ │
│ │ (Agent 編排器) │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────┐ │ │
│ │ │ _init_automation_tool() │ │ │
│ │ │ - 檢查 automation_tool 可用性 │ │ │
│ │ │ - 初始化 RequirementAnalyzer │ │ │
│ │ │ - 初始化 SystemDesigner │ │ │
│ │ └──────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────┐ │ │
│ │ │ 需求階段 │ │ │
│ │ │ _ai_generate_requirements() │ │ │
│ │ │ ├─ automation_tool? (優先) │ │ │
│ │ │ ├─ ai_provider? (降級1) │ │ │
│ │ │ └─ placeholder (降級2) │ │ │
│ │ └──────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────┐ │ │
│ │ │ 設計階段 │ │ │
│ │ │ _ai_generate_design() │ │ │
│ │ │ ├─ automation_tool? (優先) │ │ │
│ │ │ └─ placeholder (降級) │ │ │
│ │ └──────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────▼──────────────────────────────┐ │
│ │ git_manager.py │ │
│ │ (Git Worktree 管理) │ │
│ └──────────────────────────────────────────────────┘ │
└──────────────────────┬──────────────────────────────────┘
│
│ sys.path.insert()
│
┌──────────────────────▼──────────────────────────────────┐
│ automation_tool │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ requirement_analyzer/ │ │
│ │ - RequirementAnalyzer.analyze() │ │
│ │ - 生成 9 章節需求文檔 │ │
│ │ - 支援反饋循環 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ system_designer/ │ │
│ │ - SystemDesigner.design() │ │
│ │ - Tool Use API (4 個探索工具) │ │
│ │ - 迭代式專案探索 (最多 30 輪) │ │
│ │ - 生成 11 章節設計文檔 │ │
│ └─────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────┘
數據流程
Jira 任務
↓
【jira_poller 接收】
- task.key: "AG-123"
- task.summary: "優化主選單載入速度"
- task.description: "主選單載入需要 3-5 秒..."
↓
【創建 Git Worktree】
- workspace/project-wt-AG-123/
↓
【需求階段】
↓
構建需求文本 (jira_poller)
↓
調用 RequirementAnalyzer.analyze() (automation_tool)
- requirement_text: 包含 Jira 任務信息
- project_name: "AG-123"
↓
返回需求文檔 (automation_tool)
- 9 章節 Markdown 文檔
- ~2500 字
↓
保存文件 (jira_poller)
- .ai-agent/AG-123/req.md
- Git commit: "[AG-123] 需求分析"
- Jira 評論: 發表需求文檔
↓
【人工審批】@approved
↓
【設計階段】
↓
獲取需求文件路徑 (jira_poller)
- req_file: workspace/project-wt-AG-123/.ai-agent/AG-123/req.md
↓
調用 SystemDesigner.design() (automation_tool)
- requirement_file: req.md 路徑
- project_root: workspace/project-wt-AG-123/ ← 關鍵!
- output_dir: workspace/project-wt-AG-123/.ai-agent/
- user_id: "AG-123"
↓
AI Agent 探索專案 (automation_tool)
- 第 1 輪:get_project_structure
- 第 2 輪:search_code "MainMenu"
- 第 3 輪:read_file "MainMenuController.cs"
- ...
- 第 N 輪:完成探索
↓
返回設計文檔 (automation_tool)
- 11 章節 Markdown 文檔
- ~4800 字
- 包含具體文件路徑和代碼建議
↓
保存文件 (jira_poller)
- .ai-agent/AG-123/design.md
- Git commit: "[AG-123] 技術設計"
- Jira 評論: 發表設計文檔
核心實現
1. 模組引入與檢測
orchestrator.py (Line 13-24):
# 引入 automation_tool 模組
automation_tool_path = Path(__file__).parent.parent.parent / "automation_tool"
sys.path.insert(0, str(automation_tool_path))
try:
from requirement_analyzer import RequirementAnalyzer
from system_designer import SystemDesigner
import config as automation_config
AUTOMATION_TOOL_AVAILABLE = True
except ImportError as e:
logging.warning(f"無法引入 automation_tool 模組: {e}")
AUTOMATION_TOOL_AVAILABLE = False設計要點:
- 使用相對路徑計算 automation_tool 位置
- 使用 try-except 優雅處理引入失敗
- 使用全局標誌記錄可用性
2. 初始化 automation_tool
orchestrator.py (Line 550-586):
def _init_automation_tool(self):
"""初始化 automation_tool 模組"""
if not AUTOMATION_TOOL_AVAILABLE:
self.logger.warning("⚠️ automation_tool 模組不可用,將使用舊版 AI Provider")
self.requirement_analyzer = None
self.system_designer = None
return
try:
# 從 config.yaml 獲取 API key
ai_config = self.config.get('ai', {})
claude_config = ai_config.get('claude', {})
api_key = claude_config.get('api_key') or automation_config.Config.ANTHROPIC_API_KEY
if not api_key:
raise ValueError("Anthropic API Key 未設定")
# 初始化 RequirementAnalyzer
self.requirement_analyzer = RequirementAnalyzer(
api_key=api_key,
model=claude_config.get('model', 'claude-sonnet-4-20250514'),
max_tokens=4096
)
# 初始化 SystemDesigner
self.system_designer = SystemDesigner(
api_key=api_key,
model=claude_config.get('model', 'claude-sonnet-4-20250514'),
max_tokens=8192 # 設計階段需要更多 tokens
)
self.logger.info("✓ automation_tool 模組已初始化 (RequirementAnalyzer + SystemDesigner)")
except Exception as e:
self.logger.error(f"✗ automation_tool 模組初始化失敗: {e}")
self.requirement_analyzer = None
self.system_designer = None設計要點:
- 配置優先級:config.yaml > 環境變數
- SystemDesigner 使用更大的 max_tokens(8192)
- 初始化失敗不影響系統啟動
3. 需求階段整合
orchestrator.py (Line 688-794):
def _ai_generate_requirements_with_automation_tool(self, task: JiraTask, feedback: str = None) -> str:
"""使用 automation_tool 的 RequirementAnalyzer 生成需求文檔"""
try:
self.logger.info("🚀 使用 automation_tool 的 RequirementAnalyzer 分析需求...")
# 構建需求描述(符合 Unity 任務格式)
feedback_section = ""
if feedback:
feedback_section = f"\n\n## 人工反饋(請根據反饋重新分析)\n{feedback}\n"
requirement_text = f"""# Unity 開發任務需求
## 任務資訊
- **任務編號**: {task.key}
- **任務標題**: {task.summary}
- **優先級**: {task.priority or '未設定'}
- **指派給**: {task.assignee or '未指派'}
## 任務描述
{task.description or '(無描述)'}{feedback_section}
## 目標平台
Unity 遊戲引擎
## 特殊要求
這是一個 Unity 開發任務,請在分析時考慮 Unity 的特性和最佳實踐。
"""
# 調用 RequirementAnalyzer(處理異步)
import asyncio
try:
loop = asyncio.get_event_loop()
if loop.is_running():
raise RuntimeError("Cannot use async in sync context")
except RuntimeError:
# 創建新事件循環
result = asyncio.run(self.requirement_analyzer.analyze(
requirement=requirement_text,
project_name=task.key
))
if result['success']:
content = result['content']
self.logger.info(f"✓ automation_tool 需求分析完成 ({len(content)} 字)")
return content
else:
raise Exception(result.get('error', 'Unknown error'))
except Exception as e:
self.logger.error(f"✗ automation_tool 需求分析失敗: {e}")
self.logger.info("降級使用舊版 AI Provider")
return self._ai_generate_requirements_fallback(task, feedback)設計要點:
- 構建符合 Unity 任務的需求文本
- 使用 asyncio.run() 在同步環境調用異步方法
- 檢測現有事件循環避免衝突
- 失敗時降級使用舊版方法
4. 設計階段整合(核心)
orchestrator.py (Line 878-980):
def _ai_generate_design_with_automation_tool(
self, task: JiraTask, req_content: str, feedback: str = None
) -> str:
"""使用 automation_tool 的 SystemDesigner 生成設計文檔"""
try:
self.logger.info("🚀 使用 automation_tool 的 SystemDesigner 生成技術設計...")
# 獲取需求文件路徑
req_file = self.file_manager.get_stage_file_path(task.key, "需求")
if not req_file or not req_file.exists():
raise FileNotFoundError(f"需求文件不存在: {req_file}")
# 如果有反饋,創建臨時需求文件
if feedback:
original_content = req_file.read_text(encoding='utf-8')
temp_content = f"""{original_content}
---
## 人工反饋(請根據此反饋調整設計)
{feedback}
"""
temp_req_file = req_file.parent / f"{req_file.stem}_with_feedback{req_file.suffix}"
temp_req_file.write_text(temp_content, encoding='utf-8')
req_file_to_use = temp_req_file
else:
req_file_to_use = req_file
# 獲取專案根目錄(worktree 路徑)
project_root = self.current_worktree # ← 關鍵:傳入 Unity Worktree
# 調用 SystemDesigner
import asyncio
try:
loop = asyncio.get_event_loop()
if loop.is_running():
raise RuntimeError("Cannot use async in sync context")
except RuntimeError:
result = asyncio.run(self.system_designer.design(
requirement_file=str(req_file_to_use),
project_root=str(project_root), # ← Unity 專案路徑
output_dir=str(self.file_manager.base_dir),
user_id=task.key,
project_name=task.key
))
# 清理臨時文件
if feedback and temp_req_file.exists():
temp_req_file.unlink()
if result['success']:
content = result['content']
self.logger.info(f"✓ automation_tool 技術設計完成 ({len(content)} 字)")
self.logger.info(f" 工具調用次數: {result['metadata']['agent_stats']['tool_calls']}")
return content
else:
raise Exception(result.get('error', 'Unknown error'))
except Exception as e:
self.logger.error(f"✗ automation_tool 技術設計失敗: {e}")
return self._generate_placeholder_design(feedback)設計要點:
- 關鍵:傳入
project_root=self.current_worktree - 這讓 SystemDesigner 可以探索 Git Worktree 中的 Unity 專案
- 反饋通過臨時文件附加到需求
- 記錄工具調用次數用於監控
5. agent_files.py 的修改
新增兩個方法支援 automation_tool:
class AgentFileManager:
def __init__(self, ai_agent_root: Path):
self.ai_agent_root = ai_agent_root
self.base_dir = self.ai_agent_root # ← SystemDesigner 需要
def get_stage_file_path(self, task_key, stage) -> Optional[Path]:
"""獲取階段文件的完整路徑
Returns:
Optional[Path]: 文件的完整路徑,若不存在返回 None
"""
stage_filename_map = {
"需求": "req.md",
"設計": "design.md",
"開發": "changes.md",
"測試": "test_guide.md"
}
filename = stage_filename_map.get(stage)
if not filename:
return None
file_path = self.ai_agent_root / task_key / filename
if file_path.exists():
return file_path
else:
return None降級機制設計
三層降級策略
def _ai_generate_requirements(self, task, feedback):
# 第一層:automation_tool RequirementAnalyzer
if self.requirement_analyzer:
return self._ai_generate_requirements_with_automation_tool(task, feedback)
# 第二層:舊版 AI Provider
if self.ai_provider:
return self._ai_generate_requirements_fallback(task, feedback)
# 第三層:占位符
return self._generate_placeholder_requirements(task, feedback)降級場景
| 場景 | 行為 | 用戶體驗 |
|---|---|---|
| automation_tool 正常 | 使用深度分析 | ⭐⭐⭐⭐⭐ |
| automation_tool 引入失敗 | 降級使用 AI Provider | ⭐⭐⭐ |
| AI Provider 失敗 | 降級使用占位符 | ⭐ |
| 所有 AI 失敗 | 顯示錯誤和占位符 | ⭐ (但不中斷) |
日誌設計
清晰的日誌幫助診斷問題:
✓ automation_tool 模組已初始化 (RequirementAnalyzer + SystemDesigner)
🚀 使用 automation_tool 的 RequirementAnalyzer 分析需求...
✓ automation_tool 需求分析完成 (2543 字)
🚀 使用 automation_tool 的 SystemDesigner 生成技術設計...
對話輪次: 1/30
執行工具: get_project_structure - {'max_depth': 3}
對話輪次: 2/30
執行工具: search_code - {'pattern': 'MainMenu', 'file_pattern': '*.cs'}
...
✓ automation_tool 技術設計完成 (4821 字)
工具調用次數: 15
如果降級:
⚠️ automation_tool 模組不可用,將使用舊版 AI Provider
🤖 正在使用 OllamaProvider 分析需求...
✓ AI 需求分析完成 (512 字)
異步處理技巧
問題:在同步環境調用異步方法
jira_poller 的 orchestrator 是同步代碼,但 automation_tool 使用異步 API。
解決方案
import asyncio
try:
loop = asyncio.get_event_loop()
if loop.is_running():
# 已有事件循環在運行(例如 Flask 或其他異步框架)
# 不能在運行中的循環創建新任務
raise RuntimeError("Cannot use async in sync context")
except RuntimeError:
# 沒有事件循環,或檢測到衝突
# 創建新的事件循環運行異步代碼
result = asyncio.run(async_function())為什麼不直接用 asyncio.run()?
直接使用會在已有事件循環時報錯:
RuntimeError: asyncio.run() cannot be called from a running event loop
我們的方案:
- 先檢測是否有運行中的事件循環
- 如果有,拋出異常並降級
- 如果沒有,安全地使用 asyncio.run()
配置管理
config.yaml 配置
ai:
provider: "claude" # 使用 Claude 以啟用 automation_tool
claude:
api_key: ${ANTHROPIC_API_KEY} # 從環境變數讀取
model: "claude-sonnet-4-20250514"
max_tokens: 4096配置優先級
1. config.yaml 中的 api_key
2. 環境變數 ANTHROPIC_API_KEY
3. automation_tool 的 .env 文件
4. 拋出錯誤
測試驗證
整合測試指南
完整的測試指南記錄在 TESTING_GUIDE.md:
階段 0: 啟動檢查
✓ automation_tool 模組已初始化
階段 1: 需求分析測試
✓ 檢測到 AI-Agent 標籤任務
✓ 使用 automation_tool 的 RequirementAnalyzer
✓ 生成 9 章節需求文檔(~2500 字)
✓ 發表到 Jira
階段 2: 系統設計測試
✓ 審批成功(@approved)
✓ 使用 automation_tool 的 SystemDesigner
✓ Claude 探索專案(10-20 次工具調用)
✓ 生成 11 章節設計文檔(~4800 字)
✓ 發表到 Jira
實際測試結果
測試時間:2025-10-12
| 指標 | 目標 | 實際 | 狀態 |
|---|---|---|---|
| 需求分析時間 | 10-30 秒 | 18 秒 | ✅ |
| 需求文檔字數 | ~2500 字 | 2543 字 | ✅ |
| 設計階段時間 | 2-5 分鐘 | 3.2 分鐘 | ✅ |
| 設計文檔字數 | ~4800 字 | 4821 字 | ✅ |
| 工具調用次數 | 10-20 次 | 15 次 | ✅ |
| Jira 評論發表 | 成功 | ✅ | ✅ |
| Git 提交 | 成功 | ✅ | ✅ |
整合完成!
2025-10-12,整合完成並測試通過。
關鍵成就:
- ✅ 兩個獨立開發的工具成功整合
- ✅ 保持各自獨立性
- ✅ 實現優雅降級
- ✅ 需求分析質量提升 5 倍
- ✅ 系統設計從無到有
- ✅ AI 能真正理解 Unity 專案
在下一篇日誌中,我將深入探討整合的技術亮點和經驗總結。
→ 繼續閱讀:[[專案/AI自動化Unity工作流整合/日誌/03-技術深度解析|#3 技術深度解析]]
返回 專案主頁