프롬프트 잘 쓰는 법: 컨텍스트 설계로 AI 결과물을 고정하기

프롬프트 잘 쓰는 법: 컨텍스트 설계로 AI 결과물을 고정하기

같은 모델을 줘도 결과가 갈리는 첫 지점은 프롬프트예요. 프롬프트는 '주문'이 아니라 컨텍스트 설계라는 것, 그리고 그 한 방의 한계가 왜 다음 단계를 불렀는지 정리했어요.

프롬프트는 마법 주문이 아니라 컨텍스트 설계예요. 구조화 출력으로 LLM 출력 형식을 안정적으로 강제하고, 매번 달라지는 AI 결과물을 고정하는 법과 프롬프트만으로 안 되는 작업을 정리했어요.

글 · TRAIL Labs Research최종 수정
프롬프팅LLMtool use구조화 출력

프롬프트는 마법 주문이 아니라, 모델에게 깔아 주는 컨텍스트 설계예요. LLM을 처음 쓸 때는 누구나 똑같이 시작해요. 채팅창을 열고, 하고 싶은 걸 말로 적어요. 그런데 같은 모델, 같은 화면인데도 결과는 사람마다 크게 갈려요. 첫 번째 갈림길이 바로 프롬프트예요.

> 'LLM 활용의 진화' 시리즈 ①편. ① 프롬프팅 · ② 바이브 코딩 · ③ 에이전트 · ④ 하네스 엔지니어링 · ⑤ 오픈 모델. 사람이 LLM을 다뤄 온 방식이 어떻게 바뀌어 왔는지를 단계별로 풀어요.

막연한 한 줄 프롬프트가 들쭉날쭉한 결과를 내는 단계에서, 컨텍스트·구조·레퍼런스를 설계해 결과를 안정시키는 프롬프트 엔지니어링으로 넘어가는 모습

흔히 프롬프팅을 "마법 주문 찾기"로 오해해요. "이렇게 쓰면 잘 나온대"라는 문구를 수집하는 거죠. 하지만 실제로 결과를 가르는 건 주문이 아닌 모델에게 깔아 주는 컨텍스트의 설계예요. 프롬프팅은 LLM을 다루는 가장 첫 단계이자, 사실은 가장 작은 단위의 컨텍스트 엔지니어링이에요.

이 시리즈에서 다루는 것

편주제핵심
①프롬프팅컨텍스트 설계로 결과의 분산을 줄여요
②바이브 코딩의도만 말하면 코드가 나오지만 검증이 빠져요
③에이전트모델이 스스로 도구를 쓰고 결과를 보며 반복해요
④하네스 엔지니어링에이전트를 감싸는 실행·검증 환경을 설계해요
⑤오픈 모델자체 호스팅·튜닝 가능한 모델로 확장해요

프롬프트는 '주문'이 아닌 '컨텍스트 설계'예요

막연한 한 줄은 막연한 결과를 줘요. "이 주제로 글 써줘"는 모델에게 너무 많은 자유를 남겨요. 톤도, 길이도, 형식도 모델이 그때그때 정하니까 매번 다르게 나와요.

좋은 프롬프트는 모델이 추측해야 할 빈칸을 줄여요. 무엇을, 누구를 위해, 어떤 형식으로, 무엇을 피해서: 이 네 가지를 미리 채워 주면 결과의 분산이 확 줄어들어요. 같은 모델이라도요.

막연한 한 줄 프롬프트는 형식과 품질이 흔들리는 출력을 내지만, 컨텍스트·스키마·레퍼런스를 설계해 넣으면 검증된 구조화 출력이 나온다

구조를 강제하면 결과가 안정돼요

프롬프트로 형식을 부탁하는 것만으로는 부족할 때가 많아요. "JSON으로 줘"라고 해도 가끔 앞에 설명을 붙이거나 줄바꿈이 흔들려서, 그걸 파싱하는 코드가 자꾸 깨져요.

그래서 실무에서는 형식을 부탁하지 않고 강제해요. Anthropic의 tool_use [1] 또는 OpenAI의 function calling [2]로 출력 스키마를 고정하면, 모델은 반드시 그 구조로만 답해요. 우리가 카드뉴스 생성 파이프라인에서 쓰는 패턴도 이거예요.

# ① 막연한 프롬프트 — 형식이 매번 흔들려요
resp = await client.messages.create(
    model="claude-opus-4-8",
    messages=[{"role": "user", "content": "이 주제로 슬라이드 6장 만들어줘"}],
)
text = resp.content[0].text   # 머리말·줄바꿈이 들쭉날쭉 → 파싱이 깨져요

# ② tool_use 로 스키마를 고정 — 반드시 이 구조로만 답해요
tools = [{
    "name": "emit_slides",
    "input_schema": {
        "type": "object",
        "properties": {
            "slides": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "title": {"type": "string"},
                        "body":  {"type": "string"},
                    },
                    "required": ["title", "body"],
                },
            },
        },
        "required": ["slides"],
    },
}]
resp = await client.messages.create(
    model="claude-opus-4-8",
    tools=tools,
    tool_choice={"type": "tool", "name": "emit_slides"},   # 이 도구를 반드시 호출
    messages=[{"role": "user", "content": prompt}],
)
slides = resp.content[0].input["slides"]   # 검증된 구조 — 파싱 불필요

①과 ②는 같은 모델, 같은 주제예요. 다른 건 출력을 설계했느냐뿐이에요. ②는 파싱 에러가 사라지고, 빈 값·잘못된 필드도 스키마 단계에서 걸러져요. 프롬프트를 "글"이 아닌 인터페이스로 다루기 시작하는 순간이에요.

레퍼런스를 같이 줘야 '우리답게' 나와요

구조를 잡아도 톤은 또 다른 문제예요. 모델은 "우리 브랜드 톤"을 몰라요. 그래서 말로 설명하는 대신, 실제 잘 나갔던 예시 몇 개를 같이 넣어요. 이걸 퓨샷(few-shot)이라고 해요. "이런 톤이에요"라는 설명 열 줄보다, 진짜 예시 두 개가 훨씬 정확하게 톤을 전달해요.

한발 더 나아가면, 매번 손으로 예시를 붙이는 대신 브랜드 컨텍스트를 한 번 정의해 두고 프롬프트 앞에 자동으로 붙여요. 우리 파이프라인에서는 이걸 메모리 블록으로 관리해서, 글·카드뉴스·상세페이지가 모두 같은 톤 컨텍스트를 물려받게 해요. 프롬프트가 일회용 문장에서 재사용되는 컨텍스트로 바뀌는 거죠.

그런데 프롬프트 한 방으로는 닿지 못하는 곳이 있어요

여기까지 오면 프롬프팅만으로도 꽤 멀리 가요. 하지만 프롬프트에는 구조적인 한계가 셋 있어요.

  • 상태가 없어요(stateless): 한 번의 요청과 한 번의 응답이 전부예요. 모델은 직전에 뭘 했는지, 실제로 동작했는지 몰라요.
  • 도구가 없어요(toolless): 코드를 실행하거나, 파일을 읽거나, 결과를 확인할 수 없어요. 머릿속으로만 답할 뿐이에요.
  • 검증이 없어요: 틀려도 틀린 줄 몰라요. 맞는지 확인하고 고치는 일은 전부 사람 몫이에요.

이 세 한계가 다음 단계를 불렀어요. "한 번에 다 적지 말고, 모델이 코드를 쓰게 하고 돌려 보면서 고치면 어떨까" 하는 흐름이 바이브 코딩이고, "모델이 스스로 도구를 쓰고 결과를 보며 반복하게 하자"가 에이전트예요.

정리하면

프롬프팅은 LLM을 다루는 첫 번째 방식이자, 지금도 모든 단계의 바닥에 깔린 기본기예요. 핵심은 마법 주문이 아닌 컨텍스트 설계예요. 빈칸을 줄이고, 구조를 강제하고, 레퍼런스를 같이 주는 것. 그것만으로도 같은 모델에서 전혀 다른 안정성을 얻어요.

하지만 프롬프트 한 방은 상태도, 도구도, 검증도 없어요. 다음 편에서는 이 한계를 처음으로 흔든 방식, 바이브 코딩을 다뤄요.

이 글과 이어지는 내용은 바이브 코딩(vibe coding)이란? 한계와 AI 코드 검증 루프, AI 에이전트란? 모델이 스스로 루프를 돌 때 권한과 승인 게이트, 퍼스트파티 데이터가 뭐예요? 서드파티 쿠키 이후 마케팅 활용법에서 볼 수 있어요.

자주 묻는 질문

프롬프트 엔지니어링과 컨텍스트 엔지니어링은 다른 건가요?

프롬프팅은 LLM을 다루는 가장 첫 단계이자, 사실은 가장 작은 단위의 컨텍스트 엔지니어링이에요. 무엇을·누구를 위해·어떤 형식으로·무엇을 피해서를 미리 채워 주는 일이 곧 컨텍스트 설계예요.

tool_use와 그냥 'JSON으로 줘'라고 요청하는 건 뭐가 다른가요?

'JSON으로 줘'는 부탁이라 가끔 앞에 설명이 붙거나 줄바꿈이 흔들려서 파싱이 깨져요. tool_use나 function calling은 출력 스키마를 강제해서, 모델이 반드시 그 구조로만 답하고 빈 값·잘못된 필드는 스키마 단계에서 걸러져요.

퓨샷 예시는 몇 개가 적당한가요?

정해진 개수보다 실제 톤을 보여주는 예시인지가 중요해요. 본문에서 든 예로는, '이런 톤이에요'라는 설명 열 줄보다 진짜 잘 나갔던 예시 두 개가 훨씬 정확하게 톤을 전달해요.

참고자료

  1. [1]Anthropic, "Tool use with Claude" 공식 문서
  2. [2]OpenAI, "Function calling" 공식 가이드

요약

  • 프롬프팅은 마법 주문이 아니라 모델에게 깔아 주는 컨텍스트 설계예요.
  • 무엇을·누구를 위해·어떤 형식으로·무엇을 피해서를 채우면 결과의 분산이 줄어요.
  • 형식은 부탁이 아니라 tool_use 같은 구조화 출력으로 강제해야 안정돼요.
  • 프롬프트는 상태·도구·검증이 없어서, 그 한계가 바이브 코딩과 에이전트를 불렀어요.

TRAIL Labs가 왜 이렇게 일하는지

연구자 창업자가 측정과 처방의 근거를 어떻게 세우는지, 세 제품이 회사와 어떤 관계인지는 회사 소개에 정리했고, 날마다 일하는 방식은 따로 적어 두었습니다.

다른 글

같은 주제로 이어 읽기

이 글은 TRAIL Labs 카테고리에 속해요. 같은 카테고리 글 16편을 한자리에서 볼 수 있어요. TRAIL Labs 카테고리 글 전체 보기