interrupt_on 参数配置哪些工具需要审批。
基本配置
interrupt_on 参数接受一个将工具名称映射到中断配置的字典。每个工具可以配置为:
True:以默认行为启用中断(允许审批、编辑、拒绝)False:禁用该工具的中断{"allowed_decisions": [...]}:使用特定允许决策的自定义配置
Copy
from langchain.tools import tool
from deepagents import create_deep_agent
from langgraph.checkpoint.memory import MemorySaver
@tool
def delete_file(path: str) -> str:
"""Delete a file from the filesystem."""
return f"Deleted {path}"
@tool
def read_file(path: str) -> str:
"""Read a file from the filesystem."""
return f"Contents of {path}"
@tool
def send_email(to: str, subject: str, body: str) -> str:
"""Send an email."""
return f"Sent email to {to}"
# Checkpointer is REQUIRED for human-in-the-loop
checkpointer = MemorySaver()
agent = create_deep_agent(
model="claude-sonnet-4-6",
tools=[delete_file, read_file, send_email],
interrupt_on={
"delete_file": True, # Default: approve, edit, reject
"read_file": False, # No interrupts needed
"send_email": {"allowed_decisions": ["approve", "reject"]}, # No editing
},
checkpointer=checkpointer # Required!
)
决策类型
allowed_decisions 列表控制人工审查工具调用时可以采取的操作:
"approve":使用 agent 提出的原始参数执行工具"edit":在执行前修改工具参数"reject":完全跳过执行此工具调用
Copy
interrupt_on = {
# 敏感操作:允许所有选项
"delete_file": {"allowed_decisions": ["approve", "edit", "reject"]},
# 中等风险:仅允许审批或拒绝
"write_file": {"allowed_decisions": ["approve", "reject"]},
# 必须审批(不允许拒绝)
"critical_operation": {"allowed_decisions": ["approve"]},
}
处理中断
触发中断时,agent 暂停执行并返回控制权。检查结果中的中断并相应处理。Copy
import uuid
from langgraph.types import Command
# 使用 thread_id 创建配置以持久化状态
config = {"configurable": {"thread_id": str(uuid.uuid4())}}
# 调用 agent
result = agent.invoke({
"messages": [{"role": "user", "content": "Delete the file temp.txt"}]
}, config=config)
# 检查执行是否被中断
if result.get("__interrupt__"):
# 提取中断信息
interrupts = result["__interrupt__"][0].value
action_requests = interrupts["action_requests"]
review_configs = interrupts["review_configs"]
# 创建从工具名称到审查配置的查找映射
config_map = {cfg["action_name"]: cfg for cfg in review_configs}
# 向用户显示待处理的操作
for action in action_requests:
review_config = config_map[action["name"]]
print(f"Tool: {action['name']}")
print(f"Arguments: {action['args']}")
print(f"Allowed decisions: {review_config['allowed_decisions']}")
# 获取用户决策(每个 action_request 一个,按顺序)
decisions = [
{"type": "approve"} # 用户批准了删除操作
]
# 使用决策恢复执行
result = agent.invoke(
Command(resume={"decisions": decisions}),
config=config # 必须使用相同的 config!
)
# 处理最终结果
print(result["messages"][-1].content)
多个工具调用
当 agent 调用多个需要审批的工具时,所有中断会被批量合并到单个中断中。您必须按顺序为每个工具提供决策。Copy
config = {"configurable": {"thread_id": str(uuid.uuid4())}}
result = agent.invoke({
"messages": [{
"role": "user",
"content": "Delete temp.txt and send an email to admin@example.com"
}]
}, config=config)
if result.get("__interrupt__"):
interrupts = result["__interrupt__"][0].value
action_requests = interrupts["action_requests"]
# 两个工具需要审批
assert len(action_requests) == 2
# 按照 action_requests 的相同顺序提供决策
decisions = [
{"type": "approve"}, # 第一个工具:delete_file
{"type": "reject"} # 第二个工具:send_email
]
result = agent.invoke(
Command(resume={"decisions": decisions}),
config=config
)
编辑工具参数
当allowed_decisions 中包含 "edit" 时,您可以在执行前修改工具参数:
Copy
if result.get("__interrupt__"):
interrupts = result["__interrupt__"][0].value
action_request = interrupts["action_requests"][0]
# 来自 agent 的原始参数
print(action_request["args"]) # {"to": "everyone@company.com", ...}
# 用户决定编辑收件人
decisions = [{
"type": "edit",
"edited_action": {
"name": action_request["name"], # 必须包含工具名称
"args": {"to": "team@company.com", "subject": "...", "body": "..."}
}
}]
result = agent.invoke(
Command(resume={"decisions": decisions}),
config=config
)
子 agent 中断
使用子 agent 时,您可以在工具调用上和工具调用内使用中断。工具调用上的中断
每个子 agent 可以有自己的interrupt_on 配置,覆盖主 agent 的设置:
Copy
agent = create_deep_agent(
tools=[delete_file, read_file],
interrupt_on={
"delete_file": True,
"read_file": False,
},
subagents=[{
"name": "file-manager",
"description": "Manages file operations",
"system_prompt": "You are a file management assistant.",
"tools": [delete_file, read_file],
"interrupt_on": {
# 覆盖:在此子 agent 中对读取操作也要求审批
"delete_file": True,
"read_file": True, # 与主 agent 不同!
}
}],
checkpointer=checkpointer
)
__interrupt__ 并使用 Command 恢复。
工具调用内的中断
子 agent 工具可以直接调用interrupt() 来暂停执行并等待审批:
Copy
from langchain.agents import create_agent
from langchain_anthropic import ChatAnthropic
from langchain.messages import HumanMessage
from langchain.tools import tool
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import Command, interrupt
from deepagents.graph import create_deep_agent
from deepagents.middleware.subagents import CompiledSubAgent
@tool(description="Request human approval before proceeding with an action.")
def request_approval(action_description: str) -> str:
"""Request human approval using the interrupt() primitive."""
# interrupt() pauses execution and returns the value passed to Command(resume=...)
approval = interrupt({
"type": "approval_request",
"action": action_description,
"message": f"Please approve or reject: {action_description}",
})
if approval.get("approved"):
return f"Action '{action_description}' was APPROVED. Proceeding..."
else:
return f"Action '{action_description}' was REJECTED. Reason: {approval.get('reason', 'No reason provided')}"
def main():
checkpointer = InMemorySaver()
model = ChatAnthropic(
model_name="claude-sonnet-4-6",
max_tokens=4096,
)
compiled_subagent = create_agent(
model=model,
tools=[request_approval],
name="approval-agent",
)
parent_agent = create_deep_agent(
checkpointer=checkpointer,
subagents=[
CompiledSubAgent(
name="approval-agent",
description="An agent that can request approvals",
runnable=compiled_subagent,
)
],
)
thread_id = "test_interrupt_directly"
config = {"configurable": {"thread_id": thread_id}}
print("Invoking agent - sub-agent will use request_approval tool...")
result = parent_agent.invoke(
{
"messages": [
HumanMessage(
content="Use the task tool to launch the approval-agent sub-agent. "
"Tell it to use the request_approval tool to request approval for 'deploying to production'."
)
]
},
config=config,
)
# Check for interrupt
if result.get("__interrupt__"):
interrupt_value = result["__interrupt__"][0].value
print(f"\nInterrupt received!")
print(f" Type: {interrupt_value.get('type')}")
print(f" Action: {interrupt_value.get('action')}")
print(f" Message: {interrupt_value.get('message')}")
print("\nResuming with Command(resume={'approved': True})...")
result2 = parent_agent.invoke(
Command(resume={"approved": True}),
config=config,
)
if "__interrupt__" not in result2:
print("\nExecution completed!")
# Find the tool response
tool_msgs = [m for m in result2.get("messages", []) if m.type == "tool"]
if tool_msgs:
print(f" Tool result: {tool_msgs[-1].content}")
else:
print("\nAnother interrupt occurred")
else:
print("\n No interrupt - the model may not have called request_approval")
if __name__ == "__main__":
main()
Copy
Invoking agent - sub-agent will use request_approval tool...
Interrupt received!
Type: approval_request
Action: deploying to production
Message: Please approve or reject: deploying to production
Resuming with Command(resume={'approved': True})...
Execution completed!
Tool result: Great! The approval request has been processed. The action **"deploying to production"** was **APPROVED**. You can now proceed with the production deployment.
最佳实践
始终使用 checkpointer
Human-in-the-loop 需要 checkpointer 在中断和恢复之间持久化 agent 状态:Copy
from langgraph.checkpoint.memory import MemorySaver
checkpointer = MemorySaver()
agent = create_deep_agent(
tools=[...],
interrupt_on={...},
checkpointer=checkpointer # HITL 所必需
)
使用相同的 thread ID
恢复时,必须使用相同thread_id 的相同 config:
Copy
# 第一次调用
config = {"configurable": {"thread_id": "my-thread"}}
result = agent.invoke(input, config=config)
# 恢复(使用相同的 config)
result = agent.invoke(Command(resume={...}), config=config)
决策顺序与操作顺序一致
decisions 列表必须与action_requests 的顺序匹配:
Copy
if result.get("__interrupt__"):
interrupts = result["__interrupt__"][0].value
action_requests = interrupts["action_requests"]
# 按顺序为每个操作创建一个决策
decisions = []
for action in action_requests:
decision = get_user_decision(action) # 您的逻辑
decisions.append(decision)
result = agent.invoke(
Command(resume={"decisions": decisions}),
config=config
)
根据风险级别定制配置
根据工具的风险级别配置不同的中断策略:Copy
interrupt_on = {
# 高风险:完全控制(审批、编辑、拒绝)
"delete_file": {"allowed_decisions": ["approve", "edit", "reject"]},
"send_email": {"allowed_decisions": ["approve", "edit", "reject"]},
# 中等风险:不允许编辑
"write_file": {"allowed_decisions": ["approve", "reject"]},
# 低风险:无中断
"read_file": False,
"list_files": False,
}
Connect these docs to Claude, VSCode, and more via MCP for real-time answers.

