AIオーケストレーションシステム

MacOS上でClaude Codeを用いて要件定義からコード生成までを自動化。親エージェントが工程管理し、複数ワーカーが並行開発。進捗はMarkdownでリアルタイム共有。自律的な開発フローを実現します。
システム概要
要件定義書から自動的にタスクを分解し、複数のエージェントが平行処理で協調して開発を進める環境をBashベースで構築します。進捗状況は常にマークダウンファイルで確認でき、各エージェントが独立してバックグラウンドで動作しながら統合された開発フローを実現します。
特徴
- モジュラー設計: 各エージェントが独立したスクリプトファイル
- 並行処理: 複数のワーカーが同時に動作
- 進捗共有: マークダウンファイルで進捗状況を共有
- ファイルロック: 競合状態を防ぐロックメカニズム
- エラーハンドリング: 堅牢なエラー処理と復旧機能
- ログ管理: 詳細な実行ログと監査証跡
アーキテクチャ
- 親エージェント(master_agent.sh)が工程管理を担当
- 複数のワーカーエージェント(code, test, review, doc)がそれぞれの専門分野を担当
- bashスクリプトベースで構成し、各エージェントは独立したファイル
進捗管理
shared/progress.md
でリアルタイムな進捗状況を共有- JSONベースのタスクキューで依存関係を管理
- ファイルロックメカニズムで競合状態を防止
拡張性
- 新しいワーカータイプの追加が容易
- プロンプトテンプレートのカスタマイズが可能
- 並行処理数の調整が可能
ディレクトリ構成
project_root/
├── agents/
│ ├── master_agent.sh # 親エージェント(工程管理)
│ ├── worker_code.sh # コード作成ワーカー
│ ├── worker_test.sh # テスト作成ワーカー
│ ├── worker_review.sh # レビューワーカー
│ └── worker_doc.sh # ドキュメント作成ワーカー
├── config/
│ ├── agent_config.sh # 共通設定
│ └── prompts/ # エージェント用プロンプト
│ ├── master_prompt.md
│ ├── code_prompt.md
│ ├── test_prompt.md
│ ├── review_prompt.md
│ └── doc_prompt.md
├── shared/
│ ├── progress.md # 進捗状況(共有)
│ ├── task_queue.json # タスクキュー
│ └── locks/ # ファイルロック用
├── input/
│ └── requirements.md # 要件定義書(ユーザ提供)
├── output/
│ ├── code/ # 生成されたコード
│ ├── tests/ # テストファイル
│ └── docs/ # ドキュメント
├── logs/
│ └── [timestamp]/ # エージェント実行ログ
└── main.sh # メインスクリプト
主要コンポーネント
1. メインスクリプト (main.sh)
#!/bin/bash
# プロジェクトルートの設定
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${PROJECT_ROOT}/config/agent_config.sh"
# ログディレクトリの作成
LOG_DIR="${PROJECT_ROOT}/logs/$(date +%Y%m%d_%H%M%S)"
mkdir -p "${LOG_DIR}"
# 進捗ファイルの初期化
echo "# プロジェクト進捗状況" > "${PROGRESS_FILE}"
echo "開始時刻: $(date)" >> "${PROGRESS_FILE}"
# 親エージェントの起動
echo "マスターエージェントを起動します..."
"${PROJECT_ROOT}/agents/master_agent.sh" &
MASTER_PID=$!
# シグナルハンドラの設定
trap "kill ${MASTER_PID} 2>/dev/null; exit" INT TERM
# 完了まで待機
wait ${MASTER_PID}
echo "開発プロセスが完了しました。"
2. 共通設定 (config/agent_config.sh)
#!/bin/bash
# プロジェクト設定
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PROGRESS_FILE="${PROJECT_ROOT}/shared/progress.md"
TASK_QUEUE="${PROJECT_ROOT}/shared/task_queue.json"
REQUIREMENTS_FILE="${PROJECT_ROOT}/input/requirements.md"
LOCK_DIR="${PROJECT_ROOT}/shared/locks"
# Claude Code設定
CLAUDE_CMD="claude"
CLAUDE_TIMEOUT=300 # 5分
# エージェント設定
MAX_WORKERS=3
WORKER_TIMEOUT=600 # 10分
# 出力ディレクトリ
CODE_OUTPUT="${PROJECT_ROOT}/output/code"
TEST_OUTPUT="${PROJECT_ROOT}/output/tests"
DOC_OUTPUT="${PROJECT_ROOT}/output/docs"
# ディレクトリ作成
mkdir -p "${CODE_OUTPUT}" "${TEST_OUTPUT}" "${DOC_OUTPUT}" "${LOCK_DIR}"
# 共通関数
log_message() {
local level="$1"
local message="$2"
local timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
echo "[${timestamp}] [${level}] ${message}" | tee -a "${LOG_DIR}/system.log"
}
# ファイルロック関数
acquire_lock() {
local lock_name="$1"
local lock_file="${LOCK_DIR}/${lock_name}.lock"
local timeout=30
local count=0
while [ ${count} -lt ${timeout} ]; do
if mkdir "${lock_file}" 2>/dev/null; then
return 0
fi
sleep 1
count=$((count + 1))
done
return 1
}
release_lock() {
local lock_name="$1"
local lock_file="${LOCK_DIR}/${lock_name}.lock"
rmdir "${lock_file}" 2>/dev/null
}
# 進捗更新関数
update_progress() {
local task="$1"
local status="$2"
local details="$3"
if acquire_lock "progress"; then
echo "" >> "${PROGRESS_FILE}"
echo "## ${task}" >> "${PROGRESS_FILE}"
echo "- ステータス: ${status}" >> "${PROGRESS_FILE}"
echo "- 更新時刻: $(date)" >> "${PROGRESS_FILE}"
[ -n "${details}" ] && echo "- 詳細: ${details}" >> "${PROGRESS_FILE}"
release_lock "progress"
fi
}
3. 親エージェント (agents/master_agent.sh)
#!/bin/bash
# 設定読み込み
source "$(dirname "$0")/../config/agent_config.sh"
# マスターエージェントのメイン処理
main() {
log_message "INFO" "マスターエージェントを開始します"
# 要件定義書の確認
if [ ! -f "${REQUIREMENTS_FILE}" ]; then
log_message "ERROR" "要件定義書が見つかりません: ${REQUIREMENTS_FILE}"
exit 1
fi
# 要件分析とタスク分解
analyze_requirements
# タスクキューの初期化
initialize_task_queue
# ワーカーエージェントの管理
manage_workers
# 最終レポートの生成
generate_final_report
}
# 要件分析
analyze_requirements() {
log_message "INFO" "要件分析を開始します"
update_progress "要件分析" "実行中" "要件定義書を解析しています"
# Claude Codeを使用して要件分析
local analysis_prompt=$(cat "${PROJECT_ROOT}/config/prompts/master_prompt.md")
local requirements=$(cat "${REQUIREMENTS_FILE}")
# 分析結果をタスクに分解
local analysis_result=$(echo "${requirements}" | ${CLAUDE_CMD} --prompt "${analysis_prompt}")
# 分析結果をファイルに保存
echo "${analysis_result}" > "${PROJECT_ROOT}/shared/analysis_result.md"
update_progress "要件分析" "完了" "タスクリストを生成しました"
}
# タスクキューの初期化
initialize_task_queue() {
log_message "INFO" "タスクキューを初期化します"
# JSONフォーマットでタスクキューを作成
cat > "${TASK_QUEUE}" << 'EOF'
{
"tasks": [
{
"id": "code_001",
"type": "code",
"priority": 1,
"status": "pending",
"description": "メインモジュールの実装",
"dependencies": [],
"assigned_to": null,
"created_at": "",
"updated_at": ""
},
{
"id": "test_001",
"type": "test",
"priority": 2,
"status": "pending",
"description": "ユニットテストの作成",
"dependencies": ["code_001"],
"assigned_to": null,
"created_at": "",
"updated_at": ""
}
]
}
EOF
update_progress "タスクキュー初期化" "完了" "タスクキューを作成しました"
}
# ワーカー管理
manage_workers() {
log_message "INFO" "ワーカーエージェントの管理を開始します"
local workers=()
local worker_count=0
while true; do
# 利用可能なタスクの確認
local available_task=$(get_next_task)
if [ -z "${available_task}" ]; then
# タスクがない場合、完了チェック
if all_tasks_completed; then
log_message "INFO" "すべてのタスクが完了しました"
break
fi
sleep 5
continue
fi
# ワーカーの起動
if [ ${worker_count} -lt ${MAX_WORKERS} ]; then
start_worker "${available_task}"
worker_count=$((worker_count + 1))
fi
# 完了したワーカーのクリーンアップ
cleanup_completed_workers
sleep 2
done
# 全ワーカーの完了を待機
wait_for_all_workers
}
# タスク取得
get_next_task() {
if acquire_lock "task_queue"; then
# 実行可能なタスクを検索
local task=$(jq -r '.tasks[] | select(.status == "pending" and (.dependencies | length == 0 or all(. as $dep | any($dep; .status == "completed")))) | .id' "${TASK_QUEUE}" | head -1)
if [ -n "${task}" ] && [ "${task}" != "null" ]; then
# タスクをrunningに変更
jq ".tasks |= map(if .id == \"${task}\" then .status = \"running\" | .updated_at = \"$(date)\" else . end)" "${TASK_QUEUE}" > "${TASK_QUEUE}.tmp"
mv "${TASK_QUEUE}.tmp" "${TASK_QUEUE}"
echo "${task}"
fi
release_lock "task_queue"
fi
}
# ワーカー起動
start_worker() {
local task_id="$1"
local task_type=$(jq -r ".tasks[] | select(.id == \"${task_id}\") | .type" "${TASK_QUEUE}")
case "${task_type}" in
"code")
"${PROJECT_ROOT}/agents/worker_code.sh" "${task_id}" &
;;
"test")
"${PROJECT_ROOT}/agents/worker_test.sh" "${task_id}" &
;;
"review")
"${PROJECT_ROOT}/agents/worker_review.sh" "${task_id}" &
;;
"doc")
"${PROJECT_ROOT}/agents/worker_doc.sh" "${task_id}" &
;;
esac
}
# すべてのタスクが完了したかチェック
all_tasks_completed() {
local pending_count=$(jq -r '.tasks | map(select(.status == "pending" or .status == "running")) | length' "${TASK_QUEUE}")
[ "${pending_count}" -eq 0 ]
}
# 最終レポート生成
generate_final_report() {
log_message "INFO" "最終レポートを生成します"
update_progress "最終レポート" "実行中" "プロジェクト完了レポートを作成しています"
local report_file="${PROJECT_ROOT}/output/final_report.md"
cat > "${report_file}" << EOF
# プロジェクト完了レポート
## 概要
プロジェクトが正常に完了しました。
## 成果物
- コード: ${CODE_OUTPUT}/
- テスト: ${TEST_OUTPUT}/
- ドキュメント: ${DOC_OUTPUT}/
## 実行ログ
- システムログ: ${LOG_DIR}/system.log
## 完了時刻
$(date)
EOF
update_progress "最終レポート" "完了" "レポートを生成しました: ${report_file}"
}
# メイン処理の実行
main "$@"
4. コード作成ワーカー (agents/worker_code.sh)
#!/bin/bash
# 設定読み込み
source "$(dirname "$0")/../config/agent_config.sh"
# ワーカーのメイン処理
main() {
local task_id="$1"
if [ -z "${task_id}" ]; then
log_message "ERROR" "タスクIDが指定されていません"
exit 1
fi
log_message "INFO" "コード作成ワーカーを開始します - タスク: ${task_id}"
# タスク詳細の取得
local task_details=$(get_task_details "${task_id}")
local task_description=$(echo "${task_details}" | jq -r '.description')
update_progress "コード作成 - ${task_id}" "実行中" "${task_description}"
# コード生成
generate_code "${task_id}" "${task_description}"
# タスクの完了マーク
mark_task_completed "${task_id}"
log_message "INFO" "コード作成ワーカーが完了しました - タスク: ${task_id}"
}
# タスク詳細取得
get_task_details() {
local task_id="$1"
jq ".tasks[] | select(.id == \"${task_id}\")" "${TASK_QUEUE}"
}
# コード生成
generate_code() {
local task_id="$1"
local description="$2"
# プロンプトの準備
local code_prompt=$(cat "${PROJECT_ROOT}/config/prompts/code_prompt.md")
local requirements=$(cat "${REQUIREMENTS_FILE}")
# 生成プロンプトの作成
local full_prompt=$(cat << EOF
${code_prompt}
## タスク詳細
${description}
## 要件定義
${requirements}
## 出力形式
- PHPまたはJavaScriptコードを生成
- 詳細なコメントを含める
- CentOS/AlmaLinux9、nginx環境に適合させる
EOF
)
# Claude Codeでコード生成
local generated_code=$(echo "${full_prompt}" | ${CLAUDE_CMD} --timeout ${CLAUDE_TIMEOUT})
# 生成されたコードの保存
local output_file="${CODE_OUTPUT}/${task_id}.php"
echo "${generated_code}" > "${output_file}"
log_message "INFO" "コードを生成しました: ${output_file}"
}
# タスク完了マーク
mark_task_completed() {
local task_id="$1"
if acquire_lock "task_queue"; then
jq ".tasks |= map(if .id == \"${task_id}\" then .status = \"completed\" | .updated_at = \"$(date)\" else . end)" "${TASK_QUEUE}" > "${TASK_QUEUE}.tmp"
mv "${TASK_QUEUE}.tmp" "${TASK_QUEUE}"
release_lock "task_queue"
fi
update_progress "コード作成 - ${task_id}" "完了" "コードが正常に生成されました"
}
# メイン処理の実行
main "$@"
5. プロンプトテンプレート例
マスターエージェント用プロンプト (config/prompts/master_prompt.md)
# マスターエージェント - 要件分析とタスク分解
あなたは開発プロジェクトの工程管理を担当するマスターエージェントです。
## 役割
- 要件定義書の分析
- 開発タスクの分解と優先順位付け
- 依存関係の特定
- 工程管理とスケジューリング
## 分析対象
以下の要件定義書を分析し、開発タスクに分解してください。
## 出力形式
タスクリストをJSONフォーマットで出力してください。各タスクには以下の情報を含めてください:
- タスクID
- タスクタイプ(code/test/review/doc)
- 優先度
- 説明
- 依存関係
コード作成ワーカー用プロンプト (config/prompts/code_prompt.md)
# コード作成ワーカー
あなたは PHP と JavaScript の開発を専門とするコード作成エージェントです。
## 技術スタック
- PHP(最新バージョン)
- JavaScript(ES6+)
- CentOS/AlmaLinux9
- nginx
## コーディング方針
1. 詳細なコメントを日本語で記述
2. 可読性を重視したコード構造
3. エラーハンドリングの実装
4. セキュリティを考慮した実装
5. パフォーマンスの最適化
## 出力要件
- 実行可能なコードを生成
- 適切な名前空間の使用
- 設定ファイルの分離
- ログ出力の実装
使用方法
-
プロジェクトの初期化
mkdir my_project cd my_project # 上記のファイル構成を作成
-
要件定義書の準備
# input/requirements.md に要件を記述
-
システムの実行
./main.sh
-
進捗の確認
# 進捗状況の確認 cat shared/progress.md # ログの確認 tail -f logs/[timestamp]/system.log