I am curious if anyone has any techniques for controlling the order of tools and/or when the assistant uses multi_tool_use.parallel
Some scenarios I have run into:
Conflicting tools, like if you run tool A, you don’t need to run tool B
Ensuring certain tools don’t run in parallel with others
Dependencies/order, ensuring tool A runs before B
I’ve tried including some keywords in the tool description like Computationally expensive, Don't run in parallel, Use the output from tool_x etc, but haven’t had much luck putting it in the tool description.
I have had a better success putting it in the instructions (The tools should be run in order, do not run them in parallel.) but it still feels hit-or-miss especially as the tool list grows. Especially if you want different behaviors on certain tools.
Another big one has been:
Chaining assistants (and the tool “response” being something like this task has been queued to run with another assistant after this run completes, no output yet)
The assistant often either confuses it with a failure and either retries the tool, or tries another one.
I am curious if anyone has had any luck doing something similar?
I had one conflicing function set (addcompany always has to go before addcontact) and was able to handle that in the prompt.
I agree it should be easier to control - or disable. For me at least it would be good enough if there is a KNOW order. Like the order they have in the Assistant backend.
I solved chaining assistants with a ‘post completion call’ - ie I let an assistant ‘to its thing’ and I tell right then what should be done when its done. Which can include starting a new assistant. Since runs themselves as not truly persistant and can timeout (whereas Threads are persistent) I can forward a thread to a new Assistant to act upon.
I haven’t implemented this yet, WIP, but I thought of a way with ChatGPT-4’s help. Basically, you could use the arguments to chain tool calls. As an example, if you run the below script the output will be “Storing 1200 in database”.
class ToolManager:
def __init__(self):
self.state = {}
self.end_chat = False
def run_tool(self, tool_name, arguments):
if tool_name == "calculator":
expression = arguments.get("input")
result = self.run_calculator(expression)
self.state['calculator_result'] = result
if "othertool" in arguments:
next_tool = arguments["othertool"]
if next_tool == "save_to_db":
self.run_database_storage()
if "End_Chat" in arguments:
self.end_chat = True
def run_calculator(self, expression):
# Calculator logic here
return eval(expression)
def run_database_storage(self):
value = self.state.get('calculator_result')
if value is not None:
# Database storage logic here
print(f"Storing {value} in database")
# Usage
manager = ToolManager()
manager.run_tool("calculator", {"input": "500+700"})
manager.run_tool("", {"othertool": "save_to_db", "End_Chat": None})
Second idea:
Passing tool_order as a requirement to all tool calls in the assistant API functions and building the logic in your backend to handle the order of execution.
Glad that you bring this up. Many YouTuber celebrate it’s ability to support parallel execution, but when it comes to production XOR and Sequential Execution are often the important parts.
It would be so great if the model actually understand and respect the dependencies between functions.