2장. 개발 환경 준비
실습 3 — OpenAI 라이브러리로 첫 질문 보내기
OpenAI 의 파이썬 SDK(openai 패키지)로 진짜 모델에게 첫 질문을 보내고, 답의 글과 토큰 수를 읽습니다. 과목 폴더의 .env 에 교수자가 준 키가 있어야 하고, 인터넷이 연결되어 있어야 합니다. 이 절의 출력은 API 키를 받기 전에 실행하지 않고 유추한 예상 출력입니다.
1단계. 키 읽고 클라이언트 만들기
import os
from dotenv import find_dotenv, load_dotenv
from openai import OpenAI
# 과목 폴더의 .env 에서 OPENAI_API_KEY 를 읽어 환경 변수에 넣는다
# find_dotenv(usecwd=True): 지금 폴더(data)에서 위로 올라가며 .env 를 찾는다
load_dotenv(find_dotenv(usecwd=True))
# 모델 이름도 .env 에서 바꿀 수 있게 하고, 없으면 gpt-4.1-mini 를 쓴다
MODEL = os.getenv("OPENAI_MODEL", "gpt-4.1-mini")
# OpenAI(): 환경 변수 OPENAI_API_KEY 를 스스로 찾아 읽는 클라이언트를 만든다
client = OpenAI()
# 키는 찍지 않고, 읽었는지만 확인한다
print("키를 읽었나:", os.getenv("OPENAI_API_KEY") is not None)
print("모델:", MODEL)키를 읽었나: True
모델: gpt-4.1-mini코드가 의미하는 것 load_dotenv(find_dotenv(usecwd=True)) 로 과목 폴더의 .env 에서 키를 올리고, OpenAI() 가 그 키를 스스로 읽어 클라이언트를 만듭니다. 키를 찍지 않고 is not None 으로 읽었는지만 보는 것은 실습 2의 가리기처럼 키를 화면에 남기지 않으려는 것입니다. 클라이언트를 만드는 것만으로는 아직 서버에 아무것도 보내지 않습니다.
2단계. 질문 보내고 답 읽기
# 1차시에서 본 역할(role)과 내용(content)의 사전 목록
messages = [
{"role": "system", "content": "너는 구름다리여행의 친절한 여행 비서다. 두 문장 이내로 답한다."},
{"role": "user", "content": "제주도 2박 3일 여행의 첫날에 가 볼 곳을 하나 추천해 줘."},
]
# chat.completions.create: 대화(chat)를 이어 쓴 답(completion)을 만들어(create) 달라는 요청
response = client.chat.completions.create(
model=MODEL, # 쓸 모델 이름
messages=messages, # 보낼 메시지 목록
temperature=0, # 온도 0: 다시 물어도 비슷한 답이 나오게
)
# choices: 답 후보 목록. 보통 하나만 오므로 첫째([0])의 message.content 가 답의 글이다
print(response.choices[0].message.content)첫날에는 공항에서 가까운 용두암을 추천합니다. 바다를 보며 가볍게 걷기 좋아 도착한 날 일정으로 알맞습니다.코드가 의미하는 것 client.chat.completions.create 가 이 장에서 처음으로 진짜 모델에게 요청을 보내는 줄입니다. 시스템 메시지로 "두 문장 이내"라는 말투와 길이를 정하고 사용자 메시지로 질문을 넣어, 둘을 한 번에 보냅니다. 답의 글은 choices[0].message.content 에 담겨 오며, 예상 출력의 용두암 추천은 모델이 실제로 쓸 글과 다를 수 있습니다.
3단계. 토큰 수 읽기
# usage: 이 요청에서 쓴 토큰. 요금은 이 숫자로 매긴다
usage = response.usage
print("보낸 토큰(prompt_tokens):", usage.prompt_tokens)
print("받은 토큰(completion_tokens):", usage.completion_tokens)
print("모두(total_tokens):", usage.total_tokens)보낸 토큰(prompt_tokens): 60
받은 토큰(completion_tokens): 40
모두(total_tokens): 100코드가 의미하는 것 response.usage 는 이 요청의 영수증입니다. prompt_tokens 에는 시스템 메시지와 질문이, completion_tokens 에는 답의 글이 들어가고, 요금은 이 두 숫자로 매겨집니다. 예상 출력의 60, 40 은 둥글게 어림한 값이라 실제로 돌리면 달라집니다.
4단계. 모델 이름, 끝난 까닭, 요금 어림
# model: 실제로 답한 모델의 이름. 날짜가 붙은 이름으로 올 수 있다
print("답한 모델:", response.model)
# finish_reason: stop 이면 모델이 스스로 끝냈고, length 면 길이 제한에 잘렸다
print("끝난 까닭:", response.choices[0].finish_reason)
# 가정한 단가: 100만 토큰당 입력 1달러, 출력 4달러. 실제 단가는 OpenAI 가격 페이지에서 확인한다
PRICE_IN, PRICE_OUT = 1.0, 4.0
cost = (usage.prompt_tokens * PRICE_IN + usage.completion_tokens * PRICE_OUT) / 1_000_000
print(f"이 질문 한 번: {cost:.6f}달러 (가정한 단가)")답한 모델: gpt-4.1-mini-2025-04-14
끝난 까닭: stop
이 질문 한 번: 0.000220달러 (가정한 단가)코드가 의미하는 것 response.model 과 finish_reason 은 누가 답했고 왜 끝났는지를 알려 주는 칸입니다. length 가 나오면 답이 잘린 것이므로, 챗봇이 잘린 답을 사용자에게 그대로 보여 주기 전에 알아챌 수 있습니다. 요금 식은 1장 실습 3과 같고, 예상 출력의 금액은 가정한 단가와 어림한 토큰으로 낸 것입니다.
이 실습이 뜻하는 것
이 실습은 거래처 창구에 처음으로 주문을 넣어 보는 일과 같습니다. 지갑에서 출입증을 꺼내 창구 앞에 서고(1단계), 주문서를 내고 물건을 받고(2단계), 영수증의 수량을 읽고(3단계), 영수증의 나머지 칸과 값을 확인합니다(4단계). 여기서 창구가 client, 주문서가 messages, 물건이 choices[0].message.content, 영수증이 usage 입니다. 비유와 다른 점은, 같은 주문서를 다시 내도 모델은 매번 조금씩 다른 글을 돌려줄 수 있다는 것입니다.
| 단계 | 출력 | 뜻 |
|---|---|---|
| 1. 클라이언트 | 키를 읽었나 True, 모델 gpt-4.1-mini | .env 의 키가 환경 변수로 올라왔고, OPENAI_MODEL 이 없어 기본값을 썼습니다 |
| 2. 답 | 용두암을 추천하는 두 문장 | 시스템 메시지의 "두 문장 이내" 지시를 지켰습니다 |
| 3. 토큰 | 보냄 60, 받음 40, 모두 100 쯤 | 보낸 쪽에는 시스템 메시지와 질문이, 받은 쪽에는 답의 글이 들어갑니다 |
| 4. 나머지 | 날짜가 붙은 모델 이름, stop, 0.00022달러쯤 | 답이 잘리지 않고 끝났고, 가정한 단가로 한 번에 0.0002달러 남짓입니다 |
한 줄로 줄이면, 키를 읽고 클라이언트를 만들고 메시지를 보내 답과 토큰 수를 읽는 첫 호출의 모든 단계를 해 본 실습입니다. 모델이 쓰는 답의 글은 실행할 때마다, 모델마다 조금씩 달라집니다. 용두암이 아닌 다른 곳을 추천해도 이상한 것이 아닙니다. 토큰 수도 답의 길이에 따라 달라지고, 표의 숫자는 둥글게 어림한 값입니다. 4단계의 모델 이름처럼 gpt-4.1-mini 뒤에 날짜가 붙어 돌아오면, 그 날짜의 판이 답했다는 뜻입니다.
코드 설명
load_dotenv(find_dotenv(usecwd=True))는 이 과정에서 모델을 부르는 모든 코드의 첫 줄입니다.data폴더에서 실행해도 과목 폴더의.env를 찾습니다.OpenAI()에api_key=를 주지 않으면 환경 변수OPENAI_API_KEY를 스스로 읽습니다. 코드에 키를 적지 않아도 되는 까닭입니다.client.chat.completions.create(model=…, messages=…, temperature=0)는 인자 셋으로 부릅니다.model은 모델 이름,messages는 사전 목록,temperature는 1장에서 본 온도입니다.response.choices[0].message.content가 답의 글이고,response.usage의prompt_tokens,completion_tokens,total_tokens가 요금의 바탕이 되는 토큰 수입니다.- 요금 식은 1장 실습 3과 같습니다. 보낸 토큰과 받은 토큰의 단가가 달라서 따로 곱해 더합니다.