
LLMPromptLangChainLangGraphAI
🧬 LangGraph教程(四):实现人机协同(Human-in-the-loop)的关键技巧
2025-09-04
2025-09-04
4 min read
本文是 《LangGraph入门全解》https://www.wileyzhang.com/posts/264605ee-e889-806e-b847-ce4e7bcba614 系列的第四篇。在这篇文章中,我们暂时移除了搜索能力,为chat bot增加向用户求助的能力,也就是人机协同,这在实际开发中非常的有用。如果出你是新手,建议先阅读主指南以了解LangGraph的全貌。
在LangGraph中实现人机交互
在前几篇文章中,我们介绍了 LangGraph 实现多轮对话、工具调用和记忆。但在实际应用场景里,完全让Agent自主做决策并不总是最优解。有些环节需要人进行确认或干预,例如:
- 财务或法律类应用中,模型生成的建议必须经过人工确认。
- 医疗类应用中,AI 可以给出初步诊断,但医生才是最终的决策者。
- 多工具协作时,AI 可能不确定该选用哪个工具,需要人类来决定。
这就是 Human-in-the-loop (HITL) 的价值所在:在关键节点引入人工确认,保证智能应用既高效又可靠。
什么是 Human-in-the-loop?
HITL 就是人在某些环节作为节点参与决策。在 LangGraph 中,它通常通过以下方式实现:
- 工具节点(Tool Node):定义一个人类工具,模型可以调用该“人类工具”,把问题交给用户确认。
- 中断/等待机制:LangGraph 会暂停执行,直到人类提供反馈,用到了上一节的任务记忆。
- 继续执行:人类确认或修正后,流程继续。
这样做的好处是:
- 模型结果更可控
- 在多工具协同时减少错误调用
- 提升系统的安全性和可解释性
在 LangGraph 中实现 HITL
只需要定义一个人机交互工具即可。
from langgraph.types import interrupt from langchain_core.tools import tool @tool def human_assistance(query: str) -> str: """请求人类帮助,模型在不确定时会调用此工具"""human_response = interrupt({"query": query}) return human_response["data"] tools = [human_assistance] llm_with_tools = llm.bind_tools(tools)
完整代码如下
我们移除之前聊天机器人的搜索工具,当我们询问实时信息时,模型就会调用人机交互工具。
from typing import Annotated from typing_extensions import TypedDict from langchain_openai import ChatOpenAI from langgraph.graph import StateGraph, START, END from langgraph.graph.message import add_messages from langgraph.prebuilt import ToolNode, tools_condition from langgraph.types import Command, interrupt from langchain_core.tools import tool from langgraph.checkpoint.memory import InMemorySaver memory = InMemorySaver() # 此处定义你自己的模型 llm = ChatOpenAI(model="qwen3_32") # 定义人机交互工具 @tool def human_assistance(query: str) -> str: """请求人类帮助,模型在不确定时会调用此工具"""human_response = interrupt({"query": query}) return human_response["data"] tools = [human_assistance] llm_with_tools = llm.bind_tools(tools) # 定义图状态 class State(TypedDict): messages: Annotated[list, add_messages] # 此处维护完整的消息历史 graph = StateGraph(State) def chatbot(state: State): return {"messages": [llm_with_tools.invoke(state["messages"])]} tool_node = ToolNode(tools=tools) graph.add_node("chatbot", chatbot) graph.add_node("tools", tool_node) graph.add_conditional_edges( "chatbot", tools_condition, ) graph.add_edge("tools", "chatbot") graph.add_edge(START, "chatbot") graph.add_edge("chatbot", END) app = graph.compile(checkpointer=memory) if __name__ == "__main__": thread_id = "multi_tool_hitl_demo" pending_interrupt = False # 修改此处对话代码,区分普通对话和模型请求人类帮助 while True: if not pending_interrupt: user_input = input("👨💻: ") if user_input.lower() in ["quit", "exit", "q"]: print("Exiting...") break response = app.invoke( {"messages": {"role": "user", "content": user_input}}, config={"configurable": {"thread_id": thread_id}}, ) else: # 人类输入,恢复中断 human_input = input("👨💻(人工介入): ") human_command = Command(resume={"data": human_input}) response = app.invoke( human_command, config={"configurable": {"thread_id": thread_id}}, ) # 判断中断还是正常输出 if "__interrupt__" in response: pending_interrupt = True query = response["__interrupt__"][0].value["query"] print(f"🤖 <human_assistance> 请求人工帮助: {query}") else: pending_interrupt = False print(f'🤖: {response["messages"][-1].content}')
效果如下
在这个例子里:
- 模型发现用户问题涉及实时信息。
- 它没有搜索工具无法提供实时信息,所以调用
human_assistance
请求提供更多信息。 - 用户输入实时信息后,Agent继续执行。
这样,我们就实现了一个Human-in-the-loop的流程。