왜 고정 few-shot가 문제인가

프롬프트에 예시 3~5개를 하드코딩하는 방식은 간단하지만 한계가 뚜렷하다. 입력 분포가 넓은 실무 데이터에서는 고정 예시가 특정 유형에만 잘 맞고 나머지에서는 오히려 편향을 유발한다. 예를 들어 환불 문의 예시만 넣어두면 배송 문의가 들어와도 모델이 환불 스키마로 답하려 한다. 예시를 무작정 늘리면 토큰 비용과 지연이 커지고, 관련 없는 예시가 노이즈로 작동해 정확도가 떨어지는 경우도 흔하다.

동적 선택의 핵심 아이디어

해결책은 매 요청마다 입력과 가장 관련 있는 예시를 예시 풀에서 골라 프롬프트에 끼워 넣는 것이다. 흐름은 세 단계다. 첫째, 예시 풀을 임베딩해 벡터 저장소에 넣는다. 둘째, 들어온 질의를 같은 모델로 임베딩한다. 셋째, 유사도 상위 k개를 뽑아 프롬프트에 조립한다. 예시 풀은 코드가 아니라 데이터이므로, 재배포 없이 예시만 추가·수정하며 품질을 개선할 수 있다는 점이 실무에서 큰 장점이다.

구현: 임베딩 기반 유사도 선택

예시를 미리 임베딩해두고, 질의가 오면 코사인 유사도로 상위 k개를 골라 프롬프트를 만든다.

import numpy as np

def cosine(a, b):
    return a @ b / (np.linalg.norm(a) * np.linalg.norm(b))

def select_examples(query_vec, pool, k=3):
    scored = [(cosine(query_vec, e["vec"]), e) for e in pool]
    scored.sort(key=lambda x: x[0], reverse=True)
    return [e for _, e in scored[:k]]

def build_prompt(query, examples):
    shots = "\n\n".join(
        f"입력: {e['input']}\n출력: {e['output']}" for e in examples
    )
    return f"{shots}\n\n입력: {query}\n출력:"

pgvector로 예시 저장·조회

예시 풀이 커지면 in-memory 대신 벡터 인덱스가 필요하다. Postgres에 pgvector를 쓰면 별도 인프라 없이 유사도 검색을 처리할 수 있다.

CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE fewshot_examples (
    id      bigserial PRIMARY KEY,
    input   text NOT NULL,
    output  text NOT NULL,
    label   text,
    embedding vector(1536)
);

CREATE INDEX ON fewshot_examples
    USING hnsw (embedding vector_cosine_ops);

-- 질의 임베딩(:q)과 가까운 상위 3개, 라벨 다양성 확보
SELECT input, output, label
FROM fewshot_examples
ORDER BY embedding <=> :q
LIMIT 3;

유사도만으로는 부족하다: 다양성

상위 k개를 순수 유사도로만 뽑으면 거의 같은 예시가 중복 선택되기 쉽다. 이때는 MMR(Maximal Marginal Relevance)처럼 질의 관련성과 예시 간 차별성을 함께 고려해, 관련 있으면서 서로 다른 예시를 섞는다. 분류 작업이라면 라벨별로 최소 한 개씩 배분하는 규칙만 추가해도 클래스 편향이 크게 줄어든다.

선택 방식 비교

방식정확도비용운영 난이도
고정 few-shot낮음~중간낮음매우 낮음
유사도 top-k중간~높음중간중간
유사도 + 다양성(MMR)높음중간높음

실무 주의점

몇 가지를 놓치면 오히려 품질이 나빠진다.

  • 질의와 예시는 반드시 같은 임베딩 모델·버전으로 인코딩한다. 모델을 바꾸면 저장된 벡터를 전부 재생성해야 한다.
  • 예시 풀에 오답·오래된 스키마가 섞이면 유사도가 높아도 나쁜 예시를 강화한다. 풀은 정답 데이터로 관리하고 주기적으로 검증한다.
  • 동적 선택은 프롬프트가 매번 달라져 프롬프트 캐싱 효과가 줄어든다. 고정 지시부는 앞에 두고 변하는 예시는 뒤에 배치해 캐시 히트를 최대화한다.
  • k는 3~5에서 시작해 홀드아웃 세트로 튜닝한다. 무작정 늘리면 지연과 노이즈만 커진다.

정리하면, 동적 few-shot 선택은 예시를 코드에서 데이터로 옮기고, 입력마다 관련 예시를 조립하는 구조다. 유사도 검색에 다양성 규칙을 얹고 예시 풀 품질을 관리하면, 모델 교체 없이도 정확도를 꾸준히 끌어올릴 수 있다.