Human-in-the-Loop (HITL) middleware를 사용하면 에이전트 tool 호출에 사람의 감독을 추가할 수 있습니다.
모델이 검토가 필요할 수 있는 작업(예: 파일 쓰기 또는 SQL 실행)을 제안하면, middleware는 실행을 일시 중지하고 결정을 기다릴 수 있습니다.
이는 구성 가능한 정책에 따라 각 tool 호출을 확인하여 수행됩니다. 개입이 필요한 경우, middleware는 실행을 중단하는 interrupt를 발생시킵니다. Graph state는 LangGraph의 persistence layer를 사용하여 저장되므로, 실행을 안전하게 일시 중지하고 나중에 재개할 수 있습니다.
그런 다음 사람의 결정에 따라 다음 작업이 결정됩니다: 작업을 그대로 승인(approve)하거나, 실행 전에 수정(edit)하거나, 피드백과 함께 거부(reject)할 수 있습니다.
Interrupt 결정 유형
Middleware는 사람이 interrupt에 응답할 수 있는 세 가지 기본 제공 방법을 정의합니다:
| 결정 유형 | 설명 | 사용 사례 예시 |
|---|
✅ approve | 작업이 그대로 승인되어 변경 없이 실행됩니다. | 작성된 그대로 이메일 초안 보내기 |
✏️ edit | Tool 호출이 수정과 함께 실행됩니다. | 이메일을 보내기 전에 수신자 변경 |
❌ reject | Tool 호출이 거부되고, 설명이 대화에 추가됩니다. | 이메일 초안을 거부하고 다시 작성하는 방법 설명 |
각 tool에 사용 가능한 결정 유형은 interrupt_on에서 구성한 정책에 따라 다릅니다.
여러 tool 호출이 동시에 일시 중지되면, 각 작업에 대해 별도의 결정이 필요합니다.
결정은 interrupt 요청에 나타나는 작업과 동일한 순서로 제공되어야 합니다.
Tool argument를 편집할 때는 신중하게 변경하세요. 원래 argument를 크게 수정하면 모델이 접근 방식을 재평가하고 tool을 여러 번 실행하거나 예상치 못한 작업을 수행할 수 있습니다.
Interrupt 구성
HITL을 사용하려면, 에이전트를 생성할 때 에이전트의 middleware 목록에 middleware를 추가하세요.
각 작업에 허용되는 결정 유형에 대한 tool 작업의 매핑으로 구성합니다. Middleware는 tool 호출이 매핑의 작업과 일치할 때 실행을 중단합니다.
from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langgraph.checkpoint.memory import InMemorySaver
agent = create_agent(
model="openai: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(),
)
Interrupt에 응답하기
에이전트를 호출하면, 완료되거나 interrupt가 발생할 때까지 실행됩니다. Interrupt는 tool 호출이 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
)
결정 유형
✅ approve
✏️ edit
❌ reject
approve를 사용하여 tool 호출을 그대로 승인하고 변경 없이 실행하세요.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
)
실행 생명주기
Middleware는 모델이 응답을 생성한 후 tool 호출이 실행되기 전에 실행되는 after_model hook을 정의합니다:
- 에이전트가 모델을 호출하여 응답을 생성합니다.
- Middleware가 응답에서 tool 호출을 검사합니다.
- 호출 중 사람의 입력이 필요한 경우, middleware는
action_requests와 review_configs가 포함된 HITLRequest를 빌드하고 interrupt를 호출합니다.
- 에이전트가 사람의 결정을 기다립니다.
HITLResponse 결정에 따라, middleware는 승인되거나 편집된 호출을 실행하고, 거부된 호출에 대한 ToolMessage를 합성하며, 실행을 재개합니다.
사용자 정의 HITL 로직
더 전문화된 워크플로우의 경우, interrupt primitive와 middleware 추상화를 직접 사용하여 사용자 정의 HITL 로직을 구축할 수 있습니다.
Interrupt를 에이전트의 작업에 통합하는 방법을 이해하려면 위의 실행 생명주기를 검토하세요.