06. Dynamic 워크플로우 — 자동 태스크 분해와 페이즈 실행
학습 목표
이 튜토리얼을 마치면 다음을 할 수 있습니다:
--dynamic플래그가 내부적으로 하는 일을 설명한다- 복잡한 태스크를 자동으로 단계(phase)로 분해하여 실행한다
euleragent workflow show로 워크플로우 구조와 상태를 확인한다- 단계별 승인을 처리하고
euleragent workflow resume으로 재개한다 - REST API(
euleragent serve)로 외부 시스템과 연동한다 --dynamic과 일반 패턴 중 무엇을 선택할지 판단한다
사전 준비
- 워크스페이스 초기화 완료 (
euleragent init) - 에이전트 생성:
euleragent new marketing-expert --template marketing-expert
workspace.yaml설정:
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_profile이 fake가 아닌지 확인하세요:
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)를 결합하는 방법을 학습합니다.