# 人机交互

[人机交互](https://docs.langchain.com/oss/python/langchain/human-in-the-loop)（Human-in-the-loop, HITL）指智能体为了向人类索要执行权限或额外信息而主动中断，并在获得人类反馈后继续执行的过程。

LangGraph 的人机交互功能可以通过内置中间件 HumanInTheLoopMiddleware（HITL）实现。触发人机交互后，HITL 会将当前状态保存到 [checkpointer](https://docs.langchain.com/oss/javascript/langgraph/persistence#checkpointer-libraries) 检查点中，并等待人类回复。获得回复后，再将状态从检查点中恢复出来，继续执行任务。

> **Note**
> 本例只是演示 checkpoint 在人机交互中的作用，无所谓线程结束后记忆是否保持，因此使用内存存储 `InMemorySaver`。在生产环境中，请使用持久化检查点，比如：
>
> - `SqliteSaver`
> - `PostgresSaver`
> - `MongoDBSaver`
> - `RedisSaver`

```python
import os
import uuid
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langchain.tools import tool
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import Command

# 加载模型配置
_ = load_dotenv()
```

下面使用 HITL 中间件，为三种工具配置人工审批流程。

```python
# 配置大模型服务
llm = ChatOpenAI(
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url=os.getenv("DASHSCOPE_BASE_URL"),
    model="qwen3-coder-plus",
)

# 工具函数
@tool
def get_weather(city: str) -> str:
    """Get weather for a given city."""
    return f"It's always sunny in {city}!"

@tool
def add_numbers(a: float, b: float) -> float:
    """Add two numbers and return the sum."""
    return a + b

@tool
def calculate_bmi(weight_kg: float, height_m: float) -> float:
    """Calculate BMI given weight in kg and height in meters."""
    if height_m <= 0 or weight_kg <= 0:
        raise ValueError("height_m and weight_kg must be greater than 0.")
    return weight_kg / (height_m ** 2)

# 创建带工具调用的Agent
tool_agent = create_agent(
    model=llm,
    tools=[get_weather, add_numbers, calculate_bmi],
    middleware=[
        HumanInTheLoopMiddleware( 
            interrupt_on={
                # 无需触发人工审批
                "get_weather": False,
                # 需要审批，且允许approve,edit,reject三种审批类型
                "add_numbers": True,
                # 需要审批，允许approve,reject两种审批类型
                "calculate_bmi": {"allowed_decisions": ["approve", "reject"]},
            },
            description_prefix="Tool execution pending approval",
        ),
    ],
    checkpointer=InMemorySaver(),
    system_prompt="You are a helpful assistant",
)
```

```python
# 运行Agent
config = {'configurable': {'thread_id': str(uuid.uuid4())}}
result = tool_agent.invoke(
    {"messages": [{
        "role": "user",
        "content": "我身高180cm，体重180斤，我的BMI是多少"
        # "content": "what is the weather in sf"
    }]},
    config=config,
)

# result['messages'][-1].content
result.get('__interrupt__')
```

```
[Interrupt(value={'action_requests': [{'name': 'calculate_bmi', 'args': {'height_m': 1.8, 'weight_kg': 90}, 'description': "Tool execution pending approval\n\nTool: calculate_bmi\nArgs: {'height_m': 1.8, 'weight_kg': 90}"}], 'review_configs': [{'action_name': 'calculate_bmi', 'allowed_decisions': ['approve', 'reject']}]}, id='fee064093af8bee2b29f108c79c64d36')]
```

从中断信息来看，智能体已触发 `calculate_bmi` 工具调用，并进入等待审批状态。下面我们向智能体发送「审批通过」指令，使其恢复运行。

```python
# Resume with approval decision
result = tool_agent.invoke(
    Command(
        resume={"decisions": [{"type": "approve"}]}  # or "edit", "reject"
    ), 
    config=config
)

result['messages'][-1].content
```

```
'您的BMI是27.8。根据中国成人BMI标准，这属于超重范围（BMI ≥ 24为超重，≥ 28为肥胖）。建议您关注饮食健康和适量运动，如有需要可咨询医生或营养师以获得更专业的指导。'
```

> **Note**
> 除了 HITL 之外，LangChain 还提供了多种 **内置中间件**。这里仅列举其中几个，完整列表可以在 [接口文档](https://reference.langchain.com/python/langchain/middleware/#middleware-classes) 中找到。
>
> | CLASS | DESCRIPTION |
> | --- | --- |
> | SummarizationMiddleware | 在接近 token 上限时自动将对话历史进行摘要 |
> | ModelCallLimitMiddleware | 限制模型调用次数以防止成本过高 |
> | ToolCallLimitMiddleware | 通过限制调用次数来控制工具执行 |
> | ModelFallbackMiddleware | 当主模型失败时自动回退到备用模型 |
> | ...... | ...... |

参考文档：

- [langchain/human-in-the-loop](https://docs.langchain.com/oss/python/langchain/human-in-the-loop)
- [langchain/short-term-memory](https://docs.langchain.com/oss/python/langchain/short-term-memory)
- [langchain/long-term-memory](https://docs.langchain.com/oss/python/langchain/long-term-memory)
- [langgraph/persistence](https://docs.langchain.com/oss/python/langgraph/persistence)
- [langgraph/use-time-travel](https://docs.langchain.com/oss/python/langgraph/use-time-travel)
- [langgraph/add-memory](https://docs.langchain.com/oss/python/langgraph/add-memory)
