Prompt Engineering 기초 정리

LLM을 사용하다 보면 같은 모델인데도 입력하는 방식에 따라 결과가 꽤 달라진다.

“게임 개발에 대해 설명해줘”라고 하는 것과

게임 개발자를 대상으로 설명해라.
프로그래밍 경험이 있다는 것을 전제로 한다.
개념 설명보다 실제 개발에서 어떻게 사용하는지를 중심으로 작성한다.
예제 코드는 C#을 사용한다.

처럼 조건을 구체적으로 전달하는 것은 결과가 다를 수밖에 없다.

이렇게 LLM이 원하는 방향으로 답변을 생성할 수 있도록 입력을 설계하는 작업을 Prompt Engineering이라고 한다.

거창하게 들리지만 결국 LLM의 특성을 이해하고, 필요한 정보를 적절한 형태로 전달하는 작업에 가깝다.

Prompt는 단순한 질문이 아니다

LLM에게 입력하는 내용을 보통 Prompt라고 부른다.

가장 단순한 Prompt는 질문 하나다.

C#에서 Dictionary를 어떻게 사용해?

하지만 실제로는 질문 외에도 여러 정보를 함께 넣을 수 있다.

역할-> 게임 개발자

목적-> C# Dictionary 사용법 설명

조건-> 프로그래밍 경험이 있는 사람을 대상으로 설명-> 불필요한 기초 설명은 제외-> 실제 게임 개발 예제를 포함

출력 형식-> 개념 설명-> 코드-> 주의할 점

이런 정보가 하나의 Prompt를 구성한다.

LLM은 이 입력 전체를 문맥으로 보고 다음에 생성할 토큰을 결정한다.

그래서 원하는 결과가 있다면 질문 하나만 던지는 것보다 모델이 어떤 답변을 만들어야 하는지 판단할 수 있는 정보를 충분히 제공하는 것이 도움이 된다.


좋은 Prompt는 무엇이 다른가

좋은 Prompt에 정해진 문법이 있는 것은 아니다.

어떤 상황에서는 한 문장만으로 충분하고, 어떤 작업에서는 상당히 긴 지시가 필요하다.

중요한 것은 Prompt의 길이가 아니라 목표와 조건이 얼마나 명확하게 전달되는가다.

예를 들어 다음 Prompt를 보자.

Unity에서 오브젝트 풀링 설명해줘.

틀린 Prompt는 아니다.

하지만 원하는 답변의 수준이나 범위가 정해져 있지 않다.

조금 더 구체적으로 만들면,

Unity 게임 개발자를 대상으로 오브젝트 풀링을 설명해줘.

Instantiate와 Destroy를 반복하는 방식의 문제부터 설명하고,
오브젝트 풀링이 어떤 문제를 해결하는지 설명해줘.

간단한 C# 예제를 포함하되,
Unity의 기본적인 GameObject와 MonoBehaviour 사용법은 설명하지 않아도 된다.

마지막에는 어떤 상황에서 오브젝트 풀링을 사용하는 것이 적절한지 정리해줘.

이렇게 하면 모델이 답변의 방향을 결정할 때 참고할 수 있는 정보가 많아진다.

결국 Prompt Engineering에서 중요한 것은 “마법의 문장”을 찾는 것이 아니다.

모델이 작업을 수행하는 데 필요한 조건을 빠뜨리지 않는 것에 가깝다.


Context가 중요하다

LLM에게 작업을 시킬 때 결과가 좋지 않은 이유 중 하나는 필요한 정보가 Prompt에 없기 때문이다.

예를 들어 코드 리뷰를 요청한다고 하자.

이 코드 개선해줘.

이것만으로는 판단할 정보가 부족하다.

반면 다음과 같이 전달할 수 있다.

이 코드는 Unity에서 적 AI의 이동을 처리한다.

목표는 코드의 실행 성능보다 유지보수성을 높이는 것이다.
현재 구조를 크게 변경하지 않는 선에서 개선해라.

특히 다음을 확인해라.

- 책임이 너무 많이 섞여 있는지
- 불필요한 Update 호출이 있는지
- 변수와 메서드 이름이 역할을 명확하게 표현하는지

같은 코드라도 모델이 고려해야 할 기준이 달라진다.

LLM은 입력으로 제공된 문맥을 기반으로 답변을 생성하기 때문에 문맥을 어떻게 구성하느냐가 결과 품질에 직접적인 영향을 준다.


역할을 지정하는 이유

Prompt에서 “너는 전문가다” 같은 역할을 지정하는 방식도 많이 사용한다.

너는 10년차 게임 클라이언트 개발자다.

이런 지시가 있다고 해서 모델이 실제로 10년의 개발 경험을 갖게 되는 것은 아니다.

역할 지정의 의미는 모델이 답변을 작성할 때 특정 관점과 기준을 참고하도록 만드는 데 있다.

예를 들어,

게임 기획자의 관점에서 설명해라.

게임 클라이언트 프로그래머의 관점에서 설명해라.

는 같은 주제를 다루더라도 강조하는 부분이 달라질 수 있다.

다만 역할을 무작정 길게 붙이는 것이 항상 좋은 것은 아니다.

작업과 관련 없는 역할 설명은 오히려 Prompt를 복잡하게 만든다.


출력 형식을 지정하면 결과가 달라진다

LLM에게 원하는 출력 형태를 명확하게 알려주는 것도 유용하다.

예를 들어 단순히

이 기술을 비교해줘.

라고 하는 것보다,

A와 B를 비교해줘.

다음 항목을 기준으로 작성해라.

- 동작 방식
- 장점
- 단점
- 성능 특성
- 사용하기 좋은 상황

마지막에는 어떤 상황에서 A를 선택하고
어떤 상황에서 B를 선택하면 되는지 정리해라.

처럼 작성하면 결과의 구조를 어느 정도 통제할 수 있다.

특히 API 응답이나 데이터 처리처럼 결과 형식이 중요한 작업에서는 출력 형식을 명시하는 것이 더 중요해진다.

예를 들어 JSON이 필요하다면 단순히 “JSON으로 만들어줘”라고 하는 것보다 필요한 필드와 타입을 명시하는 편이 낫다.

다음 형식의 JSON만 출력해라.

{
  "name": "string",
  "score": "number",
  "reason": "string"
}

실제 애플리케이션에서 LLM 출력을 파싱해야 한다면 이런 차이가 상당히 중요하다.


Few-shot은 예제를 보여주는 방식이다

LLM에게 원하는 결과의 예시를 직접 보여줄 수도 있다.

이런 방식을 Few-shot Prompting이라고 한다.

예를 들어 게임 아이템의 설명을 일정한 형식으로 만들고 싶다면,

입력:
이름 = 철검
공격력 = 10

출력:
철검은 공격력 10을 가진 기본 무기다.

입력:
이름 = 불의 검
공격력 = 25

출력:
불의 검은 공격력 25를 가진 화염 속성 무기다.

입력:
이름 = 얼음 검
공격력 = 20

출력:

처럼 예제를 먼저 제공할 수 있다.

모델은 앞에서 제공된 입력과 출력의 패턴을 참고해서 마지막 결과를 생성한다.

이 방법은 단순한 설명보다 원하는 결과의 형태를 직접 보여주는 것이 효과적인 작업에서 특히 유용하다.


Zero-shot과 Few-shot

이 차이를 구분해두면 Prompt Engineering 관련 자료를 볼 때 편하다.

예제가 없는 경우를 Zero-shot이라고 한다.

이 문장을 영어로 번역해줘.

예제를 하나 이상 제공하는 경우는 Few-shot이다.

한국어: 안녕하세요.
영어: Hello.

한국어: 감사합니다.
영어: Thank you.

한국어: 좋은 아침입니다.
영어:

모델에게 규칙을 장황하게 설명하는 대신 몇 개의 예제를 보여주는 것이다.

어떤 작업에서는 Few-shot이 훨씬 직관적인 방법이 될 수 있다.


복잡한 작업은 나눠서 시키는 편이 낫다

하나의 Prompt에 너무 많은 작업을 넣으면 결과가 불안정해질 수 있다.

예를 들어,

이 문서를 분석하고
문제점을 찾고
개선안을 만들고
코드로 구현하고
테스트 케이스도 작성하고
문서까지 만들어줘.

라고 한 번에 요청할 수도 있다.

하지만 작업이 복잡해질수록 각 단계의 결과를 확인하기 어렵다.

차라리 작업을 나누는 방법이 있다.

각 단계에서 결과를 확인한 뒤 다음 작업으로 넘어가는 방식이다.

LLM을 단순한 질문 답변 도구가 아니라 작업 파이프라인의 한 단계로 사용하는 경우 이런 접근이 특히 유용하다.


Chain of Thought를 어떻게 봐야 하는가

Prompt Engineering 자료를 보다 보면 Chain of Thought라는 개념도 자주 등장한다.

간단히 말하면 복잡한 문제를 해결할 때 중간 추론 과정을 활용하도록 유도하는 접근이다.

예전에는 모델에게

단계별로 생각해서 답변해라.

같은 식의 Prompt를 많이 사용했다.

하지만 실제 애플리케이션에서는 모델의 내부 추론을 무조건 길게 출력하게 만드는 것이 목적은 아니다.

필요한 것은 정답의 신뢰성과 작업 결과를 개선하는 것이다.

따라서 복잡한 계산이나 논리 문제에서는 문제를 단계적으로 분해하거나 중간 결과를 검증하도록 설계하는 방식이 더 실용적인 경우가 많다.

예를 들어,

처럼 작업 자체를 구조화할 수 있다.


Prompt만으로 해결되지 않는 문제도 많다

LLM을 사용하다 보면 모든 문제를 Prompt로 해결하려는 방향으로 가기 쉽다.

하지만 실제 서비스에서는 그렇지 않다.

예를 들어 모델이 최신 회사 내부 정보를 알아야 한다고 하자.

Prompt에

우리 회사의 최신 매출 데이터를 알고 있다고 가정해라.

라고 작성한다고 실제 데이터를 알게 되는 것은 아니다.

필요한 데이터가 있다면 외부 시스템에서 가져와야 한다.

문서 검색이 필요하다면 RAG를 사용할 수도 있다.

특정 행동 패턴을 학습시키는 것이 필요하다면 Fine-tuning을 고려할 수 있다.

출력 형식이 중요하다면 Structured Output이나 별도의 검증 로직을 사용할 수 있다.

Prompt Engineering은 이런 시스템 전체를 대체하는 기술이 아니다.

LLM에게 어떤 정보를 어떤 방식으로 제공할 것인지 설계하는 한 부분이라고 보는 편이 맞다.


좋은 Prompt보다 좋은 시스템이 더 중요하다

실제 LLM 애플리케이션에서는 Prompt 하나만 잘 작성한다고 끝나지 않는다.

전체 구조를 보면 대략 이런 식이다.

여기서 Prompt는 가운데 하나의 단계다.

LLM이 잘못된 답을 만드는 원인이 Prompt 때문일 수도 있지만, 애초에 잘못된 데이터를 넣었거나 검색 결과가 좋지 않았거나 출력 검증이 없어서 발생하는 문제일 수도 있다.

그래서 Prompt를 수정하기 전에 문제가 Prompt에 있는지, 데이터에 있는지, 모델에 있는지, 시스템 구조에 있는지부터 확인하는 것이 좋다.


Prompt Engineering은 결국 인터페이스 설계에 가깝다

LLM은 일반적인 프로그램처럼 함수의 인자와 반환값만으로 모든 것을 명확하게 정의하기 어렵다.

자연어를 입력으로 받고 자연어를 출력하기 때문이다.

그래서 개발자는 모델이 이해하기 좋은 형태로 작업의 목적과 조건, 필요한 데이터, 출력 형식을 전달해야 한다.

이 관점에서 보면 Prompt Engineering은 단순히 “AI에게 질문을 잘하는 법”이라고 보기에는 범위가 조금 좁다.

LLM이라는 확률적인 시스템과 원하는 작업 사이에 어떤 입력 구조를 만들어줄 것인가에 대한 설계 문제에 가깝다.

결국 좋은 Prompt를 만드는 것도 한 번에 완성되는 작업은 아니다.

실제 결과를 확인하고 실패하는 경우를 찾아서 조건을 추가하거나 예제를 바꾸고, 필요하면 Prompt가 아니라 시스템 구조 자체를 수정해야 한다.

LLM을 제대로 활용하려면 Prompt 문구를 외우는 것보다 모델이 무엇을 알고 있고 무엇을 모르는지, 입력을 어떻게 처리하고 출력을 어떻게 생성하는지 이해하는 것이 먼저다.