本システムのUIとロジックを完全に疎結合化し、各モジュールの関心事を分離するための「ソースコード構成」と、実行時に生成される「データ構成」を定義します。
アプリケーション本体の設計構造です。UI層、ビジネスロジック層(Core)、外部エンジン層を明確に分離しています。
ai-macro-system/
├── src/
│ ├── main.py // アプリケーションのエントリポイント
│ ├── ui/ // 1画面構造・ウィジェットを管理するUI層
│ │ ├── views/ // MainWindow, RecordDialogなどのビュークラス
│ │ ├── viewmodels/ // UIの状態とビジネスロジックの橋渡し
│ │ └── resources/ // .uiファイルや.cssファイルを格納
│ ├── core/ // ビジネスロジック層
│ │ ├── recorder/ // 操作記録(OS Hook)および入力操作データの取得
│ │ ├── generator/ // 統合データからのマクロコードAI生成
│ │ ├── executor/ // 動的待機・即時ポーリングによる実行エンジン
│ │ └── healer/ // 4段階自己修復(Image Diff検証・動的再生成)エンジン
│ ├── engines/ // 外部認識エンジン層 (APIクライアント&サーバー管理)
│ │ ├── yolo/ // UI要素認識エンジン通信・ホスティング部
│ │ ├── ocr/ // テキスト認識エンジン通信・ホスティング部
│ │ └── manager.py // ローカルAIサーバーのライフサイクル管理
│ ├── models/ // workflow_IDやデータのスキーマ構造定義
│ └── utils/ // 環境変数管理、ロガーなどの共通ユーティリティ
├── tests/ // 振る舞い・エラーテスト群
├── config.json // ユーザーが設定画面から変更した接続情報を永続化するファイル
├── .env.example // 環境変数のテンプレート
├── requirements.txt // 依存パッケージ
└── README.md // 実例込みのドキュメント
ユーザーがマクロを記録・生成する際に動的に作られるデータ領域です。特に一時データ(temp)のライフサイクル管理を厳格に行います。
ai-macro-system/
└── macros/
└── wf_12345/ // 記録時に発行される一意なマクロIDごとのルートディレクトリ
├── workflow.json // 実行用マクロシナリオ (Executorが読み込む)
├── integrated.json // コンテキスト統合データ (Healerが修復時に参照)
├── images/ // 永続化される画像リソース
│ ├── evt_001_pre.png // 操作前スクショ
│ └── evt_001_crop.png // 対象UIクロップ画像
│
└── temp/ // ⚠️ 一時データ領域(生成完了後に自動破棄)
├── input_logs.json // OS Hookからの入力生ログ
├── evt_001_yolo.json // YOLOエンジンの解析生データ
└── evt_001_ocr.json // OCRエンジンの解析生データ
integrated.json と workflow.json を生成した直後に、ディレクトリごと完全に削除(自動破棄)されます。これによりJSONの冗長化やディスク容量の圧迫を防ぎます。YOLOやLLMなどのAIモデルは、プロセスの立ち上げからVRAMへのモデル展開(重みのロード)までに数十秒の時間を要します。UXを維持しタイムアウトを防ぐため、マクロ生成時ではなく「デスクトップアプリ起動時」にバックグラウンドで事前起動(ウォームアップ)させておく設計を採用します。
UIスレッド(PySide6)のフリーズを完全に防止するため、Pythonの subprocess モジュールを利用して完全に独立したプロセスとして推論サーバーを立ち上げます。アプリ終了時には atexit を用いて確実にプロセスをクリーンアップします。config.jsonを読み込み、LLMとCV(YOLO/OCR)それぞれのホスト指定がローカルである場合のみ、該当するサーバープロセスを起動します。
# engines/manager.py
# @role: Manages the lifecycle of local AI API servers as independent background processes.
import subprocess
import os
import atexit
import json
from typing import List
class LocalServerManager:
def __init__(self) -> None:
self._processes: List[subprocess.Popen] = []
def start_servers(self) -> None:
# Load custom connection settings to determine if local servers are needed.
config_path = 'config.json'
ai_mode = 'local'
llm_host = '127.0.0.1'
llm_port = '8844'
cv_host = '127.0.0.1'
cv_port = '8843'
if os.path.exists(config_path):
try:
with open(config_path, 'r', encoding='utf-8') as f:
config = json.load(f)
ai_mode = config.get('ai_mode', 'local')
llm_host = config.get('llm_host', '127.0.0.1')
# Fallback to default port if explicitly empty
llm_port = config.get('llm_port', '8844') or '8844'
cv_host = config.get('cv_host', '127.0.0.1')
cv_port = config.get('cv_port', '8843') or '8843'
except Exception:
pass
env = os.environ.copy()
# Start LLM server if local mode is selected and host points to local machine
if ai_mode == 'local' and llm_host in ['127.0.0.1', 'localhost']:
llm_cmd = ['python', '-m', 'uvicorn', 'engines.llm.server:app', '--port', llm_port]
llm_proc = subprocess.Popen(llm_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
self._processes.append(llm_proc)
# Start CV server independently if host points to local machine
if cv_host in ['127.0.0.1', 'localhost']:
yolo_cmd = ['python', '-m', 'uvicorn', 'engines.yolo.server:app', '--port', cv_port]
yolo_proc = subprocess.Popen(yolo_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
self._processes.append(yolo_proc)
atexit.register(self.stop_servers)
def stop_servers(self) -> None:
for p in self._processes:
if p.poll() is None:
p.terminate()
self._processes.clear()
AIの実体をどこで動かすかは、ホーム画面の設定エリアから変更されたIPアドレスやドメイン名(config.jsonに永続化)を基に、ソースコードを書き換えることなく透過的に切り替えられる設計とします。
127.0.0.1 または localhost を指定し、ユーザーのPC内で全て完結させる構成。LocalServerManagerが作動し、ローカルのVRAM上にモデルを展開します。192.168.x.x 等のプライベートIPや社内ローカルドメインを指定し、同一ネットワーク上のGPU搭載サーバー等に重いAI処理を委譲する構成。ローカルプロセスは起動しません。api.openai.com 等)を指定し、クラウド上の強力なLLMやCVサービスを利用する構成。セキュリティ要件に基づき外部入力は暗号化通信を前提とします。@role: ...)を記述し、関心事ごとにファイルを細かく分割する。config.json)や環境変数テンプレートに記載するトークンや認証キー等の情報は取り扱いに注意し、ハードコードを厳禁とする。