首页 / 文章

在 Next.js API 路由中调用 OpenAI 函数

在 Next.js API 路由中调用 OpenAI 函数:将工具与服务器处理程序相连,编写精确的工具描述,并对具有破坏性的调用进行授权验证。

发布者:REACTAPP.TOP阅读约 3 分钟797 词

来源: Function Calling with Next.js and OpenAI: Letting AI Actually Do Things

普通聊天回复只能生成文本。而函数调用——也称为工具使用——能让模型根据用户请求执行实际的应用操作,比如查询数据行、发送邮件或更新记录。下方的演示展示了如何将这种模式与 OpenAI 结合到 Next.js API 路由中。

核心模式

向模型描述可用工具,由模型决定是否调用某个工具以及传递哪些参数。应用代码会运行相应的处理函数,并返回结果,模型再将其转化为回复内容。

// 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" }

工具选择的准确性取决于描述的精确度。含糊的名称和模糊的描述会导致调用失败或出错;而具体的名称以及明确的描述则能保证路由的可靠性。

应将工具参数视为不可信的输入。需验证标识符,拒绝异常字段,并在涉及多张表修改时将副作用限制在事务范围内。记录工具名称、参数结构(而非敏感信息)以及授权结果,有助于在模型反复选择错误工具时更轻松地复现生产环境中的问题。

总结

函数调用使聊天界面具备了执行操作的能力,但模型意图仅是系统的一半。服务器处理程序仍需端点级别的授权机制,而精确的工具描述才能确保这些操作指向正确的工作内容而非导致错误。应为模型往返调用设置超时和重试机制,以避免因工具循环挂起而耗尽路由处理程序资源。