Home > EulerAgent > Tutorials > Pattern > Pattern 07. Multi-Route Judge — Handling 3 or More Routes...

Pattern 07. Multi-Route Judge — Handling 3 or More Routes

Learning Objectives

After completing this tutorial, you will be able to:

Prerequisites

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.

← Prev Back to List Next →