Pattern 08. Airgap Patterns and Ops Design — Web Blocking + Policy Compliance
Learning Objectives
After completing this tutorial, you will be able to:
- Design an airgap pattern that completely blocks web access using
exclude_tools - Explain business scenarios where airgap patterns are needed (security regulations, GDPR, internal-only)
- Apply Ops-specific pattern design principles (short loops, clear criteria, fast decisions)
- Implement internal processing pipelines that include policy review gates
- Understand the correct combination of
HITL_GATING_VIOLATIONand airgap settings
Prerequisites
05_web_research.mdcompleted (understanding of HITL gating)06_human_gate.mdcompleted (understanding of human review gates)
euleragent agent list
euleragent pattern list
1. What Is an Airgap Pattern?
Airgap is originally a network security term referring to a system completely isolated from the external internet. In patterns, an airgap refers to a pattern that blocks all external network access.
Why Airgap Is Needed
Compliance: - GDPR: When processing EU customer data, the data must not leave the EU - HIPAA: Patient medical information must not be transmitted to external services - SOX: External queries are prohibited when processing financial data
Security Policies: - Tasks involving internal source code or trade secrets - Processing classified information - Internal vulnerability reports (dangerous if exposed externally)
Internal-Only Data: - Internal FAQ updates - Internal onboarding documents - Company policy document drafts
Airgap vs Regular Patterns
| Aspect | Regular Pattern | Airgap Pattern |
|---|---|---|
| web.search | Available (HITL) | Completely blocked |
| web.fetch | Available (HITL) | Completely blocked |
| git.push | Available after HITL approval | HITL if needed |
| LLM internal knowledge | Available | Available |
| Internal file reading | Available | Available |
2. Implementing Airgap with exclude_tools
Apply exclude_tools to all nodes.
nodes:
- id: analyze
kind: llm
runner:
mode: execute
# 웹 접근 완전 차단
exclude_tools: [web.search, web.fetch]
- id: draft
kind: llm
runner:
mode: execute
exclude_tools: [web.search, web.fetch]
- id: policy_check
kind: llm
runner:
mode: plan
force_tool: file.write
# 파일 쓰기는 허용, 웹 접근 차단
exclude_tools: [web.search, web.fetch]
If a tool listed in exclude_tools is invoked, the runtime automatically blocks it.
3. Characteristics of Ops Patterns
Operations patterns have distinct characteristics compared to typical research/writing patterns.
Fast response time: Operational situations require short wait times. Avoid long loops.
Clear decision criteria: Reduce ambiguity and produce predictable results.
Short max_iterations: Typically 1-2 iterations. More than 3 is unsuitable for operational scenarios.
Structured output: Use standard formats such as tickets, reports, and alerts.
Escalation path: There must be a path to quickly hand off to a human when automated resolution is not possible.
4. Pattern 1: Basic Airgap Pattern
A pattern that processes customer support tickets using only internal data.
Node Flow Diagram
┌─────────────────────────────────────────────────────────────────┐
│ ops.internal_triage 패턴 흐름도 (에어갭) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ [classify] │
│ │ 티켓 분류 (llm/execute, 웹 차단) │
│ │ when: true │
│ ▼ │
│ [draft_resolution] ◄── HITL PAUSE (file.write, 정책 검토) │
│ │ 해결책 초안 (llm/plan, 웹 차단) │
│ │ when: approvals_resolved │
│ ▼ │
│ [evaluate] ──── when: judge.route == finalize ─────────────────┐
│ │ 품질 평가 (judge, 웹 차단) │
│ │ when: judge.route == revise │
│ ▼ │
│ [revise] │
│ │ 개선 (llm/execute, 웹 차단) │
│ └────────────────────────► [evaluate] (최대 2회) │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ [FINALIZE] triage_result.md 저장 │◄──┘
│ └──────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
YAML File: ops_internal_triage.yaml
id: ops.internal_triage
version: 1
category: ops
description: "에어갭 고객 지원 트리아지 — 외부 네트워크 완전 차단, 내부 지식만 활용"
defaults:
max_iterations: 2
max_total_tool_calls: 8
pass_threshold: 0.80
nodes:
# ── 노드 1: classify ──
- id: classify
kind: llm
runner:
mode: execute
# 에어갭: 외부 네트워크 완전 차단
exclude_tools: [web.search, web.fetch]
prompt:
system_append: |
당신은 내부 지원팀 트리아지 전문가입니다.
외부 정보 없이 내부 지식만으로 티켓을 분류하세요.
분류 항목:
- 카테고리: authentication / database / performance / ui / billing / other
- 우선순위: critical / high / medium / low
- 복잡도: simple / moderate / complex
- 예상 해결 시간: 1h / 4h / 1d / 1w
출력: 구조화된 분류 결과 (JSON 형식)
artifacts:
primary: classification.json
# ── 노드 2: draft_resolution (정책 검토 게이트) ──
- id: draft_resolution
kind: llm
runner:
mode: plan
force_tool: file.write
max_loops: 1
# 에어갭 유지 + 파일 쓰기는 허용
exclude_tools: [web.search, web.fetch]
prompt:
system_append: |
분류 결과를 바탕으로 해결책 초안을 작성하고 파일로 저장하세요.
파일 형식:
# 지원 티켓 해결책
## 티켓 정보
- 카테고리: [분류 결과]
- 우선순위: [분류 결과]
## 근본 원인 분석
## 즉시 조치 (1-2시간 내)
## 단기 해결책 (이번 주)
## 장기 예방 조치
## 필요 리소스
이 파일은 담당자가 검토할 것입니다.
guardrails:
tool_call_budget:
file.write: 1
artifacts:
primary: resolution_draft.md
# ── 노드 3: evaluate (Judge) ──
- id: evaluate
kind: judge
judge:
schema: evaluator_v1
route_values: [finalize, revise]
runner:
mode: execute
exclude_tools: [web.search, web.fetch]
prompt:
system_append: |
내부 지원 해결책을 평가하세요. 외부 리서치 없이 실행 가능한가?
평가 기준:
- 내부 도구로 실행 가능 여부 (40%): 외부 서비스나 정보 없이 가능한가?
- 명확성 (30%): 담당자가 바로 실행할 수 있을 만큼 구체적인가?
- 완결성 (30%): 즉각 조치, 단기, 장기가 모두 다뤄졌는가?
# ── 노드 4: revise ──
- id: revise
kind: llm
runner:
mode: execute
exclude_tools: [web.search, web.fetch]
prompt:
system_append: |
피드백을 반영하여 해결책을 개선하세요.
외부 도구나 서비스 없이 실행 가능한 해결책만 포함하세요.
artifacts:
primary: resolution_draft.md
edges:
- from: classify
to: draft_resolution
when: "true"
- from: draft_resolution
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: resolution_draft.md
5. Pattern 2: Airgap with Policy Review Gate
An advanced airgap pattern that loads policy documents and validates solutions against them.
Node Flow Diagram
┌─────────────────────────────────────────────────────────────────┐
│ ops.policy_compliant 패턴 흐름도 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ [load_policy] │
│ │ 내부 정책 파일 로드 (llm/execute, 파일 읽기만 허용) │
│ │ when: true │
│ ▼ │
│ [classify] │
│ │ 정책 기반 티켓 분류 (llm/execute, 웹 차단) │
│ │ when: true │
│ ▼ │
│ [draft] │
│ │ 해결책 초안 (llm/execute, 웹 차단) │
│ │ when: true │
│ ▼ │
│ [policy_check] ◄── HITL PAUSE (반드시 정책 담당자 검토) │
│ │ 정책 준수 확인 + 파일 저장 (llm/plan, max_loops=1) │
│ │ when: approvals_resolved │
│ ▼ │
│ [evaluate] ──── when: judge.route == finalize ─────────────────┐
│ │ 품질 평가 (judge) │
│ │ when: judge.route == revise │
│ ▼ │
│ [revise] ────────────────────────────── [evaluate] (최대 1회) │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ [FINALIZE] compliant_resolution.md 저장 │◄──┘
│ └──────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
YAML File: ops_policy_compliant.yaml
id: ops.policy_compliant
version: 1
category: ops
description: "내부 정책 로드 → 분류 → 초안 → 정책 준수 검토 게이트 → 평가 (완전 에어갭)"
defaults:
max_iterations: 1 # Ops 패턴은 짧게 유지
max_total_tool_calls: 10
nodes:
# ── 노드 1: load_policy ──
- id: load_policy
kind: llm
runner:
mode: execute
# 파일 읽기만 허용. 웹 + 파일 쓰기 + 셸 차단
exclude_tools: [web.search, web.fetch, file.write, shell.exec]
prompt:
system_append: |
내부 정책 파일을 읽고 핵심 내용을 요약하세요.
읽어야 할 파일:
- policies/support_policy.md (지원 정책)
- policies/escalation_rules.md (에스컬레이션 규칙)
- policies/data_handling.md (데이터 처리 규정)
파일이 없으면 "정책 파일 없음"으로 표시하세요.
요약은 다음 단계에서 해결책 작성 시 활용됩니다.
artifacts:
primary: policy_summary.md
# ── 노드 2: classify ──
- id: classify
kind: llm
runner:
mode: execute
exclude_tools: [web.search, web.fetch, shell.exec]
prompt:
system_append: |
로드된 내부 정책을 기반으로 티켓을 분류하세요.
에스컬레이션 규칙에 따라 자동 처리 가능한지 판단하세요.
출력:
- 자동 처리 가능 여부 (yes/no)
- 불가능하면 이유
- 담당 팀
- 예상 SLA
artifacts:
primary: classification.md
# ── 노드 3: draft ──
- id: draft
kind: llm
runner:
mode: execute
exclude_tools: [web.search, web.fetch, shell.exec]
prompt:
system_append: |
정책 요약과 분류를 바탕으로 해결책 초안을 작성하세요.
모든 단계가 내부 정책을 준수해야 합니다.
데이터 처리 규정(GDPR/개인정보보호법)에 맞는 조치만 포함하세요.
artifacts:
primary: resolution.md
# ── 노드 4: policy_check (정책 검토 게이트) ──
- id: policy_check
kind: llm
runner:
mode: plan
force_tool: file.write
max_loops: 1
# 에어갭 유지
exclude_tools: [web.search, web.fetch, shell.exec]
prompt:
system_append: |
정책 준수 담당자를 위한 검토 문서를 생성하세요.
파일에 포함할 내용:
1. 정책 준수 체크리스트
2. 해결책 초안 (원문)
3. 각 조치가 어느 정책 조항에 근거하는지 매핑
4. 잠재적 정책 위반 위험 항목 (있다면 빨간색 태그)
담당자: 이 파일을 검토하고 정책 위반이 없으면 승인하세요.
guardrails:
tool_call_budget:
file.write: 1
artifacts:
primary: policy_check_doc.md
# ── 노드 5: evaluate ──
- id: evaluate
kind: judge
judge:
schema: evaluator_v1
route_values: [finalize, revise]
runner:
mode: execute
exclude_tools: [web.search, web.fetch]
prompt:
system_append: |
정책 검토를 통과한 해결책의 최종 품질을 평가하세요.
- 실행 가능성: 내부 리소스만으로 즉시 실행 가능한가?
- 정책 준수: 검토 통과 기준에 맞는가?
- 완결성: 모든 액션 아이템이 명확한가?
# ── 노드 6: revise ──
- id: revise
kind: llm
runner:
mode: execute
exclude_tools: [web.search, web.fetch, shell.exec]
prompt:
system_append: |
피드백을 반영하여 해결책을 개선하세요.
모든 내용이 정책 검토 게이트를 다시 통과하지 않아도
될 정도로 명확하게 수정하세요.
artifacts:
primary: resolution.md
edges:
- from: load_policy
to: classify
when: "true"
- from: classify
to: draft
when: "true"
- from: draft
to: policy_check
when: "true"
- from: policy_check
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: resolution.md
6. Validation
euleragent pattern validate ops_internal_triage.yaml
euleragent pattern validate ops_policy_compliant.yaml
Expected output (policy_compliant):
Validating pattern: ops_policy_compliant.yaml
Stage 1 (Schema) PASS
Stage 2 (Structural) PASS
Airgap check: web.search excluded from all nodes ✓
HITL gate: policy_check (file.write, max_loops=1) ✓
Stage 3 (IR Analysis) PASS
All nodes have exclude_tools=[web.search, web.fetch, shell.exec] ✓
Cycle bounded: max_iterations=1 ✓
Validation complete: 0 errors, 0 warnings
Note: All nodes exclude web.search and web.fetch. This is an airgap pattern.
7. Execution Example
Running the Basic Airgap Pattern
cp ops_internal_triage.yaml .euleragent/patterns/
euleragent pattern run ops.internal_triage my-agent \
--task "고객 티켓 #T-2847: 결제 페이지에서 500 에러 발생. 오전 9시부터 지속. 영향 고객 약 200명." \
--project default
Expected output (paused at draft_resolution):
[run:m4d7i1j5] Starting pattern: ops.internal_triage
✓ classify Completed (4s)
카테고리: billing / 우선순위: critical / 복잡도: moderate
예상 해결 시간: 4h
⏸ draft_resolution PAUSED — Waiting for HITL approval (file.write × 1)
[AIRGAP: web access blocked for this run]
euleragent approve list --run-id m4d7i1j5
Pending Approvals for run: m4d7i1j5
─────────────────────────────────────────────────
Node: draft_resolution (HUMAN GATE, AIRGAP MODE)
#1 file.write path=resolution_draft.md
Content preview:
# 지원 티켓 해결책 — T-2847
## 티켓 정보
- 카테고리: billing
- 우선순위: CRITICAL
## 근본 원인 분석
결제 처리 서버의 데이터베이스 연결 풀 고갈 가능성.
오전 9시 트래픽 증가 시점과 일치.
## 즉시 조치 (30분 내)
1. 서버 로그 확인: /var/log/payment-service/error.log
2. DB 연결 수 모니터링: kubectl exec ... psql -c "SELECT count(*) ..."
3. 필요 시 결제 서버 재시작 (무중단)
...
⚠️ AIRGAP: No web resources were consulted. Internal knowledge only.
euleragent approve accept-all --run-id m4d7i1j5 --execute
euleragent pattern resume m4d7i1j5 --execute
Expected final output:
✓ draft_resolution Completed — resolution_draft.md saved
✓ evaluate Completed — score: 0.88 → route: finalize
✓ finalize Completed
[AIRGAP RUN SUMMARY]
Web searches: 0 (blocked)
File reads: 0
Internal knowledge: used
Total tool calls: 2 (file.write × 1, judge × 1)
Artifact: .euleragent/runs/m4d7i1j5/artifacts/resolution_draft.md
8. Airgap Execution Audit Log
Review the execution history of an airgap pattern.
# 감사 로그 확인
cat .euleragent/runs/m4d7i1j5/tool_calls.jsonl
{"ts":"2026-02-23T10:15:30Z","node":"draft_resolution","tool":"file.write","path":"resolution_draft.md","status":"proposed","hitl_required":true}
{"ts":"2026-02-23T10:17:45Z","node":"draft_resolution","tool":"file.write","path":"resolution_draft.md","status":"accepted","operator":"sean"}
{"ts":"2026-02-23T10:17:46Z","node":"draft_resolution","tool":"file.write","path":"resolution_draft.md","status":"executed","bytes":1847}
Confirm that no web search attempts were made:
cat .euleragent/runs/m4d7i1j5/tool_calls.jsonl | grep "web.search"
# 출력 없음 — 웹 검색 완전히 차단됨
9. Practice Exercise: Internal FAQ Update Pattern
Create an airgap pattern that updates internal FAQ documents.
Requirements
retrieve_existing: Read existing FAQ files (only file.read allowed, web blocked)draft_update: Draft new FAQ entries (web blocked)diff_review: Save changes to file + human review (file.write HITL, max_loops: 1)evaluate: Quality evaluation (judge, route_values: [finalize, revise])- finalize:
updated_faq.md
Hint
id: docs.faq_update
version: 1
category: ops
description: "사내 FAQ 업데이트 — 에어갭, 인간 검토 게이트"
defaults:
max_iterations: 1
max_total_tool_calls: 5
nodes:
- id: retrieve_existing
kind: llm
runner:
mode: execute
# 파일 쓰기, 웹, 셸 모두 차단 — 읽기만
exclude_tools: [web.search, web.fetch, file.write, shell.exec]
prompt:
system_append: |
docs/faq.md를 읽고 현재 FAQ 구조와 내용을 파악하세요.
업데이트 요청 내용과 비교해 어느 섹션을 수정해야 하는지 파악하세요.
- id: draft_update
kind: llm
runner:
mode: execute
exclude_tools: [web.search, web.fetch, shell.exec]
# ...
- id: diff_review
kind: llm
runner:
mode: plan
force_tool: file.write
max_loops: 1
exclude_tools: [web.search, web.fetch, shell.exec]
prompt:
system_append: |
변경 사항을 diff 형식으로 파일에 저장하세요.
기존 내용(-)과 새 내용(+)을 명확히 표시하세요.
guardrails:
tool_call_budget:
file.write: 1
- id: evaluate
kind: judge
judge:
schema: evaluator_v1
route_values: [finalize, revise]
runner:
mode: execute
exclude_tools: [web.search, web.fetch]
edges:
- from: retrieve_existing
to: draft_update
when: "true"
- from: draft_update
to: diff_review
when: "true"
- from: diff_review
to: evaluate
when: "approvals_resolved"
- from: evaluate
to: finalize
when: "judge.route == finalize"
- from: evaluate
to: draft_update # 수정 시 draft_update로 돌아감
when: "judge.route == revise"
finalize:
artifact: updated_faq.md
Validation:
euleragent pattern validate faq_update.yaml
Verify that no HITL_GATING_VIOLATION occurs (i.e., that mode: plan and force_tool are properly combined).
Execution:
euleragent pattern run docs.faq_update my-agent \
--task "FAQ 업데이트: 신규 결제 수단(계좌이체) 추가, 환불 정책 수정(14일→30일), 구버전 앱 지원 종료 공지 추가" \
--project default
10. Common Errors and Solutions
Error 1: HITL_GATING_VIOLATION
ERROR [HITL_GATING_VIOLATION]
Node 'policy_check': force_tool 'file.write' is set but mode is 'execute'.
force_tool requires mode: plan for HITL gating.
Solution: Change mode: execute to mode: plan.
Error 2: Execution Attempted Despite Web Blocking
This is automatically blocked at the runtime level.
[event] tool.blocked {node: "classify", tool: "web.search", reason: "excluded_by_pattern", excluded_tools: ["web.search", "web.fetch"]}
This is recorded as a warning rather than an error, and the agent continues execution without that tool.
Error 3: Node Found Without Airgap Applied
# 모든 노드의 exclude_tools 확인
euleragent pattern compile ops_policy_compliant.yaml | \
python3 -c "import json,sys; d=json.load(sys.stdin); [print(k, d['nodes'][k]['runner'].get('exclude_tools',[])) for k in d['nodes']]"
If some nodes are missing exclude_tools, the airgap is not complete.
Next Steps
You have learned about airgap patterns and Ops design. Now we move on to the most complex topology -- two independent Judge cycles.
- Next Tutorial: 09_advanced_patterns.md -- Dual Judge, Complex Topology, Pattern Deployment
- Full Reference: 10_reference.md -- All Fields, Error Codes, Checklists