Виклик функції OpenAI у маршрутах API Next.js
Виклик функцій OpenAI у маршрутах API Next.js: під’єднуйте інструменти до обробників на сервері, пишіть точні описи інструментів та вимагайте авторизації для руйнівних викликів.
Джерела: Function Calling with Next.js and OpenAI: Letting AI Actually Do Things
Звичайне доповнення чату може створювати лише текст. Виклик функцій — що також називається використанням інструментів — дозволяє моделі за запитом користувача виконувати реальні дії в додатку, такі як пошук запису, надсилання електронної пошти чи оновлення інформації. Наведений нижче опис показує, як цю схему інтегрувати у маршрут API Next.js з OpenAI.
Основна схема
Опишіть моделі інструменти. Модель вирішує, чи викликати один із них та які аргументи надіслати. Код додатку виконує обробник та повертає результат, який модель може перетворити на відповідь.
// app/api/chat/route.js
import OpenAI from 'openai';
const openai = new OpenAI();
const tools = [
{
type: 'function',
function: {
name: 'get_order_status',
description: "Get the status of a customer's order",
parameters: {
type: 'object',
properties: {
orderId: { type: 'string', description: 'The order ID' },
},
required: ['orderId'],
},
},
},
];export async function POST(request) {
const { messages } = await request.json(); const response = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages,
tools,
}); const toolCall = response.choices[0].message.tool_calls?.[0]; if (toolCall) {
const args = JSON.parse(toolCall.function.arguments);
const result = await getOrderStatus(args.orderId); // your real function // Send the result back so the model can respond in natural language
const followUp = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
...messages,
response.choices[0].message,
{ role: 'tool', tool_call_id: toolCall.id, content: JSON.stringify(result) },
],
}); return Response.json({ reply: followUp.choices[0].message.content });
} return Response.json({ reply: response.choices[0].message.content });
}
Справжня функція за інструментом
async function getOrderStatus(orderId) {
const order = await db.order.findUnique({ where: { id: orderId } });
return order ? { status: order.status, eta: order.estimatedDelivery } : { error: 'Order not found' };
}
Цей обробник — це звичайний код з боку сервера. Використання інструментів саме по собі не виконує чудес; воно лише надає моделі структурований спосіб попросити сервер виконати функцію.
Обробка кількох інструментів
const tools = [
{ type: 'function', function: { name: 'get_order_status', /* ... */ } },
{ type: 'function', function: { name: 'cancel_order', /* ... */ } },
{ type: 'function', function: { name: 'get_account_balance', /* ... */ } },
];
const toolHandlers = {
get_order_status: (args) => getOrderStatus(args.orderId),
cancel_order: (args) => cancelOrder(args.orderId),
get_account_balance: (args) => getAccountBalance(args.userId),
};// In your handler:
const handler = toolHandlers[toolCall.function.name];
const result = await handler(JSON.parse(toolCall.function.arguments));
Карта обробників працює ефективніше, ніж зростаючий ланцюг if/else, коли розширюється список інструментів.
Критично важливо: ніколи не дозволяйте моделі виконувати руйнівні дії без підтвердження
async function cancelOrder(orderId, userId) {
// Always verify the action against the actual authenticated user server-side —
// never trust that the model's tool call alone represents authorized intent
const order = await db.order.findUnique({ where: { id: orderId } });
if (order.userId !== userId) throw new Error('Unauthorized');
await db.order.update({ where: { id: orderId }, data: { status: 'cancelled' } });
}
Те, що модель вирішує викликати cancel_order, не є доказом того, що автентифікований користувач може скасувати цей замовлення. Обробник повинен щоразу перевіряти авторизацію, так само як це робив би звичайний кінець API.
Поширена помилка: нечіткі описи функцій
// Too vague — model may call this incorrectly or miss when it should
{ name: 'update', description: 'Updates something' }
// Specific enough for the model to use reliably
{ name: 'update_shipping_address', description: "Updates the shipping address for the customer's most recent unshipped order" }
Якість вибору інструментів залежить від точності опису. Неоднозначні назви та розмиті описи призводять до пропущених або неправильних викликів; конкретні назви та чіткі описи забезпечують надійну роутинг-систему.
Розглядайте аргументи інструменту як ненадійний вхідний даний. Перевіряйте ідентифікатори, відхиляйте непередбачені поля та тримайте побічні ефекти всередині транзакцій, коли змінюється кілька таблиць. Логування назви інструменту, форми аргументів (а не секретів) та результату авторизації допомагає легше відтворити інциденти у продакшені, коли модель неодноразово обирає неправильний інструмент.
Коротко кажучи
Виклик функції перетворює інтерфейс чату на щось, що може діяти, але намір моделі — це лише половина системи. Обробники на сервері все ще потребують авторизації рівня ендпоїнта, а точні описи інструментів забезпечують те, щоб ці дії спрямовувалися на правильну роботу, а не призводили до помилок. Встановіть таймаути та можливості повторних спроб під час обробки запитів від моделі, щоб цикл неробочого інструменту не міг вичерпати пул обробників шляхів.