Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
VittorioYan opened this issue Aug 8, 2024 · 10 comments

Comments

@VittorioYan
Copy link

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 !

@jacoblee93
Copy link
Collaborator

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?

@VittorioYan
Copy link
Author

@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.

@hwchase17
Copy link

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

@VittorioYan
Copy link
Author

@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.

@jacoblee93
Copy link
Collaborator

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/

@hinthornw
Copy link
Contributor

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

@VittorioYan
Copy link
Author

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

@VittorioYan
Copy link
Author

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.

@jacoblee93
Copy link
Collaborator

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

@VittorioYan
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants