ChatGPT API 대기 중 Function Calling 없이 구조화된 응답 받기

배경

프로젝트에서 OpenAI API를 사용해 사용자 입력을 분석하고 구조화된 데이터를 추출해야 했다. ChatGPT가 화제지만 아직 API는 공개되지 않았고, GPT-3 text-davinci-003 모델을 사용 중이다.

문제는 자연어 응답을 파싱하는 것이 생각보다 불안정하다는 점이었다. 같은 프롬프트에도 응답 형식이 달라지는 경우가 많았다.

시도한 방법

1. 명확한 포맷 지시

프롬프트 끝에 출력 형식을 명시했다.

const prompt = `다음 텍스트에서 날짜, 장소, 참석자를 추출하세요.

텍스트: "${userInput}"

다음 JSON 형식으로만 응답하세요:
{
  "date": "YYYY-MM-DD",
  "location": "장소명",
  "attendees": ["이름1", "이름2"]
}`;

2. Temperature 조정

temperature: 0으로 설정해 응답의 일관성을 높였다. 창의성보다 정확성이 중요한 경우 효과적이었다.

3. Stop Sequence 활용

const response = await openai.createCompletion({
  model: 'text-davinci-003',
  prompt: prompt,
  temperature: 0,
  max_tokens: 500,
  stop: ['}\n\n']
});

JSON 객체가 끝나는 지점에서 생성을 멈추도록 했다.

4. 방어적 파싱

function parseResponse(text) {
  // 코드 블록 제거
  const cleaned = text.replace(/```json\n?|```\n?/g, '').trim();
  
  try {
    return JSON.parse(cleaned);
  } catch (e) {
    // JSON 추출 시도
    const match = cleaned.match(/\{[\s\S]*\}/);
    if (match) {
      return JSON.parse(match[0]);
    }
    throw new Error('JSON 파싱 실패');
  }
}

결과

성공률이 95% 이상으로 올라갔다. 나머지 5%는 재시도 로직으로 처리했다. Function Calling 같은 기능이 나오면 좋겠지만, 현재로서는 이 방법이 가장 실용적이었다.

참고사항

  • 복잡한 중첩 구조는 여전히 불안정함
  • 프롬프트 길이가 길어질수록 토큰 비용 증가
  • 프로덕션에서는 반드시 에러 핸들링과 재시도 로직 필요