SK플래닛 ai활용 데이터엔지니어 과정 2기/ML & DL

LLM 1 - 프롬프트, 컨텍스트

dev-lee 2026. 5. 28. 17:53

프롬프트 · 컨텍스트

프롬프트 엔지니어링은 LLM이 최적의 결과를 생성하도록 자연어로 구성된 입력을 설계·최적화하는 기술/방법론임.
Bedrock을 통해 Claude, GPT, Gemma를 단일 인터페이스로 호출하고, 모델별 프롬프트 전략의 차이를 실습함.

1. API 키 발급 및 연동

세 가지 LLM 벤더(Claude, OpenAI, Gemini)의 API 키를 발급하고 Bedrock을 통해 통합 연동하는 방법을 다룸.

1.1 벤더별 API 키 발급

  • Gemini — Google AI Studio에서 API 키 발급, 하루 1,500 call 무료 제공
  • AWS Bedrock — AWS 콘솔에서 Bearer Token 발급, 사용량 기반 과금 (토큰 제한 없음)
  • Claude — Anthropic Console에서 직접 발급

1.2 Gemini 연동 확인

from google import genai

client = genai.Client(api_key=GEMINI_API_KEY)

def gemini_invoke(prompt, history):
    res = client.models.generate_content(
        model='gemini-2.5-flash',
        contents=prompt
    )
    return res.text

gradio.ChatInterface(gemini_invoke, type='messages').launch()

1.3 Bedrock 연동 확인

import boto3

bedrock = boto3.client(
    service_name='bedrock-runtime',
    region_name='us-east-1',
)

models = {
    "anthropic": "us.anthropic.claude-haiku-4-5-20251001-v1:0",
    "openai":    "openai.gpt-oss-120b-1:0",
    "google":    "google.gemma-3-27b-it"
}

Bedrock을 사용하면 벤더별 API를 각각 관리하지 않고, 동일한 AWS SDK 코드베이스에서 모델명만 바꿔 다수의 LLM을 호출할 수 있음.


2. 프롬프트 엔지니어링

효과적인 프롬프트 구성 방법과 LLM 벤더별 최적화 전략을 다룸.

2.1 공통 구성 (90% 비중)

구성 요소 설명 예시

페르소나/역할 (Role) 구체적인 아이덴티티 부여 너는 10년차 재산 관리자야
배경/상황 (Context) 구체적인 배경 정보 제공 현재 가진 자산으로 은퇴 자금 설계
지시 (Task) 구체적인 요청/요구사항 10년 플랜 초안 작성, 손해비율 산정
출력 형식/제약 (Format) 토큰 제약, 형식, 말투 지정 500자 이내, 표 형태, 반말 금지

2.2 모델 특화 구성 (10% 비중)

  • Claude — XML 태그로 섹션을 명확히 구획, 논리적·긴 글 요청에 적합하여 맥락 파악이 우수함
  • GPT — 대화형에 최적화, Few-Shot 프롬프트 활용을 권장하며 단계별 결과 유도 방식이 효과적임
  • Gemini — 구글 검색 연동에 유리, 팩트 체크 및 트렌드 반영 지시에서 강점을 보임

2.3 모델별 프롬프트 예시 — 텀블러 SNS 초안 작성

공통 프롬프트 구성

# 역할
당신은 20년차 프로 카피라이터 입니다.

# 배경
우리 회사에서 이번에 "24시간 동안 얼음이 녹지 않는 셀럽형 텀플러"를 출시하였습니다.
타겟 고객은 하루 종일 아이스 커피를 달고 사는 20대 직장인입니다.

# 지시
타겟 고객에게 보낼 홍보용 SNS 초안을 작성해 주세요.

# 제약조건, 포맷
1. 톤앤매너: 유머러스하게, 위트 있게
2. 형식: 도입부(장점 4개) + 마무리(구매유도)
3. 분량: 400자 이내

Claude 특화 — XML 태그 구획

<role>
당신은 20년차 논리적이고 설득력 있는 프로 카피라이터입니다.
</role>

<context>
제품 : 24시간동안 얼음이 녹지 않는 셀럽형 텀블러
타겟 : 하루종일 커피를 마시는 20대 직장인
</context>

<instruction>
위 타겟에 대한 홍보용 SNS 초안을 작성하세요.
</instruction>

<constraints>
톤앤매너: 전문적이지만 너무 딱딱하지 않게
형식:
  - 도입부 : 텀플러의 장점(4개 포인트)
  - 마무리 : 구매유도
</constraints>

GPT 특화 — Few-Shot 샘플 제공

# few shot 제시 (샘플 제공)
"점심시간이 되면 오전에 구매한 아이스 아메리카노(아아)가 미지근한 맹물이 되지요?
 이 고통에서 벗어날 수 있는 ..."

Gemini 특화 — 검색 연동

# 특별지시
현재 한국 20대 직장인들이 오피스 아이템 트렌드를 검색하여,
이 텀블러가 왜 필수템인지 설명하는 문구를 도입부에 자연스럽게 녹여줘.

모델별로 동일한 주제에 대해 결과물이 상이하게 나옴. Claude는 논리·구조 중심, GPT는 친근한 대화체, Gemini는 트렌드 반영 문구가 강점.


3. GPT · Claude · Gemma Bedrock 호출 코드

벤더별 프롬프트 구조와 응답 파싱 방식의 차이를 구체적인 코드로 비교함.

3.1 범용 호출 함수

def llm_invoke(prompt='', vender="openai", history=None):
    res = bedrock.invoke_model(
        modelId=models[vender],
        body=json.dumps(prompt)
    )
    content = json.loads(res['body'].read())
    return content

3.2 벤더별 프롬프트 구조 비교

항목  Claude  GPT  Gemma(Google)
시스템 역할 "system" 별도 키 messages 내 role: system converse API의 system 파라미터
few-shot messages 배열에 직접 삽입 messages 배열에 직접 삽입 content에 [{"text": ...}] 형태
응답 파싱 content['content'][0]['text'] content['choices'][0]['message']['content'] res['output']['message']['content'][0]['text']
# Claude 응답 파싱
res['content'][0]['text']

# GPT 응답 파싱
res['choices'][0]['message']['content']

# Gemma(converse API) 응답 파싱
res['output']['message']['content'][0]['text']

Bedrock을 활용하면 동일 SDK에서 모델명만 교체하여 여러 LLM을 호출할 수 있음. 다만 프롬프트 구성 방식과 응답 파싱 구조가 모델별로 다르므로, 인터페이스 클래스로 추상화하는 설계가 권장됨. sklearn에서 어떤 알고리즘을 쓰든 fit()으로 학습하는 것과 같은 패턴.


4. 제로샷 · 원샷 · 퓨샷 프롬프트

LLM에게 예시를 얼마나 제공하느냐에 따라 결과 품질과 일관성이 달라짐.

4.1 유형 비교

유형 예시  개수  언제 쓰는가
Zero-shot 0 상식적 질문, 간단한 요약
One-shot 1 특정 포맷(JSON 등) 준수 필요 시
Few-shot 2~5 복잡한 분류, 특수한 규칙 적용 시

4.2 퓨샷 특징

  • 초기 서비스 구축 단계에서 파인튜닝 없이 활용 가능
  • 예시 품질이 높으면 파인튜닝 모델보다 더 좋은 성과를 내기도 함
  • 일정한 품질의 결과물 획득이 목적 — 사내 데이터를 직접 파인튜닝에 사용하면 비용·보안 이슈가 있으나, few-shot으로 동일 효과를 얻을 수 있음

4.3 리뷰 감정 분류 — 퓨샷 실습

GPT

few_shot_prompt = [
    {"role": "system",    "content": "당신은 커피 고객 리뷰 분석가입니다. '긍정','부정','가격민감' 중 하나로 분류하세요."},
    {"role": "user",      "content": "일찍와서 핫초코 샀어요~ 맛있고 좋아요👍"},
    {"role": "assistant", "content": "긍정"},
    {"role": "user",      "content": "직원이 인사를 않 해서 기분이 나빴습니다."},
    {"role": "assistant", "content": "부정"},
    {"role": "user",      "content": "스페셜 음료는 맛있는데 매일 사 먹기엔 가격이 너무 부담스럽네요"},
    {"role": "assistant", "content": "가격민감"},
]

Claude

# Claude는 system을 messages 밖 별도 키로 분리
prompt = {
    "anthropic_version": "bedrock-2023-05-31",
    "max_tokens": 100,
    "system": "당신은 커피 고객 리뷰 분석가입니다. '긍정','부정','가격민감' 중 하나로 분류하세요.",
    "messages": few_shot_prompt  # role: system 제외한 배열
}

Gemma (Google)

# content 값을 [{"text": ...}] 형태로 변환 필요
change = lambda x: [{"text": x}]

few_shot_messages = [
    {"role": "user",      "content": change("일찍와서 핫초코 샀어요~ 맛있고 좋아요👍")},
    {"role": "assistant", "content": change("긍정")},
    ...
]

# converse API 사용
res = bedrock.converse(
    modelId=models['google'],
    messages=final_messages,
    inferenceConfig={"maxTokens": 100, "temperature": 0}
)

Bedrock 기준 Claude·Gemma가 응답이 빠르고, GPT가 상대적으로 느림. 벤더 직접 API 사용 시 GPT는 Azure에서 빠른 응답 가능.


5. LangChain — LLM 오케스트레이션

여러 모델을 단일 인터페이스로 다루고, 프롬프트 템플릿·체인·메모리를 통합 관리하는 오픈소스 프레임워크.

5.1 LangChain 개요

  • 정의 — LLM 기반 AI 애플리케이션 개발을 돕는 오픈소스 오케스트레이션 프레임워크
  • 핵심 구성 요소
    • RAG — LLM이 모르는 지식을 검색 증강으로 제공, 프롬프트에 함께 전달
    • Agent — LLM이 스스로 판단하고 행동, A2A 연동 가능
    • Chain / Graph — 단방향(체인) 또는 순환(그래프) 워크플로우 구성
    • Memory — 단기 기억(대화 맥락)과 장기 기억(벡터 DB)으로 구분
    • LangSmith — 개발·디버깅·테스트·모니터링 통합 지원 (LLMOps)

5.2 프롬프트 템플릿 종류

클래스  용도
PromptTemplate 기본형, f-string / Jinja2 변수 치환
ChatPromptTemplate 대화형 모델용, system/human/ai 역할 구조
FewShotPromptTemplate 예시 리스트 + 접두사/접미사 기능
FewShotChatMessagePromptTemplate 실제 대화 스타일로 예시 주입

PromptTemplate — f-string

from langchain_core.prompts import PromptTemplate

input_prompt = PromptTemplate(
    input_variables=['place', 'content'],
    template="{place}에서 가장 {content} 놀이기구는?"
)

input_prompt.format(place='에버랜드', content='대기줄이 가장 긴')

PromptTemplate — Jinja2

input_prompt = PromptTemplate(
    input_variables=['items', 'user_q'],
    template_format='jinja2',
    template='''
다음 예시를 참고하여 마지막 질문에 대한 답변을 제시해줘.

{% for item in items %}
  Q: {{item.q}}
  A: {{item.a}}
{% endfor %}
  Q : {{user_q}}
  A:
'''
)

ChatPromptTemplate

from langchain_core.prompts import ChatPromptTemplate

chat_template = ChatPromptTemplate.from_messages([
    ("system", "당신은 {style} 스타일의 고용관리 상담사입니다."),
    ("human",  "저는 {name}입니다."),
    ("system", "네.. 반갑습니다."),
    ("human",  "제 고민은 {topic}입니다. 조언해주세요."),
])

FewShotPromptTemplate

from langchain_core.prompts import FewShotPromptTemplate

examples = [
    {"input": "높다",   "output": "낮다"},
    {"input": "빠르다", "output": "느리다"},
    {"input": "밝다",   "output": "어둡다"},
]

example_format = PromptTemplate.from_template("단어:{input}\n반대말:{output}")

few_shot_prompt = FewShotPromptTemplate(
    examples=examples,
    example_prompt=example_format,
    prefix="다음 단어의 반대말을 알려줘.",
    suffix="단어:{user_input}\n반대말:",
    input_variables=['user_input']
)

서비스 형태에 따라 채팅 계열(ChatPromptTemplate), 일반 처리(PromptTemplate)를 구분하여 사용하는 것이 기본 원칙임.


6. Bedrock + LangChain 결합

LangChain의 `ChatBedrock`을 통해 모델과의 인터페이스를 통일하고, 파이프라인(`chain`)으로 프롬프트 → LLM → 응답을 연결함.
from langchain_aws import ChatBedrock
from langchain_core.prompts import ChatPromptTemplate

def run_bedrock_llm(model_id, user_query, style):
    llm = ChatBedrock(
        client=bedrock_client,
        model_id=models[model_id],
        model_kwargs={"max_tokens": 1024, "temperature": 0.7}
    )

    prompt = ChatPromptTemplate.from_messages([
        ('system', '당신은 {style} 스타일의 20년차 초고수 투자자입니다.'),
        ('human',  '{input}')
    ])

    # chain 연결 : 프롬프트 구성 -> LLM 호출 -> 응답
    chain = prompt | llm

    res = chain.invoke({"input": user_query, "style": style})
    return res.content  # 랭체인 통해 출력 형식 통일

LangChain의 핵심은 파이프 연산자(|) 로 표현되는 체인 구조임. prompt | llm | output_parser 형태로 확장하면 응답 파싱까지 통일된 흐름으로 처리 가능. 어떤 모델을 써도 chain.invoke()로 호출하는 인터페이스가 동일하여, sklearn의 fit() 패턴처럼 모델 교체가 간편해짐.


7. 요약

주제  핵심 내용
프롬프트 공통 구성 Role + Context + Task + Format 4요소
모델별 특화 Claude(XML), GPT(Few-Shot), Gemini(검색 연동)
퓨샷 프롬프트 파인튜닝 없이 고품질 분류·생성 가능
Bedrock 단일 SDK로 다수 모델 호출, 응답 파싱 구조는 모델별 상이
LangChain 프롬프트 템플릿·체인·메모리·에이전트 통합 오케스트레이션
설계 원칙 모델별 인터페이스 추상화 + Pydantic으로 응답 구조 통일

LLM 활용의 핵심은 "어떤 모델이 더 좋은가"가 아니라, "어떤 모델의 특성에 맞게 프롬프트를 설계하는가"임. 공통 구성 4요소를 기반으로 모델 특화 전략을 레이어로 얹는 구조가 실무에서 가장 안정적인 접근법이며, LangChain은 이 과정을 코드 레벨에서 추상화하는 도구임.