I’m using the OpenAI Agents SDK with a master agent that has specialized sub-agents registered as tools via .asTool(). One of my sub-agents has its own function tools that need to be called sequentially.
Architecture:
Master Agent
└─ SEO Agent (as tool)
├─ Tool A (returns keywords)
└─ Tool B (needs keywords from Tool A)
Issue: The sub-agent would call Tool A successfully, but wouldn’t pass Tool A’s output to Tool B. Instead, it would use the original user input for Tool B, resulting in incomplete data.
Solution we implemented: Created a composite tool that handles both operations internally:
compositeTool = tool({
execute: async (input) => {
const resultA = await apiCallA(input);
const resultB = await apiCallB(resultA);
return JSON.stringify({ data: resultB });
}
});
Now the sub-agent has all three tools available (composite + both individual tools), with instructions to prefer the composite tool for the full workflow.
Questions:
-
Is this the recommended approach for chaining operations within a sub-agent, or is there a better pattern?
-
Should nested tools in agent.asTool() contexts work sequentially, or is the composite tool pattern the intended solution?
Thanks!
1 Like
Your composite approach is good as long as you don’t nave N permutations to handle… having a lot of tools can poison the process and eat up your token budget.
Something to consider is stateful chaining. If you know that the input to B is always the last output of A, don’t make the model “pass” the actual bytes… Let the model call A and save the result in your agent state. Then when the model says to call B, pass the A state that you have saved. This stops the model from having to regurgitate the output of A which will make it faster and more reliable. think of it as a cache of tool A results… you get the benefit of using it but your agent becomes stateful and you have to expire that cache intelligently (timeout, session reset, etc.)
1 Like
Thank you for this! Could you tell me how this can be done? I’m not really sure on the architecture for thiss..
I had a case where the model didn’t initialize the tools after switching the language, so I had to reinitialize them manually each time. The model explicitly responded that it was calling a function and passing a value to it, but nothing actually happened. There was no request in the API console, even though the model confidently claimed it was using the tools, knew about them, and could apply them again. But it didn’t. Most likely, the internal runtime was just resetting to a new one, while my code didn’t include reinitialization logic. I added a button — “apply tools” — to manually reinitialize the tools, but I guess it could have been automated somehow.