LLM 호출은 함수다 — 무엇을 넣고 무엇을 받나
한 줄 요약
LLM 호출은 신비로운 무언가가 아니라 값을 입력하면 출력이 나오는 함수 하나입니다. 이 절에서 넣는 것 세 가지와 받는 것 두 가지를 정리하면, 앞으로 나올 모든 코드가 이 틀의 변형으로 보입니다.
1. 1장에서 쓴 그 두 줄
아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.
resp = client.models.generate_content(model=GEMINI_MODEL, contents=question)
return (resp.text or "")
이게 전부였습니다. 그림으로 그리면 이렇습니다.
1장에서는 model 과 contents 만 썼습니다. 이번 장에서 config 를 배웁니다.
2. 넣는 것 세 가지
① model — 어떤 모델을 쓸지
아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.
model="gemini-2.5-flash"
이 과정은 계속 gemini-2.5-flash 를 씁니다. 코드에는 직접 안 적고 common.py 의 GEMINI_MODEL 을 가져다 씁니다.
아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.
from common import GEMINI_MODEL
이렇게 해 두면 나중에 .env 의 GEMINI_MODEL 한 줄만 바꿔도 8개 파일 전부에 반영됩니다.
② contents — 무엇을 물을지
여기가 이번 장의 핵심입니다. 두 가지 형태로 넣을 수 있습니다.
아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.
# 형태 1 — 문자열 하나 (한 번 묻고 한 번 답받기)
contents="스마트워치 추천해 주세요"
# 형태 2 — 대화 목록 (지금까지의 대화를 통째로)
contents=[
Content(role="user", parts=[Part(text="제 이름은 김민준입니다")]),
Content(role="model", parts=[Part(text="김민준 고객님, 안녕하세요")]),
Content(role="user", parts=[Part(text="제 이름이 뭐라고 했죠?")]),
]
형태 2가 "멀티턴"의 정체입니다. 나중에 자세히 봅니다. 지금은 "대화 목록도 넣을 수 있다" 정도만 알아 두세요.
③ config — 어떻게 답할지
설정을 담는 객체입니다. 이번 장에서 두 가지를 배웁니다.
아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.
from google.genai import types
config = types.GenerateContentConfig(
system_instruction="너는 승승장구몰의 CS 상담원이다.", # 역할·규칙
temperature=0.3, # 답의 다양성
)
| 설정 | 무엇을 정하나 | 배우는 절 |
|---|---|---|
system_instruction |
모델의 역할·말투·규칙 | 다음 절 |
temperature |
매번 같은 답 ↔ 다양한 답 | 3절 |
tools |
쥐여 줄 도구 목록 | 3장 |
3. 받는 것 두 가지
① resp.text — 답변 문자열
거의 항상 이것만 씁니다.
아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.
print(resp.text)
② resp.usage_metadata — 얼마나 썼는지
아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.
print(resp.usage_metadata)
prompt_token_count=42 candidates_token_count=115 total_token_count=157
토큰(token) 은 LLM이 글을 세는 단위입니다. 요금이 이 숫자로 매겨집니다. 7절에서 자세히 봅니다.
resp안에는 이것 말고도 여러 가지가 들어 있습니다. 안전성 판정, 답변 후보 목록, 그리고 3장에서 결정적으로 중요해질resp.function_calls— "모델이 어떤 함수를 부르고 싶어 하는지" 가 담긴 자리입니다. 지금은.text만 씁니다.
4. 호출은 '상태가 없다'
이번 장에서 가장 중요한 성질입니다. 미리 분명히 짚어 둡니다.
generate_content를 두 번 부르면, 두 번째 호출은 첫 번째 호출을 전혀 모릅니다.
아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.
ask("제 이름은 김민준입니다.") # 호출 ①
ask("제 이름이 뭐라고 했죠?") # 호출 ② — ①을 모른다
②는 ①과 아무 관계 없는, 완전히 새로운 요청입니다. 사람으로 치면 매번 처음 만나는 사람에게 말을 거는 것과 같습니다.
이 성질을 무상태(stateless) 라고 부릅니다. 5절에서 직접 확인하고, 6절에서 이걸 우회하는 방법을 만듭니다.
"그럼 ChatGPT는 어떻게 기억하죠?" — ChatGPT도 기억하지 않습니다. 내부에서 지금까지의 대화 전체를 매번 다시 보내고 있을 뿐입니다. 우리가 6절에서 손으로 하게 될 그 일을, ChatGPT는 대신 해 주고 있는 것입니다.
5. 왜 이 틀을 외워야 하나
앞으로 나올 코드는 전부 이 틀의 변형입니다.
| 장 | 무엇이 달라지나 |
|---|---|
| 2장 | config 에 system_instruction 과 temperature 를 넣는다 |
| 3장 | config 에 tools 를 넣는다 → 에이전트가 된다 |
| 4장 | contents 에 도구 실행 결과를 쌓아 다시 보낸다 → 루프가 된다 |
| 5장 | 이 호출을 LangChain이 대신 해 준다 |
| 7장 | contents 쌓기를 LangGraph가 대신 해 준다 |
5장부터는 이 코드가 눈에 안 보이게 됩니다. 라이브러리가 감춰 주기 때문입니다. 그래서 4장까지는 일부러 손으로 씁니다. 감춰진 것이 무엇인지 알아야, 나중에 문제가 생겼을 때 어디를 볼지 알 수 있습니다.
핵심 정리
- LLM 호출은
generate_content(model, contents, config)함수 하나입니다. model은 어떤 모델,contents는 무엇을 물을지,config는 어떻게 답할지입니다.contents에는 문자열 하나를 넣을 수도, 대화 목록을 넣을 수도 있습니다. 후자가 멀티턴입니다.- 받은 응답에서
.text로 답변을,.usage_metadata로 사용량을 꺼냅니다. - 호출은 무상태입니다 — 두 번째 호출은 첫 번째를 모릅니다. ChatGPT가 기억하는 것처럼 보이는 이유는 매번 대화 전체를 다시 보내기 때문입니다.
- 앞으로 나올 모든 코드는 이 틀에
config를 채우고contents를 쌓는 이야기입니다.