목차
- 오늘 강의 개요
- 환경 설정 & 라이브러리 import
- OpenAI Chat Completions API 기본 사용법
- Response 객체 구조 상세 해부
- 역할(Role) 구분 - system / user / assistant
- Persona 설정 - system 메시지 활용
- temperature - 창의성 조절 파라미터
- LangChain ChatOpenAI 소개
- 핵심 개념 총정리
- 자주 나오는 실수 / 주의사항
1. 오늘 강의 개요
오늘은 AI LLM 과정의 첫 날이다. OpenAI API를 직접 호출하는 방법부터 시작해서 LangChain의 기초를 배운다.
오늘 배우는 것
1
2
3
4
5
6
7
8
9
10
11
| .env 파일로 API 키 관리
↓
OpenAI 클라이언트 초기화
↓
chat.completions.create() 호출
↓
Response 객체에서 텍스트 추출
↓
system/user/assistant 역할 이해
↓
LangChain ChatOpenAI 첫 사용
|
핵심 질문
- API를 왜 직접 만들지 않고 OpenAI/LangChain을 쓰나? → 이미 훈련된 초거대 모델을 API 형태로 호출해서 기능을 빌려 쓰는 것
2. 환경 설정 & 라이브러리 import
필요 패키지
1
| # pip install openai langchain-openai langchain-core python-dotenv
|
- ▶ openai : OpenAI API 직접 호출
- ▶ langchain-openai : LangChain의 OpenAI 연동 래퍼
- ▶ langchain-core : 프롬프트 템플릿, 출력 파서 등 핵심 기능
- ▶ python-dotenv : .env 파일에서 환경변수 로드
.env 파일 만들기
1
2
| # 프로젝트 루트에 .env 파일 생성 (절대 Git에 올리면 안 됨!)
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxx
|
초기화 코드
1
2
3
4
5
6
7
8
9
10
11
| import os
from openai import OpenAI
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from dotenv import load_dotenv
load_dotenv() # .env 파일 로드 → 환경변수로 등록
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
|
- ▶ load_dotenv() : .env 파일을 읽어서 os.environ에 등록
- ▶ os.getenv(“OPENAI_API_KEY”) : 환경변수에서 API 키 읽기
- ▶ temperature=0 : 항상 동일한(결정론적) 응답 생성
API 키 확인
1
2
| print(os.getenv("OPENAI_API_KEY"))
# → sk-proj-xxxxxxxx... (None이 나오면 .env 파일 위치/내용 확인 필요)
|
- ▶ None이 나오는 경우:
- .env 파일이 코드와 같은 폴더에 없는 경우
- .env 파일 내용에 공백이 있는 경우 (OPENAI_API_KEY = sk-xxx ← 공백 주의)
3. OpenAI Chat Completions API 기본 사용법
가장 기본적인 호출
1
2
3
4
5
6
7
8
9
| response = client.chat.completions.create(
model = "gpt-4o-mini",
messages = [
{
"role" : "user",
"content": "안녕하세요, 자기소개 부탁드립니다"
}
]
)
|
- ▶ client.chat.completions.create() : OpenAI Chat API 호출
- ▶ model : 사용할 모델 이름 (gpt-4o-mini, gpt-4o 등)
- ▶ messages : 대화 내용을 리스트 형태로 전달
3가지 다른 질문 한번에 해보기
1
2
3
4
5
6
7
8
9
| questions = ['파이썬이 뭐야?', 'llm이 뭐야?', 'api가 뭐야?']
for q in questions:
response = client.chat.completions.create(
model = "gpt-4o-mini",
messages = [{"role": "user", "content": q}]
)
print(f"Q: {q}")
print(f"A: {response.choices[0].message.content}")
print()
|
4. Response 객체 구조 상세 해부
Response 전체 구조
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| ChatCompletion
├── id : 'chatcmpl-DHr7Q...' (요청 고유 ID)
├── model : 'gpt-4o-mini'
├── choices : [Choice] ← 여기서 응답 텍스트 꺼냄
│ └── [0] : Choice
│ ├── finish_reason : 'stop' (정상 종료)
│ ├── index : 0
│ └── message : ChatCompletionMessage
│ ├── role : 'assistant'
│ └── content : '안녕하세요! 저는 OpenAI...' ← 실제 텍스트
└── usage
├── prompt_tokens : 입력 토큰 수
├── completion_tokens : 출력 토큰 수
└── total_tokens : 전체 토큰 수
|
응답 텍스트 추출 - 단계별로 파헤치기
1
2
3
4
| response.choices # [Choice(...)] ← 리스트
response.choices[0] # Choice(...) ← 첫 번째 응답 선택
response.choices[0].message # ChatCompletionMessage(...)
response.choices[0].message.content # '안녕하세요! 저는 OpenAI...' ← 최종 텍스트
|
- ▶ choices가 리스트인 이유:
- n 파라미터로 여러 응답을 동시에 받을 수 있음
- n=3 이면 choices[0], choices[1], choices[2] 로 3개 응답
- 기본값은 n=1 → choices[0] 만 사용
finish_reason 의미
1
2
3
| response.choices[0].finish_reason
# 'stop' → 정상 종료 (모델이 자연스럽게 응답 완료)
# 'length' → max_tokens 한도에 잘려서 종료 (응답이 잘렸을 수 있음!)
|
5. 역할(Role) 구분 - system / user / assistant
메시지 역할 3종류
| role | 설명 | 언제 쓰나? |
|---|
| system | AI의 성격, 규칙, 제약을 사전 정의 | 대화 시작 시 1번 |
| user | 사용자의 입력 메시지 | 매 질문마다 |
| assistant | AI의 응답 (대화 기록용) | 멀티턴 대화 구성 시 |
실제 messages 배열 구조
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| messages = [
{
"role" : "system",
"content": "당신은 IT 상담원입니다"
},
{
"role" : "user",
"content": "비밀번호를 잊어버렸어요"
},
{
"role" : "assistant",
"content": "비밀번호 재설정 방법을 안내드리겠습니다"
}
]
# 대화 내용 출력
for msg in messages:
print(f"[{msg['role']}] {msg['content']}")
# [system] 당신은 IT 상담원입니다
# [user] 비밀번호를 잊어버렸어요
# [assistant] 비밀번호 재설정 방법을 안내드리겠습니다
|
- ▶ system 메시지는 보통 messages 배열 맨 앞에 하나 넣는다
- ▶ user → assistant → user → assistant … 순서로 대화가 이어짐
6. Persona 설정 - system 메시지 활용
기본 질문과 Persona 설정 질문 비교
Persona 없이 질문: “1+1은?” → “1+1은 2입니다.”
Persona 설정 후: system: “당신은 초등학생을 가르치는 친절한 수학선생님입니다. 항상 단계별로 설명해주세요” “1+1은?” → “좋아요! 1 더하기 1을 같이 계산해봅시다. 1단계: 숫자 1이 하나 있습니다. 2단계: 또 다른 숫자 1을 더해볼게요…”
Persona 설정 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| response = client.chat.completions.create(
model = "gpt-4o-mini",
messages = [
{
"role" : "system",
"content": "당신은 초등학생을 가르치는 친절한 수학선생님입니다. "
"항상 단계별로 설명해주세요"
},
{
"role" : "user",
"content": "1+1은?"
}
]
)
print(response.choices[0].message.content)
|
다양한 Persona 예시
1
2
3
4
5
| # 수의사 Persona
"당신은 10년차 베테랑 수의사입니다. 의학적 관점에서 전문적으로 답변해주세요"
# 데이터 과학자 Persona
"당신은 MLOps 리더입니다. 실무 팁 위주로 두 문장 이하로 답변해주세요"
|
- ▶ Persona의 효과:
- 말투, 전문 분야, 답변 스타일을 지정할 수 있음
- 같은 질문이라도 전혀 다른 수준과 관점으로 답변
- 실무에서는 “고객 서비스 담당자”, “법률 자문” 등으로 활용
7. temperature - 창의성 조절 파라미터
temperature 개념
1
2
3
| temperature = 0.0 → 항상 같은 답변 (결정론적, 테스트에 좋음)
temperature = 0.7 → 약간의 다양성 (일반적인 챗봇에 적합)
temperature = 2.0 → 매우 창의적이고 다양한 답변 (예측 불가)
|
수학적 원리 - Softmax + Temperature
1
2
3
4
5
6
7
8
9
10
11
12
13
| import numpy as np
def calculate_softmax(logits, T):
z = np.array(logits)
e_z = np.exp(z / T - np.max(z / T))
return e_z / e_z.sum()
logits = [2.0, 1.5, 1.0, 0.5] # 각 토큰의 점수(logit)
temperature = [0.1, 0.5, 1.0, 2.0, 10.0]
for T in temperature:
probs = calculate_softmax(logits, T)
print(f"T={T}: {probs}")
|
결과 해석
1
2
3
| T=0.1 → [0.994, 0.006, 0.0, 0.0] ← 1등 토큰에 집중 (결정론적)
T=1.0 → [0.467, 0.284, 0.172, 0.104] ← 확률 분산
T=10.0 → [0.271, 0.261, 0.250, 0.240] ← 거의 균등 (무작위)
|
- ▶ T가 작을수록 = 가장 확률 높은 단어만 선택 (항상 같은 응답)
- ▶ T가 클수록 = 낮은 확률 단어도 선택 가능 (다양하고 창의적인 응답)
temperature별 같은 질문 결과 비교
1
2
3
4
5
6
7
8
9
10
11
12
| question = "인공지능의 미래에 대해 한 문장으로 말해주세요"
for temp in [0.0, 1.5]:
response = client.chat.completions.create(
model = 'gpt-4o-mini',
messages = [{'role': 'user', 'content': question}],
temperature = temp
)
print(f"temperature {temp}: {response.choices[0].message.content}")
# temperature 0.0: 인공지능의 미래는... (3번 실행해도 동일)
# temperature 1.5: 다양한 답변 (실행마다 다름)
|
8. LangChain ChatOpenAI 소개
OpenAI 직접 호출 vs LangChain 래퍼 비교
1
2
3
4
5
6
7
8
| OpenAI 직접:
client.chat.completions.create(model=..., messages=[...])
→ response.choices[0].message.content
LangChain ChatOpenAI:
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
response = llm.invoke("질문")
→ response.content
|
LangChain이 편한 이유
- 파이프(|) 연산자로 prompt | llm | parser 체인 구성 가능
- 다양한 LLM(Anthropic, Google 등)을 같은 인터페이스로 교체 가능
- 프롬프트 템플릿, 출력 파서 등 풍부한 기능 제공
첫 LangChain 호출
1
2
3
4
5
6
7
| from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
response = llm.invoke("LangChain을 한 문장으로 설명해주세요")
print(type(response)) # AIMessage
print(response.content) # 'LangChain은 LLM 기반 애플리케이션 개발을 위한...'
|
- ▶ llm.invoke() 반환값은 AIMessage 객체
- ▶ .content 속성으로 텍스트 추출
9. 핵심 개념 총정리
오늘 배운 것 한눈에 보기
| 개념 | 핵심 내용 |
|---|
| .env 파일 | API 키를 코드에 직접 쓰지 말고 환경변수로 관리 |
| chat.completions | OpenAI LLM API 호출 메서드 |
| Response 구조 | choices[0].message.content 로 텍스트 추출 |
| role: system | AI의 성격/역할을 사전 정의 |
| role: user | 사용자 입력 |
| role: assistant | AI 응답 (멀티턴 대화에서 히스토리로 사용) |
| temperature | 낮을수록 일관성, 높을수록 창의성 |
| LangChain | llm.invoke()로 더 간결하게 LLM 호출 |
choices가 리스트인 이유 다시 정리
1
2
3
4
5
6
7
8
9
| # n 파라미터로 여러 개 응답 가능
response = client.chat.completions.create(
model = "gpt-4o-mini",
messages = [{"role": "user", "content": "안녕"}],
n = 3 # 3개 응답 요청
)
response.choices[0].message.content # 첫 번째 응답
response.choices[1].message.content # 두 번째 응답
response.choices[2].message.content # 세 번째 응답
|
10. 자주 나오는 실수 / 주의사항
실수 1: API 키를 코드에 직접 작성
1
2
3
4
5
| # 절대 하지 말 것!
client = OpenAI(api_key="sk-proj-xxxxxxxxxxxx") # GitHub에 올리면 키 노출!
# 올바른 방법
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
|
실수 2: response.content 로 접근
1
2
3
4
5
6
7
8
9
| # 틀린 코드 (AttributeError 발생)
print(response.content)
# 올바른 코드
print(response.choices[0].message.content)
# LangChain AIMessage는 .content 가능
response = llm.invoke("질문")
print(response.content) # ← LangChain에서는 OK
|
실수 3: .env 파일 위치
1
2
3
4
5
6
7
| # load_dotenv()는 현재 작업 디렉토리의 .env를 찾음
# 노트북 파일과 같은 폴더에 .env가 있어야 함
my_project/
├── notebook.ipynb
├── .env ← 여기!
└── ...
|
실수 4: messages 형식 오류
1
2
3
4
5
| # 틀린 코드 (리스트 안에 딕셔너리가 아닌 문자열)
messages = ["user: 안녕하세요"] # TypeError!
# 올바른 코드
messages = [{"role": "user", "content": "안녕하세요"}]
|
[보충] OpenAI API 비용 구조
토큰 기반 과금
- OpenAI API는 토큰(token) 단위로 과금
- 입력(prompt) 토큰 + 출력(completion) 토큰 합산
- gpt-4o-mini: input $0.15 / 1M tokens, output $0.6 / 1M tokens (저렴)
- gpt-4o : input $2.5 / 1M tokens, output $10 / 1M tokens (고성능)
토큰 수 확인
1
2
3
| response.usage.prompt_tokens # 입력 토큰 수
response.usage.completion_tokens # 출력 토큰 수
response.usage.total_tokens # 전체 = 입력 + 출력
|
- ▶ 한글은 영어보다 토큰 소모가 많음 (약 1.5~2배)
- ▶ max_tokens 파라미터로 응답 길이를 제한하면 비용 절감 가능
끝