패턴 06. 명시적 인간 검토 게이트 — 반드시 사람이 확인하는 노드
학습 목표
이 튜토리얼을 마치면 다음을 할 수 있습니다.
force_tool: file.write+mode: plan으로 인간 검토 게이트를 설계할 수 있다max_loops: 1로 게이트 노드가 한 번만 실행되도록 보장할 수 있다- 파일 쓰기 후 사람이 직접 파일을 편집하고 다음 단계를 승인하는 흐름을 운영할 수 있다
when: "approvals_resolved"엣지로 검토 후 다음 단계로 넘어가는 패턴을 구현할 수 있다- Judge 자동 평가와 인간 수동 검토의 차이와 적절한 사용 시점을 설명할 수 있다
사전 준비
05_web_research.md완료- euleragent 에이전트 생성 완료
euleragent agent list
euleragent pattern list
1. 왜 인간 검토 게이트가 필요한가?
Judge 노드(04 튜토리얼)는 LLM이 LLM의 결과를 평가합니다. 이는 빠르고 자동화되어 있지만, 다음 상황에서는 부족합니다.
법적/규정 준수 문서: 의료 정보, 법률 자문, 금융 조언은 사람이 반드시 검토해야 합니다.
외부 발행 전 최종 확인: 블로그, 보고서, 이메일이 실제로 공개되기 전 편집자가 검토.
보안 감사 결과: 취약점 보고서나 패치 계획은 반드시 보안 담당자가 승인.
고객사 납품 결과물: AI가 생성했더라도 사람 이름으로 납품되는 문서는 사람이 검토.
인간 검토 게이트는 자동화된 흐름 중에 강제적인 인간 개입 지점을 만드는 패턴입니다.
2. 핵심 메커니즘: force_tool: file.write + mode: plan
왜 file.write인가?
force_tool: file.write + mode: plan의 조합이 인간 검토 게이트가 되는 이유:
- LLM이 파일을 작성하는 "제안"을 합니다 (내용 포함)
- 시스템이 pause 상태가 됩니다
- 사람이 제안된 파일 내용을 검토합니다
- 내용이 마음에 들지 않으면 파일을 직접 편집할 수 있습니다
- 승인 후 파일이 실제로 저장됩니다
LLM이 draft 제안
→ approve list (내용 미리보기)
→ 사람이 내용 직접 수정 가능
→ accept-all --execute (파일 저장)
→ resume (다음 노드)
max_loops: 1의 역할
게이트 노드는 단 한 번만 실행되어야 합니다. max_loops: 1이 없으면 루프 패턴에서 게이트가 여러 번 발생할 수 있습니다.
runner:
mode: plan
force_tool: file.write
max_loops: 1 # 이 노드는 정확히 한 번만 실행
3. 패턴 설계
초안 작성 후 사람이 검토하고, 검토 완료 후 Judge가 최종 평가하는 패턴.
[draft] → [human_review] ──HITL PAUSE──► 사람이 검토/편집
│ (파일 저장 후)
│ when: approvals_resolved
▼
[evaluate] → finalize
│
└── revise → [evaluate] (루프)
완전한 흐름:
┌─────────────────────────────────────────────────────────────────┐
│ writing.human_review 패턴 흐름도 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ [draft] │
│ │ 초안 작성 (llm/execute) │
│ │ when: true │
│ ▼ │
│ [human_review] ◄─── 반드시 HITL PAUSE (file.write) │
│ │ 초안을 파일로 저장 제안 (llm/plan, max_loops=1) │
│ │ 사람이 파일 검토/편집 가능 │
│ │ when: approvals_resolved │
│ ▼ │
│ [evaluate] ─────── when: judge.route == finalize ─────────────┐
│ │ 검토된 초안의 품질 평가 (judge/evaluator_v1) │
│ │ when: judge.route == revise │
│ ▼ │
│ [revise] │
│ │ 수정 (llm/execute) │
│ └────────────────────────► [evaluate] (최대 2회) │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ [FINALIZE] reviewed_document.md 저장 │◄──┘
│ └──────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
4. YAML 작성
writing_with_human_review.yaml 파일을 생성합니다.
id: writing.human_review
version: 1
category: writing
description: "초안 작성 후 인간 검토 게이트를 통과하는 문서 작성 패턴"
defaults:
max_iterations: 2
max_total_tool_calls: 10
pass_threshold: 0.85
nodes:
# ── 노드 1: draft ──
- id: draft
kind: llm
runner:
mode: execute
exclude_tools: [web.search, web.fetch, shell.exec]
prompt:
system_append: |
당신은 기술 문서 작가입니다.
주어진 주제에 대한 완성된 초안을 작성하세요.
요구사항:
- 전문적이고 명확한 문체
- 적절한 섹션 구분
- 실용적 예시 포함
- 마크다운 포맷
이 초안은 사람 편집자의 검토를 받을 것입니다.
완성도 있게 작성하되, 사람이 편집할 여지를 남겨두세요.
artifacts:
primary: draft.md
# ── 노드 2: human_review (인간 검토 게이트) ──
- id: human_review
kind: llm
runner:
# mode: plan + force_tool: file.write = 인간 검토 게이트의 핵심
mode: plan
force_tool: file.write
# max_loops: 1 — 이 노드는 정확히 한 번만 실행됨
# 루프가 있는 패턴에서 게이트가 반복되는 것을 방지
max_loops: 1
prompt:
system_append: |
당신은 문서 관리 보조자입니다.
작성된 초안(draft.md)을 검토용 파일로 저장하는 제안을 하세요.
파일 저장 시:
- 경로: review/document_for_review.md
- 파일 상단에 다음 헤더를 추가하세요:
<!-- 검토 요청: [날짜]
검토자: [담당자]
검토 사항: 내용의 정확성, 톤, 불필요한 섹션 확인
-->
- 이후 초안 내용을 그대로 포함
이 파일은 사람이 직접 편집할 것입니다.
# 파일 쓰기 예산 (보통 1개면 충분)
guardrails:
tool_call_budget:
file.write: 1
artifacts:
primary: review/document_for_review.md
# ── 노드 3: evaluate (Judge) ──
- id: evaluate
kind: judge
judge:
schema: evaluator_v1
route_values: [finalize, revise]
prompt:
system_append: |
사람이 검토/편집한 문서(review/document_for_review.md)를 평가하세요.
주의: 이 문서는 사람 편집자가 이미 검토했습니다.
기술적 내용보다 최종 발행 준비 상태에 집중하세요:
- 구조적 완결성 (30%): 모든 섹션이 논리적으로 연결되는가?
- 언어 품질 (30%): 문체가 일관되고 명확한가?
- 발행 준비도 (40%): 추가 수정 없이 바로 발행 가능한가?
score >= 0.85 → finalize
score < 0.85 → revise (구체적인 개선점 명시)
# ── 노드 4: revise ──
- id: revise
kind: llm
runner:
mode: execute
exclude_tools: [web.search, web.fetch, shell.exec]
prompt:
system_append: |
편집장의 피드백을 반영하여 문서를 개선하세요.
사람이 편집한 내용(review/document_for_review.md)을 기반으로 합니다.
수정된 전체 문서를 다시 작성하세요.
artifacts:
primary: review/document_for_review.md
edges:
- from: draft
to: human_review
when: "true"
# human_review 노드는 HITL pause 후 승인이 완료됐을 때만 다음으로 이동
- from: human_review
to: evaluate
when: "approvals_resolved"
- from: evaluate
to: finalize
when: "judge.route == finalize"
- from: evaluate
to: revise
when: "judge.route == revise"
- from: revise
to: evaluate
when: "true"
finalize:
artifact: review/document_for_review.md
5. 검증
euleragent pattern validate writing_with_human_review.yaml
예상 출력:
Validating pattern: writing_with_human_review.yaml
Stage 1 (Schema) PASS
Stage 2 (Structural) PASS
Stage 3 (IR Analysis) PASS
HITL gates: human_review (file.write, max_loops=1) ✓
Note: human_review gate will pause exactly once per run ✓
Cycle bounded: max_iterations=2 ✓
Validation complete: 0 errors, 0 warnings
6. 단계별 실행
단계 1: 실행 시작
cp writing_with_human_review.yaml .euleragent/patterns/
euleragent pattern run writing.human_review my-agent \
--task "사내 AI 도구 사용 가이드라인 문서 작성 — 허용 사용 사례, 금지 사항, 데이터 보안 주의사항 포함" \
--project default
예상 출력:
[run:i9e5d3c7] Starting pattern: writing.human_review
✓ draft Completed (14s) — draft.md generated (1,456 words)
⏸ human_review PAUSED — Waiting for HITL approval (file.write × 1)
Approval required:
euleragent approve list --run-id i9e5d3c7
단계 2: 파일 내용 미리보기
euleragent approve list --run-id i9e5d3c7
예상 출력:
Pending Approvals for run: i9e5d3c7
─────────────────────────────────────────────────
Node: human_review (max_loops=1, human gate)
#1 file.write path=review/document_for_review.md
Content preview (first 20 lines):
┌─────────────────────────────────────────────┐
│ <!-- 검토 요청: 2026-02-23 │
│ 검토자: [담당자] │
│ 검토 사항: 내용의 정확성, 톤, 불필요한 섹션 │
│ --> │
│ │
│ # 사내 AI 도구 사용 가이드라인 │
│ │
│ ## 1. 목적 │
│ 이 가이드라인은 조직 내 AI 도구의 책임있는 │
│ 사용을 위해 작성되었습니다... │
└─────────────────────────────────────────────┘
[Full content: 1,456 words, 89 lines]
⚠️ HUMAN GATE: This node requires human review before proceeding.
You may edit the file content before approving.
단계 3: 내용 직접 수정 (선택)
미리보기를 보고 내용을 수정하고 싶으면, 파일을 직접 편집합니다.
# 파일 내용을 먼저 추출해서 편집
euleragent approve show --run-id i9e5d3c7 --item 1 > /tmp/review_draft.md
# 선호하는 에디터로 수정
vim /tmp/review_draft.md
# 또는
code /tmp/review_draft.md
# 수정된 내용으로 대체 후 승인
euleragent approve accept --run-id i9e5d3c7 --item 1 \
--content-file /tmp/review_draft.md \
--execute
수정 없이 그대로 승인하는 경우:
euleragent approve accept-all --run-id i9e5d3c7 --execute
예상 출력:
Accepted item #1 (file.write) for run: i9e5d3c7
Writing file: review/document_for_review.md ... OK (1,456 bytes)
Human gate passed. Resuming pattern...
단계 4: 패턴 재개
euleragent pattern resume i9e5d3c7 --execute
예상 출력:
[run:i9e5d3c7] Resuming from: human_review → evaluate
✓ human_review Completed — review/document_for_review.md saved
✓ evaluate Completed — score: 0.93 → route: finalize
✓ finalize Completed
Artifact: .euleragent/runs/i9e5d3c7/artifacts/review/document_for_review.md
7. 저장된 파일 확인
# 사람이 검토/편집한 최종 문서
cat .euleragent/runs/i9e5d3c7/artifacts/review/document_for_review.md
# 원본 AI 초안 (비교용)
cat .euleragent/runs/i9e5d3c7/artifacts/draft.md
# 승인 기록
cat .euleragent/runs/i9e5d3c7/approvals.jsonl
승인 기록 예시:
{"ts":"2026-02-23T14:40:22Z","run_id":"i9e5d3c7","node":"human_review","action":"accept","item":1,"tool":"file.write","path":"review/document_for_review.md","content_modified":true,"operator":"sean","note":"금지사항 섹션에 내용 추가"}
8. Judge 평가와 인간 검토의 차이
| 기준 | Judge (자동) | Human Gate (수동) |
|---|---|---|
| 속도 | 빠름 (초 단위) | 느림 (분~시간) |
| 일관성 | 높음 (항상 같은 기준) | 낮음 (사람마다 다름) |
| 창의적 판단 | 낮음 | 높음 |
| 규정 준수 보장 | 낮음 | 높음 |
| 책임 소재 | AI | 사람 |
| 편집 능력 | 없음 | 있음 |
Judge를 사용하는 경우: 품질 기준이 명확하고, 속도가 중요하고, 반복 실행이 많을 때.
Human Gate를 사용하는 경우: 법적/윤리적 책임이 있을 때, 창의적 판단이 필요할 때, 내용이 외부에 공개될 때.
9. 주요 개념 설명
human_review 노드가 루프 내에 있으면?
만약 revise → human_review → evaluate 루프가 있다면, max_loops: 1이 없을 때 매 루프마다 사람이 검토해야 합니다. 이는 의도적일 수 있지만, 대부분의 경우 비효율적입니다.
max_loops: 1을 설정하면 이 노드는 전체 run에서 한 번만 실행됩니다. 두 번째 루프에서는 이 노드를 건너뜁니다.
approvals_resolved vs true
human_review 노드 다음 엣지에 when: "true"를 사용하면:
- 시스템이 pause 없이 즉시 다음 노드로 넘어갑니다
- 파일 쓰기가 실행되지 않습니다 (plan 모드이므로)
- 인간 검토 게이트의 의미가 없어집니다
반드시 when: "approvals_resolved"를 사용하세요.
파일 편집과 승인의 순서
승인 흐름:
1. euleragent approve list — 제안된 내용 확인
2. (선택) 파일 내용 편집 (--content-file 옵션)
3. euleragent approve accept-all --execute — 실제 파일 저장 및 계속
편집 없이 승인하면 LLM이 제안한 내용 그대로 저장됩니다.
10. 실습 과제: 보안 감사 패턴
보안 취약점 보고서를 작성하고 보안 담당자가 반드시 검토하는 패턴을 만들어보세요.
요구사항
scan노드: 코드/설정 분석 (llm/execute, file.read만 허용)draft_report노드: 취약점 보고서 초안 작성security_review노드: 보안 담당자 필수 검토 (force_tool: file.write, max_loops: 1)severity_judge노드: 심각도 평가 (judge, route_values: [critical, high_medium, low])- critical: 즉시 escalate →
escalate노드 - high_medium: → finalize
- low: → finalize
- finalize:
security_report.md
힌트 YAML 구조
id: security.audit_review
version: 1
category: ops
description: "취약점 보고서 작성 + 보안 담당자 필수 검토 패턴"
defaults:
max_iterations: 1
max_total_tool_calls: 20
nodes:
- id: scan
kind: llm
runner:
mode: execute
# 파일 읽기만 허용, 외부 연결 금지
exclude_tools: [web.search, web.fetch, shell.exec]
# ...
- id: draft_report
kind: llm
# ...
- id: security_review
kind: llm
runner:
mode: plan
force_tool: file.write
max_loops: 1 # 반드시!
# ...
- id: severity_judge
kind: judge
judge:
schema: evaluator_v1
route_values: [critical, high_medium, low]
# ...
- id: escalate
kind: llm
# ...
edges:
- from: security_review
to: severity_judge
when: "approvals_resolved" # 반드시!
- from: severity_judge
to: escalate
when: "judge.route == critical"
- from: severity_judge
to: finalize
when: "judge.route == high_medium"
- from: severity_judge
to: finalize
when: "judge.route == low"
# ...
finalize:
artifact: security_report.md
검증:
euleragent pattern validate security_audit.yaml
JUDGE_ROUTE_COVERAGE_ERROR가 발생하지 않는지 확인하고, 모든 route_values에 엣지가 있는지 점검하세요.
다음 단계
인간 검토 게이트 패턴을 구현했습니다. 이제 Judge에서 더 복잡한 다중 경로 라우팅을 다룹니다.
- 다음 튜토리얼: 07_multi_route.md — 3개 이상의 Judge 라우트를 처리하는 패턴
- 에어갭 보안: 08_airgap_and_ops.md — 웹을 완전히 차단한 내부 전용 패턴