Выклік функцыі 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" }
Якасць выбору інструментаў залежыць ад тое, насколькі точныя ў іх апісанні. Неодназначныя назвы і расплывчатыя апісанні прыводзяць да неправільных чы або вовсе не выкананых вызоў; конкрэтныя назвы і дэталізаваныя апісанні спамагаюць забезпечыць надзеяны роутынг.
Спрытваць аргументы інструменту як ненадзеяны вхідны даны. Пераканаць ідэнтыфікаторы, адхроніць нечаканыя поля, а таксама трэба зберагаць пасяродны эфекты ў межах транзакцый, калі зменяецца калькольна табелей. Журналіраванне назвы інструменту, фармату аргументаў (не секрэтных даных) і рэзультата автарызаціі дапамагае легчэй восстанавіць сітуацыю ў працэсе вырабніцтва, калі мадэль паўтараючыся выбірае некарэктны інструмент.
Асновная мысль
Выклік функцыі ператварае інтэрфейс чата на ўсталяванне, якое можа дзеяць, але намер модэлі — толькі палова сістэмы. Адпаведальныя за апрацоўку на серверэ яшчэ патрабуюць автарызацію на рэвэле канцачнай точкі, а тачныя апісанні інструментаў дапамагаюць пераканаць, што ўсі дзеяння будуць спрамованы на правільную працу, а не на помылкі. Неабходна задаць таймауты і павторныя спробы падчас раунд-трыпамоў модэлі, каб цыклы інструментаў, якія застраглі, не моглі выкарыстаць усе рэсурсы апрацоўшчыка маршрутаў.