[서평] 데이터로 경험을 디자인하라
데이터로 경험을 디자인하라 도서에 대해 리뷰해보자 :)
Prompt Engineering에 대해 알아보자 :)
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' 파일로 저장됨")
전략 이름 | 설명 | 적용 가능성 |
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 응답 생성 |
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분 넘게 배달이 안 왔어요. 전화했더니 직원이 짜증을 냈어요." # 출력 결과 * 이슈_유형: [배달 지연, 불친절] * 해당_영역: [배달, 직원 태도] * 위법_가능성: 있음 (소비자기본법 - 부당 응대 및 지연된 서비스)
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' 저장됨")
# 리뷰 "이 가게에서 환불을 요청했는데 거절당했어요." # 출력 결과 * 사고 흐름: "고객이 환불을 요청하고, 점주는 이를 거절했음." "이 경우, 환불을 거절하는 조건에 법적 근거가 있어야 함." "만약 점주가 소비자기본법에 위배된다면, 이를 거절할 수 없다는 법적 규정이 존재함." * 이슈_유형: [환불 지연] * 해당_영역: [직원 태도] * 위법_가능성: 있음 (소비자기본법 - 환불 거부)
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' 저장됨")
# 리뷰 "이 계약서에 명시된 조건이 이해가 안 가요." # 출력 결과 * 사고 흐름: "고객이 계약서의 불명확한 조항에 대해 질문함." "계약서를 분석하여 어떤 불공정한 조항이 있는지 검토 후, 해당 법률을 참조." "계약서에 위반되는 법률 조항이 있으면, 이를 감지하여 고객에게 알림." * 이슈_유형: [계약 조항] * 해당_영역: [법률, 계약서] * 위법_가능성: 있음 (상법 - 불공정 계약 조항)
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조 - 정해진 근무시간 초과 근무)
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조 * 위법 여부: 예 - 초과 근무 수당 미지급
# 프롬프트 분기 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조 위반 가능성이 있습니다.
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조 - 사용자는 정당한 이유 없이 근로자를 해고할 수 없다.