실제 사례에서 프롬프트 엔지니어링으로 정확도를 17%에서 91%로 끌어올리기
이 글은 실전 LLM 앱 구축을 다루는 시리즈의 첫 번째 편입니다. 오늘은 고객 지원 티켓 분류기를 만들고, 정확도를 다섯 배 향상시키겠습니다. 이 글은 AI 번역본입니다. 오역이 의심되는 부분이 있다면 댓글로 알려주세요.
Created on September 15|Last edited on September 15
Comment
소개
프로덕션 환경에서 LLM을 제품화하는 과정은 전통적인 소프트웨어와 상당히 다릅니다. 소프트웨어에서는 애플리케이션을 만들어 내는 코드가 최종 산출물이지만, LLM(ML과 유사)에서는 과정 중에 시도한 모든 것—데이터, 프롬프트, 파이프라인, 평가 지표 등—이 곧 산출물입니다. ML 엔지니어는 실험 중심의 사고방식을 받아들이고, 이 과정의 구성 요소들이 실제로는 창출되는 지적 재산임을 인식해야 합니다.
오늘날 기업들은 LLM을 다양한 기능에 폭넓게 배치하고 있습니다. 예를 들어 고객 지원 티켓 자동화, 영업 데이터 정리/매칭, 내부 문서화, 마케팅 문구 자동 생성 등이 있습니다.
이 시리즈에서는 한 가지 실제 사용 사례를 단계별로 안내해 드리겠습니다: 우리 고객 성공 티켓 분류하기 GPT-4의 도움을 받아 진행했습니다. 이유는 간단합니다. 문의를 주시는 모든 분께 최대한 실제적인 피드백을 드리려 하지만, 티켓별로 필요한 대응은 매우 다양합니다. 존재론적이거나 중대한 이슈를 다루는 메시지는 로그인 문제로 어려움을 겪는 분의 메시지와는 다르게 다뤄야 합니다. 분류 작업을 넘어, 전체 지원 질문 데이터셋을 이해하면 공통 주제와 개선이 필요한 영역, 우선순위를 높여야 할 제품 기능까지 도출할 수 있습니다. 또 하나 중요한 사실은, 많은 지원 티켓이 애초에 우리의 제품과 관련이 없다는 점입니다. 이를 프로그램적으로 식별할 수 있으면 고객 성공 조직이 더 효율적으로 일하고, 실행 불가능한 메시지를 읽느라 낭비하는 시간을 줄일 수 있습니다.
요점은 각 메시지를 개별적으로 더 잘 이해할수록 좋다는 것입니다. 그리고 전체적으로 볼수록, 사용자들을 더 잘 도울 수 있습니다.
오늘은 완전히 처음부터 시작해 몇 가지 평가 하네스를 시도해 보고, 프롬프트를 다듬어 어디까지 성능을 끌어올릴 수 있는지 확인해 보겠습니다. 시리즈의 후반부에서는 파인튜닝부터 RAG, 배포까지 모두 다룰 예정이지만, 그에 앞서 기반을 먼저 탄탄히 마련해야 합니다.
오늘 바로 그 기반을 만들어 봅시다! 아래 Colab 링크를 통해 이 프로젝트의 코드를 따라 하실 수 있습니다:
목차
소개목차데이터 점검하기범위 정의하기성능 평가수동 평가자동화된 평가프롬프트 엔지니어링구분 기호를 사용해 모델을 돕고 프롬프트 인젝션을 방지하기 프롬프트 컨텍스트 창에 예시 추가하기클래스의 의미 설명모델의 구성 파라미터 변경JSON 또는 HTML 같은 구조화된 출력 요청하기단계를 구체적으로 지정하기각 단계의 근거를 요청하기사고 사슬 유도 프롬프트 작성프롬프트 체이닝프롬프트용 맞춤 평가결론
데이터 점검하기
우리 고객 성공 팀(고마워요, Frida와 Artsiom!)이 약 26,000개의 메시지로 구성된 데이터셋을 보내주었습니다. 임의로 하나를 골라 살펴보고, 이 데이터로 무엇을 할 수 있을지 탐색해 봅시다. 가독성을 위해 메시지 본문을 따로 떼어 보여드리겠습니다.
안녕하세요, `wandb agent`로 학습 스크립트를 실행할 때 코드에서 발생한 치명적 예외가 콘솔에 출력되지 않는 것을 발견했습니다. 같은 코드를 `python train.py`로 직접 실행하면 명령어 출력에서 예외가 보입니다. 에이전트가 콘솔로의 예외 출력을 억제하는 것처럼 보이는데요. 이런 현상에 대해 들어보신 적 있나요?
전체 JSON:
{"description": "Hi, I've noticed that when I run a training script through `wandb agent` , a fatal exception in the code is not printed out to the console. While when I run the same code directly with `python train.py` I see the exception in the command output. Looks like if the agent was suppressing the exception printout to the console. Have you heard about something like that?","raw_subject": "[SDK] wandb agent fatal exception not printed","subject": "[SDK] wandb agent fatal exception not printed","priority": "urgent","problem_id": null,"tags": ["component_cli","enterprise_customer","halp","p0","question","sweeps"],"id": 36380,"question": "question","360042457771": ["enterprise_customer"],"360042775872": ["component_cli"],"360042457751": "sweeps","360044019192": null,"360041678631": "https://weightsandbiases.slack.com/archives/C01L79NKX5L/p1669025253633279?thread_ts=1669007664.253479&cid=C01L79NKX5L","360041794452": null,"4419373106452": null,"4419370133012": null,"4419373051540": null,"4419370299284": null,"4419380461716": null,"4417243931028": false,"4419380411796": null}
바로 메시지 본문 외에도 몇 가지 유용한 필드가 있음을 알 수 있습니다. 질문, 태그, 그리고 우선순위 후반의 몇몇 필드는 그렇지 않은 반면, 여기까지는 꽤 유망해 보입니다. 해당 필드들은 비식별화하고, 데이터를 정제한 뒤 아래와 같은 형태로 만들겠습니다:
{"description": "Hi, I've noticed that when I run a training script through `wandb agent` , a fatal exception in the code is not printed out to the console. While when I run the same code directly with `python train.py` I see the exception in the command output. Looks like if the agent was suppressing the exception printout to the console. Have you heard about something like that?","question": "question","priority": "urgent","tags": ["component_cli","enterprise_customer","halp","p0","question","sweeps"]}
여기에는 우리가 신경 쓰는 네 가지 필드만 남기고 불필요한 잡음을 제거했습니다. 이제 진짜로 시작할 시간입니다.
범위 정의하기
장기적으로는 이 티켓들을 다양한 방식으로 분류하고 싶겠지만, 제 첫 번째 직감은 태그를 예측하는 것이었습니다. 하지만 결과적으로 그 직감은 틀렸습니다.
길게 늘어놓지는 않겠지만, 이것이 모델링 초기 단계에서 아주 흔한 현상이라는 점은 짚고 넘어가고 싶습니다. 머신러닝은 실험 과학이기에 처음부터 항상 성공적으로 굴러가지는 않습니다. 첫 시도가 기대만큼 성과를 내지 못한다면, 과감히 방향을 틀고 새로운 접근을 시도할 준비가 되어 있어야 합니다.
태그의 문제는 너무 많았다는 점이었습니다. 실제로 500개가 넘었습니다. 여러 기법을 시도했지만 정확도를 40%보다 크게 끌어올리지 못했습니다. 주된 문제는 티켓 본문이 태그를 예측하는 데 충분히 유의미하지 않았고, 일부 태그는 잘못 적용되어 있었다는 것입니다. 즉, 애초에 범위를 너무 넓게 잡았던 셈입니다.
대신 W&B 관련 여부와 티켓 본문이 버그, 기능 요청, 일반 질문 중 어느 것인지 판별하는 방향으로 전환했습니다. 빠른 평가 세트를 만들기 위해 예시 몇 개를 직접 라벨링했습니다.
Goal: Given the support text, predict if 'question' is one of the following:'type_feature_request''type_bug''none''question'
교훈복잡함을 더하기 전에 더 단순한 문제에서 성공하는 것이 지나치게 야심 차게 시작하는 것보다 낫습니다. 까다로운 문제에 착수하기 전에 우선 제대로 작동하는 것을 구축하세요.
성능 평가
프롬프트 기법을 시도하기 전에, 성능을 평가할 방법이 필요합니다. 결국 모델의 성능을 제대로 평가할 수단이 없다면 얻을 수 있는 중요한 교훈을 놓치고, 우리가 하는 모든 실험은 무의미해집니다. 즉, 이 단계에서 실제 모델링을 하지는 않지만, 이후에 쌓아 올릴 수 있는 기준선을 마련하고자 합니다.
우리가 할 일은 간단한 프롬프트를 사용해 무작위로 선정한 고객 지원 티켓을 어떻게 처리하는지 평가하는 것입니다. 기본 프롬프트는 다음과 같습니다:
"""Classify the text delimited by triple backticks into one of the following classes.Classes: 'type_bug', 'none', 'type_feature_request', 'question'Text: ```{ticket_text}```Class:"""
다시 강조하자면—위 코드에서도 볼 수 있듯—우리 모델은 정답이 명확한 항목들에 대해 얼마나 잘 수행하는지로 평가할 수 있습니다. 우리는 모델이 정확하게 식별해 주기를 기대하고 있습니다. 버그, 기능 요청, 그리고 질문 덧붙이자면, 많은 다른 LLM 활용 사례(요약이나 카피라이팅 같은 텍스트 생성 작업)를 생각해 보면, 우리는 이런 사치를 누리지 못합니다.
다음 섹션에서는 우리의 사례, 즉 정오가 명확히 갈리는 출력에 대해 LLM을 평가하는 방법을 다룹니다.
수동 평가
먼저 간단한 프롬프트로 고객 지원 티켓을 테스트하고 결과를 수동으로 확인해 봅니다. 아직 클래스 균형은 신경 쓰지 말고, 모델을 평가할 무작위 예시를 자유롭게 선택하세요.
모델이 특정 예시에서 실패한다면, 그 예시들을 프롬프트에 추가하는 것을 고려하세요. 프롬프트를 수정할 때는 간단한 회귀 테스트를 수행해 새 프롬프트가 기존 예시에서도 제대로 작동하는지 확인하는 것이 좋습니다.
기능적 관점에서 우리는 성능이 완벽하지 않더라도 모델이 제대로 작동하고 모든 것이 구동되는지 확인하고 있습니다. 실제로 프롬프트 엔지니어링에 들어가기 전까지 성능이 뛰어나지 않을 가능성이 큽니다. 하지만 프롬프트는 최소한 일부 예시 데이터에 대해서는 모델이 정답을 맞히도록 이끌어야 합니다.
기준선으로 성공률을 계산한 뒤 자동화된 평가로 넘어가세요.
자동화된 평가
모델을 언제까지나 수동으로 평가할 수는 없으므로, 어느 시점에서는 자동화된 평가를 구축하는 것이 좋습니다. 이 단계에서는 예시 데이터를 늘리고 클래스 균형에도 신경 써야 합니다. 예측할 클래스가 버그, 기능 요청, 질문의 세 가지뿐이므로, 100개의 평가 세트 안에 각 클래스를 충분한 비율로 포함하도록 해야 합니다.
제가 한 일은 사용자 메시지와 정답을 매핑한 Python 딕셔너리를 만드는 것이었습니다. 그 딕셔너리의 예시들을 순회(loop)하면서 각 메시지/정답 쌍으로 LLM을 호출했고, 점수를 계산했습니다(여기서는 모델의 출력이 이상적인 정답과 일치한 횟수의 평균만 사용).
우리 앱을 평가하기 위해 간단한 평균 정확도 점수를 사용하고, 미리 정의한 100개 예시로 구성된 평가 세트에서 프롬프트를 평가하겠습니다. 다음은 우리가 사용할 코드입니다:
def basic_prompt(evalset_start=400, evalset_end=500):count, score_tags = 0, 0for element in filtered_data[evalset_start:evalset_end]:# print(element)ticket_text = element['description']# Prompt – Classify Classprompt = f"""Classify the text delimited by triple backticks into one of the following classes.Classes: {desired_tags}Text: ```{ticket_text}```Class: """response = get_completion(prompt)print("Prediction: "+response)if(response == element['question']):score_tags += 1print("Correct. Actual: "+element['question'])else:print("Incorrect. Actual: "+element['question'])count += 1print()print("__________________")print(f"Priority Accuracy: {score_tags/count}")return score_tags/countacc['basic_prompt'] = basic_prompt(evalset_start_all, evalset_end_all)
교훈처음 측정 지표는 다소 실망스러울 수 있지만, 평가 하네스를 일찍 구축해 두면 파이프라인이 기대대로 동작하는지 확인할 수 있고, 프롬프트 엔지니어링 실험도 전 과정에서 동일한 기준으로 평가할 수 있습니다.
이제 본격적으로 재미있는 부분으로 들어가 봅시다:
프롬프트 엔지니어링
범위와 초기 평가 하네스는 준비됐습니다. 다음 단계는 프롬프트를 정교화하는 것입니다.
이 실험에서 저는 오직 프롬프트만 수정해 정확도를 17%에서 90% 이상으로 끌어올렸습니다. 아래 표와 그래프에서 전반적인 접근 방식을 볼 수 있지만, 걱정하지 마세요. 중요한 변경 사항들을 하나씩 자세히 살펴보겠습니다!

각 실험과 그에 따른 정확도

구분 기호를 사용해 모델을 돕고 프롬프트 인젝션을 방지하기
구분 기호를 사용해 프롬프트의 어느 위치에 사용자 입력을 삽입할지 지정할 수 있습니다. 이렇게 하면 모델이 사용자 입력의 시작과 끝을 정확히 구분할 수 있어, 수십에서 수백 줄에 달할 수 있는 지원 티켓 같은 경우에 특히 유용합니다.
이는 악의적인 사용자가 우리의 LLM이 해서는 안 되는 작업을 하도록 조작하려는 프롬프트 인젝션을 방지하는 데도 도움이 됩니다. 구분 기호는 다음과 같은 아무 것이나 사용할 수 있습니다: ```, """, < >, <tag> </tag>, :.
제가 기본 프롬프트를 구성한 방식은 다음과 같습니다(실제로 우리가 평가 하네스를 설정할 때 사용했던 프롬프트입니다):
"""Classify the text delimited by triple backticks into one of the following classes.Classes: 'type_bug', 'none', 'type_feature_request', 'question'Text: ```{ticket_text}```Class:"""
우리의 기본 프롬프트는 다음과 같았습니다 정확도 17%시작은 했지만 훨씬 더 개선해야 합니다. 몇 가지 예시를 추가해 봅시다:
프롬프트 컨텍스트 창에 예시 추가하기
모델에 프롬프트 예시와 기대되는 생성 출력 예시를 구체적으로 제공하는 것은 성능을 빠르게 끌어올리는 가장 효과적인 방법 중 하나입니다. 이렇게 하면 LLM이 우리의 문제를 더 잘 이해하도록 도울 뿐 아니라, 예시(이번 경우에는 고객 지원 티켓)의 복잡도, 톤, 간결성까지 암묵적으로 학습하도록 돕습니다.
또한 모델이 올바른 형태의 출력을 생성하도록 안내하고 있습니다. 예를 들어, 간단한 장난감 문제를 통해 작은 변화만으로도 원하는 출력을 유도할 수 있음을 보여줄 수 있습니다. 예시를 보면, 아래처럼 프롬프트를 A:로 끝내면 모델이 자연스럽게 그 뒤에 답을 채웁니다. 예시 답안을 어떻게 구성하느냐에 따라 모델의 응답 복잡도를 구체적으로 지정할 수 있습니다.

LLM은 보통 예시 없이도 제로샷 학습을 꽤 잘하지만, 더 작은 모델을 선택하는 경우에는 원샷 또는 퓨샷 학습을 적용해 기대되는 정답 예시를 포함하면 성능을 상당히 끌어올릴 수 있습니다.
이번 실험에서는 동일한 기본 프롬프트 골격에 예시를 서로 다른 개수로 추가해 보았습니다.
"""Classify the text delimited by triple backticks into one of the following classes.Classes: {desired_tags}Text: ```{ticket_text1}```Class: questionText: ```{ticket_text2}```Class: type_bug<...more examples here>Text: ```{ticket_text}```Class:"""
예시 다섯 개를 추가하자 정확도가 25%에서 30%로 향상되었고, 프롬프트에 무작위 예시 20개를 더 추가하자 정확도가 다음과 같이 올랐습니다 70%.
교훈우리 같은 문제에서는 세밀한 프롬프트 엔지니어링에 앞서 예시를 추가하는 것을 고려하세요. 사람과 마찬가지로, 기계도 문제를 화려하게 설명하는 것보다 실제 사례에서 더 잘 학습하는 경우가 많습니다.
클래스의 의미 설명
“버그”, “기능 요청”, “질문”처럼 흔한 용어라도 각 용어의 정의를 명확히 정해 두는 것은 단순하지만 효과적인 접근입니다. 우리의 경우, 이로 인해 정확도가 5% 상승했습니다:
"""Given the following description for each class:type_feature_request: A request for a feature by a user of Weights & Biases.type_bug: A bug report by a user of Weights & Biases.question: If the request is not related to Weights & Biases; or doesn't fit the above 2 categories.Classify the text delimited by triple backticks into one of the following classes.Classes: {desired_tags}{examples}Text: ```{ticket_text}```Class: """
모델의 구성 파라미터 변경
프롬프트를 바꾸는 것 외에 할 수 있는 또 다른 일은 모델 구성값을 조정하는 것입니다. 각 파라미터는 모델이 다음 단어를 생성할 때의 의사 결정에 영향을 줄 수 있습니다. Together.ai 같은 LLM 플레이그라운드에서는 이러한 추론 시점 파라미터들을 직접 조절해 볼 수 있습니다.
- 최대 토큰: 모델이 생성할 토큰 수를 제한합니다. 이는 모델이 다음 단어를 고르기 위해 선택 과정을 반복하는 횟수에 상한을 두어, 시간과 연산 자원을 절약합니다.
- 무작위 샘플링: 항상 가장 확률이 높은 단어만 고르는 대신, 모델이 무작위로 단어를 선택합니다. 이렇게 하면 변동성이 도입되어 단어가 반복되지 않도록 할 수 있습니다.
- 탑 K 및 탑 P 샘플링무작위 샘플링을 사용하면 모델이 지나��게 창의적으로 동작해 단어를 무작위로 고를 수 있습니다. 탑 P와 탑 K 샘플링을 사용하면 변동성은 유지하되, 모델이 선택할 수 있는 단어의 범위를 제한할 수 있습니다. 탑 K는 확률이 가장 높은 k개의 토큰에서만 선택합니다. 탑 P는 누적 확률의 합이 p를 넘지 않는 예측들만 선택합니다. 탑 K에서는 모델이 고를 수 있는 토큰의 개수를 지정하고, 탑 P에서는 총 확률 질량을 지정합니다.
- 온도: 또한 출력의 무작위성을 제어하는 데 도움이 됩니다. 온도가 높을수록 무작위성이 커지고 모델이 더 창의적으로 동작합니다. 반대로 온도가 낮을수록 모델은 더 예측 가능해집니다. 다른 샘플링 기법들이 모델이 고르는 예측의 범위만 바꾸는 것과 달리, 온도를 바꾸면 모델의 예측 자체가 달라집니다.
모델 하이퍼파라미터를 이것저것 조정해 보았고, 온도를 조절하니 소폭의 성능 향상이 있었습니다. 예시 20개를 추가하고 온도를 바꾸자 다음과 같은 결과를 얻었습니다. 정확도 90%.
JSON 또는 HTML 같은 구조화된 출력 요청하기
모델이 매우 구체적인 구문으로 답하도록 요청할 수 있습니다. 이는 사람이 출력을 해석하기 쉽게 해 줄 뿐 아니라, 프롬프트 체이닝을 하고자 할 때 특히 유용합니다. 다음 LLM 호출에서 필요한 형식에 맞춰 출력을 요청하면 됩니다.
오늘 목표로 삼은 단일 작업을 넘어 더 많은 일을 이 모델에 맡기고자 할 때 이 방식을 활용할 수 있습니다. 모델은 티켓 요약, 권장 다음 조치, 그리고 추가 분류를 담은 JSON 객체를 반환합니다. 그런 다음 이 JSON 출력을 Python 리스트에 넣어 복잡한 워크플로를 구성할 수 있습니다!
예시:
"""Given the text delimited by triple backticks, perform the following actions:1 - Summarize what the user wants in 1-2 lines.2 - Recommend a next action based on the user' request.3 - Determine if the request is related to the product or company 'Weights & Biases'4 - Classify the into one of the following classes. Classes: {desired_tags}5 - Output a json object that contains the following keys: summary, recommended_action, is_wb, classHere's the text ```{ticket_text}```Make sure the output is only a json object."""
단계를 구체적으로 지정하기
제가 효과를 본 흥미로운 프롬프트 기법 중 하나는, 모델이 거쳐야 할 단계를 명시하고 그 과정을 통해 최종 출력을 생성하게 하는 방식이었습니다. 모델이 임의의 방식으로 문제에 접근하도록 두는 대신, 먼저 사용자가 원하는 바를 요약하고, 해당 요청이 W&B와 관련 있는지 판별한 다음, 클래스를 예측하도록 요구했습니다. 이렇게 하자 성능이 소폭 향상되었습니다.
"""Given the text delimited by triple backticks, perform the following actions:1 - Summarize what the user wants in 1-2 lines.2 - Recommend a next action based on the user' request.3 - Determine if the request is related to the product or company 'Weights & Biases'4 - Classify the into one of the following classes. Classes: {desired_tags}5 - Output a json object that contains the following keys: summary, recommended_action, is_wb, classHere are some examples help you with the classification step.{examples}And here's the text ```{ticket_text}```"""
각 단계의 근거를 요청하기
완전한 공개를 위해 말하자면, 모든 기법이 모든 문제에 통하는 것은 아닙니다. 이 사례가 그렇습니다. 저는 모델에게 각 단계의 근거를 설명하도록 요청해 보았습니다. 그런데 이로 인해 성능이 오히려 20% 감소하여, 결국 그 접근은 폐기했습니다.
"""Given the text delimited by triple backticks, perform the following actions:1 - summary: Summarize what the user wants in 1-2 lines.2 - summary_reasoning: Explain your reasoning for the summary.3 - recommended_action: Recommend a next action based on the user' request.4 - recommended_action_reasoning: Explain your reasoning for the recommended next action.5 - is_wb: Determine if the request is related to the product or company 'Weights & Biases'.6 - is_wb_reasoning: Explain your reasoning for detemining if the request is W&B related.7 - class: Classify the into one of the following classes. Classes: {desired_tags}8 - Output a json object that contains the following keys: summary, summary_reasoning, recommended_action, recommended_action_reasoning, is_wb, is_wb_reasoning, classHere are some examples help you with the classification step.{examples}And here's the text ```{ticket_text}```.Make sure the output is only a json object."""
사고 사슬 유도 프롬프트 작성
이전 프롬프트에서는 모델이 수행하길 바라는 일부 단계를 제가 명시했습니다. 하지만 출력 생성에 앞서 모델 스스로 중간 추론 단계, 즉 사고 사슬을 생성하도록 지시할 수도 있습니다. 이는 모델이 복잡한 추론 작업을 더 잘 수행하도록 돕습니다.

표준적인 사고 사슬 예시
프롬프트 체이닝
프롬프트 체이닝은 사고 사슬 유도 프롬프트와 비슷하지만, 하나의 프롬프트에서 모든 것을 처리하는 대신 문제를 여러 프롬프트로 나누고, 앞선 프롬프트의 생성 결과를 다음 프롬프트의 컨텍스트에 추가하는 방식입니다. 이렇게 하면 각 프롬프트가 더 집중되어 성능이 향상될 가능성이 높아지고, 각 단계가 전체 컨텍스트 윈도우를 사용할 수 있어 컨텍스트 한계를 피할 수 있으며, 비용을 줄이고 읽기 쉬운 코드로 이어집니다.
또한 각 단계를 별도의 프롬프트로 분리하면, 각 단계에 맞춘 맞춤형 평가를 작성할 수 있어 평가가 훨씬 쉬워집니다.
마지막으로, 1단계 결과에 따라 2단계에서 매우 다른 워크플로를 선택해야 하는 복잡한 워크플로가 있을 경우, 프롬프트를 단계별로 분해하면 다음에 호출할 프롬프트 세트를 동적으로 선택하는 데 도움이 됩니다.
이건 특히 복잡한 문제에서 매우 강력한 기법입니다. 하지만 우리 과제에서는 그다지 잘 맞지 않았습니다. 다만 모델을 확장해 태그 분류보다 더 미묘한 작업을 시키게 되면, 이를 위해 프롬프트 체이닝에 크게 의존하게 될 것입니다. 제가 사용한 코드는 다음과 같습니다:
# Prompt – Classify Classprompt = f"""Given the text delimited by triple backticks, perform the following actions:1 - summary: Summarize what the user wants in 1-2 lines.2 - summary_reasoning: Explain your reasoning for the summary.3 - recommended_action: Recommend a next action based on the user' request.4 - recommended_action_reasoning: Explain your reasoning for the recommended next action.5 - is_wb: Determine if the request is related to the product or company 'Weights & Biases'.6 - is_wb_reasoning: Explain your reasoning for detemining if the request is W&B related.7 - Output a json object that contains the following keys: summary, summary_reasoning, recommended_action, recommended_action_reasoning, is_wb, is_wb_reasoning, classAnd here's the text ```{ticket_text}```.Make sure the output is only a json object."""response_json = get_completion(prompt)# print("Prompt: "+prompt)print("Prediction: ")response = json.loads(response_json)print("Summary: "+response['summary'])print("Summary Reasoning: "+response['summary_reasoning'])print("Recommended Action: "+response['recommended_action'])print("Recommended Reasoning: "+response['recommended_action_reasoning'])print("Is W&B Related: "+str(response['is_wb']))print("Is W&B Related Reasoning: "+response['is_wb_reasoning'])prompt_2 = f"""Given the following info about a user request:1 - text delimited by triple backticks: ```{ticket_text}```2 - summary of what the user wants: {response['summary']}3 - recommended next action based on the user' request: {response['recommended_action']}4 - whether the request is related to the product or company 'Weights & Biases': {response['is_wb']}Classify the text into one of the following classes. Classes: {desired_tags}Here are some examples help you with the classification step.{examples}Only print the name of the class"""
프롬프트용 맞춤 평가
프롬프트가 복잡해질수록 평가 프롬프트도 함께 개선할 수 있습니다. LLM에게 모든 조건이 충족되는지 확인하도록 추가 점검 단계를 넣을 수 있습니다. 예를 들어, 중간 단계마다 합리적인 출력을 생성하는지, 추론이 타당한지, 다음에 제안한 행동이 W&B 문서에 근거하는지, 코드가 실행 가능한지 등을 확인하게 할 수 있습니다.
이러한 방법들은 모두 성능을 끌어올릴 수 있지만, 우리의 단순한 출발 과제에서는
이번 프로젝트에서는 시도하지 못했지만 다음에 꼭 해보고 싶은 고급 프롬프트 기법이 두 가지 더 있습니다. 바로 트리 오브 소트(Tree of Thought) 프롬프트와 ReAct입니다. 이 시리즈의 후속 글에서 더 자세히 다룰 예정이니, 더 발전된 프롬프트 기법을 배우고 싶다면 저는 다음을 추천합니다. 이 가이드.
결론
이번 실험에서 가장 크게 얻은 교훈은, 프롬프트를 단순히 반복 개선하고 모델 파라미터를 직관적으로 조정하는 것만으로도 출력 품질이 크게 향상된다는 점입니다. 실제로 그런 방식만으로 정확도를 17%에서 90% 이상으로 끌어올릴 수 있었습니다.
가장 효과적이었던 방법은 프롬프트에 다양한 예시와 해당 태그를 추가하고, 체인 오브 소트(chain-of-thought) 프롬프트를 활용해 모델이 문제를 단계별로 풀어가도록 돕는 것이었습니다.
이번 글은 실제 LLM 애플리케이션 구축을 다루는 연재의 첫 번째 기사입니다. 파인튜닝부터 고급 프롬프트 기법, 배포까지 모두 다룰 예정이니 다음 편도 기대해 주세요! 그동안 LLM 앱 빌딩을 주제로 한 무료 인터랙티브 코스를 수강하시려면 저희 강의 페이지를 방문하세요.
다음에 또 만나요!
Add a comment