Handling Tool Calls and Returning Execution Results
Once the model requests a function call, execute the function on your server, append the execution results to your message history, and send them back to the API.
1. Multi-Step Execution Flow
graph TD
A[User query] --> B[OpenAI requests tool: getUserSubscription]
B --> C[Server executes getUserSubscription locally]
C --> D[Server sends function result to OpenAI]
D --> E[OpenAI generates final text answer for User]2. Implementing the Tool Execution Loop
Write a script that processes tool requests dynamically:
import { openai } from "../lib/openai";
import { chatTools } from "../config/tools";
import { prisma } from "../lib/prisma";
// Local helper matching the schema tool definition
async function getUserSubscription(email: string) {
const user = await prisma.user.findUnique({
where: { email },
});
return JSON.stringify({
found: !!user,
tier: user?.pricingTier || "free",
active: user?.subscriptionActive || false,
});
}
export async function runAgentConversation(userQuery: string) {
const messages: any[] = [
{ role: "user", content: userQuery }
];
// 1. Initial request with tools registered
const firstResponse = await openai.chat.completions.create({
model: "gpt-4o",
messages,
tools: chatTools,
});
const message = firstResponse.choices[0].message;
const toolCalls = message.tool_calls;
// Check if the model requested any tool calls
if (toolCalls) {
// Append the assistant tool request to maintain message sequence
messages.push(message);
for (const toolCall of toolCalls) {
const functionName = toolCall.function.name;
const functionArgs = JSON.parse(toolCall.function.arguments);
let resultText = "";
if (functionName === "getUserSubscription") {
// Execute the local server function
resultText = await getUserSubscription(functionArgs.email);
}
// Append the tool execution output message to history
messages.push({
role: "tool",
tool_call_id: toolCall.id,
name: functionName,
content: resultText,
});
}
// 2. Submit execution outputs back to OpenAI to get final reply text
const secondResponse = await openai.chat.completions.create({
model: "gpt-4o",
messages,
});
return secondResponse.choices[0].message.content;
}
return message.content;
}3. Crucial Rules for Tool Cycles
- Preserve Message Sequence: Always append the initial model reply containing
tool_callsbefore appendingrole: "tool"execution output messages. Failure to preserve this ordering results in API validation errors. - Handle Exceptions: Wrap local function calls inside try-catch blocks. If a lookup fails, return the error message as the tool content so the model can report the issue to the user.
Published on Last updated: