1강 — 프롬프트 고급 기술
🎯 학습 목표
- 퓨샷(Few-shot)으로 AI에게 예시를 제공해 일관된 결과를 끌어낼 수 있다
- 생각의 사슬(CoT)로 AI의 추론 과정을 유도해 정확도를 높일 수 있다
- 감성 분석 외에 키워드 추출·요약 등 다양한 작업을 같은 방식으로 수행할 수 있다
- 한 번의 호출로 여러 분석을 받는 멀티태스크 분석과 JSON 출력을 만들 수 있다
📖 해설
세 번째 날의 첫 시간입니다. 지난 시간(2주차 6강)에 프롬프트 엔지니어링 기초 — 역할 부여와 출력 형식 지정 — 을 배웠죠. 오늘은 한 단계 더 나아갑니다. AI에게 예시를 보여주고, 생각하는 방법까지 알려주는 고급 기술입니다. 이 기법들을 익히면 같은 모델로도 훨씬 정확하고 일관된 결과를 뽑을 수 있습니다.
0. 지난 내용 빠르게 점검
오늘 실습의 토대가 되는 네 가지를 짚고 갑시다.
- 텍스트 정제 —
ㅋㅋㅋ나 특수문자 같은 노이즈를 걷어내야 분석 품질이 올라갑니다. 노이즈는 토큰 사용량을 늘려 비용도 키웁니다. - 임베딩 — 텍스트를 숫자 벡터로 바꿔 AI가 이해할 수 있는 형태로 만드는 것.
- API 키 관리 — Colab Secrets에 저장해 안전하게 불러옵니다. 코드에 키를 직접 넣으면 절대 안 됩니다.
- 프롬프트 기초 — 역할을 주고 형식을 지정하면 일관된 답변을 받습니다.
1. 시작 전 — 환경 설정 다시 잡기
Colab은 세션이 끊기면 설치한 라이브러리와 클라이언트 설정이 사라집니다. 그래서 매번 맨 위에서 준비 단계를 한 번 실행합니다. 새 내용이 아니라 반복하는 고정 작업이에요.
# ① OpenAI 호환 라이브러리 설치 (-q: 설치 로그를 조용히)
!pip install openai -q
# ② Colab Secrets에 저장해 둔 API 키 불러오기 (코드에 키를 직접 쓰지 않기 위함)
from google.colab import userdata
api_key = userdata.get('OPENROUTER_API_KEY')
# ③ 모델을 맨 앞에서 한 번만 정해두면, 아래 모든 함수가 이 모델을 씁니다.
# OpenRouter에는 수많은 모델이 있으니, 이 줄만 바꿔 다른 모델로 실험해 보세요.
MODEL = "mistralai/devstral-2512:free"
# ④ OpenRouter를 향하도록 클라이언트 만들기 (base_url만 바꾸면 여러 모델을 같은 코드로 호출)
from openai import OpenAI
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=api_key,
)
print("준비 완료!")
모델마다 개성이 다릅니다. ChatGPT·Claude·Gemini 같은 상용 고급 모델은 형식을 잘 지키지만, 무료 모델이나 사전 학습만 되어 있어 목적에 맞게 재구성되지 않은(파인튜닝용) 모델은 우리가 원하는 형태로 딱 떨어지지 않을 수 있어요. 모델을 바꿔가며 돌려보는 것 자체가 좋은 경험입니다. 이후 AI 앱을 만들 때 "어떤 모델이 어떤 작업에 강한가"를 아는 게 큰 자산이 됩니다.
2. 제로샷 vs 퓨샷 — 예시의 힘
- 제로샷(Zero-shot): 예시 없이 지시만 하는 것. "positive / negative / mixed 중 하나로 분류해."
- 퓨샷(Few-shot): 예시를 먼저 보여주고 지시하는 것. "이건 positive, 이건 negative… 이제 분류해."
신입사원 교육으로 비유해 봅시다. "보고서 써와"라고만 하면 뭘 어떻게 해야 할지 막막하죠. "이런 식으로 써, 예시 봐"라고 하면 훨씬 정확하게 합니다. AI도 똑같아요. 예시를 보면 우리의 기준을 파악합니다.
먼저 제로샷입니다. Not bad, but nothing special either.(나쁘진 않은데 특별할 것도 없다)처럼 애매한 리뷰를 줘 봅시다.
# 제로샷: 예시 없이 지시만 한다
response = client.chat.completions.create(
model=MODEL,
messages=[
{"role": "system", "content":
"너는 게임 리뷰 감성 분석 전문가야. "
"리뷰를 읽고 POSITIVE, NEGATIVE, MIXED 중 하나로 분류해. "
"다른 설명 없이 분류 결과만 답해."},
{"role": "user", "content": "리뷰: Not bad, but nothing special either."},
],
)
print("[제로샷 결과]", response.choices[0].message.content)
# 예시 출력: MIXED (모델·환경에 따라 다를 수 있음)
제로샷도 대체로 잘 합니다. 다만 애매한 케이스에서 일관성이 부족해요. 어떤 건 MIXED로, 비슷한 건 POSITIVE로 흔들립니다. 그래서 우리 기준을 못 박고 싶을 때 퓨샷을 씁니다.
# 퓨샷: 입력-출력 예시를 먼저 보여주고 지시한다
response = client.chat.completions.create(
model=MODEL,
messages=[
{"role": "system", "content":
"너는 게임 리뷰 감성 분석 전문가야.\n"
"아래 예시를 참고해서 리뷰의 감성을 분류해.\n\n"
"### 예시 ###\n"
'review: "Best game ever! Totally worth it."\n'
"sentiment: POSITIVE\n\n"
'review: "Terrible. Crashes every 5 minutes."\n'
"sentiment: NEGATIVE\n\n"
'review: "Fun gameplay but too many bugs."\n'
"sentiment: MIXED\n"
"### 예시 끝 ###\n\n"
"답변은 POSITIVE, NEGATIVE, MIXED 중 하나로만 해."},
{"role": "user", "content": "리뷰: Not bad, but nothing special either."},
],
)
print("[퓨샷 결과]", response.choices[0].message.content)
퓨샷의 핵심 포인트를 정리하면:
| 요소 | 설명 |
|---|---|
### 예시 ### … ### 예시 끝 ### |
예시의 시작과 끝을 분명히 표시 |
| 입력-출력 쌍 | 리뷰(입력)와 감성(출력)을 짝지어 보여주기 |
| 3개 정도 | 긍정·부정·혼합 각 1개씩. 너무 많으면 토큰 비용 증가 |
긍정·부정·혼합 예시를 하나씩 보여주면 AI가 우리 기준을 잡습니다. 도메인마다 애매하거나 특별한 기준(예: 특정 키워드가 들어가면 긍정으로 본다)이 있으면, 예시에 그걸 녹여 우리만의 규칙을 적용할 수도 있어요. 참고로 제로샷이 무조건 나쁜 게 아닙니다. 백지 상태에서 브레인스토밍하거나 새 방향을 넓게 탐색할 때는 제로샷이, 원하는 형태가 정해져 바로 쓸 결과가 필요할 때는 퓨샷이 어울립니다.
3. 생각의 사슬(CoT) — 단계별로 생각하게 하기
생각의 사슬(Chain of Thought, 줄여서 CoT)은 AI가 생각하는 과정을 드러내게 하는 기술입니다.
수학 문제로 비유해 봅시다. 답만 "402"라고 적으면 맞았는지 틀렸는지, 어디서 틀렸는지 알기 어렵죠. 풀이 과정을 보여주면 어디서 어긋났는지 보입니다. AI도 "단계별로 생각해"라고 하면 추론 과정을 거칩니다. 그리고 놀라운 점은, 과정을 정리하며 답하면 답의 정확도 자체가 올라간다는 것입니다.
비교해 봅시다. 먼저 결과만 받는 경우:
# CoT 없이 — 결과만 받기
review = "Beautiful graphics and immersive world, but the constant crashes and lag make it unplayable."
response = client.chat.completions.create(
model=MODEL,
messages=[
{"role": "system", "content":
"너는 게임 리뷰 감성 분석 전문가야. POSITIVE, NEGATIVE, MIXED 중 하나로만 답해."},
{"role": "user", "content": f"리뷰: {review}"},
],
)
print("[결과만]", response.choices[0].message.content)
이번엔 단계를 명시하고 답변 형식도 단계별로 지정합니다.
# CoT 적용 — 분석 단계를 못 박고, 형식도 단계별로 지정
response = client.chat.completions.create(
model=MODEL,
messages=[
{"role": "system", "content":
"너는 게임 리뷰 감성 분석 전문가야.\n"
"리뷰를 분석할 때 다음 단계를 따라:\n"
"1. 긍정적 표현 찾기\n"
"2. 부정적 표현 찾기\n"
"3. 어느 쪽이 더 강한지 판단\n"
"4. 최종 결론 내리기\n\n"
"답변 형식:\n"
"[분석]\n"
"긍정: (발견한 긍정 표현)\n"
"부정: (발견한 부정 표현)\n"
"판단: (어느 쪽이 강한지)\n"
"[결론]\n"
"감성: POSITIVE/NEGATIVE/MIXED"},
{"role": "user", "content": f"리뷰: {review}"},
],
)
print(response.choices[0].message.content)
# 예시 출력:
# [분석]
# 긍정: "Beautiful graphics", "immersive world"
# 부정: "constant crashes", "lag", "make it unplayable"
# 판단: 부정적 표현이 더 강함
# [결론]
# 감성: NEGATIVE
위 리뷰는 "그래픽은 예쁘고 몰입감 있지만 잦은 크래시와 렉으로 플레이 불가"라는 내용입니다. 결과만 받으면 MIXED로 흐를 수 있지만, 단계를 밟게 하면 "기술 문제가 더 심각하다"는 판단까지 내려 NEGATIVE로 잡아냅니다. 왜 그렇게 판단했는지 근거가 함께 나오니 나중에 검증하기도 편하죠.
CoT의 숨은 장점은 디버깅 도구라는 것입니다. "디버그(debug)"는 프로그래밍에서 코드의 잘못(벌레, bug)을 잡아 고친다는 뜻인데요, 같은 발상으로 CoT는 AI가 어디서 잘못 판단했는지를 드러냅니다. 결과만 받으면 POSITIVE가 맞는지 알 수 없고 틀려도 이유를 모르죠. 추론 과정을 같이 받으면 "아, AI가 버그 언급을 무시하고 긍정만 봤구나" 하고 문제를 발견할 수 있습니다. 그러면 프롬프트에 "부정 표현도 반드시 확인해"를 덧붙여 고칠 수 있어요. 참고로 요즘 고급 모델에 들어가는 '싱킹(thinking)' 기능이 바로 이 CoT가 발전한 형태입니다.
4. 다양한 분석 — 프롬프트만 바꾸면 된다
감성 분석만 할 수 있는 게 아닙니다. 프롬프트만 바꾸면 정말 다양한 작업이 가능해요. 키워드 추출, 요약, 카테고리 분류(그래픽 얘기인지 스토리 얘기인지), 개선점 추출 등. 같은 리뷰 데이터에서 다양한 인사이트를 뽑는 거죠. 여기서 여러분의 창의성이 빛납니다.
먼저 키워드 추출입니다. 함수 구조는 감성 분석과 거의 같고 프롬프트만 다릅니다.
def extract_keywords(review):
"""리뷰에서 게임 관련 핵심 키워드 3개를 뽑는다."""
response = client.chat.completions.create(
model=MODEL,
messages=[
{"role": "system", "content":
"너는 게임 리뷰 분석 전문가야.\n"
"리뷰에서 핵심 키워드 3개를 추출해.\n\n"
"규칙:\n"
"- 게임과 관련된 키워드만 (graphics, story, bugs 등)\n"
"- 쉼표로 구분해서 출력\n"
"- 다른 설명 없이 키워드만\n\n"
"예시: graphics, story, multiplayer"},
{"role": "user", "content": f"리뷰: {review}"},
],
)
return response.choices[0].message.content
test = "The game has stunning visuals and an engaging story, but the multiplayer servers are always down."
print(extract_keywords(test))
# 예시 출력: graphics, story, multiplayer
규칙을 명시(게임 관련만, 쉼표 구분, 설명 없이)하고 예시를 하나 보여주면 키워드가 깔끔하게 나옵니다. 이렇게 형식을 못 박으면 나중에 .split(",")로 쪼개 바로 코드에 활용할 수 있어요.
다음은 한 줄 요약입니다. 길이를 제한하지 않으면 요약이 길어지니 "15단어 이내, 한 문장"으로 묶습니다.
def summarize_review(review):
"""리뷰를 핵심 의견 중심으로 한 문장(15단어 이내) 요약한다."""
response = client.chat.completions.create(
model=MODEL,
messages=[
{"role": "system", "content":
"너는 게임 리뷰 요약 전문가야.\n"
"리뷰를 한 문장(15단어 이내)으로 요약해.\n\n"
"규칙:\n- 핵심 의견만 담기\n- 감성(좋음/나쁨)을 포함\n- 한국어로 작성"},
{"role": "user", "content": f"리뷰: {review}"},
],
)
return response.choices[0].message.content
long_review = ("I've been playing this game for over 100 hours and I still can't get enough. "
"The combat system is amazing, the story keeps you hooked, and the graphics are "
"top-notch. However, I wish there was more endgame content.")
print(summarize_review(long_review))
# 예시 출력: 100시간 플레이해도 질리지 않는 매력적인 게임, 하지만 엔드게임 콘텐츠가 아쉬워요.
리뷰 1,000개를 한 줄씩 요약해 한눈에 훑고 싶을 때 유용하겠죠.
5. 멀티태스크 분석 — 한 번에 여러 분석을
지금까지 배운 퓨샷·CoT·다양한 분석을 하나로 합쳐 봅시다. 한 번의 API 호출로 감성 + 키워드 + 요약 + 판단 근거 네 가지를 동시에 뽑는 멀티태스크 분석 함수입니다. 같은 리뷰를 네 번 호출할 필요가 없으니 훨씬 효율적이에요. 출력은 JSON 형식으로 받아 후처리를 쉽게 만듭니다.
def analyze_review_full(review):
"""리뷰를 종합 분석: 감성 + 키워드 + 요약 + 근거를 JSON으로 한 번에."""
response = client.chat.completions.create(
model=MODEL,
messages=[
{"role": "system", "content":
"너는 게임 리뷰 분석 전문가야.\n"
"리뷰를 분석해서 다음 정보를 JSON 형식으로 추출해.\n\n"
"### 예시 ###\n"
'review: "Amazing graphics and story, but multiplayer is broken."\n'
"{\n"
' "sentiment": "MIXED",\n'
' "keywords": ["graphics", "story", "multiplayer"],\n'
' "summary": "그래픽과 스토리는 좋지만 멀티플레이어가 문제",\n'
' "reasoning": "긍정(graphics, story) vs 부정(broken multiplayer)"\n'
"}\n"
"### 예시 끝 ###\n\n"
"반드시 JSON 형식으로만 답해."},
{"role": "user", "content": f"리뷰: {review}"},
],
)
return response.choices[0].message.content
test = "Great game with beautiful visuals. However, it crashes frequently and the multiplayer is laggy."
result = analyze_review_full(test)
print(result)
여기에 퓨샷(예시 제공), CoT(reasoning으로 근거 요구), JSON 출력이 한 함수에 다 들어 있습니다. 토큰을 많이 주고받게 되니(멀티태스크는 한 번에 많은 걸 요청하므로), 비용 감각도 함께 가져가세요.
6. JSON 파싱과 try/except — 받은 결과를 데이터로
AI가 준 답은 사실 문자열입니다. 이걸 파이썬 딕셔너리로 바꿔야 result["sentiment"]처럼 값에 바로 접근할 수 있어요. 변환은 json.loads()가 합니다.
그런데 한 가지 함정이 있습니다. "반드시 JSON으로만 답해"라고 했는데도, 모델이 응답 앞에 ```json 같은 마크다운 표시를 붙여 보내는 경우가 종종 있습니다. 이러면 json.loads()가 바로 실패해요. 그래서 앞뒤의 군더더기를 떼어내는 정제 함수를 만들어 둡니다.
import json
def clean_json_string(json_string):
"""응답 앞뒤에 붙는 ```json 마크다운 표시를 제거해 순수 JSON 문자열만 남긴다."""
if json_string.startswith('```json\n'):
json_string = json_string[len('```json\n'):] # 앞의 ```json 제거
if json_string.endswith('\n```'):
json_string = json_string[:-len('\n```')] # 뒤의 ``` 제거
return json_string
cleaned = clean_json_string(result)
# try/except로 감싸는 이유: AI가 가끔 형식을 안 지킬 수 있기 때문
try:
parsed = json.loads(cleaned) # 문자열 → 딕셔너리
print("감성:", parsed["sentiment"])
print("키워드:", ", ".join(parsed["keywords"]))
print("요약:", parsed["summary"])
print("근거:", parsed["reasoning"])
except json.JSONDecodeError as e:
print("JSON 파싱 실패:", e) # 실패해도 프로그램이 멈추지 않게
print("원본:", cleaned)
try/except는 예외 처리 문법입니다. 프로그래밍에서 가장 곤란한 상황은 서비스가 통째로 멈추는 것인데요, try/except로 감싸면 에러가 나도 시스템이 멈추지 않고 다음 작업을 이어서 처리할 수 있습니다. 리뷰 1,000개를 돌리다 한 개가 형식을 어겨도, 그 한 개만 건너뛰고 나머지를 마저 처리하게 되는 거죠. (이 "에러가 나도 멈추지 않게"가 5강 에러 처리의 핵심으로 이어집니다.)
참고로
```json자체를 막는 다른 방법도 있습니다. 프롬프트에 "앞에 json이라는 말을 붙이지 마"라고 지시하거나, 위처럼 코드로 제거하거나 — 무엇을 할지(앞의 군더더기를 떼야 한다)를 알면 방법은 여러 가지입니다..replace()로 간단히 지울 수도 있고요.
이렇게 정제·파싱한 멀티태스크 함수는 반복문에 넣어 리뷰 여러 개를 줄줄이 분석할 수 있습니다. 구조화된 데이터로 받으니 통계를 내거나 시각화하기도 편하죠.
7. 오늘의 핵심 인사이트 — 프롬프트는 프로그램이다
오늘 배운 걸 한 문장으로 누르면: 프롬프트를 잘 설계하면, AI가 복잡한 작업을 수행하는 하나의 프로그램이 됩니다.
| 단순 프롬프트 | 설계된 프롬프트 |
|---|---|
| 한 번에 하나씩 질문 | 멀티태스크로 한 번에 여러 분석 |
| 형식 없는 답변 → 후처리 어려움 | 퓨샷으로 기준 제시, JSON으로 구조화 |
| 결과만 받아 검증 불가 | CoT로 추론을 유도해 근거까지 확보 |
프롬프트 엔지니어링은 코드 대신 우리의 말로 AI를 프로그래밍하는 것입니다. 새롭게 각광받는 영역이니, 손에 익도록 많이 연습해 두세요.
🔑 핵심 용어
| 용어 | 쉬운 설명 |
|---|---|
| 제로샷(Zero-shot) | 예시 없이 지시만 하는 방식. 넓은 탐색·브레인스토밍에 적합 |
| 퓨샷(Few-shot) | 입력-출력 예시를 보여주고 지시. 기준을 못 박아 일관성↑ (예시 3~5개가 적당) |
| 생각의 사슬(CoT) | "단계별로 생각해"로 추론 과정을 드러내기. 정확도↑ + 디버깅 용이 |
| 멀티태스크 분석 | 한 번 호출로 감성·키워드·요약·근거를 동시에 추출 |
| JSON 출력 | 중괄호로 구조화된 형식. 후처리·통계·시각화가 쉬움 |
json.loads() |
JSON 문자열을 파이썬 딕셔너리로 변환 |
try/except |
예외 처리 문법. 에러가 나도 멈추지 않고 다음 작업 진행 |
| 정제 함수(clean) | 응답 앞뒤의 ```json 같은 군더더기를 떼 순수 JSON만 남기기 |
🎮 게임 기획자 시점
오늘의 세 기법은 곧 분석의 해상도를 끌어올리는 손잡이입니다. 퓨샷으로 우리 게임만의 판정 기준(예: "엔드 콘텐츠 부족 언급은 부정으로")을 못 박고, CoT로 왜 그렇게 분류됐는지 근거를 받아 두면, 리뷰 분석 결과를 회의 자리에서 "근거 있는 데이터"로 들이밀 수 있습니다. 특히 멀티태스크 분석은 리뷰 한 건에서 감성·키워드·요약을 한꺼번에 뽑으니, 패치 직후 폭증하는 리뷰를 하루치도 안 되는 비용으로 자동 분류할 수 있죠. 코딩은 AI가 하고, "어떤 기준으로 무엇을 물을지" 설계하는 건 도메인을 아는 기획자의 몫입니다.
⚠️ 흔한 오해 / 짚고 갈 점
- "퓨샷이 항상 제로샷보다 좋다"는 오해. 정해진 형식이 필요할 땐 퓨샷, 넓게 탐색할 땐 제로샷이 맞습니다. 목적에 맞춰 고르세요.
- 예시는 많을수록 좋다? 아닙니다. 예시가 늘수록 토큰 비용이 커집니다. 긍정·부정·혼합 각 1개씩, 3~5개면 충분합니다.
- "JSON으로만 답해"라고 했으니 그대로 파싱된다? 아닙니다. 모델이
```json을 붙이거나 형식을 어길 수 있어, 정제 함수 +try/except가 필요합니다. - CoT는 느리고 비싸다는 점도 기억하세요. 추론 과정만큼 토큰을 더 씁니다. 간단한 작업엔 굳이 안 써도 됩니다.
✅ 30초 정리
- 퓨샷: 예시를 보여주면 AI가 우리 기준을 파악해 일관된 결과를 냅니다.
- CoT: "단계별로 생각해" 한마디로 정확도가 오르고, 추론 과정이 디버깅 도구가 됩니다.
- 멀티태스크 + JSON: 한 번 호출로 여러 분석을 받고, 구조화된 출력으로 후처리를 자동화합니다. 단, 정제 함수와
try/except로 형식 흔들림에 대비하세요.
▶️ 다음 강 연결
다음 18차시는 프로젝트 시간입니다(영상 없이 직접 실습). 오늘 배운 퓨샷·CoT·멀티태스크 기법을 실제 리뷰 데이터에 적용해, 여러 리뷰를 반복 처리하고 결과를 정리·저장하는 것까지 해봅니다. 1주차에 직접 수집해 CSV로 저장한 여러분의 리뷰 데이터를 넣어 보길 권합니다 — 샘플보다 훨씬 의미 있는 결과가 나옵니다. 그리고 이어지는 19차시(감성 분석 심화)에서는 오늘의 "positive/negative/mixed"를 넘어, 무엇에 대한 어떤 감정인지를 더 세밀하게 쪼개는 방법으로 나아갑니다.