research

ZeroClaw — Hands (Multi-Agent)

ZeroClaw — Hands (Multi-Agent)

Layer 3 학습. 실제 소스 코드 기반. 파일: src/hands/, src/tools/delegate.rs, src/tools/swarm.rs


한 줄 정의

ZeroClaw의 multi-agent 시스템은 세 레이어로 구성돼:

Hand      — 스케줄 기반으로 반복 실행되는 자율 에이전트 패키지
            (지식을 실행 간 축적, 시간이 지날수록 스마트해짐)

Delegate  — LLM 도구 호출로 서브에이전트에 작업 위임
            (단일 에이전트 → 전문화된 서브에이전트)

Swarm     — 여러 에이전트를 파이프라인/병렬/라우터로 오케스트레이션
            (다중 에이전트 협업)

Layer 1 — Hand: 자율 반복 에이전트

개념

Hand는 "스케줄에 따라 반복 실행되며 지식을 축적하는 에이전트 패키지"야. Heartbeat 워커와 비슷하지만 더 독립적이고 도메인 특화돼 있어.

market-scanner.toml    ← Hand 정의
  ↓ 실행마다
  context.json 읽기    ← 이전 실행 결과, 학습한 사실들
  ↓
  agent::run()         ← LLM + 도구 루프
  ↓
  context.json 업데이트 ← 새 발견, 학습 사실 누적

Hand 구조체

pub struct Hand {
    pub name: String,
    pub description: String,
    pub schedule: Schedule,          // Cron / Every / At
    pub prompt: String,              // 실행 계획/시스템 프롬프트
    pub knowledge: Vec<String>,      // 주입할 도메인 지식
    pub allowed_tools: Option<Vec<String>>,  // None = 전체 허용
    pub model: Option<String>,       // 모델 오버라이드
    pub active: bool,                // 기본 true
    pub max_history: usize,          // 실행 이력 최대 개수 (기본 100)
}

HandContext — 지식 누적 구조

pub struct HandContext {
    pub hand_name: String,
    pub history: Vec<HandRun>,       // 최신 먼저, max_history 개수 제한
    pub learned_facts: Vec<String>,  // 실행 간 축적된 지식 (중복 제거)
    pub last_run: Option<DateTime<Utc>>,
    pub total_runs: u64,
}

저장 위치: {workspace}/hands/{name}/context.json

HandRun — 실행 레코드

pub struct HandRun {
    pub run_id: String,
    pub started_at: DateTime<Utc>,
    pub finished_at: Option<DateTime<Utc>>,
    pub status: HandRunStatus,       // Running | Completed | Failed { error }
    pub findings: Vec<String>,       // 이번 실행에서 발견한 것들
    pub knowledge_added: Vec<String>, // 새로 학습한 사실들
    pub duration_ms: Option<u64>,
}

record_run() 호출 시:

  • Completed이면 total_runs += 1, last_run 갱신
  • knowledge_addedlearned_facts에 중복 없이 병합
  • history 앞에 삽입(최신 먼저), max_history로 트림

Hand 파일 포맷 (TOML)

name = "market-scanner"
description = "매일 아침 기술 섹터 시장 동향 스캔"
prompt = "기술 섹터 주요 뉴스를 수집하고 핵심 트렌드를 정리해."
knowledge = [
    "관심 섹터: 반도체, AI, 클라우드",
    "주요 지표: P/E 비율, 매출 성장률"
]
allowed_tools = ["web_search", "memory_store", "memory_recall"]
model = "claude-opus-4-6"
active = true
max_history = 50

[schedule]
kind = "cron"
expr = "0 9 * * 1-5"    # 평일 오전 9시
tz = "America/New_York"

Layer 2 — Delegate: 단일 서브에이전트 위임

개념

주에이전트가 delegate 도구를 호출해 특화된 서브에이전트에 작업을 넘겨. 서브에이전트는 다른 provider/model/system_prompt를 가질 수 있어.

주에이전트 (Claude Sonnet)
    │
    └─ delegate("researcher", "AI 논문 최신 동향 조사해줘")
           │
           └─ 서브에이전트 (Ollama llama3)
                  system_prompt: "You are a research assistant"
                  → LLM 응답 반환
                  (agentic=true이면 도구 루프 실행)

DelegateTool 동작

Non-agentic 모드 (기본):

provider.chat_with_system(system_prompt, full_prompt, model, temp)
→ 단일 LLM 호출 → 응답 반환
timeout_secs = 120 (기본)

Agentic 모드 (agentic=true):

parent_tools에서 allowed_tools 필터링 → sub_tools 구성
  (delegate 자신은 명시적으로 제외)
run_tool_call_loop(sub_tools) → 도구 루프 전체 실행
agentic_timeout_secs = 300 (기본)

재귀 깊이 제어

// 위임 체인의 무한 루프 방지
if self.depth >= agent_config.max_depth {
    return Err("Delegation depth limit reached")
}
// 서브에이전트의 DelegateTool은 depth + 1로 생성

config.toml 설정

[agents.researcher]
provider = "ollama"
model = "llama3"
system_prompt = "You are a research assistant."
temperature = 0.3
max_depth = 3
agentic = false
timeout_secs = 60   # 기본 120

[agents.coder]
provider = "openrouter"
model = "anthropic/claude-sonnet-4-20250514"
api_key = "sk-..."
agentic = true
allowed_tools = ["shell", "file_read", "file_write", "git_operations"]
max_iterations = 20
agentic_timeout_secs = 600   # 기본 300
skills_directory = "skills/code-review"  # 스코프 스킬 디렉토리

서브에이전트 system prompt 구성

build_enriched_system_prompt()가 자동으로 구성:

## Tools          ← 서브에이전트 사용 가능 도구 목록
## Safety         ← 안전 지침
## Skills         ← skills_directory의 스킬 (없으면 workspace/skills/)
## Workspace      ← 워크스페이스 경로
## Current Date & Time
## Shell Policy   ← shell 도구 있을 때만 추가
{operator system_prompt}   ← config의 system_prompt

Layer 3 — Swarm: 다중 에이전트 오케스트레이션

3가지 전략

Sequential (파이프라인)

에이전트 A → 에이전트 B → 에이전트 C
             ↑              ↑
     이전 출력이 다음 입력으로

실제 프롬프트:
  - Agent A: 원본 prompt
  - Agent B: "[Previous agent output]\n{A 결과}\n\n[Original task]\n{prompt}"
  - Agent C: "[Previous agent output]\n{B 결과}\n\n[Original task]\n{prompt}"

timeout: swarm.timeout_secs / agent 수 (균등 분배)

Parallel (팬아웃/팬인)

             에이전트 A ─→ 결과 A
prompt ──→   에이전트 B ─→ 결과 B   → 합쳐서 반환
             에이전트 C ─→ 결과 C

tokio::task::JoinSet으로 모두 동시 실행
timeout: swarm.timeout_secs (각 에이전트에 동일)
타임아웃/에러는 "[Error]"/"[Timed out after Ns]"로 주석처리 후 포함
→ 일부 실패해도 전체 결과는 success: true

Router (LLM 라우팅)

router LLM ─→ "researcher" 선택
                    │
prompt ─→           └─→ 에이전트 실행

라우팅 프롬프트:
  "{router_prompt}\n\nAvailable agents:\n- researcher: ...\n- writer: ...\n\n
   Respond with ONLY the agent name"

첫 번째 에이전트의 provider 사용 (라우터 LLM)
LLM 응답 agent name이 없으면 → 첫 번째 에이전트로 폴백

config.toml 설정

[swarms.research-pipeline]
agents = ["researcher", "writer"]
strategy = "sequential"
timeout_secs = 300
description = "Research then write a report"

[swarms.parallel-analysis]
agents = ["analyst-a", "analyst-b", "analyst-c"]
strategy = "parallel"
timeout_secs = 120

[swarms.smart-router]
agents = ["researcher", "coder", "writer"]
strategy = "router"
router_prompt = "Pick the most suitable agent for this task based on the request type."
timeout_secs = 180

세 레이어 비교

| | Hand | Delegate | Swarm | |--|------|----------|-------| | 트리거 | 스케줄 (Cron/Every) | LLM 도구 호출 | LLM 도구 호출 | | 에이전트 수 | 1 | 1 | N | | 지식 축적 | ✅ (context.json) | ❌ | ❌ | | 도구 루프 | ✅ | ✅ (agentic=true) | ❌ (단일 LLM 호출) | | 흐름 제어 | 시간 기반 | 깊이 제한 | Sequential/Parallel/Router | | 용도 | 반복 모니터링/리포트 | 전문화 위임 | 협업 파이프라인 |


보안

모든 multi-agent 진입점:
  security.enforce_tool_operation(ToolOperation::Act, "delegate"|"swarm")
  → ReadOnly → 차단
  → 레이트 리밋 초과 → 차단

Delegate 아닌 도구만 서브에이전트에 전달:
  parent_tools에서 "delegate" 명시적 제외
  → 무한 재귀 방지 (depth limit과 이중 방어)

timeout 검증 (config.validate()):
  timeout_secs: 1~3600
  agentic_timeout_secs: 1~3600
  0이면 에러, 3600 초과하면 에러

실제 사용 예시

# LLM이 delegate 호출
{
  "tool": "delegate",
  "args": {
    "agent": "researcher",
    "prompt": "최신 MoE 아키텍처 논문 5개 요약해줘",
    "context": "현재 DeepSeek-V3 분석 중"
  }
}

# LLM이 swarm 호출
{
  "tool": "swarm",
  "args": {
    "swarm": "research-pipeline",
    "prompt": "Neuromorphic computing 투자 트렌드 리포트 작성",
    "context": "2026년 1분기 기준"
  }
}

관련

  • daemon — Hand 스케줄러가 daemon 컴포넌트로 실행
  • cron-scheduler — Hand의 Schedule 타입 재사용
  • agent-loop — Delegate agentic 모드에서 run_tool_call_loop() 호출
  • tool-system — DelegateTool/SwarmTool이 Tool trait 구현
  • security-policy — 모든 multi-agent 진입점에서 보안 검증
  • overview — ZeroClaw 학습 지도