포스트

2026-03-10 강의 정리

OT + 아이스브레이킹 + [프로젝트1] 안내

2026-03-10 강의 정리

목차

  1. 오늘 강의 개요
  2. 환경 설정 & 라이브러리 import
  3. OpenAI Chat Completions API 기본 사용법
  4. Response 객체 구조 상세 해부
  5. 역할(Role) 구분 - system / user / assistant
  6. Persona 설정 - system 메시지 활용
  7. temperature - 창의성 조절 파라미터
  8. LangChain ChatOpenAI 소개
  9. 핵심 개념 총정리
  10. 자주 나오는 실수 / 주의사항

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설명언제 쓰나?
systemAI의 성격, 규칙, 제약을 사전 정의대화 시작 시 1번
user사용자의 입력 메시지매 질문마다
assistantAI의 응답 (대화 기록용)멀티턴 대화 구성 시

실제 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.completionsOpenAI LLM API 호출 메서드
Response 구조choices[0].message.content 로 텍스트 추출
role: systemAI의 성격/역할을 사전 정의
role: user사용자 입력
role: assistantAI 응답 (멀티턴 대화에서 히스토리로 사용)
temperature낮을수록 일관성, 높을수록 창의성
LangChainllm.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 파라미터로 응답 길이를 제한하면 비용 절감 가능

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

인기 태그