Graph 05. Interrupt Hooks — Pausing Execution Before and After Nodes
Learning Objectives
After completing this tutorial, you will be able to:
- Use
interrupt_before: trueto pause the graph before a specific node executes. - Use
interrupt_after: trueto pause the graph after a specific node executes. - Clearly explain the difference between the HITL
force_toolapproach and the interrupt approach. - Write patterns that use both interrupts simultaneously.
- Verify interrupt_before/after settings in the IR using
graph compile. - Determine appropriate use cases for interrupt hooks.
Prerequisites
mkdir -p examples/graphs/hooks
# LangGraph 체크포인트 기능 확인
python -c "from langgraph.graph import StateGraph; print('LangGraph StateGraph OK')"
Expected output:
LangGraph StateGraph OK
What Are Interrupt Hooks?
Interrupt hooks leverage LangGraph's checkpoint mechanism to pause the graph before or after a specific node executes.
Two Types of Interrupts
| Type | Declaration | Pause Timing | State Snapshot |
|---|---|---|---|
interrupt_before |
interrupt_before: true |
Immediately before the node executes | State up to the previous node |
interrupt_after |
interrupt_after: true |
Immediately after the node executes | State after the node completes |
노드 실행 타임라인:
interrupt_before: true 선언 시:
이전_노드 → [일시 정지] → 해당_노드 → 다음_노드
interrupt_after: true 선언 시:
이전_노드 → 해당_노드 → [일시 정지] → 다음_노드
HITL force_tool vs interrupt_before/after
Both approaches create opportunities for human intervention, but they differ in mechanism and purpose.
| Aspect | HITL force_tool | interrupt_before/after |
|---|---|---|
| Mechanism | euleragent file-based JSONL approval queue | LangGraph checkpoint |
| Purpose | Approval before specific tool usage | State inspection and resumption before/after nodes |
| Persistence | Saved in approval queue file | LangGraph checkpointer (memory/DB) |
| Resume method | euleragent approve |
LangGraph graph.invoke(state) |
| Use cases | Approving web searches, file writes, shell execution | Critical node state inspection, debugging |
| Pattern support | O | X (Graph only) |
When to Use Interrupts
interrupt_before 권장 시나리오:
- publish 노드 전: "정말 배포하시겠습니까?"
- payment 노드 전: "결제를 진행하시겠습니까?"
- email_send 노드 전: "이 이메일을 발송하시겠습니까?"
- 고위험 API 호출 전: 상태 검사
interrupt_after 권장 시나리오:
- research 노드 후: 수집된 데이터 검토
- draft 노드 후: 초안 검토 후 수정 여부 결정
- 디버깅: 중간 상태를 로그로 확인
Step-by-Step Walkthrough
Step 1: interrupt_before — human_review Node
Set up an interrupt so a human can review before the critical publish step.
# examples/graphs/hooks/interrupt_before.yaml
id: graph.interrupt_before_demo
version: 1
category: demo
description: publish 노드 전 interrupt_before로 사람 검토
checkpointer: memory # ← interrupt 사용 시 필수. 미선언 시 INTERRUPT_REQUIRES_CHECKPOINTER 오류
defaults:
max_iterations: 2
max_total_tool_calls: 20
nodes:
- id: research
kind: llm
runner:
mode: plan
force_tool: web.search
guardrails:
tool_call_budget:
web.search: 3
- id: draft
kind: llm
runner:
mode: execute
artifacts:
primary: article.md
- id: human_review
kind: llm
interrupt_before: true # ← 이 노드 실행 전에 그래프 일시 정지
runner:
mode: execute
artifacts:
primary: review_notes.md
- id: publish
kind: llm
runner:
mode: execute
artifacts:
primary: published.md
edges:
- from: research
to: draft
when: "approvals_resolved"
- from: draft
to: human_review
when: "true"
- from: human_review
to: publish
when: "true"
- from: publish
to: finalize
when: "true"
finalize:
artifact: published.md
euleragent graph validate examples/graphs/hooks/interrupt_before.yaml
Expected output:
단계 3/3: Graph 추가 검증...
[✓] interrupt_before: human_review (1개 노드)
[✓] interrupt_after: 없음
완료
결과: 유효 ✓
참고: interrupt_before가 설정된 노드: [human_review]
실행 시 LangGraph 체크포인터가 필요합니다.
Step 2: Verify interrupt_before in IR with graph compile
euleragent graph compile examples/graphs/hooks/interrupt_before.yaml \
--out /tmp/interrupt_before_ir.json
# IR에서 interrupt 관련 필드 확인
python -m json.tool /tmp/interrupt_before_ir.json | \
python -c "
import sys, json
d = json.load(sys.stdin)
# 각 노드의 interrupt 설정 확인
for node in d['nodes']:
if node.get('interrupt_before') or node.get('interrupt_after'):
print(f\"노드 '{node['id']}': interrupt_before={node['interrupt_before']}, interrupt_after={node['interrupt_after']}\")
# langgraph_builder의 interrupt 설정 확인
print('langgraph_builder.interrupt_before:', d['langgraph_builder']['interrupt_before'])
print('langgraph_builder.interrupt_after:', d['langgraph_builder']['interrupt_after'])
"
Expected output:
노드 'human_review': interrupt_before=True, interrupt_after=False
langgraph_builder.interrupt_before: ['human_review']
langgraph_builder.interrupt_after: []
Check the interrupt_before and interrupt_after lists in the IR's langgraph_builder section.
These lists are used when calling LangGraph's graph.compile(interrupt_before=[...], interrupt_after=[...]).
{
"langgraph_builder": {
"add_nodes": ["research", "draft", "human_review", "publish", "finalize"],
"add_edges": [...],
"interrupt_before": ["human_review"],
"interrupt_after": []
}
}
At runtime, this materializes into the following LangGraph code.
# euleragent 내부 생성 코드 (개념적)
compiled = graph.compile(
checkpointer=checkpointer, # 상태 저장용 체크포인터
interrupt_before=["human_review"] # human_review 전에 일시 정지
)
Step 3: interrupt_after — Reviewing After the draft Node
Use interrupt_after when you want to review the generated draft after the draft node completes.
# examples/graphs/hooks/interrupt_after.yaml
id: graph.interrupt_after_demo
version: 1
category: demo
description: draft 노드 후 interrupt_after로 초안 검토
checkpointer: memory # ← interrupt 사용 시 필수
defaults:
max_iterations: 3
max_total_tool_calls: 25
nodes:
- id: research
kind: llm
runner:
mode: execute
- id: draft
kind: llm
interrupt_after: true # ← 이 노드 실행 후 그래프 일시 정지
runner:
mode: execute
artifacts:
primary: draft.md
- id: evaluate
kind: judge
judge:
schema: evaluator_v1
route_values: [finalize, revise]
- id: revise
kind: llm
runner:
mode: execute
artifacts:
primary: draft.md
edges:
- from: research
to: draft
when: "true"
- from: draft
to: evaluate
when: "true"
- from: evaluate
to: finalize
when: "judge.route == finalize"
- from: evaluate
to: revise
when: "judge.route == revise"
- from: revise
to: evaluate
when: "true"
finalize:
artifact: draft.md
euleragent graph validate examples/graphs/hooks/interrupt_after.yaml
Expected output:
결과: 유효 ✓
참고: interrupt_after가 설정된 노드: [draft]
Here is the IR representation of the draft node with interrupt_after: true.
{
"id": "draft",
"kind": "llm",
"interrupt_before": false,
"interrupt_after": true,
"runner": { "mode": "execute" },
"artifacts": { "primary": "draft.md" }
}
Step 4: Using Both Interrupts Simultaneously
Use interrupt_after: true (after draft) and interrupt_before: true (before publish) together.
This creates two intervention points: one for reviewing the completed draft, and one for final confirmation before deployment.
# examples/graphs/hooks/interrupt_both.yaml
id: graph.interrupt_both_demo
version: 1
category: demo
description: draft 후 + publish 전 두 번의 interrupt
checkpointer: memory # ← interrupt 사용 시 필수
defaults:
max_iterations: 2
max_total_tool_calls: 30
nodes:
- id: research
kind: llm
runner:
mode: execute
- id: draft
kind: llm
interrupt_after: true # ← 초안 완성 후 검토 기회
runner:
mode: execute
artifacts:
primary: content.md
- id: evaluate
kind: judge
judge:
schema: evaluator_v1
route_values: [finalize, revise]
- id: revise
kind: llm
runner:
mode: execute
artifacts:
primary: content.md
- id: prepare_publish
kind: llm
runner:
mode: execute
artifacts:
primary: publish_ready.md
- id: publish
kind: llm
interrupt_before: true # ← 배포 직전 최종 확인
runner:
mode: execute
artifacts:
primary: published.md
edges:
- from: research
to: draft
when: "true"
- from: draft
to: evaluate
when: "true"
- from: evaluate
to: prepare_publish
when: "judge.route == finalize"
- from: evaluate
to: revise
when: "judge.route == revise"
- from: revise
to: evaluate
when: "true"
- from: prepare_publish
to: publish
when: "true"
- from: publish
to: finalize
when: "true"
finalize:
artifact: published.md
euleragent graph validate examples/graphs/hooks/interrupt_both.yaml
Expected output:
단계 3/3: Graph 추가 검증...
[✓] interrupt_before: publish (1개 노드)
[✓] interrupt_after: draft (1개 노드)
[✓] 두 interrupt 호환성 검사 통과
(같은 노드에 interrupt_before와 interrupt_after 동시 선언 없음)
완료
결과: 유효 ✓
interrupt_after: [draft]
interrupt_before: [publish]
euleragent graph compile examples/graphs/hooks/interrupt_both.yaml \
--out /tmp/interrupt_both_ir.json
python -m json.tool /tmp/interrupt_both_ir.json | \
python -c "
import sys, json
d = json.load(sys.stdin)
lb = d['langgraph_builder']
print('interrupt_before:', lb['interrupt_before'])
print('interrupt_after:', lb['interrupt_after'])
"
Expected output:
interrupt_before: ['publish']
interrupt_after: ['draft']
Both values are passed to the LangGraph graph.compile() call.
# LangGraph 내부 코드 (개념적)
compiled = graph.compile(
checkpointer=checkpointer,
interrupt_before=["publish"],
interrupt_after=["draft"]
)
Step 5: Comparing Interrupt and HITL force_tool
Compare both approaches side by side for achieving the same goal (human intervention).
# HITL force_tool 방식 (Pattern 및 Graph 모두 지원)
nodes:
- id: research
kind: llm
runner:
mode: plan # plan 모드에서 tool 사용 계획 제안
force_tool: web.search # 반드시 web.search 도구 사용
# 실행 흐름:
# 1. LLM이 web.search 계획 제안
# 2. euleragent가 approvals.jsonl에 승인 요청 기록
# 3. 사람이 euleragent approve 명령으로 승인
# 4. 승인 후 web.search 실행
# 5. approvals_resolved 조건이 충족됨
# interrupt_before 방식 (Graph 전용)
nodes:
- id: publish
kind: llm
interrupt_before: true # LangGraph 체크포인트로 일시 정지
runner:
mode: execute
# 실행 흐름:
# 1. LangGraph가 publish 노드 직전에 실행 중단
# 2. LangGraph 상태가 체크포인터에 저장
# 3. 사람이 상태 확인 후 graph.invoke()로 재개
# 4. publish 노드 실행 계속
Expected Output Summary
| Command | Expected Result |
|---|---|
graph validate interrupt_before.yaml |
Valid, interrupt_before: [human_review] |
graph compile interrupt_before.yaml |
IR interrupt_before: ["human_review"] |
graph validate interrupt_after.yaml |
Valid, interrupt_after: [draft] |
graph validate interrupt_both.yaml |
Valid, both interrupts confirmed |
graph compile interrupt_both.yaml |
IR with interrupt_before: ["publish"], interrupt_after: ["draft"] |
Key Concepts Summary
| Concept | Description |
|---|---|
interrupt_before: true |
Pauses at LangGraph checkpoint before the node executes |
interrupt_after: true |
Pauses at LangGraph checkpoint after the node executes |
| Checkpointer | LangGraph component that persists the paused graph state |
| HITL force_tool | euleragent file-based approval queue (Pattern + Graph) |
| Interrupt | LangGraph checkpoint-based pause (Graph only) |
graph.compile(interrupt_before=[...]) |
LangGraph API where IR interrupt settings are mapped to |
Common Errors
Error 1: Declaring interrupt_before + interrupt_after on the Same Node
- id: critical_node
kind: llm
interrupt_before: true # ← 전
interrupt_after: true # ← 후 동시에
runner:
mode: execute
경고: INTERRUPT_DOUBLE_DECLARED
노드 'critical_node'에 interrupt_before와 interrupt_after가 모두 true입니다.
이 노드는 실행 전과 후 두 번 일시 정지됩니다.
의도한 동작인지 확인하세요.
This is a warning, not an error. If this is intentional (reviewing both before and after execution), you can safely ignore it.
Error 2: Attempting to Use Interrupt in a Pattern
# Pattern validate로 interrupt가 있는 Graph YAML 검증
euleragent pattern validate examples/graphs/hooks/interrupt_before.yaml
경고: UNKNOWN_NODE_FIELD
노드 'human_review'에 알 수 없는 필드: interrupt_before
Pattern은 interrupt_before/after를 지원하지 않습니다.
Graph 모듈에서 사용하세요: euleragent graph validate
Error 3: Using Interrupt Without a Checkpointer Declaration
Graphs that use interrupt_before/interrupt_after must have a checkpointer: field
at the top level. Without it, an error occurs immediately during the graph validate step.
# BAD — checkpointer 없이 interrupt 사용
id: my_graph
version: 1
description: ...
# checkpointer: MISSING ← 이 줄이 없으면 아래 오류 발생
nodes:
- id: review
kind: llm
interrupt_before: true # ← 검증 시 INTERRUPT_REQUIRES_CHECKPOINTER 발생
runner: {mode: execute}
오류: INTERRUPT_REQUIRES_CHECKPOINTER
노드 'review'가 interrupt_before/interrupt_after를 선언했지만
최상위에 checkpointer가 구성되지 않았습니다.
LangGraph는 브레이크포인트에서 일시 정지 후 재개하기 위해
체크포인터가 필요합니다.
최상위에 'checkpointer: true' (또는 'checkpointer: memory') 를 추가하세요.
Fix:
# GOOD — checkpointer 선언
id: my_graph
version: 1
description: ...
checkpointer: memory # ← 추가
nodes:
- id: review
kind: llm
interrupt_before: true
runner: {mode: execute}
Available values: true (default memory checkpointer), "memory", "sqlite".
If persistent storage is needed, use "sqlite" and configure the path in the euleragent runtime environment.
Error 4: Declaring Interrupt on the finalize Node
finalize:
artifact: output.md
interrupt_before: true # ← finalize는 최종 노드 — interrupt 불가
오류: INTERRUPT_ON_FINALIZE
finalize 노드에는 interrupt_before/after를 선언할 수 없습니다.
interrupt는 그래프 실행 중간 노드에만 유효합니다.
finalize 전에 interrupt를 원한다면 finalize 직전 노드에 선언하세요.
Practice Exercise
Exercise: Three-Stage Review Workflow
Write a graph using interrupt hooks with the following specification.
- Declare
checkpointer: memoryat the top level (required) - gather node: data collection (execute mode)
- analyze node: run analysis (execute mode,
interrupt_after: true-- review analysis results) - recommend node: generate recommendations (execute mode)
- send_report node: send the report (execute mode,
interrupt_before: true-- final confirmation before sending)
euleragent graph validate examples/graphs/hooks/three_stage_review.yaml
euleragent graph compile examples/graphs/hooks/three_stage_review.yaml
Verify the following in the IR:
- interrupt_after: ["analyze"]
- interrupt_before: ["send_report"]
Previous: 04_bounded_loop.md | Next: 06_state_schema.md