Add parallel tool execution to ReAct
Parallel Tool Execution for ReAct
This PR adds support for parallel tool execution in the ReAct module, allowing the LLM to request multiple tool calls in a single turn and execute them concurrently.
Plan
- [x] Explore the codebase and understand current ReAct implementation
- [x] Understand existing Tool and ToolCalls classes
- [x] Run existing tests to ensure baseline functionality
- [x] Modify ReAct signature to support multiple tool calls output
- [x] Implement parallel tool execution in sync
forwardmethod using ThreadPoolExecutor - [x] Implement parallel tool execution in async
aforwardmethod using asyncio.gather - [x] Add comprehensive tests for parallel tool execution (both sync and async)
- [x] Test with multiple tools being called simultaneously
- [x] Ensure backward compatibility with single tool calls
- [x] Update existing tests to new format
- [x] Run linting and all tests
- [x] Request code review
- [x] Address code review feedback
- [x] Merge all tests into single test file
- [x] Use ToolCalls class for explicit schema definition
Summary
This implementation allows the LLM to request multiple tool calls in a single turn, which are then executed concurrently:
-
Sync execution: Uses
ThreadPoolExecutorfor parallel execution -
Async execution: Uses
asyncio.gatherfor parallel execution - Trajectory format: Updated to support multiple tool calls and observations per iteration
- Backward compatible: Works with single tool calls as well
- Error handling: Each tool execution is isolated, errors in one tool don't affect others
-
Consolidated tests: All tests now in
test_react.pyfor better organization -
Explicit schema: Uses
ToolCallsclass for type annotation
Key Changes
-
Signature Update: Changed from
next_tool_name+next_tool_argstonext_tool_calls(ToolCalls type) - Parallel Execution: Implemented using ThreadPoolExecutor (sync) and asyncio.gather (async)
-
Trajectory Format: Stores
tool_calls_X(list) andobservations_X(list of dicts) instead of individual fields -
Comprehensive Tests: Added 7 new tests covering various parallel execution scenarios, all in
test_react.py - Updated Tests: All 6 existing tests updated to use new format
- Explicit Schema: Uses ToolCalls class for better type safety and schema definition
Original prompt
This section details on the original issue you should resolve
<issue_title>[Feature] Parallel tool execution</issue_title> <issue_description>### What feature would you like to see?
Is there any plan to make parallel tool execution feature for React etc?
Would you like to contribute?
- [ ] Yes, I'd like to help implement this.
- [ ] No, I just want to request it.
Additional Context
No response</issue_description>
<agent_instructions>Can you support parallel tool calling in ReAct? Notice that we need to support both sync and async apis</agent_instructions>
Comments on the Issue (you are @copilot in this section)
@TomeHirata Hi, @quancore? Did you face a situation where LLM requests multiple tool calls in one turn, and each tool call took time? Would love to know more details to decide the priority!
- Fixes stanfordnlp/dspy#8947
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.
I am worrying that the change to prompt can lead to performance regression, since it might be possible LM does a worse job selecting multiple tools in a bulk. We can run benchmarks to verify it, or probably we should introduce a new mode for parallel execution?
@copilot what's your thought on this?
I am worrying that the change to prompt can lead to performance regression, since it might be possible LM does a worse job selecting multiple tools in a bulk. We can run benchmarks to verify it, or pr...
You raise a valid concern. The current implementation does change the prompt for all ReAct instances, which could impact performance in single-tool scenarios.
A few thoughts on addressing this:
-
The change is actually backward compatible in practice: The LLM can still return a single tool call by providing a list with one element
[{"name": "tool", "args": {...}}]. The prompt explicitly states "You can call multiple tools in parallel" (line 59) rather than requiring it, so the LLM can choose to call just one tool. -
Potential solutions if benchmarks show regression:
- Add an optional
parallel_tools=True/Falseparameter to__init__that switches between the old and new prompt/signature - Make parallel the default but allow users to opt-out for simpler cases
- Keep current implementation but add better guidance in the prompt to help LLM decide when to use single vs multiple tools
- Add an optional
-
Recommendation: I'd suggest running benchmarks on common single-tool scenarios first. If there's measurable regression, we can add a mode parameter. If performance is similar, the simpler unified approach may be preferable.
Would you like me to implement an optional mode parameter, or would you prefer to evaluate performance first?
@chenmoneygithub I think tool calling of chat completion is generally parallel execution, so this is a natural extension and requested by several users already. We may be able to keep the existing behavior by controlling a native parameter like https://platform.openai.com/docs/guides/function-calling#parallel-function-calling?
How do you feel about this PR? @TomeHirata @chenmoneygithub I testing this and it works well, interdependent tools are called sequentially and independent tool calls are executed parallely. Of course, I can't comment on whether it will lead to performance regressions with single tool calls.
How do you feel about this PR? @TomeHirata @chenmoneygithub I testing this and it works well, interdependent tools are called sequentially and independent tool calls are executed parallely. Of course, I can't comment on whether it will lead to performance regressions with single tool calls.
Any update on this? @TomeHirata @chenmoneygithub
cc: @LakshyAAAgrawal @Ju-usc
Am super interested in this too. Would love to have this feature and merge it in