패턴 09. 고급 패턴 — 이중 Judge, 복합 토폴로지, 배포
학습 목표
이 튜토리얼을 마치면 다음을 할 수 있습니다.
- 두 개의 독립적인 Judge 사이클을 갖는 이중 Judge 패턴을 설계할 수 있다
- 복잡한 그래프 토폴로지에서
NO_ENTRY_NODE,MULTIPLE_ENTRY_NODES,FINALIZE_UNREACHABLE에러를 예방할 수 있다 - 사이클이 교차하지 않도록 그래프를 설계할 수 있다
- 패턴 ID 네이밍 컨벤션을 이해하고 적용할 수 있다
- 완성된 패턴을 워크스페이스에 배포하고
euleragent pattern list에서 확인할 수 있다
사전 준비
04_judge_and_loop.md완료 (Judge 기본)07_multi_route.md완료 (다중 라우팅)- 복잡한 YAML 작성 경험
euleragent pattern list
euleragent agent list
1. 이중 Judge 패턴이 필요한 경우
단일 Judge는 하나의 관점에서 평가합니다. 때로는 서로 독립적인 두 가지 관점의 평가가 필요합니다.
블로그 + SEO: 콘텐츠 품질(가독성, 정보성)과 SEO 최적화(키워드, 메타데이터)는 서로 다른 전문성이 필요합니다.
코드 + 보안: 기능 정확성과 보안 취약점은 독립적으로 평가해야 합니다. 기능이 완벽해도 보안 취약점이 있으면 안 됩니다.
문서 + 법률 검토: 기술적 정확성 검토 후 별도의 법률 준수 검토가 필요한 경우.
설계 원칙: 두 Judge는 서로의 루프를 교차하지 않아야 합니다. 독립적인 체인으로 설계합니다.
2. 이중 Judge 아키텍처
직렬 이중 Judge (순차적)
첫 번째 Judge를 통과한 후 두 번째 Judge가 독립적으로 평가합니다.
[draft] → [content_judge] → content_revise → content_judge (루프)
│ (finalize 선택 시)
▼
[technical_judge] → tech_revise → technical_judge (루프)
│ (finalize 선택 시)
▼
[FINALIZE]
이 설계의 특징:
- content_judge 루프와 technical_judge 루프가 완전히 분리됨
- 콘텐츠가 충분히 좋을 때만 기술 평가로 넘어감
- 각 Judge가 독립적인 max_iterations를 가짐
3. 패턴 설계: 블로그 + SEO 이중 평가
노드 흐름도
┌─────────────────────────────────────────────────────────────────┐
│ writing.dual_judge 패턴 흐름도 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ [draft] │
│ │ 블로그 초안 작성 (llm/execute) │
│ │ when: true │
│ ▼ │
│ ─── STAGE 1: 콘텐츠 평가 사이클 ───────────────────────────── │
│ │
│ [content_judge] ── when: judge.route == content_ok ────────────┐
│ │ 콘텐츠 품질 평가 (judge/evaluator_v1) │
│ │ routes: [content_ok, content_revise] │
│ │ when: judge.route == content_revise │
│ ▼ │
│ [content_revise] │
│ │ 콘텐츠 개선 (llm/execute) │
│ └──────────────────────────► [content_judge] (최대 3회) │
│ │
│ ─── STAGE 2: SEO 평가 사이클 ───────────────────────────────── │
│ │
│ [seo_optimize] ◄────────────────────────────────────────────-──┘
│ │ SEO 최적화 (llm/execute) │
│ │ when: true │
│ ▼ │
│ [seo_judge] ── when: judge.route == seo_ok ────────────────────┐
│ │ SEO 품질 평가 (judge/evaluator_v1) │
│ │ routes: [seo_ok, seo_revise] │
│ │ when: judge.route == seo_revise │
│ ▼ │
│ [seo_revise] │
│ │ SEO 재최적화 (llm/execute) │
│ └──────────────────────────► [seo_judge] (최대 2회) │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ [FINALIZE] final_post.md 저장 │◄──┘
│ └──────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
4. YAML 작성
dual_judge_blog.yaml 파일을 생성합니다.
id: writing.dual_judge
version: 1
category: writing
description: "콘텐츠 품질 + SEO 최적화 이중 Judge 블로그 작성 패턴"
defaults:
# 이중 사이클이 있으므로 충분한 max_iterations
# content_judge 사이클 최대 3회 + seo_judge 사이클 최대 2회
# defaults.max_iterations는 전체 사이클 총합에 적용됨
max_iterations: 5
max_total_tool_calls: 30
pass_threshold: 0.85
nodes:
# ── STAGE 1: 초안 작성 ──
- id: draft
kind: llm
runner:
mode: execute
exclude_tools: [web.search, web.fetch, shell.exec]
prompt:
system_append: |
당신은 기술 블로그 작가입니다.
주제에 대한 완성된 블로그 포스트 초안을 작성하세요.
요구사항:
- 길이: 1000-1500 단어
- 구성: 도입 → 본문(3섹션) → 결론
- 코드 예시 포함
- 마크다운 포맷
artifacts:
primary: draft.md
# ── STAGE 1: 콘텐츠 평가 사이클 ──
- id: content_judge
kind: judge
judge:
schema: evaluator_v1
# content 전용 route_values — seo_judge와 독립적
route_values: [content_ok, content_revise]
prompt:
system_append: |
당신은 기술 블로그 편집장입니다.
다음 기준으로 콘텐츠 품질만 평가하세요. (SEO는 나중에 별도 평가)
평가 기준:
- 기술적 정확성 (35%): 정보가 정확하고 최신인가?
- 독자 가치 (30%): 실용적 통찰과 배움이 있는가?
- 구성과 흐름 (20%): 논리적으로 연결되는가?
- 코드 품질 (15%): 예시가 실행 가능하고 명확한가?
score >= 0.85 → 'content_ok' (SEO 단계로 진행)
score < 0.85 → 'content_revise' (콘텐츠 개선 필요)
- id: content_revise
kind: llm
runner:
mode: execute
exclude_tools: [web.search, web.fetch, shell.exec]
prompt:
system_append: |
편집장의 콘텐츠 피드백을 반영하여 블로그 포스트를 개선하세요.
SEO는 아직 고려하지 마세요. 콘텐츠 품질에만 집중하세요.
artifacts:
primary: draft.md
# ── STAGE 2: SEO 최적화 + 평가 사이클 ──
- id: seo_optimize
kind: llm
runner:
mode: execute
exclude_tools: [web.search, web.fetch, shell.exec]
prompt:
system_append: |
콘텐츠 품질 평가를 통과한 포스트를 SEO 관점에서 최적화하세요.
SEO 최적화 항목:
1. 제목 태그: H1에 주요 키워드 포함 (60자 이내)
2. 메타 설명: 150-160자, 키워드 포함, 클릭 유도
3. 헤딩 구조: H1→H2→H3 계층 구조
4. 키워드 밀도: 1-2% (과다 사용 금지)
5. 내부 링크 제안: [관련 포스트: ...] 형식
6. 이미지 alt 텍스트 제안 (이미지가 있는 경우)
7. 포스트 하단에 SEO 메타데이터 섹션 추가:
```
<!-- SEO
title: ...
description: ...
keywords: ...
-->
```
artifacts:
primary: seo_draft.md
- id: seo_judge
kind: judge
judge:
schema: evaluator_v1
# seo_judge 전용 route_values — content_judge와 독립적
route_values: [seo_ok, seo_revise]
prompt:
system_append: |
당신은 SEO 전문가입니다.
콘텐츠 품질은 이미 검증됐습니다. SEO 측면만 평가하세요.
평가 기준:
- 키워드 최적화 (30%): 자연스럽게 키워드가 포함됐는가?
- 제목 최적화 (25%): 클릭률을 높이는 제목인가?
- 메타 설명 (20%): 검색 결과에서 클릭을 유도하는가?
- 구조 최적화 (25%): 헤딩 계층, 가독성이 검색 엔진 친화적인가?
score >= 0.80 → 'seo_ok' (최종 발행 준비 완료)
score < 0.80 → 'seo_revise' (SEO 재최적화 필요)
- id: seo_revise
kind: llm
runner:
mode: execute
exclude_tools: [web.search, web.fetch, shell.exec]
prompt:
system_append: |
SEO 전문가의 피드백을 반영하여 SEO를 재최적화하세요.
콘텐츠 내용은 변경하지 마세요. SEO 요소만 개선하세요.
artifacts:
primary: seo_draft.md
edges:
# Stage 1: 초안 → 콘텐츠 평가
- from: draft
to: content_judge
when: "true"
# Stage 1: 콘텐츠 사이클
- from: content_judge
to: seo_optimize # content_ok → Stage 2로 진행
when: "judge.route == content_ok"
- from: content_judge
to: content_revise # content_revise → 콘텐츠 개선
when: "judge.route == content_revise"
- from: content_revise
to: content_judge # 콘텐츠 재평가
when: "true"
# Stage 2: SEO 사이클
- from: seo_optimize
to: seo_judge
when: "true"
- from: seo_judge
to: finalize # seo_ok → 최종 완료
when: "judge.route == seo_ok"
- from: seo_judge
to: seo_revise # seo_revise → SEO 재최적화
when: "judge.route == seo_revise"
- from: seo_revise
to: seo_judge # SEO 재평가
when: "true"
finalize:
artifact: seo_draft.md
5. 검증
euleragent pattern validate dual_judge_blog.yaml
예상 출력:
Validating pattern: dual_judge_blog.yaml
Stage 1 (Schema) PASS
Stage 2 (Structural) PASS
Judge node 'content_judge':
content_ok → seo_optimize ✓
content_revise → content_revise ✓
Judge node 'seo_judge':
seo_ok → finalize ✓
seo_revise → seo_revise ✓
All route_values covered ✓
Stage 3 (IR Analysis) PASS
Cycles detected:
1. content_judge → content_revise → content_judge
bounded_by: max_iterations=5 ✓
2. seo_judge → seo_revise → seo_judge
bounded_by: max_iterations=5 ✓
Cycles are independent (no intersection) ✓
Entry node: draft ✓
All paths reach finalize ✓
Validation complete: 0 errors, 0 warnings
6. 복잡한 토폴로지 설계 시 주의사항
주의사항 1: Entry Node 유일성
진입 노드(수신 엣지 없는 노드)는 반드시 하나여야 합니다.
# 잘못된 예 — MULTIPLE_ENTRY_NODES
nodes:
- id: draft # 수신 엣지 없음 ← 진입점 1
- id: outline # 수신 엣지 없음 ← 진입점 2
edges:
- from: draft
to: content_judge
when: "true"
# outline에서 content_judge로 가는 엣지도 있다면?
# → MULTIPLE_ENTRY_NODES
euleragent pattern validate broken_multi_entry.yaml
ERROR [MULTIPLE_ENTRY_NODES]
Nodes with no incoming edges: draft, outline
Only one entry node is allowed.
Fix: Connect one of these nodes with an incoming edge,
or merge them into a single entry node.
주의사항 2: 사이클 교차 방지
두 사이클이 노드를 공유하면 복잡한 동작이 발생합니다.
# 잘못된 예 — 사이클 교차
content_judge → content_revise → content_judge (사이클 1)
seo_judge → content_revise → seo_judge (사이클 2)
↑ content_revise가 두 사이클에 속함!
이런 설계는 max_iterations 카운팅이 복잡해지고, 어느 Judge가 revise를 요청했는지 컨텍스트가 섞입니다. 사이클은 독립적으로 유지하세요.
주의사항 3: FINALIZE_UNREACHABLE
모든 경로가 finalize에 도달해야 합니다.
# 컴파일 출력에서 모든 경로 확인
euleragent pattern compile dual_judge_blog.yaml | \
python3 -c "
import json, sys
d = json.load(sys.stdin)
for route, info in d.get('route_coverage', {}).items():
print(f'{route}: reaches_finalize={info[\"reaches_finalize\"]}')
"
주의사항 4: 도달 불가 노드
정의됐지만 어떤 엣지에도 연결되지 않은 노드:
WARNING [UNREACHABLE_NODE]
Node 'orphan_node' is defined but has no incoming edges
and is not the entry node. It will never be executed.
이는 WARNING이고 ERROR가 아닙니다. 검증을 통과하지만 실행되지 않습니다.
7. 실행 예시
cp dual_judge_blog.yaml .euleragent/patterns/
euleragent pattern run writing.dual_judge my-agent \
--task "쿠버네티스 Horizontal Pod Autoscaler(HPA) 완전 가이드" \
--project default \
--stream
스트리밍 출력:
[event] pattern.start {pattern: writing.dual_judge}
[event] node.start {node: draft}
[event] node.complete {node: draft, words: 1234}
[event] node.start {node: content_judge, iteration: 1}
[event] node.complete {node: content_judge, score: 0.79, route: content_revise}
[event] node.start {node: content_revise, iteration: 1}
[event] node.complete {node: content_revise}
[event] node.start {node: content_judge, iteration: 2}
[event] node.complete {node: content_judge, score: 0.88, route: content_ok}
[event] edge.traverse {from: content_judge, to: seo_optimize, reason: "judge.route == content_ok"}
[event] node.start {node: seo_optimize}
[event] node.complete {node: seo_optimize}
[event] node.start {node: seo_judge, iteration: 1}
[event] node.complete {node: seo_judge, score: 0.91, route: seo_ok}
[event] edge.traverse {from: seo_judge, to: finalize, reason: "judge.route == seo_ok"}
[event] pattern.complete {artifact: seo_draft.md}
최종 요약:
[run:n5e8j2k6] Pattern: writing.dual_judge
✓ draft Completed (13s) — 1,234 words
✓ content_judge Completed (7s) — score: 0.79 → content_revise (iteration 1)
✓ content_revise Completed (15s) — improved
✓ content_judge Completed (6s) — score: 0.88 → content_ok (iteration 2)
✓ seo_optimize Completed (9s)
✓ seo_judge Completed (5s) — score: 0.91 → seo_ok (iteration 1)
✓ finalize Completed
Iterations: content_judge×2, seo_judge×1
Artifact: .euleragent/runs/n5e8j2k6/artifacts/seo_draft.md
8. ID 네이밍 컨벤션
패턴 ID는 카테고리.설명자 형식을 권장합니다.
내장 패턴 예시 분석
| 패턴 ID | 카테고리 | 설명자 | 의미 |
|---|---|---|---|
report.evidence |
report | evidence | 증거 기반 보고서 |
code.tdd |
code | tdd | 테스트 주도 개발 |
ops.triage |
ops | triage | 운영 트리아지 |
research.broad_to_narrow |
research | broad_to_narrow | 광범위→심층 연구 |
권장 카테고리
| 카테고리 | 설명 | 예시 |
|---|---|---|
report |
보고서 작성 | report.evidence, report.competitor |
code |
코드 관련 | code.tdd, code.pr_review, code.refactor |
ops |
운영/인프라 | ops.triage, ops.incident, ops.deploy |
research |
조사/분석 | research.broad_to_narrow, research.lit_review |
writing |
문서/콘텐츠 | writing.blog, writing.newsletter |
security |
보안 | security.audit, security.threat_model |
docs |
내부 문서 | docs.faq_update, docs.onboarding |
잘못된 ID 형식
id: myPattern # 카멜케이스 — 비권장
id: my_pattern # 언더스코어 — 비권장
id: MY.PATTERN # 대문자 — 비권장
id: my.complex.pattern.v2 # 3단계 이상 — 비권장
id: blog # 카테고리 없음 — 비권장
id: writing.dual_judge # 권장
id: ops.internal_triage # 권장
id: code.security_audit # 권장
9. 워크스페이스 배포
완성된 패턴을 워크스페이스에 배포합니다.
# 패턴 디렉토리 확인
ls .euleragent/patterns/
# 배포
cp dual_judge_blog.yaml .euleragent/patterns/dual_judge_blog.yaml
# 배포 확인
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
Workspace Patterns (.euleragent/patterns/)
─────────────────────────────────────────────────────────
blog.quality_loop writing Judge 노드로 품질 루프를 갖춘 블로그 작성 패턴
blog.web_research writing 웹 검색 + Judge 루프가 결합된 고품질 블로그 작성 패턴
code.pr_review code PR 코드 리뷰 — 승인/수정요청/거절 3-way Judge 라우팅
docs.faq_update docs 사내 FAQ 업데이트 — 에어갭, 인간 검토 게이트
my_first.pattern writing 주제를 입력받아 기술 블로그 포스트를 작성하는 단순 선형 패턴
ops.internal_triage ops 에어갭 고객 지원 트리아지
ops.policy_compliant ops 내부 정책 로드 → 분류 → 초안 → 정책 준수 검토 게이트
writing.dual_judge writing 콘텐츠 품질 + SEO 최적화 이중 Judge 블로그 작성 패턴
writing.human_review writing 초안 작성 후 인간 검토 게이트를 통과하는 문서 작성 패턴
13 patterns available (4 built-in + 9 workspace).
배포된 패턴은 ID로 직접 참조 가능합니다:
euleragent pattern validate writing.dual_judge
euleragent pattern show writing.dual_judge
euleragent pattern run writing.dual_judge my-agent --task "..."
10. 실습 과제: 블로그 작성 + SEO 이중 평가 변형
과제: 글로벌 배포 패턴
한국어 원문을 작성하고, 번역 품질과 SEO를 각각 별도로 평가하는 패턴.
[draft_korean] → [translation] → [translation_quality_judge]
│ ok → [seo_optimize_english]
│ → [seo_judge] → finalize
│ revise → [retranslate] → [translation_quality_judge]
id: writing.global_publish
version: 1
category: writing
description: "한국어 원문 작성 → 영어 번역 품질 평가 → SEO 최적화 이중 Judge 패턴"
defaults:
max_iterations: 4
max_total_tool_calls: 25
nodes:
- id: draft_korean
kind: llm
runner:
mode: execute
exclude_tools: [web.search, web.fetch, shell.exec]
prompt:
system_append: |
한국어로 완성된 기술 블로그 포스트를 작성하세요.
길이: 800-1000 단어. 전문적이고 명확한 문체.
- id: translation
kind: llm
runner:
mode: execute
exclude_tools: [web.search, web.fetch, shell.exec]
prompt:
system_append: |
한국어 블로그 포스트를 영어로 번역하세요.
직역 대신 자연스러운 영어 표현을 사용하세요.
기술 용어는 영어 원어를 사용하세요.
- id: translation_quality_judge
kind: judge
judge:
schema: evaluator_v1
route_values: [translation_ok, retranslate]
prompt:
system_append: |
영어 번역의 품질을 평가하세요.
- 자연스러운 영어 표현 (40%)
- 기술적 정확성 유지 (35%)
- 문화적 적절성 (25%)
score >= 0.82 → 'translation_ok'
score < 0.82 → 'retranslate'
- id: retranslate
kind: llm
runner:
mode: execute
exclude_tools: [web.search, web.fetch, shell.exec]
prompt:
system_append: |
번역 품질 피드백을 반영하여 영어 번역을 개선하세요.
원문의 의미를 유지하면서 더 자연스러운 영어로 수정하세요.
- id: seo_optimize_english
kind: llm
runner:
mode: execute
exclude_tools: [web.search, web.fetch, shell.exec]
prompt:
system_append: |
영어 번역 포스트를 영어권 독자를 위한 SEO로 최적화하세요.
영어 SEO 키워드 전략을 적용하세요.
- id: seo_judge
kind: judge
judge:
schema: evaluator_v1
route_values: [seo_ok, seo_revise]
prompt:
system_append: |
영어권 SEO 기준으로 평가하세요.
score >= 0.78 → 'seo_ok'
score < 0.78 → 'seo_revise'
- id: seo_revise
kind: llm
runner:
mode: execute
exclude_tools: [web.search, web.fetch, shell.exec]
prompt:
system_append: SEO 피드백 반영. 콘텐츠 내용 변경 금지.
edges:
- from: draft_korean
to: translation
when: "true"
- from: translation
to: translation_quality_judge
when: "true"
- from: translation_quality_judge
to: seo_optimize_english
when: "judge.route == translation_ok"
- from: translation_quality_judge
to: retranslate
when: "judge.route == retranslate"
- from: retranslate
to: translation_quality_judge
when: "true"
- from: seo_optimize_english
to: seo_judge
when: "true"
- from: seo_judge
to: finalize
when: "judge.route == seo_ok"
- from: seo_judge
to: seo_revise
when: "judge.route == seo_revise"
- from: seo_revise
to: seo_judge
when: "true"
finalize:
artifact: final_english_post.md
검증:
euleragent pattern validate writing_global_publish.yaml
두 Judge의 route_values가 모두 커버됐는지 확인하세요.
11. 흔한 오류와 해결법
오류 1: 사이클 내에서 max_iterations 공유
이중 Judge 패턴에서 두 사이클이 모두 defaults.max_iterations를 공유합니다. max_iterations: 3으로 설정했다면 content_judge가 3회, seo_judge가 1회를 사용할 수도 있습니다. 충분히 큰 값을 설정하세요.
오류 2: JUDGE_DEAD_END in complex topology
복잡한 그래프에서 특정 Judge 라우트가 finalize에 도달하지 못하는 경우:
euleragent pattern validate complex_pattern.yaml --format json | \
python3 -c "
import json, sys
result = json.load(sys.stdin)
for err in result['stages']['ir_analysis']['errors']:
print(err['code'], err['message'])
"
오류 3: 메모리 컨텍스트 혼재
이중 Judge 패턴에서 stage 2(SEO) Judge가 stage 1(content)의 평가 컨텍스트를 받으면 평가가 혼재될 수 있습니다. system_append에서 "SEO만 평가하세요"처럼 명확히 제한하세요.
다음 단계
고급 패턴 설계를 완성했습니다. 이제 모든 필드, 에러 코드, 설계 체크리스트를 담은 완전한 레퍼런스로 넘어갑니다.
- 완전 레퍼런스: 10_reference.md — 19개 에러 코드, 모든 YAML 필드, 20개 체크리스트