[인공지능] Prompt Engineering에 대하여

Prompt Engineering에 대해 알아보자 :)

Sep 22, 2025
notion image

1. Prompt Engineering이란?

1-1. Prompt란?

  • 정의 : 인공지능 모델에게 주어지는 입력 텍스트
  • 특징
    • 사용자가 모델에게 질문을 하거나 명령을 내릴 때 사용하는 문장
    • LLM은 프롬프트의 내용과 구조에 따라 출력이 상이
    • 따라서, 단순한 입력을 넘어, 모델의 행동을 유도하는 핵심 수단
  • ex)
    • “서울의 날씨를 알려줘.”
    • “이 문장을 영어로 번역해줘.”
    • “이력서 예시를 3개만 보여줘.”

1-2. Prompt Engineering이란?

  • 정의 : 프롬프트를 정교하게 설계해 모델의 응답 품질을 향상시키는 기법
  • 특징
    • 같은 모델이라도, 프롬프트 문장 구조를 바꾸면 결과가 달라짐
    • 특히, RAG, 에이전트 설계, 자동화 워크플로우 등에서 핵심 기술로 자리잡는 중
    • 목적에 맞는 출력을 유도하기 위해, 다양한 전략 사용
      • 명확한 지시어 사용 (ex. “목차 형태로 요약해줘.)
      • 문맥 제공 (ex. “너는 친절한 영어 교사야.)
      • 예시 포함 (Few-shot prompting)
      • 역할 설정 (System prompt 등)

2. Prompt Engineering 구조

2-1. 모델 초기 구조

from langchain.chat_models import ChatOpenAI from langchain.prompts import ChatPromptTemplate from tqdm import tqdm import pandas as pd # 1. 시스템 프롬프트 정의 system_message = """ 너는 공정거래 및 민원 분류 전문가야. 아래 고객 민원을 읽고, 다음 3가지 정보를 정확히 추출해줘: 1. 이슈_유형: 불친절, 상품 품질, 위생, 환불 지연, 가격 불만, 지점 운영시간, 고객 응대 태도 중 1~2개 2. 해당_영역: 매장, 주방, 포장, 배달, 고객센터, 직원 태도 중 1~2개 3. 위법_가능성: 있음 / 없음 / 불명 + 법률 위반 시 관련 법률명(공정거래법, 표시광고법, 소비자기본법, 개인정보보호법)과 간단한 이유 설명 📌 출력은 반드시 아래 형식처럼 해줘: 이슈_유형: [유형1, 유형2] 해당_영역: [영역1, 영역2] 위법_가능성: 있음 (공정거래법 - 환불 불가 조항 미고지) """ # 2. 사용자 입력 프롬프트 템플릿 human_prompt_template = ''' 고객 리뷰: "{review}" ''' # 3. LangChain 템플릿 설정 prompt = ChatPromptTemplate.from_messages([ ("system", system_message), ("human", human_prompt_template), ]) # 4. LLM 모델 설정 (OpenAI gpt-4-turbo) llm = ChatOpenAI(model="gpt-4-turbo", temperature=0) # 5. 분석 함수 정의 def classify_review(review: str) -> dict: messages = prompt.format_messages(review=review) result = llm(messages) output = result.content # 결과를 파싱해서 dict로 반환 lines = output.splitlines() parsed = { "이슈_유형": "", "해당_영역": "", "위법_가능성": "" } for line in lines: if line.startswith("이슈_유형"): parsed["이슈_유형"] = line.split(":", 1)[1].strip() elif line.startswith("해당_영역"): parsed["해당_영역"] = line.split(":", 1)[1].strip() elif line.startswith("위법_가능성"): parsed["위법_가능성"] = line.split(":", 1)[1].strip() return parsed # 6. 데이터 불러오기 data = pd.read_csv("/mnt/data/sample_for_con_v1.csv") reviews = data["review_content"].fillna("") # 7. 전체 리뷰 분류 results = [] for review in tqdm(reviews): res = classify_review(review) results.append(res) # 8. 결과 병합 results_df = pd.DataFrame(results) final_df = pd.concat([data.reset_index(drop=True), results_df], axis=1) # 9. 저장 final_df.to_csv("분류_결과_통합.csv", index=False) print("✅ 분류 완료! → '분류_결과_통합.csv' 파일로 저장됨")

2-2. 고도화 위한 기법 후보

전략 이름
설명
적용 가능성
1️⃣ Few-Shot Prompting
예시 몇 개를 직접 제공하여 모델 유도
이미 잘 사용 중
2️⃣ Chain-of-Thought (CoT) Prompting
모델에게 생각의 흐름을 유도하여 복잡한 판단을 하게 함
위법 여부 판단, 법률 적용 등에서 매우 효과적
3️⃣ ReAct Prompting
Reasoning + Action을 반복하여 Tool을 호출하고 생각을 조정함 (LangChain Agent와 연계)
✅ 법률 자료 검색, DB 조회 등과 함께 사용
4️⃣ Self-Consistency Prompting
CoT를 여러 번 실행하고 가장 많이 등장한 결과 선택
✅ 안정성 강화, 분류 정확도 개선
5️⃣ Instruction Tuning (명시적 유도)
역할/목표를 아주 상세하게 프롬프트에 명시하여 유도
✅ 위법 판단, 조항 분류 등에서 강력
6️⃣ Multi-Prompt Strategy
상황별로 다른 프롬프트를 분기하여 사용
✅ "리뷰" vs "계약서" vs "법률 질문" 구분
7️⃣ Multi-Stage Prompting (Chain 구성)
출력 결과를 다음 단계 프롬프트의 입력으로 사용
✅ 예: 리뷰 → 분류 → 위법 판단 → 응답 생성
시나리오
추천 기법
민원 리뷰 자동 분류
✅ Few-shot + CoT
위법 가능성 판단 + 근거 생성
✅ CoT + Self-Consistency
계약 조항 분석 및 판별
✅ ReAct + Vector Retrieval
사용자 질문 응답
✅ Multi-stage + Retrieval + LLM 응답 생성
1️⃣
Few-Shot Prompting
: 모델에게 예시 몇 개를 함께 제공함으로써, 모델이 문맥 속에서 학습처럼 추론할 수 있도록 유도하는 방법
코드 확인
from langchain.chat_models import ChatOpenAI from langchain.prompts import FewShotPromptTemplate, ChatPromptTemplate from langchain.schema import HumanMessage, SystemMessage from tqdm import tqdm import pandas as pd # 1. 샘플 데이터 불러오기 data = pd.read_csv("/mnt/data/sample_for_con_v1.csv") reviews = data["review_content"].fillna("").tolist() # 2. Few-shot 예시 정의 (수작업 라벨링 or 미리 정제된 것) examples = [ { "review": "직원이 불친절하고 환불도 안 해줘요", "output": "이슈_유형: [불친절, 환불 지연]\n해당_영역: [직원 태도]\n위법_가능성: 있음 (소비자기본법 - 환불 거부)" }, { "review": "배달 상태가 엉망이고 포장이 다 터졌어요", "output": "이슈_유형: [상품 품질]\n해당_영역: [배달, 포장]\n위법_가능성: 없음" }, { "review": "운영시간이랑 실제 문 여는 시간이 달라요", "output": "이슈_유형: [지점 운영시간]\n해당_영역: [매장]\n위법_가능성: 있음 (표시광고법 - 영업시간 허위 표시)" } ] # 3. 시스템 프롬프트 system_prompt = """ 너는 공정거래 및 민원 분류 전문가야. 고객 리뷰를 읽고, 다음 3가지 정보를 정확히 추출해줘: 1. 이슈_유형: 불친절, 상품 품질, 위생, 환불 지연, 가격 불만, 지점 운영시간, 고객 응대 태도 중 1~2개 2. 해당_영역: 매장, 주방, 포장, 배달, 고객센터, 직원 태도 중 1~2개 3. 위법_가능성: 있음 / 없음 / 불명 + 법률 위반 시 관련 법률명(공정거래법, 표시광고법, 소비자기본법, 개인정보보호법)과 간단한 이유 설명 📌 출력은 반드시 아래 형식처럼 해줘: 이슈_유형: [유형1, 유형2] 해당_영역: [영역1, 영역2] 위법_가능성: 있음 (법률명 - 사유) """ # 4. Few-Shot Prompt 템플릿 구성 example_prompt = ChatPromptTemplate.from_messages([ ("human", '고객 리뷰:\n"{review}"'), ("assistant", "{output}") ]) few_shot_prompt = FewShotPromptTemplate( examples=examples, example_prompt=example_prompt, prefix=system_prompt, suffix='고객 리뷰:\n"{review}"', input_variables=["review"], example_separator="\n---\n" ) # 5. 모델 설정 llm = ChatOpenAI(model="gpt-4-turbo", temperature=0) # 6. 분석 함수 정의 def classify_with_fewshot(review: str) -> str: prompt_text = few_shot_prompt.format(review=review) messages = [ SystemMessage(content=system_prompt), HumanMessage(content=prompt_text) ] response = llm(messages) return response.content # 7. 적용 실행 results = [] for review in tqdm(reviews[:20]): # 전체가 많다면 [:20]만 일단 실행 output = classify_with_fewshot(review) results.append(output) # 8. 결과 저장 data["fewshot_output"] = results data.to_csv("/mnt/data/분류_결과_fewshot.csv", index=False) print("✅ Few-shot 기반 분류 완료! → '분류_결과_fewshot.csv' 저장됨")
결과 확인
# 리뷰 "주문했는데 30분 넘게 배달이 안 왔어요. 전화했더니 직원이 짜증을 냈어요." # 출력 결과 * 이슈_유형: [배달 지연, 불친절] * 해당_영역: [배달, 직원 태도] * 위법_가능성: 있음 (소비자기본법 - 부당 응대 및 지연된 서비스)
2️⃣
Chain-of-Thought (CoT) Prompting
: 모델이 사고 과정을 단계적으로 풀어가며 복잡한 판단을 할 수 있게 유도하는 방법
코드 확인
from langchain.chat_models import ChatOpenAI from langchain.prompts import ChatPromptTemplate from langchain.schema import SystemMessage, HumanMessage # 시스템 프롬프트: Chain-of-Thought 유도 system_prompt_cot = """ 너는 공정거래 및 민원 분류 전문가야. 고객 리뷰를 읽고, 이슈 유형과 관련 법률을 판단할 때, 사고 과정을 차례대로 설명하고 결론을 도출해야 해. 1. 먼저 고객 리뷰에서 어떤 문제가 발생했는지 파악해. 2. 그 문제에 대해 어떤 법률이 적용될 수 있는지 생각해봐. 3. 마지막으로, 위법 여부를 판단하고 그 근거를 명확하게 설명해. 📌 출력은 반드시 아래 형식으로 해줘: 이슈_유형: [유형1, 유형2] 해당_영역: [영역1, 영역2] 위법_가능성: 있음/없음 (법률명 - 사유) """ # Chain-of-Thought을 이용한 분석 함수 def classify_with_cot(review: str) -> str: prompt_text = f""" 고객 리뷰: "{review}" 위의 리뷰를 바탕으로, 사고의 흐름을 따라가며 위법 여부를 판단해 주세요. """ messages = [ SystemMessage(content=system_prompt_cot), HumanMessage(content=prompt_text) ] response = llm(messages) return response.content # 실행 예시 results_cot = [] for review in tqdm(reviews[:20]): output = classify_with_cot(review) results_cot.append(output) # 결과 저장 data["cot_output"] = results_cot data.to_csv("/mnt/data/분류_결과_cot.csv", index=False) print("✅ Chain-of-Thought 기반 분류 완료! → '분류_결과_cot.csv' 저장됨")
결과 확인
# 리뷰 "이 가게에서 환불을 요청했는데 거절당했어요." # 출력 결과 * 사고 흐름: "고객이 환불을 요청하고, 점주는 이를 거절했음." "이 경우, 환불을 거절하는 조건에 법적 근거가 있어야 함." "만약 점주가 소비자기본법에 위배된다면, 이를 거절할 수 없다는 법적 규정이 존재함." * 이슈_유형: [환불 지연] * 해당_영역: [직원 태도] * 위법_가능성: 있음 (소비자기본법 - 환불 거부)
3️⃣
ReAct Prompting
: Reasoning과 Action을 반복하여 모델이 Tool을 호출하고, 그 결과를 바탕으로 다시 생각을 조정하는 방식
코드 확인
from langchain.chat_models import ChatOpenAI from langchain.prompts import ChatPromptTemplate from langchain.schema import SystemMessage, HumanMessage # 시스템 프롬프트: ReAct 방식으로 사고 과정을 반복 system_prompt_react = """ 너는 공정거래 및 민원 분류 전문가야. 고객 리뷰를 읽고, 각 단계별로 사고를 반복하며 위법 여부를 판단해야 해. 1. 먼저 리뷰에서 문제를 추출하고, 관련 법률이 있는지 검색해. 2. 법률을 바탕으로 위법 여부를 판단하고, 관련 사례나 법적 근거를 제시해. 3. 필요한 경우 추가적인 법률 자료를 검색하거나 DB에서 정보를 조회해봐. 📌 출력은 반드시 아래 형식으로 해줘: 이슈_유형: [유형1, 유형2] 해당_영역: [영역1, 영역2] 위법_가능성: 있음/없음 (법률명 - 사유) """ # ReAct 방식으로 분석 함수 def classify_with_react(review: str) -> str: prompt_text = f""" 고객 리뷰: "{review}" 위 리뷰를 바탕으로, 사고의 흐름을 따라가며 법률적 판단을 반복적으로 진행해 주세요. """ messages = [ SystemMessage(content=system_prompt_react), HumanMessage(content=prompt_text) ] response = llm(messages) return response.content # 실행 예시 results_react = [] for review in tqdm(reviews[:20]): output = classify_with_react(review) results_react.append(output) # 결과 저장 data["react_output"] = results_react data.to_csv("/mnt/data/분류_결과_react.csv", index=False) print("✅ ReAct 기반 분류 완료! → '분류_결과_react.csv' 저장됨")
결과 확인
# 리뷰 "이 계약서에 명시된 조건이 이해가 안 가요." # 출력 결과 * 사고 흐름: "고객이 계약서의 불명확한 조항에 대해 질문함." "계약서를 분석하여 어떤 불공정한 조항이 있는지 검토 후, 해당 법률을 참조." "계약서에 위반되는 법률 조항이 있으면, 이를 감지하여 고객에게 알림." * 이슈_유형: [계약 조항] * 해당_영역: [법률, 계약서] * 위법_가능성: 있음 (상법 - 불공정 계약 조항)
4️⃣
Self-Consistency Prompting
: 하나의 질문에 대해 동일한 Chain-of-Thought 추론을 여러 번 반복하고, 그중 가장 자주 등장한 응답을 최종 결과로 채택하는 방식
코드 확인
from langchain.chat_models import ChatOpenAI from langchain.schema import SystemMessage, HumanMessage from collections import Counter import random # CoT 유도용 시스템 프롬프트 system_prompt_cot = """ 너는 공정거래 및 민원 분류 전문가야. 고객 리뷰를 읽고, 아래 사고 흐름에 따라 위법 가능성을 판단해. 1. 문제를 이해하고 판단 과정을 단계별로 적어. 2. 관련 법률과 사례를 바탕으로 위법 가능성을 서술해. 3. 아래 형식으로 결과를 요약해. 📌 출력 형식: 이슈_유형: [유형1, 유형2] 해당_영역: [영역1, 영역2] 위법_가능성: 있음/없음 (법률명 - 사유) """ # Self-Consistency 방식으로 추론 반복 def classify_with_self_consistency(review: str, n: int = 5) -> str: outputs = [] for _ in range(n): messages = [ SystemMessage(content=system_prompt_cot), HumanMessage(content=f'고객 리뷰:\n"{review}"\n\n위 리뷰에 대해 단계적으로 사고하며 판단해주세요.') ] response = llm(messages) outputs.append(response.content.strip()) # 결과 중 가장 많이 등장한 것 선택 most_common_output = Counter(outputs).most_common(1)[0][0] return most_common_output # 실행 results_consistency = [] for review in tqdm(reviews[:20]): output = classify_with_self_consistency(review, n=5) results_consistency.append(output) # 저장 data["self_consistency_output"] = results_consistency data.to_csv("/mnt/data/분류_결과_self_consistency.csv", index=False) print("✅ Self-Consistency 기반 분류 완료! → '분류_결과_self_consistency.csv' 저장됨")
결과 확인
# 리뷰 "점주는 교대 시간을 명확히 지키지 않고 저녁까지 일을 시켰어요." # 출력 결과 * 이슈_유형: [근무 시간] * 해당_영역: [노동, 근로기준법] * 위법_가능성: 있음 (근로기준법 제50조 - 정해진 근무시간 초과 근무)
5️⃣
Instruction Tuning
: 모델에게 역할, 목표, 판단 기준 등을 아주 명확히 안내하는 프롬프트 설계 방식
코드 확인
system_prompt_instruction = """ 너는 '불공정 행위 분석 전문가'야. 목표는 고객 리뷰를 읽고 아래 형식에 맞게 정리하는 것이야. 📌 반드시 이 형식으로 출력해야 해: 1. 이슈 유형 (예: 계약, 위생, 가격) 2. 관련 법령 (예: 상법, 공정거래법) 3. 위법 여부 (예/아니오)와 이유 (법적 조항) 리뷰가 모호할 경우엔 판단을 유보해도 괜찮아. """ def classify_with_instruction(review: str) -> str: messages = [ SystemMessage(content=system_prompt_instruction), HumanMessage(content=f"리뷰:\n{review}") ] response = llm(messages) return response.content.strip() results_instruction = [] for review in tqdm(reviews[:20]): output = classify_with_instruction(review) results_instruction.append(output) data["instruction_output"] = results_instruction data.to_csv("/mnt/data/분류_결과_instruction.csv", index=False) print("✅ Instruction 기반 분류 완료! → '분류_결과_instruction.csv' 저장됨")
결과 확인
# 출력 결과 * 이슈 유형: 근로 조건 * 관련 법령: 근로기준법 제56조 * 위법 여부: 예 - 초과 근무 수당 미지급
6️⃣
Multi-Prompt Strategy
: 입력의 성격을 분류한 후, 그에 맞는 다른 프롬프트 전략을 적용하는 방식
코드 확인
# 프롬프트 분기 def select_prompt(review: str) -> str: if "계약" in review or "조항" in review: return "contract" elif "리뷰" in review or "서비스" in review: return "service" else: return "general" # 각각의 프롬프트 템플릿 prompts = { "contract": "너는 계약서 분석 전문가야. 아래 문장을 계약서 조항으로 보고 불공정 여부를 판단해.", "service": "너는 고객 응대 분석가야. 아래 리뷰에서 문제를 추출하고 민원 유형을 분류해.", "general": "너는 공정거래 전문가야. 아래 문장을 분석해서 소비자 보호법 관련 여부를 판단해." } def classify_with_multi_prompt(review: str) -> str: category = select_prompt(review) system_prompt = prompts[category] messages = [ SystemMessage(content=system_prompt), HumanMessage(content=f"입력 문장:\n{review}") ] response = llm(messages) return f"[{category}] {response.content.strip()}" results_multi_prompt = [] for review in tqdm(reviews[:20]): output = classify_with_multi_prompt(review) results_multi_prompt.append(output) data["multi_prompt_output"] = results_multi_prompt data.to_csv("/mnt/data/분류_결과_multi_prompt.csv", index=False) print("✅ Multi-Prompt 기반 분류 완료! → '분류_결과_multi_prompt.csv' 저장됨")
결과 확인
# 리뷰 "계약 조항이 소비자에게 너무 불리하게 작성돼 있어요." # 출력 결과 * [contract] 이 조항은 불공정 계약 조항으로 간주될 수 있으며, 약관법 제6조 위반 가능성이 있습니다.
7️⃣
Multi-Stage Prompting
: 여러 단계의 프롬프트를 결합하여 결과를 도출하는 방식
코드 확인
from langchain.chat_models import ChatOpenAI from langchain.schema import SystemMessage, HumanMessage # Stage 1: 이슈 추출 def extract_issue(review: str) -> str: prompt = "리뷰에서 법률 관련 이슈를 한두 문장으로 정리해줘." messages = [ SystemMessage(content="너는 소비자 민원 분석가야."), HumanMessage(content=f"리뷰:\n{review}\n\n이슈는?") ] return llm(messages).content.strip() # Stage 2: 관련 법률 검색 def find_relevant_law(issue: str) -> str: prompt = "이 이슈와 관련된 법률이나 규정은 무엇이야? 법 조항도 포함해서 설명해줘." messages = [ SystemMessage(content="너는 법률 전문가야."), HumanMessage(content=f"이슈:\n{issue}") ] return llm(messages).content.strip() # Stage 3: 위법 판단 def judge_legality(law_info: str, review: str) -> str: prompt = "다음 법률 정보를 바탕으로, 리뷰가 법 위반인지 판단해줘." messages = [ SystemMessage(content="너는 위법 판단 전문가야."), HumanMessage(content=f"법률 정보:\n{law_info}\n\n리뷰:\n{review}") ] return llm(messages).content.strip() # Stage 4: 결과 정리 def summarize_final_output(issue: str, law_info: str, judgment: str) -> str: return f"""이슈_유형: [{issue}] 해당_영역: [법률] 위법_가능성: {judgment} → 관련 법률: {law_info}""" # 전체 흐름 통합 def classify_with_multistage(review: str) -> str: issue = extract_issue(review) law_info = find_relevant_law(issue) judgment = judge_legality(law_info, review) return summarize_final_output(issue, law_info, judgment) # 실행 results_multistage = [] for review in tqdm(reviews[:20]): output = classify_with_multistage(review) results_multistage.append(output) data["multistage_output"] = results_multistage data.to_csv("/mnt/data/분류_결과_multistage.csv", index=False) print("✅ Multi-Stage 기반 분류 완료! → '분류_결과_multistage.csv' 저장됨")
결과 확인
* 이슈_유형: [부당 해고] * 해당_영역: [법률] * 위법_가능성: 있음 → 관련 법률: 근로기준법 제23조 - 사용자는 정당한 이유 없이 근로자를 해고할 수 없다.