Vercel AI sdk

최준호·2024년 5월 2일
0

FingooProject

목록 보기
2/3
post-thumbnail

우리 프로젝트에서 OpenAI의 gpt를 사용하면서 프론트엔드에서 더욱 효과적으로 사용하고자 하였고, 이에 대해 배우면서 나도 안까먹고자 정리한다.

1. Vercel AI SDK의 OpenAI 기본 사용법

  1. 프로젝트에 ai sdk, open ai 적용하기
npm i ai openai
  1. Open AI API Key 등록하기
OPEN_API_KEY = xxxxxxxx
  1. Route Handler 만들기 (app/api/chat/route.ts)
import OpenAI from 'openai'
import { OpenAIStream, StreamingTextResponse } from 'ai'

const openai = new OpenAI({
	apiKey: process.env.OEPN_API_KEY
});
// .env에 들어있는 OPEN AI의 api key 등록하기

export const dynamic = 'force-dynamic';
// Next.js application에서 동적 라우팅 구현시에 사용

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

	const response = await openai.chat.completions.create({
		model: 'gpt-3.5-turbo',
		stream: true,
		messages,
	});
	
	const stream = OpenAIStream(response);
	
	return new StreamingTextResponse(steam);
  1. UI 연결하기 (app/page.tsx)
use client';
 
import { useChat } from 'ai/react';
 
export default function Chat() {
  const { messages, input, handleInputChange, handleSubmit } = useChat();
  return (
    <div className="flex flex-col w-full max-w-md py-24 mx-auto stretch">
      {messages.map(m => (
        <div key={m.id} className="whitespace-pre-wrap">
          {m.role === 'user' ? 'User: ' : 'AI: '}
          {m.content}
        </div>
      ))}
 
      <form onSubmit={handleSubmit}>
        <input
          className="fixed bottom-0 w-full max-w-md p-2 mb-8 border border-gray-300 rounded shadow-xl"
          value={input}
          placeholder="Say something..."
          onChange={handleInputChange}
        />
      </form>
    </div>
  );
}

2. OpenAI Functions를 적용하는 AI sdk UX Flow알아보기

원래 OpenAI에서 볼 수 있었던 function calling 부분은 Deprecated 곧 더이상 사용되지 않는다고 API reference에 나와있다. 그렇지만 해당 파트가 없어져야하는 것은 아니다. tools를 사용하여 OpenAI 에서의 OpenAI의 function callling처럼 보여질 수 있다.

Open AI function calling 내용

  • function calling은 함수를 호출하는 것
  • GPT 모델이 함수를 직접 실행하는 것이 아닌 함수를 실행하도록 JSON을 보내줍니다.

AI sdk와 OpenAI api의 흐름

  • 사용자 메시지를 받았을 때 서버에서 함수호출을 자동적으로 하는 경우 OpenAI의 function call을 보내고, 이를 서버에서 받아 해당 Function을 실행, 결과를 다시 OpenAI로 보내고 응답을 받아 클라이언트에게 메시지를 전송한다.

  • 1. Automatic Function Execution 과 유사하나 function call을 보내면서 사용자에게 파악한 의도를 알려주고, 함수 실행간 진행상황을 사용자에게 보고한다.

  • 2. Automatic Function Execution with Intent & Progress 와 거의 유사하지만 차이점은 function_call 과정시에 사용자의 의도를 기반으로 함수 선택 및 확인 후에 서버에서 함수를 실행하게 되는 차이점이 있다.

다음처럼 3가지 UX Flow를 따르면서 사용자 경험이 확대된다. 이는 프롬프트를 어떻게 작성하느냐에 따라 UX Flow를 선택할 수 있을 것이다. 가령 예시는 다음과 같다.

messages: [
	{
		role: 'system',
		content:"의도작성하기"
			
}];

1번흐름 : "사용자가 요청했을 때 사용자의 의도를 예상하여 함수를 실행하고 결과를 종합하여 응답해라",

2번흐름 : "사용자가 요청했을 때 사용자의 의도를 예상해서 사용자에게 의도와 함수 실행 과정을 전달하고 결과를 종합하여 응답해라"

3번흐름 : "사용자가 요청했을 때 의도를 예상하여 실행하지말고 예상한 의도를 사용자에게 확인받은 후 함수를 실행하며 진행과정을 전달한다. 그리고 함수 실행 결과값으로 결과를 종합하여 응답해라"

❗ 프롬프트 엔지니어링 구현시 이 방법은 정답이아닙니다,,, 실험하는 과정을 꼭 진행해야합니다.

AI sdk에서 OpenAI functions 사용하기

1. 함수 정의하기

  • 함수를 전달하기 위해 함수 객체를 배열안에 저장하고 전달해줘야한다.
  • 정의한 후 함수 처리하는걸 클라이언트, 서버에서 처리한다.
import OpenAI from 'openai';
import { OpenAIStream, StreamingTextResponse } from 'ai';
//2024.05.02 기준으로 function call은 레거시가 되고 대신 Tools의 이름으로 등록이 되어있습니다. 주의바랍니다.
import type { ChatCompletionTool } from 'openai/resources/index.mjs';

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

const functions: Array<ChatCompetionTool> = [
	{
		type: 'function',
		function: {
			name: 'get_current_weather',
      description: 'Get the current weather in a given location',
			parameters: {
				type: 'object',
				properties: {
					location: {
						type: 'string'
						description: 'The city and state, e.g. San Francisco, CA'
					},
				},
				required: ['location'],
			},
		},
	}. //...
	
export async function POST(req: Request) {
	const { messages } = await. req.json()
		
	const response = await openai.chat.completions.create({
		model: 'gpt-3.5-turbo',
		stream: true,
		messages: [
			{
				role: 'system',
				content: "Do not assume what values to use for functions. If the user's request is unclear, ask for clarification"
			},
			...messages,
		],
		tools: functions,
	});
	
	const stream = OpenAIStream(response);
	return new StreamingTextResponse(stream);
}

2. 서버에서 처리하기

  • 모델이 함수를 호출할 때 experimental_onFunctionCall 콜백이 된다. 이 콜백은 OpenAIStream에 전달된다.
  • createFunctionCallMessages를 사용하여 중첩된 OpenAI 호출에서 메시지 컨텍스트를 구성하고 함수 호출 메시지를 생성할 수 다.
const stream = OpenAIStream(response, {
  experimental_onFunctionCall: async (
    { name, arguments: args },
    createFunctionCallMessages,
  ) => {
    if (name === 'get_current_weather') {
      const weatherData = {
        temperature: 20,
        unit: args.format === 'celsius' ? 'C' : 'F',
      };

      const newMessages = createFunctionCallMessages(weatherData);
      return openai.chat.completions.create({
        messages: [...messages, ...newMessages],
        stream: true,
        model: 'gpt-3.5-turbo-0613',
        functions,
      });
    }
  },
});
  • 이렇게 구성된 서버 처리 로직을 통해 모델이 함수 호출을 수행할 때 필요한 데이터를 처리하고, 해당 결과를 클라이언트에 한다. 클라이언트는 이렇게 반환된 "assistant" 메시지를 통해 함수 호출 결과를 받아볼 수 있다.

3. 클라이언트에서 함수 호출 처리하기

  • 클라이언트에서는 useCompletionuseChat 훅에 experimental_onFunctionCall 핸들러를 전달할 수 있다. 이 콜백은 서버에서 함수 호출을 처리하지 않고 클라이언트로 스트리밍할 때 호출된다.
const functionCallHandler: FunctionCallHandler = async (
  chatMessages,
  functionCall,
) => {
  if (functionCall.name === 'get_current_weather') {
    if (functionCall.arguments) {
      const parsedFunctionCallArguments = JSON.parse(functionCall.arguments);
      console.log(parsedFunctionCallArguments);
    }

    const temperature = Math.floor(Math.random() * (100 - 30 + 1) + 30);
    const weather = ['sunny', 'cloudy', 'rainy', 'snowy'][
      Math.floor(Math.random() * 4)
    ];

    const functionResponse: ChatRequest = {
      messages: [
        ...chatMessages,
        {
          id: generateId(),
          name: 'get_current_weather',
          role: 'function' as const,
          content: JSON.stringify({
            temperature,
            weather,
            info: 'This data is randomly generated and came from a fake weather API!',
          }),
        },
      ],
    };
    return functionResponse;
  }
};

const { messages, input, handleInputChange, handleSubmit } = useChat({
  experimental_onFunctionCall: functionCallHandler,
});
  • 모델이 get_current_weather 함수를 호출하면, OpenAI API는 호출할 함수의 이름과 인자를 포함한 특별한 형식의 메시지를 반환된다. 클라이언트에서는 이 핸들러를 통해 함수 호출을 처리하고 채팅을 적절히 조작할 수 있다.
profile
FE Developer(진)

0개의 댓글