docs: update DRAFT_PLAN.md with problem definitions & gRPC justification, move agent rules to .agents/
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
# AGENT.md
|
||||
|
||||
본 문서는 새로운 프로젝트에 **MQTT 메시징 백플레인 및 Tmux 기반 멀티 에이전트 오케스트레이션 워크플로우**를 도입하고, 협업하는 에이전트들이 일관된 규칙과 아키텍처에 따라 안전하고 견고하게 작업을 수행할 수 있도록 정의한 공통 지침 및 규약입니다.
|
||||
|
||||
새로운 프로젝트에서 작업하는 모든 에이전트는 작업을 시작하기 전 이 문서를 반드시 정독하고 규약을 준수해야 합니다.
|
||||
|
||||
---
|
||||
|
||||
## 1. 에이전트의 역할 정의 (Agent Roles)
|
||||
|
||||
역할군 간의 책임 및 권한을 명확히 분리하여 병목을 줄이고 작업의 완성도를 높입니다.
|
||||
|
||||
### 👑 General Manager (총괄 매니저)
|
||||
- **주요 책무**: 사용자와 직접 소통하여 요구사항 접수, 상세 작업 계획 수립, 팀장 에이전트 할당 및 작업 위임, 전체 워크플로우 통제 및 최종 완료 보고.
|
||||
- **모호성 제거**: 사용자의 요구사항에 모호한 부분이 있다면 작업을 추측하여 진행하지 말고, 즉시 사용자에게 질문하여 명확히 해야 합니다 (`/grill-me` 슬래시 명령어 권장).
|
||||
|
||||
### 👥 Team Leaders (팀장)
|
||||
새롭게 생성되는 에이전트(`antigravity`, `claude`, `cline`, `hermes` 등)는 각 팀의 **팀장** 역할을 수행합니다. 총괄 매니저로부터 작업을 위임받아 개발 또는 리뷰 워크플로우를 주도합니다.
|
||||
- **Developer Team Leader (개발 팀장)**:
|
||||
- 총괄 매니저로부터 작업을 위임받습니다.
|
||||
- **작업 분석 및 계획**: 주어진 작업을 철저히 분석하고, 작은 단위로 문제를 나누어 세부 계획을 수립합니다.
|
||||
- **내부 병렬 처리**: 내부적으로 subagent를 활용해 위임받은 작업을 병렬적으로 처리할 수 있습니다.
|
||||
- **리뷰 타당성 검증 및 거부**: 리뷰어가 지적한 피드백을 면밀히 검토합니다. 타당한 제안은 수렴하여 코드를 수정하지만, 타당하지 않다고 판단되는 안건은 반영하지 않고 **그 명확한 이유를 작성하여 리뷰어에게 되돌려 보냅니다**.
|
||||
- **완료 신호 송신**: 모든 리뷰어들로부터 `PASS`를 획득하고 변경 사항이 검증되면, 최초 작업을 위임받았던 개발 팀장이 총괄 매니저에게 최종 작업 완료 신호를 송신합니다.
|
||||
- **Reviewer Team Leader (리뷰어 팀장)**:
|
||||
- 개발 팀장으로부터 리뷰 요청을 접수합니다.
|
||||
- **문제 제시에 대한 이유와 개선 방향 포함**: 단순한 반려(`NOT PASS`) 통보는 금지됩니다. 이슈를 제기할 때는 **반드시 해당 문제가 발생하는 구체적인 이유와 확실한 개선 방향(코드 대안 포함)을 함께 작성**해야 합니다.
|
||||
- **합의 루프**: 모든 지적 사항이 해결되고 최종 `PASS`를 발행할 때까지 리뷰 루프에 동참합니다.
|
||||
|
||||
### 🛡️ 역할 범위 준수 원칙 (Role Suitability Check)
|
||||
- 모든 에이전트는 자신에게 부여된 역할에 부합하는 작업만을 수행해야 합니다. (예: 개발 팀장은 최종 PASS 여부를 결정하지 않으며, 리뷰어 팀장은 직접 프로젝트 소스코드를 작성하지 않습니다.)
|
||||
- **자신의 역할에 맞지 않는 작업이 지시된 경우**, 에이전트는 반드시:
|
||||
1. 해당 작업을 수행하기에 가장 적합한 에이전트 세션을 추천하여 위임을 유도하거나,
|
||||
2. 프로젝트 연속성을 위해 극히 필요한 경우 직접 작업을 수행합니다.
|
||||
|
||||
---
|
||||
|
||||
## 2. 메시징 백플레인 & 레지스트리 규약
|
||||
|
||||
에이전트 간의 비동기 소통과 상태 관리는 분산 이벤트 채널 및 파일/DB 레지스트리를 통해 제어됩니다.
|
||||
|
||||
### 📡 MQTT 백플레인 (MQTT Backplane)
|
||||
- **이벤트 라이프사이클**:
|
||||
- `started` (작업 개시) ➡️ `progress`/`permission_required` (진행 상황 공유) ➡️ `completed` (성공 종료) 또는 `error` (실패 종료)
|
||||
- `completed` 및 `error`는 단 한 번만 발행되는 단말(Terminal) 이벤트입니다.
|
||||
- **메시지 발행/구독 규칙**:
|
||||
- MQTT는 영속 큐를 보장하지 않으므로, 에이전트 구동 전 **반드시 구독자(`job_subscriber.py`)가 먼저 백그라운드에서 대기**해야 합니다 (Subscribe-before-Publish 원칙).
|
||||
- 단말 이벤트 발행 시 브로커에 `retain=True`로 영속화하여 늦게 합류한 구독자도 최종 상태를 읽을 수 있도록 조치합니다.
|
||||
- 전송 데이터에는 비밀번호, 개인키 등의 중요 비밀 정보나 절대 경로가 포함되지 않도록 보편화(Generalised)해야 합니다.
|
||||
|
||||
### 🗃️ 레지스트리 및 상태 관리
|
||||
- 본 아키텍처는 목적에 따라 두 가지 레지스트리를 분리하여 운영합니다:
|
||||
- **잡 레지스트리 (Job Registry)**: 각 비동기 잡의 메타데이터와 생명주기는 개별 JSON 파일(`.mam/jobs/<id>.json`)로 기록되며, 다중 세션 간의 동시 청구(claiming) 경합은 파일 단위의 `fcntl` advisory lock(`registry_lock` via `registry.py`)을 통해 방어합니다.
|
||||
- **세션 레지스트리 (Session Registry)**: TMUX 모니터링 상태 및 에이전트 구동 정보는 SQLite WAL 데이터베이스(`.mam/agent-sessions.db`)를 통해 단일 호스트 내에서 안정적인 동시 트랜잭션으로 일관되게 제어합니다. 단, SQLite WAL 모드는 NFS(네트워크 파일 시스템) 환경에서는 완전한 파일 락이 보장되지 않으므로 로컬 파일 시스템 사용을 권장합니다.
|
||||
|
||||
### 🛡️ 보안 프로토콜 (HMAC-SHA256)
|
||||
- **무인증 PoC 모드**: 잡 레지스트리 생성 시 `auth_token`이 `null`로 지정된 경우(PoC 기본 모드), 별도의 서명 검증을 생략하고 모든 이벤트를 수용합니다 (`verify_hmac`이 항상 `True`를 반환).
|
||||
- **인증 Production 모드**: 실배포 환경이나 인증이 필요한 연동 단계에서는 각 잡마다 고유 암호화 토큰(`auth_token`)을 발급합니다. 퍼블리셔는 이 토큰을 키로 삼아 `hmac_sig` 서명을 페이로드에 동반해야 하며, 수신단(`verify_hmac`)에서 서명이 없거나 일치하지 않는 메시지는 즉시 드랍하여 다운그레이드 공격을 원천 차단합니다.
|
||||
- **롤아웃 전략**: 보안 스킴 갱신 시 송수신 노드 간 불일치로 인한 이벤트 드랍을 피하기 위해, 과도기적 하이브리드 포맷 전송(평문 유출 위험 있음)을 배제하고 **모든 노드를 일제히 업데이트하는 "동시 롤아웃(Simultaneous Rollout)"**을 채택해야 합니다.
|
||||
|
||||
---
|
||||
|
||||
## 3. 협업 워크플로우 실행 절차 (Workflow Loop)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
actor User as 사용자
|
||||
participant GM as General Manager
|
||||
participant DTL as Developer Team Leader
|
||||
participant RTL as Reviewer Team Leaders
|
||||
participant M as MQTT Backplane
|
||||
|
||||
User->>GM: 요구사항 전달
|
||||
GM->>DTL: 작업 위임 (예: 랜딩 페이지 제작)
|
||||
Note over DTL: 작업 분석, 세분화 및 subagent 병렬 구동
|
||||
DTL->>M: 'started' 이벤트 발행
|
||||
Note over DTL: 코드 변경 및 구현
|
||||
DTL->>M: 'completed' 발행
|
||||
DTL->>RTL: 리뷰 요청 (랜딩 페이지를 제작했습니다. 리뷰를 진행해주세요)
|
||||
Note over RTL: 교차 분석 & 검증
|
||||
alt 결함 발견 (리뷰어 피드백)
|
||||
RTL->>DTL: NOT PASS / 피드백 (반드시 이유와 확실한 개선 방향 포함)
|
||||
Note over DTL: DTL이 피드백의 타당성 검증
|
||||
alt 타당한 피드백
|
||||
Note over DTL: DTL이 수용하여 코드 수정
|
||||
else 타당하지 않은 피드백
|
||||
DTL->>RTL: 반론 및 거부 이유 전달 (부적절한 항목 미반영)
|
||||
end
|
||||
DTL->>RTL: 재리뷰 요청 (리뷰 안건 수정 완료)
|
||||
else 검증 통과
|
||||
RTL->>DTL: PASS
|
||||
end
|
||||
DTL->>GM: 최종 완료 신호 송신
|
||||
GM->>User: 사용자에게 작업 완료 통보
|
||||
```
|
||||
|
||||
1. **계획 수립 및 할당**: 총괄 매니저는 개발 팀장에게 작업을 인가합니다.
|
||||
2. **분석 및 내부 실행**: 개발 팀장은 작업을 분석하고 세분화하여 계획을 세운 뒤 내부 subagent를 가동하여 구현을 완료합니다. 이후 `started`를 거쳐 `completed` 이벤트를 발행하고 리뷰어에게 검수를 요청합니다.
|
||||
3. **이의 제기 및 정제 루프**:
|
||||
- 리뷰어 팀장은 상세 피드백 시 반드시 이유와 보완 방향을 제시해야 합니다.
|
||||
- 개발 팀장은 의견을 검토해 타당하면 수정하고, 타당하지 않으면 반론과 근거를 회신합니다.
|
||||
- 리뷰어 전원이 `PASS`를 인가할 때까지 이 과정이 반복됩니다.
|
||||
4. **최종 보고**: 개발 팀장이 총괄 매니저에게 완료 신호를 보내면 총괄 매니저가 사용자에게 완료를 알립니다.
|
||||
|
||||
---
|
||||
|
||||
## 4. 분석 인프라 패턴 & 실무 가이드 (Infra Patterns)
|
||||
|
||||
장기 실행 에이전트 분석 중 발생하는 유실 및 인프라적 장애를 예방하기 위한 중요 지침입니다.
|
||||
|
||||
### 📸 TUI 뷰포트 절단 방지 (Pane Snapshotting 3대 규칙)
|
||||
TMUX 환경에서 실행되는 에이전트가 화면 스크롤 한계로 인해 이전 출력이나 장문의 디버깅 로그를 잃지 않도록 아래의 **스냅샷 패턴을 의무적으로 수행**합니다.
|
||||
1. **Pre-brief Capture**: 작업 지침(Brief)을 전송한 직후, 즉시 해당 세션의 pane을 캡처(`capture-pane -S -200`)해두어 입력 기록의 시작점을 백업합니다.
|
||||
2. **Loop Snapshot**: 장기 실행(5분 이상) 중인 에이전트 세션의 경우, 주기적으로(예: 30초마다) 뷰포트를 스캔하여 증분 데이터를 `/tmp/pane-snap.txt`에 계속 누적(append) 기록합니다.
|
||||
3. **Post-job Capture**: 잡 완료/에러 반환 즉시 전체 pane 상태를 마지막으로 캡처하여 전체 작업 궤적을 보존합니다.
|
||||
|
||||
### 📄 장문 브리핑 전달 방식
|
||||
- TMUX `send-keys`나 입력 버퍼를 통해 수백 줄의 장문 지시나 프롬프트를 직렬로 입력하면, 에이전트의 TUI가 이를 모두 온전히 소화하지 못하고 일부 문자나 문단이 탈취/누락될 수 있습니다.
|
||||
- **해결 지침**: 지시 사항이 긴 경우, 반드시 `/tmp/brief-<job_id>.md` 등의 파일 경로로 지시문을 별도 작성해 전달하고, 에이전트에는 `"Read /tmp/brief-... and execute"` 라는 단순화된 실행 명령만 전달하십시오.
|
||||
|
||||
### ⏱️ 타임아웃 구성 및 정렬 규칙
|
||||
- **잡 실행 제한 (`timeout_sec` & `idle_timeout_sec`)**: 각 잡은 전체 실행 만료 시간(`timeout_sec`, 기본 3600s)과 메세지 미수신 유휴 시간(`idle_timeout_sec`, 기본 120s)을 독립적으로 가집니다.
|
||||
- **모니터 유휴 대기 (`SUB_IDLE_TIMEOUT`)**: 모니터 스크립트(`reconcile.sh`)의 유휴 대기 시간(`SUB_IDLE_TIMEOUT`) 기본값은 잡 최대 예산에 맞춰 `3600s`(1시간) 이상으로 항상 넉넉히 설정해야 합니다. 모니터가 작업 완료 전에 유휴 감지로 조기 자동 종료되어 백그라운드 태스크 관리를 소실하는 문제를 방지하기 위함입니다.
|
||||
|
||||
---
|
||||
|
||||
## 5. 새 프로젝트 적용 체크리스트 (Setup Checklist)
|
||||
|
||||
새 프로젝트에 이 에이전트 오케스트레이션 모델을 구축할 때의 체크리스트입니다.
|
||||
|
||||
- [ ] **가상환경 의존성**: `pyyaml`, `paho-mqtt` 등 필요한 Python 패키지가 `.venv` 또는 `requirements.txt`에 포함되었는가?
|
||||
- [ ] **환경 설정 파일**: MQTT 브로커 주소 및 보안 Credential이 `.env` 파일에 안전하게 로드되고 공유되는가?
|
||||
- [ ] **디렉토리 규약**: 레지스트리 경로(`.mam/jobs/`) 및 로깅 경로(`.mam/delegate_job_logs/`)가 `.gitignore`에 등록되었는가?
|
||||
- [ ] **스크립트 구비**: `mqtt_common.py`, `publish_event.py`, `job_subscriber.py`, `registry.py` 등의 핵심 모듈이 배치되었는가?
|
||||
- [ ] **HMAC 활성화**: 새로운 레지스트리 잡 발급 시 난수 기반의 `auth_token`이 정상적으로 주입되고, 서명 기반의 상호 인증이 활성화되는가?
|
||||
- [ ] **운영 헌장 배치**: 본 규약 파일(`AGENT.md`)이 새 프로젝트의 **.agents/ 디렉터리**에 배치되었는가? (프로젝트 루트를 깔끔하게 유지하면서도 온보딩하는 에이전트들이 규칙을 이해할 수 있도록 `.agents/` 경로 배치가 권장됩니다.)
|
||||
|
||||
---
|
||||
|
||||
*본 가이드는 협업 효율성과 코드 보안의 엄격한 균형을 유지하기 위한 규범입니다. 변경 사항이 필요한 경우 총괄 매니저 및 전체 팀장의 합의를 거쳐 본 문서를 업데이트해야 합니다.*
|
||||
|
||||
---
|
||||
|
||||
## 6. AIoT 멀티 에이전트 인터페이스 모듈 설계 지침 (A2A 및 Agent Card)
|
||||
|
||||
향후 연구 및 개발 단계에서 구현할 통합 인터페이스 모듈(Unified Interface Module)은 이종 에이전트 간의 자율 협업 및 상호운용성 확보를 위해 **A2A(Agent-to-Agent)** 표준을 포함하며, 다음의 설계 지침을 철저히 준수해야 합니다.
|
||||
|
||||
### 📇 1. 에이전트 카드(Agent Card)의 표준 지원
|
||||
- **규격화된 JSON 스키마**: 인터페이스 모듈은 에이전트의 메타데이터 및 입출력 인터페이스를 정의한 에이전트 카드를 JSON 형태로 자동 생성 및 서빙해야 합니다.
|
||||
- **필수 명세 항목**:
|
||||
- 에이전트의 명칭 및 페르소나/역할 (Name, Persona, Role)
|
||||
- 호출 가능한 엔드포인트 정보 (Endpoint URI 및 프로토콜 규격)
|
||||
- 입력 데이터 스키마 (Tool Schema 및 입출력 제약사항)
|
||||
- 보안 인증 및 인가 사양 (Authentication & Security Credentials)
|
||||
- **배포 및 탐색 규격**: 시스템 내의 다른 이종 에이전트들이 실시간으로 탐색할 수 있도록 `/.well-known/agent-card.json` 표준 경로를 통한 에이전트 카드 서빙 인프라를 내장해야 합니다.
|
||||
|
||||
### 📡 2. A2A 및 ACP 프로토콜 커넥터 설계
|
||||
- **프로토콜 상호운용성**: IBM ACP(Agent Communication Protocol)와 Linux Foundation A2A 표준의 병합 사양에 기반하여, 에이전트 간 비동기 위임(Task Delegation), 제어권 이관, 상태 동기화가 가능한 프로토콜 커넥터를 인터페이스 모듈 내에 구현해야 합니다.
|
||||
- **이벤트 기반 자율 제어**: 상시 센서 모니터링은 경량 물리 노드 및 모니터 프로그램이 수행하도록 분리하고, 감지된 물리 이벤트는 A2A 프로토콜 메시지를 통해 비동기적으로 특정 AI 에이전트에 작업을 자율 위임하고 처리 후 즉시 생명주기 및 제어권을 회수하는 비동기 인터페이스를 탑재해야 합니다.
|
||||
|
||||
### 🛠️ 3. 워크플로우 정합성 및 이슈 추적
|
||||
- **실행 일관성 보장**: 이종 에이전트가 동일한 작업을 재수행할 때 동일한 결과를 얻을 수 있도록 워크플로우 실행을 원자적(Atomic)으로 통제해야 합니다.
|
||||
- **표준 이슈 트래킹**: 실행 예외나 네트워크 단절 등의 장애가 발생한 경우, 복구 및 추적이 용이하도록 표준 규격화된 이슈 추적 인터페이스(Issue Tracking Interface)를 연동 및 마련해야 합니다.
|
||||
|
||||
### 🛡️ 4. 멱등성(Idempotency) 보장 스키마
|
||||
- **물리 제어 명령의 멱등성 분류**: 인터페이스 모듈은 전달되는 제어 명령을 멱등성 보장 여부에 따라 분류해야 합니다. 정보 조회·상태 설정 등 멱등성이 자연 보장되는 명령과, 농약 투포·급수·밸브 개폐 등 부작용이 누적되어 재실행 시 치명적 안전 사고로 이어질 수 있는 비멱등성(non-idempotent) 물리 제어 명령을 명시적으로 구분해야 합니다.
|
||||
- **중복 발행 억제 메커니즘**: 비동기 재시도 루프에서 동일한 제어 의도가 새로운 작업 ID로 중복 발행되는 것을 방지하기 위해, 제어 의도 식별자(Control Intent Key) 기반의 중복 억제(Deduplication) 스키마를 인터페이스 계층에 내장해야 합니다. 재시도 시 신규 작업 ID가 아닌 원본 제어 의도에 바인딩된 멱등성 키를 재사용하여 동일 제어의 재실행 가능성을 시스템 차원에서 통제해야 합니다.
|
||||
- **물리 장치측 사전 검증**: 중복 제어가 물리 장치에 도달하기 전에 인터페이스 모듈이 사전 검증(pre-flight check)을 수행하여, 이미 실행된 제어 의도에 대한 재실행 요청을 사전 차단하거나 장치측 상태와 대조 후 승인하는 안전망을 제공해야 합니다.
|
||||
@@ -0,0 +1,169 @@
|
||||
# AGENT.md
|
||||
|
||||
This document serves as the common guidelines and protocol for introducing the **MQTT messaging backplane and Tmux-based multi-agent orchestration workflow** to a new project. It defines the rules and architecture to ensure collaborating agents perform tasks safely, robustly, and consistently.
|
||||
|
||||
All agents working on a new project must read this document thoroughly and comply with the defined protocols before starting any tasks.
|
||||
|
||||
---
|
||||
|
||||
## 1. Agent Roles Definition (Agent Roles)
|
||||
|
||||
We clearly separate responsibilities and permissions between roles to reduce bottlenecks and enhance the quality of execution.
|
||||
|
||||
### 👑 General Manager (Orchestrator)
|
||||
- **Core Responsibility**: Interact directly with the user, receive high-level requirements, establish task plans, delegate tasks to Team Leaders, control the overall workflow, and report completion back to the user.
|
||||
- **Ambiguity Resolution**: If a user's requirements contain ambiguous details, do not guess. Immediately ask the user for clarification (we recommend using the `/grill-me` slash command).
|
||||
|
||||
### 👥 Team Leaders (팀장)
|
||||
Newly spawned agents (e.g., `antigravity`, `claude`, `cline`, `hermes`) act as **Team Leaders** of their respective groups. They receive delegated tasks from the General Manager and manage implementation or review workflows.
|
||||
- **Developer Team Leader (개발 팀장)**:
|
||||
- Receives tasks from the General Manager.
|
||||
- **Task Breakdown & Planning**: Thoroughly analyzes the task, breaks it down into small units, and creates a plan.
|
||||
- **Internal Parallelism**: Can run subagents in parallel internally to handle the delegated work.
|
||||
- **Review Integrity & Refusal**: Thoroughly reviews feedback from Reviewers. Adopts/implements recommendations if valid. If any recommendation is judged invalid, the Developer Team Leader must **not** implement it, but instead return the refutation along with detailed reasons to the Reviewer.
|
||||
- **Completion Signal**: Once all reviewers yield a `PASS` and changes are verified, the Developer Team Leader who first received the task sends a completion signal back to the General Manager.
|
||||
- **Reviewer Team Leader (리뷰어 팀장)**:
|
||||
- Receives review requests from the Developer Team Leader.
|
||||
- **Detailed Feedback with Directions**: Simply rejecting changes (`NOT PASS`) is forbidden. Reviewers **must** specify the exact reason for the issue and provide a concrete, stable, and verified alternative direction for improvement.
|
||||
- **Consensus Loop**: Engages in the review cycle until all objections are resolved and a final `PASS` is issued.
|
||||
|
||||
### 🛡️ Role Suitability Check Principle (자신의 역할 범위 수행 원칙)
|
||||
- Every agent must only perform tasks suitable for its designated role (e.g., Developer Team Leaders do not issue final reviews, and Reviewer Team Leaders do not write project code).
|
||||
- **If an agent receives a task that does not fit its role**, it must either:
|
||||
1. Recommend the optimal agent session to delegate the task to, or
|
||||
2. Perform the task directly if strictly necessary for project continuity.
|
||||
|
||||
---
|
||||
|
||||
## 2. Messaging Backplane & Registry Protocol
|
||||
|
||||
Asynchronous communication and state management between agents are controlled via distributed event channels and file/DB registries.
|
||||
|
||||
### 📡 MQTT Backplane
|
||||
- **Event Lifecycle**:
|
||||
- `started` (Job execution starts) ➡️ `progress`/`permission_required` (Share intermediate progress) ➡️ `completed` (Successful termination) or `error` (Failed termination)
|
||||
- `completed` and `error` are terminal events that are published exactly once.
|
||||
- **Publish/Subscribe Rules**:
|
||||
- Since MQTT does not guarantee persistent queues, the subscriber (`job_subscriber.py`) **must be running in the background before the agent starts** (the Subscribe-before-Publish principle).
|
||||
- When publishing terminal events, publish with `retain=True` on the broker so that subscribers joining late can still read the final state.
|
||||
- Generalize all transmitted data to ensure that sensitive secrets like passwords, private keys, or absolute system paths are not included.
|
||||
|
||||
### 🗃️ Registry & State Management
|
||||
- This architecture maintains two distinct registries based on their purpose:
|
||||
- **Job Registry**: The metadata and lifecycle of each asynchronous job are recorded in individual JSON files (`.mam/jobs/<id>.json`). Concurrency conflicts (claiming races) across multiple sessions are prevented via file-based `fcntl` advisory locks (`registry_lock` via `registry.py`).
|
||||
- **Session Registry**: TMUX monitoring states and running agent metadata are consistently controlled using a SQLite WAL database (`.mam/agent-sessions.db`) to support reliable concurrent transactions on a single host. However, since SQLite WAL mode does not guarantee complete file locking in Network File System (NFS) environments, we recommend using a local file system.
|
||||
|
||||
### 🛡️ Security Protocol (HMAC-SHA256)
|
||||
- **Unauthenticated PoC Mode**: If the `auth_token` in the job registry is set to `null` (the default PoC mode), signature verification is skipped and all events are accepted (`verify_hmac` always returns `True`).
|
||||
- **Authenticated Production Mode**: In production environments or integrations requiring authentication, a unique cryptographic token (`auth_token`) is issued for each job. The publisher must include an `hmac_sig` signature in the payload keyed by this token, and the receiving end (`verify_hmac`) will immediately drop messages that lack a signature or have mismatching signatures to prevent downgrade attacks.
|
||||
- **Rollout Strategy**: To avoid event drops caused by inconsistencies between publishing and receiving nodes when updating security schemes, hybrid transition formats (which risk leaking plaintext tokens) must not be used. Instead, adopt a **"Simultaneous Rollout"** where all nodes are updated at once.
|
||||
|
||||
---
|
||||
|
||||
## 3. Collaborative Workflow Execution Loop (Workflow Loop)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
actor User as User
|
||||
participant GM as General Manager
|
||||
participant DTL as Developer Team Leader
|
||||
participant RTL as Reviewer Team Leaders
|
||||
participant M as MQTT Backplane
|
||||
|
||||
User->>GM: Hand over requirements
|
||||
GM->>DTL: Delegate task (e.g., create landing page)
|
||||
Note over DTL: Analyze, breakdown & spawn parallel subagents
|
||||
DTL->>M: Publish 'started' event
|
||||
Note over DTL: Modify code & implement
|
||||
DTL->>M: Publish 'completed'
|
||||
DTL->>RTL: Request review (I created landing page. Please review it)
|
||||
Note over RTL: Cross-analysis & verification
|
||||
alt Defect Found (Reviewer feedback)
|
||||
RTL->>DTL: NOT PASS / Feedback (Must include reason & improvement direction)
|
||||
Note over DTL: DTL checks validity of suggestions
|
||||
alt Valid feedback
|
||||
Note over DTL: DTL adopts and modifies code
|
||||
else Invalid feedback
|
||||
DTL->>RTL: Send refutation & reasons (Did not reflect inappropriate parts)
|
||||
end
|
||||
DTL->>RTL: Request review again (Modified review items)
|
||||
else Verification Pass
|
||||
RTL->>DTL: PASS
|
||||
end
|
||||
DTL->>GM: Send completion signal
|
||||
GM->>User: Notify task completion
|
||||
```
|
||||
|
||||
1. **Planning and Allocation**: The General Manager delegates the task to the Developer Team Leader.
|
||||
2. **Analysis and Internal Execution**: The Developer Team Leader analyzes the task, breaks it down, plans execution, and optionally spawns parallel subagents. It publishes `started`, completes the task, and requests review from the Reviewer Team Leader.
|
||||
3. **Objection & Refinement Loop**:
|
||||
- The Reviewer Team Leader must provide clear reasons and improvement directions for any issues.
|
||||
- The Developer Team Leader validates the feedback. Valid suggestions are implemented; invalid ones are refuted with reasons and returned to the reviewer.
|
||||
- This cycle repeats until all reviewers issue a `PASS`.
|
||||
4. **Completion and Report**: The Developer Team Leader sends the final completion signal to the General Manager, who notifies the user.
|
||||
|
||||
---
|
||||
|
||||
## 4. Analysis Infrastructure Patterns & Practical Guide (Infra Patterns)
|
||||
|
||||
These are critical instructions for preventing data loss and infrastructure-level failures during long-running agent analyses.
|
||||
|
||||
### 📸 Preventing TUI Viewport Truncation (The 3 Pane Snapshotting Rules)
|
||||
To ensure that agents running in TMUX environments do not lose debug logs or previous outputs due to screen scrollback limits, the following **snapshotting pattern must be enforced**:
|
||||
1. **Pre-brief Capture**: Capture the pane (`capture-pane -S -200`) immediately after sending the task instruction (Brief) to back up the starting point of the input history.
|
||||
2. **Loop Snapshot**: For long-running agent sessions (5 minutes or more), periodically (e.g., every 30 seconds) scan the viewport and append the incremental data to `/tmp/pane-snap.txt`.
|
||||
3. **Post-job Capture**: Capture the complete pane state one final time immediately after a job completes or returns an error to preserve the entire execution trajectory.
|
||||
|
||||
### 📄 Handling Long Briefing Instructions
|
||||
- Sending long instructions or prompts (hundreds of lines) sequentially via TMUX `send-keys` or input buffers can overwhelm the agent's TUI, leading to lost characters or truncated paragraphs.
|
||||
- **Resolution**: If instructions are long, write them separately to a file path (e.g., `/tmp/brief-<job_id>.md`) and send a simplified execution command to the agent: `"Read /tmp/brief-... and execute"`.
|
||||
|
||||
### ⏱️ Timeout Configuration & Alignment Rules
|
||||
- **Job Execution Limits (`timeout_sec` & `idle_timeout_sec`)**: Each job independently manages its overall execution timeout (`timeout_sec`, default 3600s) and idle timeout without receiving messages (`idle_timeout_sec`, default 120s).
|
||||
- **Monitor Idle Waiting (`SUB_IDLE_TIMEOUT`)**: The idle timeout for the monitor script (`reconcile.sh`), `SUB_IDLE_TIMEOUT`, must always be set generously to `3600s` (1 hour) or more to align with the maximum job budget. This prevents the monitor from terminating early due to idle detection, which would lose control over background tasks before they finish.
|
||||
|
||||
---
|
||||
|
||||
## 5. Setup Checklist for New Projects (Setup Checklist)
|
||||
|
||||
Use this checklist when deploying this agent orchestration model to a new project:
|
||||
|
||||
- [ ] **Virtualenv Dependencies**: Are required Python packages like `pyyaml` and `paho-mqtt` included in `.venv` or `requirements.txt`?
|
||||
- [ ] **Configuration File**: Are the MQTT broker address and security credentials safely loaded and shared via the `.env` file?
|
||||
- [ ] **Directory Convention**: Are the registry path (`.mam/jobs/`) and logging path (`.mam/delegate_job_logs/`) added to `.gitignore`?
|
||||
- [ ] **Core Scripts**: Are the core scripts (`mqtt_common.py`, `publish_event.py`, `job_subscriber.py`, and `registry.py`) in place?
|
||||
- [ ] **HMAC Enablement**: When a new registry job is created, is a random `auth_token` correctly injected, and is signature-based mutual authentication active?
|
||||
- [ ] **Charter Placement**: Is this protocol file (`AGENT.md`) placed in the **.agents/ directory** of the new project? (Placing it in `.agents/` is essential to keep the project root clean while allowing onboarding agents to align on the rules.)
|
||||
|
||||
---
|
||||
|
||||
*This guide balances collaboration efficiency with strict code security. Any required changes must be discussed and agreed upon by the General Manager and all Team Leaders before updating this document.*
|
||||
|
||||
---
|
||||
|
||||
## 6. AIoT Multi-Agent Interface Module Design Guidelines (A2A and Agent Card)
|
||||
|
||||
The Unified Interface Module to be implemented in future research and development stages must incorporate the **A2A (Agent-to-Agent)** standard to achieve autonomous collaboration and interoperability among heterogeneous agents, and must comply with the following design guidelines:
|
||||
|
||||
### 📇 1. Standard Support for Agent Cards
|
||||
- **Structured JSON Schema**: The interface module must automatically generate and serve Agent Cards defining the agent's metadata and input/output interfaces in JSON format.
|
||||
- **Mandatory Specifications**:
|
||||
- Agent Name and Persona/Role (Name, Persona, Role)
|
||||
- Callable Endpoint Information (Endpoint URI and Protocol Specifications)
|
||||
- Input Data Schema (Tool Schema and I/O constraints)
|
||||
- Authentication and Authorization Specifications (Authentication & Security Credentials)
|
||||
- **Deployment and Discovery Specification**: An Agent Card serving infrastructure must be built-in to serve the metadata via the standard path `/.well-known/agent-card.json`, allowing other heterogeneous agents in the system to discover it in real-time.
|
||||
|
||||
### 📡 2. A2A and ACP Protocol Connector Design
|
||||
- **Protocol Interoperability**: Implement a protocol connector inside the interface module based on the merged specifications of IBM ACP (Agent Communication Protocol) and Linux Foundation A2A standards to handle task delegation, control transfer, and state synchronization.
|
||||
- **Event-Driven Autonomous Control**: Decouple sensor monitoring to be executed by lightweight physical nodes and monitoring programs, and implement an asynchronous interface that delegates tasks to specific AI agents upon detecting physical events via A2A protocol messages, reclaiming lifecycles and control rights immediately upon task completion.
|
||||
|
||||
### 🛠️ 3. Workflow Coherence and Issue Tracking
|
||||
- **Execution Consistency**: Control workflow execution atomically to guarantee identical outputs when heterogeneous agents re-execute the same tasks.
|
||||
- **Standardized Issue Tracking**: Provide a standardized Issue Tracking Interface to support debugging, tracking, and recovery in case of execution exceptions or network disconnections.
|
||||
|
||||
### 🛡️ 4. Idempotency Assurance Schema
|
||||
- **Idempotency Classification of Physical Control Commands**: The interface module must classify control commands by their idempotency guarantees. It must explicitly distinguish between commands where idempotency is naturally guaranteed (e.g., information queries, state settings) and non-idempotent physical control commands where side effects accumulate and re-execution may lead to critical safety incidents (e.g., pesticide spraying, irrigation, valve actuation).
|
||||
- **Duplicate Dispatch Suppression Mechanism**: To prevent the same control intent from being dispatched under a new job ID during asynchronous retry loops, a deduplication schema based on a Control Intent Key must be embedded in the interface layer. Upon retry, the system must reuse the idempotency key bound to the original control intent rather than issuing a new job ID, thereby systemically controlling the possibility of duplicate execution of the same control.
|
||||
- **Pre-flight Verification at the Interface Layer**: Before a duplicate control reaches the physical device, the interface module must perform a pre-flight check that pre-blocks re-execution requests for already-executed control intents, or approves them only after cross-checking against device-side state, providing a safety net against physical double actuation.
|
||||
+45
-3
@@ -19,7 +19,7 @@ WORKSPACE_ROOT="$(cd "$SKILL_DIR/../.." && pwd)"
|
||||
AGENT_SESSIONS_YAML="${AGENT_SESSIONS_YAML:-$WORKSPACE_ROOT/.mam/agent-sessions.yaml}"
|
||||
|
||||
# Workspace-relative defaults with environment overrides (Phase Z)
|
||||
HOME_DIR="${HOME_DIR:-$WORKSPACE_ROOT}"
|
||||
HOME_DIR="${HOME_DIR:-$HOME}"
|
||||
CLAUDE_PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$HOME/.claude/projects}"
|
||||
LOCAL_BIN="${LOCAL_BIN:-$HOME/.local/bin}"
|
||||
|
||||
@@ -305,6 +305,8 @@ def _validate(d):
|
||||
raise SystemExit(f"VALIDATE: tmux_sessions[{i}] not a mapping")
|
||||
if not s.get('name') or not s.get('status'):
|
||||
raise SystemExit(f"VALIDATE: tmux_sessions[{i}] missing name/status")
|
||||
if s.get('role') is not None and (not isinstance(s['role'], str) or not s['role'].strip()):
|
||||
raise SystemExit(f"VALIDATE: tmux_sessions[{i}] {s.get('name')!r} role must be a non-empty string")
|
||||
if s['status'] not in valid:
|
||||
raise SystemExit(f"VALIDATE: tmux_sessions[{i}] {s.get('name')!r} bad status {s['status']!r}")
|
||||
if not isinstance(s.get('pane'), dict):
|
||||
@@ -366,10 +368,17 @@ try:
|
||||
d['tmux_sessions'] = []
|
||||
|
||||
old_terminals = get_terminal_set(d)
|
||||
old_roles = {s.get('name'): s.get('role') for s in db_sessions if s.get('role')}
|
||||
|
||||
# --- caller mutation (module scope: sees d, yaml, os, glob, subprocess) ---
|
||||
exec(compile(os.environ['AGENT_SESSIONS_MUTATION'], '<mutation>', 'exec'), globals())
|
||||
|
||||
# Role immutability check
|
||||
for s in d.get('tmux_sessions', []):
|
||||
name = s.get('name')
|
||||
if name in old_roles and s.get('role') != old_roles[name]:
|
||||
raise SystemExit(f"VALIDATE: role of session {name!r} cannot be modified from {old_roles[name]!r} to {s.get('role')!r}")
|
||||
|
||||
_validate(d)
|
||||
|
||||
# Separate globals and sessions for normalization
|
||||
@@ -475,7 +484,7 @@ def db_exists(uuid):
|
||||
|
||||
|
||||
def hermes_exists(uuid):
|
||||
hdb = f"{home}/.mam/state.db"
|
||||
hdb = f"{home}/.hermes/state.db"
|
||||
if not os.path.exists(hdb):
|
||||
return False
|
||||
try:
|
||||
@@ -487,6 +496,10 @@ def hermes_exists(uuid):
|
||||
return False
|
||||
|
||||
|
||||
def cline_exists(uuid):
|
||||
return os.path.exists(f"{home}/.cline/data/sessions/{uuid}/{uuid}.json")
|
||||
|
||||
|
||||
def emit(u):
|
||||
print(u)
|
||||
raise SystemExit(0)
|
||||
@@ -536,6 +549,10 @@ for s in sessions:
|
||||
cand = s.get('hermes_conversation_id_own')
|
||||
if cand and hermes_exists(cand):
|
||||
emit(cand)
|
||||
if agent == 'cline' and name.endswith('-creator-cline'):
|
||||
cand = s.get('cline_conversation_id_own')
|
||||
if cand and cline_exists(cand):
|
||||
emit(cand)
|
||||
|
||||
# 2) disk scan scoped to THIS workspace
|
||||
if agent == 'claude':
|
||||
@@ -565,7 +582,7 @@ elif agent == 'agy':
|
||||
if cand and db_exists(cand):
|
||||
emit(cand)
|
||||
elif agent == 'hermes':
|
||||
hdb = f"{home}/.mam/state.db"
|
||||
hdb = f"{home}/.hermes/state.db"
|
||||
if os.path.exists(hdb):
|
||||
cand = None
|
||||
try:
|
||||
@@ -578,6 +595,27 @@ elif agent == 'hermes':
|
||||
cand = None
|
||||
if cand:
|
||||
emit(cand)
|
||||
elif agent == 'cline':
|
||||
sessions_dir = f"{home}/.cline/data/sessions"
|
||||
if os.path.isdir(sessions_dir):
|
||||
candidates = []
|
||||
for session_folder in glob.glob(f"{sessions_dir}/*"):
|
||||
if os.path.isdir(session_folder):
|
||||
folder_name = os.path.basename(session_folder)
|
||||
json_file = f"{session_folder}/{folder_name}.json"
|
||||
if os.path.exists(json_file):
|
||||
candidates.append(json_file)
|
||||
candidates.sort(key=os.path.getmtime, reverse=True)
|
||||
for j in candidates:
|
||||
try:
|
||||
with open(j) as f:
|
||||
sdata = json.load(f)
|
||||
if sdata.get('cwd') == ws or sdata.get('workspace_root') == ws:
|
||||
sid = sdata.get('session_id')
|
||||
if sid:
|
||||
emit(sid)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 3) agent_identities cache, ONLY when its project_cwd == this workspace
|
||||
ai = {}
|
||||
@@ -609,6 +647,10 @@ if ai_agent.get('project_cwd') == ws:
|
||||
cand = ai_agent.get('session_id') or ai.get('conversation_id')
|
||||
if cand and hermes_exists(cand):
|
||||
emit(cand)
|
||||
elif agent == 'cline':
|
||||
cand = ai_agent.get('session_id') or ai.get('conversation_id')
|
||||
if cand and cline_exists(cand):
|
||||
emit(cand)
|
||||
|
||||
print('')
|
||||
PYEOF
|
||||
|
||||
@@ -74,12 +74,12 @@ To prevent this, you can run this skill inside an **isolated tmux server** using
|
||||
```
|
||||
2. **Via Option Flag**:
|
||||
```bash
|
||||
bash scripts/create_session.sh --workspace /path/to/project --agent claude --tmux-server multi-agent-canary
|
||||
bash scripts/create_session.sh --workspace /path/to/project --agent claude --role developer --tmux-server multi-agent-canary
|
||||
```
|
||||
3. **Submit Job Integration**:
|
||||
You can automatically register a delegated job with a prompt when creating a session:
|
||||
```bash
|
||||
bash scripts/create_session.sh --workspace /path/to/project --agent claude --submit-job "Task prompt here"
|
||||
bash scripts/create_session.sh --workspace /path/to/project --agent claude --role developer --submit-job "Task prompt here"
|
||||
```
|
||||
|
||||
### Recommended Alias
|
||||
@@ -173,7 +173,7 @@ Use the `agent-sessions-yaml-edit` script in `scripts/` to safely append (preser
|
||||
|
||||
```bash
|
||||
bash .agents/skills/multi-agent-mux-create/scripts/create_session.sh \
|
||||
--workspace "$WORKSPACE" --agent "$AGENT" --session "$SESSION_NAME"
|
||||
--workspace "$WORKSPACE" --agent "$AGENT" --role "$ROLE" --session "$SESSION_NAME"
|
||||
```
|
||||
|
||||
The script handles the YAML append, pane capture, and the `last_visible_status` placeholder.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# create_session.sh — multi-agent-mux-create 의 부속 스크립트
|
||||
# Usage:
|
||||
# bash create_session.sh --workspace <path> --agent <claude|agy> [--session <name>] [--wrapper]
|
||||
# bash create_session.sh --workspace <path> --agent <claude|agy> --role <role> [--session <name>] [--wrapper]
|
||||
#
|
||||
# 동작:
|
||||
# 1) preflight: tmux/claude/agy 가용성, workspace 존재
|
||||
@@ -23,11 +23,12 @@ source "$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)/lib.sh"
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $0 --workspace <path> --agent <claude|agy|hermes> [options]
|
||||
Usage: $0 --workspace <path> --agent <claude|agy|hermes|cline> --role <role> [options]
|
||||
|
||||
Options:
|
||||
--workspace PATH project directory (required)
|
||||
--agent AGENT claude | agy | hermes (required)
|
||||
--agent AGENT claude | agy | hermes | cline (required)
|
||||
--role ROLE assigned role (required)
|
||||
--session NAME tmux session name (default: derived from workspace)
|
||||
--wrapper force use of ~/.local/bin/<session> wrapper even if not present
|
||||
--dry-run print commands without executing
|
||||
@@ -39,6 +40,7 @@ EOF
|
||||
|
||||
WORKSPACE=""
|
||||
AGENT=""
|
||||
ROLE=""
|
||||
SESSION_NAME=""
|
||||
USE_WRAPPER=0
|
||||
DRY_RUN=0
|
||||
@@ -49,6 +51,7 @@ while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--workspace) WORKSPACE="$2"; shift 2 ;;
|
||||
--agent) AGENT="$2"; shift 2 ;;
|
||||
--role) ROLE="$2"; shift 2 ;;
|
||||
--session) SESSION_NAME="$2"; shift 2 ;;
|
||||
--wrapper) USE_WRAPPER=1; shift ;;
|
||||
--dry-run) DRY_RUN=1; shift ;;
|
||||
@@ -66,6 +69,7 @@ fi
|
||||
# Preflight
|
||||
[ -n "$WORKSPACE" ] || { echo "ERROR: --workspace required" >&2; usage; exit 2; }
|
||||
[ -n "$AGENT" ] || { echo "ERROR: --agent required" >&2; usage; exit 2; }
|
||||
[ -n "$ROLE" ] || { echo "ERROR: --role required" >&2; usage; exit 2; }
|
||||
[ -d "$WORKSPACE" ] || { echo "ERROR: workspace $WORKSPACE not a directory" >&2; exit 1; }
|
||||
command -v tmux >/dev/null || { echo "ERROR: tmux not installed" >&2; exit 1; }
|
||||
command -v "$AGENT" >/dev/null || { echo "ERROR: $AGENT CLI not in PATH" >&2; exit 1; }
|
||||
@@ -86,6 +90,11 @@ elif [ "$AGENT" = "hermes" ]; then
|
||||
echo "ERROR: hermes is not functional. Run 'hermes setup' first." >&2
|
||||
exit 1
|
||||
fi
|
||||
elif [ "$AGENT" = "cline" ]; then
|
||||
if ! cline history --json >/dev/null 2>&1; then
|
||||
echo "ERROR: cline is not functional or configured." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 세션 이름 — lib.sh::derive_session_name 이 단일 소스 (P0-A)
|
||||
@@ -119,7 +128,10 @@ spawn() {
|
||||
hermes)
|
||||
_tmux new-session -d -s "$SESSION_NAME" -x 140 -y 40 -c "$WORKSPACE" "hermes"
|
||||
;;
|
||||
*) echo "ERROR: --agent must be claude, agy or hermes, got: $AGENT" >&2; exit 2 ;;
|
||||
cline)
|
||||
_tmux new-session -d -s "$SESSION_NAME" -x 140 -y 40 -c "$WORKSPACE" "cline -i"
|
||||
;;
|
||||
*) echo "ERROR: --agent must be claude, agy, hermes or cline, got: $AGENT" >&2; exit 2 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
@@ -145,6 +157,7 @@ case "$AGENT" in
|
||||
claude) CMD_FULL='claude --dangerously-skip-permissions' ;;
|
||||
agy) CMD_FULL='agy --dangerously-skip-permissions' ;;
|
||||
hermes) CMD_FULL='hermes' ;;
|
||||
cline) CMD_FULL='cline -i' ;;
|
||||
esac
|
||||
|
||||
# 시작 명령
|
||||
@@ -161,7 +174,7 @@ case "$AGENT" in
|
||||
START_CMD="$local_tmux new-session -d -s \"$SESSION_NAME\" -x 140 -y 40 -c \"$WORKSPACE\" \"claude --dangerously-skip-permissions\""
|
||||
fi
|
||||
;;
|
||||
agy|hermes)
|
||||
agy|hermes|cline)
|
||||
START_CMD="$local_tmux new-session -d -s \"$SESSION_NAME\" -x 140 -y 40 -c \"$WORKSPACE\" \"$CMD_FULL\""
|
||||
;;
|
||||
esac
|
||||
@@ -174,6 +187,8 @@ if [ -n "$SUBMIT_JOB_PROMPT" ]; then
|
||||
delegate_agent="claude-code"
|
||||
elif [ "$AGENT" = "hermes" ]; then
|
||||
delegate_agent="hermes-agent"
|
||||
elif [ "$AGENT" = "cline" ]; then
|
||||
delegate_agent="cline-agent"
|
||||
else
|
||||
delegate_agent="antigravity-cli"
|
||||
fi
|
||||
@@ -191,7 +206,7 @@ fi
|
||||
# 모든 값은 환경변수로 전달 — heredoc interpolation 없음 (P1-B).
|
||||
# 자식 pid 는 bash 에서 pgrep 으로 미리 구함 (P2: 도구명 필터).
|
||||
CHILD_PID=0
|
||||
if { [ "$AGENT" = "agy" ] || [ "$AGENT" = "hermes" ]; } && [ -n "$PANE_PID" ]; then
|
||||
if { [ "$AGENT" = "agy" ] || [ "$AGENT" = "hermes" ] || [ "$AGENT" = "cline" ]; } && [ -n "$PANE_PID" ]; then
|
||||
CHILD_PID=$(pgrep -P "$PANE_PID" -x "$AGENT" 2>/dev/null | head -1 || true)
|
||||
CHILD_PID="${CHILD_PID:-0}"
|
||||
fi
|
||||
@@ -201,9 +216,10 @@ atomic_dump_yaml "$AGENT_SESSIONS_YAML" \
|
||||
TMUX_EPOCH="$TMUX_EPOCH" PANE_PID="$PANE_PID" PANE_CWD="$PANE_CWD" \
|
||||
CMD_FULL="$CMD_FULL" START_CMD="$START_CMD" CHILD_PID="$CHILD_PID" \
|
||||
TMUX_SERVER_NAME="${TMUX_SERVER_NAME:-default}" \
|
||||
DELEGATE_JOB_ID="$DELEGATE_JOB_ID" <<'PYEOF'
|
||||
DELEGATE_JOB_ID="$DELEGATE_JOB_ID" ROLE="$ROLE" <<'PYEOF'
|
||||
name = os.environ['SESSION_NAME']
|
||||
agent = os.environ['AGENT']
|
||||
role = os.environ['ROLE']
|
||||
pid = os.environ.get('PANE_PID', '')
|
||||
epoch = os.environ.get('TMUX_EPOCH', '')
|
||||
server_name = os.environ.get('TMUX_SERVER_NAME', 'default')
|
||||
@@ -222,6 +238,7 @@ sessions[:] = [s for s in sessions if s.get('name') != name]
|
||||
entry = {
|
||||
'name': name,
|
||||
'status': 'running',
|
||||
'role': role,
|
||||
'tmux_session_created_at': os.environ['NOW_ISO'],
|
||||
'tmux_session_epoch': int(epoch) if epoch.isdigit() else 0,
|
||||
'tmux_server': server_name,
|
||||
@@ -265,6 +282,11 @@ elif agent == 'hermes':
|
||||
entry['child_pid'] = int(cp) if cp.isdigit() else 0
|
||||
entry['hermes_conversation_id_own'] = None
|
||||
entry['last_visible_status'] = "TUI started; awaiting first user message"
|
||||
elif agent == 'cline':
|
||||
cp = os.environ.get('CHILD_PID', '0')
|
||||
entry['child_pid'] = int(cp) if cp.isdigit() else 0
|
||||
entry['cline_conversation_id_own'] = None
|
||||
entry['last_visible_status'] = "TUI started; awaiting first user message"
|
||||
|
||||
sessions.append(entry)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# multi-agent-mux-delegate-job 스킬
|
||||
|
||||
작업(Job)을 자율 에이전트(claude-code/codex/opencode/human)에게 위임하고 MQTT
|
||||
이벤트 채널로 비동기 관찰하는 Hermes 스킬. **시작점은 [`SKILL.md`](./SKILL.md).**
|
||||
작업(Job)을 자율 에이전트(claude-code/hermes/agy/cline/codex/opencode/human)에게 위임하고 MQTT
|
||||
이벤트 채널로 비동기 관찰하는 범용 에이전트 협업 스킬. **시작점은 [`SKILL.md`](./SKILL.md).**
|
||||
|
||||
- 프로토콜/스키마: [`job-protocol.md`](./job-protocol.md)
|
||||
- 브로커 PoC→운영 전환: [`mqtt-broker-setup.md`](./mqtt-broker-setup.md)
|
||||
|
||||
@@ -1,385 +1,94 @@
|
||||
---
|
||||
name: multi-agent-mux-delegate-job
|
||||
description: "Delegate a unit of work to any autonomous agent (claude-code, codex, opencode, or a human) and observe it asynchronously over an MQTT event channel. Each job gets a unique id, a registry record (prompt, broker, status, timeouts), and a single per-job topic that carries started/permission_required/progress/completed/error events as schema-versioned JSON. The delegator starts a subscriber first, runs the agent, and treats a completed/error event or a timeout as the job's terminal state. Ships a working reference implementation (publish_event.py, job_subscriber.py, registry.py, mqtt_common.py, multi-agent-mux-delegate-job wrapper) plus a PoC-to-production path: validate on a public broker, then move to an authenticated TLS broker by changing config only — no code change. Use when you need fire-and-observe delegation, multi-job fan-out across tmux sessions, or a uniform completion-signal protocol shared by several agent types."
|
||||
version: 1.0.0
|
||||
author: Hermes Agent
|
||||
description: "Delegate a unit of work to any autonomous agent (claude-code, hermes, agy, cline, codex, or a human) and observe it asynchronously over an MQTT event channel. Supported roles include orchestrator, worker, and reviewer."
|
||||
version: 1.1.0
|
||||
author: Multi-Agent System
|
||||
license: MIT
|
||||
platforms: [linux, macos, windows]
|
||||
metadata:
|
||||
hermes:
|
||||
tags: [agent-delegation, mqtt, jobs, orchestration, async-completion]
|
||||
related_skills: [claude-code, codex, opencode, hermes-agent-skill-authoring]
|
||||
---
|
||||
|
||||
# multi-agent-mux-delegate-job — Async Job Delegation over MQTT
|
||||
|
||||
Delegate a unit of work to an autonomous agent, then **observe** it instead of
|
||||
blocking on it. Every job gets a unique id and a registry record; the agent
|
||||
publishes lifecycle events (`started`, `permission_required`, `progress`,
|
||||
`completed`, `error`) to a per-job MQTT topic; the delegator subscribes and
|
||||
treats `completed`/`error` — or a timeout — as the terminal state.
|
||||
Delegate a unit of work to any autonomous agent, then **observe** it asynchronously instead of blocking. Every job gets a unique ID and a registry record. The worker agent publishes lifecycle events (`started`, `permission_required`, `progress`, `completed`, `error`) to a per-job MQTT topic, and the delegator/orchestrator subscribes to verify the final state.
|
||||
|
||||
This skill is a **reference implementation**: copy the files in this directory
|
||||
into your project and customise. The `communication_over_mqtt` project is the
|
||||
canonical concrete instance.
|
||||
This skill allows any agent (`claude-code`, `hermes`, `agy`, `cline`, etc.) to play any role: **Orchestrator/Delegator**, **Worker/Implementer**, or **Reviewer**.
|
||||
|
||||
## Overview
|
||||
---
|
||||
|
||||
The model is deliberately small. A **job** is one delegated task. An **agent**
|
||||
is a worker (a claude-code tmux session, a codex run, a human). The **registry**
|
||||
(`.mam/jobs/<id>.json`) holds everything about a job so nothing important
|
||||
lives in environment variables — which means one tmux session can process many
|
||||
jobs sequentially, and many sessions can fan out in parallel, with no env
|
||||
collisions. The **event channel** is one MQTT topic per job carrying JSON
|
||||
payloads; `event` discriminates the type.
|
||||
## Roles in Multi-Agent Mux
|
||||
|
||||
Responsibility is split into exactly one entry point each:
|
||||
[`publish_event.py`](./scripts/publish_event.py) emits events (registry lookup,
|
||||
monotonic `seq`, retry+backoff) and [`job_subscriber.py`](./scripts/job_subscriber.py)
|
||||
observes them (timeouts, terminal state machine, defensive parsing). Shared
|
||||
logic lives in [`mqtt_common.py`](./scripts/mqtt_common.py); registry I/O in
|
||||
[`registry.py`](./scripts/registry.py). The demo `publisher.py`/`subscriber.py`
|
||||
in the host project stay frozen.
|
||||
- **Orchestrator (Delegator)**: Initiates the job, coordinates other agents, handles loops and reviews, and commits final changes.
|
||||
- **Worker (Implementer)**: Receives the brief file or task prompt, performs the implementation, and emits started/completed/error events.
|
||||
- **Reviewer**: Evaluates git diffs or artifacts produced by the worker, and responds with a `completed` event containing `"PASS"` or feedback.
|
||||
|
||||
Two stages, same code. **PoC** runs on the public `broker.hivemq.com` to wire up
|
||||
the protocol. **Production** moves to your own authenticated TLS broker — the
|
||||
switch is **config only** (env vars + the registry `broker.*` block), never a
|
||||
code change. See [`mqtt-broker-setup.md`](./mqtt-broker-setup.md).
|
||||
---
|
||||
|
||||
## When to Use / When NOT to Use
|
||||
## Core Commands (CLI)
|
||||
|
||||
**Use when:**
|
||||
- you want **fire-and-observe** delegation — kick off work and get a completion
|
||||
signal rather than blocking a terminal;
|
||||
- several agent types (claude-code, codex, opencode, human) must follow **one**
|
||||
completion protocol;
|
||||
- you need **multi-job fan-out** across tmux sessions with safe job claiming;
|
||||
- you want a clean PoC → authenticated-broker upgrade path.
|
||||
|
||||
**Do NOT use when:**
|
||||
- a one-shot `claude -p '…'` that returns inline is enough (no async signal
|
||||
needed) — just use the [claude-code](../claude-code/SKILL.md) skill directly;
|
||||
- you need request/response RPC or large artifact transfer (this is a
|
||||
one-direction event stream, not a data bus);
|
||||
- the payload would carry secrets and you're still on the public broker — move
|
||||
to the own-broker stage first.
|
||||
|
||||
## Quick Start
|
||||
|
||||
The one-line wrapper handles register + subscriber-first + agent launch. If
|
||||
you're new, **start here** and only fall back to the manual 5-step flow when
|
||||
you need finer control.
|
||||
The `multi-agent-mux-delegate-job` bash wrapper handles job registration, subscriber management, agent session targeting, and validation hooks:
|
||||
|
||||
```bash
|
||||
# 1) one line: register → start subscriber → launch agent in tmux
|
||||
# (uses public broker by default; last stdout line is the audit-log dir)
|
||||
# 1) Submit a new job to a targeted agent session (e.g. tmux session name 'demo')
|
||||
multi-agent-mux-delegate-job submit \
|
||||
--agent claude-code \
|
||||
--prompt "정렬 문제 10개를 만들어 sort_problems.md로 저장" \
|
||||
--workdir /path/to/project \
|
||||
--agent-session tmux:demo \
|
||||
--agent <claude-code|hermes-agent|agy-agent|cline-agent|human> \
|
||||
--agent-session tmux:<session_name> \
|
||||
--prompt "Task description or instructions here" \
|
||||
--timeout 3600 --idle-timeout 120
|
||||
# → stdout: registered job: <JID>
|
||||
# subscriber pid: …
|
||||
# agent launched in tmux session: demo
|
||||
# subscriber output: <one line per event>
|
||||
# /path/to/project/.mam/delegate_job_logs/<JID> ← audit log dir
|
||||
|
||||
# 2) at any time, query the job or its audit log
|
||||
multi-agent-mux-delegate-job status --job <JID>
|
||||
multi-agent-mux-delegate-job logs <JID> # pretty timeline
|
||||
multi-agent-mux-delegate-job logs --list # every job, live status
|
||||
# 2) Submit a job with a feedback loop (Worker-Reviewer Loop)
|
||||
multi-agent-mux-delegate-job submit \
|
||||
--agent <worker_agent> --agent-session tmux:<worker_session> \
|
||||
--type loop --reviewer <reviewer_agent> --reviewer-session tmux:<reviewer_session> \
|
||||
--prompt "Task description"
|
||||
|
||||
# 3) run a user-supplied validator against the job's artifacts
|
||||
multi-agent-mux-delegate-job verify --job <JID> --validate ./validate.sh
|
||||
# 3) Check job status and audit logs
|
||||
multi-agent-mux-delegate-job status --job <JOB_ID>
|
||||
multi-agent-mux-delegate-job logs <JOB_ID> # Chronological log of events
|
||||
multi-agent-mux-delegate-job list # Summary of all registered jobs
|
||||
|
||||
# 4) Verify job artifacts with a validation script
|
||||
multi-agent-mux-delegate-job verify --job <JOB_ID> --validate ./validate.sh
|
||||
```
|
||||
|
||||
The wrapper enforces the **subscribe-before-publish** ordering and **forwards
|
||||
the freshly-minted `JOB_ID` into the agent's prompt** (so the agent calls
|
||||
`publish_event.py --job <JID>` with the right id — see Pitfall §"Wrong job_id
|
||||
propagated to the agent"). When you need finer control, the manual flow is:
|
||||
---
|
||||
|
||||
```bash
|
||||
# Manual 5-step (same outcome, more knobs)
|
||||
PY=.venv/bin/python
|
||||
SKILL=./.agents/skills/multi-agent-mux-delegate-job/scripts
|
||||
## Task Delegation Types
|
||||
|
||||
# 1) register
|
||||
JID=$($PY "$SKILL/registry.py" register \
|
||||
--prompt "…" --agent claude-code --agent-session tmux:demo \
|
||||
--timeout 3600 --idle-timeout 120)
|
||||
Supported job types include:
|
||||
- `direct` (default): Single agent execution (direct tasking).
|
||||
- `loop` (Worker-Reviewer Loop): Alternates worker execution and reviewer evaluation until reviewer approves (`PASS`) or iterations run out.
|
||||
- `discuss` (Research & Discussion): Collaboration between two agents to reach a consensus (e.g., agreeing on a design or plan).
|
||||
|
||||
# 2) START THE SUBSCRIBER FIRST (MQTT does not queue non-retained msgs)
|
||||
$PY "$SKILL/job_subscriber.py" --job "$JID" --timeout 3600 --idle-timeout 120 &
|
||||
For detailed state machine diagrams and configurations, see [DELEGATION_TYPES.md](./DELEGATION_TYPES.md).
|
||||
|
||||
# 3) pass JID to the agent and instruct it to publish events with --job "$JID"
|
||||
# (don't hard-code a job id you saw earlier — see Pitfall §"Wrong job_id")
|
||||
---
|
||||
|
||||
# 4) on completion the subscriber prints events and exits 0/1/2
|
||||
## The Event Protocol Contract
|
||||
|
||||
# 5) inspect any time
|
||||
$PY "$SKILL/registry.py" get --job "$JID"
|
||||
$PY "$SKILL/registry.py" logs "$JID" # positional job id
|
||||
$PY "$SKILL/registry.py" logs --list
|
||||
```
|
||||
Every agent participating in the delegation contract must follow the same lifecycle publishing protocol using `publish_event.py`:
|
||||
|
||||
## Job Protocol
|
||||
1. **On Start**: Publish `started` event.
|
||||
`python3 .agents/skills/multi-agent-mux-delegate-job/scripts/publish_event.py --job "$JOB_ID" --event started`
|
||||
2. **On Tool/Permission Prompt**: Publish `permission_required` event.
|
||||
`python3 ... --job "$JOB_ID" --event permission_required --detail "<tool>:<reason>"`
|
||||
3. **On Progress Update (Optional)**: Publish `progress` event.
|
||||
`python3 ... --job "$JOB_ID" --event progress --detail "<status_update>"`
|
||||
4. **On Success**: Publish `completed` event.
|
||||
`python3 ... --job "$JOB_ID" --event completed --detail "<summary>"` (Reviewer should include `"PASS"` in the detail to approve).
|
||||
5. **On Failure/Feedback**: Publish `error` event.
|
||||
`python3 ... --job "$JOB_ID" --event error --detail "<reason_or_feedback>"`
|
||||
|
||||
One topic per job: `python/mqtt/jobs/<job_id>/events`. Payload (JSON, UTF-8,
|
||||
`schema_version=1`):
|
||||
|
||||
```json
|
||||
{ "schema_version": 1, "seq": 7, "job_id": "abc12345",
|
||||
"event": "started|permission_required|progress|completed|error",
|
||||
"timestamp": "2026-06-19T09:32:00Z", "detail": "generalised text",
|
||||
"data": { "optional": "metadata" } }
|
||||
```
|
||||
|
||||
- `seq` is monotonic per job (first = 1); the subscriber uses it to spot
|
||||
reorder/duplication.
|
||||
- `timestamp` is advisory — timeouts are measured from **receive** time.
|
||||
- `detail`/`data` carry **no** secrets or absolute paths.
|
||||
- A `schema_version` or `job_id` mismatch is **dropped** (defensive parsing).
|
||||
|
||||
`started` and `completed`/`error` are the mandatory bookends; `completed`→exit 0,
|
||||
`error`→exit 1. Full catalogue + production `auth_token` handling:
|
||||
[`job-protocol.md`](./job-protocol.md).
|
||||
|
||||
## Registry Format
|
||||
|
||||
```
|
||||
.mam/jobs/<id>.json # metadata record (single source of truth)
|
||||
.mam/jobs/<id>.events.log # append-only JSON-lines log (debug, optional)
|
||||
.mam/jobs/.lock # fcntl advisory lock for the registry
|
||||
```
|
||||
|
||||
The record holds `status`, `prompt`, `agent`, `agent_session`, a `broker` block,
|
||||
`topic_prefix`, `timeout_sec`/`idle_timeout_sec`, `expected_artifacts`,
|
||||
`last_seq`, and (production) `auth_token`. Because the `broker` block lives in
|
||||
the record, `publish_event.py` connects from the registry alone. Concurrency,
|
||||
the atomic rename trick, and multi-session job claiming are in
|
||||
[`registry.md`](./registry.md).
|
||||
---
|
||||
|
||||
## Audit Logs
|
||||
|
||||
Every job's lifecycle is mirrored to a **persistent, append-only audit log**
|
||||
under `.mam/delegate_job_logs/` (override with `DELEGATE_JOB_LOGS_DIR`;
|
||||
default `<cwd>/.mam/delegate_job_logs`). Unlike the registry — live state
|
||||
mutated in place and liable to be cleaned up — the audit log is durable
|
||||
history you can replay after the fact. It is git-ignored.
|
||||
Job lifecycle execution events are persistently mirrored to an append-only log under `.mam/delegate_job_logs/<job_id>/` (containing `meta.json`, `events.ndjson`, and `status.json`). Use `multi-agent-mux-delegate-job logs <job_id>` to view the timeline.
|
||||
|
||||
```
|
||||
.mam/delegate_job_logs/<job_id>/
|
||||
meta.json # registration snapshot: prompt, agent, broker, timeouts, …
|
||||
events.ndjson # append-only, one JSON event per line, in time order
|
||||
status.json # current status only (fast point-query)
|
||||
```
|
||||
---
|
||||
|
||||
**What is logged, automatically:**
|
||||
## Best Practices and Pitfalls
|
||||
|
||||
| When | `events.ndjson` line | Written by |
|
||||
|------|----------------------|------------|
|
||||
| job registered | `registered` (also seeds meta.json + status.json) | `registry.register_job` |
|
||||
| any status change | `status_changed` (`from`/`to`; also rewrites status.json) | `update_job_status`, `pick_pending` |
|
||||
| event published | `published` (carries the exact payload — reproducible) | `publish_event.py` |
|
||||
| event received | `received` (subscriber's external view) | `job_subscriber.py` |
|
||||
|
||||
Both the emitter side (`published`) and the observer side (`received`) are
|
||||
recorded, so a dropped publish or a missed receive is still visible from the
|
||||
other. Every write is **best-effort and isolated** — an fcntl-locked append
|
||||
guarded by `try/except` that only ever emits a `logger.warning`, so a logging
|
||||
failure can never break a publish, a subscribe, or a registry write. stdout is
|
||||
never touched.
|
||||
|
||||
**Reading them:**
|
||||
|
||||
```bash
|
||||
multi-agent-mux-delegate-job logs <job_id> # pretty-print one job's timeline
|
||||
multi-agent-mux-delegate-job logs --list # summarise every logged job (with live status)
|
||||
# or directly via the registry CLI:
|
||||
$PY scripts/registry.py logs <job_id> [--tail N] [--json]
|
||||
$PY scripts/registry.py logs --list [--json]
|
||||
```
|
||||
|
||||
`submit` prints the job's audit-log directory as its last stdout line, so a
|
||||
caller can `tail -n1` to locate it.
|
||||
|
||||
## Broker Setup
|
||||
|
||||
| Stage | Broker | Auth | Transport |
|
||||
|-------|--------|------|-----------|
|
||||
| PoC | `broker.hivemq.com` | none | 1883 plaintext |
|
||||
| Production | self-hosted Mosquitto/EMQX | user/pass + ACL | 8883 TLS |
|
||||
|
||||
All connection settings come from env (`MQTT_BROKER`, `MQTT_PORT`, `MQTT_TLS`,
|
||||
`MQTT_USERNAME`/`MQTT_PASSWORD`, `MQTT_CA_CERTS`, …) resolved by
|
||||
`broker_config_from_env()`, with the registry `broker.*` block overriding per
|
||||
job. Moving to your own broker is **config only**: install Mosquitto, set
|
||||
`persistence true` + `acl_file` + `password_file` + a TLS `listener 8883`, grant
|
||||
the worker `write python/mqtt/jobs/+/events` and Hermes `read`, then flip
|
||||
`MQTT_TLS=1` and fill the registry `broker.*`. Step-by-step (conf, ACL,
|
||||
`mosquitto_passwd`, self-signed/private-CA certs, cut-over verification):
|
||||
[`mqtt-broker-setup.md`](./mqtt-broker-setup.md).
|
||||
|
||||
## Agent Adapters
|
||||
|
||||
Each agent voluntarily follows the contract: receive a `JOB_ID` (or registry
|
||||
path), call `publish_event.py` at lifecycle points, exit 0/1/2. **The contract
|
||||
in one line**: every event call uses `--job "$JOB_ID"` where `$JOB_ID` is the
|
||||
**freshly-issued id from the registry record for *this* delegation** — never a
|
||||
job_id you saw in an earlier session (Pitfall §"Wrong job_id propagated to the
|
||||
agent").
|
||||
|
||||
- **claude-code** — Claude Code calls `publish_event.py` via its Bash tool at
|
||||
lifecycle points. `submit --mode tmux` injects a prompt that already names
|
||||
`$JOB_ID`; if you drive claude manually, hand it the id explicitly. Reference
|
||||
instruction block (the wrapper injects something equivalent):
|
||||
|
||||
```text
|
||||
Your job_id is "$JOB_ID" (read it from the registry record for this delegation —
|
||||
do not reuse any job_id you saw before).
|
||||
|
||||
On start: $PY multi-agent-mux-delegate-job/scripts/publish_event.py --job "$JOB_ID" --event started
|
||||
On permission: $PY … --job "$JOB_ID" --event permission_required --detail "<tool>:<what>"
|
||||
On progress: $PY … --job "$JOB_ID" --event progress --detail "<short status>"
|
||||
On success: $PY … --job "$JOB_ID" --event completed --detail "<one-line summary>"
|
||||
On failure: $PY … --job "$JOB_ID" --event error --detail "<one-line reason>"
|
||||
|
||||
Task: <the user's prompt>
|
||||
|
||||
The subscriber for "$JOB_ID" is already running; your completed/error event
|
||||
ends the job. Exit codes: 0 completed, 1 error, 2 publish failure.
|
||||
```
|
||||
|
||||
See [claude-code](../claude-code/SKILL.md) for tmux orchestration patterns.
|
||||
- **codex** — same contract. Invoke `codex exec "<instruction-block-above>"` or
|
||||
wire `publish_event.py` as an MCP tool so the agent can call it directly.
|
||||
- **opencode** — wire `publish_event.py` as a tool/command the agent can call;
|
||||
identical event points.
|
||||
- **human** — a person does the work, reads the registry record, then runs
|
||||
`publish_event.py --job <id> --event completed` (or `error`) by hand.
|
||||
|
||||
## User Interface
|
||||
|
||||
The [`multi-agent-mux-delegate-job`](./multi-agent-mux-delegate-job) bash wrapper bundles register +
|
||||
subscribe-first + run-agent + validate:
|
||||
|
||||
```bash
|
||||
multi-agent-mux-delegate-job submit --agent claude-code \
|
||||
--prompt "정렬 문제 10개를 만들어 sort_problems.md로 저장" \
|
||||
--workdir /path/to/project --timeout 3600 [--validate ./validate.sh]
|
||||
multi-agent-mux-delegate-job status --job <id> # one record, pretty-printed
|
||||
multi-agent-mux-delegate-job list # all jobs, one line each
|
||||
multi-agent-mux-delegate-job verify --job <id> --validate ./validate.sh # runs it, reports exit code
|
||||
multi-agent-mux-delegate-job wait [--job <id>] # block until terminal (else --wait-any)
|
||||
```
|
||||
|
||||
`submit` **always starts the subscriber before the agent** (the ordering
|
||||
dependency), runs the agent in `--mode print` (one-shot) or `--mode tmux`, and
|
||||
calls `--validate` afterward if given. The skill automates job-id generation,
|
||||
registry creation, broker resolution, subscriber-first ordering, agent launch,
|
||||
and completion detection; it does **not** automate the agent's internals or your
|
||||
business-logic validation — those are hooks you fill (`validate.sh` reads
|
||||
`$JOB_ID`/`$REGISTRY_DIR`).
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- **Publishing before subscribing** — MQTT does not queue non-retained messages
|
||||
for absent subscribers. Start `job_subscriber.py` *before* the agent, or rely
|
||||
on retained terminal events (production). `submit` enforces this.
|
||||
- **Wrong job_id propagated to the agent** — the wrapper prints a fresh `JOB_ID`
|
||||
on every `submit`. If your agent instruction (or the wrapper's prompt template)
|
||||
hard-codes an old job_id, the agent calls `publish_event.py --job <wrong>`,
|
||||
the subscriber's defensive parser drops it as a `job_id` mismatch, and the
|
||||
delegator waits until idle timeout (exit 2). Fix: instruct the agent to
|
||||
**read the job_id from the registry record for *this* delegation** (or pass it
|
||||
in via env / `--prompt` interpolation), never from prior runs. `submit`'s
|
||||
default prompt template interpolates `$JOB_ID` for you — if you build a custom
|
||||
prompt, do the same.
|
||||
- **tmux session name collision** — `submit --mode tmux` derives the session
|
||||
name from `--agent-session tmux:<name>` (default `tmux:claude`). If a session
|
||||
with that name is already attached (e.g. you ran the demo and the previous
|
||||
session is still open), `tmux new-session -d -s <name>` fails and the agent
|
||||
never launches. Pick a unique `--agent-session` per concurrent delegation
|
||||
(e.g. `tmux:demo`, `tmux:claude-a`, `tmux:claude-b`) or kill the stale one
|
||||
(`tmux kill-session -t claude`) before re-running.
|
||||
- **Timeout before `started`** — a cold-starting agent may not emit `started`
|
||||
for a while; the wall-clock timeout starts at subscribe time so a stuck agent
|
||||
still terminates. Don't set `--timeout` so low you false-positive a slow start.
|
||||
- **No retry on publish** — a dropped `completed` would hang the delegator
|
||||
forever; `publish_event.py` retries with exponential backoff and exits 2 if it
|
||||
still fails, so the delegator is never left waiting silently.
|
||||
- **QoS-1 duplicates / reorders** — a terminal event can arrive twice, or
|
||||
`error` can trail `completed`; the subscriber's terminal state machine
|
||||
finalises each job once and ignores the rest.
|
||||
- **Trusting the public broker** — anyone can publish there; never make a real
|
||||
decision on a PoC signal. Add `auth_token` + an authenticated broker first.
|
||||
- **Secrets in `detail`/`data`** — keep payloads generalised; no paths, keys, or
|
||||
tokens (except the production `auth_token` in `data`).
|
||||
|
||||
## Subagent Orchestration Pattern
|
||||
|
||||
When using this skill from a Hermes `delegate_task` subagent to dispatch work to
|
||||
a coding-agent CLI (agy/claude) running in a tmux session, the following pattern
|
||||
has been verified (2026-06-21, 6-batch refactoring sprint):
|
||||
|
||||
### Roles
|
||||
- **Main worker** (implementation): one agent session (e.g. `agy-new`) receives
|
||||
brief files and executes code changes.
|
||||
- **Reviewers** (spec compliance + code quality): two other agent sessions
|
||||
(e.g. `agy-existing`, `claude-existing`) review the diff in parallel.
|
||||
- **Hermes** (orchestrator): dispatches subagents, verifies diffs, commits,
|
||||
and falls back to direct fixes when reviewers find issues.
|
||||
|
||||
### Key lessons learned
|
||||
1. **Brief delivery via file path** — don't paste long briefs inline via
|
||||
`tmux send-keys`; the TUI may swallow them. Instead, send a short instruction
|
||||
like "follow /tmp/batch1-brief.md" and let the agent read the file.
|
||||
2. **Polling vs MQTT subscriber** — for short tasks (<5min), pane polling
|
||||
(`capture-pane` + grep for completion markers) is simpler and more reliable
|
||||
than registering a job via `registry.py` + `job_subscriber.py`. Use MQTT
|
||||
subscriber only for long-running jobs (>5min) where push notification matters.
|
||||
3. **Reviewers catch different bugs** — in practice, agy (Flash) caught
|
||||
semantic issues (slash matching, export scope), while claude (Opus) caught
|
||||
API signature mismatches (paho v2 5-arg vs 4-arg `on_disconnect`). Two
|
||||
reviewers with different models provide complementary coverage.
|
||||
4. **Hermes fallback fix** — when reviewers find a small, well-defined issue
|
||||
(wrong argument count, missing slash), Hermes should fix it directly rather
|
||||
than re-dispatching the implementer. This saves a full round-trip.
|
||||
5. **Batch grouping** — group 2-3 FW items per batch when they touch different
|
||||
files (no file overlap). This amortises the dispatch overhead. Items touching
|
||||
the same file must be in separate batches to avoid conflicts.
|
||||
6. **Pane Snapshots & Truncation Prevention** — to prevent long agent responses from being scrolled out and truncated due to TUI viewport limitations, enforce the following snapshotting pattern:
|
||||
- Immediately after dispatching a brief, capture the pre-brief pane buffer via `capture-pane -S -200`.
|
||||
- During long execution, run a background loop taking incremental snapshots (e.g. every 30 seconds `>> /tmp/pane-snap.txt`).
|
||||
- Immediately after job termination, capture the entire final pane state to ensure no terminal logs are lost.
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
- [ ] `started` → `completed` over the public broker: subscriber prints the
|
||||
lines and exits **0**.
|
||||
- [ ] `error` path: subscriber exits **1**.
|
||||
- [ ] timeout path: no terminal event within `--timeout`/`--idle-timeout` →
|
||||
exit **2**.
|
||||
- [ ] polluted payload (bad JSON, wrong `schema_version`, wrong `job_id`) is
|
||||
dropped with a warning, not crashed on.
|
||||
- [ ] one tmux session processes two registry jobs in sequence; a second
|
||||
session with a different `agent_session` claims only its own.
|
||||
- [ ] broker cut-over: same scripts reach an authenticated TLS broker with env
|
||||
changes only; a credential without write ACL is rejected; a late
|
||||
subscriber still receives the retained terminal event.
|
||||
- [ ] `publisher.py`/`subscriber.py`/`README.md` demo on `python/mqtt/sample`
|
||||
still works unchanged (regression).
|
||||
- [ ] **audit log integrity** — for a completed job,
|
||||
`.mam/delegate_job_logs/<JID>/events.ndjson` contains `registered` →
|
||||
`received started` → `published completed` (in that order), and
|
||||
`status.json.status == "completed"` matches the registry record. A
|
||||
logging failure (e.g. read-only log dir) does not break the publish or
|
||||
subscribe path — only a `logger.warning` is emitted.
|
||||
- [ ] **end-to-end demo smoke** — run
|
||||
`multi-agent-mux-delegate-job submit --agent claude-code --agent-session tmux:demo-smoke
|
||||
--prompt "echo hello and call publish_event.py --job <JID>
|
||||
--event completed" --timeout 120` and confirm
|
||||
(a) registered job id echoed, (b) subscriber pid echoed, (c) tmux session
|
||||
name printed, (d) `events.ndjson` grows as the agent runs, (e) final
|
||||
stdout line is the audit-log dir.
|
||||
- **Subscribe-Before-Publish**: The subscriber must be running before the agent starts publishing. The `submit` command handles this automatically by launching the subscriber in the background first.
|
||||
- **Fresh job_id Propagation**: Make sure the worker agent receives the correct `JOB_ID` generated for the current run, rather than reusing stale IDs from previous sessions.
|
||||
- **Brief delivery via file path**: For long or complex prompts, write the instructions to a file (e.g. `/tmp/task-brief.md`) and pass a short prompt pointing to the file path to prevent terminal buffer overflows.
|
||||
- **Batch Grouping**: Group non-overlapping tasks into batches to parallelize execution across multiple agent sessions, reducing overhead.
|
||||
|
||||
@@ -16,6 +16,13 @@ set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# Load local .env if it exists in current dir or workspace root
|
||||
if [[ -f .env ]]; then
|
||||
set -a; source .env; set +a
|
||||
elif [[ -f "$SCRIPT_DIR/../../.env" ]]; then
|
||||
set -a; source "$SCRIPT_DIR/../../.env"; set +a
|
||||
fi
|
||||
|
||||
# Pick an interpreter: prefer a project .venv, else python3.
|
||||
pick_python() {
|
||||
local py_bin
|
||||
@@ -46,6 +53,8 @@ multi-agent-mux-delegate-job <command> [options]
|
||||
submit --agent <name> --prompt <text> [--workdir <dir>] [--agent-session <label>]
|
||||
[--timeout <sec>] [--idle-timeout <sec>] [--validate <script>]
|
||||
[--registry-dir <dir>] [--dry-run]
|
||||
[--type <direct|loop|discuss>] [--reviewer <reviewer_agent>]
|
||||
[--reviewer-session <reviewer_session>] [--max-iterations <count>]
|
||||
# The skill is tmux-interactive only; --mode print was removed.
|
||||
status --job <id> [--registry-dir <dir>]
|
||||
list [--registry-dir <dir>]
|
||||
@@ -59,6 +68,7 @@ EOF
|
||||
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"
|
||||
TYPE="direct"; REVIEWER="hermes"; REVIEWER_SESSION="tmux:hermes"; MAX_ITERATIONS=5
|
||||
|
||||
parse_opts() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
@@ -73,6 +83,10 @@ parse_opts() {
|
||||
--job) JOB_ID="$2"; shift 2;;
|
||||
--registry-dir) REGISTRY_DIR="$2"; shift 2;;
|
||||
--dry-run) DRY_RUN=1; shift;;
|
||||
--type) TYPE="$2"; shift 2;;
|
||||
--reviewer) REVIEWER="$2"; shift 2;;
|
||||
--reviewer-session) REVIEWER_SESSION="$2"; shift 2;;
|
||||
--max-iterations) MAX_ITERATIONS="$2"; shift 2;;
|
||||
*) echo "unknown option: $1" >&2; usage; exit 1;;
|
||||
esac
|
||||
done
|
||||
@@ -88,9 +102,12 @@ cmd_submit() {
|
||||
# 1) register job (prints the new job id)
|
||||
JOB_ID="$("$PY" "$SCRIPT_DIR/scripts/registry.py" --registry-dir "$REGISTRY_DIR" register \
|
||||
--prompt "$PROMPT" --agent "$AGENT" --agent-session "$AGENT_SESSION" \
|
||||
--timeout "$TIMEOUT" --idle-timeout "$IDLE_TIMEOUT")"
|
||||
--timeout "$TIMEOUT" --idle-timeout "$IDLE_TIMEOUT" \
|
||||
--job-type "$TYPE" --reviewer "$REVIEWER" --reviewer-session "$REVIEWER_SESSION" \
|
||||
--max-iterations "$MAX_ITERATIONS")"
|
||||
echo "registered job: $JOB_ID"
|
||||
|
||||
if [[ "$TYPE" == "direct" ]]; then
|
||||
# 2) START THE SUBSCRIBER FIRST (ordering dependency — MQTT does not queue
|
||||
# non-retained messages for absent subscribers).
|
||||
local logf="$REGISTRY_DIR/$JOB_ID.subscriber.out"
|
||||
@@ -149,10 +166,155 @@ Task: $PROMPT"
|
||||
# "Audit Logs"). Callers can scrape `tail -n1` to find it.
|
||||
local logs_root="${DELEGATE_JOB_LOGS_DIR:-$WORKDIR/delegate_job_logs}"
|
||||
echo "$logs_root/$JOB_ID"
|
||||
else
|
||||
# Implement loop/discuss orchestrator
|
||||
local iteration=1
|
||||
local current_prompt="$PROMPT"
|
||||
local current_session="$AGENT_SESSION"
|
||||
local current_role="worker"
|
||||
|
||||
if [[ "$DRY_RUN" == "1" ]]; then
|
||||
echo "[dry-run] orchestrator loop would start for job: $JOB_ID type: $TYPE"
|
||||
echo "worker session: $AGENT_SESSION, reviewer session: $REVIEWER_SESSION"
|
||||
local logs_root_dry="${DELEGATE_JOB_LOGS_DIR:-$WORKDIR/delegate_job_logs}"
|
||||
echo "$logs_root_dry/$JOB_ID"
|
||||
return 0
|
||||
fi
|
||||
|
||||
while true; do
|
||||
echo "=================================================="
|
||||
echo "Iteration $iteration - Role: $current_role"
|
||||
echo "Session: $current_session"
|
||||
echo "=================================================="
|
||||
|
||||
# Update job details in registry
|
||||
"$PY" "$SCRIPT_DIR/scripts/registry.py" --registry-dir "$REGISTRY_DIR" update \
|
||||
--job "$JOB_ID" \
|
||||
--agent-session "$current_session" \
|
||||
--prompt "$current_prompt" \
|
||||
--iteration "$iteration" \
|
||||
--status "pending"
|
||||
|
||||
# Start subscriber
|
||||
local logf="$REGISTRY_DIR/${JOB_ID}.iter_${iteration}_${current_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 &
|
||||
local sub_pid=$!
|
||||
echo "subscriber pid: $sub_pid (log: $logf)"
|
||||
sleep 1
|
||||
|
||||
# Format instruction block
|
||||
local pub="$PY $SCRIPT_DIR/scripts/publish_event.py --registry-dir $REGISTRY_DIR --job $JOB_ID"
|
||||
local instructions="Your job_id is \"$JOB_ID\" (the one just registered for THIS delegation — read it from the registry record, do NOT reuse any job_id you saw in earlier runs).
|
||||
|
||||
On start run: $pub --event started.
|
||||
On permission/tool prompt run: $pub --event permission_required --detail '<tool>:<what>'.
|
||||
On progress (optional): $pub --event progress --detail '<short status>'.
|
||||
On success run: $pub --event completed --detail '<one-line summary>'.
|
||||
On failure run: $pub --event error --detail '<one-line reason>'.
|
||||
|
||||
The subscriber for this job_id is already running; your completed/error event ends the job. Exit codes: 0 completed, 1 error, 2 publish failure.
|
||||
|
||||
Task: $current_prompt"
|
||||
|
||||
# Trigger agent
|
||||
run_agent "$JOB_ID" "$instructions" "$current_session"
|
||||
|
||||
# Wait for subscriber
|
||||
local sub_rc=0
|
||||
wait "$sub_pid" || sub_rc=$?
|
||||
echo "subscriber output:"; cat "$logf" || true
|
||||
|
||||
# Check job status based on subscriber exit code
|
||||
local job_status="running"
|
||||
if [[ $sub_rc -eq 0 ]]; then
|
||||
job_status="completed"
|
||||
elif [[ $sub_rc -eq 1 ]]; then
|
||||
job_status="error"
|
||||
else
|
||||
job_status="timeout"
|
||||
fi
|
||||
|
||||
echo "Job role $current_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 [[ "$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"
|
||||
current_session="$REVIEWER_SESSION"
|
||||
# Build reviewer prompt based on type
|
||||
if [[ "$TYPE" == "loop" ]]; then
|
||||
current_prompt="Review the changes/artifacts generated for job $JOB_ID. Check if they meet the requirements. If correct, publish completed event with 'PASS'. If there are issues, publish error event with detailed feedback/nits. CRITICAL: When raising issues or giving a review, you MUST include the exact reason for the issue and a clear direction for improvement (문제 제시에 대한 이유와 확실한 개선 방향을 반드시 포함해야 합니다)."
|
||||
elif [[ "$TYPE" == "discuss" ]]; then
|
||||
current_prompt="Read draft/documents generated for job $JOB_ID. Review the feasibility and content. Write your feedback/objections. If you agree with the plan, reply with 'AGREE'."
|
||||
fi
|
||||
else
|
||||
if [[ "$job_status" != "completed" ]]; then
|
||||
echo "Reviewer did not complete successfully (status: $job_status). Terminating workflow."
|
||||
break
|
||||
fi
|
||||
|
||||
# Reviewer finished. Check if pass/agree
|
||||
local success=0
|
||||
if [[ "$TYPE" == "loop" ]]; then
|
||||
if [[ "${feedback,,}" == *"pass"* ]]; then
|
||||
success=1
|
||||
fi
|
||||
elif [[ "$TYPE" == "discuss" ]]; then
|
||||
if [[ "${feedback,,}" == *"agree"* ]]; then
|
||||
success=1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$success" == "1" ]]; then
|
||||
echo "Reviewer approved the work. Finalizing job as completed."
|
||||
"$PY" "$SCRIPT_DIR/scripts/registry.py" --registry-dir "$REGISTRY_DIR" status --job "$JOB_ID" --set "completed"
|
||||
break
|
||||
else
|
||||
# Reviewer rejected/provided feedback. Increment & check max iterations
|
||||
if [[ $iteration -ge $MAX_ITERATIONS ]]; then
|
||||
echo "Max iterations ($MAX_ITERATIONS) reached without approval. Terminating workflow."
|
||||
"$PY" "$SCRIPT_DIR/scripts/registry.py" --registry-dir "$REGISTRY_DIR" status --job "$JOB_ID" --set "error"
|
||||
break
|
||||
fi
|
||||
|
||||
iteration=$((iteration + 1))
|
||||
current_role="worker"
|
||||
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
|
||||
fi
|
||||
done
|
||||
|
||||
# 4) optional validation hook
|
||||
if [[ -n "$VALIDATE" ]]; then
|
||||
echo "running validation: $VALIDATE"
|
||||
if JOB_ID="$JOB_ID" REGISTRY_DIR="$REGISTRY_DIR" bash "$VALIDATE"; then
|
||||
echo "validation: PASS"
|
||||
else
|
||||
local rc=$?
|
||||
echo "validation: FAIL (exit $rc)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Last stdout line: the persistent audit-log dir
|
||||
local logs_root="${DELEGATE_JOB_LOGS_DIR:-$WORKDIR/delegate_job_logs}"
|
||||
echo "$logs_root/$JOB_ID"
|
||||
fi
|
||||
}
|
||||
|
||||
run_agent() {
|
||||
local job_id="$1"; local instructions="$2"
|
||||
local job_id="$1"; local instructions="$2"; local target_session="${3:-$AGENT_SESSION}"
|
||||
# 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
|
||||
@@ -168,7 +330,7 @@ run_agent() {
|
||||
echo "[human agent] complete the task, then run publish_event.py --event completed"
|
||||
return
|
||||
fi
|
||||
local sess="${AGENT_SESSION#tmux:}"
|
||||
local sess="${target_session#tmux:}"
|
||||
|
||||
if [[ "$DRY_RUN" == "1" ]]; then
|
||||
echo "[dry-run] would delegate task to running agent '$AGENT' in tmux session '$sess' with instructions:"
|
||||
@@ -202,6 +364,7 @@ run_agent() {
|
||||
echo "살아있는 에이전트 세션 '$sess'에 작업을 위임합니다..."
|
||||
$_tmux set-buffer -b "job_buf_$job_id" "$instructions"
|
||||
$_tmux paste-buffer -b "job_buf_$job_id" -t "$sess"
|
||||
sleep 0.5
|
||||
$_tmux send-keys -t "$sess" C-m
|
||||
$_tmux delete-buffer -b "job_buf_$job_id"
|
||||
|
||||
|
||||
@@ -59,11 +59,11 @@ def _format_line(topic: str, payload: Dict[str, Any]) -> str:
|
||||
class _Watcher:
|
||||
"""Holds the shared queue + the set of job_ids we accept events for."""
|
||||
|
||||
def __init__(self, expected_job_ids: Set[str], expected_tokens: Dict[str, Optional[str]]):
|
||||
def __init__(self, expected_job_ids: Set[str], expected_tokens: Dict[str, Optional[str]], expected_seqs: Dict[str, int]):
|
||||
self.events: "queue.Queue[Tuple[str, Dict[str, Any]]]" = queue.Queue()
|
||||
self.expected = set(expected_job_ids)
|
||||
self.tokens = expected_tokens # job_id -> expected auth_token (or None)
|
||||
self.last_seq: Dict[str, int] = {jid: 0 for jid in expected_job_ids}
|
||||
self.last_seq = dict(expected_seqs)
|
||||
|
||||
def on_message(self, _client, _userdata, msg) -> None:
|
||||
# --- defensive parsing -------------------------------------------
|
||||
@@ -153,7 +153,8 @@ def main(argv=None) -> int:
|
||||
|
||||
expected_ids: Set[str] = {j["job_id"] for j in jobs}
|
||||
tokens = {j["job_id"]: j.get("auth_token") for j in jobs}
|
||||
watcher = _Watcher(expected_ids, tokens)
|
||||
seqs = {j["job_id"]: int(j.get("last_seq", 0)) for j in jobs}
|
||||
watcher = _Watcher(expected_ids, tokens, seqs)
|
||||
|
||||
# Resolve timeouts from CLI, falling back to the (first) job's settings.
|
||||
base_job = jobs[0]
|
||||
|
||||
@@ -59,6 +59,10 @@ def register_job(
|
||||
expected_artifacts: Optional[List[str]] = None,
|
||||
bits: int = 32,
|
||||
auth_token: Optional[str] = None,
|
||||
job_type: str = "direct",
|
||||
reviewer: Optional[str] = None,
|
||||
reviewer_session: Optional[str] = None,
|
||||
max_iterations: int = 5,
|
||||
) -> str:
|
||||
"""Create a new ``pending`` job record and return its id.
|
||||
|
||||
@@ -90,6 +94,11 @@ def register_job(
|
||||
"expected_artifacts": expected_artifacts or [],
|
||||
"last_seq": 0,
|
||||
"auth_token": auth_token,
|
||||
"job_type": job_type,
|
||||
"reviewer": reviewer,
|
||||
"reviewer_session": reviewer_session,
|
||||
"max_iterations": int(max_iterations),
|
||||
"iteration": 1,
|
||||
}
|
||||
with registry_lock(registry_dir):
|
||||
if mqtt_common._job_path(job_id, registry_dir).exists():
|
||||
@@ -164,7 +173,7 @@ def append_event(job_id: str, registry_dir: str, payload: Dict[str, Any]) -> Non
|
||||
# convenience re-export so callers can `from registry import load_job`
|
||||
__all__ = [
|
||||
"register_job", "pick_pending", "update_status", "load_job",
|
||||
"list_jobs", "append_event", "generate_job_id",
|
||||
"list_jobs", "append_event", "generate_job_id", "get_feedback",
|
||||
]
|
||||
|
||||
|
||||
@@ -180,6 +189,44 @@ def _iter_records(registry_dir: str):
|
||||
logger.warning("skipping unreadable record %s: %s", path, exc)
|
||||
|
||||
|
||||
def get_feedback(job_id: str, registry_dir: str = DEFAULT_REGISTRY_DIR) -> str:
|
||||
"""Read the job's audit log or events log and return the detail of the last completed/error event."""
|
||||
# 1) Try the unified audit log first (ndjson) since it's written synchronously by the subscriber
|
||||
try:
|
||||
import mqtt_common
|
||||
logs_dir = mqtt_common.LOGS_DIR
|
||||
events = list(mqtt_common.iter_logged_events(job_id, logs_dir))
|
||||
for e in reversed(events):
|
||||
if e.get("source_event") in ("completed", "error"):
|
||||
return e.get("detail", "")
|
||||
if e.get("event") in ("completed", "error"):
|
||||
return e.get("detail", "")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 2) Fallback to local .events.log
|
||||
log_path = Path(registry_dir) / f"{job_id}.events.log"
|
||||
if log_path.exists():
|
||||
feedback = ""
|
||||
try:
|
||||
with open(log_path, "r", encoding="utf-8") as fh:
|
||||
for line in fh:
|
||||
if not line.strip():
|
||||
continue
|
||||
try:
|
||||
payload = json.loads(line)
|
||||
if payload.get("event") in ("completed", "error"):
|
||||
feedback = payload.get("detail", "")
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
except OSError:
|
||||
pass
|
||||
if feedback:
|
||||
return feedback
|
||||
|
||||
return ""
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# CLI (so the bash wrapper can shell out without inline python)
|
||||
# --------------------------------------------------------------------------
|
||||
@@ -197,6 +244,10 @@ def _build_parser() -> argparse.ArgumentParser:
|
||||
p_reg.add_argument("--bits", type=int, default=32, help="32 (PoC) or 128 (prod)")
|
||||
p_reg.add_argument("--artifact", action="append", default=[], dest="artifacts")
|
||||
p_reg.add_argument("--auth-token", default=None, help="HMAC auth token for the job (auto-generated if secure broker is detected)")
|
||||
p_reg.add_argument("--job-type", default="direct", choices=["direct", "loop", "discuss"])
|
||||
p_reg.add_argument("--reviewer", default=None)
|
||||
p_reg.add_argument("--reviewer-session", default=None)
|
||||
p_reg.add_argument("--max-iterations", type=int, default=5)
|
||||
|
||||
p_list = sub.add_parser("list", help="list jobs (optionally by status)")
|
||||
p_list.add_argument("--status", default=None)
|
||||
@@ -209,6 +260,16 @@ def _build_parser() -> argparse.ArgumentParser:
|
||||
p_status.add_argument("--job", required=True)
|
||||
p_status.add_argument("--set", required=True, dest="status")
|
||||
|
||||
p_update = sub.add_parser("update", help="update a job record")
|
||||
p_update.add_argument("--job", required=True)
|
||||
p_update.add_argument("--status", default=None)
|
||||
p_update.add_argument("--agent-session", default=None)
|
||||
p_update.add_argument("--prompt", default=None)
|
||||
p_update.add_argument("--iteration", type=int, default=None)
|
||||
|
||||
p_feedback = sub.add_parser("get-feedback", help="get the last feedback detail (completed/error) for a job")
|
||||
p_feedback.add_argument("--job", required=True)
|
||||
|
||||
p_pick = sub.add_parser("pick", help="claim a pending job for a session; prints id")
|
||||
p_pick.add_argument("--agent-session", default="tmux:claude")
|
||||
|
||||
@@ -247,6 +308,10 @@ def main(argv: Optional[List[str]] = None) -> int:
|
||||
expected_artifacts=args.artifacts,
|
||||
bits=args.bits,
|
||||
auth_token=args.auth_token,
|
||||
job_type=args.job_type,
|
||||
reviewer=args.reviewer,
|
||||
reviewer_session=args.reviewer_session,
|
||||
max_iterations=args.max_iterations,
|
||||
)
|
||||
print(job_id)
|
||||
return 0
|
||||
@@ -279,6 +344,27 @@ def main(argv: Optional[List[str]] = None) -> int:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
if args.command == "update":
|
||||
fields = {}
|
||||
if args.status is not None:
|
||||
fields["status"] = args.status
|
||||
if args.agent_session is not None:
|
||||
fields["agent_session"] = args.agent_session
|
||||
if args.prompt is not None:
|
||||
fields["prompt"] = args.prompt
|
||||
if args.iteration is not None:
|
||||
fields["iteration"] = args.iteration
|
||||
try:
|
||||
mqtt_common.update_job_status(args.job, rd, **fields)
|
||||
except FileNotFoundError as exc:
|
||||
print(str(exc), file=sys.stderr)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
if args.command == "get-feedback":
|
||||
print(get_feedback(args.job, rd))
|
||||
return 0
|
||||
|
||||
if args.command == "pick":
|
||||
job_id = pick_pending(args.agent_session, rd)
|
||||
if job_id is None:
|
||||
|
||||
@@ -282,7 +282,7 @@ mkdir -p "$STATE_DIR"
|
||||
# atomic_dump_yaml(flock + temp+rename) 로 같은 소스를 돌린다. atomic 래퍼에서는
|
||||
# 'actions' 가 없으면 SystemExit(0) 으로 쓰기를 건너뛴다 (불필요한 재포맷 방지).
|
||||
read -r -d '' RECON_SRC <<'PYEOF' || true
|
||||
import os, json, glob, subprocess, time
|
||||
import os, json, glob, subprocess, time, sqlite3
|
||||
from datetime import datetime, timezone
|
||||
import yaml
|
||||
|
||||
@@ -403,14 +403,28 @@ if tmux_confirmed:
|
||||
name = t['name']
|
||||
if name in yaml_session_names:
|
||||
continue
|
||||
if not (name.endswith('-creator-claude') or name.endswith('-creator-agy')):
|
||||
if name.endswith('-creator-claude'):
|
||||
agent = 'claude'
|
||||
elif name.endswith('-creator-agy'):
|
||||
agent = 'agy'
|
||||
elif name.endswith('-creator-hermes'):
|
||||
agent = 'hermes'
|
||||
elif name.endswith('-creator-cline'):
|
||||
agent = 'cline'
|
||||
else:
|
||||
continue
|
||||
srv = t.get('server', 'default')
|
||||
pm = pane_meta(name, srv)
|
||||
if not pm:
|
||||
continue
|
||||
agent = 'claude' if name.endswith('-creator-claude') else 'agy'
|
||||
cmd_full = 'claude --dangerously-skip-permissions' if agent == 'claude' else 'agy --dangerously-skip-permissions'
|
||||
if agent == 'claude':
|
||||
cmd_full = 'claude --dangerously-skip-permissions'
|
||||
elif agent == 'agy':
|
||||
cmd_full = 'agy --dangerously-skip-permissions'
|
||||
elif agent == 'hermes':
|
||||
cmd_full = 'hermes'
|
||||
elif agent == 'cline':
|
||||
cmd_full = 'cline -i'
|
||||
server_opt = f"-L {srv} " if srv != 'default' else ""
|
||||
entry = {
|
||||
'name': name,
|
||||
@@ -430,7 +444,7 @@ if tmux_confirmed:
|
||||
entry['tui'] = {'model': '(unknown — capture after first message)', 'provider': 'anthropic',
|
||||
'plan': '(unknown)', 'account': '(unknown)', 'version': '(unknown)'}
|
||||
entry['claude_session_id_own'] = None
|
||||
else:
|
||||
elif agent == 'agy':
|
||||
entry['child_pid'] = 0
|
||||
entry['agy_conversation_id_own'] = None
|
||||
entry['mcp_attachments'] = [
|
||||
@@ -440,6 +454,12 @@ if tmux_confirmed:
|
||||
'endpoint': 'https://stitch.googleapis.com/mcp'
|
||||
}
|
||||
]
|
||||
elif agent == 'hermes':
|
||||
entry['child_pid'] = 0
|
||||
entry['hermes_conversation_id_own'] = None
|
||||
elif agent == 'cline':
|
||||
entry['child_pid'] = 0
|
||||
entry['cline_conversation_id_own'] = None
|
||||
d.setdefault('tmux_sessions', []).append(entry)
|
||||
yaml_session_names.add(name)
|
||||
drifts.append({'class': 'B', 'name': name,
|
||||
@@ -505,6 +525,66 @@ for s in d.get('tmux_sessions', []):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# === drift C (hermes): hermes 새 session id materialize (per-row own id) ===
|
||||
for s in d.get('tmux_sessions', []):
|
||||
if not s.get('name', '').endswith('-creator-hermes'):
|
||||
continue
|
||||
if s.get('status') != 'running':
|
||||
continue
|
||||
if s.get('hermes_conversation_id_own'):
|
||||
continue
|
||||
cwd = (s.get('pane') or {}).get('cwd', '')
|
||||
if not cwd:
|
||||
continue
|
||||
hdb = f"{home}/.hermes/state.db"
|
||||
if os.path.exists(hdb):
|
||||
try:
|
||||
conn = sqlite3.connect(hdb)
|
||||
r = conn.execute("SELECT id FROM sessions WHERE cwd=? ORDER BY started_at DESC LIMIT 1", (cwd,)).fetchone()
|
||||
conn.close()
|
||||
if r:
|
||||
cid = r[0]
|
||||
s['hermes_conversation_id_own'] = cid
|
||||
drifts.append({'class': 'C', 'name': s['name'], 'msg': f"{s['name']}: conversation id materialized: {cid}"})
|
||||
actions.append(f"updated conversation id: {cid}")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# === drift C (cline): cline 새 session id materialize (per-row own id) ===
|
||||
for s in d.get('tmux_sessions', []):
|
||||
if not s.get('name', '').endswith('-creator-cline'):
|
||||
continue
|
||||
if s.get('status') != 'running':
|
||||
continue
|
||||
if s.get('cline_conversation_id_own'):
|
||||
continue
|
||||
cwd = (s.get('pane') or {}).get('cwd', '')
|
||||
if not cwd:
|
||||
continue
|
||||
sessions_dir = f"{home}/.cline/data/sessions"
|
||||
if os.path.isdir(sessions_dir):
|
||||
candidates = []
|
||||
for session_folder in glob.glob(f"{sessions_dir}/*"):
|
||||
if os.path.isdir(session_folder):
|
||||
folder_name = os.path.basename(session_folder)
|
||||
json_file = f"{session_folder}/{folder_name}.json"
|
||||
if os.path.exists(json_file):
|
||||
candidates.append(json_file)
|
||||
candidates.sort(key=os.path.getmtime, reverse=True)
|
||||
for j in candidates:
|
||||
try:
|
||||
with open(j) as f:
|
||||
sdata = json.load(f)
|
||||
if sdata.get('cwd') == cwd or sdata.get('workspace_root') == cwd:
|
||||
cid = sdata.get('session_id')
|
||||
if cid:
|
||||
s['cline_conversation_id_own'] = cid
|
||||
drifts.append({'class': 'C', 'name': s['name'], 'msg': f"{s['name']}: session id materialized: {cid}"})
|
||||
actions.append(f"updated session id: {cid}")
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# === drift D: stale UUID (cache 의 artifact 가 사라짐) — 보고만, 변경 없음 ===
|
||||
ai = d.get('agent_identities', {}) or {}
|
||||
cl = (ai.get('claude') or {})
|
||||
@@ -519,6 +599,28 @@ if ag.get('conversation_id'):
|
||||
if not os.path.exists(f"{home}/.gemini/antigravity-cli/conversations/{cid}.db"):
|
||||
drifts.append({'class': 'D', 'name': '(agy identity cache)',
|
||||
'msg': f"stale UUID in agent_identities.agy.conversation_id: {cid} (.db missing)"})
|
||||
hr = (ai.get('hermes') or {})
|
||||
if hr.get('session_id'):
|
||||
sid = hr['session_id']
|
||||
hdb = f"{home}/.hermes/state.db"
|
||||
has_session = False
|
||||
if os.path.exists(hdb):
|
||||
try:
|
||||
conn = sqlite3.connect(hdb)
|
||||
r = conn.execute("SELECT 1 FROM sessions WHERE id=?", (sid,)).fetchone()
|
||||
conn.close()
|
||||
has_session = r is not None
|
||||
except Exception:
|
||||
pass
|
||||
if not has_session:
|
||||
drifts.append({'class': 'D', 'name': '(hermes identity cache)',
|
||||
'msg': f"stale UUID in agent_identities.hermes.session_id: {sid} (session missing from db)"})
|
||||
cn = (ai.get('cline') or {})
|
||||
if cn.get('session_id'):
|
||||
sid = cn['session_id']
|
||||
if not os.path.exists(f"{home}/.cline/data/sessions/{sid}/{sid}.json"):
|
||||
drifts.append({'class': 'D', 'name': '(cline identity cache)',
|
||||
'msg': f"stale UUID in agent_identities.cline.session_id: {sid} (session file missing)"})
|
||||
|
||||
result = {
|
||||
'timestamp': now_iso,
|
||||
|
||||
@@ -33,8 +33,8 @@ done
|
||||
[ -n "$WORKSPACE" ] || { echo "ERROR: --workspace required" >&2; exit 2; }
|
||||
[ -n "$AGENT" ] || { echo "ERROR: --agent required" >&2; exit 2; }
|
||||
case "$AGENT" in
|
||||
claude|agy|hermes) ;;
|
||||
*) echo "ERROR: --agent must be claude or agy or hermes" >&2; exit 2 ;;
|
||||
claude|agy|hermes|cline) ;;
|
||||
*) echo "ERROR: --agent must be claude, agy, hermes, or cline" >&2; exit 2 ;;
|
||||
esac
|
||||
|
||||
find_workspace_uuid "$WORKSPACE" "$AGENT"
|
||||
|
||||
@@ -41,6 +41,7 @@ if [ -z "$AGENT" ]; then
|
||||
*-creator-claude) AGENT=claude ;;
|
||||
*-creator-agy) AGENT=agy ;;
|
||||
*-creator-hermes) AGENT=hermes ;;
|
||||
*-creator-cline) AGENT=cline ;;
|
||||
*) echo "ERROR: cannot infer agent from '$SESSION_NAME'; pass --agent" >&2; exit 2 ;;
|
||||
esac
|
||||
fi
|
||||
@@ -51,7 +52,7 @@ NOW_ISO=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
PANE_PID=$(tmux list-panes -t "$SESSION_NAME" -F '#{pane_pid}' 2>/dev/null | head -1 || true)
|
||||
PANE_PID="${PANE_PID:-}"
|
||||
CHILD_PID=0
|
||||
if { [ "$AGENT" = "agy" ] || [ "$AGENT" = "hermes" ]; } && [ -n "$PANE_PID" ]; then
|
||||
if { [ "$AGENT" = "agy" ] || [ "$AGENT" = "hermes" ] || [ "$AGENT" = "cline" ]; } && [ -n "$PANE_PID" ]; then
|
||||
CHILD_PID=$(pgrep -P "$PANE_PID" -x "$AGENT" 2>/dev/null | head -1 || true)
|
||||
CHILD_PID="${CHILD_PID:-0}"
|
||||
fi
|
||||
@@ -144,6 +145,13 @@ elif agent == 'hermes':
|
||||
cp = os.environ.get('CHILD_PID', '0')
|
||||
if cp.isdigit() and int(cp) > 0:
|
||||
target['child_pid'] = int(cp)
|
||||
elif agent == 'cline':
|
||||
target['pane']['cmd'] = 'cline'
|
||||
target['pane']['cmd_full'] = f'cline -i --id {uuid}'
|
||||
target['cline_conversation_id_own'] = uuid
|
||||
cp = os.environ.get('CHILD_PID', '0')
|
||||
if cp.isdigit() and int(cp) > 0:
|
||||
target['child_pid'] = int(cp)
|
||||
|
||||
snap = d.setdefault('snapshot', {})
|
||||
snap['taken_at'] = now
|
||||
|
||||
@@ -76,6 +76,7 @@ if [ -z "$AGENT" ]; then
|
||||
*-creator-claude) AGENT=claude ;;
|
||||
*-creator-agy) AGENT=agy ;;
|
||||
*-creator-hermes) AGENT=hermes ;;
|
||||
*-creator-cline) AGENT=cline ;;
|
||||
*) echo "ERROR: cannot infer agent from '$SESSION_NAME'; pass --agent" >&2; exit 2 ;;
|
||||
esac
|
||||
fi
|
||||
@@ -184,6 +185,7 @@ graceful_stop() {
|
||||
claude) exitkey="/exit" ;;
|
||||
agy) exitkey="Exit" ;;
|
||||
hermes) exitkey="/exit" ;;
|
||||
cline) exitkey="/exit" ;;
|
||||
*) exitkey="/exit" ;;
|
||||
esac
|
||||
echo "graceful: send-keys '$exitkey' to $SESSION_NAME"
|
||||
@@ -263,6 +265,8 @@ if captured and not purge:
|
||||
target['agy_conversation_id_own'] = captured
|
||||
elif agent == 'hermes':
|
||||
target['hermes_conversation_id_own'] = captured
|
||||
elif agent == 'cline':
|
||||
target['cline_conversation_id_own'] = captured
|
||||
target['resumable'] = True
|
||||
|
||||
# --purge-conversation: 워크스페이스 격리된 UUID 의 디스크 artifact 만 삭제 (P0-C)
|
||||
@@ -286,23 +290,29 @@ if purge and purge_uuid:
|
||||
print(f"purged: {brain}", flush=True)
|
||||
target['agy_conversation_id_own'] = None
|
||||
elif agent == 'hermes':
|
||||
json_file = f"{home}/.mam/sessions/session_{purge_uuid}.json"
|
||||
json_file = f"{home}/.hermes/sessions/session_{purge_uuid}.json"
|
||||
if os.path.exists(json_file):
|
||||
os.remove(json_file)
|
||||
print(f"purged: {json_file}", flush=True)
|
||||
hdb = f"{home}/.mam/state.db"
|
||||
hdb = f"{home}/.hermes/state.db"
|
||||
if os.path.exists(hdb):
|
||||
try:
|
||||
import sqlite3
|
||||
conn = sqlite3.connect(hdb)
|
||||
conn.execute("DELETE FROM sessions WHERE id=?", (purge_uuid,))
|
||||
conn.execute("DELETE FROM messages WHERE session_id=?", (purge_uuid,))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
hconn = sqlite3.connect(hdb)
|
||||
hconn.execute("DELETE FROM sessions WHERE id=?", (purge_uuid,))
|
||||
hconn.execute("DELETE FROM messages WHERE session_id=?", (purge_uuid,))
|
||||
hconn.commit()
|
||||
hconn.close()
|
||||
print(f"purged db records for session: {purge_uuid}", flush=True)
|
||||
except Exception as e:
|
||||
print(f"WARN: purge hermes db records failed: {e}", flush=True)
|
||||
target['hermes_conversation_id_own'] = None
|
||||
elif agent == 'cline':
|
||||
sessions_dir = f"{home}/.cline/data/sessions/{purge_uuid}"
|
||||
if os.path.isdir(sessions_dir):
|
||||
shutil.rmtree(sessions_dir)
|
||||
print(f"purged: {sessions_dir}", flush=True)
|
||||
target['cline_conversation_id_own'] = None
|
||||
# agent_identities 는 cache — 이 워크스페이스 것일 때만 비운다
|
||||
ai = (d.get('agent_identities') or {}).get(agent) or {}
|
||||
if ai.get('project_cwd') == ws:
|
||||
@@ -317,6 +327,8 @@ if purge and purge_uuid:
|
||||
ai['conversation_brain_dir'] = None
|
||||
elif agent == 'hermes' and ai.get('session_id') == purge_uuid:
|
||||
ai['session_id'] = None
|
||||
elif agent == 'cline' and ai.get('session_id') == purge_uuid:
|
||||
ai['session_id'] = None
|
||||
elif purge and not purge_uuid:
|
||||
print("WARN: --purge-conversation requested but no workspace-scoped UUID resolved; nothing purged", flush=True)
|
||||
|
||||
|
||||
@@ -4,3 +4,5 @@
|
||||
.mam/*.db-wal
|
||||
.mam/*.db-shm
|
||||
.mam/*.bak
|
||||
.agents/
|
||||
|
||||
|
||||
@@ -1 +1,59 @@
|
||||
tmux_sessions: []
|
||||
snapshot:
|
||||
taken_at: '2026-07-03T01:49:10Z'
|
||||
cwd: /Users/godopu16/PuKi/lab/canary_projects/multi-agent-paper
|
||||
tmux_sessions:
|
||||
- name: canary-projects-multi-agent-paper-creator-claude
|
||||
status: stopped
|
||||
role: researcher-reviewer
|
||||
tmux_session_created_at: '2026-06-30T02:14:27Z'
|
||||
tmux_session_epoch: 1782785667
|
||||
tmux_server: multi-agent-paper
|
||||
delegate_job_id: null
|
||||
pane:
|
||||
index: 0
|
||||
pid: 13504
|
||||
cmd: claude
|
||||
cmd_full: claude --dangerously-skip-permissions -r d6eb5e41-a7a6-4c92-b6f2-4133da3a292e
|
||||
cwd: /Users/godopu16/PuKi/lab/canary_projects/multi-agent-paper
|
||||
start_command: tmux -L multi-agent-paper new-session -d -s "canary-projects-multi-agent-paper-creator-claude" -x 140 -y 40 -c "/Users/godopu16/PuKi/lab/canary_projects/multi-agent-paper" "claude --dangerously-skip-permissions"
|
||||
attach_command: tmux -L multi-agent-paper attach -t canary-projects-multi-agent-paper-creator-claude
|
||||
kill_command: tmux -L multi-agent-paper kill-session -t canary-projects-multi-agent-paper-creator-claude
|
||||
tui:
|
||||
model: (unknown — capture after first message)
|
||||
provider: anthropic
|
||||
plan: (unknown)
|
||||
account: (unknown — read from claude auth status)
|
||||
version: (unknown — read from TUI)
|
||||
claude_session_id_own: d6eb5e41-a7a6-4c92-b6f2-4133da3a292e
|
||||
last_visible_status: resumed conversation d6eb5e41-a7a6-4c92-b6f2-4133da3a292e at 2026-07-03T01:49:07Z
|
||||
last_visible_status_at_termination: ' 32 33 -셋째, **구조화된 직렬화 기반의 태스크 생명주기 관리 및 부하 분산 효율**이다. Protobuf(Protocol Buffers)의 스키마 정의형 바이너리 직렬화는 텍스트 기반 JSON 파싱 대비 직렬화·역직렬화 연 -산 비용과 페이로드 크기를 현저히 절감하여, 동적으로 생성·소멸하는 다수의 서브에이전트와 오케스트레이터 간에 폭증하는 제어 메시지를 메모리 효율적으로 처리할'
|
||||
stopped_at: '2026-07-03T14:17:34Z'
|
||||
stopped_at_epoch: 1783088254
|
||||
stop_reason: manual_stop
|
||||
termination_mode: graceful
|
||||
resumable: true
|
||||
- name: canary-projects-multi-agent-paper-creator-cline
|
||||
status: stopped
|
||||
role: researcher-reviewer
|
||||
tmux_session_created_at: '2026-06-30T02:23:09Z'
|
||||
tmux_session_epoch: 1782786189
|
||||
tmux_server: multi-agent-paper
|
||||
delegate_job_id: null
|
||||
pane:
|
||||
index: 0
|
||||
pid: 13609
|
||||
cmd: cline
|
||||
cmd_full: cline -i --id 1782786184987_wcllh
|
||||
cwd: /Users/godopu16/PuKi/lab/canary_projects/multi-agent-paper
|
||||
start_command: tmux -L multi-agent-paper new-session -d -s "canary-projects-multi-agent-paper-creator-cline" -x 140 -y 40 -c "/Users/godopu16/PuKi/lab/canary_projects/multi-agent-paper" "cline -i"
|
||||
attach_command: tmux -L multi-agent-paper attach -t canary-projects-multi-agent-paper-creator-cline
|
||||
kill_command: tmux -L multi-agent-paper kill-session -t canary-projects-multi-agent-paper-creator-cline
|
||||
child_pid: 0
|
||||
cline_conversation_id_own: 1782786184987_wcllh
|
||||
last_visible_status: resumed conversation 1782786184987_wcllh at 2026-07-03T01:49:10Z
|
||||
last_visible_status_at_termination: ' │IDL을 "명확한 계약서"에 비유 │33 │"사전에 문서로 못박아 두는 ''명확한 계약서'' 역할 ... 계약서에 서명한 양측이 조항을 재협상할 필요 없이 곧바로 거래할 수 있듯이" │✅ 정확 반영 │ ├────────────────────────────────┼───┼────────────────────────────────────'
|
||||
stopped_at: '2026-07-03T14:17:37Z'
|
||||
stopped_at_epoch: 1783088257
|
||||
stop_reason: manual_stop
|
||||
termination_mode: graceful
|
||||
resumable: true
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
.agents/AGENT.md
|
||||
.agents/AGENT.ko.md
|
||||
.agents/skills/multi-agent-mux-stop/scripts/stop_session.sh
|
||||
.agents/skills/multi-agent-mux-stop/SKILL.md
|
||||
.agents/skills/multi-agent-mux-monitor/scripts/reconcile.sh
|
||||
@@ -5,6 +7,7 @@
|
||||
.agents/skills/multi-agent-mux-delegate-job/mqtt-broker-setup.md
|
||||
.agents/skills/multi-agent-mux-delegate-job/requirements.txt
|
||||
.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job
|
||||
.agents/skills/multi-agent-mux-delegate-job/DELEGATION_TYPES.md
|
||||
.agents/skills/multi-agent-mux-delegate-job/README.md
|
||||
.agents/skills/multi-agent-mux-delegate-job/scripts/publish_event.py
|
||||
.agents/skills/multi-agent-mux-delegate-job/scripts/registry.py
|
||||
@@ -21,8 +24,6 @@
|
||||
.agents/skills/multi-agent-mux-resume/SKILL.md
|
||||
.agents/skills/multi-agent-mux-status/scripts/status.sh
|
||||
.agents/skills/multi-agent-mux-status/SKILL.md
|
||||
AGENT.md
|
||||
AGENT.ko.md
|
||||
MESSAGING.md
|
||||
BOOTSTRAP.md
|
||||
BOOTSTRAP.ko.md
|
||||
|
||||
-126
@@ -1,126 +0,0 @@
|
||||
# AGENT.md
|
||||
|
||||
본 문서는 새로운 프로젝트에 **MQTT 메시징 백플레인 및 Tmux 기반 멀티 에이전트 오케스트레이션 워크플로우**를 도입하고, 협업하는 에이전트들이 일관된 규칙과 아키텍처에 따라 안전하고 견고하게 작업을 수행할 수 있도록 정의한 공통 지침 및 규약입니다.
|
||||
|
||||
새로운 프로젝트에서 작업하는 모든 에이전트는 작업을 시작하기 전 이 문서를 반드시 정독하고 규약을 준수해야 합니다.
|
||||
|
||||
---
|
||||
|
||||
## 1. 에이전트의 역할 정의 (Agent Roles)
|
||||
|
||||
역할군 간의 책임 및 권한을 명확히 분리하여 병목을 줄이고 작업의 완성도를 높입니다.
|
||||
|
||||
### 👤 Project Manager (PM / Orchestrator)
|
||||
- **주요 책무**: 사용자 요구사항 접수, 상세 작업 계획 수립, 작업자 할당/지시, 전체 워크플로우 통제 및 최종 결과 보고.
|
||||
- **모호성 제거**: 사용자의 요구사항에 모호한 부분이 있다면 작업을 추측하여 진행하지 말고, 즉시 사용자에게 질문하여 명확히 해야 합니다 (`/grill-me` 슬래시 명령어 권장).
|
||||
- **피드백 루프 조정**: Reviewer들의 검증 의견을 분석하여 개선 방향을 의사결정합니다. 결정하기 까다로운 기술적 난제는 Worker 및 Reviewer들의 조사를 거쳐 PM 본인의 판단을 더한 최종 보고서를 작성해 사용자에게 제시하고 프로젝트의 방향을 결정합니다.
|
||||
- **자가 치유 (Hermes Fallback Fix)**: Reviewer가 지적한 결함이 아주 경미하거나 단순 오탈자/설정 누락인 경우, Worker에게 재할당하지 않고 PM이 직접 소스코드를 수정하여 전체 왕복(Round-trip) 비용을 최소화합니다.
|
||||
|
||||
### 🛠️ Worker (Implementation Agent)
|
||||
- **주요 책무**: PM으로부터 위임받은 구체적인 비즈니스 로직 설계 및 소스코드 구현.
|
||||
- **협업 및 소통**: 할당받은 업무 범위에서 구현 방향이 모호하거나 인터페이스 설계 변경이 필요한 경우 PM에게 질문하여 합의를 이룬 후 수술적(Surgical) 변경을 적용합니다.
|
||||
- **계약 준수**: PM이 전달한 단일 작업 지침(Brief) 및 고유 Job ID 규약을 준수하며, 작업 시작 시 `started`, 종료 시 `completed`/`error` 이벤트를 백플레인에 발행해야 합니다.
|
||||
|
||||
### 🔍 Reviewer (Verification Agent)
|
||||
- **주요 책무**: Worker가 제출한 소스코드 변경 사항(Diff)과 구현 명세를 검증하고, 보안 결함 탐지, 성능 개선안 도출 및 설계 일관성을 심사하는 조력자.
|
||||
- **구체적 대안 제시**: 단순한 반려(`NOT PASS`) 통보를 금지하며, 문제를 제기할 때는 **안정적이고 검증된 구체적인 코드 대안(Alternative Code)이나 해결 방안을 반드시 함께 제시**해야 합니다.
|
||||
- **교차 검증의 상호보완성**: 에이전트의 모델 특성(예: Flash 계열은 의미론적 셸 결함 포착에 강하고, Opus/Sonnet 계열은 API 서명 및 논리 회귀 분석에 강함)을 살려 병렬로 상호보완적 심사를 수행합니다.
|
||||
|
||||
---
|
||||
|
||||
## 2. 메시징 백플레인 & 레지스트리 규약
|
||||
|
||||
에이전트 간의 비동기 소통과 상태 관리는 분산 이벤트 채널 및 파일/DB 레지스트리를 통해 제어됩니다.
|
||||
|
||||
### 📡 MQTT 백플레인 (MQTT Backplane)
|
||||
- **이벤트 라이프사이클**:
|
||||
- `started` (작업 개시) ➡️ `progress`/`permission_required` (진행 상황 공유) ➡️ `completed` (성공 종료) 또는 `error` (실패 종료)
|
||||
- `completed` 및 `error`는 단 한 번만 발행되는 단말(Terminal) 이벤트입니다.
|
||||
- **메시지 발행/구독 규칙**:
|
||||
- MQTT는 영속 큐를 보장하지 않으므로, 에이전트 구동 전 **반드시 구독자(`job_subscriber.py`)가 먼저 백그라운드에서 대기**해야 합니다 (Subscribe-before-Publish 원칙).
|
||||
- 단말 이벤트 발행 시 브로커에 `retain=True`로 영속화하여 늦게 합류한 구독자도 최종 상태를 읽을 수 있도록 조치합니다.
|
||||
- 전송 데이터에는 비밀번호, 개인키 등의 중요 비밀 정보나 절대 경로가 포함되지 않도록 보편화(Generalised)해야 합니다.
|
||||
|
||||
### 🗃️ 레지스트리 및 상태 관리
|
||||
- 본 아키텍처는 목적에 따라 두 가지 레지스트리를 분리하여 운영합니다:
|
||||
- **잡 레지스트리 (Job Registry)**: 각 비동기 잡의 메타데이터와 생명주기는 개별 JSON 파일(`.mam/jobs/<id>.json`)로 기록되며, 다중 세션 간의 동시 청구(claiming) 경합은 파일 단위의 `fcntl` advisory lock(`registry_lock` via `registry.py`)을 통해 방어합니다.
|
||||
- **세션 레지스트리 (Session Registry)**: TMUX 모니터링 상태 및 에이전트 구동 정보는 SQLite WAL 데이터베이스(`.mam/agent-sessions.db`)를 통해 단일 호스트 내에서 안정적인 동시 트랜잭션으로 일관되게 제어합니다. 단, SQLite WAL 모드는 NFS(네트워크 파일 시스템) 환경에서는 완전한 파일 락이 보장되지 않으므로 로컬 파일 시스템 사용을 권장합니다.
|
||||
|
||||
### 🛡️ 보안 프로토콜 (HMAC-SHA256)
|
||||
- **무인증 PoC 모드**: 잡 레지스트리 생성 시 `auth_token`이 `null`로 지정된 경우(PoC 기본 모드), 별도의 서명 검증을 생략하고 모든 이벤트를 수용합니다 (`verify_hmac`이 항상 `True`를 반환).
|
||||
- **인증 Production 모드**: 실배포 환경이나 인증이 필요한 연동 단계에서는 각 잡마다 고유 암호화 토큰(`auth_token`)을 발급합니다. 퍼블리셔는 이 토큰을 키로 삼아 `hmac_sig` 서명을 페이로드에 동반해야 하며, 수신단(`verify_hmac`)에서 서명이 없거나 일치하지 않는 메시지는 즉시 드랍하여 다운그레이드 공격을 원천 차단합니다.
|
||||
- **롤아웃 전략**: 보안 스킴 갱신 시 송수신 노드 간 불일치로 인한 이벤트 드랍을 피하기 위해, 과도기적 하이브리드 포맷 전송(평문 유출 위험 있음)을 배제하고 **모든 노드를 일제히 업데이트하는 "동시 롤아웃(Simultaneous Rollout)"**을 채택해야 합니다.
|
||||
|
||||
---
|
||||
|
||||
## 3. 협업 워크플로우 실행 절차 (Workflow Loop)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
actor User as 사용자
|
||||
participant PM as Project Manager
|
||||
participant W as Worker
|
||||
participant R as Reviewers
|
||||
participant M as MQTT Backplane
|
||||
|
||||
User->>PM: 요구사항 전달
|
||||
Note over PM: grill-me 및 계획 수립
|
||||
PM->>M: Job 등록 및 Subscriber 구동
|
||||
PM->>W: 작업 위임 (Job ID & Brief 전달)
|
||||
W->>M: 'started' 이벤트 발행
|
||||
Note over W: 코드 변경 및 구현
|
||||
W->>M: 'completed' (혹은 'error') 발행
|
||||
PM->>R: 병렬 리뷰 요청 (Diff 전달)
|
||||
Note over R: 교차 분석 & 검증
|
||||
alt 결함 발견
|
||||
R->>PM: NOT PASS (대안 포함 피드백)
|
||||
Note over PM: 경미한 결함은 PM이 직접 수정
|
||||
PM->>W: 피드백 반영 및 재할당
|
||||
else 검증 통과
|
||||
R->>PM: PASS
|
||||
end
|
||||
PM->>User: 최종 검증 통과 보고 & 커밋
|
||||
```
|
||||
|
||||
1. **계획 수립 및 할당**: PM은 사용자 요청을 구체화하고 의존성이 겹치지 않는 범위에서 잡을 정의합니다.
|
||||
2. **작업 개시 및 통보**: PM은 구독자를 띄운 뒤 Worker 세션에 잡을 인가하며, Worker는 로직을 수행하고 단말 이벤트를 전송해 세션을 자동 종료합니다.
|
||||
3. **교차 검수 반복 (Review Loop)**: PM은 작업 완료 후 변경분을 Reviewer 에이전트들에게 병렬 회람시킵니다. 리뷰어 전원이 `PASS` 의견을 낼 때까지 수정-반려 주기를 무한 반복(Loop)하여 코드 완성도를 보증합니다.
|
||||
4. **릴리즈 및 정리**: 검증이 완료된 코드는 Git에 커밋하고, 임시 세션 리소스를 회수합니다.
|
||||
|
||||
---
|
||||
|
||||
## 4. 분석 인프라 패턴 & 실무 가이드 (Infra Patterns)
|
||||
|
||||
장기 실행 에이전트 분석 중 발생하는 유실 및 인프라적 장애를 예방하기 위한 중요 지침입니다.
|
||||
|
||||
### 📸 TUI 뷰포트 절단 방지 (Pane Snapshotting 3대 규칙)
|
||||
TMUX 환경에서 실행되는 에이전트가 화면 스크롤 한계로 인해 이전 출력이나 장문의 디버깅 로그를 잃지 않도록 아래의 **스냅샷 패턴을 의무적으로 수행**합니다.
|
||||
1. **Pre-brief Capture**: 작업 지침(Brief)을 전송한 직후, 즉시 해당 세션의 pane을 캡처(`capture-pane -S -200`)해두어 입력 기록의 시작점을 백업합니다.
|
||||
2. **Loop Snapshot**: 장기 실행(5분 이상) 중인 에이전트 세션의 경우, 주기적으로(예: 30초마다) 뷰포트를 스캔하여 증분 데이터를 `/tmp/pane-snap.txt`에 계속 누적(append) 기록합니다.
|
||||
3. **Post-job Capture**: 잡 완료/에러 반환 즉시 전체 pane 상태를 마지막으로 캡처하여 전체 작업 궤적을 보존합니다.
|
||||
|
||||
### 📄 장문 브리핑 전달 방식
|
||||
- TMUX `send-keys`나 입력 버퍼를 통해 수백 줄의 장문 지시나 프롬프트를 직렬로 입력하면, 에이전트의 TUI가 이를 모두 온전히 소화하지 못하고 일부 문자나 문단이 탈취/누락될 수 있습니다.
|
||||
- **해결 지침**: 지시 사항이 긴 경우, 반드시 `/tmp/brief-<job_id>.md` 등의 파일 경로로 지시문을 별도 작성해 전달하고, 에이전트에는 `"Read /tmp/brief-... and execute"` 라는 단순화된 실행 명령만 전달하십시오.
|
||||
|
||||
### ⏱️ 타임아웃 구성 및 정렬 규칙
|
||||
- **잡 실행 제한 (`timeout_sec` & `idle_timeout_sec`)**: 각 잡은 전체 실행 만료 시간(`timeout_sec`, 기본 3600s)과 메세지 미수신 유휴 시간(`idle_timeout_sec`, 기본 120s)을 독립적으로 가집니다.
|
||||
- **모니터 유휴 대기 (`SUB_IDLE_TIMEOUT`)**: 모니터 스크립트(`reconcile.sh`)의 유휴 대기 시간(`SUB_IDLE_TIMEOUT`) 기본값은 잡 최대 예산에 맞춰 `3600s`(1시간) 이상으로 항상 넉넉히 설정해야 합니다. 모니터가 작업 완료 전에 유휴 감지로 조기 자동 종료되어 백그라운드 태스크 관리를 소실하는 문제를 방지하기 위함입니다.
|
||||
|
||||
---
|
||||
|
||||
## 5. 새 프로젝트 적용 체크리스트 (Setup Checklist)
|
||||
|
||||
새 프로젝트에 이 에이전트 오케스트레이션 모델을 구축할 때의 체크리스트입니다.
|
||||
|
||||
- [ ] **가상환경 의존성**: `pyyaml`, `paho-mqtt` 등 필요한 Python 패키지가 `.venv` 또는 `requirements.txt`에 포함되었는가?
|
||||
- [ ] **환경 설정 파일**: MQTT 브로커 주소 및 보안 Credential이 `.env` 파일에 안전하게 로드되고 공유되는가?
|
||||
- [ ] **디렉토리 규약**: 레지스트리 경로(`.mam/jobs/`) 및 로깅 경로(`.mam/delegate_job_logs/`)가 `.gitignore`에 등록되었는가?
|
||||
- [ ] **스크립트 구비**: `mqtt_common.py`, `publish_event.py`, `job_subscriber.py`, `registry.py` 등의 핵심 모듈이 배치되었는가?
|
||||
- [ ] **HMAC 활성화**: 새로운 레지스트리 잡 발급 시 난수 기반의 `auth_token`이 정상적으로 주입되고, 서명 기반의 상호 인증이 활성화되는가?
|
||||
- [ ] **운영 헌장 배치**: 본 규약 파일(`AGENT.md`)이 새 프로젝트의 **최상위 루트(Root) 디렉터리**에 배치되었는가? (협업을 수행하는 에이전트들이 온보딩 시 규칙을 가장 먼저 인지할 수 있도록 루트 경로 배치가 필수적입니다.)
|
||||
|
||||
---
|
||||
|
||||
*본 가이드는 협업 효율성과 코드 보안의 엄격한 균형을 유지하기 위한 규범입니다. 변경 사항이 필요한 경우 PM 및 Reviewer의 전원 합의를 거쳐 본 문서를 업데이트해야 합니다.*
|
||||
@@ -1,126 +0,0 @@
|
||||
# AGENT.md
|
||||
|
||||
This document serves as the common guidelines and protocol for introducing the **MQTT messaging backplane and Tmux-based multi-agent orchestration workflow** to a new project. It defines the rules and architecture to ensure collaborating agents perform tasks safely, robustly, and consistently.
|
||||
|
||||
All agents working on a new project must read this document thoroughly and comply with the defined protocols before starting any tasks.
|
||||
|
||||
---
|
||||
|
||||
## 1. Agent Roles Definition (Agent Roles)
|
||||
|
||||
We clearly separate responsibilities and permissions between roles to reduce bottlenecks and enhance the quality of execution.
|
||||
|
||||
### 👤 Project Manager (PM / Orchestrator)
|
||||
- **Core Responsibility**: Receive user requirements, establish detailed task plans, assign and instruct workers, control the overall workflow, and report final results.
|
||||
- **Ambiguity Resolution**: If a user's requirements contain ambiguous details, do not guess. Immediately ask the user for clarification (we recommend using the `/grill-me` slash command).
|
||||
- **Feedback Loop Adjustment**: Analyze verification feedback from Reviewers to decide on improvement paths. For complex technical challenges, direct Workers and Reviewers to research options, add the PM's own assessment, and present a final report to the user to decide the project's direction.
|
||||
- **Self-Healing (Hermes Fallback Fix)**: If a defect pointed out by a Reviewer is extremely minor or is a simple typo/configuration omission, the PM should directly fix the source code instead of reassigning it to the Worker, thereby minimizing the round-trip cost.
|
||||
|
||||
### 🛠️ Worker (Implementation Agent)
|
||||
- **Core Responsibility**: Design business logic and implement source code as delegated by the PM.
|
||||
- **Collaboration & Communication**: If the implementation path is ambiguous or interface design changes are required within the assigned scope, ask the PM for consensus before applying surgical changes.
|
||||
- **Contract Adherence**: Comply with the single task instructions (Brief) and the unique Job ID convention provided by the PM. Workers must publish a `started` event when starting work, and a `completed` or `error` event to the backplane upon termination.
|
||||
|
||||
### 🔍 Reviewer (Verification Agent)
|
||||
- **Core Responsibility**: Verify source code changes (Diff) and implementation specifications submitted by Workers. Reviewers act as facilitators by detecting security vulnerabilities, proposing performance improvements, and examining design consistency.
|
||||
- **Provide Concrete Alternatives**: Simply rejecting changes (`NOT PASS`) is forbidden. When raising an issue, Reviewers must propose a **concrete, stable, and verified alternative code block or solution**.
|
||||
- **Complementary Cross-Verification**: Leverage the unique characteristics of different agent models (e.g., Flash-class models are skilled at capturing semantic shell bugs, while Opus/Sonnet-class models excel at API signatures and logical regression analysis) to perform parallel and mutually-supportive reviews.
|
||||
|
||||
---
|
||||
|
||||
## 2. Messaging Backplane & Registry Protocol
|
||||
|
||||
Asynchronous communication and state management between agents are controlled via distributed event channels and file/DB registries.
|
||||
|
||||
### 📡 MQTT Backplane
|
||||
- **Event Lifecycle**:
|
||||
- `started` (Job execution starts) ➡️ `progress`/`permission_required` (Share intermediate progress) ➡️ `completed` (Successful termination) or `error` (Failed termination)
|
||||
- `completed` and `error` are terminal events that are published exactly once.
|
||||
- **Publish/Subscribe Rules**:
|
||||
- Since MQTT does not guarantee persistent queues, the subscriber (`job_subscriber.py`) **must be running in the background before the agent starts** (the Subscribe-before-Publish principle).
|
||||
- When publishing terminal events, publish with `retain=True` on the broker so that subscribers joining late can still read the final state.
|
||||
- Generalize all transmitted data to ensure that sensitive secrets like passwords, private keys, or absolute system paths are not included.
|
||||
|
||||
### 🗃️ Registry & State Management
|
||||
- This architecture maintains two distinct registries based on their purpose:
|
||||
- **Job Registry**: The metadata and lifecycle of each asynchronous job are recorded in individual JSON files (`.mam/jobs/<id>.json`). Concurrency conflicts (claiming races) across multiple sessions are prevented via file-based `fcntl` advisory locks (`registry_lock` via `registry.py`).
|
||||
- **Session Registry**: TMUX monitoring states and running agent metadata are consistently controlled using a SQLite WAL database (`.mam/agent-sessions.db`) to support reliable concurrent transactions on a single host. However, since SQLite WAL mode does not guarantee complete file locking in Network File System (NFS) environments, we recommend using a local file system.
|
||||
|
||||
### 🛡️ Security Protocol (HMAC-SHA256)
|
||||
- **Unauthenticated PoC Mode**: If the `auth_token` in the job registry is set to `null` (the default PoC mode), signature verification is skipped and all events are accepted (`verify_hmac` always returns `True`).
|
||||
- **Authenticated Production Mode**: In production environments or integrations requiring authentication, a unique cryptographic token (`auth_token`) is issued for each job. The publisher must include an `hmac_sig` signature in the payload keyed by this token, and the receiving end (`verify_hmac`) will immediately drop messages that lack a signature or have mismatching signatures to prevent downgrade attacks.
|
||||
- **Rollout Strategy**: To avoid event drops caused by inconsistencies between publishing and receiving nodes when updating security schemes, hybrid transition formats (which risk leaking plaintext tokens) must not be used. Instead, adopt a **"Simultaneous Rollout"** where all nodes are updated at once.
|
||||
|
||||
---
|
||||
|
||||
## 3. Collaborative Workflow Execution Loop (Workflow Loop)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
actor User as User
|
||||
participant PM as Project Manager
|
||||
participant W as Worker
|
||||
participant R as Reviewers
|
||||
participant M as MQTT Backplane
|
||||
|
||||
User->>PM: Hand over requirements
|
||||
Note over PM: Run grill-me & plan tasks
|
||||
PM->>M: Register Job & start Subscriber
|
||||
PM->>W: Delegate task (Provide Job ID & Brief)
|
||||
W->>M: Publish 'started' event
|
||||
Note over W: Modify code & implement
|
||||
W->>M: Publish 'completed' (or 'error')
|
||||
PM->>R: Request parallel review (Provide Diff)
|
||||
Note over R: Cross-analysis & verification
|
||||
alt Defect Found
|
||||
R->>PM: NOT PASS (Feedback with alternatives)
|
||||
Note over PM: PM directly fixes minor defects
|
||||
PM->>W: Apply feedback & re-delegate
|
||||
else Verification Pass
|
||||
R->>PM: PASS
|
||||
end
|
||||
PM->>User: Report final pass & commit changes
|
||||
```
|
||||
|
||||
1. **Planning and Allocation**: The PM defines requirements and outlines independent jobs to avoid conflicting dependencies.
|
||||
2. **Execution and Notification**: The PM launches a subscriber, then assigns the job to a Worker session. The Worker performs the logic and sends a terminal event, automatically closing the session.
|
||||
3. **Cross-Verification Iteration (Review Loop)**: Once the task is complete, the PM circulates the changes to the Reviewer agents in parallel. The modify-reject cycle repeats until all reviewers yield a `PASS`, ensuring high-quality code.
|
||||
4. **Release and Cleanup**: Code that passes verification is committed to Git, and temporary session resources are reclaimed.
|
||||
|
||||
---
|
||||
|
||||
## 4. Analysis Infrastructure Patterns & Practical Guide (Infra Patterns)
|
||||
|
||||
These are critical instructions for preventing data loss and infrastructure-level failures during long-running agent analyses.
|
||||
|
||||
### 📸 Preventing TUI Viewport Truncation (The 3 Pane Snapshotting Rules)
|
||||
To ensure that agents running in TMUX environments do not lose debug logs or previous outputs due to screen scrollback limits, the following **snapshotting pattern must be enforced**:
|
||||
1. **Pre-brief Capture**: Capture the pane (`capture-pane -S -200`) immediately after sending the task instruction (Brief) to back up the starting point of the input history.
|
||||
2. **Loop Snapshot**: For long-running agent sessions (5 minutes or more), periodically (e.g., every 30 seconds) scan the viewport and append the incremental data to `/tmp/pane-snap.txt`.
|
||||
3. **Post-job Capture**: Capture the complete pane state one final time immediately after a job completes or returns an error to preserve the entire execution trajectory.
|
||||
|
||||
### 📄 Handling Long Briefing Instructions
|
||||
- Sending long instructions or prompts (hundreds of lines) sequentially via TMUX `send-keys` or input buffers can overwhelm the agent's TUI, leading to lost characters or truncated paragraphs.
|
||||
- **Resolution**: If instructions are long, write them separately to a file path (e.g., `/tmp/brief-<job_id>.md`) and send a simplified execution command to the agent: `"Read /tmp/brief-... and execute"`.
|
||||
|
||||
### ⏱️ Timeout Configuration & Alignment Rules
|
||||
- **Job Execution Limits (`timeout_sec` & `idle_timeout_sec`)**: Each job independently manages its overall execution timeout (`timeout_sec`, default 3600s) and idle timeout without receiving messages (`idle_timeout_sec`, default 120s).
|
||||
- **Monitor Idle Waiting (`SUB_IDLE_TIMEOUT`)**: The idle timeout for the monitor script (`reconcile.sh`), `SUB_IDLE_TIMEOUT`, must always be set generously to `3600s` (1 hour) or more to align with the maximum job budget. This prevents the monitor from terminating early due to idle detection, which would lose control over background tasks before they finish.
|
||||
|
||||
---
|
||||
|
||||
## 5. Setup Checklist for New Projects (Setup Checklist)
|
||||
|
||||
Use this checklist when deploying this agent orchestration model to a new project:
|
||||
|
||||
- [ ] **Virtualenv Dependencies**: Are required Python packages like `pyyaml` and `paho-mqtt` included in `.venv` or `requirements.txt`?
|
||||
- [ ] **Configuration File**: Are the MQTT broker address and security credentials safely loaded and shared via the `.env` file?
|
||||
- [ ] **Directory Convention**: Are the registry path (`.mam/jobs/`) and logging path (`.mam/delegate_job_logs/`) added to `.gitignore`?
|
||||
- [ ] **Core Scripts**: Are the core scripts (`mqtt_common.py`, `publish_event.py`, `job_subscriber.py`, and `registry.py`) in place?
|
||||
- [ ] **HMAC Enablement**: When a new registry job is created, is a random `auth_token` correctly injected, and is signature-based mutual authentication active?
|
||||
- [ ] **Charter Placement**: Is this protocol file (`AGENT.md`) placed in the **top-level root directory** of the new project? (Placing it at the root is essential so that onboarding agents can recognize the rules immediately.)
|
||||
|
||||
---
|
||||
|
||||
*This guide balances collaboration efficiency with strict code security. Any required changes must be discussed and agreed upon by the PM and all Reviewers before updating this document.*
|
||||
+33
-13
@@ -10,7 +10,10 @@
|
||||
|
||||
본 프로젝트를 새로운 환경에 복제(Clone)한 후, 핵심 구성 요소들의 위치와 역할을 먼저 파악해야 합니다.
|
||||
|
||||
* `.agents/skills/`: 멀티 에이전트 구동 및 비동기 잡 처리를 수행하는 셸 스크립트 모음
|
||||
* `.agents/`: 오케스트레이션 및 에이전트 커스텀 스킬 디렉터리
|
||||
* `AGENT.md`: 에이전트 간의 역할 분담(PM, Worker, Reviewer) 및 이벤트 발행 규약 정의
|
||||
* `AGENT.ko.md`: 에이전트 간의 역할 분담(PM, Worker, Reviewer) 및 이벤트 발행 규약 정의 (한국어)
|
||||
* `skills/`: 멀티 에이전트 구동 및 비동기 잡 처리를 수행하는 셸 스크립트 모음
|
||||
* `lib.sh`: 오케스트레이션의 핵심 셸 함수 및 가상환경(venv) 자동 연동 라이브러리
|
||||
* `multi-agent-mux-create/`: 격리된 tmux 에이전트 세션을 시작하는 스크립트
|
||||
* `multi-agent-mux-stop/`: 세션을 정상적으로 중지하고 상태를 업데이트하는 스크립트
|
||||
@@ -24,16 +27,33 @@
|
||||
* `job_subscriber.py`: 백그라운드 이벤트 구독기 및 오디팅 로그 생성기
|
||||
* `publish_event.py`: 실행 상태 및 에러 트랩 시 이벤트 퍼블리셔
|
||||
* `mqtt_common.py`: 공통 MQTT 브로커 연결 유틸리티
|
||||
* `AGENT.md`: 에이전트 간의 역할 분담(PM, Worker, Reviewer) 및 이벤트 발행 규약 정의
|
||||
* `MESSAGING.md`: 에이전트 간 주고받는 MQTT 메시지 와이어 스킴 가이드라인
|
||||
|
||||
---
|
||||
|
||||
## 2. 환경 설정 파일 생성 (.env)
|
||||
## 2. 워크스페이스 프로비저닝 (단계 0 — 참고용)
|
||||
|
||||
에이전트가 이 안내서를 읽기 전에 워크스페이스 설치가 선행되어야 합니다. 이 단계는 이미 개발자(사람)가 설치기를 통해 수행 완료한 상태입니다. 에이전트가 환경 검증 작업을 수행할 때 파일들이 어떤 경로로 복사되고 설치되었는지에 대한 기초 사실(Reference)을 제공하기 위해 여기에 기록해 둡니다.
|
||||
|
||||
### 단계 0.1: 설치 스크립트 실행 (이미 기수행됨)
|
||||
인스톨러를 통해 워크스페이스를 기동하기 위해 사람이 다음 명령을 구동하였습니다:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://git.godopu.com/tmpl/multi-agent-mux/raw/branch/main/deploy/install.sh | bash
|
||||
```
|
||||
|
||||
인스톨러는 시스템 의존성 유효성을 검증하고 리포지토리를 불러와 오케스트레이션을 위한 `.agents/` 스켈레톤 및 관련 문서 파일(본 `BOOTSTRAP.ko.md` 포함)을 복사하고 `.env` 및 `.venv` 가상환경 설정을 자동으로 수행했습니다.
|
||||
|
||||
> [!NOTE]
|
||||
> **검증 전용 경고**: 본 가이드를 읽고 있는 AI 에이전트는 인스톨러 스크립트를 스스로 **다시 실행해서는 안 됩니다.** 즉시 환경 변수 설정 및 검증 테스트 단계로 넘어가십시오.
|
||||
|
||||
---
|
||||
|
||||
## 3. 환경 설정 파일 생성 (.env)
|
||||
|
||||
메시징 브로커 설정 및 실행 경로를 설정하기 위해 로컬 환경 설정 파일(`.env`)을 생성하고 수정해야 합니다.
|
||||
|
||||
### 단계 2.1: 자동 생성 스크립트 실행
|
||||
### 단계 3.1: 자동 생성 스크립트 실행
|
||||
프로젝트 루트에서 제공되는 환경 설정 템플릿 복사 스크립트를 실행합니다.
|
||||
|
||||
```bash
|
||||
@@ -44,7 +64,7 @@
|
||||
./scripts/generate-env.sh --force
|
||||
```
|
||||
|
||||
### 단계 2.2: 환경 변수 수정 및 설정
|
||||
### 단계 3.2: 환경 변수 수정 및 설정
|
||||
생성된 `.env` 파일을 열어 설정을 필요에 따라 구성합니다.
|
||||
|
||||
> [!NOTE]
|
||||
@@ -60,15 +80,15 @@
|
||||
> [!WARNING]
|
||||
> **보안 모드 기본값 안내**:
|
||||
> 시스템의 기본 설정은 **무인증 PoC 모드**입니다. 잡 등록 시 `auth_token`이 명시적으로 주입되지 않으면(또는 `null`인 경우) HMAC 서명 검증이 생략됩니다.
|
||||
> 공개 브로커 사용 환경이나 실제 프로덕션 단계에서는 잡 등록 시 `auth_token`을 고유 난수값으로 생성 및 주입하여 HMAC 보안 서명을 활성화해야 합니다. (자세한 보안 규약은 [MESSAGING.md](./MESSAGING.md) 및 [AGENT.ko.md](./AGENT.ko.md)의 `2.3 보안 프로토콜` 섹션을 참조하십시오. 현재 CLI를 통한 자동 토큰 생성/주입 기능 지원은 향후 로드맵의 `FW-N6` 과제로 처리 예정입니다.)
|
||||
> 공개 브로커 사용 환경이나 실제 프로덕션 단계에서는 잡 등록 시 `auth_token`을 고유 난수값으로 생성 및 주입하여 HMAC 보안 서명을 활성화해야 합니다. (자세한 보안 규약은 [MESSAGING.md](./MESSAGING.md) 및 [AGENT.ko.md](.agents/AGENT.ko.md)의 `2.3 보안 프로토콜` 섹션을 참조하십시오. 현재 CLI를 통한 자동 토큰 생성/주입 기능 지원은 향후 로드맵의 `FW-N6` 과제로 처리 예정입니다.)
|
||||
|
||||
---
|
||||
|
||||
## 3. 의존성 및 가상환경 설정 (Venv Setup)
|
||||
## 4. 의존성 및 가상환경 설정 (Venv Setup)
|
||||
|
||||
오케스트레이션 및 MQTT 메시징을 구동하기 위한 Python 3 의존성을 설정합니다.
|
||||
|
||||
### 단계 3.1: Python 가상환경 구축
|
||||
### 단계 4.1: Python 가상환경 구축
|
||||
프로젝트 루트에서 `.venv` 가상환경을 생성하고 활성화합니다.
|
||||
|
||||
```bash
|
||||
@@ -79,7 +99,7 @@ python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
### 단계 3.2: 의존성 패키지 설치
|
||||
### 단계 4.2: 의존성 패키지 설치
|
||||
`multi-agent-mux-delegate-job` 디렉터리에 기재된 `requirements.txt` 의존성 목록을 가상환경에 설치합니다.
|
||||
|
||||
```bash
|
||||
@@ -89,7 +109,7 @@ pip install -r .agents/skills/multi-agent-mux-delegate-job/requirements.txt
|
||||
|
||||
---
|
||||
|
||||
## 4. 디렉터리 준비 및 보안 감시 가이드
|
||||
## 5. 디렉터리 준비 및 보안 감시 가이드
|
||||
|
||||
에이전트 제어 상태 및 잡 기록을 위해 로컬 레지스트리 디렉터리가 정상적으로 생성되었는지 확인합니다.
|
||||
|
||||
@@ -110,7 +130,7 @@ pip install -r .agents/skills/multi-agent-mux-delegate-job/requirements.txt
|
||||
|
||||
---
|
||||
|
||||
## 5. 실행 환경 검증 및 부트스트랩 테스트
|
||||
## 6. 실행 환경 검증 및 부트스트랩 테스트
|
||||
|
||||
환경 구축이 오작동 없이 안전하게 완료되었는지 아래의 체크리스트를 실행해 검증합니다.
|
||||
|
||||
@@ -158,8 +178,8 @@ rm -f ".mam/jobs/$JID.json" ".mam/jobs/$JID.lock"
|
||||
|
||||
---
|
||||
|
||||
## 6. 에이전트 온보딩 가이드 (New Agent Onboarding)
|
||||
## 7. 에이전트 온보딩 가이드 (New Agent Onboarding)
|
||||
|
||||
본 환경 구축을 무사히 마쳤다면, 협업하는 에이전트는 즉시 프로젝트 루트에 있는 **[AGENT.ko.md](./AGENT.ko.md)** 문서를 읽어야 합니다.
|
||||
본 환경 구축을 무사히 마쳤다면, 협업하는 에이전트는 즉시 .agents/ 디렉터리에 있는 **[AGENT.ko.md](.agents/AGENT.ko.md)** 문서를 읽어야 합니다.
|
||||
|
||||
해당 문서에는 에이전트가 각 역할(PM, Worker, Reviewer)로 구동될 때 지켜야 할 **수술적 변경 규칙, 교차 검증 통과 규약, Tmux 뷰포트 유실 방지를 위한 스냅샷 패턴** 등이 서술되어 있어 안정적인 멀티 에이전트 워크플로우에 즉시 기여할 수 있도록 돕습니다.
|
||||
|
||||
+33
-13
@@ -10,7 +10,10 @@ A new agent can follow the steps in this guide sequentially to establish a stabl
|
||||
|
||||
Before cloning this project into a new environment, you must first understand the locations and roles of its core components:
|
||||
|
||||
* `.agents/skills/`: A collection of shell scripts that execute multi-agent coordination and asynchronous job processing.
|
||||
* `.agents/`: Orchestration and custom agent skills root.
|
||||
* `AGENT.md`: Definition of agent roles (PM, Worker, Reviewer) and event publication rules.
|
||||
* `AGENT.ko.md`: Definition of agent roles (PM, Worker, Reviewer) and event publication rules (Korean).
|
||||
* `skills/`: A collection of shell scripts that execute multi-agent coordination and asynchronous job processing.
|
||||
* `lib.sh`: The core orchestration shell functions and virtual environment (venv) auto-loading library.
|
||||
* `multi-agent-mux-create/`: Script to launch isolated tmux agent sessions.
|
||||
* `multi-agent-mux-stop/`: Script to gracefully stop agent sessions and update states.
|
||||
@@ -24,16 +27,33 @@ Before cloning this project into a new environment, you must first understand th
|
||||
* `job_subscriber.py`: Background event subscriber and audit log generator.
|
||||
* `publish_event.py`: Event publisher for runtime states and error traps.
|
||||
* `mqtt_common.py`: Common utility for connecting to the MQTT broker.
|
||||
* `AGENT.md`: Definition of agent roles (PM, Worker, Reviewer) and event publication rules.
|
||||
* `MESSAGING.md`: Messaging scheme and wire protocol guidelines for MQTT communication between agents.
|
||||
|
||||
---
|
||||
|
||||
## 2. Environment Configuration (.env)
|
||||
## 2. Workspace Provisioning (Step 0 — Reference Only)
|
||||
|
||||
Before any agent can read this guide, the workspace must be provisioned. This step has already been performed by the human developer using the installer. It is documented here strictly as a reference for verifying agents to understand how the assets arrived.
|
||||
|
||||
### Step 0.1: Run the Installer One-Liner (Pre-Cloned/Provisioned)
|
||||
The human developer bootstrapped the workspace by running:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://git.godopu.com/tmpl/multi-agent-mux/raw/branch/main/deploy/install.sh | bash
|
||||
```
|
||||
|
||||
The installer verified system dependencies, staged the repository, copied the runtime `.agents/` scaffolding and documentation files (including this `BOOTSTRAP.md`), and set up the default `.env` and `.venv` environments.
|
||||
|
||||
> [!NOTE]
|
||||
> **Verify Only**: AI agents reading this guide must **not** attempt to run the installation script again. Proceed directly to configuration and verification steps.
|
||||
|
||||
---
|
||||
|
||||
## 3. Environment Configuration (.env)
|
||||
|
||||
To set up the messaging broker and execution paths, you must create and modify a local environment configuration file (`.env`).
|
||||
|
||||
### Step 2.1: Run the Generation Script
|
||||
### Step 3.1: Run the Generation Script
|
||||
Run the environment template copy script provided in the project root:
|
||||
|
||||
```bash
|
||||
@@ -44,7 +64,7 @@ Run the environment template copy script provided in the project root:
|
||||
./scripts/generate-env.sh --force
|
||||
```
|
||||
|
||||
### Step 2.2: Modify Environment Variables
|
||||
### Step 3.2: Modify Environment Variables
|
||||
Open the generated `.env` file to configure settings as needed.
|
||||
|
||||
> [!NOTE]
|
||||
@@ -60,15 +80,15 @@ Open the generated `.env` file to configure settings as needed.
|
||||
> [!WARNING]
|
||||
> **Security Mode Default Warning**:
|
||||
> The system's default setting is the **unauthenticated PoC mode**. If an `auth_token` is not explicitly provided (or is `null`) during job registration, HMAC signature verification is skipped.
|
||||
> In a public broker environment or production phase, you must generate and inject a unique random `auth_token` during job registration to enable HMAC signature security. (For detailed security protocols, refer to section `2.3 Security Protocol` in [MESSAGING.md](./MESSAGING.md) and [AGENT.md](./AGENT.md). Automated token generation and injection via CLI is on the roadmap under task `FW-N6`.)
|
||||
> In a public broker environment or production phase, you must generate and inject a unique random `auth_token` during job registration to enable HMAC signature security. (For detailed security protocols, refer to section `2.3 Security Protocol` in [MESSAGING.md](./MESSAGING.md) and [AGENT.md](.agents/AGENT.md). Automated token generation and injection via CLI is on the roadmap under task `FW-N6`.)
|
||||
|
||||
---
|
||||
|
||||
## 3. Dependency and Virtualenv Setup
|
||||
## 4. Dependency and Virtualenv Setup
|
||||
|
||||
Set up the Python 3 dependencies required to run the orchestration and MQTT messaging backplane.
|
||||
|
||||
### Step 3.1: Build Python Virtual Environment
|
||||
### Step 4.1: Build Python Virtual Environment
|
||||
Create and activate a `.venv` virtual environment in the project root:
|
||||
|
||||
```bash
|
||||
@@ -79,7 +99,7 @@ python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
### Step 3.2: Install Dependency Packages
|
||||
### Step 4.2: Install Dependency Packages
|
||||
Install the required packages listed in `requirements.txt` under `multi-agent-mux-delegate-job`:
|
||||
|
||||
```bash
|
||||
@@ -89,7 +109,7 @@ pip install -r .agents/skills/multi-agent-mux-delegate-job/requirements.txt
|
||||
|
||||
---
|
||||
|
||||
## 4. Directory Structure and Security Audit Guide
|
||||
## 5. Directory Structure and Security Audit Guide
|
||||
|
||||
Ensure that the local registry directories required to track agent states and jobs are successfully created:
|
||||
|
||||
@@ -110,7 +130,7 @@ Ensure that the local registry directories required to track agent states and jo
|
||||
|
||||
---
|
||||
|
||||
## 5. Execution Verification and Bootstrap Tests
|
||||
## 6. Execution Verification and Bootstrap Tests
|
||||
|
||||
To verify that the environment has been successfully built without runtime errors, run the following verification checklist.
|
||||
|
||||
@@ -159,8 +179,8 @@ rm -f ".mam/jobs/$JID.json" ".mam/jobs/$JID.lock"
|
||||
|
||||
---
|
||||
|
||||
## 6. Onboarding Collaborating Agents (New Agent Onboarding)
|
||||
## 7. Onboarding Collaborating Agents (New Agent Onboarding)
|
||||
|
||||
Once the setup is verified, onboarding agents should immediately read the **[AGENT.md](./AGENT.md)** guidelines in the project root.
|
||||
Once the setup is verified, onboarding agents should immediately read the **[AGENT.md](.agents/AGENT.md)** guidelines in the .agents/ directory.
|
||||
|
||||
The guidelines describe essential workflows—such as **surgical change constraints, cross-verification review loops, and pane snapshotting to prevent viewport truncation**—allowing new agents to quickly and safely integrate with the multi-agent workflow.
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
# CLAUDE.md
|
||||
# INSTRUCTION.md
|
||||
|
||||
Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed.
|
||||
|
||||
@@ -64,4 +64,4 @@ Strong success criteria let you loop independently. Weak criteria ("make it work
|
||||
|
||||
**These guidelines are working if:** fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.
|
||||
|
||||
Read AGENT.md first before working and follow the instructions for orchestration.
|
||||
Read .agents/AGENT.md first before working and follow the instructions for orchestration.
|
||||
-368
@@ -1,368 +0,0 @@
|
||||
---
|
||||
marp: true
|
||||
theme: gaia
|
||||
_class: lead
|
||||
paginate: true
|
||||
backgroundColor: #0f172a
|
||||
color: #f8fafc
|
||||
style: |
|
||||
section {
|
||||
font-family: 'Noto Sans KR', 'Inter', sans-serif;
|
||||
padding: 40px;
|
||||
background: radial-gradient(circle at 50% 50%, #1e1b4b 0%, #0f0c29 50%, #03001e 100%);
|
||||
}
|
||||
h1 {
|
||||
color: #818cf8;
|
||||
background: linear-gradient(to right, #ffffff, #818cf8);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
h2 {
|
||||
color: #818cf8;
|
||||
border-bottom: 2px solid rgba(129, 140, 248, 0.2);
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
footer {
|
||||
color: #64748b;
|
||||
}
|
||||
a {
|
||||
color: #a78bfa;
|
||||
}
|
||||
code {
|
||||
background: #0d0e15;
|
||||
color: #a78bfa;
|
||||
}
|
||||
blockquote {
|
||||
background: rgba(129, 140, 248, 0.08);
|
||||
border-left: 4px solid #818cf8;
|
||||
padding: 12px 20px;
|
||||
margin: 10px 0;
|
||||
font-style: italic;
|
||||
color: #e2e8f0;
|
||||
}
|
||||
blockquote cite {
|
||||
display: block;
|
||||
font-size: 0.8rem;
|
||||
color: #94a3b8;
|
||||
margin-top: 5px;
|
||||
font-style: normal;
|
||||
}
|
||||
.grid-2 {
|
||||
display: grid;
|
||||
grid-template-columns: 1.2fr 1fr;
|
||||
gap: 30px;
|
||||
align-items: center;
|
||||
}
|
||||
.grid-equal {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
align-items: center;
|
||||
}
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 4px 12px;
|
||||
background: #818cf8;
|
||||
color: #0f0c29;
|
||||
font-weight: 800;
|
||||
border-radius: 50px;
|
||||
font-size: 0.8rem;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
---
|
||||
|
||||
<!-- _class: lead -->
|
||||
<!-- _paginate: false -->
|
||||
<div class="badge">세미나 발표자료</div>
|
||||
|
||||
# 멀티 에이전트란
|
||||
### 개념 설명, 장점, 실제 구축 경험 공유
|
||||
|
||||
발표 스크립트 및 발표자료 (Marp 버전)
|
||||
|
||||
---
|
||||
|
||||
## 1. AI Agent 란?
|
||||
|
||||
<div class="grid-2">
|
||||
<div>
|
||||
|
||||
- **자율 LLM 시스템**: 사용자의 단순 응답을 넘어 스스로 계획하고 도구를 사용해 목표를 완수합니다.
|
||||
- **Planning (계획)**: 작업 분해(Decomposition) 및 자기 성찰(Self-Reflection).
|
||||
- **Memory (기억)**: 컨텍스트 내 단기 기억과 벡터 DB 기반 장기 기억(RAG).
|
||||
- **Tool Use (도구)**: 코드 샌드박스, 브라우저, 외부 API 호출.
|
||||
|
||||
</div>
|
||||
<div>
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 2. AI 에이전트를 확장하는 Skills
|
||||
|
||||
- **Skills (기술 모듈)**: 에이전트가 외부 환경과 동적으로 상호작용하도록 결합하는 기능 패키지.
|
||||
- **프롬프트 그 이상**: 단순 텍스트 프롬프트를 넘어 실행용 코드, 스키마 명세, 예시(Few-shot)를 결합한 모듈입니다.
|
||||
- **동적 모듈화**: 필요한 상황에 기술을 실시간 로드 및 해제.
|
||||
- **한계 돌파**: 언어 모델의 한계를 넘어 서버 배포, 파일 제어, 데이터 수집 등의 실무 대행력을 부여합니다.
|
||||
|
||||
---
|
||||
|
||||
## 3. Claude Cowork vs Claude Code
|
||||
|
||||
<div class="grid-2">
|
||||
<div>
|
||||
|
||||
- **인프라와 목적의 차이**: 최상위 모델을 공유하지만 작동 방식이 상이함.
|
||||
- **Claude Cowork**:
|
||||
- 클라우드 샌드박스 구동
|
||||
- PPT, Excel, PDF 등 범용 사무 업무 대행
|
||||
- **Claude Code**:
|
||||
- 로컬 기기에 직접 모듈을 설치해 구동
|
||||
- 터미널 개발 코딩 작업에 최적화
|
||||
|
||||
</div>
|
||||
<div>
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 4. 대표적인 Skills 활용 사례
|
||||
|
||||
<div class="grid-equal">
|
||||
<div>
|
||||
|
||||
### 💻 개발 및 분석 (Dev Skill)
|
||||
- Grep 탐색 및 File replace
|
||||
- Linting 및 에러 자동 검증
|
||||
- Git Auto Commit & Push
|
||||
|
||||
### 🌐 브라우저 자동화 (QA Skill)
|
||||
- Playwright/Puppeteer 연동
|
||||
- 실시간 웹 데이터 크롤링
|
||||
- 웹 UI 자동 QA 시나리오 기동
|
||||
|
||||
</div>
|
||||
<div>
|
||||
|
||||
### ⚙️ DevOps 및 인프라 (DevOps)
|
||||
- Firebase Hosting 정적 파일 배포
|
||||
- Cloud DB 보안 규칙 수정
|
||||
- 클라우드 자원 모니터링 및 라우팅
|
||||
|
||||
### 🧪 학술 및 도메인 특화 (Research)
|
||||
- ChEMBL 화학물 정보 탐색
|
||||
- PubMed / arXiv 학술 조사
|
||||
- dbSNP 유전체 데이터 체크
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 5. Github 트렌드 & Understand Skill
|
||||
|
||||
- **Understand Skill**: 코드베이스나 문서를 인터랙티브 지식 그래프로 변환해 자율 분석합니다.
|
||||
|
||||
<div class="grid-equal">
|
||||
<div>
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
<div>
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 6. 개발 사례: multi-agent-mux 스킬
|
||||
|
||||
- **multi-agent-mux**: 다양한 에이전트 프레임워크와 이종 모델을 통합해 다중화 연산 처리를 수행하는 스킬.
|
||||
|
||||
<div class="grid-equal">
|
||||
<div>
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<video src="assets/images/Recording%20Jun%2025,%202026%20-%2011_56%20AM.mp4" controls autoplay muted loop width="400"></video>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 7. Multi-Agent 란?
|
||||
|
||||
- **협력 네트워크**: 단일 모델의 정보 누락과 대형 태스크 시의 환각율 한계를 극복하기 위해 에이전트들이 조율(Orchestration)하고 분업하는 구조.
|
||||
- **분할 정복 (Divide and Conquer)**: 설계, 구현, 검증 등 독립적인 태스크로 쪼개 병렬 정밀 처리 지원.
|
||||
- **역할 정의 (Role Specialization)**: 전담 역할과 도구만 쥐여주어 입력 프롬프트 노이즈를 극소화.
|
||||
- **이종 모델 융합**: 역할별 최적 모델 매치 및 교차 피드백으로 결과 신뢰도 극대화.
|
||||
|
||||
---
|
||||
|
||||
## 8. Multi Agent 예시: Subagent
|
||||
|
||||
<div class="grid-2">
|
||||
<div>
|
||||
|
||||
- **하위 작업 동적 위임**: 부모 에이전트의 오더를 받아 임시 기동, 과업 대행 후 소멸.
|
||||
- **컨텍스트 격리 (Context Isolation)**:
|
||||
- 상위 맥락 오염을 방지하기 위해 하위 리팩토링/검색 작업만 독립 분기(Branch) 처리.
|
||||
- 토큰 소모량을 파격적으로 최적화하고 병렬 속도를 개선.
|
||||
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<div style="display: flex; flex-direction: column; gap: 10px;">
|
||||
<img src="assets/images/Pasted%20image%2020260625224821.png" width="80%" />
|
||||
<img src="assets/images/Pasted%20image%2020260625224849.png" width="80%" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 9. Multi Agent 예시: Team Agent
|
||||
|
||||
<div class="grid-2">
|
||||
<div>
|
||||
|
||||
- **수평적 대화 조율 (Debate)**: 수직 계층을 넘어 독립적인 에이전트들이 의결하고 협상하는 토론형 구조.
|
||||
- **의견 충돌 및 타협 시뮬레이션**:
|
||||
- 아키텍트와 보안 에이전트가 충돌하며 최적의 절충안을 도출하는 워크플로우.
|
||||
- **비선형적 점진 고도화**: 피드백 루프를 반복해 고품질 산출물 확보.
|
||||
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<div style="display: flex; flex-direction: column; gap: 10px;">
|
||||
<img src="assets/images/Pasted%20image%2020260625230135.png" width="80%" />
|
||||
<img src="assets/images/Pasted%20image%2020260625224919.png" width="80%" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 10. Context Engineering & LangGraph
|
||||
|
||||
- **Context Engineering**: 에이전트 간의 흐름, 중앙 공유 상태(State), 조건부 루프를 설계하는 방법론.
|
||||
- **상태 보존 및 순환 제어 (Cyclic Control)**:
|
||||
- 반복 루프(반복 검증), 실패 시 롤백 등을 순환형 그래프(Cyclic Graph) 구조로 설계 관리.
|
||||
- **영속적 상태 관리**:
|
||||
- 중앙 저장소에서 상태 변화를 추적하므로 오케스트레이션 도중 실패 시 안전한 재시작 지원.
|
||||
|
||||
---
|
||||
|
||||
## 11. 오케스트레이션 소프트웨어
|
||||
|
||||
<div class="grid-2">
|
||||
<div>
|
||||
|
||||
- **CrewAI**: 역할(Role), 목표(Goal), 백스토리(Backstory) 기반의 크루(Crew) 단위 순차/계층 조율.
|
||||
- **MS AutoGen**: 에이전트 간 자율 대화(Conversational Design) 및 코드 피드백, 인간의 개입에 최적화.
|
||||
- **BeeAI**: 오픈소스 분산 에이전트 공유 배포 플랫폼.
|
||||
|
||||
</div>
|
||||
<div>
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 12. 에이전트 통신 프로토콜 표준
|
||||
|
||||
- **MCP (Model Context Protocol)**:
|
||||
- Anthropic 제안. AI 호스트와 클라이언트 간의 로컬/원격 도구 및 리소스 연결 공통 인터페이스.
|
||||
- **ACP (Agent Communication Protocol)**:
|
||||
- IBM 제안. 서비스 발견, 태스크 위임 규격 표준 (A2A로 공식 통합).
|
||||
- **A2A (Agent-to-Agent)**:
|
||||
- Google 및 50여 파트너사 공동 제안. 이종 에이전트 간 결합 코드(Glue code) 없는 통합 통신 오픈 표준.
|
||||
- **Agent Card (에이전트 카드)**:
|
||||
- 에이전트 기능 명세서 JSON 사양 (`/.well-known/agent-card.json`).
|
||||
|
||||
---
|
||||
|
||||
## 13. 실제 오케스트레이션 구현 시 문제점들
|
||||
|
||||
<div class="grid-2">
|
||||
<div>
|
||||
|
||||
- **세션 및 생명주기 관리**: 동적 subagent들의 UUID 동기화 및 세션 상태 영속성 관리의 어려움.
|
||||
- **상호 탐색과 SPOF**: 마스터-슬레이브(SPOF 위협) vs P2P(네트워크 자원 비용 급증).
|
||||
- **메시지 유실 및 Silent Death**:
|
||||
- 송/수신 주체 다운 시 비동기 알림 유실.
|
||||
- subagent 무통보 고사(Silent Death)로 인한 교착 상태(Deadlock) 및 리소스 누수.
|
||||
|
||||
</div>
|
||||
<div>
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 14. 멀티 에이전트 구축의 장점
|
||||
|
||||
- **제어 루프 아키텍처의 진화**:
|
||||
> "코딩 에이전트에 프롬프트를 더 욱여넣지 마세요. 에이전트를 자율 제어하는 루프 아키텍처를 설계하십시오."
|
||||
> <cite>- 페터 슈타인베르거 (OpenClaw 크리에이터, OpenAI)</cite>
|
||||
- **컨텍스트 설명 불필요**: 공유 작업 공간(Shared Workspaces)과 Git 공유로 불필요한 맥락 분석 토큰 소모 최소화.
|
||||
- **이종 가중치 피드백**: 상호 맹점 보완 및 Gemini Flash 등 저비용 모델 위주 분산 라우팅을 통한 비용 최적화.
|
||||
|
||||
---
|
||||
|
||||
## 15. 신뢰도 확보를 위한 기본 필수 요소
|
||||
|
||||
<div class="grid-2">
|
||||
<div>
|
||||
|
||||
- **A2A Agent Card 기반 작업 환경 공유**: 신뢰성 있는 역할/보안인증 규격 동기화.
|
||||
- **동적 작동 인프라**: 서비스 발견(Discovery)과 가상 포트 맵 라우팅 내재.
|
||||
- **이슈 & 워크플로우 트래커**: 일관성 있는 재현 및 장애 롤백 인터페이스.
|
||||
- **고성능 양방향 메시징**: 대용량 멀티모달 푸시/스트리밍 지원 (HTTP/3).
|
||||
|
||||
</div>
|
||||
<div>
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 16. 왜 gRPC가 해답인가?
|
||||
|
||||
- **A2A 호환성 및 스마트 팩토리/팜 최적화**: 디바이스 계층(MQTT)과 게이트웨이/엣지 노드(gRPC)의 하이브리드 설계에 적합.
|
||||
- **멀티모달 바이너리 스트리밍**: HTTP/2, HTTP/3 백본 기반 양방향 스트리밍 단일 포트 통합.
|
||||
- **Protobuf 직렬화 및 감시 체계**: JSON 문자열 파싱 대비 수배 빠르며, Envoy 및 Kubernetes 생명주기 검증 도구(liveness probe) 재활용 용이.
|
||||
|
||||
---
|
||||
|
||||
<!-- _class: lead -->
|
||||
<!-- _paginate: false -->
|
||||
|
||||
# 감사합니다
|
||||
### Q&A 및 토론
|
||||
@@ -79,7 +79,7 @@ Subagent는 부모 에이전트(Parent Agent 또는 Orchestrator)에 의해 동
|
||||
- **컨텍스트 격리**: 상위 에이전트의 전체 대화 컨텍스트를 오염시키지 않기 위해 하위 작업의 맥락만을 분기(Branch)하여 처리함으로써, 에이전트 실행 도중 누적되는 토큰 소모량을 최적화하고 속도를 개선합니다.
|
||||
- **예시**: 메인 에이전트가 "전체 코드 리팩토링"을 수행하면서, 특정 모듈에 대한 에러 복구 작업만을 subagent에게 위임하여 독립적으로 문제를 해결하게 하는 경우입니다.
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||
@@ -132,6 +132,7 @@ Team agent는 단일 계층적인 수직 구조를 넘어, 수평적이고 다
|
||||
- **송신/수신 주체의 돌발 종료**: A 에이전트가 작업을 위임한 후 예상치 못하게 다운되었다가 재가동(Restart)된다면, B 에이전트가 작업을 완료한 뒤 발송한 비동기식 실시간 알람이 허공으로 날아가 결국 전체 협업 루프가 끊어집니다.
|
||||
- **무한 대기 및 리소스 누수(Deadlock & Resource Leak)**: 작업을 전달받은 subagent가 아무런 예외 통보(Timeout 또는 Error Event) 없이 갑자기 고사(Silent death)하는 경우, 작업을 위임하고 대기하던 부모 에이전트는 무한 대기(Blocking) 상태에 빠져 리소스를 계속 낭비하게 되며 시스템 모니터링에서도 이를 감지하기 어렵습니다.
|
||||
|
||||
---
|
||||
# Multi Agent Orchestration 의 장점
|
||||
|
||||
1. **프롬프트의 간소화 및 루프 엔진의 진화**
|
||||
@@ -145,6 +146,8 @@ Team agent는 단일 계층적인 수직 구조를 넘어, 수평적이고 다
|
||||
4. **비용 효율적인 토큰 분배 (Cost Optimization)**
|
||||
- 모든 에이전트가 값비싼 최상위 프론티어 LLM 모델만을 사용할 필요가 없습니다. 작업의 요구 난이도와 속도, 정확성을 저울질하여 쉬운 코드 생성이나 정보 검색은 경량화된 저비용 모델(예: Gemini Flash 세대)을 탑재한 에이전트에 분산 위임하고, 고난도의 논리적 추론이 필요한 부분에만 최상위 고비용 모델을 탑재한 에이전트를 적절히 매칭함으로써 종합적인 API 비용을 효율적으로 제어할 수 있습니다.
|
||||
|
||||
---
|
||||
|
||||
# What We Need?
|
||||
성공적인 Multi-Agent Orchestration 구현을 위해 갖추어야 할 기본 필수 구성요소들입니다:
|
||||
1. **Agent 간 작업 환경 공유 (A2A의 Agent Card 기반)**
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
# AI Multi-Agents 오케스트레이션 기술 동향 및 사례
|
||||
|
||||
## 특강 내용 구성
|
||||
|
||||
* **AI 에이전트 구현을 위한 3대 핵심 엔지니어링 기법**
|
||||
* **AI 모델(LLM)과 AI 에이전트(AI Agent)의 개념 차이**: 단순 텍스트 입력을 받아 다음 단어를 예측하고 응답을 생성하는 대화형 모델(Chatbot)과 달리, 주어진 최종 목표(Goal)를 달성하기 위해 환경을 스스로 인식하고 자율적으로 행동하는 AI 에이전트의 개념.
|
||||
* **에이전트 구현을 위한 3대 핵심 엔지니어링 기법**:
|
||||
* *프롬프트 엔지니어링 (Prompt Engineering)*: 에이전트에게 명확한 역할 페르소나와 추론 가이드라인을 설계하고, 효율적인 추론 경로를 유도하기 위해 프롬프트 구조와 예시(Few-shot)를 최적화하는 기법.
|
||||
* *컨텍스트 엔지니어링 (Context Engineering)*: 대화 상태(State)와 흐름을 보존하며, 외부 데이터베이스나 기억(Memory) 장치로부터 필요한 정보를 적시에 모델의 컨텍스트 윈도우에 효율적으로 바인딩하고 공급하는 기술.
|
||||
* *하네스 엔지니어링 (Harness Engineering)*: 에이전트가 계산기, 웹 브라우저, 외부 API 및 파일 시스템 등의 물리적 도구(Tools/Tool Use)에 안전하고 유연하게 연결되어 명령을 실행할 수 있도록 연결 고리를 결합하는 도구 연동 인터페이스 기술.
|
||||
|
||||
* **AI Agent 기능 확장**: MCP & SKILL
|
||||
* **MCP, CLI 알아보기**:
|
||||
* **MCP (Model Context Protocol)**: Anthropic이 **2024년 11월** 발표한 오픈 소스 프로토콜로, AI 애플리케이션이 로컬/원격의 도구(Tools), 데이터(Resources), 프롬프트 템플릿을 통일된 규격으로 호출할 수 있는 인터페이스.
|
||||
* **CLI(Claude Code 등) 알아보기**: Claude Code처럼 터미널에서 직접 구동되어 로컬 컴퓨터의 필요한 모듈/소프트웨어를 바탕으로 작업 디렉터리에서 직접 파일을 읽고 수정 및 실행하는 코딩 에이전트의 작동 원리. (클라우드 기반 비서인 Claude Cowork와의 작동 방식 차이 비교)
|
||||
* **SKILL 추가해보기**: 단순 프롬프트 지시를 넘어, 실행 코드 + 도구 명세 스키마(Tool Schema) + 사용 예시(Few-shot)를 하나로 패키징한 자율 확장 도구 모음.
|
||||
* *개념*: 상황에 따라 필요한 기술을 동적으로 로드해 쓰고, 완료 후 반환하는 모듈화 방식.
|
||||
* *효과*: 텍스트 생성이라는 언어 모델의 한계를 넘어 파일 제어·서버 배포·물리 데이터 수집 등 실질적 업무 대행 능력을 에이전트에 부여하는 효과.
|
||||
* *대표 사례*: 코드베이스 탐색·치환·검증·커밋 자동화, 헤드리스 브라우저 기반 스크래핑, 클라우드·서버리스 배포 제어, 학술/도메인 특화 API(ChEMBL, PubMed, arXiv 등) 연동.
|
||||
|
||||
* **멀티 에이전트 협업 체계 및 오케스트레이션 인프라**: TMUX, crewAI, LangGraph
|
||||
* **싱글 에이전트의 한계**: 대규모 컨텍스트를 다룰 때 중간 정보가 누락되는(Lost in the middle) 현상과 여러 도구를 다룰 때 발생하는 환각(Hallucination) 증가. 역할 집중으로 인한 프롬프트의 장황화, 멀티태스킹 오류 및 컨텍스트 인지 한계의 문제점.
|
||||
* **멀티 에이전트란 무엇이고, 어떤 장점을 가지는가?**: 페르소나와 전문 도구를 갖춘 여러 에이전트가 협력 네트워크를 형성해 복잡한 목표를 조율·분할 해결하는 구조.
|
||||
* *역할 세분화(Role Specialization)*: 각 에이전트에 한정된 역할(PM, Developer, QA, Researcher 등)과 타겟 도구만 부여하여 노이즈를 제어하고 추론 정확도를 향상시키는 효과.
|
||||
* *교차 검증*: 서로 다른 이종 모델(Gemini 작성 → Claude 검토 등) 간의 비평 루프를 통한 맹점(Blind spot) 상호 보완 및 결과물 신뢰성 극대화.
|
||||
* **멀티 에이전트를 구성하기 위한 기술들**:
|
||||
* **TMUX**: 에이전트들의 독립적인 작업 공간을 제공하는 가상 터미널 관리 도구로, 백그라운드 내 장기 작업(Long-running task) 세션을 안정적으로 유지하고 AI Agent의 동작을 TUI로 실시간 모니터링 및 제어할 수 있도록 도와주는 소프트웨어.
|
||||
* **crewAI**: 각 에이전트에 역할(Role)·목표(Goal)·배경(Backstory)을 부여하고, 순차(Sequential) 및 계층(Hierarchical) 프로세스로 '크루(Crew)' 단위를 조율하는 역할 기반 협업 설계를 도와주는 소프트웨어 모듈.
|
||||
* **LangGraph**: 단순 선형 체인을 넘어 루프(반복 검증)·조건부 분기·실패 시 롤백을 순환형 그래프(Cyclic Graph) 구조로 관리하고, 중앙 저장소의 상태 추적 및 동기화를 통해 예외 발생 시 이전 상태부터 복구하는 상태 보존형 흐름을 제어하기 위해 사용하는 소프트웨어.
|
||||
|
||||
|
||||
* **에이전트 상호운용성 확보를 위한 표준**: ACP, A2A
|
||||
* **왜 이종 에이전트 연동 표준이 필요한가?**: 서로 다른 벤더·아키텍처로 설계된 에이전트들이 복잡한 결합 코드 없이 서비스 검색, 역할 전파 및 태스크 위임을 수행하기 위한 개방형 공통 통신 규격의 필요성.
|
||||
* **ACP(Agent Communication Protocol)**: IBM Research가 **2025년 3월** 발표한 오픈 표준으로, 서비스 발견·상호작용 협상·태스크 위임을 규격화하고 이후 **2025년 8월 27일** Linux Foundation A2A 프로토콜로 공식 통합·병합된 표준 인터페이스.
|
||||
* **A2A(Agent-to-Agent) 프로토콜**: Google과 파트너사들이 **2025년 4월** 공동 발표하고 **2025년 6월** Linux Foundation에 기증한 이종 에이전트 간 표준 메시지 교환 및 상태 추적용 통합 통신 오픈 표준.
|
||||
* **에이전트 카드(Agent Card)**: A2A 생태계에서 에이전트의 기능 명세서 역할을 하는 JSON 포맷 규격서.
|
||||
* *명세 항목*: 에이전트 명칭, 제공 역량(Capabilities), 호출 엔드포인트 정보, 보안 인증 사양(Authentication) 등.
|
||||
* *역할*: 서비스 검색 시스템(Service Discovery)이 에이전트의 역할을 실시간으로 탐색할 수 있도록 지원하는 기능.
|
||||
* *배포*: 주로 `/.well-known/agent-card.json` 경로에서 탐색 가능하도록 배포됨.
|
||||
|
||||
* **지능형 사물인터넷 (AIoT) 및 gRPC 기술 동향**
|
||||
* **AIoT 환경에서 멀티 에이전트가 유용한 이유**: 스마트 팩토리·스마트 팜 등 다수의 기기로 구성된 Physical 계층과 엣지 연산 계층이 혼재하는 환경에서 센서 데이터 수집·이상 탐지·자율 제어 등의 역할을 분산 배치하여 자원 제약 하에서도 협업 기반의 자율 제어를 달성하기 위한 연구/기술 동향.
|
||||
* **고성능 양방향 메시징(gRPC 등)의 중요성**:
|
||||
* 초경량 센서 디바이스 계층의 MQTT/CoAP 혼용 및 상위 엣지 연산 계층의 gRPC 기반 자율 통합 제어 백본 레이어 설계 기법.
|
||||
* HTTP/2 및 HTTP/3 기반으로 양방향 스트리밍, Request/Response, Pub/Sub 통신 스타일을 단일 포트에서 소화하여 대용량 멀티모달 바이너리를 고속으로 전송하는 성능.
|
||||
* Protobuf 기반의 직렬화 처리를 통한 메모리 최적화 및 Envoy, Kubernetes gRPC liveness probe 등의 인프라를 활용한 에이전트 라이프사이클 감시 체계 구축의 용이성.
|
||||
* **기술 동향 요약**: 자원 제약 환경 내 경량 프로토콜(MQTT/CoAP)과 고성능 프로토콜(gRPC)의 계층별 완충 혼용 트렌드 및 MSA 이식 확장성.
|
||||
|
||||
---
|
||||
|
||||
## 📌 특강 최종 요약 (핵심 요약)
|
||||
|
||||
### <AI Multi-Agents 오케스트레이션 기술 동향 및 사례>
|
||||
* **AI 에이전트 구현을 위한 3대 핵심 엔지니어링 기법**: 프롬프트, 컨텍스트, 하네스 엔지니어링
|
||||
* **AI Agent 기능 확장**: MCP & SKILL
|
||||
* **멀티 에이전트 협업 체계 및 오케스트레이션 인프라**: TMUX, crewAI, LangGraph
|
||||
* **에이전트 상호운용성 확보를 위한 표준**: ACP, A2A
|
||||
* **지능형 사물인터넷 (AIoT) 및 gRPC 기술 동향**
|
||||
@@ -0,0 +1,142 @@
|
||||
# Introduction
|
||||
|
||||
## Evolution of AI Models and Services
|
||||
최근 거대 언어 모델(Large Language Model, LLM)의 급격한 성장은 자연어 처리 분야를 넘어 인공지능 서비스 전반의 패러다임 변화를 주도하고 있다. 초기 생성형 AI 모델이 사용자의 입력(Prompt)에 대해 단순히 텍스트 응답을 생성하는 수동적 대화형 서비스(Chatbot)에 머물렀던 것과 달리, 현대의 AI 시스템은 스스로 목표를 인지하고 외부 환경과 상호작용하며 과업을 완수하는 자율 실행 주체, 즉 'AI 에이전트(AI Agent)'로 급격히 진화하고 있다. 이러한 진화는 단순한 언어적 출력을 넘어, 실제 운영체제(OS), 웹 브라우저, 외부 데이터베이스 및 API 시스템 제어 권한을 획득하여 능동적으로 업무를 대행하는 에이전트 기술의 대중화를 이끌고 있다.
|
||||
|
||||
## Rise of AI Agents and Industrial Paradigm Shift
|
||||
AI 에이전트의 자율적 문제 해결 능력은 산업 현장의 제어 및 운영 시스템에도 지대한 변화를 불러일으키고 있다. 특히 농업 분야의 스마트 팜(Smart Farm)이나 제조업 분야의 스마트 팩토리(Smart Factory)와 같이 실시간 센싱 데이터 수집과 기기 자율 제어가 결합된 사물지능융합기술(AIoT, Artificial Intelligence of Things) 생태계에 AI 에이전트가 도입되기 시작하였다. 기존의 AIoT 서비스는 단순 임계치 기반의 룰(Rule) 제어나 고정된 알고리즘에 의존하여 유연성이 낮았으나, 의사결정 능력을 갖춘 LLM 기반 에이전트가 연동되면서 유연한 예외 처리, 비정형 데이터 분석, 자연어 인터페이스 기반의 장치 통합 제어 등 고도화된 자율 제어 패러다임으로 전환되고 있다.
|
||||
|
||||
## Motivation and Contributions
|
||||
그러나 실질적인 AIoT 산업 현장에 AI 에이전트를 배치하기 위해서는 해결해야 할 심각한 구조적 간극이 존재한다. 첫째, 상시 센서 데이터 수집을 전제로 설계된 지속 연결(Persistent Connection) 기반의 IoT 통신 모델과 달리, 거대 언어 모델은 매 호출 시점마다 비동기적인 작업 단위(Job-based)로만 연동되는 패러다임 불일치(Execution Paradigm Mismatch)를 겪는다. 둘째, 공장이나 농장의 다양한 이기종 장치 제어 스키마 및 AI 추론 모델을 매번 수작업으로 매핑하는 것은 대규모 배포 환경에서 비현실적(Dynamic Binding Barrier)이다. 셋째, 에이전트가 상시 대기 상태로 물리 노드를 점유하는 구조는 연산 자원 및 트래픽 측면에서 극도로 비효율적(Resource Allocation Inefficiency)이다. 마지막으로, 통신 및 도구 호출 예외로 인한 자율 재시도 루프 시, 중복 명령 살포(Control Idempotency Deficit)로 인한 물리적 장치의 오작동 및 안전 사고의 위험성이 잔존한다.
|
||||
|
||||
본 논문에서는 이러한 4대 문제를 해소하기 위하여 **gRPC 기반의 통합 에이전트 인터페이스 모듈(gRPC-based Unified Interface Module)**을 설계하고 제안한다. 통신 백본으로 gRPC를 채택한 것은 다음의 세 가지 기술적 근거에 기반한다. 첫째, 초경량 센서 계층(MQTT/CoAP)과 게이트웨이·엣지 연산 계층으로 이원화된 산업용 AIoT 구조에서, gRPC는 상위 계층의 통합 제어 백본으로서 이종 에이전트 연동 의미론(A2A 계열 표준)과 자연스럽게 정합된다. 둘째, HTTP/2 멀티플렉싱 기반의 양방향 스트리밍은 단일 연결 위에서 대용량 멀티모달 데이터(이미지, 음향 주파수 등)의 저지연 상향 관측과 하향 제어를 동시에 소화하여, 지속 연결 계층과 작업 단위 에이전트 계층 간의 실행 주기 불일치를 전송 계층 수준에서 중재한다. 셋째, Protobuf의 구조화된 바이너리 직렬화와 Envoy·Kubernetes 등 검증된 클라우드 네이티브 인프라의 재사용은 대량의 에이전트 태스크 생명주기 관리와 부하 분산을 별도의 커스텀 구축 없이 실현 가능하게 한다(상세 논거는 2장에서 다룬다). 본 연구의 주요 기여는 다음과 같이 요약된다:
|
||||
- 지속 연결 기반의 AIoT 물리 계층과 비동기식 작업 단위의 에이전트 계층을 양방향 스트리밍으로 유기적으로 중재하는 gRPC 기반 백본 메커니즘을 정립하였다.
|
||||
- 장치 및 모델의 명세를 에이전트 카드(Agent Card)로 동적 바인딩하여 수작업 설정을 최소화하고, 이벤트 감지기와 에이전트 간의 비동기 제어권 위임 생명주기를 구현하여 자원 효율성을 극대화하였다.
|
||||
- 제어 명령의 멱등성 분류 및 Control Intent Key 기반 필터링 스키마를 설계하여, 에이전트 재시도 루프에서의 중복 제어 및 물리적 오작동 문제를 선제적으로 해결하였다.
|
||||
|
||||
## Paper Organization
|
||||
본 논문의 구성은 다음과 같다. 2장에서는 AIoT 구조 및 AI 에이전트, 오케스트레이션 프레임워크와 표준 프로토콜 등의 관련 연구를 소개한다. 3장에서는 제안하는 gRPC 기반 통합 인터페이스 모듈의 구조와 핵심 설계 스키마를 설명한다. 4장과 5장에서는 스마트 팜 가상 테스트베드 환경을 통한 실험 구성 및 성능 분석 결과를 제시하며, 마지막 6장에서 연구를 요약하고 향후 과제를 제시한다.
|
||||
|
||||
# Background and Related Technologies
|
||||
|
||||
## AIoT (Artificial Intelligence of Things)
|
||||
사물지능융합기술(AIoT)은 사물인터넷(IoT) 기술에 인공지능(AI)을 결합하여, 종단 기기(Edge Device)에서 발생하는 원천 데이터를 수집할 뿐 아니라 엣지 및 클라우드 컴퓨팅 인프라를 통해 고도화된 자율 추론과 제어 기능을 제공하는 융합 기술이다. 전형적인 AIoT 시스템은 (1) 말단의 센서 및 액추에이터 디바이스 계층, (2) 이들을 집선하여 로컬 연산을 처리하거나 라우팅을 조율하는 게이트웨이 계층, (3) 대용량 연산 및 통합 모니터링을 담당하는 엣지/클라우드 컴퓨팅 계층의 3-tier 아키텍처를 따른다. 물리 센서의 시계열 정합성과 실시간 장치 복구를 보장하기 위하여, 기존 AIoT 통신 계층은 주로 MQTT나 CoAP와 같은 초경량 프로토콜을 사용해 지속 연결(Persistent Connection) 상태를 유지하고 양방향으로 고속 제어 신호를 전달하는 방식을 선호한다.
|
||||
|
||||
## gRPC as an Integration Backbone in AIoT Edge Architectures
|
||||
|
||||
본 연구가 제안 모듈의 통신 백본으로 gRPC(Google Remote Procedure Call)를 선정한 기술적 근거는 다음의 세 가지로 정리된다.
|
||||
|
||||
### 계층 이원화 구조와의 아키텍처 정합성 및 에이전트 표준과의 호환성
|
||||
|
||||
스마트 팜·스마트 팩토리와 같은 산업용 AIoT 환경은 두 개의 성격이 다른 계층으로 이원화되는 것이 일반적이다. 하부의 초경량 센서 디바이스 계층은 온도·습도 측정값과 같이 작고 단순한 데이터를 주기적으로 위로 올려보내는 "단방향 데이터 전달"에 특화되어 있으며, 대역폭과 연산 자원이 극도로 제약되어 있어 MQTT/CoAP 같은 초경량 프로토콜이 최적이다. 반면 상부의 게이트웨이·엣지 연산 계층은 단순 전달을 넘어, 에이전트가 "농약을 살포하라", "출입문 상태를 확인하라"와 같이 대상과 절차가 명확한 **행동 명령을 호출하고 그 결과를 회신받는 원격 프로시저 호출(RPC)** 성격의 상호작용을 조율해야 한다. 즉 하부는 '값을 나르는 계층'이고 상부는 '행동을 시키고 확인하는 계층'이므로 요구되는 통신 모델 자체가 다르다. 따라서 말단 센서 계층에서는 기존 경량 프로토콜을 보완적으로 유지하되, 게이트웨이 이상의 상위 계층에서는 행동 명령 호출에 특화된 gRPC가 자율 통합 제어의 백본으로 기능하도록 계층별로 최적 배치하는 이원화 전략이 성립한다.
|
||||
|
||||
이때 gRPC의 인터페이스 정의 언어(IDL, Interface Definition Language)는 장치와 에이전트 사이에 교환 가능한 함수 목록과 데이터 형식을 사전에 문서로 못박아 두는 **"명확한 계약서"** 역할을 한다. 계약서에 서명한 양측이 조항을 재협상할 필요 없이 곧바로 거래할 수 있듯이, IDL에 명세된 서비스 정의만 공유되면 서로 다른 벤더가 만든 에이전트와 장치도 즉시 상호 호출이 가능하다. 특히 이 강타입 서비스·메시지 명세 체계는 이종 에이전트 간 기능 명세 교환을 규정하는 A2A 계열 표준의 Agent Card 시맨틱 — "이 에이전트는 어떤 기능을, 어떤 입력으로, 어떤 엔드포인트에서 제공하는가" — 과 문서 구조상 동형(isomorphic)에 가깝기 때문에, Agent Card의 각 항목을 전송 계층의 서비스 정의로 정합적으로 매핑할 수 있어 표준 준수형 에이전트 연동 모듈의 구현 기반으로 적합하다.
|
||||
|
||||
### 고성능 멀티모달 바이너리 스트리밍 지원
|
||||
|
||||
AIoT 기반 지능형 에이전트 시스템은 텍스트뿐 아니라 고해상도 이미지(작물 병해 사진), 음향 주파수(설비 이상음), 센서 시계열 등 대용량 멀티모달 데이터를 상시 교환해야 한다. 그러나 기존 방식은 이 요구에 구조적 한계를 보인다. MQTT는 애초에 짧은 측정값 전달을 위해 설계되어 대용량 파일 전송을 고려하지 않았으므로 큰 페이로드가 유입되면 브로커에 병목이 발생하며, REST(HTTP/1.1)는 요청할 때마다 연결을 새로 맺고 끊는 방식이라 미디어 데이터를 반복 요청할수록 연결 수립(Handshake) 오버헤드가 누적된다.
|
||||
|
||||
gRPC가 기반하는 HTTP/2의 다중화(Multiplexing)는 이 문제를 전송 원리 수준에서 해소한다. 이를 비유하면 **하나의 파이프라인(단일 TCP 연결) 안에 여러 차선(스트림)을 두어 서로 다른 데이터가 동시에 달리는 고속도로**와 같다. 한 차선에서는 에이전트의 텍스트 제어 명령이, 옆 차선에서는 카메라 이미지 스트림이, 또 다른 차선에서는 장치 상태 회신이 서로를 가로막지 않고 동시에 흐르므로, 연결을 매번 새로 맺을 필요 없이 단일 연결·단일 포트 위에서 Request/Response와 양방향 스트리밍(Bidirectional Streaming)이 저지연으로 공존한다. 또한 스트리밍 프리미티브를 조합하여 외부 브로커 없이 단일 서비스 내에서 한정적 Pub/Sub 상호작용 패턴을 구현할 수 있으나, N:N 팬아웃(Fan-out)이나 메시지 보존(Retention)이 필요한 경우에는 별도의 메시지 브로커 연계가 요구된다. 나아가 손실이 잦은 무선 엣지 네트워크 환경을 겨냥한 QUIC 기반 HTTP/3 전송 지원이 실험적 단계로 진행 중이므로, 향후 전송 계층의 고속화 진화를 수용할 확장성을 확보한다. gRPC가 이미 마이크로서비스 아키텍처(MSA)의 사실상 표준 인터페이스로 통용되고 있다는 점은 제안 모듈의 현업 시스템 이식성과 확장 속도 측면에서도 이점으로 작용한다.
|
||||
|
||||
### 구조화된 직렬화 기반의 태스크 생명주기 관리 및 부하 분산 효율
|
||||
|
||||
통신 성능은 데이터를 "어떤 형태로 포장해 보내는가"에도 크게 좌우된다. 널리 쓰이는 JSON은 사람이 읽을 수 있는 텍스트 형식이라는 장점이 있으나, 컴퓨터 입장에서는 긴 문자열을 한 글자씩 해석(파싱)하여 숫자와 구조로 복원해야 하므로 CPU 연산과 메모리를 상당히 소모하며, 필드 이름까지 문자로 반복 전송되어 페이로드도 비대해진다. 반면 gRPC가 사용하는 Protobuf(Protocol Buffers)는 송수신 양측이 스키마(필드 구조)를 미리 약속해 두고 데이터만 압축된 이진수로 변환해 전송하는 방식이므로, 문자열 해석 과정이 사라져 직렬화·역직렬화 속도가 비약적으로 빨라지고 전송 크기도 현저히 절감된다. 이는 동적으로 생성·소멸을 반복하는 다수의 서브에이전트와 오케스트레이터 사이에 폭증하는 제어 메시지를 자원 제약적 엣지 노드에서도 메모리 효율적으로 처리할 수 있게 하며, 강타입 스키마가 이종 에이전트 간 메시지 규격 불일치를 컴파일 시점에 미리 차단하므로 연동 신뢰성 또한 높아진다.
|
||||
|
||||
운영 관점의 이점도 크다. 도시가 새 건물을 지을 때 **전력망과 도로 교통 관제 체계를 처음부터 새로 건설하지 않고 기존 기반 시설에 접속해 쓰는 것**처럼, gRPC 생태계에는 Envoy 기반 L7 로드 밸런싱(트래픽을 여러 서버로 고르게 분배하는 관제소 역할)과 Kubernetes의 gRPC 네이티브 liveness probe(각 서비스의 생존 여부를 주기적으로 점검하는 상태 감시 체계) 등 이미 대규모 현장에서 검증된 클라우드 네이티브 인프라가 갖춰져 있다. 제안 모듈은 이러한 기성 인프라에 그대로 접속함으로써, 에이전트 생명주기 감시 및 부하 분산 체계를 밑단부터 구축할 필요 없이 기성 클라우드 네이티브 도구 위에서 비교적 손쉽게 확보할 수 있다는 실용적 이점을 갖는다.
|
||||
|
||||
## AI Agents and Multi-Agent Systems
|
||||
|
||||
### LLM-based AI Agent
|
||||
LLM 기반 AI 에이전트는 기계학습 모델의 단순한 예측 범위를 확장하여, 내재된 지식과 추론을 바탕으로 자율적인 행동을 조율하는 시스템을 의미한다. 일반적으로 에이전트는 거대한 문제 상황을 조각내어 실행 계획을 세우는 계획 및 추론(Planning), 과거 정보를 적시에 바인딩하는 단/장기 기억 장치(Memory), 그리고 웹 브라우징이나 API 제어 등 외부 연동을 담당하는 도구 사용(Tool Use)의 3대 요소를 통해 구현된다. 그러나 단일 에이전트(Single Agent) 구조에서는 장문의 문서나 방대한 컨텍스트를 다룰 때 중간 정보를 유실(Lost in the middle)하거나 여러 도구에 접근할수록 모델의 한계로 인해 환각(Hallucination)이 급격히 증가하며, 복잡한 비선형 흐름 제어 시 심각한 실행 실패율을 보이는 한계를 안고 있다.
|
||||
|
||||
### Multi-Agent Orchestration Frameworks
|
||||
멀티 에이전트 오케스트레이션(Multi-Agent Orchestration)은 이러한 단일 에이전트의 인지적 한계를 극복하기 위해, 특화된 페르소나와 제한된 전용 도구만 가진 여러 에이전트가 협업 네트워크를 구축하여 문제를 조율·해결하는 분할 정복(Divide-and-Conquer) 아키텍처다. 대표적으로 **crewAI**는 각 에이전트의 역할(Role), 목표(Goal), 배경 설명(Backstory)을 엄격히 분리하여 PM, 개발자, QA 등의 인간 사회 조직 구조를 단순하고 직관적으로 투영하는 데 강점을 갖는다. 한편 **LangGraph**는 상태(State) 보존 기능과 함께 제어 흐름을 순환 그래프(Cyclic Graph) 구조로 캡슐화하여, 에러나 장애 발생 시 롤백 및 체크포인터 기반 복구 등 고도화된 워크플로우 통제 능력을 강건하게 제공한다.
|
||||
|
||||
## Agent Interoperability Protocols
|
||||
서로 다른 아키텍처나 모델 벤더로 구성된 에이전트들이 공통 통신 언어로 발견 및 협업할 수 있도록 하는 표준 규격 역시 활발히 논의되고 있다. Anthropic이 주도한 **MCP(Model Context Protocol, 2024년 11월 발표)**는 AI Host 애플리케이션과 클라이언트 간에 로컬/원격 도구 및 컨텍스트 스키마를 안전하고 구조화된 프로토콜로 공유할 수 있도록 설계된 인터페이스이다. 나아가 이종 에이전트 간의 자율적 통신과 태스크 위임을 위해 IBM Research의 **ACP(2025년 3월 발표)** 규격이 제안되었으며, 이는 표준 연동 신뢰성 확보를 위해 구글 등 50여 사의 연대로 Linux Foundation에 기증된 **A2A(Agent-to-Agent, 2025년 4월 발표, 6월 LF 기증, 8월 ACP와 공식 병합)** 프로토콜 사양으로 통합되었다. A2A 표준 생태계에서는 각 에이전트의 식별과 기능 명세를 기록한 **에이전트 카드(Agent Card)**가 `/.well-known/agent-card.json` 등의 표준 엔드포인트를 통해 실시간 서빙 및 탐색을 가능케 한다.
|
||||
|
||||
## AIoT Services using AI Agents
|
||||
현재 AI 에이전트와 AIoT 계층을 결합하려는 실무적 시도들은 주로 터미널 CLI 기반 에이전트(Claude Code 등)에 물리 장치 API 제어용 SKILL을 모듈 형태로 동적 주입하여 자동 배포나 파일 입출력을 대행하게 하는 방식으로 이루어지고 있다. 그러나 이러한 적용 방식은 여전히 단일 사용자 환경 내에 국한되어 있으며, 가용 자원 및 네트워크 대역폭이 극도로 제약된 산업용 AIoT 엣지 단말 환경에서 실시간으로 대량의 시계열 센서와 물리 액추에이터 제어 명령을 양방향 스트리밍으로 유기적으로 중재하고, 이기종 간의 자원 점유 최적화 및 멱등적 장애 복구를 제공하는 멀티 에이전트 오케스트레이션 모듈 연구는 아직 초기 단계에 머물러 있어 본 연구의 필요성을 더욱 강력히 부각한다.
|
||||
|
||||
## Problem Definition
|
||||
|
||||
AIoT 서비스 환경에서 멀티 에이전트 오케스트레이션(Multi-Agent Orchestration)을 구현하고, 제안하고자 하는 통합 인터페이스 모듈의 필요성을 정당화하기 위해 해결해야 할 핵심 문제는 다음과 같이 시간, 연결, 자원, 안정성의 4대 관점으로 정의된다.
|
||||
|
||||
1. **상시 연결과 작업 단위 실행 모델 간의 패러다임 불일치 (Execution Paradigm Mismatch - 시간 축)**
|
||||
- 기존 AIoT 서비스 환경은 센서 데이터의 상시 수집 및 실시간 제어를 위해 지속적 연결(Persistent Connection) 상태를 전제로 구동되는 반면, LLM 기반 AI 에이전트는 특정 과업(Job) 단위로 호출되어 비동기적으로 실행되는 모델을 따른다. 이처럼 상시 연결 기반의 물리 기기 계층과 비동기식 작업 단위의 에이전트 계층 간에 발생하는 실행 생명주기 및 제어 주기의 불일치를 조율할 수 있는 중재 계층이 부재하다.
|
||||
|
||||
2. **연결 계층의 동적 바인딩 및 다유형 메시지 교환 장벽 (Dynamic Binding & Multi-Type Messaging Barrier - 연결 축)**
|
||||
- **동적 파라미터 바인딩 결여**: AIoT 서비스 환경에서 에이전트가 물리 장치를 제어하거나 전용 인공지능 모델(예: 스마트 팜의 질병 진단용 지도학습 모델 등)의 추론 결과를 획득하기 위해서는, 대상 시스템의 API 주소, 파라미터 규격, 제어 명령 명세를 사전에 정확히 매핑하여야 한다. 그러나 관리자가 이를 매번 수작업으로 매핑하는 것은 확장성 측면에서 비현실적이며, 새로운 에이전트가 네트워크에 진입할 때마다 물리 기기 및 추론 모델의 입력 명세를 런타임에 동적으로 연동(Binding)해주는 표준 인터페이스 계층이 요구된다.
|
||||
- **다유형 메시지 교환 규격 결여**: AIoT 환경에서 에이전트와 물리 계층 간에 교환되는 정보는 단순 텍스트 명령에 국한되지 않고, 고해상도 이미지·음향 주파수·센서 시계열·상태 구조체·상세 에러 트레이스 등 다양한 유형의 멀티모달 페이로드를 포함한다. 그러나 기존 MQTT나 단순 메시지 큐(MQ) 방식은 토픽 세분화와 페이로드 크기 한계로 인해 이러한 다유형 대용량 메시지를 통합 교환하는 데 병목을 겪으며, 단편적인 이벤트 알림 전송에 머무르는 규격의 한계가 존재한다. 또한 기존 에이전트 프레임워크(AutoGen·CrewAI 등)는 주로 텍스트 기반 메시지 교환을 전제로 설계되어, 이종 에이전트 간 다유형 멀티모달 페이로드를 규격화하여 교환할 표준 메시지 스키마 및 풍부한 페이로드 지원 메시징 인터페이스가 결여되어 있다.
|
||||
|
||||
3. **상시 점유의 비효율성과 자원 최적화 한계 (Resource Allocation Inefficiency - 자원 축)**
|
||||
- 에이전트가 모니터링 작업을 직접 수행하며 물리 노드를 지속적으로 점유하는 구조는 한정된 엣지 인프라의 네트워크 트래픽 및 연산 자원 측면에서 비효율적이다. 따라서 평상시에는 경량화된 별도 감시 프로그램에 의해 이벤트를 식별하고, 이벤트 발생 시에만 비동기적으로 특정 에이전트에게 제어권 및 태스크를 위임하고 완료 후 즉시 회수할 수 있는 표준화된 자원 통제 및 위임 인터페이스가 결여되어 있다.
|
||||
|
||||
4. **비동기 재시도 루프에서의 물리 제어 멱등성 결여 (Control Idempotency Deficit - 안정성 축)**
|
||||
- 비동기 작업 위임 구조에서 에이전트가 예외 발생 시 재시도를 수행하는 과정에서, 동일한 제어 의도가 새로운 작업 ID로 중복 발행되어 물리 장치에 대한 이중 제어가 발생할 위험이 존재한다. 예컨대 스마트 팜 환경에서 농약 중복 투포나 중복 급수와 같이 물리적·화학적 작용이 누적되는 제어 명령은 재실행 시 치명적 안전 사고로 이어질 수 있다.
|
||||
- 그러나 기존 에이전트 프레임워크의 재시도 메커니즘은 주로 정보 조회나 텍스트 생성 등 멱등성이 자연 보장되는 작업을 전제로 설계되어 있어, 부작용이 누적되는 물리 제어 명령의 멱등성(Idempotency)을 보장하는 표준화된 제어 스키마 및 중복 발행 억제 인터페이스가 결여되어 있다.
|
||||
|
||||
이 네 가지 문제(실행 주기 불일치, 동적 바인딩 및 다유형 메시지 교환, 자원 최적화 위임, 물리 제어 멱등성 결여)는 에이전트와 물리 계층의 경계면에서 공통적으로 발생하며, 에이전트의 상태, 장치 명세, 태스크 생명주기 및 제어 안정성을 유기적으로 공유할 때 비로소 제어가 가능하다. 따라서 이를 개별 기능 단위로 분리하여 접근하기보다는 하나의 단일 통합 인터페이스 모듈(Unified Interface Module)로 설계하여 처리하는 것이 통신 연동의 정합성, 전체 시스템 효율성 및 물리 제어 안정성 확보 측면에서 필수적이다.
|
||||
|
||||
# gRPC-based Unified Interface Module for Multi-Agent Orchestration in AIoT Services
|
||||
|
||||
본 논문에서는 다음과 같은 범용적인 AIoT 서비스 구조에서 사용될 수 있는 Multi-Agent 인터페이스 모듈을 설계 제안한다.
|
||||
|
||||
## Design Goals and Requirements
|
||||
- 4대 문제 정의(시간·연결·자원·안정성 축)와 설계 요구사항 간의 매핑(Traceability)
|
||||
- 통합 모듈로 설계해야 하는 이유의 아키텍처 수준 재정리
|
||||
|
||||
## Overall Architecture
|
||||
- 전체 시스템 구조도: 에이전트 계층 – 통합 인터페이스 모듈 – 물리 기기/추론 모델 계층
|
||||
- 모듈 내부 구성요소 및 상호작용 흐름 개요
|
||||
|
||||
## gRPC Communication Backbone
|
||||
- Protobuf 기반 메시지 스키마 정의 (태스크, 이벤트, 제어 명령 규격)
|
||||
- HTTP/2 기반 양방향 스트리밍을 통한 상시 연결 계층과 작업 단위 계층의 중재 (시간 축 해소)
|
||||
- Request/Response 및 스트리밍 기반 Pub/Sub 패턴 구현
|
||||
|
||||
## Agent Card-based Dynamic Binding Layer
|
||||
- 에이전트/장치/추론 모델 명세의 Agent Card 규격화 및 서빙 (`/.well-known/agent-card.json`)
|
||||
- 서비스 디스커버리 및 런타임 동적 바인딩 절차 (연결 축 해소)
|
||||
|
||||
## Event-Driven Task Delegation and Lifecycle Management
|
||||
- 경량 감시 프로그램과 에이전트의 역할 분리 구조
|
||||
- 이벤트 발생 시 태스크 위임–실행–제어권 회수의 생명주기 관리 (자원 축 해소)
|
||||
- 로드 밸런싱 및 에이전트 상태 감시 (liveness) 연동
|
||||
|
||||
## Idempotent Control Schema
|
||||
- 제어 명령의 멱등/비멱등 분류 명세
|
||||
- Control Intent Key 기반 중복 발행 억제 및 사전 검증(pre-flight check) 절차 (안정성 축 해소)
|
||||
|
||||
# Testbed Configuration
|
||||
|
||||
## Smart Farm Scenario Design
|
||||
- 방제 자동화 시나리오: 병해 감지(지도학습 모델) → 약품 추천 → 자동 투약 제어
|
||||
- 시나리오별 참여 에이전트 및 역할 정의
|
||||
|
||||
## Hardware and Network Configuration
|
||||
- 센서 디바이스/게이트웨이/엣지 노드 구성 및 네트워크 토폴로지
|
||||
- 자원 제약 조건 (대역폭, 연산 자원) 설정
|
||||
|
||||
## Software Stack and Agent Deployment
|
||||
- 에이전트 프레임워크 및 제안 모듈 구현 스택 (gRPC/Protobuf 버전 포함)
|
||||
- 비교군(Baseline) 구성: 기존 REST/MQTT 기반 연동 방식
|
||||
|
||||
## Evaluation Scenarios and Metrics
|
||||
- 평가 시나리오: 정상 경로, 재시도/장애 주입(Fault Injection), 동시 다중 위임
|
||||
- 평가 지표: 지연 시간, 처리량, 자원 점유율, 중복 제어 발생률
|
||||
|
||||
# Performance Analysis
|
||||
|
||||
## Experimental Results
|
||||
- 통신 성능 비교 (gRPC vs. Baseline): 지연 시간 및 처리량
|
||||
- 자원 효율성 분석: 이벤트 기반 위임 구조의 노드 점유율 절감 효과
|
||||
- 안정성 검증: 재시도 루프 하에서의 중복 제어 억제율 (멱등성 보장 효과)
|
||||
|
||||
## Discussion
|
||||
- 4대 문제 축별 해소 여부 검증 및 한계 분석
|
||||
- 제안 모듈의 일반화 가능성 (스마트 팩토리 등 타 도메인 확장)
|
||||
|
||||
# Conclusion and Future Work
|
||||
- 연구 요약 및 기여 재정리
|
||||
- 향후 연구: 표준 프로토콜(A2A) 연계 확장, 대규모 에이전트 스웜 환경 검증
|
||||
@@ -172,3 +172,20 @@ Team agent는 단일 계층적인 수직 구조를 넘어, 수평적이고 다
|
||||
3. **구조화된 직렬화 기반의 효율적 태스크 생명주기 및 부하 분산**
|
||||
- Protobuf(Protocol Buffers)를 통한 강력한 스키마 정의는 대량의 동적 서브에이전트들과 오케스트레이터 간에 쏟아지는 통신 메시지를 JSON 문자열 파싱 대비 수배 이상 빠르고 메모리 효율적으로 처리할 수 있게 합니다.
|
||||
- 이미 검증된 수많은 gRPC 기반 로드 밸런싱 및 이슈 트래킹 도구(예: Envoy, Kubernetes gRPC liveness probe 등)를 적극 도입함으로써, 에이전트 라이프사이클 감시 체계를 커스텀 빌드할 필요 없이 기성 인프라로 손쉽게 구축할 수 있습니다.
|
||||
|
||||
# 특강 및 실습 프로그램
|
||||
|
||||
본 세미나/특강에서는 AI 에이전트의 기본 개념부터 시작하여, 멀티 에이전트 프레임워크와 표준 프로토콜 실습까지 차근차근 다지는 입문 프로그램으로 구성되어 있습니다:
|
||||
|
||||
1. **AI 모델과 AI Agent의 차이 이해 (싱글 에이전트 기본 원리)**
|
||||
- 단순 텍스트 생성을 수행하는 AI 모델(LLM)과 스스로 계획을 세우고(Planning), 맥락을 기억하며(Memory), 외부 도구를 호출하여 행동(Tool Use)하는 자율적인 AI 에이전트의 근본적인 차이점과 에이전트 3대 요소를 이해합니다.
|
||||
2. **싱글 에이전트의 한계 극복 및 멀티 에이전트 확장**
|
||||
- 1인 에이전트 구동 시 마주하는 한계(컨텍스트 제약, 복잡도 과부하 등)를 극복하기 위해 역할을 분담하고 협업하는 멀티 에이전트 협업 체계의 필요성을 배웁니다.
|
||||
3. **멀티 에이전트 팀 구성 및 상태 흐름 제어 실습 (crewAI, LangGraph 실습)**
|
||||
- 역할 기반으로 팀을 꾸리는 crewAI 실습과, 상태 흐름도(State Graph)를 직접 그려 제어 흐름을 표현하는 LangGraph 실습을 가볍게 맛봅니다.
|
||||
4. **이종 에이전트 간 연동 프로토콜 체험 (ACP, A2A 실습)**
|
||||
- 서로 다르게 만들어진 에이전트끼리 상호 연동을 위한 규격 표준인 ACP 및 A2A 프로토콜과 에이전트 카드(Agent Card)의 작동 방식을 실습해봅니다.
|
||||
5. **AIoT 멀티 에이전트 연동 기술 동향**
|
||||
- 스마트 팩토리, 스마트 팜 등 하드웨어 자원과 네트워크가 제한된 AIoT 기기 환경에서 작동하는 멀티 에이전트 최신 기술 동향(gRPC 등)을 가볍게 소개합니다.
|
||||
|
||||
|
||||
|
||||
-1031
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
paho-mqtt>=2.0.0
|
||||
pyyaml
|
||||
Reference in New Issue
Block a user