본문으로 건너뛰기
AIDevOps
  • Learn
  • Learning Paths
  • Practice
  • Open Source
  • Books
  • Engineering

    AI DevOpsAI 서비스 개발·운영 전체 지도LLMOpsLLM 배포·평가·관측실전 프로젝트AI Agent 프로젝트 실습

    Knowledge

    Docs기술 문서 모음Blog엔지니어링 아티클Plogger개발 기록 피드

    Validate

    Certification3단계 역량 인증 · 준비 중
AI Models
LlamaMistralGemmaDeepSeekQwen
🧠 AI Core
AI 입문 & 로드맵ML FundamentalsLLM Fundamentals|Python AIC++|PyTorchTensorFlowJAX
🤖 AI 실전 개발
AI 실전 입문 & 로드맵Hugging FaceLangChainLlamaIndexLLMOps|LangGraphMCPMulti-AgentAgent Evaluation
🧠 AI Agent 개발
금융 AI AgentLLM API 서버주식 투자 AgentAIOps AI Agent교육 AI Agent코딩 AI Agent
🌱 Spring Cloud
Spring 입문 & 로드맵Spring Cloud GatewaySpring BootJava|Spring AISpring SecuritySpring BatchSpring JPA
🐳 DevOps
DevOps 입문 & 로드맵LinuxDockerCI/CD|Kubernetes 기본K8s 심화/실무PrometheusGrafana
🧱 인프라
인프라 입문 & 로드맵NginxRedis
☁️ 클라우드
클라우드 입문 & 로드맵AWSGCPAzureNCPCloudflare
🎨 Frontend
Frontend 입문 & 로드맵JavaScriptTypeScript|ReactNext.js|VueNuxt
📱 Mobile
Mobile 입문 & 로드맵KotlinAndroidFlutter
⚙️ Backend
Backend 입문 & 로드맵Python 기본FastAPIDjangoFlask|CGoGinNode.js
💾 Database
DB 입문 & 로드맵공통 SQLOracleMySQLPostgreSQL|MongoDB벡터 DB
🧪 검증
k6JMeternGrinder
AIDevOps

Engineering AI. From Code to Production.
AI와 AI Agent를 개발하고 운영하기 위한 엔지니어링 학습 플랫폼

Learn

  • 전체 가이드
  • Learning Paths
  • Practice
  • Books

Resources

  • AI DevOps
  • LLMOps
  • 실전 프로젝트
  • Docs
  • Blog
  • Plogger
  • Open Source
  • Certification (준비 중)

Start Here

  • AI Core 로드맵
  • AI 실전 개발 로드맵
  • Spring Cloud 로드맵
  • DevOps 로드맵
  • 인프라 로드맵

 

  • 클라우드 로드맵
  • Frontend 로드맵
  • Mobile 로드맵
  • Backend 로드맵
  • Database 로드맵
© 2026 AI DevOps Korea. All rights reserved.
이용약관개인정보처리방침Sitemaptestforge.kr
AI Agent 개발 오픈소스 LLM 가이드

🦙 Llama 완전 가이드

Visitors

Meta Llama 3.1은 Function Calling과 128K 컨텍스트를 지원하는 AI Agent 구축에 최적화된 오픈소스 LLM입니다. Ollama 로컬 실행부터 vLLM 프로덕션 서버, RAG 파이프라인, LoRA 파인튜닝까지 전 과정을 다룹니다.

← AI DevOps 홈으로
🦙

Llama 실전 예제

Function Calling Agent, RAG 파이프라인, vLLM 서버, LoRA 파인튜닝 예제 코드 모음.

실전 예제 보기 →

목차

0 / 8
  1. 모델 라인업 & 선택 기준
  2. Ollama 로컬 실행
  3. OpenAI 호환 API
  4. Function Calling Agent
  5. JSON 구조화 출력
  6. RAG 파이프라인
  7. vLLM 프로덕션 서버
  8. LoRA / QLoRA 파인튜닝
목차 8개 섹션
  1. 모델 라인업 & 선택 기준
  2. Ollama 로컬 실행
  3. OpenAI 호환 API
  4. Function Calling Agent
  5. JSON 구조화 출력
  6. RAG 파이프라인
  7. vLLM 프로덕션 서버
  8. LoRA / QLoRA 파인튜닝

모델 라인업 & 선택 기준

Llama 3.1은 Function Calling, JSON 모드, 128K 컨텍스트를 지원해 AI Agent 구축에 적합합니다. 용도와 VRAM에 따라 모델을 선택하세요.

모델파라미터특징최소 VRAM추천 용도
Llama 3.2 3B3B경량, 빠른 응답4GB간단한 분류·요약
Llama 3.1 8B8BFunction Calling 지원8GB단일 도구 Agent
Llama 3.1 70B70B고성능 추론48GB복잡한 멀티 Agent
Llama 3.1 405B405BGPT-4 수준800GB최고 품질 추론
💡

VRAM이 부족하면 Q4_K_M 양자화 GGUF 모델로 약 50% 메모리 절감. 8B Q4는 6GB VRAM으로 실행됩니다.

Ollama 로컬 실행

Ollama는 LLM을 가장 쉽게 로컬에서 실행하는 도구입니다. Docker처럼 모델을 pull해서 바로 실행합니다.

BASH
# 설치 (macOS/Linux)
curl -fsSL https://ollama.ai/install.sh | sh

# 모델 다운로드 및 실행
ollama pull llama3.1          # 8B 기본 (4.7GB)
ollama pull llama3.1:70b      # 70B (40GB)
ollama run llama3.1           # 대화형 REPL

# 백그라운드 서버로 실행
ollama serve                  # 기본 포트 11434

# 모델 관리
ollama list                   # 설치된 모델 목록
ollama ps                     # 현재 실행 중인 모델
ollama rm llama3.1            # 모델 삭제
ℹ️

Ollama는 macOS에서 Metal GPU, Linux에서 CUDA/ROCm을 자동 감지합니다. GPU 없이도 CPU로 실행 가능합니다.

OpenAI 호환 API

Ollama는 OpenAI API와 완전히 호환됩니다. 기존 OpenAI 코드에서 base_url만 변경하면 Llama로 전환됩니다.

llama_client.pyPYTHON
import asyncio
from openai import OpenAI, AsyncOpenAI

# ── 동기 클라이언트 ───────────────────────────────
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")

# 기본 호출
resp = client.chat.completions.create(
    model="llama3.1",
    messages=[
        {"role": "system", "content": "당신은 시니어 Python 개발자입니다."},
        {"role": "user",   "content": "asyncio.gather와 asyncio.wait의 차이를 설명해줘"},
    ],
    temperature=0.7,
    max_tokens=1024,
)
print(resp.choices[0].message.content)

# ── 스트리밍 ─────────────────────────────────────
async def stream_response(prompt: str):
    aclient = AsyncOpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
    async with aclient.chat.completions.stream(
        model="llama3.1",
        messages=[{"role": "user", "content": prompt}],
    ) as stream:
        async for chunk in stream:
            delta = chunk.choices[0].delta.content
            if delta:
                print(delta, end="", flush=True)

asyncio.run(stream_response("Python 비동기 패턴을 설명해줘"))

Function Calling Agent

Llama 3.1 8B 이상은 Function Calling을 지원합니다. 도구 스키마를 정의하면 LLM이 적절한 도구를 선택하고 인자를 생성합니다.

llama_agent.pyPYTHON
import json
from openai import OpenAI

client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")

# ── 도구 함수 정의 ────────────────────────────────
def get_weather(city: str, unit: str = "celsius") -> str:
    """도시 날씨 조회 (실제 구현 시 API 호출)"""
    return json.dumps({"city": city, "temp": 23, "unit": unit, "condition": "맑음"})

def search_docs(query: str, top_k: int = 3) -> str:
    """내부 문서 검색"""
    return json.dumps({"results": [f"문서: {query} 관련 내용 {i}" for i in range(top_k)]})

TOOLS = {
    "get_weather": get_weather,
    "search_docs": search_docs,
}

TOOL_SCHEMAS = [
    {"type": "function", "function": {
        "name": "get_weather",
        "description": "특정 도시의 현재 날씨를 조회합니다",
        "parameters": {"type": "object",
            "properties": {
                "city": {"type": "string", "description": "도시명 (예: 서울)"},
                "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
            }, "required": ["city"]},
    }},
    {"type": "function", "function": {
        "name": "search_docs",
        "description": "내부 지식베이스에서 관련 문서를 검색합니다",
        "parameters": {"type": "object",
            "properties": {
                "query": {"type": "string", "description": "검색 질의"},
                "top_k": {"type": "integer", "description": "반환할 결과 수", "default": 3},
            }, "required": ["query"]},
    }},
]

# ── ReAct 루프 ────────────────────────────────────
def run_agent(user_query: str) -> str:
    messages = [
        {"role": "system", "content": "당신은 도움이 되는 AI 어시스턴트입니다. 필요할 때 도구를 사용하세요."},
        {"role": "user", "content": user_query},
    ]

    for step in range(5):
        resp = client.chat.completions.create(
            model="llama3.1",
            messages=messages,
            tools=TOOL_SCHEMAS,
            tool_choice="auto",
        )
        msg = resp.choices[0].message
        messages.append(msg)

        if not msg.tool_calls:          # 최종 답변
            return msg.content

        for tc in msg.tool_calls:       # 도구 실행
            args   = json.loads(tc.function.arguments)
            result = TOOLS[tc.function.name](**args)
            print(f"[Tool] {tc.function.name}({args})")
            messages.append({
                "role": "tool",
                "tool_call_id": tc.id,
                "content": result,
            })
    return "최대 스텝 초과"

print(run_agent("서울 날씨를 알려주고, 우산이 필요한지 판단해줘"))

JSON 구조화 출력

Ollama의 format: "json" 옵션으로 LLM이 항상 유효한 JSON을 반환하도록 강제합니다. Pydantic과 조합하면 타입 안전한 구조화 출력을 보장할 수 있습니다.

structured_llama.pyPYTHON
import json
from openai import OpenAI
from pydantic import BaseModel, Field

client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")

class SentimentResult(BaseModel):
    sentiment: str     = Field(description="positive|negative|neutral")
    score:     float   = Field(description="0.0~1.0 신뢰도")
    reason:    str     = Field(description="판단 근거 한 문장")
    keywords:  list[str] = Field(description="핵심 키워드 목록")

def analyze_sentiment(text: str) -> SentimentResult:
    schema = SentimentResult.model_json_schema()
    resp = client.chat.completions.create(
        model="llama3.1",
        messages=[
            {"role": "system",
             "content": f"감정 분석 결과를 반드시 다음 JSON 스키마로 반환하세요:\n{json.dumps(schema, ensure_ascii=False)}"},
            {"role": "user", "content": f"분석할 텍스트: {text}"},
        ],
        response_format={"type": "json_object"},  # JSON 강제
    )
    raw = json.loads(resp.choices[0].message.content)
    return SentimentResult(**raw)   # Pydantic 검증

result = analyze_sentiment("이 제품은 정말 실망스럽네요. 배송도 늦고 품질도 별로예요.")
print(f"감정: {result.sentiment} (신뢰도: {result.score:.0%})")
print(f"근거: {result.reason}")
print(f"키워드: {', '.join(result.keywords)}")

RAG 파이프라인

Llama를 LLM으로, ChromaDB를 벡터 저장소로 사용해 완전히 로컬에서 실행되는 RAG 시스템을 구축합니다. 외부 API 의존 없이 기업 문서를 안전하게 검색합니다.

BASH
uv add chromadb sentence-transformers langchain langchain-community
local_rag.pyPYTHON
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.chat_models import ChatOllama
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain.text_splitter import RecursiveCharacterTextSplitter

# ── 임베딩 모델 (로컬 실행) ───────────────────────
embeddings = HuggingFaceEmbeddings(
    model_name="BAAI/bge-m3",          # 한국어 포함 다국어 지원
    model_kwargs={"device": "cuda"},
    encode_kwargs={"normalize_embeddings": True},
)

# ── 문서 인덱싱 ──────────────────────────────────
documents = [
    "AI DevOps Korea는 AI · Cloud · DevOps 학습 플랫폼입니다.",
    "k6는 JavaScript로 작성하는 현대적인 부하 테스트 도구입니다.",
    "JMeter는 Java 기반의 GUI 지원 부하 테스트 도구입니다.",
]
splitter = RecursiveCharacterTextSplitter(chunk_size=512, chunk_overlap=50)
docs     = splitter.create_documents(documents)
vectordb = Chroma.from_documents(docs, embeddings, persist_directory="./chroma_db")

# ── RAG 체인 (LCEL) ──────────────────────────────
llm = ChatOllama(model="llama3.1", temperature=0.1)

prompt = ChatPromptTemplate.from_template("""다음 컨텍스트를 바탕으로 질문에 답하세요.
컨텍스트에 없는 내용은 모른다고 하세요.

컨텍스트: {context}

질문: {question}""")

rag_chain = (
    {"context": vectordb.as_retriever(search_kwargs={"k": 3}), "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

answer = rag_chain.invoke("k6와 JMeter의 차이점은?")
print(answer)

vLLM 프로덕션 서버

vLLM은 PagedAttention으로 Ollama 대비 최대 24배 높은 처리량을 제공하는 프로덕션급 LLM 서버입니다. OpenAI API와 완전히 호환됩니다.

BASH
# vLLM 설치
pip install vllm

# 서버 실행 (단일 GPU)
python -m vllm.entrypoints.openai.api_server \
  --model meta-llama/Meta-Llama-3.1-8B-Instruct \
  --port 8000 \
  --max-model-len 32768 \
  --gpu-memory-utilization 0.9

# 멀티 GPU (tensor parallelism)
python -m vllm.entrypoints.openai.api_server \
  --model meta-llama/Meta-Llama-3.1-70B-Instruct \
  --tensor-parallel-size 4 \
  --port 8000
vllm_client.pyPYTHON
import asyncio
from openai import AsyncOpenAI

# vLLM은 OpenAI SDK를 그대로 사용
client = AsyncOpenAI(base_url="http://localhost:8000/v1", api_key="none")

# ── 배치 추론 (높은 처리량) ──────────────────────
async def batch_inference(prompts: list[str]) -> list[str]:
    tasks = [
        client.chat.completions.create(
            model="meta-llama/Meta-Llama-3.1-8B-Instruct",
            messages=[{"role": "user", "content": p}],
            max_tokens=512,
        )
        for p in prompts
    ]
    responses = await asyncio.gather(*tasks)
    return [r.choices[0].message.content for r in responses]

# ── 서버 성능 모니터링 ────────────────────────────
import httpx

async def check_metrics():
    async with httpx.AsyncClient() as http:
        metrics = await http.get("http://localhost:8000/metrics")
        # Prometheus 형식으로 TPS, 지연시간, 큐 길이 확인
        print(metrics.text[:500])

asyncio.run(batch_inference(["Python이란?", "Go란?", "Rust란?"]))
💡

프로덕션에서는 vLLM을 Docker로 실행하고 Nginx로 로드밸런싱하세요. 모델은 ReadWriteMany PVC나 S3에 저장해 여러 파드가 공유합니다.

LoRA / QLoRA 파인튜닝

도메인 특화 데이터로 파인튜닝하면 8B 모델도 특정 작업에서 GPT-4를 능가할 수 있습니다. QLoRA는 4-bit 양자화로 16GB VRAM에서 70B까지 파인튜닝 가능합니다.

BASH
uv add transformers peft datasets trl bitsandbytes accelerate
finetune_llama.pyPYTHON
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig, TrainingArguments
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
from trl import SFTTrainer
from datasets import load_dataset

MODEL_ID = "meta-llama/Meta-Llama-3.1-8B-Instruct"

# ── QLoRA: 4-bit 양자화 로딩 ─────────────────────
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16,
    bnb_4bit_use_double_quant=True,
)
model = AutoModelForCausalLM.from_pretrained(
    MODEL_ID, quantization_config=bnb_config, device_map="auto"
)
model = prepare_model_for_kbit_training(model)

# ── LoRA 설정 ────────────────────────────────────
lora_config = LoraConfig(
    r=16,                    # 랭크 (클수록 표현력↑, 메모리↑)
    lora_alpha=32,           # 스케일링 = alpha/r
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
                    "gate_proj", "up_proj", "down_proj"],
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM",
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# trainable: 41,943,040 || all: 8,072,204,288 || 0.52%

# ── 학습 ─────────────────────────────────────────
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
dataset   = load_dataset("json", data_files="train.jsonl", split="train")

trainer = SFTTrainer(
    model=model,
    train_dataset=dataset,
    tokenizer=tokenizer,
    args=TrainingArguments(
        output_dir="./llama-finetuned",
        num_train_epochs=3,
        per_device_train_batch_size=4,
        gradient_accumulation_steps=4,
        learning_rate=2e-4,
        bf16=True,
        save_steps=100,
        logging_steps=10,
    ),
)
trainer.train()

# LoRA 어댑터만 저장 (수십 MB)
model.save_pretrained("./lora-adapter")

# ── 추론 시 어댑터 로드 ───────────────────────────
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained(MODEL_ID, torch_dtype=torch.bfloat16)
model      = PeftModel.from_pretrained(base_model, "./lora-adapter")