Today we will discuss multi-turn dialogues, and we will write a program that allows users to engage in multi-turn dialogues with one or more agents. We will still use interrupt to get the user’s input and then return to the active agent node.
An agent can serve as a node in a graph, executing agent steps and determining the next action: first waiting for the user’s input to continue the dialogue, and then navigating to another agent (or returning to itself, for example, in a loop).
The code is as follows
def human(state: MessagesState) -> Command[Literal["agent", "another_agent"]]: """Get user input.""" user_input = interrupt(value="Ready for user input.") # Determine the active agent active_agent = ... ... return Command( update={ "messages": [{ "role": "human", "content": user_input, }] }, goto=active_agent )def agent(state) -> Command[Literal["agent", "another_agent", "human"]]: # The conditions for routing to run or stop can be triggered by any event, such as LLM tool calls or structured outputs. goto = get_next_agent(...) # 'agent' / 'another_agent' if goto: return Command(goto=goto, update={"my_state_key": "my_state_value"}) else: return Command(goto="human")
Defining agents
In our newly written example, we will establish a team of travel assistant agents that can communicate with each other.
First, we will create two agents:
travel_advisor agent: Can help recommend travel destinations and can ask the hotel_advisor agent for assistance.
hotel_advisor agent: Can help recommend hotels and can ask the travel_advisor agent for assistance.
We will use `create_react_agent` to build the agents, as each agent will have tools specific to its area of expertise and special tools for handing off to another agent. Next, we will define our tools:
import randomfrom typing import Annotated, Literalfrom langchain_core.tools import toolfrom langchain_core.tools.base import InjectedToolCallIdfrom langgraph.prebuilt import InjectedState@tooldef get_travel_recommendations(): """Get travel city recommendations""" return random.choice(["Beijing", "Shanghai"])@tooldef get_hotel_recommendations(location: Literal["Beijing", "Shanghai"]): """Get hotel recommendations""" return { "Beijing": [ "Bulgari", "Hilton" ], "Shanghai": ["Bund 18", "Dihou"], }[location]# Build transfer tooldef make_handoff_tool(*, agent_name: str): tool_name = f"transfer_to_{agent_name}" @tool(tool_name) def handoff_to_agent( state: Annotated[dict, InjectedState], tool_call_id: Annotated[str, InjectedToolCallId], ): """Ask another agent for help.""" tool_message = { "role": "tool", "content": f"Successfully transferred to {agent_name}", "name": tool_name, "tool_call_id": tool_call_id, } return Command( goto=agent_name, graph=Command.PARENT, update={"messages": state["messages"] + [tool_message]}, ) return handoff_to_agent
Now we will set up a dialogue system that includes multiple AI agents. When these agents finish the dialogue, the system will automatically switch back to the human user’s node, allowing the user to continue inputting. This setup ensures the order and interactivity of the dialogue flow. Each agent’s response is designed to automatically turn back to the human user upon completion, achieved through a specific command structure.
The general steps are:
-
First, use the pre-built create_react_agent function to create agents
-
Then define a dedicated human node with interrupt functionality
-
After the agent gives the final response, the system will route to this human node
-
To achieve this functionality, each agent’s call is encapsulated in a separate node function
-
This node function will return a Command with the goto=”human” parameter
# Define travel advisor tools and ReAct agenttravel_advisor_tools = [ get_travel_recommendations, make_handoff_tool(agent_name="hotel_advisor"),]travel_advisor = create_react_agent( model, travel_advisor_tools, state_modifier=( "You are a general travel expert who can recommend travel destinations (e.g., countries, cities, etc.)." "If you need hotel recommendations, please ask 'hotel_advisor' for help." "Before transferring to another agent, you must include a response that humans can read." ),)def call_travel_advisor( state: MessagesState,) -> Command[Literal["hotel_advisor", "human"]]: response = travel_advisor.invoke(state) return Command(update=response, goto="human")# Define hotel advisor tools and ReAct agenthotel_advisor_tools = [ get_hotel_recommendations, make_handoff_tool(agent_name="travel_advisor"),]hotel_advisor = create_react_agent( model, hotel_advisor_tools, state_modifier=( "You are a hotel expert who can provide hotel recommendations for specific travel destinations." "If you need help choosing a travel destination, please ask travel_advisor for help." "Before transferring to another agent, you must include a response that humans can read." ),)def call_hotel_advisor( state: MessagesState,) -> Command[Literal["travel_advisor", "human"]]: response = hotel_advisor.invoke(state) return Command(update=response, goto="human")# Define a human node to track the last interacting advisor with the user, ensuring the conversation can continue correctly.def human_node( state: MessagesState, config) -> Command[Literal["hotel_advisor", "travel_advisor", "human"]]: """A node for collecting user input.""" user_input = interrupt(value="Waiting for user input.") langgraph_triggers = config["metadata"]["langgraph_triggers"] if len(langgraph_triggers) != 1: raise AssertionError("Expected exactly 1 trigger in human node") active_agent = langgraph_triggers[0].split(":")[1] return Command( update={ "messages": [ { "role": "human", "content": user_input, } ] }, goto=active_agent, )builder = StateGraph(MessagesState)builder.add_node("travel_advisor", call_travel_advisor)builder.add_node("hotel_advisor", call_hotel_advisor)builder.add_node("human", human_node)builder.add_edge(START, "travel_advisor")checkpointer = MemorySaver()graph = builder.compile(checkpointer=checkpointer)from IPython.display import display, Imagedisplay(Image(graph.get_graph().draw_mermaid_png()))
Now let’s take a look at its graph:

Testing Multi-Turn Dialogue
Let’s use this application to test multi-turn dialogues. We will create a three-turn dialogue:
import uuidthread_config = {"configurable": {"thread_id": uuid.uuid4()}}inputs = [ { "messages": [ {"role": "user", "content": "I want to go to a warm place in Sanya"} ] }, Command( resume="Can you recommend a nice hotel in one of the areas? Tell me where it is." ), Command( resume="I like the first recommendation. Can you recommend something else near the hotel?" ),]for idx, user_input in enumerate(inputs): print() print(f"--- Conversation Turn {idx + 1} ---") print() print(f"User: {user_input}") print() for update in graph.stream( user_input, config=thread_config, stream_mode="updates", ): for node_id, value in update.items(): if isinstance(value, dict) and value.get("messages", []): last_message = value["messages"][-1] if isinstance(last_message, dict) or last_message.type != "ai": continue print(f"{node_id}: {last_message.content}")
Let’s see our results:

Isn’t it amazing? Of course, we can derive many functions from this multi-turn dialogue to enhance the system’s capabilities and user experience. For example, expanding the range of destinations and hotel recommendations, adding more cities and hotel options, and even providing personalized recommendations based on user preferences (such as budget, type of travel). We can also integrate external APIs, such as flight information, weather forecasts, and attraction recommendations, to provide more comprehensive travel planning services.
To support multi-language users, we can add multi-language support functions. Additionally, the system can introduce user preferences and history features to provide more accurate recommendations based on users’ historical choices. In terms of dialogue management, we can enhance context memory and state recovery capabilities to ensure coherence in long conversations. If more complex collaboration is needed, we can add more agents (such as attraction recommendation agents, transportation agents, etc.) to achieve multi-agent collaborative work. A user feedback mechanism can also be added, allowing users to rate or provide feedback on the recommended results, thereby optimizing the recommendation algorithm. To enhance natural language understanding capabilities, more advanced models can be utilized to better understand user intentions. Finally, a visual interface (such as a web or mobile application) can be developed to facilitate user interaction with the system, and even integrate payment and booking functions to allow users to book hotels or flights directly through the system. The addition of these features will make the system smarter, more comprehensive, and user-friendly, providing higher quality travel planning services.