> EulerAgent > 튜토리얼 > 기본 > 06. Dynamic 워크플로우 — 자동 태스크 분해와...

06. Dynamic 워크플로우 — 자동 태스크 분해와 페이즈 실행


학습 목표

이 튜토리얼을 마치면 다음을 할 수 있습니다:


사전 준비

euleragent new marketing-expert --template marketing-expert
cat .euleragent/config/workspace.yaml
llm_profiles:
  local:
    provider: ollama
    base_url: http://localhost:11434
    model: qwen3:32b        # tool calling 지원 모델 필요
    timeout_seconds: 120
    keep_alive: 5m
    is_external: false
  openai:
    provider: openai
    api_key: ''
    model: gpt-4o-mini
    base_url: https://api.openai.com/v1
    is_external: true
default_llm_profile: local  # --dynamic은 실제 LLM 필수 (fake 불가)

rag:
  web_search:
    provider: fake          # 테스트용 (실제: tavily | brave)
    require_approval: true

중요: --dynamic은 LLM이 system.plan_workflow 도구를 호출하여 태스크를 분해합니다. default_llm_profile: fake로는 이 기능이 작동하지 않습니다. Ollama나 OpenAI 등 실제 LLM이 필요합니다.


--dynamic이란?

일반 euleragent run은 단일 에이전틱 루프로 태스크를 처리합니다. --dynamic을 추가하면 LLM이 먼저 태스크를 여러 단계(phase)로 자동 분해한 후 각 단계를 순서대로 실행합니다.

두 단계 프로토콜

Stage 1 — 플래닝: LLM에게 system.plan_workflow 도구만 제공합니다. LLM은 태스크를 phase 목록으로 분해합니다.

{
  "workflow_title": "마케팅 전략 분석",
  "phases": [
    {"title": "리서치", "task": "경쟁사 정보 수집", "mode": "plan"},
    {"title": "분석", "task": "데이터 종합 분석", "mode": "plan"},
    {"title": "보고서 작성", "task": "최종 리포트 생성", "mode": "execute"}
  ]
}

Stage 2 — 단계별 실행: - plan 단계: web.search만 LLM에 제공 → 검색 쿼리 제안 → 승인 큐 → 워크플로우 일시 중지 - execute 단계: 수집된 결과를 컨텍스트에 주입 → 최종 결과물 생성

Phase 분류 규칙

mode 키워드 분류
execute, synthesis, write, report, draft, create, generate, produce, compile execute 단계
research, gather, collect, search, analyze plan 단계
마지막 단계에 어떤 키워드도 없으면 자동으로 execute로 승격

MCP 검색 소스와 워크플로우 페이즈: MCP 검색 프로바이더를 구성한 환경에서는 각 워크플로우 페이즈가 서로 다른 검색 소스를 활용할 수 있습니다. 예를 들어 리서치 단계에서는 학술 소스(academic)를, 분석 단계에서는 뉴스 소스(news)를 사용하도록 --source-set을 지정할 수 있습니다. SearchRouter가 페이즈별 쿼리 특성에 따라 적절한 MCP 소스로 자동 라우팅합니다.


단계별 실습

Step 1: --dynamic으로 워크플로우 시작

euleragent run marketing-expert \
  --task "LangChain, CrewAI, AutoGen 3개 AI 에이전트 프레임워크를 분석하고 EulerAI의 경쟁 전략 마케팅 보고서를 작성해줘. 웹 검색 결과 기반으로 작성해야 해." \
  --dynamic

내부 동작: 1. LLM이 system.plan_workflow를 호출하여 태스크를 단계별로 분해합니다 2. 러너가 각 단계를 분류합니다 (plan/execute) 3. WorkflowRunner가 첫 번째 단계를 실행합니다 4. web.search가 제안되면 워크플로우가 자동 일시 중지됩니다

예상 출력:

Workflow 'AI Framework Competitive Analysis' created (3 phases, run_id=a1b2c3d4e5f6)
  Phase 0: [plan] 리서치 단계 — LangChain, CrewAI, AutoGen 정보 수집
  Phase 1: [plan] 분석 단계 — 프레임워크 비교 및 EulerAI 포지셔닝
  Phase 2: [execute] 보고서 작성 — 경쟁 마케팅 전략 보고서 생성

Workflow paused at phase 0 (2 approval(s) pending).
  Use: euleragent approve accept-all --run-id a1b2c3d4e5f6 --actor "user:you" --execute
  Then: euleragent workflow resume a1b2c3d4e5f6 --execute

Step 2: 워크플로우 상태 확인

euleragent workflow show a1b2c3d4e5f6

예상 출력:

Workflow: AI Framework Competitive Analysis
  Run ID:  a1b2c3d4e5f6
  Status:  paused
  Phases:  3 total, current=0

  → [0] 리서치 단계 (plan) — paused
        Approvals pending: apv_001, apv_002
    [1] 분석 단계 (plan) — pending
    [2] 보고서 작성 (execute) — pending

JSON 형식으로 확인:

euleragent workflow show a1b2c3d4e5f6 --format json
{
  "workflow_id": "wf_x1y2z3",
  "run_id": "a1b2c3d4e5f6",
  "title": "AI Framework Competitive Analysis",
  "status": "paused",
  "current_phase": 0,
  "phases": [
    {
      "index": 0,
      "title": "리서치 단계",
      "mode": "plan",
      "status": "paused",
      "pending_approvals": ["apv_001", "apv_002"]
    },
    {
      "index": 1,
      "title": "분석 단계",
      "mode": "plan",
      "status": "pending"
    },
    {
      "index": 2,
      "title": "보고서 작성",
      "mode": "execute",
      "status": "pending"
    }
  ]
}

워크플로우 JSON 파일을 직접 확인:

cat .euleragent/runs/a1b2c3d4e5f6/workflow.json

Step 3: 첫 번째 단계 승인 및 실행

# 승인 목록 확인
euleragent approve list --run-id a1b2c3d4e5f6

예상 출력:

Pending approvals (2):

  ID        TOOL        RISK    STATUS
  apv_001   web.search  medium  pending
  apv_002   web.search  medium  pending

첫 번째 단계의 모든 승인을 수락하고 실행:

euleragent approve accept-all --run-id a1b2c3d4e5f6 --actor "user:you" --execute

예상 출력:

Accepted and executed 2 approval(s).
  [OK] web.search (apv_001) — "LangChain features 2025" → 5 results
  [OK] web.search (apv_002) — "CrewAI AutoGen comparison" → 4 results
Executed 2/2 successfully.

Step 4: 워크플로우 재개

--execute 플래그를 붙여 이후 모든 단계의 승인을 자동 처리합니다:

euleragent workflow resume a1b2c3d4e5f6 --execute

예상 출력:

Resuming workflow 'AI Framework Competitive Analysis' from phase 0...

Phase 0 (리서치 단계) — approvals resolved → marked finished.

Starting phase 1 (분석 단계, plan)...
Phase 1 paused (1 new approval(s)) — auto-executing with --execute...
  [OK] web.search (apv_003) — "EulerAI positioning AI agent market" → 3 results
Executed 1/1 — continuing...

Starting phase 2 (보고서 작성, execute)...
Phase 2 paused (1 new approval(s)) — auto-executing with --execute...
  [OK] web.search (apv_004) — "EulerAI competitive advantages" → 4 results

Phase 2 re-running with search results injected (web.search excluded)...
  LLM generating final report...

Workflow 'AI Framework Competitive Analysis' finished (run_id=a1b2c3d4e5f6).

Artifacts:
  .euleragent/runs/a1b2c3d4e5f6/artifacts/phase_0/plan.md
  .euleragent/runs/a1b2c3d4e5f6/artifacts/phase_1/plan.md
  .euleragent/runs/a1b2c3d4e5f6/artifacts/phase_2/result.md

--execute의 역할: 이후 단계에서 발생하는 새로운 승인을 자동으로 수락하고 실행합니다. --execute 없이 workflow resume을 실행하면 새 승인이 발생할 때마다 수동 처리가 필요합니다.


Step 5: 아티팩트 확인

각 단계의 출력이 별도 서브디렉토리에 저장됩니다:

ls .euleragent/runs/a1b2c3d4e5f6/artifacts/
phase_0/    phase_1/    phase_2/
# 리서치 단계 결과 (plan.md: 검색 제안 및 요약)
cat .euleragent/runs/a1b2c3d4e5f6/artifacts/phase_0/plan.md

# 분석 단계 결과 (plan.md: 비교 분석)
cat .euleragent/runs/a1b2c3d4e5f6/artifacts/phase_1/plan.md

# 최종 보고서 (result.md: 마케팅 전략 리포트)
cat .euleragent/runs/a1b2c3d4e5f6/artifacts/phase_2/result.md

전체 런 디렉토리 구조:

.euleragent/runs/a1b2c3d4e5f6/
├── input.json              # 실행 입력 (플래닝 메타데이터)
├── messages.jsonl          # 모든 단계의 LLM 대화 기록 (누적)
├── tool_calls.jsonl        # 모든 단계의 도구 호출 기록 (누적)
├── approvals.jsonl         # 모든 승인 기록 (누적)
├── workflow.json           # 최종 워크플로우 상태
├── workflow_events.jsonl   # 이벤트 로그 (created/started/paused/resumed/finished)
├── search_routing.jsonl    # 검색 라우팅 결정 기록 (MCP 사용 시)
└── artifacts/
    ├── phase_0/
    │   └── plan.md
    ├── phase_1/
    │   └── plan.md
    └── phase_2/
        └── result.md

MCP 검색 프로바이더를 사용하는 경우, search_routing.jsonl에 각 페이즈별 검색 라우팅 결정이 기록됩니다:

cat .euleragent/runs/a1b2c3d4e5f6/search_routing.jsonl
{"phase": 0, "query": "LangChain features 2025", "routed_to": "tavily", "source_set": "default", "timestamp": 1740268215.0}
{"phase": 0, "query": "CrewAI AutoGen comparison", "routed_to": "brave", "source_set": "default", "timestamp": 1740268216.0}
{"phase": 1, "query": "EulerAI positioning AI agent market", "routed_to": "tavily", "source_set": "default", "timestamp": 1740268240.0}

워크플로우 이벤트 히스토리 확인:

cat .euleragent/runs/a1b2c3d4e5f6/workflow_events.jsonl
{"type": "workflow.created", "run_id": "a1b2c3d4e5f6", "phases": 3, "timestamp": 1740268200.0}
{"type": "workflow.phase.started", "phase_id": "phase_0", "mode": "plan", "timestamp": 1740268210.0}
{"type": "workflow.paused", "phase_id": "phase_0", "approval_ids": ["apv_001", "apv_002"], "timestamp": 1740268215.0}
{"type": "workflow.resumed", "phase_id": "phase_0", "timestamp": 1740268230.0}
{"type": "workflow.phase.finished", "phase_id": "phase_0", "timestamp": 1740268231.0}
{"type": "workflow.phase.started", "phase_id": "phase_1", "mode": "plan", "timestamp": 1740268232.0}
{"type": "workflow.phase.finished", "phase_id": "phase_1", "timestamp": 1740268250.0}
{"type": "workflow.phase.started", "phase_id": "phase_2", "mode": "execute", "timestamp": 1740268251.0}
{"type": "workflow.finished", "run_id": "a1b2c3d4e5f6", "timestamp": 1740268310.0}

Step 6: REST API — euleragent serve

CI/CD 파이프라인이나 대시보드와 연동할 때 REST API를 사용합니다:

# 터미널 1: 서버 시작
euleragent serve

예상 출력:

euleragent API server running at http://localhost:8844
  WebSocket: ws://localhost:8844/ws/events
  Press Ctrl+C to stop.

터미널 2에서 API 호출:

# 런 상태 확인
curl http://localhost:8844/runs/a1b2c3d4e5f6/status
{"run_id": "a1b2c3d4e5f6", "state": "paused", "agent": "marketing-expert"}
# 워크플로우 상태 확인
curl http://localhost:8844/runs/a1b2c3d4e5f6/workflow
{
  "status": "paused",
  "current_phase": 0,
  "phases": [{"index": 0, "status": "paused"}, ...]
}
# 승인 목록 조회
curl "http://localhost:8844/approvals?run_id=a1b2c3d4e5f6"
[
  {"id": "apv_001", "tool_name": "web.search", "status": "pending"},
  {"id": "apv_002", "tool_name": "web.search", "status": "pending"}
]
# CI/CD: 모든 승인 일괄 수락 + 실행
curl -X POST \
  "http://localhost:8844/approvals/accept-all?run_id=a1b2c3d4e5f6&execute=true"
{"accepted": 2, "executed": 2, "failed": 0}

WebSocket으로 실시간 이벤트 수신:

# wscat 설치: npm install -g wscat
wscat -c ws://localhost:8844/ws/events
{"type": "server.connected", "timestamp": 1740268200.0}
{"type": "workflow.phase.started", "run_id": "a1b2c3d4e5f6", "phase_id": "phase_1"}
{"type": "workflow.paused", "run_id": "a1b2c3d4e5f6", "approval_ids": ["apv_003"]}
{"type": "workflow.finished", "run_id": "a1b2c3d4e5f6"}

--dynamic vs 일반 패턴 — 선택 가이드

상황 권장 방식
태스크가 단순하고 단계가 명확 일반 euleragent run
여러 웹 검색 후 보고서 작성 --dynamic
태스크가 복잡하고 어떻게 분해할지 불명확 --dynamic
동일한 파이프라인을 반복 실행 --task-file로 정형화
단계 간 의존성이 복잡 --dynamic
빠른 단일 작업 일반 euleragent run --mode execute
CI/CD 자동화 euleragent serve + REST API

예상 출력 요약

# 워크플로우 생성
Workflow 'AI Framework Competitive Analysis' created (3 phases, run_id=a1b2c3d4e5f6)
  Phase 0: [plan] 리서치 단계
  Phase 1: [plan] 분석 단계
  Phase 2: [execute] 보고서 작성
Workflow paused at phase 0 (2 approval(s) pending).

# 워크플로우 상태 확인
euleragent workflow show a1b2c3d4e5f6
  → [0] 리서치 단계 (plan) — paused
    [1] 분석 단계 (plan) — pending
    [2] 보고서 작성 (execute) — pending

# 승인 후 재개
euleragent workflow resume a1b2c3d4e5f6 --execute
  Phase 1 auto-executing...  [OK] web.search
  Phase 2 re-running with results injected...
Workflow finished.

자주 묻는 질문 / 흔한 오류

Q: "에이전트가 system.plan_workflow를 호출하지 않았습니다"라는 오류가 납니다.

--dynamic은 실제 LLM이 필요합니다. workspace.yaml에서 default_llm_profilefake가 아닌지 확인하세요:

default_llm_profile: local   # fake가 아닌 실제 LLM 프로필

Ollama가 실행 중이고 tool calling을 지원하는 모델이 있는지 확인합니다:

ollama list
ollama serve

Q: 모든 단계가 [plan]으로 표시되고 result.md가 생성되지 않습니다.

정상적으로 작동하는 경우 마지막 단계는 자동으로 execute로 승격됩니다. 구버전에서는 이 기능이 없을 수 있습니다. 업그레이드 후 재시도하세요:

pip install -e . --upgrade

수동으로 확인하려면:

euleragent workflow show <run-id> --format json | python3 -m json.tool
# 마지막 phase의 "mode" 확인

Q: workflow resume 후 같은 단계에서 계속 일시 중지됩니다.

남은 pending 승인이 있는지 확인하세요:

euleragent approve list --run-id a1b2c3d4e5f6

아직 pending이 있다면:

euleragent approve accept-all --run-id a1b2c3d4e5f6 --actor "user:you" --execute
euleragent workflow resume a1b2c3d4e5f6 --execute

Q: 워크플로우가 승인 단계 없이 즉시 finished가 됩니다.

이것은 이전 버전의 버그였습니다. 최신 버전에서는 plan 단계에서 web.search만 LLM에 제공하여 반드시 검색 제안을 생성하도록 합니다. 업그레이드 후 재시도하세요.

web.search가 에이전트의 tools.yaml allowlist에 있는지도 확인합니다:

cat .euleragent/agents/marketing-expert/tools.yaml
# allowlist에 web.search 포함 여부 확인

Q: euleragent serve의 기본 포트를 바꾸고 싶습니다.

euleragent serve --port 9000

그러면 http://localhost:9000으로 접근합니다.


자주 하는 실수 (순서 어긋남)

증상 원인 복구
Error: No workflow found for run 'X'. --dynamic 없이 실행한 run에 workflow resume 시도 euleragent run <agent> --task '...' --dynamic
Error: Run 'X' not found. 잘못된 run ID ls .euleragent/runs/
Pending approvals 남아 있어 resume 불가 승인 처리 미완료 euleragent approve accept-all --run-id <id> --execute

다음 단계: 07_web_rag.md — 웹 검색과 로컬 지식베이스(RAG)를 결합하는 방법을 학습합니다.

← 이전 목록으로 다음 →