You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Vercel AI与OpenAI Functions,实现Next.js客户端与服务端互通的最简方案

Next.js API中Vercel AI与OpenAI Functions桥接客户端函数的最简方案

核心思路

通过Vercel AI的流式响应机制,在服务端拦截OpenAI返回的函数调用指令,将指令传递给客户端执行;客户端完成函数调用后,把结果回传给服务端,服务端再将结果注入对话历史,继续与OpenAI交互。

实现步骤

1. 服务端API路由(app/api/chat/route.ts)

处理对话请求,拦截OpenAI的函数调用指令并转发给客户端:

import OpenAI from 'openai';
import { OpenAIStream, StreamingTextResponse } from 'ai';

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY!,
});

export const runtime = 'edge';

// 定义客户端可调用的函数描述
const functions = [
  {
    name: 'get_user_location',
    description: '获取当前用户的地理位置',
    parameters: {
      type: 'object',
      properties: {},
      required: [],
    },
  },
];

export async function POST(req: Request) {
  const { messages } = await req.json();

  const response = await openai.chat.completions.create({
    model: 'gpt-3.5-turbo-0613',
    stream: true,
    messages,
    functions,
    function_call: 'auto',
  });

  // 拦截函数调用指令,转为JSON格式发送给客户端
  const stream = OpenAIStream(response, {
    async onFunctionCall({ name, arguments: args }) {
      return JSON.stringify({
        type: 'function_call',
        name,
        args,
      });
    },
  });

  return new StreamingTextResponse(stream);
}

2. 客户端组件(app/page.tsx)

监听服务端流,解析函数调用指令并执行客户端函数,将结果回传:

'use client';

import { useChat } from 'ai/react';

export default function ChatPage() {
  const { messages, input, handleInputChange, handleSubmit, addMessage } = useChat({
    api: '/api/chat',
    onResponse: async (response) => {
      const reader = response.body.getReader();
      const decoder = new TextDecoder();

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        const chunk = decoder.decode(value);
        // 尝试解析是否为函数调用指令
        try {
          const data = JSON.parse(chunk);
          if (data.type === 'function_call') {
            let result;
            // 匹配并执行对应客户端函数
            if (data.name === 'get_user_location') {
              result = await getCurrentLocation();
            }
            // 将函数结果以function角色消息回传给服务端
            addMessage({
              role: 'function',
              name: data.name,
              content: JSON.stringify(result),
            });
          }
        } catch (e) {
          // 非函数调用指令,按普通文本处理
        }
      }
    },
  });

  // 客户端本地函数:获取地理位置
  const getCurrentLocation = async (): Promise<{ latitude: number; longitude: number }> => {
    return new Promise((resolve, reject) => {
      if (!navigator.geolocation) {
        reject(new Error('浏览器不支持地理定位'));
        return;
      }
      navigator.geolocation.getCurrentPosition(
        (position) => {
          resolve({
            latitude: position.coords.latitude,
            longitude: position.coords.longitude,
          });
        },
        (error) => reject(error)
      );
    });
  };

  return (
    <div className="max-w-md mx-auto p-4">
      <div className="space-y-2">
        {messages.map((m) => (
          <div key={m.id} className={`p-2 rounded ${m.role === 'user' ? 'bg-blue-100' : 'bg-gray-100'}`}>
            {m.role === 'function' ? (
              <div>
                <strong>{m.name} 结果:</strong> {m.content}
              </div>
            ) : (
              m.content
            )}
          </div>
        ))}
      </div>
      <form onSubmit={handleSubmit} className="mt-4">
        <input
          type="text"
          value={input}
          onChange={handleInputChange}
          placeholder="问我需要获取你位置的问题..."
          className="w-full p-2 border rounded"
        />
        <button type="submit" className="mt-2 w-full p-2 bg-blue-500 text-white rounded">
          发送
        </button>
      </form>
    </div>
  );
}

关键桥接点说明

  • 服务端通过onFunctionCall回调捕获OpenAI的函数调用请求,将其序列化为特定格式的JSON字符串插入流式响应
  • 客户端通过onResponse监听流式返回,解析出函数调用指令后执行本地函数
  • 客户端完成函数执行后,通过addMessage将结果以function角色的消息发送回服务端,服务端会自动将该消息加入对话历史,继续与OpenAI完成后续交互

内容的提问来源于stack exchange,提问作者CoolkcaH

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 07:08:26