Human-in-the-Loop (HITL) 미들웨어를 사용하면 에이전트 도구 호출에 사람의 감독을 추가할 수 있습니다.
모델이 검토가 필요할 수 있는 작업(예: 파일 쓰기 또는 SQL 실행)을 제안하면, 미들웨어가 실행을 일시 중지하고 결정을 기다릴 수 있습니다.
이는 각 도구 호출을 구성 가능한 정책과 비교하여 확인하는 방식으로 작동합니다. 개입이 필요한 경우, 미들웨어는 실행을 중단하는 interrupt를 발행합니다. 그래프 상태는 LangGraph의 persistence layer를 사용하여 저장되므로, 실행을 안전하게 일시 중지하고 나중에 재개할 수 있습니다.
그런 다음 사람의 결정이 다음에 일어날 일을 결정합니다: 작업을 있는 그대로 승인(approve)하거나, 실행 전에 수정(edit)하거나, 피드백과 함께 거부(reject)할 수 있습니다.
Interrupt decision types
미들웨어는 사람이 인터럽트에 응답할 수 있는 세 가지 기본 제공 방법을 정의합니다:
| Decision Type | Description | Example Use Case |
|---|---|---|
✅ approve |
작업이 있는 그대로 승인되어 변경 없이 실행됩니다. | 작성된 그대로 이메일 초안을 정확히 보내기 |
✏️ edit |
도구 호출이 수정과 함께 실행됩니다. | 이메일을 보내기 전에 수신자 변경 |
❌ reject |
도구 호출이 거부되며, 설명이 대화에 추가됩니다. | 이메일 초안을 거부하고 다시 작성하는 방법 설명 |
각 도구에 사용 가능한 결정 유형은 interrupt_on에서 구성한 정책에 따라 달라집니다.
여러 도구 호출이 동시에 일시 중지되면, 각 작업에는 별도의 결정이 필요합니다.
결정은 인터럽트 요청에 나타나는 작업 순서와 동일한 순서로 제공되어야 합니다.
도구 인수를 편집할 때는 신중하게 변경하세요. 원래 인수를 크게 수정하면 모델이 접근 방식을 재평가하여 도구를 여러 번 실행하거나 예기치 않은 작업을 수행할 수 있습니다.
Configuring interrupts
HITL을 사용하려면 에이전트를 생성할 때 에이전트의 middleware 목록에 미들웨어를 추가합니다.
각 작업에 허용되는 결정 유형에 대한 도구 작업 매핑으로 구성합니다. 미들웨어는 도구 호출이 매핑의 작업과 일치할 때 실행을 중단합니다.
from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langgraph.checkpoint.memory import InMemorySaver
agent = create_agent(
model="gpt-4o",
tools=[write_file_tool, execute_sql_tool, read_data_tool],
middleware=[
HumanInTheLoopMiddleware(
interrupt_on={
"write_file": True, # All decisions (approve, edit, reject) allowed
"execute_sql": {"allowed_decisions": ["approve", "reject"]}, # No editing allowed
# Safe operation, no approval needed
"read_data": False,
},
# Prefix for interrupt messages - combined with tool name and args to form the full message
# e.g., "Tool execution pending approval: execute_sql with query='DELETE FROM...'"
# Individual tools can override this by specifying a "description" in their interrupt config
description_prefix="Tool execution pending approval",
),
],
# Human-in-the-loop requires checkpointing to handle interrupts.
# In production, use a persistent checkpointer like AsyncPostgresSaver.
checkpointer=InMemorySaver(),
)
인터럽트 전반에 걸쳐 그래프 상태를 유지하려면 checkpointer를 구성해야 합니다.
프로덕션 환경에서는AsyncPostgresSaver와 같은 영구 checkpointer를 사용하세요. 테스트 또는 프로토타이핑의 경우InMemorySaver를 사용하세요.에이전트를 호출할 때, 실행을 대화 스레드와 연결하기 위해 thread ID를 포함하는
config를 전달합니다.
자세한 내용은 LangGraph interrupts documentation을 참조하세요.
Responding to interrupts
에이전트를 호출하면 완료되거나 인터럽트가 발생할 때까지 실행됩니다. 인터럽트는 도구 호출이 interrupt_on에서 구성한 정책과 일치할 때 트리거됩니다. 이 경우 호출 결과에는 검토가 필요한 작업이 포함된 __interrupt__ 필드가 포함됩니다. 그런 다음 해당 작업을 검토자에게 제시하고 결정이 제공되면 실행을 재개할 수 있습니다.
from langgraph.types import Command
# Human-in-the-loop leverages LangGraph's persistence layer.
# You must provide a thread ID to associate the execution with a conversation thread,
# so the conversation can be paused and resumed (as is needed for human review).
config = {"configurable": {"thread_id": "some_id"}}
# Run the graph until the interrupt is hit.
result = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "Delete old records from the database",
}
]
},
config=config
)
# The interrupt contains the full HITL request with action_requests and review_configs
print(result['__interrupt__'])
# > [
# > Interrupt(
# > value={
# > 'action_requests': [
# > {
# > 'name': 'execute_sql',
# > 'arguments': {'query': 'DELETE FROM records WHERE created_at < NOW() - INTERVAL \'30 days\';'},
# > 'description': 'Tool execution pending approval\n\nTool: execute_sql\nArgs: {...}'
# > }
# > ],
# > 'review_configs': [
# > {
# > 'action_name': 'execute_sql',
# > 'allowed_decisions': ['approve', 'reject']
# > }
# > ]
# > }
# > )
# > ]
# Resume with approval decision
agent.invoke(
Command(
resume={"decisions": [{"type": "approve"}]} # or "edit", "reject"
),
config=config # Same thread ID to resume the paused conversation
)
Decision types
✅ approve
approve를 사용하여 도구 호출을 있는 그대로 승인하고 변경 없이 실행합니다.
agent.invoke(
Command(
# Decisions are provided as a list, one per action under review.
# The order of decisions must match the order of actions
# listed in the `__interrupt__` request.
resume={
"decisions": [
{
"type": "approve",
}
]
}
),
config=config # Same thread ID to resume the paused conversation
)
✏️ edit
edit를 사용하여 수정된 인수로 도구 호출을 실행합니다.
agent.invoke(
Command(
resume={
"decisions": [
{
"type": "edit",
"tool_call": {
"name": "execute_sql",
"arguments": {
"query": "DELETE FROM records WHERE created_at < NOW() - INTERVAL '90 days';"
}
}
}
]
}
),
config=config
)
❌ reject
reject를 사용하여 도구 호출을 거부하고 거부 이유를 포함합니다. 거부 메시지는 대화에 추가되어 모델이 다른 접근 방식을 시도할 수 있습니다.
agent.invoke(
Command(
resume={
"decisions": [
{
"type": "reject",
"rejection_message": "Cannot delete records. Please archive them instead."
}
]
}
),
config=config
)
여러 도구 호출이 검토 중인 경우, __interrupt__ 요청에 나열된 작업 순서와 동일한 순서로 각 작업에 대한 결정을 제공해야 합니다.
agent.invoke(
Command(
resume={
"decisions": [
{"type": "approve"}, # First action
{"type": "reject", "rejection_message": "Not authorized"}, # Second action
{"type": "edit", "tool_call": {...}} # Third action
]
}
),
config=config
)
Execution lifecycle
미들웨어는 모델이 응답을 생성한 후 도구 호출이 실행되기 전에 실행되는 after_model 후크를 정의합니다:
- 에이전트가 모델을 호출하여 응답을 생성합니다.
- 미들웨어가 응답에서 도구 호출을 검사합니다.
- 호출에 사람의 입력이 필요한 경우, 미들웨어는
action_requests및review_configs와 함께HITLRequest를 구축하고interrupt를 호출합니다. - 에이전트가 사람의 결정을 기다립니다.
HITLResponse결정에 따라, 미들웨어는 승인되거나 편집된 호출을 실행하고, 거부된 호출에 대한ToolMessage를 합성하고, 실행을 재개합니다.
Custom HITL logic
더 전문화된 워크플로우의 경우, interrupt primitive 및 middleware 추상화를 직접 사용하여 사용자 정의 HITL 로직을 구축할 수 있습니다.
인터럽트를 에이전트의 작업에 통합하는 방법을 이해하려면 위의 execution lifecycle을 검토하세요.
출처: https://docs.langchain.com/oss/python/langchain/human-in-the-loop
Langchain v1.0
- LangChain 개요
- LangChain v1
- LangChain v1 마이그레이션 가이드
- LangChain 설치
- QuickStart
- Philosophy
- Agents
- Models
- Messages
- Tools
- Short-term memory
- Streaming
- Middleware
- Structured output
- Guardrails
- Runtime
- Context Engineering
- Model Context Protocol (MCP)
Human-in-the-loop- Multi-agent
- Retrieval
- Long-term memory
- Studio
- Test
- Deploy
- Agent Chat UI
- Observability
