Ollama Python 사용법

개요

Ollama는 로컬에서 대규모 언어 모델(LLM)을 실행할 수 있는 도구이다. Python의 requests 라이브러리를 사용하여 Ollama API를 호출할 수 있다.

사전 준비

1. Ollama 설치

# Ollama 공식 사이트에서 설치: https://ollama.ai
# 모델 다운로드
ollama pull llama3.2:latest

2. Python 라이브러리 설치

pip install requests

기본 사용법

API 엔드포인트

Ollama는 기본적으로 http://localhost:11434에서 실행된다.

import requests
import json

OLLAMA_URL = "http://localhost:11434/api/chat"
MODEL = "llama3.2:latest"

def call_ollama(user_input: str) -> str:
    payload = {
        "model": MODEL,
        "messages": [
            {"role": "user", "content": user_input}
        ],
        "stream": False
    }

    response = requests.post(OLLAMA_URL, json=payload, timeout=30)
    response.raise_for_status()

    return response.json()["message"]["content"]

# 사용 예시
result = call_ollama("안녕하세요")
print(result)

Payload 옵션

messages와 role 옵션

messages는 대화를 구성하는 메시지 배열이며, 각 메시지는 rolecontent로 구성된다.

role의 종류

Ollama API에서 사용할 수 있는 role은 세 가지가 있다:

  1. system: 시스템 프롬프트 - LLM의 역할과 동작 방식을 정의
  2. user: 사용자 입력 - 사용자가 입력한 메시지
  3. assistant: LLM 응답 - 모델이 생성한 응답

role: "system" - 시스템 프롬프트

시스템 프롬프트는 LLM의 역할, 성격, 응답 방식을 정의한다. 대화의 맥락을 설정하고 모델의 행동을 제어한다.

사용 방법

messages = [
    {
        "role": "system",
        "content": "당신은 친절한 AI 어시스턴트입니다. 항상 존댓말을 사용하고 도움이 되는 답변을 제공하세요."
    }
]

예시 1: 역할 정의

SYSTEM_PROMPT = """당신은 프로그래밍 전문가입니다.
코드 예시를 제공하고, 버그를 찾아주며, 최적화 방법을 제안합니다.
답변은 항상 한국어로 작성하세요."""

payload = {
    "model": MODEL,
    "messages": [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": "Python에서 리스트를 정렬하는 방법을 알려주세요."}
    ],
    "stream": False
}

예시 2: 응답 형식 지정

SYSTEM_PROMPT = """사용자의 질문을 분석하여 JSON 형식으로 응답하세요.
형식: {"category": "카테고리", "answer": "답변"}

카테고리는 다음 중 하나: "기술", "일반", "기타"
"""

payload = {
    "model": MODEL,
    "messages": [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": "Python이란 무엇인가요?"}
    ],
    "stream": False,
    "format": "json"
}

예시 3: 성격 설정

SYSTEM_PROMPT = """당신은 75세의 불교 스님입니다.
인자하고 차분한 존댓말을 사용하며, 상대의 마음을 존중하며 공감하는 표현을 사용합니다.
판단하거나 단정하지 않고, 부드럽게 질문으로 이끌어갑니다."""

payload = {
    "model": MODEL,
    "messages": [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": "요즘 스트레스를 많이 받고 있어요."}
    ],
    "stream": False
}

role: "user" - 사용자 입력

사용자가 입력한 메시지를 나타낸다. LLM이 응답해야 할 질문이나 요청을 포함한다.

사용 방법

messages = [
    {"role": "user", "content": "오늘 날씨가 어때요?"}
]

예시 1: 단순 질문

payload = {
    "model": MODEL,
    "messages": [
        {"role": "user", "content": "파이썬에서 딕셔너리를 만드는 방법은?"}
    ],
    "stream": False
}

예시 2: 복잡한 요청

payload = {
    "model": MODEL,
    "messages": [
        {
            "role": "system",
            "content": "당신은 코드 리뷰 전문가입니다."
        },
        {
            "role": "user",
            "content": """다음 코드를 리뷰해주세요:
def calculate_sum(numbers):
    total = 0
    for num in numbers:
        total += num
    return total"""
        }
    ],
    "stream": False
}

role: "assistant" - LLM 응답

모델이 생성한 응답을 나타낸다. 대화 기록을 유지하거나 Few-shot Learning을 구현할 때 사용한다.

사용 방법

messages = [
    {"role": "assistant", "content": "안녕하세요! 무엇을 도와드릴까요?"}
]

예시 1: 대화 기록 유지

conversation_history = []

def chat_with_history(user_input: str) -> str:
    messages = [
        {"role": "system", "content": "당신은 친절한 AI 어시스턴트입니다."}
    ]

    # 이전 대화 기록 추가
    messages.extend(conversation_history)

    # 현재 사용자 입력 추가
    messages.append({"role": "user", "content": user_input})

    payload = {
        "model": MODEL,
        "messages": messages,
        "stream": False
    }

    response = requests.post(OLLAMA_URL, json=payload, timeout=30)
    result = response.json()["message"]["content"]

    # 대화 기록 업데이트
    conversation_history.append({"role": "user", "content": user_input})
    conversation_history.append({"role": "assistant", "content": result})

    return result

# 사용 예시
print(chat_with_history("내 이름은 철수야"))
print(chat_with_history("내 이름이 뭐였지?"))  # 이전 대화를 기억함

예시 2: Few-shot Learning

Few-shot Learning은 몇 가지 예시를 제공하여 모델이 원하는 패턴을 학습하도록 하는 기법이다.

messages = [
    {
        "role": "system",
        "content": "사용자의 문장을 감정으로 분류하세요."
    },
    {
        "role": "user",
        "content": "오늘 정말 기분이 좋아요!"
    },
    {
        "role": "assistant",
        "content": "긍정"
    },
    {
        "role": "user",
        "content": "비가 와서 우울해요."
    },
    {
        "role": "assistant",
        "content": "부정"
    },
    {
        "role": "user",
               "content": "이 영화는 최고였어요!"  # 이 문장의 감정을 분류
    }
]

payload = {
    "model": MODEL,
    "messages": messages,
    "stream": False
}

예시 3: 대화 맥락 유지

def multi_turn_conversation():
    messages = [
        {"role": "system", "content": "당신은 도서관 사서입니다."}
    ]

    # 첫 번째 대화
    messages.append({"role": "user", "content": "추천 도서를 알려주세요"})
    response1 = requests.post(OLLAMA_URL, json={"model": MODEL, "messages": messages}, timeout=30)
    assistant_reply1 = response1.json()["message"]["content"]
    messages.append({"role": "assistant", "content": assistant_reply1})

    # 두 번째 대화 (이전 맥락 유지)
    messages.append({"role": "user", "content": "그 중에서도 과학 소설을 좋아해요"})
    response2 = requests.post(OLLAMA_URL, json={"model": MODEL, "messages": messages}, timeout=30)
    assistant_reply2 = response2.json()["message"]["content"]

    return assistant_reply2

완전한 예시

대화형 챗봇 구현

import requests
import json

OLLAMA_URL = "http://localhost:11434/api/chat"
MODEL = "llama3.2:latest"

SYSTEM_PROMPT = """당신은 친절한 AI 어시스턴트입니다.
항상 존댓말을 사용하고, 도움이 되는 답변을 제공하세요."""

def chat_bot():
    conversation_history = []

    print("챗봇을 시작합니다. 'quit'를 입력하면 종료됩니다.\n")

    while True:
        user_input = input("사용자> ").strip()

        if user_input.lower() in ("quit", "exit", "종료"):
            print("챗봇을 종료합니다.")
            break

        # 메시지 구성
        messages = [
            {"role": "system", "content": SYSTEM_PROMPT}
        ]

        # 대화 기록 추가
        messages.extend(conversation_history)

        # 현재 사용자 입력 추가
        messages.append({"role": "user", "content": user_input})

        # API 호출
        payload = {
            "model": MODEL,
            "messages": messages,
            "stream": False
        }

        try:
            response = requests.post(OLLAMA_URL, json=payload, timeout=30)
            response.raise_for_status()

            assistant_reply = response.json()["message"]["content"]

            # 대화 기록 업데이트
            conversation_history.append({"role": "user", "content": user_input})
            conversation_history.append({"role": "assistant", "content": assistant_reply})

            # 최근 20개 대화만 유지
            if len(conversation_history) > 20:
                conversation_history = conversation_history[-20:]

            print(f"봇> {assistant_reply}\n")

        except requests.exceptions.RequestException as e:
            print(f"오류 발생: {e}\n")

if __name__ == "__main__":
    chat_bot()

기타 Payload 옵션

stream 옵션

payload = {
    "model": MODEL,
    "messages": messages,
    "stream": False  # False: 전체 응답을 한 번에 받음, True: 실시간 스트리밍
}

format 옵션

payload = {
    "model": MODEL,
    "messages": messages,
    "format": "json"  # JSON 형식으로 응답 받기
}

options 옵션

payload = {
    "model": MODEL,
    "messages": messages,
    "options": {
        "temperature": 0.7,  # 창의성 조절 (0.0~1.0)
        "top_p": 0.9,       # 토큰 선택 범위
        "top_k": 40         # 상위 K개 토큰만 고려
    }
}

temperature

  • 용도: 응답의 창의성과 무작위성을 조절
  • 범위: 0.0 ~ 1.0 (또는 그 이상)
  • 낮은 값 (0.0~0.3): 일관되고 결정적인 답변, 사실 기반 응답에 적합
  • 높은 값 (0.7~1.0): 창의적이고 다양한 답변, 창작이나 아이디어 생성에 적합
  • 예시:
    • temperature: 0.0: 정확한 정보 요청 (날짜, 사실 등)
    • temperature: 0.7: 일반적인 대화
    • temperature: 1.0: 창의적 글쓰기, 시나리오 작성

top_p (Nucleus Sampling)

  • 용도: 확률 분포에서 상위 토큰들을 선택하는 범위를 제한
  • 범위: 0.0 ~ 1.0
  • 작동 방식: 누적 확률이 top_p 값에 도달할 때까지의 토큰만 고려
  • 낮은 값 (0.1~0.5): 더 집중적이고 일관된 답변, 상위 확률 토큰만 선택
  • 높은 값 (0.9~1.0): 더 다양한 답변, 넓은 범위의 토큰 고려
  • 장점:
    • 낮은 확률의 이상한 토큰을 제외하여 품질 향상
    • temperature와 함께 사용하면 더 나은 제어 가능
  • 예시:
    • top_p: 0.1: 매우 집중적인 답변 (기술 문서, 코드 등)
    • top_p: 0.9: 균형잡힌 답변 (일반 대화)
    • top_p: 1.0: 모든 토큰 고려 (최대 다양성)

top_k

  • 용도: 다음 토큰 선택 시 고려할 상위 K개 토큰만 제한
  • 범위: 1 이상의 정수
  • 작동 방식: 확률이 높은 상위 K개 토큰만 후보로 고려
  • 낮은 값 (1~10): 매우 집중적이고 예측 가능한 답변
  • 높은 값 (40~100): 더 다양한 답변 가능
  • 장점:
    • 계산 비용 절감 (고려할 토큰 수 제한)
    • 낮은 확률의 이상한 토큰 제외
  • 예시:
    • top_k: 1: 항상 가장 확률 높은 토큰만 선택 (매우 결정적)
    • top_k: 40: 상위 40개 토큰 중에서 선택 (균형잡힌 다양성)
    • top_k: 100: 상위 100개 토큰 고려 (높은 다양성)

옵션 조합 예시

# 정확한 정보 요청 (날짜, 사실 등)
options = {
    "temperature": 0.0,
    "top_p": 0.1,
    "top_k": 10
}

# 일반적인 대화
options = {
    "temperature": 0.7,
    "top_p": 0.9,
    "top_k": 40
}

# 창의적 글쓰기
options = {
    "temperature": 1.0,
    "top_p": 0.95,
    "top_k": 100
}

# 코드 생성 (일관성 중시)
options = {
    "temperature": 0.2,
    "top_p": 0.5,
    "top_k": 20
}

top_p vs top_k

  • top_p: 확률 기반 선택 (누적 확률 기준)
  • top_k: 개수 기반 선택 (상위 K개만 고려)
  • 함께 사용: 두 옵션을 함께 사용하면 더 정밀한 제어 가능
    • 예: top_k: 40top_p: 0.9를 함께 사용하면 상위 40개 중에서 누적 확률 0.9에 해당하는 토큰만 선택

주의사항

  1. role 순서: 일반적으로 systemuserassistantuser → ... 순서로 구성
  2. 대화 기록 관리: 대화가 길어지면 메모리 사용량이 증가하므로 최근 N개만 유지
  3. 에러 처리: 네트워크 오류나 타임아웃에 대한 예외 처리 필요
  4. 시스템 프롬프트: 명확하고 구체적인 시스템 프롬프트가 더 나은 결과를 만든다

참고 자료