프롬프트를 작성할 때 모델에게 ‘어떻게 행동하라’고 말하기보다, 원하는 행동을 예시로 보여주는 편이 왜 더 효과적인지 설명한다.
프롬프트를 쓸 때 사람들이 아주 흔히 저지르는 실수는 프롬프트를 이런 문장으로 시작하는 것입니다:
당신은 Google, Meta, NASA에서 분산 시스템을 설계한 40년 경력의 Staff+ 소프트웨어 아키텍트입니다. 업계 모범 사례를 따르는 최적의 프로덕션 준비 완료 코드를, 오직 그것만 출력하세요.
이건 대형 언어 모델에게 어떻게 행동해야 하는지를 보여주는 대신, 어떻게 행동해야 하는지를 말로 지시하는 프롬프트의 한 예입니다.
이런 스타일의 프롬프팅의 한 가지 문제는, 사람에게도 통하지 않는다는 것입니다. 예를 들어, 당신이 매니저이고 성과가 저조한 팀원을 도와주려 한다고 합시다. 그 팀원에게 이렇게 말하면 어떨까요? “야, 너는 저성과자가 아니야. 사실 너는 고성과자야! 고품질 코드를 작성해.” 정말로 그 사람의 성과나 코딩 실력이 좋아질 거라고 기대하나요? (그러지 않길 바랍니다!)
저성과자가 실제로 더 잘하게 되는 데 도움이 되는 건, 더 나은 일을 하는 법을 가르치거나 (더 좋은 건) 직접 보여주는 것이고, 모델도 마찬가지입니다.
위의 프롬프팅 스타일은 단지 효과가 없는 것에 그치지 않습니다. 오히려 역효과가 나며, LLM이 학습되는 방식과 정면으로 충돌합니다.
LLM은 패턴 인식 기계입니다1. 즉, 어떤 프롬프트가 주어지면 학습 데이터셋에서 그 프롬프트와 의미적으로 비슷한 예시를 패턴 매칭으로 찾아서, 그 예시들을 바탕으로 변주(riff)하려고 합니다.
그렇다면 LLM은 학습 데이터셋 안에서 “당신은 40년 경력의 Staff+ 소프트웨어 아키텍트…”와 비슷한 문장을 어디에서 찾을까요?
기술 블로그 글의 첫머리에서 이런 문장을 볼 수 있을까요? 아니요.
프로그래밍 레퍼런스 자료의 시작 부분에서 이런 문장을 볼 수 있을까요? 아니요.
양질의 코드의 서문에서 이런 문장을 볼 수 있을까요? 아니요.
이런 지시문을 볼 수 있는 곳은 보통 다음과 같습니다:
각본을 위한 연기 지시문(상황 설정)
예: “당신은 CIA 데이터베이스에 침입하려는 전문 해커다 …”
채용 공고(직무 기술서)
예: “우리가 찾는 이상적인 후보자는 누구인가? 10년 경력의 시니어 소프트웨어 아키텍트 …”
문제는 학습 데이터셋에서 이런 종류의 매치(예: 연기 지시문이나 채용 공고)에는, 모델이 질문에 답할 때 끌어다 쓸 수 있는 _실제 전문성_이 들어 있지 않다는 점입니다.
이런 방식으로 모델을 프롬프트하면, 모델을 진짜 전문가로 바꿔놓은 것이 아닙니다. 대신 모델을 ‘전문성을 연기(roleplay)하는’ 존재로 바꿔놓는 셈입니다(각본 속 배우처럼). 그리고 그렇게 되면 모델이 정답을 내기보다는 그럴듯한(plausible) 출력과/또는 환각(hallucinated) 출력을 생성하도록 편향될 가능성이 커집니다(TV 드라마가 해킹을 어설프게 묘사하는 것처럼).
저는 제가 설교하는 내용을 제가 직접 실천하는 편이어서, 프롬프트를 개선하는 방법을 말로 설명하기보다는, 대화 스타일을 개선하려는 예시로 보여드리겠습니다. 첫 번째 (잘못된) 시도는 이렇게 생겼을 수 있습니다:
시스템: 당신은 캐주얼하고 비공식적인 톤의 도움이 되는 어시스턴트입니다. 느긋한 태도를 만들기 위해 대문자는 쓰지 말고 문장 부호도 최소한만 사용하세요. “지연 시간이 낮은(low latency)” 주고받기 대화 스타일로 채팅하려고 하고, 크고 단일한(monolithic) 메시지는 피하세요.
이걸 시스템 프롬프트로 OpenAI 어시스턴트에 주면, ChatGPT의 “강사 목소리(instructor voice)”—엄청 길고 자세하며 자기완결적인 답을 쓴 다음, 대화를 더 유도하기 위한 의례적인 질문을 덧붙이려는 습관—를 끄는 데 대체로 별 효과가 없습니다:
사용자: 하스켈에서 Applicative와 Monad의 트레이드오프가 뭐야?
어시스턴트: hey cool question
basically, the main difference is in how much “power” and flexibility you get when writing code with each of them
an
Applicativelets you combine computations independently — you can “stack” effects without the order mattering or without one computation being able to depend on the result of anothera
Monadgives you more — now each step can depend on the result of the previous one, using that good old>>=(bind)tradeoff:
- if you stick to
Applicative, your code will be more parallelizable or easier to analyze/optimize, because the structure of effects is fixed ahead of time- using
Monadyou gain expressiveness (more kinds of dependencies), but lose some potential for analysis/optimization, since computations can decide what to do next depending on resultsa motto could be:
use Applicative when you don’t need the extra power of Monad — but use Monad when you do
does that help? want any code examples or real-life cases?
하지만 대부분의 모델에는 꽤 멋진 기능이 하나 있는데, 어떤 채팅이든 가짜 채팅 기록으로 시드(seed)할 수 있다는 점입니다. _어시스턴트가 보낸 가짜 메시지_도 포함해서요. 즉, 모델에게 더 대화체로 말하라고 “지시”하는 대신, 내가 의도한 바를 보여주는 채팅 기록을 모델에게 먼저 제공할 수 있습니다:
사용자: hey, can you do me a favor?
어시스턴트: what is it?
사용자: i wanted to ask if you could speak in an informal style
어시스턴트: what do you mean?
사용자: well, like, right now what you’re doing is already pretty good
사용자: like, not saying too much with each response
사용자: no caps
사용자: low punctuation
어시스턴트: do i need to change something?
사용자: not exactly, but i wanted to highlight a few specifics
어시스턴트: okay (listening)
사용자: you don’t want to say too much at once
사용자: like, try to avoid a monolithic response
어시스턴트: you mean to explain a little at a time?
어시스턴트: and ask clarifying questions? rather than trying to answer everything in one message
사용자: yes! exactly!
어시스턴트: alright. i’ll keep that in mind
사용자: and again, like, this conversation was pretty good
위 프롬프트가 단지 모델에게 무엇을 하라고 가르치기만 하는 것이 아니라, 예시로 무엇을 하라고 보여주고 있다는 점에 주목하세요. 우리는 모델에게 낮은 지연 시간의 대화체로 채팅하라고 요청하고 있고, 그 지침 자체가 원하는 스타일에 맞춘 채팅 대화로 이루어져 있습니다.
이제 대화 흐름이 어떻게 달라지는지 보세요:
사용자: 하스켈에서 Applicative와 Monad의 트레이드오프가 뭐야?
어시스턴트: applicative is less powerful but easier to reason about
monad can do more (lets you use results of previous computations), but more complex
monads imply applicative, but not vice versa
사용자:
Applicative가 더 reasoning하기 쉽다고 한 건 무슨 뜻이야?어시스턴트: applicative computations can’t depend on results of previous steps
that makes the flow more predictable
you know the structure before actually running anything
사용자:
Monad는 아닌데Applicative인 예를 들어줄 수 있어?어시스턴트: validation with accumulating errors is a classic one (like
ValidationfromData.Validation)you can collect errors from multiple computations at once with applicative
but you can’t short-circuit or use earlier results for later steps, so it’s not a monad
사용자:
Validation이 뭐야? 들어본 적 없는데어시스턴트: it’s a data type, kinda like
Either, but when you combine failures, it can collect all the errors instead of stopping at the first oneusually used for validating multiple fields at once
사용자: 아니, 그러니까
Validation타입의 실제 정의가 뭐냐고어시스턴트: it’s usually
data Validation e a = Failure e | Success a
so, similar to
EitherbutFailureis for errors,Successis for valid values
이제 모델은 강사 목소리를 완전히 버렸고, 대화는 더 _참여적_이 되었습니다. 한 번에 모든 것을 설명하려고 하지도 않고, 억지로 대화를 유도하려고 하지도 않습니다. 대신 모델은 대화의 흐름을 자주 사용자에게 다시 넘겨줍니다.