예시로 가르친다 — few-shot
Article
8강에서 구조화된 지시로 메모 정리 비서를 만들었다. 분류 기준이 흔들리던 문제, 팁이 따라붙던 문제는 잡혔다. 오늘은 규칙 하나를 더 얹는다, 할 일 내용을 8자 이내로 짧게 줄여라. 문장으로 못 박으면 될 것 같은데, 실제로 돌려보면 얘기가 다르다.
규칙을 문장으로 추가해본다
my-ai-assistant 폴더에 no-examples.ts 를 만든다. 8강의 index.ts 에 <output_format> 규칙 하나만 추가했다.
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const SYSTEM_PROMPT = `<role>
너는 메모 정리 비서다. 사용자가 두서없이 적은 메모에서 할 일을 뽑아 목록으로 만든다.
</role>
<task>
입력 메모에서 "해야 할 일"을 모두 찾아낸다. 마감이 있으면 그대로 살리고, 없으면 "미정"으로 표시한다.
</task>
<output_format>
- 마크다운, 이모지, 헤더, 굵은 글씨를 쓰지 않는다. 순수 텍스트 줄만 출력한다.
- 할 일 한 줄은 반드시 이 형식이다: "- [마감기한] 할 일 내용"
- 할 일 내용은 8자 이내로 짧게 압축한다. 조사와 불필요한 수식어는 뺀다.
- 마감이 급한 순서로 정렬한다. 마감이 없는 항목은 맨 뒤에 둔다.
- 목록 앞뒤로 인사말, 설명, 팁, 요약을 절대 덧붙이지 않는다. 목록 줄만 출력한다.
</output_format>`;
async function organize(note: string) {
const response = await client.messages.create({
model: "claude-haiku-4-5",
max_tokens: 300,
system: SYSTEM_PROMPT,
messages: [{ role: "user", content: note }],
});
const text = response.content[0].type === "text" ? response.content[0].text : "";
console.log(`\n--- 입력 ---\n${note}`);
console.log(`--- 출력 ---\n${text}`);
}
await organize(
"내일까지 3분기 마케팅 예산안 초안을 작성해서 팀장님한테 검토받아야 하고, 이번 주 금요일에는 신규 입사자 온보딩 자료도 준비해야 함. 시간 날 때 회사 근처 도서관에 반납할 책도 챙기기.",
);
await organize(
"이번 주 안에 치과 예약을 다시 잡아야 하고, 프로젝트 문서 최종 리뷰는 오늘 중으로 끝내야 함. 엄마 생신 선물로 뭘 살지 다음 달 초까지 정해야 함.",
);
전보다 메모를 좀 더 복잡하게(문장을 길게) 만들었다. 실행한다.
npx tsx --env-file=.env no-examples.ts
- --- 입력 ---
- 내일까지 3분기 마케팅 예산안 초안을 작성해서 팀장님한테 검토받아야 하고, 이번 주 금요일에는 신규 입사자 온보딩 자료도 준비해야 함. 시간 날 때 회사 근처 도서관에 반납할 책도 챙기기.
- --- 출력 ---
- - [내일] 3분기 마케팅 예산안 초안
- - [금요일] 신규 입사자 온보딩 자료
- - [미정] 도서관에 책 반납
- --- 입력 ---
- 이번 주 안에 치과 예약을 다시 잡아야 하고, 프로젝트 문서 최종 리뷰는 오늘 중으로 끝내야 함. 엄마 생신 선물로 뭘 살지 다음 달 초까지 정해야 함.
- --- 출력 ---
- - [오늘] 문서 최종 리뷰
- - [이번 주] 치과 예약
- - [다음 달 초] 생신 선물 결정
글자 수를 실제로 세어보면 규칙이 얼마나 안 지켜졌는지 드러난다.
| 3분기 마케팅 예산안 초안 | 14자 | 초과 |
| 신규 입사자 온보딩 자료 | 13자 | 초과 |
| 도서관에 책 반납 | 9자 | 초과 |
| 문서 최종 리뷰 | 8자 | 통과 |
| 치과 예약 | 5자 | 통과 |
| 생신 선물 결정 | 8자 | 통과 |
6개 중 3개가 규칙을 어겼다. "3분기 마케팅 예산안 초안"은 14자로, 8자 기준의 거의 두 배다.
예시 몇 개를 먼저 보여준다
이럴 때 쓰는 게 few-shot이다. 규칙을 문장으로 설명하는 대신, "이렇게 압축한다"는 예시 대화를 실제 질문 앞에 몇 개 놔둔다. 사람도 "짧게 써"보다 "이 정도로 짧게 써"라며 예시를 보여주면 훨씬 빨리 감을 잡는데, 모델도 마찬가지다.
- 지시문 (zero-shot)"8자 이내로 압축해라", 기준을 말로 설명한다
- 예시 대화 (few-shot)실제 입력→출력 쌍을 몇 개 먼저 보여준다
- 진짜 질문예시 뒤에 이어붙이면, 모델이 그 압축 폭을 그대로 흉내 낸다
index.ts 를 만든다. no-examples.ts 와 시스템 프롬프트는 똑같고, messages 배열 앞에 예시 두 쌍을 추가한다.
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const SYSTEM_PROMPT = `<role>
너는 메모 정리 비서다. 사용자가 두서없이 적은 메모에서 할 일을 뽑아 목록으로 만든다.
</role>
<task>
입력 메모에서 "해야 할 일"을 모두 찾아낸다. 마감이 있으면 그대로 살리고, 없으면 "미정"으로 표시한다.
</task>
<output_format>
- 마크다운, 이모지, 헤더, 굵은 글씨를 쓰지 않는다. 순수 텍스트 줄만 출력한다.
- 할 일 한 줄은 반드시 이 형식이다: "- [마감기한] 할 일 내용"
- 할 일 내용은 8자 이내로 짧게 압축한다. 조사와 불필요한 수식어는 뺀다.
- 마감이 급한 순서로 정렬한다. 마감이 없는 항목은 맨 뒤에 둔다.
- 목록 앞뒤로 인사말, 설명, 팁, 요약을 절대 덧붙이지 않는다. 목록 줄만 출력한다.
</output_format>`;
// 실제 압축 폭을 예시 두 개로 보여준다. 규칙은 이미 system 에 있으니, 여기서는 "이 정도로 줄인다"는 감각만 심는다.
const FEW_SHOT: Anthropic.MessageParam[] = [
{
role: "user",
content:
"내일까지 부서 워크샵 장소를 예약해야 하고, 다다음 주 화요일에 있을 신입사원 환영회 준비물도 미리 챙겨놔야 함.",
},
{
role: "assistant",
content: "- [내일] 워크샵 예약\n- [다다음 주 화요일] 환영회 준비물",
},
{
role: "user",
content:
"이번 주 목요일까지 계약서 최종본에 서명해서 법무팀에 보내야 함. 사무실 정수기 필터도 갈아야 하는데 날짜는 상관없음.",
},
{
role: "assistant",
content: "- [이번 주 목요일] 계약서 서명\n- [미정] 정수기 필터 교체",
},
];
async function organize(note: string) {
const response = await client.messages.create({
model: "claude-haiku-4-5",
max_tokens: 300,
system: SYSTEM_PROMPT,
messages: [...FEW_SHOT, { role: "user", content: note }],
});
const text = response.content[0].type === "text" ? response.content[0].text : "";
console.log(`\n--- 입력 ---\n${note}`);
console.log(`--- 출력 ---\n${text}`);
}
await organize(
"내일까지 3분기 마케팅 예산안 초안을 작성해서 팀장님한테 검토받아야 하고, 이번 주 금요일에는 신규 입사자 온보딩 자료도 준비해야 함. 시간 날 때 회사 근처 도서관에 반납할 책도 챙기기.",
);
await organize(
"이번 주 안에 치과 예약을 다시 잡아야 하고, 프로젝트 문서 최종 리뷰는 오늘 중으로 끝내야 함. 엄마 생신 선물로 뭘 살지 다음 달 초까지 정해야 함.",
);
바뀐 건 한 곳이다. messages: [{ role: "user", content: note }] 대신 messages: [...FEW_SHOT, { role: "user", content: note }] 를 보냈다. 모델 입장에서는 이게 실제 대화 기록인지 예시인지 구분하지 않는다. 그냥 앞에 그런 대화가 있었던 것처럼 받아들이고, 그 패턴을 이어간다.
다시 실행하면
같은 두 메모, 같은 규칙이다. 예시만 앞에 붙었다.
npx tsx --env-file=.env index.ts
- --- 입력 ---
- 내일까지 3분기 마케팅 예산안 초안을 작성해서 팀장님한테 검토받아야 하고, 이번 주 금요일에는 신규 입사자 온보딩 자료도 준비해야 함. 시간 날 때 회사 근처 도서관에 반납할 책도 챙기기.
- --- 출력 ---
- - [내일] 마케팅 예산안 초안
- - [이번 주 금요일] 온보딩 자료 준비
- - [미정] 도서관 반납
- --- 입력 ---
- 이번 주 안에 치과 예약을 다시 잡아야 하고, 프로젝트 문서 최종 리뷰는 오늘 중으로 끝내야 함. 엄마 생신 선물로 뭘 살지 다음 달 초까지 정해야 함.
- --- 출력 ---
- - [오늘] 문서 최종 리뷰
- - [이번 주 안에] 치과 예약
- - [다음 달 초] 생신 선물 구매
글자 수를 다시 잰다.
| 마케팅 예산안 초안 | 10자 | 초과 |
| 온보딩 자료 준비 | 9자 | 초과 |
| 도서관 반납 | 6자 | 통과 |
| 문서 최종 리뷰 | 8자 | 통과 |
| 치과 예약 | 5자 | 통과 |
| 생신 선물 구매 | 8자 | 통과 |
통과 개수는 3/6에서 4/6으로 늘었고, 가장 심하게 어겼던 항목도 14자에서 10자로 줄었다. 예시 두 개를 보여준 것만으로 압축 강도가 눈에 띄게 세졌다.
그런데 완벽하진 않다
"마케팅 예산안 초안"(10자), "온보딩 자료 준비"(9자)는 여전히 8자를 넘겼다. few-shot 이 방향은 잡아줬지만 규칙을 100% 강제하지는 못했다.
정리하면
문장으로 아무리 강조해도 모델이 감을 못 잡는 규칙이 있다. 그럴 때는 규칙을 설명하는 대신, 원하는 입력→출력 쌍을 messages 배열 앞에 예시로 놓는다. 완벽하진 않아도 지시문만 쓸 때보다 훨씬 규칙에 가까워진다.
- 01
문장으로 규칙을 추가했지만 절반이 어겨졌다
8자 압축 규칙을 문장으로 추가했더니, 6개 중 3개가 기준을 넘겼다.
- 02
예시 두 쌍을 messages 앞에 추가했다
user·assistant 예시 대화를 실제 질문 앞에 붙여, 원하는 압축 폭을 직접 보여줬다.
npx tsx --env-file=.env index.ts - 03
글자 수로 개선 폭을 직접 쟀다
통과 항목이 3/6에서 4/6으로 늘었고, 최악의 위반도 14자에서 10자로 줄었다. 완벽하진 않다는 것도 함께 확인했다.
프롬프트만으로는 규칙을 100% 강제할 수 없다는 걸 오늘 눈으로 봤다. 다음 강의에서는 접근을 바꾼다. 텍스트 형식을 요구하는 대신, JSON 으로 답을 받아 프로그램이 직접 다루기 쉬운 구조로 바꾼다. 그리고 11강에서 그 JSON 이 깨져 나올 때를 대비해 검증까지 마치면, Course A 의 결과물인 CLI 어시스턴트가 완성된다.