diff --git a/.agents/skills/lib.sh b/.agents/skills/lib.sh index bb3c3cd..6e34f33 100644 --- a/.agents/skills/lib.sh +++ b/.agents/skills/lib.sh @@ -1192,7 +1192,7 @@ send_keys_safe() { local cur_content cur_content=$(_pane_capture "$sess") # Hardened Submission Checks - if printf '%s\n' "$cur_content" | grep -Eq "● |Twisting|Thinking"; then + if printf '%s\n' "$cur_content" | grep -Eq "● |✽ |[A-Za-z]+ing…|[A-Za-z]+ing\.\.\.|esc to interrupt"; then return 0 fi if [ "$was_popup" = "0" ] && ! _pane_tail "$sess" 3 | grep -Fq "$marker" && [ "$cur_content" != "$pre_submit" ]; then diff --git a/.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job b/.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job index bbf1ad7..bb78cfe 100755 --- a/.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job +++ b/.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job @@ -55,6 +55,7 @@ multi-agent-mux-delegate-job [options] [--registry-dir ] [--dry-run] [--role ] [--type ] [--reviewer ] [--reviewer-session ] [--max-iterations ] + [--counterpart-role ] [--strict-role-check] # The skill is tmux-interactive only; --mode print was removed. status --job [--registry-dir ] list [--registry-dir ] @@ -69,6 +70,11 @@ AGENT="claude-code"; PROMPT=""; WORKDIR="$(pwd)"; AGENT_SESSION="tmux:claude" TIMEOUT=3600; IDLE_TIMEOUT=120; VALIDATE=""; DRY_RUN=0 JOB_ID=""; REGISTRY_DIR="$REGISTRY_DIR_DEFAULT"; DELEGATE_ROLE="Worker" TYPE="direct"; REVIEWER="hermes"; REVIEWER_SESSION="tmux:hermes"; MAX_ITERATIONS=5 +DEFAULT_COUNTERPART_ROLE="Reviewer" +COUNTERPART_ROLE="$DEFAULT_COUNTERPART_ROLE" +STRICT_ROLE_CHECK=0 +ROLE_ALIASES_JSON='{"worker": ["worker", "creator"], "planner": ["planner"], "reviewer": ["reviewer"]}' +COUNTERPART_ROLE_EXPLICIT=0 parse_opts() { while [[ $# -gt 0 ]]; do @@ -88,6 +94,8 @@ parse_opts() { --reviewer) REVIEWER="$2"; shift 2;; --reviewer-session) REVIEWER_SESSION="$2"; shift 2;; --max-iterations) MAX_ITERATIONS="$2"; shift 2;; + --counterpart-role) COUNTERPART_ROLE="$2"; COUNTERPART_ROLE_EXPLICIT=1; shift 2;; + --strict-role-check) STRICT_ROLE_CHECK=1; shift;; *) echo "unknown option: $1" >&2; usage; exit 1;; esac done @@ -197,7 +205,8 @@ EOF local iteration=1 local current_prompt="$PROMPT" local current_session="$AGENT_SESSION" - local current_role="worker" + local _phase="worker" + local display_role="$DELEGATE_ROLE" if [[ "$DRY_RUN" == "1" ]]; then echo "[dry-run] orchestrator loop would start for job: $JOB_ID type: $TYPE" @@ -209,7 +218,7 @@ EOF while true; do echo "==================================================" - echo "Iteration $iteration - Role: $current_role" + echo "Iteration $iteration - Role: $display_role" echo "Session: $current_session" echo "==================================================" @@ -223,7 +232,7 @@ EOF - **Job ID**: $JOB_ID - **Target Agent/Session**: $current_session -- **Role**: $current_role +- **Role**: $display_role - **Iteration**: $iteration - **Output Report Path**: .mam/jobs/$JOB_ID/${clean_session}-reports/report-final.md @@ -239,11 +248,11 @@ EOF --agent-session "$current_session" \ --prompt "$current_prompt" \ --iteration "$iteration" \ - --role "$current_role" \ + --role "$display_role" \ --status "pending" # Start subscriber - local logf="$REGISTRY_DIR/${JOB_ID}.iter_${iteration}_${current_role}.subscriber.out" + local logf="$REGISTRY_DIR/${JOB_ID}.iter_${iteration}_${display_role}.subscriber.out" "$PY" "$SCRIPT_DIR/scripts/job_subscriber.py" --registry-dir "$REGISTRY_DIR" \ --job "$JOB_ID" --timeout "$TIMEOUT" --idle-timeout "$IDLE_TIMEOUT" \ >"$logf" 2>&1 & @@ -272,7 +281,12 @@ EOF local instructions="Your job_id is \"$JOB_ID\". Detailed task requirements, instructions, and target output paths for iteration $iteration are documented in the task brief file at: .mam/jobs/$JOB_ID/brief.md. Please READ and follow .mam/jobs/$JOB_ID/brief.md to complete your work. Commands: start='$pub --event started', success='$pub --event completed --detail ', error='$pub --event error --detail '." # Trigger agent - run_agent "$JOB_ID" "$instructions" "$current_session" + local force_warn_only=0 + if [[ "$_phase" == "reviewer" && "$COUNTERPART_ROLE_EXPLICIT" -eq 1 \ + && "${COUNTERPART_ROLE,,}" != "${DEFAULT_COUNTERPART_ROLE,,}" ]]; then + force_warn_only=1 + fi + run_agent "$JOB_ID" "$instructions" "$current_session" "$force_warn_only" # Wait for subscriber local sub_rc=0 @@ -289,21 +303,22 @@ EOF job_status="timeout" fi - echo "Job role $current_role finished with status: $job_status" + echo "Job role $display_role finished with status: $job_status" # Retrieve feedback from the last event local feedback feedback="$("$PY" "$SCRIPT_DIR/scripts/registry.py" --registry-dir "$REGISTRY_DIR" get-feedback --job "$JOB_ID")" echo "Feedback/Detail: $feedback" - if [[ "$current_role" == "worker" ]]; then + if [[ "$_phase" == "worker" ]]; then if [[ "$job_status" != "completed" ]]; then echo "Worker did not complete successfully (status: $job_status). Terminating workflow." break fi # Worker completed successfully, now switch to reviewer - current_role="reviewer" + _phase="reviewer" + display_role="$COUNTERPART_ROLE" current_session="$REVIEWER_SESSION" # Build reviewer prompt based on type if [[ "$TYPE" == "loop" ]]; then @@ -342,7 +357,8 @@ EOF fi iteration=$((iteration + 1)) - current_role="worker" + _phase="worker" + display_role="$DELEGATE_ROLE" current_session="$AGENT_SESSION" current_prompt="The reviewer provided the following feedback for job $JOB_ID: $feedback. Please modify the code/artifacts to address these comments. CRITICAL: As the Developer Team Leader, you must thoroughly review the suggested modifications, verify their validity, adopt/implement them if valid, and if you judge any recommendation to be invalid, do NOT implement it but instead explain your reasons clearly in your response and send it back to the reviewer (수정안을 최대한 꼼꼼히 검토하여 타당성을 검증하고, 타당하다면 수렴하여 수정을 진행하되, 타당하지 않다고 판단되는 부분이 있다면 그 이유를 명확히 밝혀 리뷰어에게 전달하십시오)." fi @@ -367,7 +383,7 @@ EOF } run_agent() { - local job_id="$1"; local instructions="$2"; local target_session="${3:-$AGENT_SESSION}" + local job_id="$1"; local instructions="$2"; local target_session="${3:-$AGENT_SESSION}"; local force_warn_only="${4:-0}" # The skill is INTERACTIVE-ONLY. We never invoke `claude -p` or any other # one-shot print mode, because: # - claude -p exits the moment stdin is drained, so there's nothing to @@ -408,6 +424,53 @@ run_agent() { return 1 fi + # Check role suitability + source "$SCRIPT_DIR/../lib.sh" + local sess_role job_role + sess_role=$(SESS_NAME="$sess" MAM_STATE_JSON="$(load_state_json)" "$PY" -c " +import os, json +d = json.loads(os.environ.get('MAM_STATE_JSON', '{}')) +name = os.environ.get('SESS_NAME') +for s in d.get('tmux_sessions', []): + if s.get('name') == name: + print(s.get('role', '')) + break +" 2>/dev/null || echo "") + + job_role=$("$PY" -c " +import json +try: + with open('$REGISTRY_DIR/$job_id.json') as f: + print(json.load(f).get('role', '')) +except Exception: + pass +" 2>/dev/null || echo "") + + if [[ -n "$job_role" && -n "$sess_role" ]]; then + local check_result + check_result=$(JOB_ROLE="$job_role" SESS_ROLE="$sess_role" ROLE_ALIASES_JSON="$ROLE_ALIASES_JSON" "$PY" -c " +import os, json +job = os.environ.get('JOB_ROLE', '').lower() +sess = os.environ.get('SESS_ROLE', '').lower() +aliases = json.loads(os.environ.get('ROLE_ALIASES_JSON', '{}')) +candidates = aliases.get(job, [job]) +if any(c in sess for c in candidates): + print('OK') +else: + print('MISMATCH') +" 2>/dev/null || echo "OK") + + if [[ "$check_result" == "MISMATCH" ]]; then + local mismatch_msg="Target session '$sess' has role '$sess_role' which does not match job role '$job_role'." + if [[ "$STRICT_ROLE_CHECK" -eq 1 && "$force_warn_only" -ne 1 ]]; then + echo "ERROR: role suitability mismatch. $mismatch_msg" >&2 + return 1 + else + echo "WARNING: $mismatch_msg" >&2 + fi + fi + fi + # Before launching the agent, set up error trap to publish error event if [ -n "${job_id:-}" ] && [ -n "${PY:-}" ]; then pub_script="$SCRIPT_DIR/scripts/publish_event.py" @@ -415,7 +478,6 @@ run_agent() { fi echo "살아있는 에이전트 세션 '$sess'에 작업을 위임합니다..." - source "$SCRIPT_DIR/../lib.sh" if ! send_keys_safe "$sess" "$instructions" "$job_id"; then echo "ERROR: 프롬프트 주입 실패 — 세션 '$sess' (프롬프트 잠금 의심)" >&2 return 1 diff --git a/.agents/skills/multi-agent-mux-loop/scripts/run_loop.sh b/.agents/skills/multi-agent-mux-loop/scripts/run_loop.sh index 8693bf4..a13b66b 100644 --- a/.agents/skills/multi-agent-mux-loop/scripts/run_loop.sh +++ b/.agents/skills/multi-agent-mux-loop/scripts/run_loop.sh @@ -67,6 +67,18 @@ if [ -z "$TARGET_AGENT" ] || [ -z "$TASK" ]; then echo "ERROR: --target-agent and --task are mandatory fields." usage fi +delegate_job_safe() { + local orig_script="$REPO_ROOT/.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job" + local tmp_script + tmp_script="${orig_script}.${RANDOM}_$$.tmp" + cp "$orig_script" "$tmp_script" + trap 'rm -f "$tmp_script"' EXIT INT TERM HUP + local rc=0 + bash "$tmp_script" "$@" || rc=$? + rm -f "$tmp_script" + trap - EXIT INT TERM HUP + return $rc +} log_info() { echo -e "\033[1;34m[*]\033[0m $1" @@ -221,7 +233,7 @@ if [ "$PLAN_MODE" = true ]; then # Step 1.1: Request initial plan from Planner log_info "Requesting initial implementation plan from Planner..." - PLAN_JOB_OUTPUT=$(bash .agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job submit \ + PLAN_JOB_OUTPUT=$(delegate_job_safe submit \ --agent-session "tmux:$PLANNER_SESSION" \ --agent "$(resolve_agent_type "$PLANNER_SESSION")" \ --type "direct" \ @@ -255,7 +267,7 @@ if [ "$PLAN_MODE" = true ]; then log_info "Discussion Turn $turn/$PLAN_TALK_TURNS: Creator challenging the plan..." # Creator critique job - DEBATE_JOB_OUTPUT=$(bash .agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job submit \ + DEBATE_JOB_OUTPUT=$(delegate_job_safe submit \ --agent-session "tmux:$TARGET_AGENT" \ --agent "$(resolve_agent_type "$TARGET_AGENT")" \ --type "direct" \ @@ -283,7 +295,7 @@ if [ "$PLAN_MODE" = true ]; then CRITIQUE_TEXT=$(cat "$CRITIQUE_FILE") log_info "Planner refining plan with Creator's feedback..." - REFINE_JOB_OUTPUT=$(bash .agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job submit \ + REFINE_JOB_OUTPUT=$(delegate_job_safe submit \ --agent-session "tmux:$PLANNER_SESSION" \ --agent "$(resolve_agent_type "$PLANNER_SESSION")" \ --type "direct" \ @@ -339,7 +351,7 @@ if [ -n "$CURRENT_PLAN" ]; then EXECUTION_PROMPT="이미 수립된 다음 계획서에 입각하여 작업자의 판단하에 코드를 구현하고 작업 목표를 완성해주세요. 계획서:\n$CURRENT_PLAN\n\n작업 목표: $TASK" fi -EXEC_JOB_OUTPUT=$(bash .agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job submit \ +EXEC_JOB_OUTPUT=$(delegate_job_safe submit \ --agent-session "tmux:$TARGET_AGENT" \ --agent "$(resolve_agent_type "$TARGET_AGENT")" \ --type "direct" \ @@ -383,7 +395,7 @@ while [ "$loop_count" -le "$MAX_LOOP" ]; do if [ "${#REVIEWERS[@]}" -eq 0 ]; then log_warn "No reviewers specified. Conducting Creator Self-Review..." - SELF_REV_OUTPUT=$(bash .agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job submit \ + SELF_REV_OUTPUT=$(delegate_job_safe submit \ --agent-session "tmux:$TARGET_AGENT" \ --agent "$(resolve_agent_type "$TARGET_AGENT")" \ --type "direct" \ @@ -427,7 +439,7 @@ while [ "$loop_count" -le "$MAX_LOOP" ]; do CHANGES_DIFF=$(git diff 2>/dev/null || echo "No git diff available") fi - REV_OUTPUT=$(bash .agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job submit \ + REV_OUTPUT=$(delegate_job_safe submit \ --agent-session "tmux:$rev" \ --agent "$(resolve_agent_type "$rev")" \ --type "direct" \ @@ -498,7 +510,7 @@ while [ "$loop_count" -le "$MAX_LOOP" ]; do if [ "$PLAN_MODE" = true ] && [ "$COMPLEX_FIX" = true ]; then log_warn "Feedback involves complex code modifications. Diverting to Planner to revise plan..." - REFINE_PLAN_OUTPUT=$(bash .agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job submit \ + REFINE_PLAN_OUTPUT=$(delegate_job_safe submit \ --agent-session "tmux:$PLANNER_SESSION" \ --agent "$(resolve_agent_type "$PLANNER_SESSION")" \ --type "direct" \ @@ -527,7 +539,7 @@ while [ "$loop_count" -le "$MAX_LOOP" ]; do fi # Creator execution corrective job - CORRECT_JOB_OUTPUT=$(bash .agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job submit \ + CORRECT_JOB_OUTPUT=$(delegate_job_safe submit \ --agent-session "tmux:$TARGET_AGENT" \ --agent "$(resolve_agent_type "$TARGET_AGENT")" \ --type "direct" \ diff --git a/RECOMMENDED.md b/RECOMMENDED.md new file mode 100644 index 0000000..991fa1d --- /dev/null +++ b/RECOMMENDED.md @@ -0,0 +1,101 @@ +# 📋 Recommended Multi-Agent Session Architecture Guide + +본 문서는 `multi-agent-mux` 환경에서 오케스트레이션 루프(`multi-agent-mux-loop`)를 활용해 고품질 소프트웨어를 개발할 때 가장 권장되는 **3-에이전트 역할 분리 아키텍처**와 설정 방법 및 추천 이유에 대해 설명합니다. + +--- + +## 👥 1. 추천 3-에이전트 구성 (Roles & Configuration) + +`multi-agent-mux` 환경에서는 다음 세 가지 전문 세션을 생성하여 상시 기동해 두는 것이 가장 이상적입니다. + +```mermaid +graph TD + User([사용자/Orchestrator]) <--> AGY_Parent[Antigravity Parent] + AGY_Parent -->|1. 계획 수립 위임| Planner[Planner 세션
claude] + AGY_Parent -->|2. 구현 위임| Creator[Creator 세션
agy] + AGY_Parent -->|3. 교차 검증 위임| Reviewer[Reviewer 세션
cline] + + Planner -->|설계/피드백 루프| Creator + Creator -->|구현 완료| Reviewer + Reviewer -->|Verdict PASS/NOT PASS| Planner +``` + +### ① Planner 에이전트 +* **역할 (Role)**: `planner-reviewer` +* **주요 임무**: 전체 아키텍처 아웃라인 설계, 구현 계획서 수립, 이의 제기 수렴 및 계획 개정(Refinement). +* **추천 에이전트 종류**: `claude` (긴 추론 맥락과 설계 완성도가 높음) +* **생성 명령어**: + ```bash + # planner-reviewer 역할로 claude 세션 기동 + bash .agents/skills/multi-agent-mux-create/multi-agent-mux-create \ + --agent claude \ + --role planner-reviewer \ + --name canary-projects-multi-agent-mux-planner-reviewer-claude + ``` + +### ② Creator 에이전트 (주작업자) +* **역할 (Role)**: `creator` +* **주요 임무**: 계획서상의 제약조건 검토 및 이의제기(Challenge), 실제 코드베이스 구현 편집, DoD 자가 검증. +* **추천 에이전트 종류**: `agy` (기민한 도구 실행 속도 및 로컬 파일 편집 최적화) +* **생성 명령어**: + ```bash + # creator 역할로 agy 세션 기동 + bash .agents/skills/multi-agent-mux-create/multi-agent-mux-create \ + --agent agy \ + --role creator \ + --name canary-projects-multi-agent-mux-creator-agy + ``` + +### ③ Reviewer 에이전트 +* **역할 (Role)**: `reviewer` +* **주요 임무**: 구현된 변경분(`git diff`)과 구현 계획서를 기반으로 빌드 가능성, 린트, 로직 유실 교차 피어 리뷰. +* **추천 에이전트 종류**: `cline` (안정적인 컴파일 도구 활용 및 린터 체크 강점) +* **생성 명령어**: + ```bash + # reviewer 역할로 cline 세션 기동 + bash .agents/skills/multi-agent-mux-create/multi-agent-mux-create \ + --agent cline \ + --role reviewer \ + --name canary-projects-multi-agent-mux-reviewer-cline + ``` + +--- + +## 💡 2. 왜 3개의 에이전트 분리를 강력히 추천하는가? + +부모 에이전트(Antigravity)가 오케스트레이션과 코드 개발을 모두 처리하지 않고, 별도의 격리된 3개의 역할 세션을 두는 데에는 다음과 같은 명확한 공학적 이유가 있습니다. + +### ① 대화창 컨텍스트(Context Window) 오염 방지 +* **디테일의 지옥**: 에이전트가 코드를 탐색하고, 컴파일 오류를 잡고, 수많은 파일라인을 편집하는 세부 구현 과정은 수십만 토큰에 달하는 방대한 런타임 로그와 코드를 누적시킵니다. +* **해결책**: 만약 오케스트레이터(부모 에이전트)가 이를 직접 수행하면 사용자님과의 대화창 컨텍스트가 구현 로그로 가득 차, 이전에 의논했던 아키텍처 제약이나 중요 요구사항을 쉽게 잊어버립니다. 역할을 격리함으로써 각 세션은 자신의 세부 구현 컨텍스트만 소비하고 소멸합니다. + +### ② 비동기 개발 자율성 (Asynchronous Autonomy) +* **대기 시간 최소화**: 오케스트레이션 루프가 설계 검토, 피드백, 자가 수정 등을 수차례 반복하며 백그라운드(tmux)에서 스스로 문제를 해결해 나가는 동안, 사용자님은 저(부모 에이전트)와 멈춤 없이 계속해서 고수준 설계 및 다른 기능에 대한 논의를 이어나갈 수 있습니다. +* **생산성 극대화**: 부모 에이전트가 코딩을 하느라 대화를 블로킹하는 현상이 발생하지 않습니다. + +### ③ 교차 검증을 통한 객관성 확보 (Peer Review Objectivity) +* **작성자와 검증자의 분리**: 코드를 직접 짠 에이전트가 자기 자신의 코드를 완벽하게 리뷰하는 것은 불가능에 가깝습니다(인지 편향 발생). +* **해결책**: 구현을 전담한 `Creator`와, 이를 객관적인 삼자 관점에서 검토하는 `Reviewer` 세션을 철저히 독립시킴으로써 코드 품질 결함을 높은 확률로 선제 필터링할 수 있습니다. + +### ④ 이기종 모델/도구의 결합 (Heterogeneous Collaboration) +* **각자 잘하는 분야의 극대화**: + * **Planner (Claude)**: 설계 및 아키텍처 정합성 수립에 특화 + * **Creator (Antigravity/Agy)**: 신속하고 정확한 로컬 파일 편집 및 도구 호출에 특화 + * **Reviewer (Cline)**: 린트 체크, 빌드 테스트 등 철저한 안전망 검증에 특화 +* 이러한 하이브리드 조합을 구성할 때 루프 전체의 최종 도달 성공률이 가장 높게 나타납니다. + +--- + +## 🛠️ 3. 3-에이전트 루프 실행 방법 + +에이전트들이 생성되어 기동(Running) 중인 경우, 다음과 같이 계획 수립(`--plan`) 및 전체 교차 리뷰(`--all-reviewer`) 옵션을 주어 자율 협업 개발을 시작할 수 있습니다. + +```bash +bash .agents/skills/multi-agent-mux-loop/scripts/run_loop.sh \ + --target-agent "canary-projects-multi-agent-mux-creator-agy" \ + --plan \ + --all-reviewer \ + --task "여기에 개발하고자 하는 태스크의 최종 목표를 상세히 기술합니다." +``` + +이 루프는 **기획 ➡️ 작업자 이의제기 ➡️ 계획 개정 ➡️ 코드 개발 ➡️ 교차 피어 리뷰 ➡️ 피드백 수렴 재구현**의 전 과정을 자동으로 진행하여, 빌드 및 린트가 보장되는 코드를 저장소에 자동으로 커밋 및 병합합니다.