04. 태스크 파일, 변수 치환, 배치 리서치, 재개 실행
학습 목표
이 튜토리얼을 마치면 다음을 할 수 있습니다:
--task-file로 마크다운 파일에서 태스크를 불러온다- 좋은 태스크 파일 작성 요령을 이해한다
--var KEY=VALUE로 변수를 치환하여 태스크 파일을 재사용한다--min-proposals와--max-loops를 조합하여 배치 정보 수집을 강제한다--resume-run으로 이전 실행의 컨텍스트를 이어받아 실행한다- Plan 단계 → Execute 단계 핸드오프 패턴을 실습한다
사전 준비
- 워크스페이스 초기화 완료 (
euleragent init) - 에이전트 생성:
euleragent new research-agent --template marketing-expert
왜 태스크 파일을 쓰나?
인라인 --task "..." 방식은 짧고 단순한 태스크에 적합합니다. 하지만 다음 상황에서는 파일이 훨씬 낫습니다:
| 상황 | 인라인 태스크 | 태스크 파일 |
|---|---|---|
| 태스크가 3줄 이상 | 셸 이스케이프 지옥 | 마크다운으로 깔끔하게 |
| 같은 태스크를 반복 실행 | 매번 타이핑 | 파일 재사용 |
| 변수로 태스크 커스터마이징 | 불가능 | --var로 치환 |
| 팀에서 공유 | 어려움 | 버전 관리 가능 |
| 구조화된 요구사항 | 어려움 | 마크다운 헤딩/목록 활용 |
단계별 실습
Step 1: 첫 번째 태스크 파일 작성
research_task.md를 현재 디렉토리에 생성합니다:
# ${COMPANY} 경쟁사 분석
다음 주제에 대해 웹에서 각각 최소 2개 이상의 출처를 조사해주세요:
## 조사 항목
1. **주요 경쟁자**: ${COMPANY}와 경쟁하는 주요 AI 에이전트 프레임워크 (LangChain, CrewAI, AutoGen 등)
2. **차별화 요소**: 각 프레임워크의 핵심 기능과 강점
3. **시장 동향**: 2024-2025년 AI 에이전트 시장 성장 추세
## 출력 형식
- 각 항목에 대해 마크다운 섹션으로 구분하여 작성
- 출처 URL을 각 항목 끝에 참조 형식으로 포함
- 전체 분량: 500~800 단어
- 언어: 한국어
## 저장
결과를 `${OUTPUT_FILE}` 파일에 저장해주세요.
이 파일은 ${COMPANY}와 ${OUTPUT_FILE} 두 개의 변수를 사용합니다.
Step 2: 태스크 파일로 실행 (변수 없이)
먼저 변수 없이 실행하면 ${COMPANY}가 그대로 전달됩니다:
euleragent run research-agent \
--task-file research_task.md \
--mode plan
예상 출력:
Run a1b2c3d4e5f6 started (task-file: research_task.md)
[warn] Unresolved variables: ${COMPANY}, ${OUTPUT_FILE}
Task will include literal variable placeholders.
Step 3: --var로 변수 치환
--var KEY=VALUE 옵션으로 태스크 파일의 변수를 치환합니다. ${KEY} 형식과 {KEY} 형식 모두 지원합니다:
euleragent run research-agent \
--task-file research_task.md \
--var COMPANY=EulerAI \
--var OUTPUT_FILE=competitor_analysis.md \
--mode plan \
--max-loops 5
예상 출력:
Run b2c3d4e5f6a1 started
Task file: research_task.md
Variables:
COMPANY=EulerAI
OUTPUT_FILE=competitor_analysis.md
Resolved task:
# EulerAI 경쟁사 분석
...결과를 `competitor_analysis.md` 파일에 저장해주세요.
[loop 1/5] Generating plan...
→ Proposed: web.search (query: "EulerAI competitor AI agent framework 2025")
[loop 2/5] Continuing...
→ Proposed: web.search (query: "LangChain CrewAI AutoGen comparison 2025")
[loop 3/5] Continuing...
→ Proposed: file.write (path: competitor_analysis.md)
Run b2c3d4e5f6a1 completed (state: PENDING_APPROVAL)
3 approval(s) pending.
Step 4: 여러 변수 조합 — 재사용 가능한 태스크 파일
같은 태스크 파일을 다른 회사에 재사용합니다:
# EulerAI 분석
euleragent run research-agent \
--task-file research_task.md \
--var COMPANY=EulerAI \
--var OUTPUT_FILE=eulerAI_analysis.md \
--mode plan
# LangChain 분석 (같은 파일 재사용)
euleragent run research-agent \
--task-file research_task.md \
--var COMPANY=LangChain \
--var OUTPUT_FILE=langchain_analysis.md \
--mode plan
이렇게 하면 같은 분석 형식으로 여러 회사를 일관되게 비교할 수 있습니다.
MCP 검색 프로바이더를 구성한 경우, 각 배치 실행마다 --source-set을 다르게 지정하여 검색 소스를 분리할 수 있습니다:
# 학술 소스만 사용
euleragent run research-agent \
--task-file research_task.md \
--var COMPANY=EulerAI \
--var OUTPUT_FILE=eulerAI_academic.md \
--source-set academic \
--mode plan
# 뉴스 소스만 사용
euleragent run research-agent \
--task-file research_task.md \
--var COMPANY=EulerAI \
--var OUTPUT_FILE=eulerAI_news.md \
--source-set news \
--mode plan
Step 5: --min-proposals로 배치 정보 수집 강제
배치 리서치에서는 에이전트가 충분한 웹 검색을 제안하도록 강제합니다:
euleragent run research-agent \
--task-file research_task.md \
--var COMPANY=EulerAI \
--var OUTPUT_FILE=research_output.md \
--mode plan \
--min-proposals 5 \
--max-loops 8
--source-set옵션: MCP 검색 프로바이더를 사용하는 환경에서는--source-set옵션으로 이 배치 실행에서 사용할 검색 소스 집합을 지정할 수 있습니다. 자세한 내용은 07_web_rag.md의 "MCP 검색 프로바이더 설정" 섹션을 참조하세요.
예상 출력:
Run c3d4e5f6a1b2 started
min-proposals: 5, max-loops: 8
[loop 1/8] Generating plan... (proposals: 0/5)
→ Proposed: web.search ("EulerAI AI agent framework features")
[loop 2/8] Continuing... (proposals: 1/5)
→ Proposed: web.search ("LangChain vs EulerAI comparison 2025")
[loop 3/8] Continuing... (proposals: 2/5)
→ Proposed: web.search ("CrewAI AutoGen market share AI agents")
[loop 4/8] Continuing... (proposals: 3/5)
→ Proposed: web.search ("AI agent framework growth trends 2024 2025")
[loop 5/8] Continuing... (proposals: 4/5)
→ Proposed: web.search ("EulerAI local-first AI agent benchmark")
[loop 6/8] Continuing... (proposals: 5/5) ← min-proposals 충족!
→ Proposed: file.write (path: research_output.md)
Run c3d4e5f6a1b2 completed (state: PENDING_APPROVAL)
6 approval(s) pending.
[5x] web.search
[1x] file.write
--min-proposals 5가 없었다면 에이전트는 2~3개 검색 후 조기 종료했을 수 있습니다.
Step 6: 배치 승인 및 실행
5개의 웹 검색 제안을 한 번에 검토하고 수락합니다:
# 목록 확인
euleragent approve list --run-id c3d4e5f6a1b2
예상 출력:
Pending approvals (6):
ID TOOL RISK STATUS
apv_001 web.search medium pending
apv_002 web.search medium pending
apv_003 web.search medium pending
apv_004 web.search medium pending
apv_005 web.search medium pending
apv_006 file.write medium pending
웹 검색만 먼저 수락하고 실행합니다:
euleragent approve accept-all \
--run-id c3d4e5f6a1b2 \
--tool web.search \
--actor "user:you" \
--execute
예상 출력:
Accepted and executed 5 web.search approval(s).
[OK] apv_001 — "EulerAI AI agent framework features" → 5 results
[OK] apv_002 — "LangChain vs EulerAI comparison 2025" → 5 results
[OK] apv_003 — "CrewAI AutoGen market share AI agents" → 4 results
[OK] apv_004 — "AI agent framework growth trends 2024 2025" → 5 results
[OK] apv_005 — "EulerAI local-first AI agent benchmark" → 3 results
Executed 5/5 successfully.
1 approval(s) remain (file.write).
Step 7: --resume-run으로 이전 실행 이어받기
웹 검색 결과가 컨텍스트에 쌓인 상태에서, 이제 --resume-run으로 이전 실행을 이어받아 보고서를 작성합니다.
--resume-run이 하는 일:
- 이전 실행(run-id)의 메시지 히스토리와 도구 실행 결과를 로드합니다
- 5개의 웹 검색 결과가 이미 컨텍스트에 있는 상태에서 새 태스크를 시작합니다
euleragent run research-agent \
--task "앞서 수집한 검색 결과를 바탕으로 EulerAI 경쟁사 분석 보고서를 작성해줘. competitor_analysis.md 파일에 저장해줘." \
--resume-run c3d4e5f6a1b2 \
--mode execute \
--artifact-name analysis_report.md
예상 출력:
Run d4e5f6a1b2c3 started
Resuming context from run c3d4e5f6a1b2
Loaded: 5 web search results, 10 messages from previous run
[loop 1/5] Generating...
Context: [5 web search results injected]
→ Proposed: file.write
path: competitor_analysis.md
content: "# EulerAI 경쟁사 분석 보고서\n\n## 개요\n..."
Run d4e5f6a1b2c3 completed (state: PENDING_APPROVAL)
1 approval(s) pending.
파일 쓰기 승인 후 완료:
euleragent approve accept-all --run-id d4e5f6a1b2c3 --actor "user:you" --execute
예상 출력:
Accepted and executed 1 approval(s).
[OK] file.write — competitor_analysis.md created (2.8KB)
Run d4e5f6a1b2c3 state: RUN_FINALIZED
Artifacts:
.euleragent/runs/d4e5f6a1b2c3/artifacts/analysis_report.md
competitor_analysis.md ← 실제 파일
Step 8: 전체 리서치 파이프라인 — Plan → Execute 핸드오프
지금까지 배운 내용을 조합하여 완전한 리서치 파이프라인을 구성합니다:
Phase 1: 배치 정보 수집 (Plan 모드)
# research_task.md 파일 준비
cat > research_task.md << 'EOF'
# ${COMPANY} 시장 분석
다음을 각각 독립적으로 조사해주세요 (각 항목마다 별도 웹 검색 수행):
1. ${COMPANY}의 주요 경쟁사와 시장 포지셔닝
2. AI 에이전트 프레임워크 시장 규모와 성장률 (2023-2025)
3. ${COMPANY}의 기술적 차별화 요소
4. 주요 경쟁사들의 최신 제품 업데이트 (최근 6개월)
5. 개발자 커뮤니티 반응과 GitHub 스타 추세
EOF
euleragent run research-agent \
--task-file research_task.md \
--var COMPANY=EulerAI \
--mode plan \
--min-proposals 5 \
--max-loops 8
# 실행 ID 기록
PHASE1_RUN=c3d4e5f6a1b2 # 실제 출력에서 복사
# 웹 검색 일괄 승인 + 실행
euleragent approve accept-all \
--run-id $PHASE1_RUN \
--tool web.search \
--actor "user:you" \
--execute
Phase 2: 보고서 합성 (Execute 모드, resume)
# synthesis_task.md 파일 준비
cat > synthesis_task.md << 'EOF'
# 분석 보고서 작성 요청
앞서 수집한 ${COMPANY} 관련 시장 조사 데이터를 바탕으로:
## 보고서 구성
1. **경영진 요약** (Executive Summary, 200단어)
2. **시장 현황** (Market Overview, 400단어)
3. **경쟁사 비교표** (Competitive Matrix, 표 형식)
4. **전략적 제언** (Strategic Recommendations, 300단어)
## 형식 요구사항
- 파일명: ${OUTPUT_FILE}
- 언어: 한국어 (기술 용어는 영어 병기)
- 총 분량: 1000~1500 단어
EOF
euleragent run research-agent \
--task-file synthesis_task.md \
--var COMPANY=EulerAI \
--var OUTPUT_FILE=market_analysis_report.md \
--resume-run $PHASE1_RUN \
--mode execute \
--max-loops 3
# 파일 쓰기 승인 + 실행
PHASE2_RUN=d4e5f6a1b2c3 # 실제 출력에서 복사
euleragent approve accept-all \
--run-id $PHASE2_RUN \
--actor "user:you" \
--execute
최종 결과 확인:
cat market_analysis_report.md
좋은 태스크 파일 작성 요령
권장 구조
# [태스크 제목]
## 배경 / 컨텍스트
(에이전트가 알아야 할 배경 정보)
## 수행 항목
1. 항목 1
2. 항목 2
...
## 출력 형식
- 파일명: ${OUTPUT_FILE}
- 언어: 한국어
- 형식: 마크다운
## 제약 조건 / 주의사항
- 민감 정보 검색 금지
- 최신 정보 (2024-2025년) 우선
변수 작성 규칙
| 규칙 | 예시 | 설명 |
|---|---|---|
| 대문자 권장 | ${COMPANY} |
식별하기 쉬움 |
| 두 형식 모두 지원 | ${KEY} or {KEY} |
둘 다 치환됨 |
| 기본값 없음 | — | 미지정 시 경고 |
| 공백 포함 불가 | ${MY_COMPANY} |
언더스코어 사용 |
예상 출력 요약
# --task-file + --var 실행
Run b2c3d4... started
Variables:
COMPANY=EulerAI
OUTPUT_FILE=competitor_analysis.md
# --min-proposals 충족 시
[loop 5/8] Continuing... (proposals: 5/5) ← min-proposals 충족!
6 approval(s) pending.
# --resume-run으로 이전 컨텍스트 로드
Run d4e5f6... started
Resuming context from run c3d4e5f6...
Loaded: 5 web search results, 10 messages from previous run
자주 묻는 질문 / 흔한 오류
Q: 변수가 치환되지 않고 ${COMPANY} 그대로 나옵니다.
--var COMPANY=값 옵션을 지정했는지 확인하세요. --var 앞에 --task-file이 있어야 합니다:
# 올바른 순서
euleragent run my-agent --task-file task.md --var COMPANY=EulerAI
# 잘못된 예 (--var가 태스크 파일 앞에 올 경우 일부 셸에서 문제 발생)
euleragent run my-agent --var COMPANY=EulerAI --task-file task.md
Q: 태스크 파일에 {나 } 문자가 포함되어 있는데 변수로 인식됩니다.
JSON이나 코드 블록 안의 중괄호가 변수로 인식되는 경우, 이중 중괄호 {{ }}를 사용하면 리터럴 중괄호로 처리됩니다:
JSON 예시:
{{
"key": "value"
}}
Q: --resume-run을 사용하면 어떤 데이터가 로드되나요?
이전 런의 다음 데이터가 로드됩니다:
- messages.jsonl: 전체 LLM 대화 기록
- tool_calls.jsonl: 실행된 도구 결과 (웹 검색 결과 포함)
- artifacts/: 이전 아티팩트 목록
로드 범위는 --max-loops에 따라 컨텍스트 크기가 조정됩니다.
Q: --min-proposals와 --max-loops를 동시에 쓸 때 어떤 게 우선인가요?
--max-loops가 항상 상한선입니다. --min-proposals가 충족되기 전에 --max-loops에 도달하면 에이전트가 종료됩니다. 따라서 --min-proposals N을 쓸 때는 --max-loops를 N + 2 이상으로 설정하는 것을 권장합니다:
# min-proposals=5이면 max-loops는 7~8 정도로
euleragent run my-agent --task-file task.md \
--min-proposals 5 --max-loops 8
Q: 태스크 파일을 버전 관리하려면 어떻게 하나요?
태스크 파일은 일반 마크다운 파일이므로 Git으로 버전 관리할 수 있습니다. .euleragent/ 디렉토리는 .gitignore에 추가하고, 태스크 파일은 tasks/ 같은 별도 디렉토리에 보관하는 것을 권장합니다:
my-project/
├── .euleragent/ # gitignore 권장
├── tasks/
│ ├── research_task.md # 버전 관리
│ └── synthesis_task.md
└── .gitignore
다음 단계: 05_chat_and_memory.md — 대화 모드와 장기기억을 학습합니다.