构建 AI 用户界面开发利器-Vercel AI SDK

635 阅读3分钟

背景

随着语言大模型的快速发展,相关的AI应用也如雨后春笋一样起来,如何快速开发就成为了大家比较关心的问题,而 Vercel AI SDK 就是一个构件 AI 用户界面的开发利器,帮助开发者快速开发类 ChatGPT 应用,内置对 LangChain、OpenAI、Anthropic、Hugging Face 的支持、支持主流前端框架的模板(Next.js、SvelteKit、Nuxt)。

使用

快速开发 ChatGPT 应用

与 Next.js 结合,50行代码就能写一个 ChatGPT 应用

安装依赖库

pnpm install openai-edge ai

服务端代码,Next.js App Route

// ./app/api/chat/route.js
import { Configuration, OpenAIApi } from 'openai-edge'
import { OpenAIStream, StreamingTextResponse } from 'ai'

// 配置 OpenAI 的 Key
const config = new Configuration({
  apiKey: process.env.OPENAI_API_KEY
})
const openai = new OpenAIApi(config)

export const runtime = 'edge'

export async function POST(req) {
  const { messages } = await req.json()
  // 请求模型 gpt-4 
  const response = await openai.createChatCompletion({
    model: 'gpt-4',
    stream: true,
    messages
  })
  // 将响应 Response 转换成 stream,并且会对返回信息进行处理,剥离出字段 content
  const stream = OpenAIStream(response)
  // 将 stream 转换成 Response,配置 status code=200,Content-Type='text/plain; charset=utf-8'
  return new StreamingTextResponse(stream)
}

客户端代码

// ./app/page.js
'use client'

import { useChat } from 'ai/react'

export default function Chat() {
  // useChat 是快速创建对话用户界面的工具类,messages 是返回的消息列表,input 是请求的 Prompt
  const { messages, input, handleInputChange, handleSubmit } = useChat()

  return (
    <div>
      {messages.map(m => (
        <div key={m.id}>
          {m.role}: {m.content}
        </div>
      ))}

      <form onSubmit={handleSubmit}>
        <input
          value={input}
          placeholder="Say something..."
          onChange={handleInputChange}
        />
      </form>
    </div>
  )
}

阻塞式响应

有些场景中需要的不是流式响应,而是阻塞式响应,比如非用户主动触发,由服务端自动生成内容。

import {Configuration, OpenAIApi} from 'openai-edge'
import {OpenAIStream, StreamingTextResponse} from 'ai'

const config = new Configuration({
  apiKey: process.env.OPENAI_API_KEY,
})

const openai = new OpenAIApi(config)

const response = await openai.createChatCompletion({
  model: 'gpt-3.5-turbo',
  messages: [{
    "role": "assistant",
    "content": "请帮我生成一个训练计划",
  }]
})

const ret = await response.json()
console.error('返回内容:', ret.choices[0].message.content)

生命周期回调

我们往往需要把对话数据保存下来,比如放到数据库中,这时候就可以使用 OpenAIStream 的 callbacks 参数

  const stream = OpenAIStream(response, {
      // 接收对话开始
      onStart: () => {
        // 保存 Prompt
        console.info('onStart')
      },
      // 接收对话中
      onToken: (token) => {
        console.info('onToken', token)
      },
      // 接收对话结束
      onCompletion: (token) => {
        // 保存 conversation
        console.info('onCompletion', token)
      }
  })

支持 function calling

在我上一篇文章如何使用 OpenAI Chat Completions API 新参数 functions里有提到,在模型 gpt-3.5-turbo 开始支持 function calling,而 ai 库也及时提供了支持,让我们可以非常方便的使用该能力。

import {Configuration, OpenAIApi} from 'openai-edge'
import {OpenAIStream, StreamingTextResponse} from 'ai'

const config = new Configuration({
  apiKey: process.env.OPENAI_API_KEY,
})

const openai = new OpenAIApi(config)
const messages = [{
  "role": "assistant",
  "content": "杭州今天天气如何?",
}]
const functions = [
  {
    name: 'get_current_weather',
    description: 'Get the current weather',
    parameters: {
      type: 'object',
      properties: {
        location: {
          type: 'string',
          description: 'The city and state, e.g. San Francisco, CA'
        },
        format: {
          type: 'string',
          enum: ['celsius', 'fahrenheit'],
          description:
            'The temperature unit to use. Infer this from the users location.'
        }
      },
      required: ['location', 'format']
    }
  }
]


const response = await openai.createChatCompletion({
  model: 'gpt-3.5-turbo',
  stream: true,
  messages,
  functions
})

const stream = OpenAIStream(response, {
  experimental_onFunctionCall: ({ name, arguments: args }, createFunctionCallMessages) => {
    // {name: get_current_weather, args: { location: '杭州', format: 'celsius' }}
    // 接收到 openai 返回,选择使用函数 get_current_weather
    if (name === 'get_current_weather') {
      // 实现函数 get_current_weather
      const weatherData = {
        temperature: 20,
        unit: args.format === 'celsius' ? 'C' : 'F'
      }

      // 生成 assistant 和 function 消息
      const newMessages = createFunctionCallMessages(weatherData)
      // newMessages [
      //   {
      //     role: 'assistant',
      //     content: '',
      //     function_call: {
      //       name: 'get_current_weather',
      //       arguments: '{\n"location": "杭州",\n"format": "celsius"\n}'
      //     }
      //   },
      //     {
      //       role: 'function',
      //       name: 'get_current_weather',
      //       content: '{"temperature":20,"unit":"C"}'
      //     } 
      //   ]

      // 再次发送给 openAI 进行总结
      return openai.createChatCompletion({
        messages: [...messages, ...newMessages],
        stream: true,
        model: 'gpt-3.5-turbo',
        functions
      })
    }
  }
})
const streamingTextResponse = new StreamingTextResponse(stream)
// 接受结果: 杭州今天的天气是20℃

与 openai-sdk 对比

openai 也提供了官方库 openai-node,但只是对 HTTP API 接口进行了封装,缺少对 Stream、UI界面、生命周期回调的支持,都需要自行实现。

PS: Stream 可以通过How to use stream: true?进行实现

参考