Add an Option to createReactAgent ToolNode for Sequential Tool Execution #59

Closed
opened 2026-02-15 17:15:11 -05:00 by yindo · 12 comments
Owner

Originally created by @VittorioYan on GitHub (Aug 8, 2024).

Description:

Currently, the createReactAgent ToolNode executes tools in parallel, which is efficient for many use cases. However, there are scenarios where sequential execution is necessary, such as when the output of one tool is required as the input for the next tool in the sequence.

Feature Request

Add an option to the ToolNode in the createReactAgent that allows tools to be executed sequentially instead of in parallel. This could be implemented as a configuration option or as a separate method.

Thank you !

Originally created by @VittorioYan on GitHub (Aug 8, 2024). **Description:** Currently, the createReactAgent ToolNode executes tools in parallel, which is efficient for many use cases. However, there are scenarios where sequential execution is necessary, such as when the output of one tool is required as the input for the next tool in the sequence. **Feature Request** Add an option to the ToolNode in the createReactAgent that allows tools to be executed sequentially instead of in parallel. This could be implemented as a configuration option or as a separate method. Thank you !
yindo closed this issue 2026-02-15 17:15:11 -05:00
Author
Owner

@jacoblee93 commented on GitHub (Aug 8, 2024):

Hey @VittorioYan,

Would you pass the result of one tool back to the model in between executions? If so, you might find this guide helpful:

https://js.langchain.com/v0.2/docs/how_to/tool_calling_parallel/

Could you give a clearer example of what you mean?

@jacoblee93 commented on GitHub (Aug 8, 2024): Hey @VittorioYan, Would you pass the result of one tool back to the model in between executions? If so, you might find this guide helpful: https://js.langchain.com/v0.2/docs/how_to/tool_calling_parallel/ Could you give a clearer example of what you mean?
Author
Owner

@VittorioYan commented on GitHub (Aug 8, 2024):

@jacoblee93 Thank you for your prompt response.
I am using LangGraph to create an agent that can help me fill out and submit forms. I tried the method you provided, and it indeed solved my problem.
However, considering token savings, I hope to minimize API calls (as I have a very long context). Therefore, I still hope to obtain multiple tool calls at once (sometimes I receive a dozen tools). However, sometimes due to the lack of sequential execution, it results in the form being submitted before the data is entered, causing errors. I checked the model's output, and the order is correct, so I need a method that can execute the tool calls in sequence.

@VittorioYan commented on GitHub (Aug 8, 2024): @jacoblee93 Thank you for your prompt response. I am using LangGraph to create an agent that can help me fill out and submit forms. I tried the method you provided, and it indeed solved my problem. However, considering token savings, I hope to minimize API calls (as I have a very long context). Therefore, I still hope to obtain multiple tool calls at once (sometimes I receive a dozen tools). However, sometimes due to the lack of sequential execution, it results in the form being submitted before the data is entered, causing errors. I checked the model's output, and the order is correct, so I need a method that can execute the tool calls in sequence.
Author
Owner

@hwchase17 commented on GitHub (Aug 8, 2024):

how would you expect the LLM to convey that it needs result from step 1 as input to step2?

@hwchase17 commented on GitHub (Aug 8, 2024): how would you expect the LLM to convey that it needs result from step 1 as input to step2?
Author
Owner

@VittorioYan commented on GitHub (Aug 8, 2024):

@hwchase17 You can understand what I'm doing like this: I have a form. I need to fill in the input first and then click the submit button. All operations on the form jointly maintain a state as the system prompt.
When I request the LLM with an empty form state, two tool_calls will be returned to me, [input, click]. My function will perform the operations, but they must be in order; otherwise, there will be an error.
Indeed, using parallel_tool_calls: false allows the state to be updated with each operation, but it leads to significant token consumption.

@VittorioYan commented on GitHub (Aug 8, 2024): @hwchase17 You can understand what I'm doing like this: I have a form. I need to fill in the input first and then click the submit button. All operations on the form jointly maintain a state as the system prompt. When I request the LLM with an empty form state, two tool_calls will be returned to me, [input, click]. My function will perform the operations, but they must be in order; otherwise, there will be an error. Indeed, using parallel_tool_calls: false allows the state to be updated with each operation, but it leads to significant token consumption.
Author
Owner

@jacoblee93 commented on GitHub (Aug 8, 2024):

We can discuss but for now you could always replace the prebuilt with a custom implementation like in the second example here:

https://langchain-ai.github.io/langgraphjs/how-tos/tool-calling-errors/

@jacoblee93 commented on GitHub (Aug 8, 2024): We can discuss but for now you could always replace the prebuilt with a custom implementation like in the second example here: https://langchain-ai.github.io/langgraphjs/how-tos/tool-calling-errors/
Author
Owner

@hinthornw commented on GitHub (Aug 8, 2024):

Ya echoing this thread, it seems fairly straightforward to implement in your own graph but more error prone if we pushed this into creatReactAgent.

Rewoo and llmcompiler (https://blog.langchain.dev/planning-agents/) are two examples of agents that do something similar to what you're discussing (create tool calls that reference previous ones), but the interfaces are different enough to not clearly share an abstraction

@hinthornw commented on GitHub (Aug 8, 2024): Ya echoing this thread, it seems fairly straightforward to implement in your own graph but more error prone if we pushed this into `creatReactAgent`. Rewoo and llmcompiler (https://blog.langchain.dev/planning-agents/) are two examples of agents that do something similar to what you're discussing (create tool calls that reference previous ones), but the interfaces are different enough to not clearly share an abstraction
Author
Owner

@VittorioYan commented on GitHub (Aug 8, 2024):

Thank you very much for your response. I will try your approach.

@VittorioYan commented on GitHub (Aug 8, 2024): Thank you very much for your response. I will try your approach.
Author
Owner

@VittorioYan commented on GitHub (Aug 8, 2024):

I still have a doubt .
In react_agent_executor.ts:

export type CreateReactAgentParams = {
  llm: BaseChatModel;
  tools:
    | ToolNode<MessagesState>
    | (StructuredToolInterface | RunnableToolLike)[];
  messageModifier?:
    | SystemMessage
    | string
    | ((messages: BaseMessage[]) => BaseMessage[])
    | ((messages: BaseMessage[]) => Promise<BaseMessage[]>)
    | Runnable;
  checkpointSaver?: BaseCheckpointSaver;
  interruptBefore?: N[] | All;
  interruptAfter?: N[] | All;
};

Tools can transfer the ToolNode type, but in createReactAgent:

const workflow = new StateGraph<AgentState>({
    channels: schema,
  })
    .addNode(
      "agent",
      RunnableLambda.from(callModel).withConfig({ runName: "agent" })
    )
    .addNode("tools", new ToolNode<AgentState>(toolClasses))
    .addEdge(START, "agent")
    .addConditionalEdges("agent", shouldContinue, {
      continue: "tools",
      [END]: END,
    })
    .addEdge("tools", "agent");

addNode still add a new ToolNode. I feel that supporting the transfer of ToolNode doesn't make sense.

@VittorioYan commented on GitHub (Aug 8, 2024): I still have a doubt . In react_agent_executor.ts: ``` typescript export type CreateReactAgentParams = { llm: BaseChatModel; tools: | ToolNode<MessagesState> | (StructuredToolInterface | RunnableToolLike)[]; messageModifier?: | SystemMessage | string | ((messages: BaseMessage[]) => BaseMessage[]) | ((messages: BaseMessage[]) => Promise<BaseMessage[]>) | Runnable; checkpointSaver?: BaseCheckpointSaver; interruptBefore?: N[] | All; interruptAfter?: N[] | All; }; ``` Tools can transfer the ToolNode type, but in createReactAgent: ``` typescript const workflow = new StateGraph<AgentState>({ channels: schema, }) .addNode( "agent", RunnableLambda.from(callModel).withConfig({ runName: "agent" }) ) .addNode("tools", new ToolNode<AgentState>(toolClasses)) .addEdge(START, "agent") .addConditionalEdges("agent", shouldContinue, { continue: "tools", [END]: END, }) .addEdge("tools", "agent"); ``` addNode still add a new ToolNode. I feel that supporting the transfer of ToolNode doesn't make sense.
Author
Owner

@jacoblee93 commented on GitHub (Aug 8, 2024):

Yeah you'll need to implement your own ToolNode equivalent with the desired behavior

@jacoblee93 commented on GitHub (Aug 8, 2024): Yeah you'll need to implement your own ToolNode equivalent with the desired behavior
Author
Owner

@VittorioYan commented on GitHub (Aug 8, 2024):

Yes, I originally intended to rewrite ToolNode and still use createReactAgent. However, when I checked the source code, I found that the rewritten ToolNode did not take effect because the built-in ToolNode was still being used here, only the tools from my ToolNode were used.

@VittorioYan commented on GitHub (Aug 8, 2024): Yes, I originally intended to rewrite ToolNode and still use createReactAgent. However, when I checked the source code, I found that the rewritten ToolNode did not take effect because the built-in ToolNode was still being used here, only the tools from my ToolNode were used.
Author
Owner

@dqbd commented on GitHub (Jun 17, 2025):

Closing the issue. We recommend constructing the graph manually (while still using ToolNode if needed)

@dqbd commented on GitHub (Jun 17, 2025): Closing the issue. We recommend constructing the graph manually (while still using `ToolNode` if needed)
Author
Owner

@hasansalimkanmaz commented on GitHub (Oct 29, 2025):

@VittorioYan I guess you can do something similar like here. Basically, you run all tool calls in a for loop in your custom tool node ;)

@hasansalimkanmaz commented on GitHub (Oct 29, 2025): @VittorioYan I guess you can do something similar like [here](https://langchain-ai.github.io/langgraph/how-tos/react-agent-from-scratch/#define-nodes-and-edges). Basically, you run all tool calls in a for loop in your custom tool node ;)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraphjs#59