AI는 같은 질문에도 왜 다른 답을 할까?

LLM이 다음 Token을 선택해 답을 만드는 원리와 Temperature가 출력의 다양성에 미치는 영향, Spring AI의 Prompt와 Structured Output을 쉽게 설명합니다.

Spring AI로 챗봇을 넘어 일하는 AI 만들기 3/9

Java·Spring 개발자가 LLM, RAG, Tool Calling, MCP를 공부하며 이해한 것들

1편 — Spring AI는 AI 모델을 만드는 도구일까?

2편 — 인터넷 없이도 AI 챗봇을 만들 수 있을까?

2편에서 로컬로 모델을 실행하는 구조를 살펴봤습니다. 그런데 같은 모델에 똑같은 질문을 했는데도 답의 표현이나 순서가 조금씩 달라질 수 있습니다. 왜 그럴까요?

짧은 답은 이렇습니다. LLM(Large Language Model, 대규모 언어 모델)은 완성된 정답 문장을 검색하는 기계가 아니라, 지금까지 입력된 내용을 바탕으로 다음 Token을 반복해서 선택하는 생성 모델이기 때문입니다.

Prompt와 Temperature는 이 선택 과정에 영향을 줍니다. 하지만 더 낮은 Temperature나 더 자세한 Prompt가 사실성과 완전한 재현성을 자동으로 보장하지는 않습니다.

이 글은 학습 순서에 맞춰 2026년 3월 19일에 배치했습니다. 이틀 전인 3월 17일에 Spring AI 1.1.3과 2.0.0-M3가 발표됐으므로 당시 안정 버전 흐름은 1.1.3이었고 2.0은 마일스톤 단계였습니다. 코드와 API 설명은 2026년 8월 14일의 공식 안정 버전인 Spring AI 2.0.0을 기준으로 다시 확인했으며, 실제 모델을 실행해 성능이나 답변을 측정한 기록은 아닙니다.

같은 질문인데 왜 답이 달라졌을까?

“오늘 점심에는 무엇을 먹을까?”라는 질문을 떠올려 보겠습니다. 누군가는 김밥을 먼저 말하고, 누군가는 라면이나 피자를 고릅니다. 가능한 답이 하나만 있는 질문이 아니기 때문입니다.

LLM도 비슷하게 문장을 이어갑니다. “오늘 점심에는”이라는 문맥 뒤에 올 후보를 만들고, 각 후보가 이어질 가능성을 계산한 다음 하나를 고릅니다. 선택한 말을 문맥에 붙이고 다시 그다음 후보를 계산합니다.

여기서 말하는 Prompt(프롬프트)는 모델에 전달하는 지시와 입력입니다. 질문 하나일 수도 있고, 역할·대화 기록·출력 조건을 함께 담은 입력 묶음일 수도 있습니다.

AI는 문장을 통째로 꺼내지 않는다

데이터베이스 조회는 저장된 값에서 조건에 맞는 결과를 찾습니다. 회원 번호로 이름을 조회하면 저장된 이름을 그대로 돌려주는 식입니다.

LLM의 기본 생성은 다릅니다. 저장된 완성 문장 목록에서 정답 한 줄을 고르는 것이 아니라, 현재 문맥 다음에 이어질 작은 단위를 하나씩 생성합니다. 그래서 같은 질문도 후보 선택에 따라 다른 길로 이어질 수 있습니다.

RAG(Retrieval-Augmented Generation, 검색 증강 생성)나 Tool Calling(도구 호출)을 붙이면 외부 문서나 API에서 정보를 가져올 수 있습니다. 다만 가져온 정보가 있어도 최종 문장을 만드는 기본 과정에는 다음 Token 선택이 포함됩니다. 이번 글은 그 생성 과정에 집중합니다.

Token은 단어 하나일까?

Token(토큰)은 모델이 텍스트를 처리하는 단위입니다. Tokenization(토큰화)은 입력 문장을 모델이 처리할 Token들로 나누는 과정입니다.

Token은 항상 단어 하나나 글자 하나와 같지 않습니다. 자주 쓰는 단어가 하나의 Token이 될 수도 있고, 긴 단어가 여러 Token으로 나뉠 수도 있습니다. 띄어쓰기와 문장 부호도 분할에 영향을 줄 수 있습니다.

예를 들어 “오늘 점심에는”을 [오늘] [점심] [에는]처럼 나눠 생각하면 원리를 이해하기 쉽습니다. 하지만 이것은 단순화한 예시입니다. 실제 Token 분할은 모델이 사용하는 Tokenizer에 따라 달라지므로 특정 한국어 문장이 반드시 이렇게 나뉜다고 볼 수는 없습니다.

모델은 Token으로 바뀐 현재 문맥을 읽고 다음 Token 후보를 계산합니다. 후보에는 우리가 예상한 단어 조각뿐 아니라 공백, 문장 부호, 예상 밖의 조각도 들어갈 수 있습니다.

다음 Token은 어떻게 고를까?

모델은 각 후보에 Logit(로짓, 확률로 바꾸기 전의 후보별 점수)을 만듭니다. 이 점수들을 Probability Distribution(확률 분포), 즉 후보별 가능성을 모두 합해 1이 되는 형태로 바꿉니다.

그다음 분포에 따라 Token 하나를 선택합니다. 선택된 Token은 문맥 뒤에 붙고, 늘어난 문맥으로 다시 후보 점수와 확률을 계산합니다.

문장 입력 → Tokenization → 후보별 Logit → 확률 분포
→ Token 선택 → 문맥에 추가 → 다음 Token 계산

움직이는 다음 Token 선택기

같은 가상 Logit에 서로 다른 Temperature를 적용해 다음 Token 후보의 확률 분포와 선택 결과가 어떻게 달라지는지 보여줍니다.

Prompt

오늘 점심에는김밥

  1. 김밥선택
    가상 확률90.3%
    가상 Logit 2.6
  2. 라면선택
    가상 확률8.8%
    가상 Logit 1.9
  3. 피자선택
    가상 확률0.8%
    가상 Logit 1.2
  4. 샐러드선택
    가상 확률0.1%
    가상 Logit 0.5
softmax(logit / temperature)

확률과 선택 결과는 원리를 설명하기 위한 가상 예시이며, 특정 모델의 측정 결과가 아닙니다.

선택된 Token: 김밥

낮은 Temperature에서는 높은 점수의 후보에 분포가 더 집중됩니다. 높은 Temperature에서는 분포가 상대적으로 평평해져 다른 후보가 선택될 여지가 커집니다.

선택기에는 네 개의 가상 후보만 넣었습니다. 실제 모델은 훨씬 많은 후보를 다루며, 화면의 Logit·확률·선택 결과는 특정 모델에서 측정한 값이 아닙니다.

Temperature를 바꾸면 무엇이 달라질까?

Temperature(온도)는 다음 Token 선택 분포의 집중도를 조절하는 값입니다. 낮게 설정하면 높은 점수의 후보에 확률이 더 집중됩니다. 답의 표현이 상대적으로 일관될 수 있지만, 틀린 후보가 가장 높은 점수를 받았다면 그 오답에도 더 집중할 수 있습니다.

높게 설정하면 분포가 상대적으로 평평해집니다. 낮은 점수의 후보도 선택될 여지가 커져 표현이나 아이디어가 다양해질 수 있지만, 변동성도 커질 수 있습니다.

따라서 Temperature는 사실성 버튼이 아닙니다. 이미 계산된 후보의 선택 분포를 조절할 뿐 모델에 새로운 지식을 넣거나 근거를 확인해 주지 않습니다.

선택기의 0.3과 1.2는 차이를 눈으로 보여주기 위한 가상 조건입니다. 모든 모델과 업무에 그대로 적용할 권장값이 아닙니다. 제공자와 모델은 허용 범위, 기본값, 다른 Sampling 옵션과의 상호작용이 다를 수 있습니다.

Temperature 0이면 항상 같을까?

Temperature를 0에 가깝게 낮추면 보통 높은 점수 후보에 선택이 집중됩니다. 그렇다고 모든 환경에서 같은 출력이 절대 보장된다고 단정하면 안 됩니다.

모델 버전이 바뀌거나 System Message와 대화 기록이 달라질 수 있습니다. Temperature 외의 Sampling 옵션, 모델 제공자의 구현과 실행 환경도 결과에 영향을 줄 수 있습니다. 같은 첫 Token을 골라도 이후 단계에서 다른 길이 생길 수 있습니다.

일관성이 중요한 업무라면 Temperature 하나로 끝내지 않습니다. 입력과 모델 버전을 고정하고, 출력 형식과 업무 규칙을 따로 검증하며, 대표 입력으로 평가 테스트를 반복해야 합니다. 실패했을 때 사람의 검토나 안전한 기본 처리로 보내는 경로도 필요합니다.

Spring AI는 Prompt를 어떻게 전달할까?

Message(메시지)는 역할과 내용을 가진 대화 단위입니다. Prompt는 여러 Message와 모델 호출 옵션을 묶어 전달할 수 있습니다.

System Message(시스템 메시지)는 모델의 역할과 기본 행동을 안내합니다. User Message(사용자 메시지)는 실제 사용자의 요청을 담습니다. Temperature 같은 옵션도 같은 모델 요청에 포함할 수 있습니다.

다음은 Spring AI 2.0.0의 ChatClient와 공통 ChatOptions를 사용한 최소 예제입니다.

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.prompt.ChatOptions;

String answer = chatClient.prompt()
    .system("중학생도 이해할 수 있는 짧은 한국어로 답하세요.")
    .user("Temperature는 어떤 역할을 하나요?")
    .options(ChatOptions.builder()
        .temperature(0.3))
    .call()
    .content();

system()은 기본 역할과 지침을, user()는 사용자의 질문을 넣습니다. options()는 이 요청의 Temperature를 지정합니다. Spring AI 2.0.0의 ChatClient는 완성된 옵션 객체가 아니라 옵션 Builder를 받도록 바뀌었으므로 1.1.x 예제와 섞지 않았습니다.

call()은 동기 방식으로 요청을 보내고 content()는 응답 본문을 문자열로 꺼냅니다. 이 코드는 Spring AI 2.0.0 공식 문서 기반 예제이며, 현재 환경에서 모델 응답을 측정한 결과는 아닙니다. 모델마다 지원하는 옵션과 실제 처리 방식이 다를 수 있습니다.

Prompt Template은 왜 필요할까?

학교 급식 추천을 매일 만든다고 생각해 보겠습니다. “알레르기 식품을 피하고 재료 안에서 메뉴 하나를 추천하라”는 지시는 고정되지만, 날짜와 남은 재료는 매번 달라집니다.

Prompt Template(프롬프트 템플릿)은 고정된 지시와 바뀌는 값을 분리해 재사용하는 입력 구조입니다. {date}{ingredients} 같은 자리에 실행 시점의 값을 넣으면 팀이 같은 형태의 Prompt를 반복해서 만들기 쉽습니다.

하지만 Template은 입력 구조를 일정하게 할 뿐입니다. 모델이 모르는 사실을 추가하지 않고, 같은 출력을 보장하지 않으며, 업무 규칙 준수나 사실 검증도 대신하지 않습니다. 길게 쓰는 것보다 필요한 정보와 금지 조건을 분명하게 나누는 일이 중요합니다.

JSON이면 믿어도 될까?

Structured Output(구조화 출력)은 모델 응답을 JSON이나 Java 객체처럼 정해진 구조로 받는 방식입니다. 예를 들어 고객 문의를 category, priority, reason 필드가 있는 Java record로 바꾸면 후속 코드가 다루기 쉬워집니다.

Spring AI의 StructuredOutputConverter는 모델에게 원하는 형식을 안내하고 텍스트 응답을 객체로 바꾸는 일을 돕습니다. 공식 문서도 이를 best effort, 즉 가능한 범위에서 형식을 맞추는 방식으로 설명하며 별도 검증을 권합니다.

형식 검증은 JSON 문법이 맞는지, 필수 필드가 있는지, 자료형과 객체 변환이 가능한지 확인합니다. 내용 검증은 값이 사실인지, 업무 정책에 맞는지, 허용된 코드인지, 금액 범위가 유효한지 확인합니다.

두 검증은 다른 문제입니다. JSON 형식이 정확하다는 것과 JSON 안의 내용이 사실이라는 것은 같지 않습니다. 객체 변환에 성공한 뒤에도 Bean Validation, 허용 목록, 데이터베이스 조회와 업무 규칙 검사가 필요할 수 있습니다.

한 단계 더 깊게: Temperature 식 읽기

softmax(logit / temperature)

Softmax는 후보별 Logit을 합이 1인 확률 분포로 바꾸는 계산입니다. 계산할 때 Logit을 Temperature로 나누므로 낮은 값에서는 점수 차이가 더 크게 드러나 분포가 뾰족해지고, 높은 값에서는 차이가 완만해져 분포가 상대적으로 평평해집니다.

실제 구현은 수치 안정성을 위해 가장 큰 Logit을 먼저 빼고 지수 계산을 할 수 있습니다. Temperature가 정확히 0이면 이 식에 그대로 나눌 수 없으므로 제공자와 Sampling 구현의 처리 방식을 확인해야 합니다. 어떤 경우에도 Temperature가 지식이나 사실을 추가하지는 않습니다.

자주 생기는 네 가지 오해

Temperature 0이면 항상 같은 답인가?

항상 같다고 보장할 수 없습니다. 모델 버전, 입력 문맥, 다른 Sampling 옵션과 제공자 구현까지 함께 고정하고 결과를 평가해야 합니다.

Prompt를 길게 쓰면 더 정확해지는가?

길이 자체가 정확성을 만들지는 않습니다. 관련 없는 지시가 늘면 핵심 조건이 흐려질 수 있고, 모델이 모르는 사실은 긴 Prompt만으로 생기지 않습니다.

Token은 단어 하나인가?

항상 그렇지 않습니다. 단어 일부, 글자, 공백이나 문장 부호가 Token이 될 수 있으며 분할 방식은 모델과 Tokenizer에 따라 다릅니다.

JSON이면 내용도 믿을 수 있는가?

아닙니다. JSON 문법과 객체 변환이 성공해도 사실성, 허용 값, 금액과 업무 정책은 별도로 검증해야 합니다.

다음 Token을 이해하면 제어의 경계가 보인다

  1. LLM은 다음 Token을 반복해서 선택하며 답을 만듭니다.
  2. Temperature는 선택의 다양성에 영향을 주지만 사실성을 보장하지 않습니다.
  3. Prompt와 Structured Output은 결과를 다루기 쉽게 만들지만 검증을 대신하지 않습니다.

다음 편의 제목은 챗봇은 왜 방금 한 말을 잊어버릴까?입니다.

모델이 답을 만드는 원리는 알았다. 그런데 왜 조금 전에 나눈 대화를 잊어버릴까?

다음 글 — 챗봇은 왜 방금 한 말을 잊어버릴까?

공식 문서