23장 · Human-in-the-Loop왜 사람이 끼어들어야 하는가 — 되돌릴 수 없는 일 앞에서AI의 일과 사람의 일 — 준비는 코드와 AI가, 결정은 사람이interrupt — 그래프를 멈추고 이어 가기요청서 — 사람이 바로 판단하게 만든다따라하기 — 환불 승인 게이트정리와 체크리스트실습문제와 해답
23장. Human-in-the-Loop
정리와 체크리스트
23장에서 배운 것
- Human-in-the-Loop(HITL) — 자동으로 흘러가는 처리의 결정 지점에 사람을 끼워 넣는 설계입니다.
- 승인 게이트 — 되돌릴 수 없는 실행 앞에서 사람의 승인이나 반려를 기다리는 자리입니다.
- 코드·AI·사람의 일 — 코드는 기간 판정, 승인 요청 등록(
request_refund), 결과 기록(decide)을, AI는 사유 분류와 요청서 쓰기를, 사람은 승인·반려를 맡습니다. - 에스컬레이션 조건 — 어떤 건을 사람에게 올려 보낼지 정하는 규칙. 요건 미달은 게이트 앞에서 거릅니다.
interrupt— 그래프를 멈추고 값을 밖으로 내보냅니다.- 체크포인터와
thread_id— 멈춘 상태를 저장하고, 어느 실행을 이어 갈지 가리킵니다. Command(resume=값)— 멈춘 곳부터 이어서 실행합니다. 그 값이interrupt()의 반환값이 됩니다.- 요청서 — 사람이 한 화면으로 판단할 수 있게, 값 네 가지와 코드가 붙인
[시스템 확인]줄을 담습니다. - 처리 기록의 상태 —
승인대기로 등록되고, 사람이 결정한 뒤에만승인완료또는반려가 됩니다.
한눈에 보는 핵심
# 검토 노드 — 판정과 등록은 코드가
classification = classify_reason(state["question"])
req = actions["request_refund"](
state["order_id"], state["question"], classification.reason_category)
# 멈추는 쪽 (노드 안)
decision = interrupt({"action_id": state["action_id"], "review": state["review"],
"ask": "이 환불을 실행할까요?"})
return {"approved": decision == "approve"}
# 사람의 결정을 기록하는 쪽
done = decide(state["action_id"], approve=state["approved"], by="상담원") # 승인완료 / 반려
# 조립할 때 — 체크포인터가 있어야 멈출 수 있다
graph = builder.compile(checkpointer=InMemorySaver())
# 이어 가는 쪽 (그래프 밖) — 같은 thread_id
config = {"configurable": {"thread_id": "refund-C003-001"}}
result = graph.invoke(처음_상태, config=config) # 멈춘다
result = graph.invoke(Command(resume="approve"), config=config) # 이어 간다
| 누가 | 하는 일 | 코드에서 |
|---|---|---|
| 코드 | 기간 판정, 승인 요청 등록, 결과 기록 | request_refund, decide |
| AI | 사유 분류, 상담원이 볼 요청서 작성 | review_node의 LLM 호출 |
| 사람 | 승인·반려 | Command(resume="approve") 또는 "reject" |
| 사람에게 넘기는 길 | 언제 | 사람이 하는 일 |
|---|---|---|
| 티켓 (8장, 21장) | AI가 처리할 수 없을 때, 사람과 상담을 원할 때 | 넘겨받아 처리한다 |
| 승인 게이트 (23장) | 준비는 끝났지만 되돌릴 수 없는 실행일 때 | 요청서를 확인하고 승인·반려한다 |
체크리스트
- [ ] 요청처리 에이전트가 환불·주문 취소를 승인 요청까지만 만드는 이유를 설명할 수 있다.
- [ ] 티켓으로 넘기는 것과 승인 게이트의 차이를 말할 수 있다.
- [ ] 환불 한 건에서 코드의 일, AI의 일, 사람의 일을 나눠 말할 수 있다.
- [ ] 요건 미달 건을 사람에게 보내지 않는 이유를 설명할 수 있다.
- [ ] 기간 판정을 모델이 아니라 코드가 하게 한 이유를 설명할 수 있다.
- [ ]
decide가 모델의 도구 목록에 없다는 것이 왜 중요한지 말할 수 있다. - [ ]
interrupt, 체크포인터,thread_id,Command(resume=…)가 각각 무엇을 하는지 안다. - [ ] 이어 갈 때 노드가 처음부터 다시 실행된다는 것과, 그래서 검토와 승인을 나눈 이유를 설명할 수 있다.
- [ ]
InMemorySaver가 상태를 어디에 저장하는지, 오래 기다리려면 무엇을 바꾸는지 말할 수 있다. - [ ] 따라하기의 요청서에서 AI가 쓴 줄과 코드가 붙인 줄을 가려낼 수 있다.
- [ ]
actions.json에서승인대기가승인완료로 바뀌는 것을 실행 결과로 확인했다. - [ ] 기간이 지난 주문이 승인 게이트까지 가지 않은 것을 실행 결과로 확인했다.
한 줄 핵심
코드가 판정하고 기록하고, AI가 정리하고, 되돌릴 수 없는 결정은 사람이 내립니다. 사람이 제대로 결정하려면 결론이 아니라 재료가 보여야 합니다.