Pattern 07. Multi-Route Judge — Handling 3 or More Routes
Learning Objectives
After completing this tutorial, you will be able to:
- Declare 3 or more values in a Judge node's
route_valuesand connect edges for each - Understand
JUDGE_ROUTE_COVERAGE_ERRORand verify that all routes are covered - Design patterns where multiple edges connect to a single destination node (
finalize) - Implement an approve/request changes/reject 3-way routing pattern
- Prevent
JUDGE_DEAD_ENDerrors that commonly occur in complex Judge branching
Prerequisites
04_judge_and_loop.mdcompleted (basic Judge understanding)06_human_gate.mdcompletion recommended
euleragent agent list
euleragent pattern list
1. Why Is 3-Way Routing Necessary?
The Judge in Tutorial 04 made binary decisions: finalize or revise. However, real business processes are often more complex.
PR code review scenario:
- approve: Code looks good, merge immediately
- request_changes: Changes needed, send feedback to the developer
- reject: Fundamental issues, redesign required
Document review scenario:
- publish: Publish immediately
- minor_edit: Publish after minor corrections
- major_revision: Full revision required
Support ticket handling scenario:
- resolve: Issue resolved
- escalate: Escalate to the next level
- reject: Out of support scope, decline
2. Rules for Multi-Route
Rule 1: Every route_value Must Have an Edge
judge:
route_values: [approve, request_changes, reject]
edges:
- from: evaluate
to: finalize
when: "judge.route == approve" # approve 커버
- from: evaluate
to: revise
when: "judge.route == request_changes" # request_changes 커버
- from: evaluate
to: finalize # 같은 목적지도 가능!
when: "judge.route == reject" # reject 커버
Rule 2: Multiple route_values Can Go to the Same Destination
A pattern where both approve and reject go to finalize is valid. (approve completes as a success, reject completes as an immediate termination.)
Rule 3: Preventing JUDGE_DEAD_END
All paths originating from a Judge node must eventually reach finalize. If any branch cannot reach finalize, a JUDGE_DEAD_END error is raised.
3. PR Review Pattern Design
A pattern that analyzes PR code and evaluates it with 3-way routing:
approve → finalize (PR 승인, 머지 안내)
request_changes → revise → PR 수정 → evaluate (루프)
reject → finalize (PR 거절, 재설계 안내)
Full flow:
┌─────────────────────────────────────────────────────────────────┐
│ code.pr_review 패턴 흐름도 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ [analyze] │
│ │ PR 코드 분석 (llm/execute) │
│ │ when: true │
│ ▼ │
│ [evaluate] ─────── when: judge.route == approve ───────────────┐
│ │ 3-way 평가 (judge/evaluator_v1) │
│ ├─────── when: judge.route == reject ────────────────────────┤
│ │ when: judge.route == request_changes │
│ ▼ │
│ [draft_feedback] │
│ │ 수정 요청 피드백 작성 (llm/execute) │
│ │ when: true │
│ ▼ │
│ [apply_changes] │
│ │ 수정 사항 적용 (llm/execute) │
│ │ when: true │
│ └────────────────────────► [evaluate] (루프 최대 3회) │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ [FINALIZE] review_result.md 저장 │◄──┘
│ └──────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
4. Writing the YAML
Create the pr_review.yaml file.
id: code.pr_review
version: 1
category: code
description: "PR 코드 리뷰 — 승인/수정요청/거절 3-way Judge 라우팅"
defaults:
# request_changes 루프 최대 3회
max_iterations: 3
max_total_tool_calls: 20
pass_threshold: 0.85
nodes:
# ── 노드 1: analyze ──
- id: analyze
kind: llm
runner:
mode: execute
# PR 분석은 코드 읽기만 허용
exclude_tools: [web.search, web.fetch, shell.exec]
prompt:
system_append: |
당신은 시니어 코드 리뷰어입니다.
제공된 PR(Pull Request)을 분석하세요.
분석 항목:
1. 코드 변경 범위 및 목적
2. 설계 패턴 준수 여부
3. 잠재적 버그 또는 엣지 케이스
4. 테스트 커버리지
5. 성능 영향
6. 보안 고려사항
출력: 구조화된 분석 보고서 (마크다운)
artifacts:
primary: analysis.md
# ── 노드 2: evaluate (3-way Judge) ──
- id: evaluate
kind: judge
judge:
schema: evaluator_v1
# 3개의 route_values — 모두 엣지가 필요!
route_values: [approve, request_changes, reject]
prompt:
system_append: |
당신은 테크 리드입니다. PR 분석을 바탕으로 최종 결정을 내리세요.
결정 기준:
- 'approve' (score >= 0.85):
* 코드 품질이 높고 버그 없음
* 테스트 충분
* 설계 원칙 준수
→ 즉시 머지 가능
- 'request_changes' (score 0.55-0.84):
* 수정 가능한 문제가 있음
* 구체적인 피드백으로 개선 가능
* 전면 재설계는 불필요
→ 수정 후 재검토
- 'reject' (score < 0.55):
* 근본적인 설계 문제
* 수정으로 해결 불가
* 재설계 또는 다른 접근법 필요
→ 이번 PR 종료, 재시작
중요: suggestions에 구체적이고 실행 가능한 피드백을 작성하세요.
# ── 노드 3: draft_feedback ──
# request_changes 라우트에서만 실행
- id: draft_feedback
kind: llm
runner:
mode: execute
exclude_tools: [web.search, web.fetch]
prompt:
system_append: |
코드 리뷰 피드백 문서를 작성하세요.
Judge의 suggestions를 바탕으로 개발자가 이해하기 쉬운
구체적인 수정 요청사항을 작성하세요.
형식:
## 필수 수정사항 (must fix)
## 권장 수정사항 (should fix)
## 선택 개선사항 (nice to have)
각 항목에 코드 예시나 참고 링크를 포함하세요.
artifacts:
primary: feedback.md
# ── 노드 4: apply_changes ──
- id: apply_changes
kind: llm
runner:
mode: execute
exclude_tools: [web.search, web.fetch]
prompt:
system_append: |
피드백(feedback.md)을 반영하여 코드를 수정하세요.
수정된 코드와 변경 사항 요약을 작성하세요.
출력:
1. 수정된 코드 파일 (코드 블록 포함)
2. 변경사항 요약 (무엇을, 왜 변경했는지)
artifacts:
primary: revised_code.md
edges:
# 분석 완료 후 평가
- from: analyze
to: evaluate
when: "true"
# ─── 3-way Judge 라우팅 ───
# approve → finalize (성공 완료)
- from: evaluate
to: finalize
when: "judge.route == approve"
# request_changes → 피드백 작성 → 수정 → 재평가
- from: evaluate
to: draft_feedback
when: "judge.route == request_changes"
# reject → finalize (거절 완료)
# 같은 목적지(finalize)이지만 다른 라우트 — 유효한 패턴
- from: evaluate
to: finalize
when: "judge.route == reject"
# request_changes 루프 연결
- from: draft_feedback
to: apply_changes
when: "true"
- from: apply_changes
to: evaluate
when: "true"
finalize:
artifact: review_result.md
5. Validation
euleragent pattern validate pr_review.yaml
Expected output:
Validating pattern: pr_review.yaml
Stage 1 (Schema) PASS
Stage 2 (Structural) PASS
Judge node 'evaluate': route_values coverage check
approve → finalize (when: judge.route == approve) ✓
request_changes → draft_feedback (when: judge.route == request_changes) ✓
reject → finalize (when: judge.route == reject) ✓
All 3 route_values covered ✓
Stage 3 (IR Analysis) PASS
Cycle: evaluate → draft_feedback → apply_changes → evaluate (bounded: max_iterations=3) ✓
Dead-end check: all paths reach finalize ✓
approve path: evaluate → finalize ✓
request_changes path: evaluate → draft_feedback → apply_changes → evaluate → ... → finalize ✓
reject path: evaluate → finalize ✓
Validation complete: 0 errors, 0 warnings
6. Compilation -- Route Coverage Verification
euleragent pattern compile pr_review.yaml
Check the Judge's route_coverage in the compilation output:
{
"nodes": {
"evaluate": {
"kind": "judge",
"judge": {
"route_values": ["approve", "request_changes", "reject"],
"route_coverage": {
"approve": {
"covered": true,
"destination": "finalize",
"path_to_finalize": ["finalize"]
},
"request_changes": {
"covered": true,
"destination": "draft_feedback",
"path_to_finalize": ["draft_feedback", "apply_changes", "evaluate", "...finalize"]
},
"reject": {
"covered": true,
"destination": "finalize",
"path_to_finalize": ["finalize"]
}
},
"all_paths_reach_finalize": true
}
}
}
}
7. Execution Simulation
Scenario A: approve Path
cp pr_review.yaml .euleragent/patterns/
euleragent pattern run code.pr_review my-agent \
--task "PR #142: 사용자 인증 미들웨어 리팩토링 — JWT 토큰 검증 로직 개선, 테스트 12개 추가" \
--project default
Expected output (approve path):
[run:j1a4f8e2] Starting pattern: code.pr_review
✓ analyze Completed (9s)
변경: 3개 파일, +247/-89 lines
테스트: 12개 추가, 커버리지 87% → 94%
✓ evaluate Completed (6s)
score: 0.91 → route: approve
reason: "코드 품질 우수, 테스트 충분, JWT 보안 처리 올바름"
✓ finalize Completed
Review result: APPROVED
Artifact: .euleragent/runs/j1a4f8e2/artifacts/review_result.md
Scenario B: request_changes Path
euleragent pattern run code.pr_review my-agent \
--task "PR #143: 결제 처리 모듈 추가 — Stripe 통합, 에러 핸들링 부재" \
--project default
Expected output (request_changes path):
[run:k2b5g9f3] Starting pattern: code.pr_review
✓ analyze Completed (11s)
문제 발견: 에러 핸들링 부재, 트랜잭션 롤백 미구현
✓ evaluate Completed (7s)
score: 0.62 → route: request_changes
suggestions:
- "결제 실패 시 롤백 로직 추가 (Stripe.error.CardError 처리)"
- "멱등성 키(idempotency key) 구현 — 중복 결제 방지"
- "단위 테스트 추가 (최소 실패 케이스 5개)"
✓ draft_feedback Completed (8s) — feedback.md 작성
✓ apply_changes Completed (14s) — 에러 핸들링 추가, 테스트 작성
✓ evaluate Completed (6s)
score: 0.88 → route: approve
✓ finalize Completed
Review result: APPROVED (after 1 revision)
Artifact: .euleragent/runs/k2b5g9f3/artifacts/review_result.md
Scenario C: reject Path
euleragent pattern run code.pr_review my-agent \
--task "PR #144: 전체 인증 시스템 재작성 — 설계 문서 없음, 기존 API 호환성 파괴" \
--project default
Expected output (reject path):
[run:l3c6h0g4] Starting pattern: code.pr_review
✓ analyze Completed (10s)
심각: 기존 API 34개와 하위 호환성 파괴
✓ evaluate Completed (8s)
score: 0.31 → route: reject
reason: "근본적인 설계 문제 — 하위 호환성 파괴, 마이그레이션 경로 없음"
✓ finalize Completed
Review result: REJECTED
Artifact: .euleragent/runs/l3c6h0g4/artifacts/review_result.md
The reject path proceeds immediately to finalize without going through the draft_feedback or apply_changes nodes.
8. Intentionally Triggering a JUDGE_ROUTE_COVERAGE_ERROR
Let's remove the edge for the reject route.
edges:
- from: evaluate
to: finalize
when: "judge.route == approve"
- from: evaluate
to: draft_feedback
when: "judge.route == request_changes"
# reject 엣지 제거!
- from: draft_feedback
to: apply_changes
when: "true"
- from: apply_changes
to: evaluate
when: "true"
Validation:
euleragent pattern validate pr_review_broken.yaml
Expected output:
Stage 2 (Structural) FAIL
ERROR [JUDGE_ROUTE_COVERAGE_ERROR]
Node 'evaluate': route_value 'reject' has no outgoing edge.
All values in route_values must have a corresponding edge
with matching 'when: "judge.route == reject"' condition.
route_values declared: [approve, request_changes, reject]
route_values covered: [approve, request_changes]
route_values missing: [reject]
Fix: Add an edge from 'evaluate' for route 'reject'.
9. Understanding JUDGE_DEAD_END
JUDGE_DEAD_END occurs when a particular route's path cannot reach finalize.
# 잘못된 예 — JUDGE_DEAD_END
nodes:
- id: evaluate
kind: judge
judge:
route_values: [approve, request_changes, reject]
- id: dead_end_node # finalize로 가는 엣지가 없는 노드
edges:
- from: evaluate
to: finalize
when: "judge.route == approve"
- from: evaluate
to: draft_feedback
when: "judge.route == request_changes"
- from: evaluate
to: dead_end_node # 이 노드는 finalize에 도달 불가!
when: "judge.route == reject"
- from: draft_feedback
to: evaluate
when: "true"
# dead_end_node에서 다른 노드로 가는 엣지 없음
This pattern passes JUDGE_ROUTE_COVERAGE_ERROR (since the edge exists), but Stage 3 IR analysis detects JUDGE_DEAD_END.
10. Key Concepts Explained
Multiple Routes to the Same Destination
edges:
- from: evaluate
to: finalize
when: "judge.route == approve" # 성공 완료
- from: evaluate
to: finalize
when: "judge.route == reject" # 거절도 완료
This is a perfectly valid pattern. Both paths go to finalize, but the content recorded in review_result.md differs (approve_message vs reject_message). The runtime includes the Judge's reason in the finalize artifact.
Calculating max_iterations in Complex Branching
In the request_changes loop (evaluate -> draft_feedback -> apply_changes -> evaluate), with max_iterations: 3:
- Round 1: request_changes -> revision -> re-evaluate
- Round 2: request_changes -> revision -> re-evaluate
- Round 3: request_changes -> revision -> re-evaluate
- Round 4: max_iterations reached -> forced finalize
The approve and reject paths are not part of the loop, so they do not affect the iteration count.
Order of route_values
The declaration order of route_values does not affect routing. The Judge LLM autonomously selects a value. The order is used for documentation purposes only.
11. Exercise: Extending the PR Review Pattern
Exercise 1: Adding a Security Review Node
Add a security-focused review before approve.
evaluate → approve → security_check → finalize
→ request_changes → (루프)
→ reject → finalize
security_check node:
- kind: llm
- Focused exclusively on security vulnerability review
- Add a Judge node to route back to reject if a serious security issue is found
Exercise 2: 4-Way Routing
route_values: [approve, minor_fix, major_revision, reject]
Design each route:
- approve: Immediately finalize
- minor_fix: Small fix -> apply_changes -> finalize (no loop)
- major_revision: Full revision -> analyze -> evaluate (loop)
- reject: Immediately finalize (rejection)
Validate and run the pattern.
Next Steps
You have completed the multi-route Judge pattern. Now let's learn about specialized patterns for security and compliance.
- Next tutorial: 08_airgap_and_ops.md -- Airgap patterns that completely block external network access
- Advanced topologies: 09_advanced_patterns.md -- Two independent Judge cycles