Comand does not actually hand off to the respective Agent #598

Closed
opened 2026-02-20 17:40:54 -05:00 by yindo · 0 comments
Owner

Originally created by @Saisiva123 on GitHub (Apr 22, 2025).

Checked other resources

  • This is a bug, not a usage question. For questions, please use GitHub Discussions.
  • I added a clear and detailed title that summarizes the issue.
  • I read what a minimal reproducible example is (https://stackoverflow.com/help/minimal-reproducible-example).
  • I included a self-contained, minimal example that demonstrates the issue INCLUDING all the relevant imports. The code run AS IS to reproduce the issue.

Example Code

from typing import Literal

from dotenv import load_dotenv
from langchain_core.messages import SystemMessage, AIMessage
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI

import os

from langgraph.graph import StateGraph, START, MessagesState, END
from langgraph.prebuilt import ToolNode
from langgraph.types import Command
from utils.configs import SupervisorResponseStructure, supervisor_agent_prompt, addition_agent_prompt, multiplication_agent_prompt

load_dotenv()

os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = os.getenv("LANGCHAIN_API_KEY")

@tool
def add_numbers(a: int, b: int):
    '''This helps in adding two numbers.'''
    return a+b

@tool
def multiply_numbers(a: int, b: int):
    '''This helps in multiplying two numbers.'''
    return a*b

model =  ChatOpenAI(model="gpt-4o", openai_api_key=os.getenv("OPEN_AI_API_KEY"))

# supervisor agent
def supervisor_agent(state: MessagesState) -> Command[Literal['addition_expert_agent', 'multiplication_expert_agent', END]]:
   supervisor_model = model.with_structured_output(SupervisorResponseStructure)

   response = supervisor_model.invoke([SystemMessage(content = supervisor_agent_prompt)] + state['messages'])

   if response['agent_to_call'] and response['agent_to_call'] in ['addition_expert_agent', 'multiplication_expert_agent']:
       agent_to_call = response['agent_to_call']

       return Command(goto=agent_to_call, update={"messages": [ AIMessage(content=f"Successfully transferred to the agent: {agent_to_call}")]})

   else:
       return {"messages": [AIMessage(content=response["message"])]}


# Addition expert agent
def addition_expert_agent(state: MessagesState):

    def call_model(state: MessagesState):
        response = model.bind_tools([add_numbers]).invoke([SystemMessage(content=addition_agent_prompt)] + state['messages'])
        if len(response.tool_calls) > 0:
            return Command(goto='tool_node', update={'messages': [response]})

        return Command(update={'messages': [response]})

    tool_node = ToolNode([add_numbers])

    addition_expert_graph = StateGraph(MessagesState)
    addition_expert_graph.add_node('call_model', call_model)
    addition_expert_graph.add_node('tool_node', tool_node)

    addition_expert_graph.add_edge(START, 'call_model')
    addition_expert_graph.add_edge('tool_node', 'call_model')

    return addition_expert_graph.compile()


# Multiplication expert agent
def multiplication_expert_agent(state: MessagesState):
    def call_model(state: MessagesState):
        response = model.bind_tools([add_numbers]).invoke(
            [SystemMessage(content=multiplication_agent_prompt)] + state['messages'])
        if len(response.tool_calls) > 0:
            return Command(goto='tool_node', update={'messages': [response]})

        return Command(update={'messages': [response]})

    tool_node = ToolNode([multiply_numbers])

    multiplication_expert_graph = StateGraph(MessagesState)
    multiplication_expert_graph.add_node('call_model', call_model)
    multiplication_expert_graph.add_node('tool_node', tool_node)

    multiplication_expert_graph.add_edge(START, 'call_model')
    multiplication_expert_graph.add_edge('tool_node', 'call_model')

    return multiplication_expert_graph.compile()


supervisor_graph = StateGraph(MessagesState)


supervisor_graph.add_node('supervisor_node', supervisor_agent)
supervisor_graph.add_node('addition_expert_agent', addition_expert_agent)
supervisor_graph.add_node('multiplication_expert_agent', multiplication_expert_agent)

supervisor_graph.add_edge(START, 'supervisor_node')

supervisor_agent = supervisor_graph.compile()


# Utils/configs.py file

from typing import TypedDict, Literal, Union

from pydantic import Field

supervisor_agent_prompt = '''You act like a supervisor/receptionist who would transfer the incoming user requests either to the Addition expert(addition_expert_agent) or the Multiplication expert(multiplication_expert_agent).
Your job is to transfer the incoming requests that's all and if there is no need to transfer to any agent then just simply respond'''

addition_agent_prompt = '''You are an export in adding two numbers and you dont know any except the addition of two numbers.'''
multiplication_agent_prompt = '''You are an export in multiplying two numbers and you dont know any except the multiplication of two numbers.'''


class SupervisorResponseStructure(TypedDict):
    agent_to_call: Union[None, Literal['addition_expert_agent', 'multiplication_expert_agent']] = Field(description="The agent that suppose to be called")
    message: Union[None, str] =  Field(description="The message that needs to be conveyed to the user.")

Error Message and Stack Trace (if applicable)


Description

I have two agents (addition and multiplication experts) and the supervisor agent, once the supervisor hands off to that respective agent, the user question is not sent to that respective agent instead its passing from the supervisor agent.

Expected behavior: once the supervisor agents hands off to the respective agent then in the next turn the user request should be passed to that respective agent instead of passing through the first node.

Image

System Info

Using langgraph 0.3.31

Originally created by @Saisiva123 on GitHub (Apr 22, 2025). ### Checked other resources - [x] This is a bug, not a usage question. For questions, please use GitHub Discussions. - [x] I added a clear and detailed title that summarizes the issue. - [x] I read what a minimal reproducible example is (https://stackoverflow.com/help/minimal-reproducible-example). - [x] I included a self-contained, minimal example that demonstrates the issue INCLUDING all the relevant imports. The code run AS IS to reproduce the issue. ### Example Code ```python from typing import Literal from dotenv import load_dotenv from langchain_core.messages import SystemMessage, AIMessage from langchain_core.tools import tool from langchain_openai import ChatOpenAI import os from langgraph.graph import StateGraph, START, MessagesState, END from langgraph.prebuilt import ToolNode from langgraph.types import Command from utils.configs import SupervisorResponseStructure, supervisor_agent_prompt, addition_agent_prompt, multiplication_agent_prompt load_dotenv() os.environ["LANGCHAIN_TRACING_V2"] = "true" os.environ["LANGCHAIN_API_KEY"] = os.getenv("LANGCHAIN_API_KEY") @tool def add_numbers(a: int, b: int): '''This helps in adding two numbers.''' return a+b @tool def multiply_numbers(a: int, b: int): '''This helps in multiplying two numbers.''' return a*b model = ChatOpenAI(model="gpt-4o", openai_api_key=os.getenv("OPEN_AI_API_KEY")) # supervisor agent def supervisor_agent(state: MessagesState) -> Command[Literal['addition_expert_agent', 'multiplication_expert_agent', END]]: supervisor_model = model.with_structured_output(SupervisorResponseStructure) response = supervisor_model.invoke([SystemMessage(content = supervisor_agent_prompt)] + state['messages']) if response['agent_to_call'] and response['agent_to_call'] in ['addition_expert_agent', 'multiplication_expert_agent']: agent_to_call = response['agent_to_call'] return Command(goto=agent_to_call, update={"messages": [ AIMessage(content=f"Successfully transferred to the agent: {agent_to_call}")]}) else: return {"messages": [AIMessage(content=response["message"])]} # Addition expert agent def addition_expert_agent(state: MessagesState): def call_model(state: MessagesState): response = model.bind_tools([add_numbers]).invoke([SystemMessage(content=addition_agent_prompt)] + state['messages']) if len(response.tool_calls) > 0: return Command(goto='tool_node', update={'messages': [response]}) return Command(update={'messages': [response]}) tool_node = ToolNode([add_numbers]) addition_expert_graph = StateGraph(MessagesState) addition_expert_graph.add_node('call_model', call_model) addition_expert_graph.add_node('tool_node', tool_node) addition_expert_graph.add_edge(START, 'call_model') addition_expert_graph.add_edge('tool_node', 'call_model') return addition_expert_graph.compile() # Multiplication expert agent def multiplication_expert_agent(state: MessagesState): def call_model(state: MessagesState): response = model.bind_tools([add_numbers]).invoke( [SystemMessage(content=multiplication_agent_prompt)] + state['messages']) if len(response.tool_calls) > 0: return Command(goto='tool_node', update={'messages': [response]}) return Command(update={'messages': [response]}) tool_node = ToolNode([multiply_numbers]) multiplication_expert_graph = StateGraph(MessagesState) multiplication_expert_graph.add_node('call_model', call_model) multiplication_expert_graph.add_node('tool_node', tool_node) multiplication_expert_graph.add_edge(START, 'call_model') multiplication_expert_graph.add_edge('tool_node', 'call_model') return multiplication_expert_graph.compile() supervisor_graph = StateGraph(MessagesState) supervisor_graph.add_node('supervisor_node', supervisor_agent) supervisor_graph.add_node('addition_expert_agent', addition_expert_agent) supervisor_graph.add_node('multiplication_expert_agent', multiplication_expert_agent) supervisor_graph.add_edge(START, 'supervisor_node') supervisor_agent = supervisor_graph.compile() # Utils/configs.py file from typing import TypedDict, Literal, Union from pydantic import Field supervisor_agent_prompt = '''You act like a supervisor/receptionist who would transfer the incoming user requests either to the Addition expert(addition_expert_agent) or the Multiplication expert(multiplication_expert_agent). Your job is to transfer the incoming requests that's all and if there is no need to transfer to any agent then just simply respond''' addition_agent_prompt = '''You are an export in adding two numbers and you dont know any except the addition of two numbers.''' multiplication_agent_prompt = '''You are an export in multiplying two numbers and you dont know any except the multiplication of two numbers.''' class SupervisorResponseStructure(TypedDict): agent_to_call: Union[None, Literal['addition_expert_agent', 'multiplication_expert_agent']] = Field(description="The agent that suppose to be called") message: Union[None, str] = Field(description="The message that needs to be conveyed to the user.") ``` ### Error Message and Stack Trace (if applicable) ```shell ``` ### Description I have two agents (addition and multiplication experts) and the supervisor agent, once the supervisor hands off to that respective agent, the user question is not sent to that respective agent instead its passing from the supervisor agent. Expected behavior: once the supervisor agents hands off to the respective agent then in the next turn the user request should be passed to that respective agent instead of passing through the first node. <img width="1513" alt="Image" src="https://github.com/user-attachments/assets/3c8612d2-b88e-4953-87c8-34404ec7aa59" /> ### System Info Using langgraph 0.3.31
yindo closed this issue 2026-02-20 17:40:54 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraph#598