LangChain v1Note 14

第 8 章:人在回路(HITL)

对有外部副作用的工具使用 HumanInTheLoopMiddleware。它在模型提出匹配工具调用后生成 LangGraph interrupt;调用方展示 action,再以 Command(resume={"decisions": [...]}) 恢复。必须配 checkpointer,否则没有可恢复状态。

对有外部副作用的工具使用 HumanInTheLoopMiddleware。它在模型提出匹配工具调用后生成 LangGraph interrupt;调用方展示 action,再以 Command(resume={"decisions": [...]}) 恢复。必须配 checkpointer,否则没有可恢复状态。

agent = create_agent(
    model, tools=[send_message], checkpointer=InMemorySaver(),
    middleware=[HumanInTheLoopMiddleware({"send_message": True})],
)
first = await agent.ainvoke(input, config=config)
resumed = await agent.ainvoke(
    Command(resume={"decisions": [{"type": "approve"}]}), config=config
)

interrupt_on{工具名: 配置}True 允许 approve/edit/reject/respond;也可指定 allowed_decisions 和动态 when。每个 interrupt 中可能有多个 action,decisions 数量与顺序必须匹配。approve 执行原调用;edit 执行编辑后的参数;reject 生成错误 ToolMessagerespond 由人工直接提供工具结果。

本地示例 12_hitl.py 只调用内存中的 draft_notice,不会发送网络消息。它先断言出现 interrupt,再批准,最后确认工具确实执行。生产实现还要将审批人、时间、原参数、编辑后参数和结果记入审计系统,并在工具本身再次授权。

相关资源

  • 查看示例代码:docs/langchain/examples/12_hitl.py
    """Chapter 8: pause for local approval before executing an in-memory action."""
    
    import asyncio
    
    from dotenv import load_dotenv
    from langchain.agents import create_agent
    from langchain.agents.middleware import HumanInTheLoopMiddleware
    from langchain.chat_models import init_chat_model
    from langchain_core.tools import tool
    from langgraph.checkpoint.memory import InMemorySaver
    from langgraph.types import Command
    
    from open_deep_research.configuration import Configuration
    
    
    load_dotenv()
    
    EXECUTED = []
    
    
    @tool
    def draft_notice(text: str) -> str:
        """Record a notice draft in local memory. This does not send anything."""
        EXECUTED.append(text)
        return "草稿已记录,未发送。"
    
    
    async def main() -> None:
        settings = Configuration.from_env()
        agent = create_agent(
            init_chat_model(model=settings.research_model, max_tokens=80),
            tools=[draft_notice],
            checkpointer=InMemorySaver(),
            middleware=[HumanInTheLoopMiddleware({"draft_notice": True})],
            system_prompt="必须调用 draft_notice 记录用户提供的草稿。",
        )
        config = {"configurable": {"thread_id": "lesson-hitl"}}
        first = await agent.ainvoke(
            {"messages": [("user", "调用 draft_notice 记录:明天上午十点开会。 ")]}, config=config
        )
        assert first.get("__interrupt__"), first
        final = await agent.ainvoke(
            Command(resume={"decisions": [{"type": "approve"}]}), config=config
        )
        assert EXECUTED and "明天" in EXECUTED[0]
        print(f"审批后结果: {final['messages'][-1].content}")
    
    
    if __name__ == "__main__":
        asyncio.run(main())