효율적으로 AI 토큰을 사용하는 4가지 방법(feat. Structured Outputs)

디스콰이엇 DISQUIET*·2024년 8월 8일
1


Claude, Perplexity, GPT 등을 사용하다보면 답변의 길이가 대체로 비슷하다는 것을 알 수 있어요. 프롬프팅에 따라 약간의 조정이 가능하지만, 그렇다고 해서 완전한 리포트를 작성하거나, 책 하나를 한 번에 작성해주지는 않죠.

한 번에 작성해주면 좋을텐데 왜 못하는걸까요? 오늘은 AI를 잘 사용하기 위해 꼭 이해해야 하는 토큰에 대해 알아보고, 토큰을 효율적으로 활용하는 방법에 대해 알아봅니다.

아래의 내용을 다룰 예정이에요.

  • 토큰은 스무고개의 제한된 질문횟수와 유사합니다. 질문의 양이 한정되어 있고, 정보를 얻어내는 것에 집중해야 합니다.

  • 대표적으로 예시를 들거나, 문제를 잘게 나누거나, 배경지식(System Prompt)을 통해 정해진 토큰 안에서 효율적으로 프롬프팅을 할 수 있습니다.

  • OpenAI의 Structured Outputs를 사용해 JSON 스키마에 기반한 답변을 생성할 수 있습니다.

토큰이란

스무고개를 떠올려볼까요? 우리는 최대 20개의 질문을 할 수 있고, 초반 몇 번의 질문으로 큰 그림을 파악하고 점점 좁혀나갑니다. 이때 이전에 질문했던 것과 겹치지 않고, 구체적인 질문을 할수록 효율적으로 정답에 가까워질 수 있어요. 누군가는 20개의 질문을 전부 소진해 답을 맞추기도 하고, 10개의 질문만으로 정답에 도달할 수도 있습니다.

AI도 마찬가지에요. 인공지능에게 모두가 수백개의 질문을 던질 수 있다면 좋겠지만, 아쉽게도 그걸 감당할 전력, 비용 등은 유한해요. 이런 맥락에서 토큰 수는 인공지능에게 질문할 수 있는 일종의 제한된 권한인 셈이에요.

입력 토큰, 출력토큰, 컨텍스트 윈도우

토큰에 대한 이해가 어느 정도 생겼다면, 입력토큰, 출력토큰, 컨텍스트 윈도우로 나누어 이해할 수 있어요.

  • 입력토큰 : 사용자가 AI에게 보내는 메세지를 구성하는 단위. 단어나 단어의 일부, 문장부호 등이 토큰이 될 수 있습니다. AI는 입력토큰을 분석해 사용자의 프롬프트를 이해하고요.

  • 출력토큰 : AI가 생성하는 응답을 구성하는 작은 단위에요.

  • 컨텍스트 윈도우 : AI가 새로운 텍스트를 생성할 때 참조할 수 있는 이전 텍스트의 양입니다. 대화의 흐름을 유지하는 것에 큰 영향을 줍니다.

AI API 문서 이해하기

여기까지 이해가 되었다면 실제 AI API의 문서도 이해할 수 있어요.

우선 컨텍스트 윈도우 값은 200K로, Claude 3.5 Sonnet가 약 2배 정도 좋은 것을 확인할 수 있어요. Antrophic 문서에 따르면 200K 토큰은 약 15만개의 단어로, 보통의 장편소설 두 권 정도되는 분량입니다. 덕분에 대화의 맥락을 더 오랫동안 유지하게 돕고, 긴 문서나 대화에서 세부적인 디테일을 놓치지 않고 처리하는 것에 유리하고요. 또한 의도적으로 주어진 컨텍스트 윈도우를 충분히 활용해보는 것도 답변의 퀄리티를 높이는 것에 도움이 될 수 있어요.

반면, 최대 출력값은 GPT-4o mini와 GPT-4o가 압도적으로 높습니다. 이는 복잡한 개념이나 절차를 단계별로 설명해야할 때 유리하고, 구조적인 답변 뿐만 아니라 발산적인 작업에서도 좋은 성능을 발휘할 수 있다는 의미기도 해요.

마지막으로 비용 역시 GPT-4o mini가 압도적으로 저렴한데요. 그럼 mini 써야하는게 아닌가? 싶지만, 그건 메이커의 프롬프팅 역량, 제품의 특징에 따라 달라지다보니 직접 여러 모델을 테스트해보시는 것을 추천드려요. 벤치마크 점수를 참고하는 것도 좋지만, 우리 제품을 사용할 유저의 데이터가 아니다보니 무조건 의존하는 것은 지양해야 하고요.

그럼 무조건 토큰을 적게 쓰면 좋을까?

그렇지는 않습니다. 아래의 질문을 생각해볼까요?

상황 : 당신은 AI에게 오늘의 날씨에 대해 물어보려 합니다.
질문 : 다음 세 가지 프롬프트 중 가장 가장 적절한 것은 무엇일까요?

  1. 안녕하세요, 오늘의 날씨가 어떤지 자세히 알려주시겠어요? 기온, 습도, 강수 확률 등 구체적인 정보를 포함해주세요.

  2. 오늘 날씨 어때요?

  3. 현재 기상 상황을 상세히 설명해 주십시오. 기온, 습도, 풍속, 구름 양, 자외선 지수 등의 정보를 포함해 종합적인 날씨 보고서를 제공해 주시기 바랍니다.

이미 아시다시피, ‘정답을 고를 수 없다’가 맞아요. 파악하고 싶은 정보에 따라 적합한 프롬프트는 달리지기 때문이죠. 질문 별로 아래와 같은 상황이 있을 수 있어요.

이처럼 상황에 따라 적절한 프롬프트를 작성해 토큰을 효율적으로 사용할 수 있어요.

효과적인 프롬프트 작성하기 - Chain of thought

프롬프팅이 어려운 대부분의 이유는 원하는 결과물이 있지만 그걸 얻어내기 위해서는 어떤 질문을 해야하는지 모르기 때문이에요 이때 AI의 추론능력을 높여 복잡한 문제도 정확하고 효율적으로 해결할 수 있도록 프롬프팅할 수 있는데요. 대표적인 3가지 방법을 소개해요.

1. 예시 들기(few shot 프롬프팅)

다른 사람에게 무언가를 빠르게 이해시키기 위해 비유 또는 예시를 들곤 합니다. 패턴을 학습하게 하는 것인데요. 잘 활용하면 코드 주석, 제품 시나리오 등을 생성할 때 유용하게 사용할 수 있어요.

예를 들어, 유저 해결하고 싶은 문제/니즈(JTBD)에 기반한 기능개발을 위해 문서를 적어야하는 상황에서 아래와 같이 프롬프트를 입력해볼 수 있어요. 각 기능 별 유저 JTBD를 파악하는 것이 핵심입니다.

<프롬프트>

기능: 로그인
사용자 JTBD: 사용자로서 나는 이메일과 비밀번호로 로그인하고 싶다. 그래야 개인화된 경험을 할 수 있기 때문이다.

기능: 검색
사용자 JTBD: 

기능: 장바구니
사용자 JTBD: 

기능: 알림 설정
사용자 JTBD:
<생성된 답변>

기능: 로그인
사용자 JTBD: 사용자로서 나는 이메일과 비밀번호로 로그인하고 싶다. 그래야 개인화된 경험을 할 수 있기 때문이다.

기능: 검색
사용자 JTBD: 사용자로서 나는 필요한 상품이나 정보를 빠르게 찾기 위해 검색 기능을 이용하고 싶다. 그래야 원하는 제품이나 정보를 쉽게 찾을 수 있기 때문이다.

기능: 장바구니
사용자 JTBD: 사용자로서 나는 원하는 상품을 장바구니에 담아둘 수 있기를 원한다. 그래야 나중에 한 번에 결제하거나 구매를 고려할 수 있기 때문이다.

기능: 알림 설정
사용자 JTBD: 사용자로서 나는 새로운 상품, 할인 정보, 혹은 주문 상태 업데이트를 받을 수 있도록 알림을 설정하고 싶다. 그래야 중요한 정보를 놓치지 않고 받아볼 수 있기 때문이다.

보시는 것처럼, ‘로그인’의 사용자 JTBD를 먼저 설명해주면서 이를 참고해 아래 3개 기능에 대한 답변을 유사하게 얻어낼 수 있어요. 만약 다양한 유형의 출력값이 필요하거나, 프롬프트 복잡도가 높다면 예시의 개수를 늘려보는 것도 방법이에요.

2. 문제를 작고 관리 가능한 단위로 나누기

만약, 모델의 고유한 추론 능력을 활용해 빠르게 프롬프팅하고 싶다면, 문제를 작고 관리 가능한 수준으로 나눠볼 수도 있습니다. 예를 들어, 단계 별로 설명할 것을 요구하거나, 문제 해결에 앞서 문제를 구성하는 요소를 먼저 정리하고 요소 간의 관계를 정의하게 할 수도 있죠.

예시로, 테넌트를 활용해 CRM SaaS를 만든다고 가정해볼게요.

<문제를 쪼개지 않은 프롬프트>
최고의 CRM SaaS를 설계해줘. 다중 테넌트를 지원해야하고, 확장 가능하며, 다양한 산업군에 적용할 수 있어야 해

이렇게 광범위한 요청을 했을 때는 기대치를 낮추는 것이 좋습니다. 일반적으로 별도의 조정을 하지 않은 AI의 답변은 중간값에 가깝기 때문에 여러분이 거시적으로 파악하고 있는만큼의 정보만 도출할 가능성이 크고요.

이때 unique insight를 찾기 위해서는 이렇게 프롬프트를 개선해볼 수 있습니다.
<문제를 잘게 나눈 프롬프트>

우리는 다중 테넌트를 지원하는 CRM SaaS 플랫폼을 개발하려고 합니다. 다음 항목들에 대해 단계별로 조언해 주세요:

1. 다중 테넌트 아키텍처:
데이터 분리와 보안을 고려한 다중 테넌트 아키텍처 설계 방식을 제안해 주세요. 데이터베이스 설계와 애플리케이션 레이어에서의 접근 방식을 포함해 주세요.

2. 확장성 전략:
사용자 수가 1만에서 100만으로 증가할 때 대응할 수 있는 구체적인 확장 전략을 제시해 주세요. 데이터베이스 샤딩, 로드 밸런싱 등의 기술적 접근 방식을 포함해 주세요.

3. 핵심 기능 및 산업별 모듈:
CRM의 핵심 기능 5가지를 나열하고, 3개 주요 산업군(예: 소매, 금융, 의료)에 대한 특화 모듈을 각각 제안해 주세요.

4. API 설계:
RESTful API 설계의 주요 엔드포인트를 제안하고, API 버전 관리와 인증 방식에 대한 전략을 설명해 주세요.

5. 구독 모델 설계:
기본, 프로, 엔터프라이즈 등 3단계 구독 모델을 제안하고, 각 단계별 주요 기능과 제한사항을 명시해 주세요.

6. 보안 및 규정 준수:
다중 테넌트 환경에서의 데이터 보안 방안과 주요 규정(예: GDPR, HIPAA) 준수를 위한 전략을 제시해 주세요.

7. 개발 로드맵:
6개월 간의 개발 로드맵을 제안해 주세요. MVP(Minimum Viable Product) 출시와 주요 기능의 단계적 구현 일정을 포함해 주세요.

각 항목에 대해 간결하게 답변해 주시고, 필요한 경우 기술적 예시나 다이어그램을 포함해 주세요. 우선 1단계부터 차근차근 설명해주세요.

이렇게 프롬프트를 입력하면 프로젝트의 큰 흐름을 파악한 다음, 단계별로 구체화할 수 있어 복잡한 문제를 구조적으로 이해하고 풀어나가는데 도움이 됩니다.

3. 배경지식 추가하기(System Prompt)

이렇게 문제 쪼개기는 꼭 개발자가 아니어도 다양한 직군에서 활용할 수 있는데요. 예를 들어, 마케터가 캠페인을 점진적으로 개선하는 과정을 AI와 해볼 수도 있습니다.

마케팅의 경우, 액션→성과분석를 반복하며 이전 성과를 꾸준히 개선하는 것이 중요하죠. 그래서 아래와 같이 이전 데이터를 포함한 system prompt를 미리 입력할 수 있습니다. (ChatGPT에서는 Custom Instruction, Claude는 Project라는 기능이 동일한 역할을 해요)

당신은 10년 경력의 SEO 마케팅 전문가입니다. 우리 회사의 검색 엔진 최적화 전략 수립과 실행을 돕는 것이 당신의 주요 역할입니다. 다음 지침을 바탕으로 유연하게 응답하세요:

1. 전문 지식:
   - 온페이지/오프페이지 SEO, 기술적 SEO, 콘텐츠 SEO, 로컬 SEO에 대한 최신 트렌드와 모범 사례를 적용합니다.
   - 주요 SEO 도구(Google Search Console, SEMrush, Ahrefs 등)를 활용한 분석을 제안합니다.
   - SEO를 중심으로 하되, 필요시 관련된 디지털 마케팅 영역의 조언도 제공할 수 있습니다.

2. 분석적 접근:
   - SEO 전략 제안 시 비즈니스 목표, 타겟 키워드, 경쟁사 분석, 기술적 제약을 종합적으로 고려합니다.
   - 데이터 기반의 의사 결정을 강조하며, 필요에 따라 의견을 강조하고 싶다면, 관련 SEO 통계나 연구 결과를 인용합니다.

3. 윤리적 고려사항:
   - 화이트햇 SEO 기법만을 권장하고, 구글의 웹마스터 가이드라인을 준수합니다.

4. 회사 정보 요약 (실제 상황에 따라 조정 가능):
   - 월 평균 유기 검색 트래픽: 약 80,000명
   - 상위 10위 내 키워드 수: 약 1,000개
   - 도메인 권위(DA): 45 수준
   - 주요 타겟 키워드의 평균 순위: 15위 근처

5. 주요 SEO KPI:
   - 유기 검색 트래픽 증가율, 목표 키워드의 순위 변동, 유기 트래픽 전환율, 페이지별 체류 시간과 이탈률

6. 산업 및 타겟:
   - B2C 전자상거래 (패션 및 액세서리)
   - 주요 타겟: 25-40세 도시 거주 밀레니얼 세대
   - 패션 산업의 특성(시즌성, 트렌드 변화, 비주얼 중심 마케팅 등)을 SEO 전략에 반영하세요.

7. SEO 개선 접근:
   - 기술적 SEO, 콘텐츠 품질, 사용자 경험, 브랜드 가치 등을 균형있게 고려한 전략을 제시합니다.
   - 정기적인 SEO 감사, 콘텐츠 최적화, 백링크 프로필 개선, 경쟁사 분석 등을 포함합니다.
   - 최신 SEO 트렌드를 반영하되, 제공된 회사 정보가 현재와 다를 수 있음을 고려하세요.

8. 응답 구조:
   - SEO 관련 질문에 대해 직접적이고 명확한 답변을 제공합니다.
   - 상황에 따라 단계별 가이드, bullet point, 도표 등 다양한 형식을 활용할 수 있습니다.
   - 전문 용어 사용 시 간단한 설명을 덧붙입니다.
   - 응답 구조는 유연하게 조정할 수 있으나, 일관성 있는 커뮤니케이션을 유지합니다.

9. 응답 시 고려사항:
   - 회사의 현재 SEO 상황을 고려한 맥락화된 조언을 제시합니다.
   - 새로운 전략 제안 시, 현재 성과와 비교하여 예상 개선점을 제시합니다.
   - 제안하는 전략이 주요 SEO KPI에 미칠 잠재적 영향을 설명합니다.
   - 패션 및 액세서리 산업의 특성과 타겟 고객을 고려한 SEO 전략을 제시합니다.

이 지침을 바탕으로 SEO 관련 질문에 유연하게 답하고, 회사의 상황에 가장 적합한 전략을 제시하세요. 필요시 추가 정보를 요청하거나 가정을 명시할 수 있습니다.

만약 시스템 프롬프트를 사용해본 분이라면 아시겠지만, 너무 많은 정보를 디테일하게 입력해둘 경우, 오히려 AI가 지나치게 틀에 갇혀 사고하는 바람에 불편함을 느낄 수 있어요. 그래서 위의 시스템 프로프트는 이런 부분을 개선해 작성했어요.

  • 항상 이런 구조로 답변하세요 → 유연하고, 상황에 가장 적합한 답변/전략을 제시하세요

  • 당신은 SEO 전문가입니다 → SEO를 중심으로 하되, 필요시 디지털 마케팅 영역의 조언도 제공할 수 있습니다

  • SEO 관련 통계, 인용문구를 활용해 답변합니다 → 필요에 따라 의견을 강조하고 싶다면, SEO 관련~

OpenAI의 Structured Outputs

8월 6일 기준으로, OpenAI에서 Structured Outputs를 발표했는데요. 이건 AI의 답변을 개발자가 정의한 JSON Schema에 따라 구조화할 수 있는 기능을 제공해요. 모델의 답변을 통제해 더 예측가능한 환경에서 프롬프팅을 해볼 수 있는 것이죠. 뿐만 아니라, 구조화된 입출력은 효율적은 토큰 사용에도 도움이 되고요.

사실 기존에도 JSON으로 답변을 처리해 프롬프팅하는 여러 방법이 있었는데요. 기존에는 JSON에 기반한 '요청'은 가능했지만, AI가 항상 정확한 JSON을 반환한다는 보장이 없어 어려움이 있었습니다. 그래서 정확도를 높이려면 AI의 응답을 파싱하고, 에러를 처리하는 추가 로직을 구현해야 했고요.

또한, 원하는 구조로 응답받기 위해 복잡하고 상세하게 프롬프트를 작성해야 했는데, 때문에 프롬프트를 수정/배포하는 과정이 번거롭기도 했습니다.

예제 코드로 비교해볼까요?

<기존 방식으로 유저 프로필 정보를 추출하는 경우>

import json
import openai

def get_user_info(description):
    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=[
            {"role": "system", "content": "사용자 정보를 JSON 형식으로 추출해주세요. 이름, 나이, 직업을 포함해야 합니다."},
            {"role": "user", "content": description}
        ]
    )
    
    try:
        data = json.loads(response.choices[0].message.content)
        return {
            "name": data.get("name", "Unknown"),
            "age": int(data.get("age", 0)),
            "job": data.get("job", "Unknown")
        }
    except (json.JSONDecodeError, KeyError, ValueError) as e:
        print(f"Error processing response: {e}")
        return None

// 사용 예
user_info = get_user_info("제 이름은 김철수이고, 29살입니다. 현재 소프트웨어 개발자로 일하고 있어요.")
if user_info:
    print(f"이름: {user_info['name']}, 나이: {user_info['age']}, 직업: {user_info['job']}")
else:
    print("사용자 정보를 추출할 수 없습니다.")

이 코드를 이렇게 바꿔볼 수 있습니다.

👉 Structured Outputs으로 개선한 코드 보러가기

profile
프로덕트에 진심인 메이커들의 소셜 네트워크

0개의 댓글