localaiforum
로그인
가이드 › 모델 활용

프롬프트와 챗 템플릿

당신이 보내는 모든 메시지는 모델이 보기 전에 마크업으로 감싸집니다. 챗 템플릿은 역할 태그가 붙은 메시지들을 하나의 평평한 토큰 문자열로 바꾸고, 모델은 정확히 그 문자열을 읽도록 학습되었습니다 — 당신의 말끔한 API 객체가 아닙니다. 봉투를 제대로 얻으면 3B 인스트럭트 모델이 말을 듣습니다. 틀리게 얻으면 70B 모델이 말도 안 되는 텍스트를 뱉습니다.

봉투: 대화 하나, 방언 셋

당신의 API는 역할을 노출합니다: system, user, assistant. 모델은 특수 토큰을 봅니다. 같은 두-메시지 대화를 세 가지 방식으로 렌더링하면.

ChatML — Qwen 패밀리 모델과 Hermes 혈통 파인튜닝:

<|im_start|>system
You are a concise assistant.<|im_end|>
<|im_start|>user
Summarize this file.<|im_end|>
<|im_start|>assistant

Llama 2 스타일 — 시스템 마커가 첫 인스트럭션 안에 중첩:

<s>[INST] <<SYS>>You are a concise assistant.<</SYS>>

Summarize this file. [/INST] The file implements a tokenizer... </s>

Gemma 스타일 — 시스템 역할이 없습니다. 지시는 첫 user 턴에 얹히고 어시스턴트는 model로 불립니다:

<bos><start_of_turn>user
You are a concise assistant.

Summarize this file.<end_of_turn>
<start_of_turn>model

같은 대화, 호환되지 않는 봉투 셋. ChatML로 학습된 모델은 Llama 마크업을 "알아서 파악"하지 못합니다 — 낯선 마크업을 평범한 텍스트로 취급하고 그에 맞춰 응답합니다.

엔진은 모델 파일에서 템플릿을 읽습니다

GGUF 메타데이터는 chat_template(Jinja 스니펫)을 내장합니다. llama.cpp, Ollama, LM Studio, Open WebUI가 이것을 읽어 메시지를 자동으로 렌더링합니다. 제대로 된 GGUF로 챗이 즉시 되는 이유입니다. 클래식 함정 두 개: 날것 완성 엔드포인트(/completion)는 템플릿을 건너뛰고 모델에게 맨 텍스트를 줍니다 — 챗 엔드포인트를 쓰세요. 그리고 GGUF는 템플릿이 빠졌거나 틀린 채로 배포될 수 있습니다 — 명시적으로 전달하세요(llama.cpp의 --chat-template, Ollama의 Modelfile) 또는 제대로 만든 곳의 같은 모델 다른 quant를 잡으세요.

틀린 템플릿의 증상

  • 즉시 빈 응답 — 렌더링된 프롬프트가 이미 끝난 대화처럼 보여 모델이 즉시 EOS를 뱉습니다.
  • 역할 누출 — 출력이 user: sure thing! assistant: 처럼 읽힙니다 — 모델이 답하는 대신 봉투를 흉내 냅니다.
  • 쓰레기 — 한 시간 전까지 잘 챗하던 모델의 베이스 모델풍 횡설수설. 파일을 템플릿 없이 바꿨기 때문입니다.

진단하려면 렌더링된 프롬프트를 덤프하세요 — llama.cpp 서버가 장황하게 로그에 남기고, OLLAMA_DEBUG=1이 Ollama에서 보여줍니다. 그런 다음 템플릿이나 파일을 고치세요: 문제 해결.

시스템 프롬프트와 우선순위

시스템 역할은 지속 동작을 정합니다. 페르소나, 언어, 출력 형식, 안전 자세. 펌웨어가 아니라 컨텍스트입니다 — 긴 대화가 씻어낼 수 있고, 작은 모델은 당신이 바라는 만큼 무겁게 다루지 않습니다. 실용 규칙 둘: 하중을 받치는 것(언어, 형식 계약, 단단한 제약)은 시스템 프롬프트에 넣고 동시에 마지막 user 메시지에서 재진술하세요. 생성에 가까운 지시가 이깁니다.

제로샷 대 퓨샷

제로샷은 요청하고, 퓨샷은 보여줍니다. 3B 모델로 버그 리포트에서 구조화 필드를 뽑는다고 하자. 제로샷 — "제목, 컴포넌트, 심각도를 JSON으로 추출해줘" — 는 대략 3분의 2 확률로 유효하고 키가 일관된 JSON을 주지만, 키 이름이 표류합니다(severity였다가 다음 턴엔 priority).

퓨샷: 같은 모델, 완성된 예시 두 개, 세 번째 리포트를 열어둔 채:

Report: "Login button does nothing on Safari 17."
{"title": "Login button unresponsive", "component": "auth", "severity": "medium"}

Report: "App crashes when uploading 4K video."
{"title": "Crash on 4K upload", "component": "upload", "severity": "high"}

Report: "Dark mode makes settings text unreadable."

예시들이 형식 계약을 짊어지므로 샘플러는 스키마를 발명하는 대신 내용을 고릅니다. 이 등급의 과제에서 형식 실패율은 보통 ~30%에서 몇 퍼센트로 떨어집니다. 예시 두세 개가 곡선의 무릎입니다. 그 너머는 컨텍스트 토큰을 지불하는 것입니다. 엄격한 스키마라면 추측을 통째로 건너뛰세요: 구조화 출력.

작은 모델에서 살아남는 패턴

패턴 작동하는 이유
프롬프트당 과제 하나 과제들이 어텐션을 두고 다툼; 진 모델은 버려짐
명시적 출력 형식 ("JSON, 키 a, b, c") 형식을 거의 결정론적 선택으로 바꿈
번호 단계 ("1) 찾기 2) 설명 3) 패치") 사슬이 배회하는 대신 레일 위에 유지됨
설명보다 예시 모델은 산문을 따르는 것보다 흉내 내는 것이 더 나음
짧은 메타 지시 추가 규칙 하나마다 다른 모든 규칙이 희석됨

지시가 죽는 곳은 컨텍스트의 한가운데입니다

어텐션은 프롬프트의 시작과 끝에서 가장 강합니다 — 패밀리를 넘어 안정적인 "lost in the middle" 효과입니다. 실용적 배치: 목표와 형식 계약을 맨 위에, 처리할 문서를 맨 아래에, 긴 문서라면 페이로드 뒤에 한 줄 지시 반복을. 위치는 내용만큼 하중을 받칩니다 — 그리고 컨텍스트의 매 턴은 메모리를 씁니다: KV 캐시.

스톱 시퀀스

생성은 모델이 EOS 토큰을 뱉을 때 끝납니다 — 어떤 토큰인지는 템플릿이 정의합니다. 추가 스톱 시퀀스(llama.cpp 서버와 OpenAI 호환 API의 stop 파라미터)는 토큰 대신 문자열에서 자릅니다. 수다스러운 모델이 당신의 다음 user 턴을 쓰기 시작할 때 유용합니다. "턴 종료"만을 의미할 수 있는 스톱을 고르세요(ChatML 모델의 \n<|im_start|>). 정당한 답변 안에 등장하는 문자열은 절대 안 됩니다 — 맨 줄바꿈 하나가 여러 줄 코드를 블록 중간에서 자릅니다.

~7B 아래에서는 간결함이 장황함을 이깁니다

작은 인스트럭트 모델은 400단어 루브릭보다 40단어 지시를 더 잘 따릅니다. 제한된 어텐션이 주어진 것 전체에 흩어지기 때문입니다. 산문 규칙 대신 구조와 예시를 쓰세요. 수필 프롬프트는 큰 모델에게 아껴두세요. 매 줄을 읽는 것은 그쪽뿐입니다.

재사용 가능한 스니펫 템플릿

추출:

Extract from the text below: person names, dates, money amounts.
Return a JSON array, one object per item, keys: name, date, amount.
No prose before or after the JSON.
Text: {text}

요약:

Summarize in exactly 3 bullet points, each under 15 words,
for someone who skipped the meeting. Most important decision first.
Text: {text}

코드 리뷰:

Review the diff below. List at most 5 findings, ordered by severity.
Format each as: [severity] file:line — problem — suggested fix.
Skip praise and summary. Diff: {diff}

관련: 토큰 · 구조화 출력 · 엔진 파라미터 · 문제 해결