Skip to content
바람부는 자유
Go back
LLM Engineering

LLM API 중급 (Part 2/3)

LLM Engineering (5/21)

  1. LLM 토큰 기본 개념
  2. LLM Tokenizer 추가 학습
  3. LLM Inference 이해하기: 토큰 예측의 마법
  4. LLM API 기초 (Part 1/3)
  5. LLM API 중급 (Part 2/3)
  6. LLM API 고급 (Part 3/3)
  7. System Message 활용하기
  8. LLM을 활용한 회의록 자동 요약 시스템
  9. Multi-Modal AI 기초
  10. Gradio 기본 사용법
  11. Hugging Face 완전 정복: AI 모델의 GitHub
  12. Google Colab 사용해보기: 무료로 GPU 환경에서 AI 모델 실행하기
  13. Tool Use (Function Calling)
  14. LLM 벤치마크 완전 가이드: 모델 성능 평가의 모든 것
  15. Vector Embeddings와 RAG 기초
  16. LangChain vs LiteLLM 비교 가이드
  17. 고급 RAG: 벡터 데이터베이스를 활용한 문서 검색 시스템
  18. RAG 기반 고객 상담 챗봇 만들기
  19. RAG 시스템 평가 (RAG Evaluation)
  20. 고급 RAG 기법 (Advanced RAG Techniques)
  21. 08-1. 데이터셋 개념 정리

이 노트북은 LLM API 시리즈의 두 번째 파트로, 실무에서 필요한 다양한 기법들을 다룹니다.

학습 목표

목표설명
다양한 활용코드 생성, 감정 분석 등 고급 활용
API 파라미터temperature, max_tokens 등 제어
스트리밍실시간 응답 출력 구현
에러 처리안정적인 API 호출 패턴
다중 LLMOpenAI, Claude, Ollama 비교
비용 관리토큰 사용량 기반 비용 계산

시리즈 구성

사전 요구사항

import os
import time
from dotenv import load_dotenv
from openai import OpenAI, APIError, RateLimitError, AuthenticationError
from IPython.display import Markdown, display, update_display

load_dotenv(override=True)

# 클라이언트 초기화
client = OpenAI()

1. 다양한 활용 예시

Part 1에서 다룬 요약, 번역, Q&A 외에 더 다양한 활용 사례를 살펴봅니다.

# 공통 헬퍼 함수
def ask_llm(system_prompt: str, user_message: str, model: str = "gpt-4o-mini") -> str:
    """시스템 프롬프트와 사용자 메시지로 LLM을 호출합니다."""
    response = client.chat.completions.create(
        model=model,
        messages=[
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": user_message}
        ]
    )
    return response.choices[0].message.content

1.1 코드 생성

프로그래밍 언어와 스타일을 지정하여 코드를 생성합니다.

# 코드 생성
code_prompt = """당신은 Python 전문 프로그래머입니다.
요청된 기능을 구현하는 깔끔하고 효율적인 Python 코드를 작성해주세요.
코드에 간단한 docstring을 포함해주세요."""

code_request = "두 개의 리스트를 받아서 공통 요소만 반환하는 함수를 작성해주세요."

code = ask_llm(code_prompt, code_request)
print("=== 코드 생성 결과 ===")
display(Markdown(code))
=== 코드 생성 결과 ===
<IPython.core.display.Markdown object>

1.2 감정 분석 (텍스트 분류)

텍스트를 정해진 카테고리로 분류합니다. 출력 형식을 명확히 지정하면 후처리가 쉬워집니다.

# 감정 분석
sentiment_prompt = """당신은 감정 분석 전문가입니다.
주어진 텍스트의 감정을 분석하고 다음 형식으로만 응답하세요:
- 감정: [긍정/부정/중립]
- 신뢰도: [높음/중간/낮음]
- 근거: [1문장 설명]"""

reviews = [
    "이 제품 정말 최고예요! 배송도 빠르고 품질도 훌륭합니다.",
    "그냥 그래요. 나쁘진 않은데 특별히 좋지도 않네요.",
    "최악이에요. 돈 아깝습니다."
]

print("=== 감정 분석 결과 ===\n")
for review in reviews:
    result = ask_llm(sentiment_prompt, review)
    print(f'리뷰: "{review}"')
    print(result)
    print("-" * 50)
=== 감정 분석 결과 ===

리뷰: "이 제품 정말 최고예요! 배송도 빠르고 품질도 훌륭합니다."
- 감정: 긍정
- 신뢰도: 높음
- 근거: 제품에 대한 높은 만족감과 긍정적인 경험이 명확하게 표현되고 있습니다.
--------------------------------------------------
리뷰: "그냥 그래요. 나쁘진 않은데 특별히 좋지도 않네요."
- 감정: 중립
- 신뢰도: 높음
- 근거: 긍정과 부정이 혼재되어 있지 않고, 평범함을 나타내는 표현이 사용되었다.
--------------------------------------------------
리뷰: "최악이에요. 돈 아깝습니다."
- 감정: 부정
- 신뢰도: 높음
- 근거: "최악이에요"와 "돈 아깝습니다"는 명백한 부정적인 감정을 표현하고 있습니다.
--------------------------------------------------

2. API 파라미터

Chat Completion API에서 자주 사용되는 파라미터들을 알아봅니다.

파라미터설명기본값범위
model사용할 모델필수-
messages대화 메시지 배열필수-
temperature창의성 조절1.00.0~2.0
max_tokens최대 출력 토큰 수모델별 상이-
top_p누적 확률 기반 샘플링1.00.0~1.0
stream스트리밍 응답FalseTrue/False

2.1 temperature 파라미터

# temperature 비교
def compare_temperature(prompt: str):
    """temperature 값에 따른 응답 차이를 비교합니다."""
    for temp in [0.0, 1.0, 1.8]:
        response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[{"role": "user", "content": prompt}],
            temperature=temp,
            max_tokens=50
        )
        print(f"temperature={temp}: {response.choices[0].message.content}\n")

# 테스트
print("=== temperature 비교 ===")
compare_temperature("랜덤한 색상 하나를 추천해주세요.")
=== temperature 비교 ===
temperature=0.0: 랜덤한 색상으로 "코발트 블루"를 추천합니다! 이 색상은 깊고 선명한 파란색으로, 시원하고 활기찬 느낌을 줍니다. 다양한 디자인에 잘 어울리

temperature=1.0: 랜덤한 색상으로 "청록색"을 추천합니다. 청록색은 청색과 녹색이 조화를 이루는 색상으로, 시원하고 자연적인 느낌을 줍니다. 디자인이나 인테리

temperature=1.8: 쇼킹 핑크(pure pink)를 추천드립니다! 신나는 에너지를 주는 색상으로, 다양한 요소와 잘 어울리며 튀는 느낌을 줄 수 있습니다. 또한, 시각적으로도 Legendary(백발

2.2 max_tokens 파라미터

출력의 최대 길이를 제한합니다. 비용 관리와 응답 시간 단축에 유용합니다.

# max_tokens 비교
for max_tok in [20, 50, 100]:
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": "파이썬의 장점을 설명해주세요."}],
        max_tokens=max_tok
    )
    content = response.choices[0].message.content
    print(f"max_tokens={max_tok}: {content[:80]}...")
    print(f"  (실제 출력: {response.usage.completion_tokens} 토큰)\n")
max_tokens=20: 파이썬은 여러 가지 장점을 가진 널리 사용되는 프로그래밍 언어...
  (실제 출력: 20 토큰)

max_tokens=50: 파이썬은 다양한 이유로 인기가 높은 프로그래밍 언어입니다. 아래는 파이썬의 주요 장점들입니다:

1. **문법이 간단하고 가독성이 높음**: 파...
  (실제 출력: 50 토큰)

max_tokens=100: 파이썬은 다양한 장점 덕분에 많은 개발자와 기업에서 널리 사용되고 있는 프로그래밍 언어입니다. 다음은 파이썬의 주요 장점입니다:

1. **쉬운...
  (실제 출력: 100 토큰)

3. 스트리밍 응답

긴 응답의 경우 스트리밍을 사용하면 토큰이 생성되는 대로 실시간으로 출력받을 수 있습니다.

방식특징사용 사례
일반전체 응답 완료 후 수신짧은 응답, 후처리 필요 시
스트리밍토큰 단위로 실시간 수신긴 응답, 챗봇 UI
def stream_response(user_message: str, system_prompt: str = "You are helpful."):
    """스트리밍 방식으로 응답을 받아 실시간 출력합니다."""
    stream = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": user_message}
        ],
        stream=True
    )
    
    full_response = ""
    display_handle = display(Markdown(""), display_id=True)
    
    for chunk in stream:
        delta = chunk.choices[0].delta.content or ""
        full_response += delta
        update_display(Markdown(full_response), display_id=display_handle.display_id)
    
    return full_response
# 스트리밍 테스트
print("=== 스트리밍 응답 ===")
_ = stream_response("파이썬의 장점을 3가지만 간단히 설명해주세요.")
=== 스트리밍 응답 ===
<IPython.core.display.Markdown object>

4. 에러 처리

API 호출 시 다양한 오류가 발생할 수 있습니다.

에러원인해결책
AuthenticationError잘못된 API 키API 키 확인
RateLimitError요청 한도 초과대기 후 재시도
APIError서버 오류재시도
APIConnectionError네트워크 오류연결 확인
def call_with_retry(messages: list, max_retries: int = 3):
    """에러 처리와 재시도 로직이 포함된 API 호출 함수"""
    for attempt in range(max_retries):
        try:
            response = client.chat.completions.create(
                model="gpt-4o-mini",
                messages=messages
            )
            return response.choices[0].message.content
        
        except AuthenticationError as e:
            print(f"인증 오류: API 키를 확인하세요.")
            raise  # 재시도 불필요
        
        except RateLimitError as e:
            wait_time = 2 ** attempt  # 지수 백오프: 1, 2, 4초
            print(f"요청 한도 초과. {wait_time}초 후 재시도... ({attempt + 1}/{max_retries})")
            time.sleep(wait_time)
        
        except APIError as e:
            print(f"API 오류: {e}")
            if attempt < max_retries - 1:
                time.sleep(1)
            else:
                raise
    
    raise Exception("최대 재시도 횟수 초과")

# 테스트
result = call_with_retry([{"role": "user", "content": "Hello!"}])
print(f"응답: {result}")

5. 다중 LLM 활용

여러 LLM 제공업체의 API를 비교해봅니다.

5.1 OpenAI vs Anthropic 비교

항목OpenAIAnthropic
라이브러리openaianthropic
메서드chat.completions.create()messages.create()
system 전달messages 배열에 포함별도 system 파라미터
응답 접근response.choices[0].message.contentresponse.content[0].text
import anthropic

# Anthropic 클라이언트 초기화
claude_client = anthropic.Anthropic()

def call_claude(system_prompt: str, user_message: str, model: str = "claude-sonnet-4-20250514"):
    """Anthropic Claude API를 호출합니다."""
    response = claude_client.messages.create(
        model=model,
        max_tokens=1024,
        system=system_prompt,  # system은 별도 파라미터
        messages=[{"role": "user", "content": user_message}]
    )
    return response

# Claude 호출 테스트
claude_response = call_claude(
    system_prompt="You are helpful. Respond in Korean.",
    user_message="파이썬의 장점을 2가지만 알려주세요."
)

print("=== Claude 응답 ===")
print(f"Content: {claude_response.content[0].text}")
print(f"\n--- 메타데이터 ---")
print(f"Model: {claude_response.model}")
print(f"Input: {claude_response.usage.input_tokens}, Output: {claude_response.usage.output_tokens} tokens")
---------------------------------------------------------------------------
ConnectError                              Traceback (most recent call last)
File ~/workspace/ws.study/ai-engineering/.venv/lib/python3.13/site-packages/httpx/_transports/default.py:101, in map_httpcore_exceptions()
    100 try:
--> 101     yield
    102 except Exception as exc:

File ~/workspace/ws.study/ai-engineering/.venv/lib/python3.13/site-packages/httpx/_transports/default.py:250, in HTTPTransport.handle_request(self, request)
    249 with map_httpcore_exceptions():
--> 250     resp = self._pool.handle_request(req)
    252 assert isinstance(resp.stream, typing.Iterable)

File ~/workspace/ws.study/ai-engineering/.venv/lib/python3.13/site-packages/httpcore/_sync/connection_pool.py:256, in ConnectionPool.handle_request(self, request)
    255     self._close_connections(closing)
--> 256     raise exc from None
    258 # Return the response. Note that in this case we still have to manage
    259 # the point at which the response is closed.

File ~/workspace/ws.study/ai-engineering/.venv/lib/python3.13/site-packages/httpcore/_sync/connection_pool.py:236, in ConnectionPool.handle_request(self, request)
    234 try:
    235     # Send the request on the assigned connection.
--> 236     response = connection.handle_request(
    237         pool_request.request
    238     )
    239 except ConnectionNotAvailable:
    240     # In some cases a connection may initially be available to
    241     # handle a request, but then become unavailable.
    242     #
    243     # In this case we clear the connection and try again.

File ~/workspace/ws.study/ai-engineering/.venv/lib/python3.13/site-packages/httpcore/_sync/connection.py:101, in HTTPConnection.handle_request(self, request)
    100     self._connect_failed = True
--> 101     raise exc
    103 return self._connection.handle_request(request)

File ~/workspace/ws.study/ai-engineering/.venv/lib/python3.13/site-packages/httpcore/_sync/connection.py:78, in HTTPConnection.handle_request(self, request)
     77 if self._connection is None:
---> 78     stream = self._connect(request)
     80     ssl_object = stream.get_extra_info("ssl_object")

File ~/workspace/ws.study/ai-engineering/.venv/lib/python3.13/site-packages/httpcore/_sync/connection.py:156, in HTTPConnection._connect(self, request)
    155 with Trace("start_tls", logger, request, kwargs) as trace:
--> 156     stream = stream.start_tls(**kwargs)
    157     trace.return_value = stream

File ~/workspace/ws.study/ai-engineering/.venv/lib/python3.13/site-packages/httpcore/_backends/sync.py:154, in SyncStream.start_tls(self, ssl_context, server_hostname, timeout)
    150 exc_map: ExceptionMapping = {
    151     socket.timeout: ConnectTimeout,
    152     OSError: ConnectError,
    153 }
... (출력 172줄 생략)

5.2 Ollama 로컬 LLM

Ollama를 사용하면 로컬에서 오픈소스 LLM을 실행할 수 있습니다.

장점설명
무료API 비용 없음
프라이버시데이터가 로컬에서만 처리
오프라인인터넷 연결 불필요
# 설치 후 모델 다운로드
ollama pull llama3.2
ollama pull exaone3.5
# Ollama 클라이언트 (OpenAI 호환 API)
ollama_client = OpenAI(
    base_url="http://localhost:11434/v1",
    api_key="ollama"  # Ollama는 인증 불필요
)

def call_ollama(user_message: str, model: str = "exaone3.5"):
    """Ollama 로컬 LLM을 호출합니다."""
    try:
        response = ollama_client.chat.completions.create(
            model=model,
            messages=[{"role": "user", "content": user_message}]
        )
        return response.choices[0].message.content
    except Exception as e:
        return f"오류: {e} (Ollama가 실행 중인지 확인하세요)"

# Ollama 테스트 (Ollama 서버가 실행 중이어야 함)
ollama_result = call_ollama("What is Python? Answer in one sentence.")
print(f"=== Ollama 응답 ===")
print(ollama_result)
=== Ollama 응답 ===
Python is a high-level programming language known for its readability and versatility, widely used for web development, data analysis, artificial intelligence, and more.

6. OpenAI 호환 인터페이스

OpenAI의 API 형식이 사실상 표준이 되어, 많은 제공업체가 호환 API를 제공합니다. base_url만 변경하면 동일한 코드로 다양한 LLM에 접근할 수 있습니다.

# 다양한 서비스 엔드포인트
ENDPOINTS = {
    "gemini": "https://generativelanguage.googleapis.com/v1beta/openai/",
    "groq": "https://api.groq.com/openai/v1",
    "ollama": "http://localhost:11434/v1",
}

# Gemini 클라이언트 예시
google_api_key = os.getenv("GOOGLE_API_KEY")
if google_api_key:
    gemini_client = OpenAI(
        api_key=google_api_key,
        base_url=ENDPOINTS["gemini"]
    )
    
    response = gemini_client.chat.completions.create(
        model="gemini-2.0-flash",
        messages=[{"role": "user", "content": "What is 2+2?"}]
    )
    print(f"Gemini 응답: {response.choices[0].message.content}")
else:
    print("GOOGLE_API_KEY가 설정되지 않았습니다.")
GOOGLE_API_KEY가 설정되지 않았습니다.

7. 비용 계산

API 사용 시 토큰 수에 따라 비용이 발생합니다.

주요 모델 가격 (2025년 기준, 1M 토큰당)

모델InputOutput특징
gpt-4o$2.50$10.00고성능
gpt-4o-mini$0.15$0.60가성비
claude-sonnet-4$3.00$15.00균형

가격은 변동될 수 있으니 공식 문서를 확인하세요.

# 비용 계산 함수
PRICING = {
    "gpt-4o": {"input": 2.50, "output": 10.00},
    "gpt-4o-mini": {"input": 0.15, "output": 0.60},
    "gpt-4.1": {"input": 2.00, "output": 8.00},
}

def calculate_cost(usage, model: str = "gpt-4o-mini") -> dict:
    """토큰 사용량을 기반으로 비용을 계산합니다."""
    if model not in PRICING:
        return {"error": f"Unknown model: {model}"}
    
    price = PRICING[model]
    input_cost = (usage.prompt_tokens / 1_000_000) * price["input"]
    output_cost = (usage.completion_tokens / 1_000_000) * price["output"]
    
    return {
        "input_tokens": usage.prompt_tokens,
        "output_tokens": usage.completion_tokens,
        "input_cost": input_cost,
        "output_cost": output_cost,
        "total_cost": input_cost + output_cost
    }
# 비용 계산 테스트
response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "system", "content": "You are helpful."},
        {"role": "user", "content": "파이썬의 장점을 5가지 알려주세요."}
    ]
)

cost = calculate_cost(response.usage, "gpt-4o-mini")
print("=== 비용 계산 결과 ===")
print(f"입력 토큰: {cost['input_tokens']:,}")
print(f"출력 토큰: {cost['output_tokens']:,}")
print(f"입력 비용: ${cost['input_cost']:.6f}")
print(f"출력 비용: ${cost['output_cost']:.6f}")
print(f"총 비용: ${cost['total_cost']:.6f}")

8. 요약

이번 노트북에서 학습한 내용:

주제핵심 내용
활용 예시코드 생성, 감정 분석 등 system prompt로 역할 지정
파라미터temperature(창의성), max_tokens(길이) 등 제어
스트리밍stream=True로 실시간 응답 수신
에러 처리try-except와 지수 백오프로 안정성 확보
다중 LLMOpenAI, Claude, Ollama 상황에 맞게 선택
호환 APIbase_url 변경으로 다양한 LLM 접근
비용 관리토큰 사용량 추적으로 비용 최적화

다음 단계

Part 3 (고급) 에서 다룰 내용:


Share this post on:

Previous Post
LLM API 고급 (Part 3/3)
Next Post
LLM API 기초 (Part 1/3)