궁극의 프롬프팅

- 구글에서 내 놓은 비법서?

by 이정봉 변호사

Google에서 2025년 2월 발행한 '프롬프트 엔지니어링' 가이드가 있습니다. 저자는 Lee Boonstra입니다. 최근 화제가 되고 있어 정리해 봅니다.


이 가이드의 핵심 목표는 대규모 언어 모델(LLM), 특히 Google의 Gemini 모델을 효과적으로 활용하기 위한 프롬프트 설계 및 최적화 방법론을 제공하는 것입니다. 데이터 과학자나 엔지니어뿐만 아니라 LLM을 사용하는 누구나 더 나은 결과를 얻을 수 있도록, 다양한 프롬프트 기법, 코드 관련 활용법, 그리고 실질적인 모범 사례들을 상세히 소개합니다. Vertex AI 환경에서의 Gemini 사용을 중심으로 설명하지만, 제시된 원칙과 기법들은 다른 LLM에도 폭넓게 적용될 수 있습니다.


주요 내용 상세 분석



프롬프트 엔지니어링의 기본 이해 LLM은 기본적으로 텍스트 시퀀스를 입력받아, 학습 데이터 기반으로 통계적으로 가장 가능성 높은 다음 단어(토큰)를 예측하는 방식으로 작동합니다. 이 과정을 반복하여 문장이나 문단을 생성합니다. '프롬프트 엔지니어링'은 바로 이 LLM이 사용자의 의도에 맞는 정확하고 유용한 응답을 생성하도록 유도하는 '입력 설계 과정'입니다. 이는 단순히 질문을 던지는 것을 넘어, 모델의 특성을 이해하고 최적의 프롬프트를 찾아가는 반복적인 실험과 개선의 과정입니다. 프롬프트의 품질은 결과에 직접적인 영향을 미칩니다. 명확하지 않거나 잘못된 프롬프트는 모호하거나 부정확한 답변, 심지어는 모델이 의미 있는 결과를 내는 데 방해가 될 수 있습니다.


LLM 출력 제어를 위한 핵심 설정 (Configuration) 모델의 응답 방식과 품질을 조절하기 위해 몇 가지 중요한 설정을 이해하고 조정해야 합니다.

출력 길이 (Max Output Tokens): 생성될 최대 토큰 수를 제한합니다. 응답이 길어질수록 더 많은 계산 시간과 비용이 소요됩니다. 단순히 길이를 줄인다고 내용이 간결해지는 것은 아니며, 간결한 응답을 원한다면 프롬프트 자체에서 명확히 유도해야 합니다. 때로는 ReAct 기법처럼 불필요한 토큰이 계속 생성될 수 있는 경우, 이 제한이 유용할 수 있습니다.

샘플링 제어 (Sampling Controls): LLM은 다음 토큰으로 하나의 단어만 결정하는 것이 아니라, 어휘 사전에 있는 각 토큰에 대해 확률 분포를 계산합니다. 샘플링 제어는 이 확률 분포에서 다음 토큰을 어떻게 '선택'할지를 결정하여 응답의 무작위성과 다양성을 조절합니다.

온도 (Temperature): 값의 범위는 보통 0에서 1 이상이며, 다음 토큰 선택의 무작위성 정도를 제어합니다. 낮은 값 (예: 0.1~0.3): 확률이 높은 토큰이 선택될 가능성이 커져, 보다 예측 가능하고 일관되며 결정론적인 응답을 생성합니다. 사실 기반 질문이나 요약에 적합합니다.

높은 값 (예: 0.7~1.0): 낮은 확률의 토큰도 선택될 가능성이 높아져, 더 다양하고 창의적이며 예상치 못한 결과가 나올 수 있습니다. 브레인스토밍이나 창의적인 글쓰기에 유용합니다.

0 값 ("Greedy Decoding"): 항상 가장 확률이 높은 토큰을 선택합니다. (단, 확률이 동일한 토큰이 있을 경우 구현 방식에 따라 결과가 달라질 수 있음)

Top-K: 다음 토큰 예측 시, 확률 분포에서 가장 높은 확률을 가진 K개의 토큰만 후보로 고려합니다. K 값이 작을수록(예: 1~10) 더 집중되고 안정적인 응답이, 클수록(예: 40 이상) 더 다양한 응답이 나옵니다. K=1은 Temperature 0과 동일하게 작동합니다. Top-P (Nucleus Sampling): 확률 분포의 상위부터 토큰 확률을 누적하여, 그 합이 P값이 될 때까지의 토큰들만 후보로 고려합니다. P 값은 0에서 1 사이이며, 1에 가까울수록 더 많은 토큰을 고려하여 다양성이 높아지고, 0에 가까울수록 결정론적이 됩니다. Top-K와 함께 사용되어 후보 토큰 범위를 더 정교하게 조절할 수 있습니다. 설정의 상호작용 및 권장 값: 이 설정들은 복합적으로 작용합니다. 일반적으로 Top-K와 Top-P로 후보 토큰을 먼저 거른 후, Temperature 설정에 따라 최종 토큰을 샘플링합니다. 하나의 값을 극단적으로 설정하면 다른 설정의 영향력이 줄어듭니다. (예: Temp=0이면 K, P 무관 / K=1이면 Temp, P 무관) 일반적 시작점: T=0.2, P=0.95, K=40 (비교적 일관되면서 약간의 창의성) 창의적 결과: T=0.9, P=0.99, K=40 덜 창의적/사실적 결과: T=0.1, P=0.9, K=20 정답이 하나인 경우 (수학 등): T=0 주의사항 (반복 루프 버그): 설정 값이 부적절하면 모델이 특정 단어나 구문을 무한히 반복하는 "반복 루프"에 빠질 수 있습니다. 이는 온도가 너무 낮아 지나치게 결정론적이 되거나, 너무 높아 지나치게 무작위적이 되어 발생할 수 있습니다. 최적의 균형점을 찾는 세심한 조정이 필요합니다.


다양한 프롬프트 설계 기법 제로 샷 (Zero-shot): 모델에게 어떤 예시도 제공하지 않고, 작업에 대한 설명이나 질문만으로 응답을 요청하는 가장 기본적인 방식입니다. (예: "이 영화 리뷰를 긍정, 중립, 부정으로 분류해줘: [리뷰 내용]") 원샷/퓨샷 (One-shot/Few-shot): 모델이 원하는 결과물의 형식이나 패턴을 학습하도록 1개(One-shot) 또는 여러 개(Few-shot)의 예시를 프롬프트에 포함시키는 방식입니다. 특정 출력 구조(예: JSON)를 요구하거나 복잡한 작업을 수행할 때 효과적입니다. 일반적으로 3~5개 이상의 고품질 예시가 권장되며, 예시는 실제 작업과 관련성이 높고 다양해야 하며, 예상되는 예외 상황(엣지 케이스)을 포함하는 것이 좋습니다. (예: 피자 주문 텍스트를 JSON으로 변환하는 예시 여러 개 제시) 시스템, 컨텍스트, 역할 프롬프트: 모델의 행동과 응답 스타일을 보다 정교하게 제어하는 기법입니다.

시스템 프롬프트: 대화나 작업 전반에 걸쳐 적용되는 기본 규칙, 지침, 모델의 역할, 또는 출력 형식 등을 설정합니다. (예: "당신은 프랑스어 번역 전문가입니다. 모든 답변은 프랑스어로 해주세요.", "결과는 반드시 유효한 JSON 형식으로 반환하세요.") 안전 가이드라인(예: "정중하게 답변하세요.") 설정에도 유용합니다.

컨텍스트 프롬프트: 현재 진행 중인 특정 작업이나 질문과 관련된 구체적인 배경 정보나 맥락을 제공하여 모델이 상황을 더 잘 이해하고 정확하게 응답하도록 돕습니다. (예: "나는 지금 80년대 레트로 아케이드 게임에 대한 블로그 글을 쓰고 있어. 이 주제에 맞는 소제목 3개를 추천해줘.")

역할 프롬프트: 모델에게 특정 역할이나 페르소나(예: 친절한 여행 가이드, 엄격한 코드 리뷰어, 유머러스한 작가)를 부여하여, 해당 역할에 맞는 말투, 어조, 지식 수준으로 응답하도록 유도합니다. (예: "당신은 암스테르담 전문 여행 가이드입니다. 박물관 위주로 3곳을 추천해주세요.", "맨해튼 여행 계획을 짜는 것을 도와줘. 유머러스한 스타일로 답변해줘.")

스텝백 (Step-back) 프롬프트: 복잡한 문제에 대해 직접 답을 구하기 전에, 관련된 더 일반적인 원칙이나 개념에 대해 먼저 생각하게 한 후, 그 결과를 바탕으로 원래 문제에 접근하게 하는 방식입니다. 추상적 사고를 통해 더 깊이 있는 추론과 통찰력 있는 답변을 유도할 수 있습니다. (예: 특정 FPS 게임 레벨 스토리를 짜기 전에, "매력적인 FPS 게임 레벨의 핵심 요소는 무엇인가?"를 먼저 묻고, 그 답변을 활용하여 스토리를 구체화)

생각의 사슬 (Chain-of-Thought, CoT): 모델이 최종 답변에 도달하기까지의 중간 추론 과정이나 논리적 단계를 명시적으로 생성하도록 유도하는 기법입니다. 특히 수학 문제나 다단계 추론이 필요한 작업에서 정확도를 크게 향상시킬 수 있습니다. "단계별로 생각해보자(Let's think step-by-step)"와 같은 문구를 추가하는 제로 샷 방식이나, 추론 과정을 포함한 예시를 제공하는 퓨샷 방식으로 활용됩니다. 결과에 대한 해석 가능성을 높이지만, 응답 길이가 길어지는 단점이 있습니다. (예: 나이 계산 문제에서 각 계산 단계를 설명하도록 유도) 코드 생성 시 요구사항을 코드 라인으로 매핑하거나, 제품 설명을 생성할 때 가정 단계를 거치게 하는 등 다양하게 응용됩니다.

자기 일관성 (Self-Consistency): CoT 기법을 사용할 때, 동일한 질문에 대해 높은 온도로 여러 번 추론 과정을 생성하게 한 후, 그 결과들 중 가장 자주 등장하는 최종 답변을 선택하는 방식입니다. 단일 추론 경로의 오류 가능성을 줄이고 답변의 신뢰도와 일관성을 높이는 데 효과적입니다. 계산 비용이 많이 드는 단점이 있습니다. (예: 애매모호한 이메일의 중요도를 분류할 때 여러 번 CoT 추론을 시도하여 가장 일관된 분류 결과를 채택)

생각의 나무 (Tree of Thoughts, ToT): CoT가 단일 추론 경로를 따르는 것과 달리, 여러 가능한 추론 경로를 동시에 탐색하고 평가하며 마치 나무가지처럼 확장해 나가는 방식입니다. 탐색과 복잡한 의사결정이 필요한 문제 해결에 더 강력한 접근법을 제공합니다.

ReAct (Reason and Act): LLM의 추론(Reason) 능력과 외부 도구(Action) 사용 능력을 결합한 패러다임입니다. 모델은 문제 해결을 위해 먼저 계획을 세우고(Thought), 필요한 정보를 얻거나 작업을 수행하기 위해 외부 API(예: 구글 검색, 계산기, 코드 실행기)를 호출(Action)하고, 그 결과를 관찰(Observation)하여 다시 추론을 업데이트하는 과정을 반복합니다. 최신 정보 검색이나 복잡한 계산 등 LLM 자체 능력만으로 해결하기 어려운 작업에 효과적입니다. (예: "메탈리카 멤버들의 자녀 수는 총 몇 명인가?" 질문에 대해, 멤버 검색 -> 각 멤버 자녀 수 검색 -> 합산 과정을 수행) 자동 프롬프트 엔지니어링 (APE): 사람이 직접 프롬프트를 설계하는 대신, LLM을 활용하여 특정 작업에 대한 프롬프트 후보들을 생성하고, 이를 평가 지표(예: BLEU, ROUGE 점수)로 평가하여 최적의 프롬프트를 자동으로 찾아내는 방법론입니다. 프롬프트 생성 과정을 자동화하고 인간 엔지니어 수준의 성능을 목표로 합니다. (예: 상품 주문 챗봇 학습을 위해 다양한 주문 방식 문장 변형을 LLM이 생성하도록 요청)


코드 관련 프롬프트 활용 LLM은 다양한 코딩 관련 작업을 지원하여 개발 생산성을 높일 수 있습니다.

코드 생성: 자연어 설명을 바탕으로 특정 프로그래밍 언어(Bash, Python 등)의 코드를 작성합니다. (예: "폴더 내 모든 파일 이름 앞에 'draft_'를 붙이는 Bash 스크립트 작성해줘.") 생성된 코드는 반드시 개발자가 검토하고 테스트해야 합니다.

코드 설명: 기존 코드 스니펫이 어떤 기능을 수행하는지 자연어로 설명해줍니다. (예: "이 Bash 스크립트가 하는 일을 설명해줘.")

코드 번역: 한 언어로 작성된 코드를 다른 언어로 변환합니다. (예: "이 Bash 스크립트를 Python 코드로 바꿔줘.") Vertex AI Studio 등 일부 환경에서는 코드 복사 시 들여쓰기 유지에 주의해야 합니다.

코드 디버깅 및 검토: 코드의 오류(Traceback 등 명시)를 찾아 수정하고, 더 나아가 잠재적인 문제점을 지적하며 코드 품질 개선 방안(예: 파일 확장자 유지, 공백 처리, f-string 사용, 예외 처리 추가)까지 제안합니다.


멀티모달 프롬프트: 텍스트뿐만 아니라 이미지, 오디오 등 여러 종류의 데이터를 입력으로 함께 사용하는 프롬프트 방식을 간략하게 언급합니다.


프롬프트 엔지니어링 모범 사례 예시 활용 (Few-shot): 모델이 사용자의 의도와 원하는 출력 형식을 명확히 파악하도록 예시를 제공하는 것은 매우 효과적인 방법입니다.

명확성 및 간결성: 프롬프트는 사람이 이해하기 쉬워야 모델도 잘 이해할 수 있습니다. 불필요한 정보나 모호한 표현을 피하고, '작성하라', '분석하라', '분류하라' 등 명확한 행동 동사를 사용하세요.

구체적인 출력 요구: 원하는 결과물의 길이, 형식, 포함/제외할 내용 등을 구체적으로 명시할수록 모델이 목표에 집중하여 정확도를 높일 수 있습니다. (예: "상위 5개 비디오 게임 콘솔에 대해 3문단짜리 블로그 글을 작성해줘." vs "비디오 게임 콘솔에 대해 글 써줘.") 지침 우선 (Instructions > Constraints): 모델에게 '하지 말아야 할 것'(제약 조건)을 나열하기보다 '해야 할 일'(지침)을 명확히 알려주는 것이 일반적으로 더 효과적입니다. 긍정적인 지시는 모델이 허용 범위 내에서 창의성을 발휘하게 하지만, 과도한 제약은 잠재력을 제한하고 서로 충돌할 수 있습니다. 단, 안전 문제나 엄격한 형식 준수가 필요할 때는 제약 조건이 유용합니다.

출력 길이 제어: 모델 설정(Max Output Tokens)을 이용하거나, 프롬프트 내에서 직접 길이를 명시하세요. (예: "양자역학을 트윗 길이로 설명해줘.") 변수 사용: 프롬프트를 재사용하거나 동적으로 변경해야 할 때, 변수(예: {city})를 활용하면 효율성을 높이고 코드 통합을 용이하게 합니다. 형식과 스타일 실험: 동일한 요청이라도 질문, 서술문, 명령문 등 형식이나 단어 선택, 스타일에 따라 결과가 달라질 수 있으므로 다양하게 시도해보는 것이 좋습니다. (예: Sega Dreamcast에 대한 질문 vs. 설명 요청)

Few-shot 분류 시 클래스 혼합: 분류 작업에서 예시를 제공할 때는 특정 클래스 순서에 모델이 과적합되지 않도록 여러 클래스의 예시를 골고루 섞어 제시해야 합니다.

모델 업데이트 적응: LLM은 계속 업데이트되므로, 최신 모델 버전의 특징을 파악하고 이에 맞춰 프롬프트를 테스트하고 조정해야 합니다. Vertex AI Studio와 같은 도구가 버전 관리에 도움이 됩니다.

구조화된 출력 형식 실험 (JSON 등): 특히 데이터 추출, 분류 등 비창의적 작업에서는 JSON, XML 등 구조화된 형식으로 출력을 요청하는 것이 좋습니다. 이는 출력의 일관성을 보장하고, 후속 처리(파싱)를 용이하게 하며, 모델의 환각(hallucination)을 줄이는 데 도움이 됩니다.

JSON 복구: 토큰 제한 등으로 JSON 출력이 중간에 잘릴 경우, json-repair와 같은 라이브러리를 사용하여 손상된 JSON을 복구할 수 있습니다. 구조화된 입력 (JSON Schema): 복잡한 정보를 입력으로 제공할 때, JSON 스키마를 정의하여 데이터의 구조와 타입을 명확히 알려주면 모델이 정보를 더 정확하게 해석하고 관련 필드에 집중하는 데 도움이 됩니다. (예: 제품 정보를 자유 텍스트 대신 정의된 스키마에 따라 JSON으로 입력)

협업 및 실험: 좋은 프롬프트를 찾는 것은 여러 사람의 아이디어와 실험을 통해 더 효과적으로 이루어질 수 있습니다.

CoT 관련: CoT 프롬프팅 시에는 일반적으로 결정론적인 추론을 위해 온도를 0으로 설정하고, 최종 답변은 추론 과정 뒤에 명시적으로 오도록 유도하는 것이 좋습니다.

체계적인 문서화 (매우 중요): 프롬프트 엔지니어링은 실험의 연속입니다. 모델 버전, 사용된 설정값, 프롬프트 문구, 목표, 실제 출력 결과, 성공 여부 및 피드백 등을 반드시 상세하게 기록하고 관리해야 합니다. 이는 시간이 지난 후 작업을 검토하거나, 다른 모델/설정에서 성능을 비교하거나, 문제를 디버깅하는 데 필수적입니다. (문서 내 표 형식 템플릿 제안) RAG 시스템 사용 시 관련 설정(쿼리, 청크 등)도 기록해야 합니다. 최종 프롬프트는 코드와 분리하여 관리하고, 자동화된 테스트 및 평가를 통해 성능을 검증하는 것이 이상적입니다.



결론 및 시사점


이 문서는 프롬프트 엔지니어링을 단순한 '질문 기술'이 아닌, LLM의 잠재력을 최대한 끌어내기 위한 '엔지니어링적 접근법'으로 제시합니다. 다양한 기법과 구체적인 예시, 그리고 체계적인 실험과 문서화의 중요성을 강조하며, 독자들이 LLM을 더 효과적이고 정교하게 활용할 수 있도록 실질적인 지식과 통찰력을 제공하는 매우 유용한 가이드입니다. 프롬프트 엔지니어링은 지속적인 학습과 실험이 요구되는 분야임을 명확히 보여줍니다.

keyword
작가의 이전글키메라팩토리