패턴 01. 패턴이란 무엇인가? — 개념과 아키텍처
학습 목표
이 튜토리얼을 마치면 다음을 할 수 있습니다.
- euleragent 패턴이 무엇인지, 왜 필요한지 설명할 수 있다
- YAML 패턴 파일의 핵심 구조를 이해하고 읽을 수 있다
- YAML → IR → 런타임으로 이어지는 처리 파이프라인을 설명할 수 있다
pattern list,pattern show,pattern validate,pattern compile명령을 사용할 수 있다- Node 타입 3가지(llm, judge, finalize)와 Edge 조건의 차이를 구분할 수 있다
사전 준비
docs/tutorials/basic/01_getting_started.md완료- euleragent 설치 완료 (
pip install -e .) euleragent doctor통과
# 환경 확인
euleragent --version
euleragent doctor
1. 패턴이란 무엇인가?
에이전트에게 "보고서 써줘"라고 하면 어떻게 될까요? 에이전트는 한 번에 글을 쓰고 끝낼 수도 있고, 먼저 조사하고 초안을 쓰고 검토하는 단계를 밟을 수도 있습니다. 패턴(Pattern)은 이 작업 흐름을 명시적으로 정의하는 방법입니다.
패턴은 YAML로 정의된 방향성 그래프(Directed Graph)입니다. 노드는 작업 단계이고, 엣지는 단계 간의 전환 조건입니다.
단순 에이전트 요청:
"보고서 써줘" → (에이전트가 알아서 판단) → 결과
패턴을 사용한 에이전트 요청:
"보고서 써줘" → [outline] → [gather] → [draft] → [evaluate] → [revise or finalize] → 결과
↑__________________________|
(평가 점수가 낮으면 재작성)
패턴의 핵심 가치는 다음 세 가지입니다.
예측 가능성: 에이전트가 어떤 단계를 밟는지 사전에 알 수 있습니다. 블랙박스 대신 투명한 워크플로우.
제어: 특정 단계에서 인간 승인을 강제하고, 도구 사용을 제한하고, 반복 횟수를 캡으로 묶을 수 있습니다.
재사용: 한 번 정의한 패턴은 여러 에이전트와 작업에 재사용할 수 있습니다.
2. YAML → IR → 런타임 파이프라인
패턴 YAML 파일은 실행 전에 여러 단계를 거칩니다.
┌─────────────────────────────────────────────────────────────────┐
│ 패턴 처리 파이프라인 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ my_pattern.yaml │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Stage 1 │ JSON Schema 검증 │
│ │ Schema │ - 필수 필드 존재 여부 │
│ │ Validator │ - 타입 검증 (string, int, enum) │
│ └──────┬──────┘ │
│ │ 통과 │
│ ▼ │
│ ┌─────────────┐ │
│ │ Stage 2 │ YAML 구조적 검증 │
│ │ YAML │ - 중복 노드 ID │
│ │ Structural │ - 엣지 참조 유효성 │
│ │ Validator │ - Judge 라우트 선언 │
│ └──────┬──────┘ │
│ │ 통과 │
│ ▼ │
│ ┌─────────────┐ │
│ │ Compiler │ YAML → IR (Intermediate Representation) │
│ │ │ JSON 형식의 내부 표현으로 변환 │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Stage 3 │ IR 분석 검증 │
│ │ IR │ - 진입점 유일성 │
│ │ Analyzer │ - 사이클 경계 확인 │
│ │ │ - 도달 불가 노드 │
│ │ │ - 예산 일관성 │
│ └──────┬──────┘ │
│ │ 통과 │
│ ▼ │
│ ┌─────────────┐ │
│ │ Runtime │ IR을 로드해서 에이전트 실행 │
│ │ Engine │ - 노드별 LLM 호출 │
│ │ │ - HITL pause/resume │
│ │ │ - 이벤트 스트림 │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
euleragent pattern validate 명령은 Stage 1, 2, 3을 모두 실행합니다. euleragent pattern compile 명령은 컴파일만 수행하고 IR JSON을 출력합니다.
3. 핵심 구조: 노드와 엣지
노드 타입 3가지
llm 노드: LLM을 호출해서 실제 작업을 수행합니다. 조사, 초안 작성, 코드 생성 등 "생산" 작업에 사용합니다.
nodes:
- id: draft
kind: llm
runner:
mode: execute
artifacts:
primary: draft.md
judge 노드: LLM에게 이전 단계의 결과를 평가하도록 합니다. 평가 결과에 따라 다른 노드로 라우팅합니다.
nodes:
- id: evaluate
kind: judge
judge:
schema: evaluator_v1
route_values: [finalize, revise]
finalize 노드: 그래프의 종료 지점입니다. 최종 결과물을 지정된 아티팩트 경로에 저장하고 실행을 완료합니다.
finalize:
artifact: result.md
주의:
finalize는nodes배열 안에 정의하지 않습니다. 최상위 키로 별도 선언합니다.
엣지와 when 조건
엣지는 노드 간의 전환을 정의합니다. when 필드가 전환 조건입니다.
edges:
- from: draft
to: evaluate
when: "true" # 항상 전환
- from: evaluate
to: finalize
when: "judge.route == finalize" # judge 결과가 finalize일 때
- from: evaluate
to: revise
when: "judge.route == revise" # judge 결과가 revise일 때
- from: gather
to: draft
when: "approvals_resolved" # HITL 승인이 완료됐을 때
when DSL의 4가지 형식:
| 형식 | 예시 | 사용 시점 |
|---|---|---|
"true" |
when: "true" |
항상 전환 |
"approvals_resolved" |
when: "approvals_resolved" |
HITL 승인 완료 후 |
"judge.route == <값>" |
when: "judge.route == finalize" |
Judge 라우팅 |
"judge.score >= <값>" |
when: "judge.score >= 0.9" |
Judge 점수 임계값 |
4. 진입점과 최종 노드
진입점 (Entry Node)
수신 엣지(incoming edge)가 없는 유일한 노드가 진입점이 됩니다. 패턴 실행 시 가장 먼저 실행되는 노드입니다.
진입점이 없거나 두 개 이상이면 검증 오류가 발생합니다.
올바른 예:
[outline] → [gather] → [draft] → [evaluate]
↑
진입점 (수신 엣지 없음)
잘못된 예 (진입점 없음):
[outline] ⇄ [gather] # 두 노드가 서로를 가리켜서 둘 다 수신 엣지가 있음
잘못된 예 (진입점 둘):
[outline] # 수신 엣지 없음 ← 진입점 1
[gather] → [draft] # 수신 엣지 없음 ← 진입점 2
최종 노드 (Finalize)
finalize 키에 선언한 아티팩트 경로가 최종 결과물입니다. 런타임 엔진은 finalize 노드에 도달하면 실행을 종료하고 결과물을 저장합니다.
finalize:
artifact: result.md # 최종 결과물 저장 경로
5. Judge 라우팅과 JUDGE_ROUTE_COVERAGE_ERROR
Judge 노드는 route_values에 선언한 값들 중 하나를 선택합니다. 모든 route_values에 대응하는 엣지가 반드시 있어야 합니다. 그렇지 않으면 JUDGE_ROUTE_COVERAGE_ERROR가 발생합니다.
# 올바른 예
nodes:
- id: evaluate
kind: judge
judge:
schema: evaluator_v1
route_values: [finalize, revise] # 두 개 선언
edges:
- from: evaluate
to: finalize
when: "judge.route == finalize" # finalize 커버됨
- from: evaluate
to: revise
when: "judge.route == revise" # revise 커버됨
# 잘못된 예 — JUDGE_ROUTE_COVERAGE_ERROR
nodes:
- id: evaluate
kind: judge
judge:
schema: evaluator_v1
route_values: [finalize, revise] # 두 개 선언
edges:
- from: evaluate
to: finalize
when: "judge.route == finalize" # finalize만 커버
# revise에 대한 엣지가 없음! → 에러
6. 사이클 경계와 max_iterations
패턴은 DAG(비순환 방향 그래프)가 아닙니다. 사이클(루프)을 허용합니다. 하지만 무한 루프를 방지하기 위해 사이클이 있으면 반드시 max_iterations를 선언해야 합니다.
defaults:
max_iterations: 3 # 평가-수정 루프를 최대 3회 반복
max_iterations가 없는 상태에서 사이클이 감지되면 UNBOUNDED_CYCLE 에러가 발생합니다.
7. YAML과 IR의 차이
YAML은 사람이 읽기 좋은 형식입니다. IR(Intermediate Representation)은 런타임 엔진이 읽기 좋은 형식입니다.
주요 변환 내용:
| YAML | IR |
|---|---|
| 노드 목록 + 엣지 목록 | 인접 리스트(adjacency list) 형태의 그래프 |
when: "judge.route == finalize" |
파싱된 AST 형태의 조건식 |
runner.force_tool |
HITL 플래그 포함된 실행 설정 |
judge.route_values |
유효한 라우트 집합 + 커버리지 맵 |
defaults |
각 노드에 적용된 resolved 설정 |
8. 단계별 실습
단계 1: 내장 패턴 목록 확인
euleragent pattern list
예상 출력:
Built-in Patterns
─────────────────────────────────────────────────────────
report.evidence research Evidence-based report writing
code.tdd code Test-driven development workflow
ops.triage ops Operations ticket triage
research.broad_to_narrow research Broad-to-narrow research synthesis
4 patterns available. Use 'euleragent pattern show <id>' for details.
단계 2: 패턴 구조 확인
euleragent pattern show report.evidence
예상 출력:
Pattern: report.evidence
Category: research
Version: 1
Description: Evidence-based report writing with web research and quality loop
Defaults:
max_iterations: 3
max_total_tool_calls: 30
max_web_search_calls: 10
pass_threshold: 0.85
dedupe_web_search: true
Nodes:
[entry] outline llm mode=execute
gather llm mode=plan force_tool=web.search
draft llm mode=execute
evaluate judge schema=evaluator_v1 routes=[finalize, revise]
revise llm mode=execute
[fin] finalize finalize artifact=report.md
Edges:
outline → gather when: true
gather → draft when: approvals_resolved
draft → evaluate when: true
evaluate → finalize when: judge.route == finalize
evaluate → revise when: judge.route == revise
revise → evaluate when: true
Finalize:
artifact: report.md
단계 3: 패턴 검증
euleragent pattern validate report.evidence
예상 출력:
Validating pattern: report.evidence
Stage 1 (Schema) PASS All required fields present, types valid
Stage 2 (Structural) PASS Nodes, edges, judge routes all consistent
Stage 3 (IR Analysis) PASS Entry node unique, cycles bounded, all nodes reachable
Validation complete: 0 errors, 0 warnings
JSON 형식으로 결과 받기:
euleragent pattern validate report.evidence --format json
예상 출력:
{
"pattern_id": "report.evidence",
"stages": {
"schema": { "status": "pass", "errors": [] },
"structural": { "status": "pass", "errors": [] },
"ir_analysis": { "status": "pass", "errors": [], "warnings": [] }
},
"overall": "pass",
"error_count": 0,
"warning_count": 0
}
단계 4: 패턴 컴파일
euleragent pattern compile report.evidence
예상 출력 (IR JSON):
{
"id": "report.evidence",
"version": 1,
"category": "research",
"entry_node": "outline",
"nodes": {
"outline": {
"id": "outline",
"kind": "llm",
"runner": {
"mode": "execute",
"force_tool": null,
"exclude_tools": [],
"min_proposals": null,
"max_loops": null
},
"resolved_defaults": {
"max_iterations": 3,
"max_total_tool_calls": 30
},
"outgoing_edges": ["gather"]
},
"gather": {
"id": "gather",
"kind": "llm",
"runner": {
"mode": "plan",
"force_tool": "web.search",
"hitl_required": true
},
"outgoing_edges": ["draft"],
"edge_conditions": {
"draft": { "type": "approvals_resolved" }
}
}
},
"adjacency": {
"outline": ["gather"],
"gather": ["draft"],
"draft": ["evaluate"],
"evaluate": ["finalize", "revise"],
"revise": ["evaluate"]
},
"cycles": [
{ "path": ["evaluate", "revise", "evaluate"], "bounded_by": "max_iterations" }
],
"finalize": {
"artifact": "report.md"
}
}
9. 주요 개념 설명
YAML vs 에이전트 프롬프트
패턴 없는 에이전트는 시스템 프롬프트와 태스크만으로 동작합니다. 에이전트가 스스로 어떤 단계를 밟을지 결정합니다. 이는 유연하지만 예측이 어렵습니다.
패턴을 사용하면 워크플로우가 YAML에 고정됩니다. 에이전트는 각 노드에서 정해진 역할만 수행합니다. 예측 가능성이 높아지고, 특정 단계에 HITL을 삽입하거나 예산을 제어하기 쉬워집니다.
IR이 필요한 이유
YAML은 사람이 편집하기 좋지만, 런타임이 직접 실행하기에는 적합하지 않습니다. IR은 다음을 사전에 계산해둡니다.
- 진입점 노드 식별
- 인접 리스트 (빠른 그래프 탐색)
- 조건식 파싱 (when DSL → AST)
- 사이클 감지 결과
- defaults가 각 노드에 상속된 resolved 값
런타임은 YAML을 다시 파싱하지 않고 IR을 직접 로드합니다.
evaluator_v1 스키마
judge.schema: evaluator_v1은 내장 평가 스키마입니다. 이 스키마를 사용하면 Judge 노드는 다음 구조로 LLM에게 평가를 요청합니다.
score: 0.0~1.0 품질 점수route:route_values중 하나reason: 평가 근거suggestions: 개선 제안 목록
10. 흔한 오류와 해결법
오류 1: NO_FINALIZE
ERROR [NO_FINALIZE] Pattern has no finalize section
원인: 최상위 finalize: 키가 없습니다.
해결: YAML 최상위에 finalize: 섹션을 추가합니다.
finalize:
artifact: result.md
오류 2: JUDGE_ROUTE_COVERAGE_ERROR
ERROR [JUDGE_ROUTE_COVERAGE_ERROR] Node 'evaluate': route_value 'revise' has no outgoing edge
원인: route_values에 선언한 값에 대응하는 엣지가 없습니다.
해결: 누락된 route_value에 대한 엣지를 추가합니다.
오류 3: UNBOUNDED_CYCLE
ERROR [UNBOUNDED_CYCLE] Cycle detected: evaluate → revise → evaluate. Set defaults.max_iterations.
원인: 사이클이 있는데 max_iterations가 없습니다.
해결: defaults 섹션에 max_iterations를 추가합니다.
defaults:
max_iterations: 3
오류 4: MULTIPLE_ENTRY_NODES
ERROR [MULTIPLE_ENTRY_NODES] Multiple nodes with no incoming edges: outline, gather
원인: 두 개 이상의 노드에 수신 엣지가 없습니다.
해결: 진입점이 아닌 노드에 수신 엣지를 추가하거나, 진입점을 하나로 통합합니다.
11. 다음 단계
개념을 이해했으니 실제로 내장 패턴을 실행해보겠습니다.
- 다음 튜토리얼: 02_builtin_patterns.md — 4가지 내장 패턴을 직접 실행하고 결과를 확인합니다
- 패턴 만들기: 03_simple_linear.md — 첫 번째 커스텀 패턴을 처음부터 작성합니다
- 전체 레퍼런스: 10_reference.md — 모든 필드와 에러 코드의 완전한 목록