[AI] LLM 03


HuggingFace

LLM 03 - LangChain (Ollama 포함)

Created: August 27, 2026 3:14 PM Class: LLM

Ollama

1. Ollama란?

  • 로컬 환경에서 LLM을 실행할 수 있게 해주는 오픈소스 런타임/도구
  • 내부적으로 llama.cpp를 엔진으로 사용하며, GGUF 포맷 모델을 로드해 추론 수행
  • 모델 다운로드, 버전 관리, 실행, API 서빙을 CLI 명령어 및 REST API로 제공
  • OpenAI 호환 API(/v1/chat/completions)를 함께 제공하여 기존 OpenAI SDK 코드와 호환 가능
  • 기본 실행 시 로컬 포트(기본 11434)에서 서버 프로세스로 상시 대기

2. 설치

Windows

  1. https://ollama.com/download 에서 Windows 설치 파일 다운로드
  2. 설치 후 자동으로 백그라운드 서비스로 등록 (시스템 트레이 상주)
  3. PowerShell에서 설치 확인
ollama --version

macOS / Linux

# macOS: 앱 다운로드 또는 brew
brew install ollama

# Linux
curl -fsSL https://ollama.com/install.sh | sh

GPU 인식 확인 (Windows, RTX 4070 기준)

  • NVIDIA 드라이버가 설치되어 있으면 자동으로 CUDA 가속 사용
  • 확인:
ollama run llama3.2:3b
  • 실행 중 작업관리자에서 GPU 사용률 확인 가능

3. 모델 불러오기 (Pull / Run)

명령어설명
ollama pull <model>모델만 다운로드 (실행 X)
ollama run <model>모델 다운로드(없을 경우) 후 대화형 실행
ollama run <model> "프롬프트"단발성 실행, 결과 출력 후 종료
ollama create <name> -f ModelfileModelfile 기반 커스텀 모델 생성
# 예시
ollama pull exaone3.5:7.8b
ollama run llama3.2:3b "한국의 수도는?"
  • 모델 태그: 모델명:파라미터크기 형식 (예: llama3.2:3b, exaone3.5:7.8b)
  • 태그 생략 시 기본(latest) 버전 사용

4. 모델 리스트 / 정보 확인

명령어설명
ollama list로컬에 다운로드된 모델 목록
ollama ps현재 메모리에 로드되어 실행 중인 모델
ollama show <model>모델 파라미터, 템플릿, 시스템 프롬프트 등 상세 정보
ollama list
ollama show exaone3.5:7.8b

5. 모델 제거

명령어설명
ollama rm <model>로컬 저장소에서 모델 삭제
ollama stop <model>메모리에서 언로드 (파일은 유지)
ollama rm llama3.2:3b

6. 기타 사용법

REST API 호출

curl http://localhost:11434/api/generate -d '{
  "model": "llama3.2:3b",
  "prompt": "안녕하세요"
}'

OpenAI 호환 엔드포인트

curl http://localhost:11434/v1/chat/completions -d '{
  "model": "llama3.2:3b",
  "messages": [{"role": "user", "content": "안녕하세요"}]
}'

Python 라이브러리

import ollama

response = ollama.chat(
    model="exaone3.5:7.8b",
    messages=[{"role": "user", "content": "안녕하세요"}]
)
print(response["message"]["content"])

LangChain 연동 (langchain_ollama)

from langchain_ollama import ChatOllama

llm = ChatOllama(model="exaone3.5:7.8b")
llm.invoke("안녕하세요")

Modelfile로 커스텀 모델 만들기

FROM llama3.2:3b
SYSTEM "당신은 친절한 한국어 assistant입니다."
PARAMETER temperature 0.7
ollama create my-korean-bot -f Modelfile

주요 환경변수

변수설명
OLLAMA_MODELS모델 저장 경로 변경
OLLAMA_HOST서버 바인딩 주소/포트 변경
OLLAMA_NUM_PARALLEL동시 요청 처리 수

7. Ollama vs LM Studio 비교

항목OllamaLM Studio
인터페이스CLI 중심 (GUI는 부가 기능)GUI 중심 (데스크톱 앱)
대상 사용자개발자, API/자동화 연동 중심비개발자 포함, 직관적 사용 중심
API 제공REST API, OpenAI 호환 API 기본 제공OpenAI 호환 로컬 서버 지원
모델 포맷GGUF (llama.cpp 엔진)GGUF, MLX(Apple Silicon 최적화) 지원
모델 관리pull/rm 등 명령어로 관리, Modelfile로 커스터마이징GUI에서 모델 검색·다운로드·전환
자동화/스크립팅용이 (CLI, Python 라이브러리, LangChain 연동)상대적으로 제한적
백그라운드 서비스상시 상주 서버 방식앱 실행 시에만 서버 동작(옵션)
학습 곡선명령어 학습 필요낮음 (GUI 클릭 기반)
확장 생태계LangChain, LangGraph 등 프레임워크 연동 활발로컬 실험/테스트 용도에 특화

정리: 자동화·에이전트 개발·API 연동이 목적이면 Ollama, 빠른 로컬 테스트·GUI 기반 탐색이 목적이면 LM Studio가 적합.


Langchain

1. 기본 활용

Ollama

from langchain_ollama import ChatOllama

# ChatOllama 모델 초기화
llm = ChatOllama(
    model="exaone3.5:7.8b",
    base_url="http://localhost:11434",
    temperature=0.1,
)

# system/human 메시지 구성
msg = [
    ("system", "당신은 매우 숙련된 쉐프입니다."),
    ("human", "오징어 볶음 레시피를 알려줘, 10인분을 할건데 각 재료의 양도 알려줘.")
]

# 동기 호출
res = llm.invoke(msg)
print(res.model_dump_json(indent=2))

print("="*30)

# 스트리밍 호출 (텍스트만 순차 출력)
for chunk in llm.stream(msg):
    print(chunk.text, end="")
  • base_url: Ollama 서버 주소 (로컬이면 생략 가능)
  • temperature: 낮을수록 일관된 답변, 높을수록 다양한 답변
  • msg: system(역할 지정) + human(질문) 메시지 쌍
  • invoke(): 전체 응답 완성 후 한 번에 반환, AIMessage 객체 반환
  • stream(): 토큰 단위로 순차 반환, 실시간 출력에 적합. chunk.text로 텍스트만 추출

OpenAI

from langchain_openai import ChatOpenAI

model = ChatOpenAI(
    model="gpt-5.6-luna",
    temperature=0,
)

msg = [
    ("system", "당신은 매우 숙련된 쉐프입니다."),
    ("human", "오징어 볶음 레시피를 알려줘, 10인분을 할건데 각 재료의 양도 알려줘.")
]

model.invoke(msg)

LMStudio

from langchain_openai import ChatOpenAI

model = ChatOpenAI(
    model="google/gemma-4-12bn-qat",
    base_url="http://localhost:1234/v1",
    temperature=0,
)

msg = [
    ("system", "당신은 매우 숙련된 쉐프입니다."),
    ("human", "오징어 볶음 레시피를 알려줘, 10인분을 할건데 각 재료의 양도 알려줘.")
]

model.invoke(msg)

2. 간단한 chain

Ollama

from langchain_core.prompts import ChatPromptTemplate
from langchain_ollama import ChatOllama

# 프롬프트 템플릿 정의 ({menu} 변수 포함)
template = ChatPromptTemplate.from_template(
    "{menu}의 레시피를 알려줘, 10인분을 할건데 각 재료의 양도 알려줘"
)

# ChatOllama 모델 초기화
model = ChatOllama(
    model="exaone3.5:7.8b",
    base_url="http://localhost:11434",
    temperature=0.1,
)

# 체인 연결 (LCEL: template -> model)
chain = template | model

# 실행 (변수 값 전달)
chain.invoke({"menu": "짜장면"})
  • ChatPromptTemplate.from_template(): {변수명} 형태의 플레이스홀더가 있는 프롬프트 템플릿 생성
  • template | model: LCEL(| 파이프 연산자)로 템플릿과 모델을 연결한 체인. 왼쪽 출력이 오른쪽 입력으로 전달됨
  • chain.invoke({"menu": "짜장면"}): 템플릿의 {menu}에 값 대입 → 완성된 프롬프트가 모델로 전달 → 응답 반환
  • 기존 방식(메시지 리스트 직접 작성) 대비, 변수만 바꿔가며 재사용 가능한 구조

OpenAI

from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI

template = ChatPromptTemplate.from_template(
    "{menu}의 레시피를 알려줘, 10인분을 할건데 각 재료의 양도 알려줘"
)

model = ChatOpenAI(
    model="gpt-5.6-luna",
    temperature=0.1,
)

chain = template | model

chain.invoke({"menu":"짜장면"})

LMStudio

from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI

template = ChatPromptTemplate.from_template(
    "{menu}의 레시피를 알려줘, 10인분을 할건데 각 재료의 양도 알려줘"
)

model = ChatOpenAI(
    model="google/gemma-4-12b-qat",
    base_url="http://localhost:1234/v1",
    temperature=0.1,
)

chain = template | model

chain.invoke({"menu":"짜장면"})

3. 반복되는 명령처리

Ollama

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_ollama import ChatOllama

# 프롬프트 템플릿 정의
prompt = ChatPromptTemplate.from_template(
    "{menu}의 레시피를 알려줘, 10인분을 할건데 각 재료의 양도 알려줘"
)

# ChatOllama 모델 초기화
model = ChatOllama(
    model="exaone3.5:7.8b",
    base_url="http://localhost:11434",
    temperature=0.1,
)

# 출력 파서 (AIMessage -> 문자열만 추출)
output_parser = StrOutputParser()

# 체인 연결 (prompt -> model -> output_parser)
chain = prompt | model | output_parser

# 실행 (여러 메뉴 반복 호출)
print(chain.invoke({"menu": "짜장면"}))
print("="*80)
print(chain.invoke({"menu": "비빔밥"}))
print("="*80)
print(chain.invoke({"menu": "제육볶음"}))
  • StrOutputParser(): 체인의 최종 출력을 AIMessage 객체가 아닌 순수 문자열(content)로 변환
  • chain = prompt | model | output_parser: 3단계 LCEL 체인. 프롬프트 생성 → 모델 응답 → 문자열 추출
  • 출력 파서가 없으면 res.content로 직접 꺼내야 하지만, 파서를 붙이면 invoke() 결과 자체가 바로 문자열
  • 동일한 체인을 변수만 바꿔 여러 번 invoke() 호출 → 반복적인 요청 처리에 재사용 가능한 패턴

OpenAI

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI

prompt= ChatPromptTemplate.from_template("{menu}의 레시피를 알려줘, 10인분을 할건데 각 재료의 양도 알려줘")
model = ChatOpenAI(
    model="gpt-5.6-terra",
    temperature=0,
    max_completion_tokens=None,
    timeout=None,
    max_retries=2,
)

output_parser = StrOutputParser()

chain= prompt | model | output_parser

print(chain.invoke({"menu":"짜장면"}))
print("="*80)
print(chain.invoke({"menu":"비빔밥"}))
print("="*80)
print(chain.invoke({"menu":"제육볶음"}))

LMStudio

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI

prompt= ChatPromptTemplate.from_template("{menu}의 레시피를 알려줘, 10인분을 할건데 각 재료의 양도 알려줘")
model = ChatOpenAI(
    model="google/gemma-4-12b-qat",
	base_url="http://localhost:1234/v1",
    temperature=0,
)

output_parser = StrOutputParser()

chain= prompt | model | output_parser

print(chain.invoke({"menu":"짜장면"}))
print("="*80)
print(chain.invoke({"menu":"비빔밥"}))
print("="*80)
print(chain.invoke({"menu":"제육볶음"}))

4. 명령의 명령을 연결하기

Ollama

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_ollama import ChatOllama

# 첫 번째 프롬프트 (레시피 요청)
prompt = ChatPromptTemplate.from_template(
    "{menu}의 레시피를 알려줘, 10인분을 할건데 각 재료의 양도 알려줘"
)

# ChatOllama 모델 초기화
model = ChatOllama(
    model="exaone3.5:7.8b",
    base_url="http://localhost:11434",
    temperature=0.1,
)

output_parser = StrOutputParser()

# 첫 번째 체인 (menu -> 한글 레시피 텍스트)
chain = prompt | model | output_parser

# 두 번째 체인을 이어서 연결
# 두 번째 프롬프트 (번역 요청)
prompt2 = ChatPromptTemplate.from_template("다믕 문장을 영어로 번역해줘 #다음 : {kor_output}")

# chain의 출력을 prompt2의 {kor_output} 입력으로 매핑 후 연결
chain2 = {"kor_output": chain} | prompt2 | model | output_parser

# 실행 (menu 입력 -> 레시피 생성 -> 영어 번역까지 한 번에)
print(chain2.invoke({"menu": "짜장면"}))
print("="*80)
print(chain2.invoke({"menu": "비빔밥"}))
print("="*80)
print(chain2.invoke({"menu": "제육볶음"}))
  • {"kor_output": chain}: 딕셔너리 형태로 체인을 매핑 → 앞 체인(chain)의 출력 문자열을 kor_output 키에 담아 다음 프롬프트(prompt2)의 {kor_output} 변수로 전달
  • chain2 = {"kor_output": chain} | prompt2 | model | output_parser: 체인 안에 체인을 중첩시켜 다단계 파이프라인 구성 (레시피 생성 → 번역)
  • chain2.invoke({"menu": "짜장면"}) 하나의 입력값만으로 내부적으로 두 번의 모델 호출이 순차 실행됨
  • LCEL의 조합성(composability)을 보여주는 패턴: 체인 자체를 다른 체인의 구성 요소로 재사용 가능

OpenAI

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI

prompt = ChatPromptTemplate.from_template("{menu}의 레시피를 알려줘, 10인분을 할건데 각 재료의 양도 알려줘")
model = ChatOpenAI(
    model="gpt-5.6-terra",
    temperature=0,
    max_completion_tokens=None,
    timeout=None,
    max_retries=2,
)

output_parser = StrOutputParser()

chain = prompt | model | output_parser

prompt2 = ChatPromptTemplate.from_template("다믕 문장을 영어로 번역해줘 #다음 : {kor_output}")
chain2 = {"kor_output": chain} | prompt2 | model | output_parser

print(chain2.invoke({"menu":"짜장면"}))
print("="*80)
print(chain2.invoke({"menu":"비빔밥"}))
print("="*80)
print(chain2.invoke({"menu":"제육볶음"}))

LMStudio

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI

prompt = ChatPromptTemplate.from_template("{menu}의 레시피를 알려줘, 10인분을 할건데 각 재료의 양도 알려줘")
model = ChatOpenAI(
    model="google/gemma-4-12b-qat",
	base_url="http://localhost:1234/v1",
    temperature=0,
)

output_parser = StrOutputParser()

chain = prompt | model | output_parser

prompt2 = ChatPromptTemplate.from_template("다믕 문장을 영어로 번역해줘 #다음 : {kor_output}")
chain2 = {"kor_output": chain} | prompt2 | model | output_parser

print(chain2.invoke({"menu":"짜장면"}))
print("="*80)
print(chain2.invoke({"menu":"비빔밥"}))
print("="*80)
print(chain2.invoke({"menu":"제육볶음"}))

LLM을 섬세하게 파라미터 조정

OpenAI

from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser

# 프롬프트 템플릿: system/assistant/user 메시지로 구성
prompt = ChatPromptTemplate.from_messages([
    ("system", "너는 영국의 고든램지 요리사야"),
    ("assistant", "나는 마늘을 많이 넣어줘"),
    ("user", "{menu}의 레시피를 알려줘, 1인분을 할건데 각 재료의 양도 알려줘")
])

# ChatOpenAI 모델 초기화 (파라미터를 top-level로 직접 전달)
llm = ChatOpenAI(
    model="gpt-4o",
    temperature=0.5,
    frequency_penalty=0.5,  # 이미 등장한 단어의 재등장 확률 억제
    presence_penalty=0.5,   # 새로운 단어(주제) 도입 유도
)

output_parser = StrOutputParser()

# 체인 연결
chain = prompt | llm | output_parser

# 실행
print(chain.invoke({"menu": "알리오 올리오"}))
  • frequency_penalty / presence_penalty: 예전에는 model_kwargs={...}로 묶어서 전달했으나, 현재는 ChatOpenAI 생성자에 top-level 인자로 직접 전달하는 방식이 맞음
  • frequency_penalty=0.5: 반복 단어 사용을 줄이는 방향으로 페널티 부여 (값이 클수록 반복 억제 강함)
  • presence_penalty=0.5: 아직 등장하지 않은 새로운 단어/주제를 도입하도록 유도 (값이 클수록 다양한 주제 확장)
  • 두 파라미터 모두 2.0 ~ 2.0 범위, 기본값은 0
  • 해당 파라미터들은 OpenAI 계열 API 특화 기능이라 Ollama(ChatOllama)에서는 지원되지 않거나 무시될 수 있음

Ollama

from langchain_core.prompts import ChatPromptTemplate
from langchain_ollama import ChatOllama
from langchain_core.output_parsers import StrOutputParser

prompt= ChatPromptTemplate.from_messages([
    ("system","너는 영국의 고든램지 요리사야"),
    ("assistant","나는 마늘을 많이 넣어줘"),
    ("user","{menu}의 레시피를 알려줘, 1인분을 할건데 각 재료의 양도 알려줘")
])

llm = ChatOllama(
    model="exaone3.5:7.8b",
    base_url="http://localhost:11434",
    temperature=0.1,
    repeat_penalty=1.3, 
)

output_parser = StrOutputParser()

chain = prompt | llm | output_parser

print(chain.invoke({"menu":"알리오 올리오"}))

LMStudio

from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser

prompt= ChatPromptTemplate.from_messages([
    ("system","너는 영국의 고든램지 요리사야"),
    ("assistant","나는 마늘을 많이 넣어줘"),
    ("user","{menu}의 레시피를 알려줘, 1인분을 할건데 각 재료의 양도 알려줘")
])

llm = ChatOpenAI(
    model="google/gemma-4-12b-qat",
    base_url="http://localhost:1234/v1",
    temperature=0.5,
    frequency_penalty=0.5, 
	presence_penalty=0.5, 
)

output_parser = StrOutputParser()

chain = prompt | llm | output_parser

print(chain.invoke({"menu":"알리오 올리오"}))






© 2017. by isme2n

Powered by aiden