본문으로 건너뛰기
5분 AI.
GITHUB TRENDING GO WEEKLY #4 딥다이브 · 2026-10-23 분석

LocalAI 딥다이브
— 하나의 API 뒤에 여러 AI 엔진을 연결하는 법

AI 서버

빠른 판단

무엇
여러 로컬 추론 엔진을 설치·관리하고 공통 HTTP API·웹 화면으로 제공하는 Go AI 서버
이럴 때
기존 AI 클라이언트를 자체 모델에 연결하고 텍스트·음성·이미지 엔진을 한 서버 주소에서 운영할 때
입력
선택한 모델·추론 백엔드와 텍스트·음성·미디어 요청
결과
호환 API로 반환되는 모델별 텍스트·음성·미디어 결과
채팅·이미지·음성을 내 컴퓨터나 서버에서 제공하는 오픈소스 AI 실행 플랫폼입니다. 이 글에서는 모델의 수학보다 서로 다른 추론 엔진을 같은 API로 묶는 서버 설계를 공부합니다. Go 요청 처리, gRPC 프로세스 경계, React 화면, 모델 설정, 동시성 제어를 실제 소스에 연결해 읽습니다. 저장소: mudler/LocalAI · 주 언어 Go · MIT · 스타 49,866 · 포크 4,533. 수치는 2026-10-23 GitHub 기준, 소스 설명은 커밋 f1ff707 기준입니다.
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 하드웨어 / 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

이미 학습된 AI 모델을, 익숙한 API로 호출하게 만드는 실행 플랫폼

LocalAI는 여러 추론 엔진을 설치·실행·관리하고, 그 위에 공통 HTTP API와 웹 화면을 제공하는 자체 호스팅 AI 서버입니다. 텍스트는 llama.cpp나 vLLM, 음성은 Whisper 계열이나 TTS 엔진, 이미지는 생성 엔진에 맡기고, 사용자는 하나의 서버 주소를 통해 요청합니다.

핵심은 역할 구분입니다. 같은 API를 유지하면서 실제 추론을 맡는 엔진을 바꿀 수 있게 만드는 것이 LocalAI의 중심 설계입니다. 이 경계를 이해하면 새로운 모델을 직접 학습하지 않아도 AI 서비스를 만드는 데 필요한 서버 기술을 배울 수 있습니다.

핵심 비유

여러 전문 주방을 연결한 하나의 주문 창구

손님은 같은 창구에서 주문하지만, 국수·빵·음료는 서로 다른 주방이 만듭니다. LocalAI의 HTTP API는 주문 창구, 모델 설정은 주문서, gRPC는 주방에 보내는 공통 전표, 추론 백엔드는 전문 주방입니다.

주방마다 장비와 조리법이 달라도 창구의 주문 형식은 유지할 수 있습니다. 다만 빵집에 국수를 주문할 수는 없습니다. 공통 API가 있어도 해당 모델과 백엔드가 요청한 기능을 지원해야 합니다.

먼저 구분할 세 가지
모델 · 추론 엔진 · API 서버
모델은 학습된 가중치와 관련 설정입니다. 추론 엔진은 그 가중치를 읽고 결과를 계산합니다. API 서버는 요청·인증·모델 선택·응답을 관리합니다. LocalAI 저장소에는 서버와 엔진 연결 코드가 있고, 모델 가중치는 별도로 받아야 합니다.

1-1. 사용자에게 보이는 최소 단위

다음은 채팅 요청의 형태입니다. model은 설치된 모델 설정의 이름이고, messages는 역할과 내용을 가진 대화 목록입니다. 이 JSON 자체가 AI를 계산하는 것은 아닙니다. 서버가 이를 해석해 적절한 엔진으로 전달합니다.

{
  "model": "study-chat",
  "messages": [
    {"role": "user", "content": "API와 함수의 차이를 쉽게 설명해 줘."}
  ],
  "stream": false
}

실제 등록 지점은 core/http/routes/openai.go의 RegisterOpenAIRoutes()입니다. /v1/chat/completions와 /chat/completions가 같은 ChatEndpoint()를 사용합니다. 경로가 두 개여도 처리 코드를 공유하는 사례입니다.

1-2. 이 저장소를 읽고 얻을 것

학습 질문답을 찾을 소스
HTTP 요청은 어디로 가나?core/http/routes/openai.go → core/http/endpoints/openai/chat.go
Go가 C++·Python과 어떻게 통신하나?backend/backend.proto, pkg/grpc/client.go
큰 모델의 중복 로딩은 어떻게 막나?pkg/model/loader.go의 loadModel()
웹 화면은 어떻게 배포하나?core/http/react-ui/, core/http/app.go의 go:embed
읽기 목표

처음부터 모든 기능을 이해할 필요는 없습니다. 채팅 요청 하나가 모델 설정을 찾고, 백엔드를 준비하고, 응답을 돌려주는 길을 설명할 수 있으면 첫 번째 학습 목표를 달성한 것입니다.

2왜 주목받는가

로컬 AI의 관심을, 실제 코드에 드러난 선택으로 해석하기

2026-10-23 GitHub Trending Go Weekly #4에 선정되었고, GitHub 기준 스타는 49,866개입니다. 순위만으로 관심의 원인을 증명할 수는 없습니다. 여기서는 README의 기능 설명과 소스 구조를 바탕으로 관심을 끌 만한 기술적 이유를 해석합니다.

LocalAI의 차별점은 하나의 추론 엔진보다 여러 엔진을 함께 운영하는 공통 계층에 있습니다. 따라서 경쟁 도구와 비교할 때도 단순 속도 순위보다 어디까지 묶어서 제공하는지를 살펴봐야 합니다.

2-1. 비슷해 보여도 공부하는 층이 다르다

도구공식 자료에서 확인한 중심 기능LocalAI와 비교해 볼 학습 지점
LocalAI여러 백엔드와 모달리티를 공통 API·UI로 통합엔진 추상화, 프로세스 수명, 설정, 권한을 함께 배우기 좋음
Ollama로컬 서버에 대한 OpenAI API 일부 호환 제공호환 API 자체는 독점적 장점이 아님. 모델 관리 경험과 통합 범위를 비교
vLLMvllm serve와 모델별 OpenAI 호환 API 제공LocalAI에서는 백엔드로도 사용. 직접 서빙과 공통 관리 계층의 차이를 관찰
llama.cppC/C++ 추론, 양자화, CPU·GPU 실행과 서버 제공LocalAI가 실제 계산을 위임하는 엔진 중 하나. 엔진과 통합 서버의 경계를 비교

비교 근거: Ollama 호환 API 문서, vLLM 서빙 문서, llama.cpp 공식 저장소. 각 도구의 지원 범위는 계속 바뀌며, 위 표는 속도·품질 벤치마크가 아닙니다.

2-2. 최근 커밋에서 읽히는 방향: 엔진도 라이브러리로 관리

f1ff707의 제목은 feat(addons): list and install backends from a library (#12816)입니다. 대응 소스인 core/gallery/backend_library.go는 gallery/backends/index.yaml을 백엔드 목록의 원본으로 설명하고, backend/index.yaml은 이전 릴리스와의 호환을 위한 생성본으로 구분합니다.

BackendLibraryEntries()는 플랫폼별 이미지 정보를 라이브러리 항목으로 바꾸고, BackendsFromLibrary()는 기존 갤러리 형식으로 되돌립니다. .github/workflows/backend-library.yml은 실제 목록의 변환과 생성본 일치 여부를 검사합니다. 새 저장 형식을 도입하면서 기존 소비자를 유지하는 호환성 설계 사례입니다.

비유

상품 목록의 원본 양식을 바꿔도 구형 계산대가 읽는 전표는 계속 출력하는 셈입니다. 중요한 것은 목록 파일이 두 개라는 사실보다, 어느 쪽이 원본이고 어느 쪽이 자동 생성되는지 명확히 정했다는 점입니다.

2-3. 소개 문구는 설정과 함께 읽는다

소개 문구소스에서 확인한 적용 범위
GPU 불필요CPU용 이미지와 llama.cpp CPU 빌드 경로가 있음. 모든 모델·모달리티가 CPU에서 쾌적하게 동작한다는 뜻은 아님
인프라 안에 데이터 유지로컬 추론 구성이 가능함. cloud-proxy, 외부 MCP 도구, 모델 다운로드를 사용하면 외부 통신이 생김
다중 사용자 지원인증 미들웨어가 있지만 RunCMD.AuthEnabled 기본값은 false. 활성화 여부를 확인해야 함
최신 소식README의 첫 소식은 4.11.0이지만 2026-10-23 GitHub 기준 최신 릴리스는 v4.12.0. README와 릴리스 목록의 갱신 시점이 다름

최신 릴리스 v4.12.0은 2026-10-22에 공개되었고, 분석 기준 커밋은 다음 날의 커밋입니다. 따라서 이 글의 모든 소스 관찰을 v4.12.0의 배포 기능과 동일시하면 안 됩니다.

3기술 스택 전체 지도

의존성 선언, 잠금 파일, 빌드 설정을 서로 다른 증거로 읽기

3-1. Go 백엔드: API와 실행 제어의 중심

루트 go.mod의 모듈은 github.com/mudler/LocalAI, Go 선언은 1.26.0입니다. 현재 HTTP 서버가 가져오는 프레임워크는 Echo입니다. 의존성 목록에 다른 웹 프레임워크가 간접 의존성으로 보이더라도 주 서버라고 판단하면 안 됩니다.

분야go.mod의 버전읽을 이유
HTTPlabstack/echo/v4 v4.15.1라우트·미들웨어·핸들러의 연결
CLIalecthomas/kong v1.15.0구조체 태그를 명령행 인자와 환경변수에 연결
프로세스 간 통신grpc v1.80.0
protobuf v1.36.11
언어가 다른 서버를 공통 계약으로 호출
설정gopkg.in/yaml.v3 v3.0.1모델 YAML을 타입이 있는 구조체로 변환
DB 접근gorm v1.31.1
SQLite·Postgres 드라이버 각각 v1.6.0
사용자·상태 같은 관계형 데이터 관리
분산 메시징nats.go v1.52.0선택적 분산 모드의 메시지·작업 전달
관측otel v1.44.0
client_golang v1.23.2
지표를 노출하고 요청·실행 비용 관찰
MCPmodelcontextprotocol/go-sdk v1.5.0모델이 사용할 도구를 서버에 연결
테스트ginkgo/v2 v2.29.0
gomega v1.41.0
행동 중심 테스트와 단언 방식
이미지 유통oras-go/v2 v2.6.0
sigstore-go v1.1.4
OCI 배포물과 서명 검증의 기반

SQLite·Postgres는 모델 가중치를 넣는 필수 저장소라는 뜻이 아닙니다. 가중치 파일, 모델 YAML, 인증 DB, 분산 상태의 역할이 다릅니다. core/application/distributed.go의 initDistributed()도 분산 모드가 꺼져 있으면 서비스를 만들지 않고 반환합니다.

3-2. 추론 백엔드: 언어도 의존성도 엔진마다 다르다

경로실제 확인한 값해석
backend/cpp/llama-cpp/MakefileLLAMA_VERSION?=c0fd78626405c6d9b934efef0ec7291fcc20e3d6상위 엔진의 특정 커밋을 기본값으로 고정
backend/python/vllm/requirements.txtgrpcio==1.84.0
protobuf는 버전 미고정
Python gRPC와 Go gRPC의 패키지 버전은 별개
backend/python/vllm/requirements-cublas13-after.txtvllm==0.31.0CUDA 13 프로필의 추가 요구사항. 모든 프로필의 공통 버전은 아님
backend/python/vllm/requirements-cublas12-after.txtvllm버전을 고정하지 않아 설치 시점에 따라 결과가 달라질 수 있음
backend/rust/kokoros/Cargo.tomlRust edition 2021
tonic="0.13", prost="0.13"
음성 백엔드의 Rust gRPC 구현. 루트 서버가 Rust라는 뜻은 아님

두 requirements 파일은 모두 backend/python/vllm/ 아래에 있습니다. backend/python/vllm/install.sh에는 Intel·ROCm·Darwin·Jetson별 분기가 있고 Python 3.12 선택도 분기별로 다릅니다. 이 저장소 전체의 Python·PyTorch·vLLM을 단일 버전으로 요약하는 것은 부정확합니다.

의존성 읽기
버전 선언과 설치 결과는 다르다
==0.31.0은 특정 버전 지정이고, 버전 없는 vllm은 설치 시 해결됩니다. Makefile의 ?=는 외부에서 덮어쓸 수 있는 기본값입니다. 파일에 적힌 값이 모든 배포 이미지에 그대로 들어갔다고 단정하지 않는 습관을 들이세요.

3-3. 프론트엔드: React와 JavaScript/JSX

현재 UI는 core/http/react-ui/의 React 앱입니다. 페이지의 실제 확장자는 주로 .jsx이며, TypeScript 앱이라고 소개할 근거는 없습니다. 이전 UI 자산은 core/http/static/에 남아 있습니다.

의존성package.jsonpackage-lock.json
React / React DOM각각 ^19.1.0각각 19.2.8
Vite^8.0.148.0.16
React Router8.3.18.3.1
React Router DOM7.18.27.18.2
MCP SDK^1.30.01.30.0
DOMPurify^3.4.133.4.13
Playwright1.58.21.58.2

^19.1.0은 19.1.0 하나만 허용하는 표기가 아닙니다. 설치 가능한 범위는 의존성 선언에서, 기록된 해결 버전은 잠금 파일에서 확인해야 합니다. 두 Router 패키지는 선언과 잠금 파일 모두 서로 다른 주 버전입니다. 숫자를 임의로 통일해서 읽지 마세요.

core/http/react-ui/vite.config.js의 개발 서버는 3000번 포트이고, 기본 API 대상은 http://localhost:8080입니다. /api, /v1, /models 등을 프록시합니다. 배포 결과는 dist에 생성되고 Go 서버가 이를 포함합니다.

// core/http/app.go — 정적 UI를 Go 바이너리에 포함
//go:embed react-ui/dist/*
var reactUI embed.FS

core/http/react-ui/src/router.jsx는 React의 lazy()와 동적 import로 페이지를 나누고, preloadRoute()로 미리 불러올 수 있게 합니다. core/http/react-ui/src/utils/api.js의 streamChat()은 stream: true를 보내고 응답의 ReadableStream을 반환합니다.

3-4. 인프라 · 빌드 · CI

영역실제 설정배울 점
루트 Dockerfile기본 ubuntu:24.04, Go 1.26.0, CMake 3.31.10공통 요구사항·장치별 요구사항·빌드·실행 단계를 분리
UI 빌드 단계node:26-slim, npm install, npm run buildUI 결과물을 Go 빌드 단계로 복사
핵심 테스트 CI.github/workflows/test.yml: Go 1.26.x, Node 22, Linux·macOSDocker 빌드와 CI의 Node 버전이 다름
UI 테스트 CI.github/workflows/tests-ui-e2e.yml: Bun 1.3.11, Playwright·커버리지 검사가짜 백엔드를 이용해 화면 동작을 검증
백엔드 배포.github/backend-matrix.yml, 언어별 DockerfileOS·CPU 아키텍처·GPU 계열마다 배포물을 선택
상태 확인유예 60m · 간격 1m · 제한 10s · 재시도 3긴 최초 준비 시간과 개별 상태 확인의 제한 시간을 구별

이미지 태그와 일부 Python 의존성이 가변적이고, Dockerfile은 npm ci 대신 npm install을 사용합니다. 잠금 파일이 있다는 사실만으로 모든 설치 경로의 완전한 재현성을 보장할 수는 없습니다.

4아키텍처 심화 분석

요청 하나의 이동 경로에서 설계 패턴 네 가지를 발견하기

4-0. 전체 시스템 구조

아래 그림은 일반적인 로컬 채팅 경로를 중심으로 단순화했습니다. 인증·라우팅·압축·도구 사용은 설정에 따라 달라지고, 분산 모드는 별도 분기입니다. 다운로드는 준비 단계, gRPC 추론은 실행 단계라는 점을 구분하세요.

[브라우저 React UI] [외부 HTTP 클라이언트] │ │ └──────── POST /v1/chat/completions ┘ │ ▼ ┌─────────────────────────────────────┐ │ Go / Echo 서버 │ │ core/http/app.go │ │ 전역 인증 → 라우트별 미들웨어 │ └─────────────────┬───────────────────┘ │ 설정 선택·요청 파싱 │ 모델 라우팅·수용 제어·PII 처리 ▼ ┌─────────────────────────────────────┐ │ ChatEndpoint → ComputeChoices │ │ 대화 형식·도구·스트리밍 응답 관리 │ └─────────────────┬───────────────────┘ ▼ ┌─────────────────────────────────────┐ │ core/backend.ModelInference │ │ 설정 → ModelOptions / PredictOptions│ └─────────────────┬───────────────────┘ ▼ ┌─────────────────────────────────────┐ │ pkg/model.ModelLoader │ │ 캐시 확인·로딩 합류·프로세스 준비 │ └────────┬──────────────────┬─────────┘ │ 로컬 모드 │ 분산 모드 선택 시 ▼ ▼ [실행 파일 시작/주소 연결] [SmartRouter → 워커·복제본 선택] │ │ └──── Go 클라이언트 ─────┘ │ gRPC 예측 호출 │ ┌─────────────────┼─────────────────┐ ▼ ▼ ▼ [C++ 백엔드 프로세스] [Python 백엔드 프로세스] [음성·이미지 백엔드] llama.cpp 등 vLLM 등 로컬 또는 선택된 워커에서 실행 │ │ │ └──────── 모델 가중치 + CPU / GPU ───┘ │ Reply 스트림 / 일반 응답 ▼ HTTP JSON / SSE → 화면 준비 경로: 라이브러리 목록 → 호환 백엔드 선택 → 배포물 설치 모델 파일·설정 → 실행 시 로더가 사용 저장 대상: 모델 파일 / 백엔드 파일 / 설정·사용자·운영 상태

4-1. 패턴 ① 어댑터와 공통 계약

어댑터는 서로 다른 인터페이스 사이의 번역기입니다. LocalAI에서는 HTTP JSON을 받는 코드와 각 엔진을 호출하는 코드 사이에 Protocol Buffers 계약이 놓여 있습니다. 엔진마다 고유한 인자를 HTTP 핸들러 곳곳에 직접 넣지 않도록 경계를 만듭니다.

  1. RegisterOpenAIRoutes()가 채팅 핸들러와 요청 해석 미들웨어를 조합합니다. 전역 인증은 core/http/app.go가 설치합니다.
  2. ChatEndpoint()는 해석된 OpenAIRequest와 ModelConfig를 컨텍스트에서 꺼냅니다. 대화 메시지와 시스템 프롬프트를 정리합니다.
  3. core/http/endpoints/openai/inference.go의 ComputeChoices()가 backend.ModelInferenceFunc를 호출합니다.
  4. ModelInference()는 로더로 백엔드를 준비하고 실제 예측용 함수를 반환합니다. ComputeChoices()가 그 함수를 호출할 때 예측이 진행됩니다.
  5. 엔진별 서버가 LoadModel·Predict·PredictStream을 자신의 구현에 연결합니다.
// backend/backend.proto — 주요 계약만 발췌
service Backend {
  rpc Health(HealthMessage) returns (Reply) {}
  rpc LoadModel(ModelOptions) returns (Result) {}
  rpc Predict(PredictOptions) returns (Reply) {}
  rpc PredictStream(PredictOptions) returns (stream Reply) {}
  rpc Embedding(PredictOptions) returns (EmbeddingResult) {}
  rpc TTS(TTSRequest) returns (Result) {}
}

C++ 쪽은 backend/cpp/llama-cpp/grpc-server.cpp의 BackendServiceImpl이 이 계약을 구현합니다. LoadModel()에는 params_parse(), llama_backend_init() 등이 보입니다. Python 쪽은 backend/python/vllm/backend.py의 BackendServicer.LoadModel()이 AsyncEngineArgs를 만들고 AsyncLLMEngine.from_engine_args()로 엔진을 초기화합니다.

설계 용어
계약 중심 설계
호출하는 쪽은 메시지의 형태와 메서드 약속을 알고, 구현하는 쪽은 자기 언어와 라이브러리로 약속을 지킵니다. 새 기능을 추가할 때는 계약·서버 구현·호출부가 함께 맞아야 합니다. 계약에 메서드가 있다고 모든 백엔드가 구현한 것은 아닙니다.

추상화의 효과는 구현 차이를 감추는 데 있지만, 기능 지원 여부와 실패까지 없애 주지는 않습니다. 예를 들어 선택적 RPC는 백엔드에 따라 UNIMPLEMENTED를 반환할 수 있습니다. 이때 HTTP 경로가 존재한다는 사실만으로 실행 성공을 예상하면 안 됩니다.

4-2. 패턴 ② 지연 로딩과 같은 작업에 합류하기

모델은 일반 객체보다 훨씬 큽니다. 같은 모델을 요구하는 요청 10개가 동시에 들어왔다고 가중치를 10번 읽고 프로세스 10개를 띄우면 메모리가 부족해질 수 있습니다. 로컬 모드의 pkg/model/loader.go는 이 문제를 loading 맵과 채널로 조정합니다.

요청 A ── 캐시 없음 ── 로딩 대표가 됨 ── 모델 준비 ── 캐시에 등록 │ │ 요청 B ── 같은 modelID ── loading 채널에서 기다림 ◀───────┤ 요청 C ── 같은 modelID ── loading 채널에서 기다림 ◀───────┘ 채널 닫기 후속 요청 ── 준비된 모델 재사용
// loadModel()의 핵심 흐름을 줄인 의사 코드
잠금 획득
이미 준비된 모델이 있으면 반환
같은 modelID의 로딩 채널이 있으면:
    잠금 해제
    채널이 닫힐 때까지 기다림
    준비된 모델 확인 또는 재시도 경로로 이동
새 로딩 채널 등록
잠금 해제

모델 로딩 실행                 // 오래 걸리는 작업
결과를 모델 저장소에 등록
로딩 채널 정리·닫기

실제 함수는 LoadModelWithFile() → loadModel()입니다. 오래 걸리는 loader(...) 호출 동안 전역 잠금을 잡지 않는 점이 중요합니다. 다른 모델의 로딩까지 같은 잠금으로 막히는 일을 줄입니다. 실패한 모델을 계속 요청할 때 새 프로세스를 반복 생성하지 않도록 실패 후 대기 시간도 둡니다.

pkg/model/initializers.go의 spawnGRPCModel()은 실행 파일이면 StartProcess()를 호출하고, 주소이면 그 서버에 연결합니다. 준비 상태 확인 후 LoadModel RPC를 보내며, 실패하면 준비한 프로세스를 정리합니다. 로컬 실행 주소는 127.0.0.1의 빈 포트에서 선택합니다.

쉽게 놓치는 구분
배포 이미지와 실행 프로세스는 역할이 다르다

백엔드를 OCI 이미지로 유통한다는 설명만 보고 매 요청마다 Docker 컨테이너를 만든다고 이해하면 안 됩니다. 이 로컬 실행 경로의 핵심은 등록된 실행 파일을 별도 프로세스로 시작하거나, 이미 실행 중인 gRPC 주소에 연결하는 것입니다.

메모리 관리도 이 수명 관리와 연결됩니다. ModelLoader.Load()는 새 모델 로딩 전 그룹 배타성과 LRU 제한을 검사하고, pkg/model/watchdog.go는 사용 중인 요청·유휴 시간·고정 모델 등을 추적합니다. 다만 DefaultWatchDogOptions()에서는 유휴 검사·사용 중 시간 검사·메모리 회수 기능이 꺼져 있고 LRU 제한은 0, 즉 무제한입니다. 실제 활성화 설정을 함께 읽어야 합니다.

분산 모드에는 중요한 예외가 있습니다. loadModel()은 로컬 캐시로 요청을 고정하지 않고 라우터를 다시 거칩니다. 이전 요청의 원격 복제본만 계속 재사용하면 부하 분산이 무너지기 때문입니다. 같은 캐시 정책을 로컬과 분산에 무조건 적용할 수 없다는 교훈입니다.

4-3. 패턴 ③ 설정 기본값의 단계적 적용

서버에는 모델 이름, 엔진, 샘플링 값, 문맥 길이, 스레드 수, 하드웨어 옵션이 함께 들어옵니다. core/config/model_config.go의 ModelConfig.SetDefaults()는 이 설정을 순서대로 보완합니다. 아래는 로직의 주요 순서입니다.

모델 YAML / 로딩 옵션 │ ▼ ApplyInferenceDefaults 모델 계열의 추론 기본값 ▼ ApplyServingDefaults 서빙 정책 기본값 ▼ ApplyGenericDefaults 일반 기본값 ▼ 스레드·전역 문맥 옵션 보완 ▼ runBackendHooks 백엔드별 처리·GGUF 정보 반영 ▼ ApplyHardwareDefaults 최종 문맥 길이를 보고 장치 기본값 보완

하드웨어 단계를 마지막에 두는 이유가 주석으로 명확합니다. 배치 크기 판단에 최종 문맥 길이가 필요하기 때문입니다. 긴 문맥에서 큰 배치를 고르면 연산용 메모리가 커질 수 있으므로, 문맥을 추정하기 전에 하드웨어 값을 확정하면 잘못된 결정을 할 수 있습니다.

# ModelConfig에 대응하는 학습용 YAML 예시
# 파일 이름은 실제로 준비한 GGUF 파일로 바꿔야 합니다.
name: study-chat
backend: llama-cpp
parameters:
  model: example.Q4_K_M.gguf
  temperature: 0.3
context_size: 4096
threads: 4
template:
  use_tokenizer_template: true

Name은 API에 노출하는 이름이고, parameters.model은 엔진이 읽는 모델 참조입니다. 둘이 반드시 같을 필요는 없습니다. Threads나 여러 불리언 옵션을 포인터로 둔 것도 살펴보세요. 설정이 없다는 상태와 사용자가 0·false를 명시한 상태를 구분하는 데 사용됩니다.

비유

여행 예약의 빈칸을 채우는 순서와 비슷합니다. 여행자가 이미 정한 좌석을 덮어쓰면 안 되고, 목적지를 알아야 이동 시간을 계산할 수 있습니다. 기본값은 단순한 상수 모음이 아니라 선행 정보에 의존하는 정책입니다.

위 YAML은 필드 관계를 설명하기 위한 예시입니다. 모든 모델이 같은 대화 템플릿을 쓰지는 않습니다. use_tokenizer_template 경로에서는 구조화된 메시지를 엔진에 넘기고, 다른 경로에서는 Go 템플릿 평가기가 프롬프트를 구성합니다.

4-4. 패턴 ④ 스트리밍 파이프라인과 취소 전달

채팅 응답을 조금씩 보여주려면 엔진의 출력과 HTTP 응답을 연결해야 합니다. 바깥은 SSE이고 안쪽은 gRPC 서버 스트림입니다. ChatEndpoint()는 text/event-stream 헤더를 설정하고 채널에서 응답 조각을 받아 씁니다.

# SSE 응답 형태를 줄인 예시
data: {"choices":[{"delta":{"content":"안녕"}}]}

data: {"choices":[{"delta":{"content":"하세요"}}]}

data: [DONE]

pkg/grpc/client.go의 Client.PredictStream()은 stream.Recv()를 반복하며 콜백에 조각을 전달합니다. ctx.Done()과 RPC 오류를 확인하고, defer로 연결 종료와 사용 중 상태 정리를 예약합니다. HTTP 쪽도 요청 컨텍스트 취소를 확인합니다.

core/backend/llm.go에는 초보자에게 유용한 세부 구현이 있습니다. 수신 바이트를 partialRune에 모은 뒤 utf8.DecodeRune()으로 완성된 문자를 분리합니다. 한글 한 글자의 UTF-8 바이트가 서로 다른 응답 조각에 나뉠 수 있기 때문입니다. 네트워크 조각과 글자·토큰은 일대일 관계가 아닙니다.

실시간 처리의 기준
취소도 데이터처럼 다음 단계로 전달한다
사용자가 응답을 중단했는데 뒤의 계산이 계속되면 자원이 낭비됩니다. UI의 AbortSignal, HTTP 요청 컨텍스트, gRPC 컨텍스트를 연결하는 이유입니다. 취소 신호가 있는지만 보지 말고 각 대기 지점이 그 신호를 실제로 확인하는지도 읽어야 합니다.

소스 연결: 라우트 조합 · 추론·문자 조각 처리 · 로딩 합류 · 프로세스 준비 · gRPC 클라이언트.

5디렉토리 구조 해부

폴더 이름보다 어떤 책임이 어느 경계를 넘는지 보기
LocalAI/ ├── cmd/ │ ├── local-ai/ CLI 실행 진입점 │ └── launcher/ 데스크톱 실행기 ├── core/ │ ├── cli/ 명령·플래그·환경변수 │ ├── application/ 서비스 조립·시작·종료 │ ├── config/ 모델·앱 설정과 기본값 │ ├── http/ │ │ ├── routes/ API 경로 등록 │ │ ├── endpoints/ API별 처리 │ │ ├── middleware/ 요청 해석·제어 │ │ ├── auth/ 인증·권한 │ │ └── react-ui/ 현재 React 웹 화면 │ ├── backend/ 추론 옵션·결과 변환 │ ├── gallery/ 모델·백엔드 목록과 설치 │ ├── library/ 라이브러리 소스 관리 │ └── services/ 분산·작업·도구 등 서비스 ├── pkg/ │ ├── model/ 모델 로더·프로세스·감시 │ ├── grpc/ 공통 RPC 클라이언트·서버 │ ├── modelartifacts/ 모델 배포물 확보·관리 │ ├── system/ 장치 능력·백엔드 상태 │ ├── vram/ 모델 메모리 추정 │ └── addons/ 부가 기능의 계약·실행 기반 ├── backend/ │ ├── backend.proto 여러 언어가 공유하는 계약 │ ├── cpp/ llama.cpp 등 연결 구현 │ ├── python/ vLLM 등 연결 구현 │ ├── go/ Go 백엔드 │ └── rust/ Rust 백엔드 ├── addons/ 부가 앱·기능 구현 ├── gallery/ 모델·백엔드·부가 기능 목록 ├── configuration/ 설정 예제 ├── tests/ 통합·종단 간 테스트와 자료 ├── docs/ 사용자·개발 문서 ├── .github/ CI·빌드 행렬 ├── Dockerfile 서버 이미지 조립 └── Makefile 빌드·생성·검사 명령

5-1. 읽는 순서를 책임 순서로 바꾸기

단계먼저 열 파일확인할 질문
실행cmd/local-ai/main.go, core/cli/run.go환경변수와 명령 옵션은 어디서 들어오는가?
조립core/application/application.go, core/http/app.go어떤 서비스와 미들웨어를 만드는가?
요청core/http/routes/openai.go, core/http/endpoints/openai/chat.go파싱한 요청을 누가 처리하는가?
실행 제어core/backend/llm.go, pkg/model/initializers.go모델 설정을 어떤 RPC로 바꾸는가?
계산 위임backend/backend.proto, backend/cpp/llama-cpp/grpc-server.cpp엔진 경계에서 어떤 값이 오가는가?

요청 처리의 출발점은 core/http/endpoints/openai/chat.go입니다. 처음에는 core/http/의 요청 경로에서 필요한 호출만 따라가고, core/services/의 분산 노드나 에이전트 기능은 그다음에 살펴보세요.

큰 저장소를 읽을 때는 폴더를 모두 훑기보다 하나의 입력이 통과하는 책임 경계를 따라가면 이해가 빨라집니다. 같은 방법으로 음성을 공부할 때는 TranscriptEndpoint()와 AudioTranscription 계약을 새 출발점으로 삼을 수 있습니다.

5-2. 이름이 비슷한 세 가지 목록

공통 카탈로그를 사용하더라도 설치하는 대상의 의미는 다릅니다. 모델 이름, 백엔드 이름, 부가 기능 ID를 모두 같은 식별자로 생각하면 설정과 로그를 해석하기 어려워집니다.

6학습 포인트 (기술별)

기술 이름을 외우기보다, 해결하는 문제와 실험을 연결하기
Go · HTTP 서버

요청 전처리와 핵심 처리를 나누는 법

읽을 곳: core/http/routes/openai.go, core/http/endpoints/openai/chat.go. 라우트 등록부에서 모델 선택·요청 파싱·라우팅·수용 제어·PII 처리를 조합하고, 핸들러는 준비된 요청을 사용합니다.

배울 것: 미들웨어 순서도 기능입니다. 실제 서비스 모델을 정한 뒤 그 모델의 제한을 검사하도록 수용 제어를 배치하는 이유를 설명해 보세요.

실습 아이디어: 연습용 Echo 서버에 기록 미들웨어 두 개를 붙이고 요청 전후의 출력 순서를 비교합니다. 처리 중 오류가 날 때 어떤 후처리가 필요한지도 적어 봅니다.

Go · 동시성

공유 상태를 보호하되 느린 작업은 잠금 밖에서

읽을 곳: pkg/model/loader.go의 loadModel(). 모델 저장소와 로딩 채널을 바꾸는 짧은 구간, 모델을 실제로 준비하는 긴 구간을 구분합니다.

배울 것: 잠금은 데이터 경쟁을 막지만, 오래 잡으면 관련 없는 요청도 기다립니다. 채널 닫기는 값을 보내지 않고도 대기자들에게 완료를 알리는 방법입니다.

실습 아이디어: 실제 모델 대신 호출 횟수만 세는 가짜 로더를 만듭니다. 같은 ID를 동시에 요청할 때 성공한 준비 작업이 한 번인지 확인합니다.

gRPC · 여러 언어 연결

같은 프로토콜을 써도 라이브러리 버전은 다를 수 있다

읽을 곳: backend/backend.proto, pkg/grpc/client.go, backend/python/vllm/backend.py. 같은 메시지가 Go·Python·C++에서 어떤 타입으로 나타나는지 비교합니다.

배울 것: 한 번 응답하는 RPC와 여러 조각을 보내는 RPC의 차이, 정상 스트림 종료와 오류 종료, 지원하지 않는 메서드의 처리를 배울 수 있습니다.

실습 아이디어: 가짜 PredictStream이 세 조각을 반환하도록 설계하고, 클라이언트가 둘째 조각 뒤에 취소하면 서버가 어떻게 종료되어야 하는지 순서도를 그립니다.

설정 · Python 어댑터

설정이 없을 때와 값이 0일 때를 구별하기

읽을 곳: ModelConfig.SetDefaults(), BackendServicer.LoadModel(). Go 설정의 포인터와 Python 쪽의 request.GPUMemoryUtilization != 0 같은 분기를 살펴봅니다.

배울 것: 필드 존재 여부를 잃으면 사용자가 지정한 값과 자동 기본값을 구분하기 어렵습니다. vLLM 어댑터는 개별 설정 필드, options 목록, YAML의 engine_args 순서로 적용하므로 뒤의 설정이 앞의 값을 덮어쓸 수 있습니다.

실습 아이디어: 미지정·명시적 false·명시적 양수의 세 경우를 표로 만들고, 어떤 값이 어느 단계에서 결정되는지 예측합니다.

React · 배포 이후의 화면

페이지 분할은 실패 복구와 함께 읽는다

읽을 곳: core/http/react-ui/src/router.jsx의 page(), claimReload(), preloadRoute(). 페이지를 필요할 때 불러오고, 오래 열린 탭이 이전 배포의 파일을 요구할 때 복구합니다.

배울 것: RELOAD_WINDOW_MS = 10_000과 sessionStorage는 무한 새로고침을 제한합니다. 성능을 위한 코드 분할이 배포 시 어떤 실패 조건을 만드는지 볼 수 있습니다.

실습 아이디어: 연습용 앱에서 페이지 가져오기를 한 번 실패하게 하고, 복구가 한 번만 일어나는지 확인합니다. 네트워크를 끈 경우에도 무한 반복되지 않아야 합니다.

테스트 · 빌드 시스템

AI 모델을 띄우지 않고 서버를 검증하는 법

읽을 곳: core/http/endpoints/openai/inference_test.go, tests/e2e/mock-backend/, .github/workflows/tests-ui-e2e.yml. 추론 함수를 가짜 구현으로 바꾸거나 모의 백엔드를 사용합니다.

배울 것: 요청 파싱·JSON 형태·오류·취소 동작은 모델 품질 평가와 다른 문제입니다. 어떤 의존성을 가짜로 바꿀지 정하면 큰 가중치 없이도 많은 경로를 검사할 수 있습니다.

실습 아이디어: 빈 결과, 정상 결과, RPC 오류의 세 응답을 설계하고 HTTP 클라이언트가 각각 어떤 상태와 본문을 받아야 하는지 먼저 적습니다.

AI 서버 공부에서도 빠른 피드백을 만드는 핵심은 실제 모델 없이 검증할 수 있는 경계를 찾는 것입니다. 실제 엔진과의 연동, 장치별 추론 속도와 답변 품질은 엔진·가중치·장치를 갖춘 환경에서 따로 검증해야 합니다.

7하드웨어 / 시스템 요구사항

서버가 실행되는 조건과 모델이 쾌적하게 동작하는 조건은 따로 계산

7-1. 하나의 최소 사양으로 요약하기 어려운 이유

LocalAI 코어가 시작되는 것과 선택한 모델을 메모리에 올리는 것은 다릅니다. 모델 크기, 양자화, 문맥 길이, 동시 요청, 모달리티에 따라 요구 자원이 크게 달라집니다. README의 CPU 지원을 모든 영상·이미지 모델에도 같은 비용으로 적용할 수는 없습니다.

사용 경로시스템 조건확인할 지점
CPU 추론해당 CPU·OS용 백엔드와 모델을 담을 RAM기본 CPU 이미지, llama.cpp 빌드 변형. GPU는 필수 아님
NVIDIA호환 드라이버·장치 노출·충분한 VRAMCUDA 12·13 이미지와 드라이버 호환성을 맞춤
AMD / Intel지원 장치·런타임·장치 파일 권한ROCm·oneAPI 계열 이미지 및 모델별 백엔드 지원
Apple Silicon해당 Darwin ARM64 백엔드와 통합 메모리llama.cpp Metal·MLX 등. Linux 컨테이너 경로와 네이티브 경로 구별
컨테이너Docker·Podman 등 OCI 실행 환경Linux, macOS·Windows의 컨테이너/가상화 경로. GPU 전달은 OS별로 다름
소스 빌드Go·Node·C/C++ 도구와 선택한 엔진의 의존성Go 선언 1.26.0, Docker CMake 3.31.10. 실행만 할 때와 구별

컨테이너 안내는 docs/content/getting-started/containers.md, 장치별 빌드 분기는 backend/cpp/llama-cpp/Makefile과 .github/backend-matrix.yml에서 확인할 수 있습니다. GPU 제조사가 같아도 모든 세대가 같은 백엔드를 지원하는 것은 아닙니다.

7-2. 메모리는 가중치 파일보다 더 필요하다

가중치의 이론적 크기 ≈ 파라미터 수 × 저장 비트 수 ÷ 8
실행 메모리 ≈ 가중치 + KV 캐시 + 연산용 버퍼 + 런타임 여유

7B 모델의 단순 가중치 계산:
  16비트: 7,000,000,000 × 16 ÷ 8 = 약 14 GB
   4비트: 7,000,000,000 ×  4 ÷ 8 = 약 3.5 GB

# GB는 십진 단위입니다. GGUF의 실제 크기·필요 RAM과 다릅니다.
# 양자화 메타데이터, 혼합 정밀도, 부가 모델도 비용에 포함됩니다.

KV 캐시는 이전 토큰의 계산 결과를 보관하는 공간입니다. 문맥 길이와 동시 처리 슬롯이 늘면 이 공간도 커질 수 있습니다. 같은 모델 파일을 사용하더라도 긴 대화를 여러 개 처리하면 메모리 부족이 발생할 수 있습니다.

모델 파일이 메모리보다 작다는 사실만으로 그 모델의 실행 가능 여부를 판단할 수는 없습니다. 처음에는 작은 모델·짧은 문맥·단일 요청을 기준으로 측정하고 하나씩 조건을 바꾸는 편이 원인을 찾기 쉽습니다.

7-3. 소스에 있는 보수적 기본값

상수·설정값의미
DefaultContextSize4096문맥을 알 수 없을 때의 대체값
DefaultAutoContextSize8192GGUF에서 자동 도출할 문맥의 상한. 추가 축소 가능
DefaultPhysicalBatch512일반적인 물리 배치 기본값
BlackwellPhysicalBatch2048Compute Capability 주 버전 12 이상에서 문맥·VRAM 여유 검사 후 적용. 10.0인 데이터센터 Blackwell은 제외
스레드 대체값4SetDefaults()에서 로딩 옵션이 0일 때 사용

출처는 core/config/defaults.go, core/config/hardware_defaults.go, core/config/model_config.go입니다. 이 숫자는 전체 백엔드의 고정 실행값이 아닙니다. 명시적 설정·모델 정보·백엔드별 처리가 최종값에 영향을 줍니다.

PhysicalBatchForContext()는 Blackwell 장치라도 VRAM이 알려져 있지 않거나 문맥에 비해 여유가 부족하면 512를 유지합니다. 여러 GPU의 메모리 합이 아니라 장치 하나의 연산 버퍼 여유를 고려한다는 점도 읽을 만합니다.

7-4. 디스크·네트워크·저장 위치

가중치 외에 백엔드 배포물, Python 환경, 다운로드 캐시, 생성 결과도 공간을 차지합니다. 루트 Dockerfile은 /models, /backends, /configuration, /data를 볼륨 대상으로 선언합니다. 컨테이너를 교체해도 필요한 데이터가 남도록 각각의 역할을 확인해야 합니다.

요구사항 해석의 한계

여기에 공통 RAM 최소값이나 초당 토큰 수를 제시하지 않는 이유는 모델·장치 조합의 실행 측정값이 아니기 때문입니다. 특히 영상 생성, 긴 문맥, 큰 배치는 텍스트 소형 모델의 감각으로 예측하면 안 됩니다. 오프라인 실행도 필요한 모델·백엔드·부가 파일이 미리 준비되어 있어야 합니다.

8직접 해볼 수 있는 실습 과제

읽기 → 요청 관찰 → 가짜 구현으로 설계 검증

실습은 난이도 순서입니다. 모델이 없는 환경에서는 1·4·5번을 먼저 진행할 수 있습니다. 요청 실습은 별도로 준비한 로컬 서버와 설치된 채팅 모델을 전제로 하며, 예시의 모델 이름·인증 키를 그 환경에 맞춰 바꿉니다.

과제 1 · 입문

채팅 요청의 지도 한 장 만들기 소스 읽기

할 일: RegisterOpenAIRoutes()에서 시작해 ChatEndpoint(), ComputeChoices(), ModelInference(), ModelLoader.Load(), PredictStream()을 연결합니다.

제출물: 함수·입력·출력을 한 줄씩 적은 여섯 칸 표와, 일반 응답/스트리밍 응답이 갈라지는 위치 표시.

완료 기준: 모델을 고르는 단계, 메모리에 준비하는 단계, 실제 예측하는 단계를 구분해 설명할 수 있습니다.

추가 질문: 왜 ModelInference()의 반환값에 함수가 들어 있을까요? 준비와 실행을 나눈 비용·책임을 찾아보세요.

과제 2 · 입문

같은 질문의 일반 응답과 스트림 비교 API 관찰

할 일: 먼저 설치 모델 목록을 확인하고, 동일한 모델·질문을 stream: false와 true로 각각 요청합니다. 답변의 문장 내용보다 응답의 구조와 도착 방식을 비교합니다.

# 인증이 설정된, 이미 준비된 로컬 실습 서버의 예시
curl http://127.0.0.1:8080/v1/models \
  -H "Authorization: Bearer $LOCALAI_STUDY_KEY"

curl -N http://127.0.0.1:8080/v1/chat/completions \
  -H "Authorization: Bearer $LOCALAI_STUDY_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "study-chat",
    "messages": [{"role":"user","content":"동시성을 한 문장으로 설명해 줘."}],
    "stream": true,
    "max_tokens": 80
  }'

제출물: 응답 헤더, 첫 응답까지의 시간, SSE 조각 두 개, 종료 표시. 일반 응답은 stream을 false로 바꿔 비교합니다.

완료 기준: 일반 JSON 한 개와 SSE의 여러 이벤트를 구별합니다. 존재하지 않는 모델을 요청했을 때의 상태·오류 본문도 기록합니다.

주의할 해석: 처음 요청은 모델 준비 비용이 섞일 수 있습니다. 한 번의 시간 차이를 추론 엔진 자체의 속도 차이라고 결론 내리지 않습니다.

과제 3 · 중급

설정 하나씩 바꾸어 원인 찾기 설정과 자원

할 일: 같은 작은 모델을 대상으로 문맥 길이와 스레드 수의 설정표를 만듭니다. 우선 SetDefaults()를 읽어 미지정 값이 어떻게 결정되는지 예측한 뒤, 준비된 실습 환경에서 한 항목씩 바꿔 관찰합니다.

제출물: 모델·백엔드·장치·문맥·스레드·동시 요청 수를 고정 조건과 변경 조건으로 나눈 기록표. 각 회차의 메모리와 첫 응답 시간을 함께 적습니다.

완료 기준: 문맥을 늘리는 실험과 스레드를 늘리는 실험을 분리하고, 관측 결과를 설명할 때 KV 캐시와 연산 버퍼를 언급할 수 있습니다.

실패에서 배울 것: 큰 설정에서 준비가 실패하면 직전의 작은 설정으로 돌아갑니다. 파일 손상·미지원 엔진·메모리 부족은 서로 다른 가설로 기록합니다.

과제 4 · 중급

한글 조각과 취소를 처리하는 모의 스트림 모델 불필요

할 일: 별도의 연습 프로그램에서 안녕하세요의 UTF-8 바이트를 글자 경계와 무관하게 나눠 전달합니다. 소스의 partialRune 아이디어를 참고해 완성된 문자만 출력합니다.

입력: "안녕하세요"를 UTF-8 바이트로 변환
전송: 1바이트, 2바이트, 1바이트, 나머지 순서로 조각 전송
수신: 미완성 바이트는 다음 조각까지 보관
출력: 원래 문자열과 같은지 비교
취소: 둘째 조각 뒤 중단 신호를 보내 생산자 종료 확인

제출물: 정상 완료·중간 취소·중간 오류의 세 경우에 대한 출력과 종료 기록. 응답이 한 글자씩 온다고 가정한 구현과 비교합니다.

완료 기준: 깨진 문자·중복 문자 없이 합쳐지고, 취소 후 생산자와 수신자가 모두 종료됩니다. HTTP 조각·토큰·문자 사이의 차이를 설명합니다.

확장: 유효하지 않은 바이트가 들어오면 보관할지 대체 문자로 바꿀지 정책을 정합니다. 소스의 부분 문자 처리와 완전한 입력 검증을 구분해 보세요.

과제 5 · 고급

동시 로딩 조정기를 작게 다시 설계하기 모델 불필요

할 일: 별도의 연습 프로젝트에서 ID별 캐시와 로딩 채널을 구현합니다. 실제 엔진 대신 호출 횟수를 세는 가짜 로더를 연결하고 동일 ID에 요청 20개를 보냅니다.

검사할 세 경우: 모두 성공, 대표 로딩 실패, 기다리던 요청 하나의 취소. 서로 다른 ID는 불필요하게 직렬화되지 않는지도 확인합니다.

제출물: 로더 호출 횟수·최대 동시 실행 수·대기자 종료 결과, 그리고 실패 시 채널을 누가 언제 닫는지 나타낸 상태도.

완료 기준: 같은 ID의 성공 준비가 중복되지 않고, 오류와 취소 뒤에도 영원히 기다리는 요청이 없습니다. 실제 loadModel()의 <-loadingChan 대기와 취소 가능한 select 설계의 차이도 비교합니다.

확장: 분산 모드에서 준비된 원격 클라이언트를 영구 캐시하면 왜 특정 복제본에 요청이 쏠리는지 설명합니다. 코드의 분산 분기를 근거로 삼습니다.

실습 결과물은 실행 성공 화면보다 입력·예상·관측·실패 조건을 함께 남긴 기록이어야 합니다. 이 네 가지가 있으면 모델이나 장치를 바꿔도 배운 설계 지식을 재사용할 수 있습니다.

9관련 기술 심화 학습 로드맵

6주 동안 하나의 요청을 깊게 읽고, 작은 부품을 직접 만들어 보기

주차는 진도의 예시입니다. HTTP·JSON·기본 함수 호출을 알고 있다는 전제이며, 낯선 개념이 있으면 해당 주차를 더 길게 잡아도 됩니다. 매주 읽을 범위를 제한하고 설명 가능한 결과물을 하나씩 만드세요.

주차학습 범위와 소스이번 주 결과물
1주 · API HTTP 메서드·상태·JSON·미들웨어. core/http/routes/openai.go와 core/http/endpoints/openai/chat.go 채팅 요청 이동 지도. 400·인증 실패·모델 실행 실패를 구분하는 오류표
2주 · 설정 Go 구조체·포인터·YAML 태그. ModelConfig, SetDefaults(), core/config/defaults.go 미지정/명시 값 비교표. 모델 이름과 가중치 참조를 분리한 예제
3주 · RPC Protocol Buffers·일반 RPC·스트림. backend/backend.proto, pkg/grpc/client.go 가짜 예측 서버의 계약. 정상 종료·오류·취소 상태도
4주 · 동시성 goroutine·mutex·channel·컨텍스트. pkg/model/loader.go, pkg/model/initializers.go 동일 ID 로딩 조정기. 성공·실패·취소·다른 ID 병렬 처리 기록
5주 · 웹 UI React 상태·지연 로딩·스트림 소비. core/http/react-ui/src/router.jsx, core/http/react-ui/src/utils/api.js, core/http/react-ui/src/pages/Chat.jsx 응답 표시·중단 버튼이 있는 작은 화면. 페이지 로딩 실패 복구 정책
6주 · 운영 이미지·볼륨·준비 상태·권한·카탈로그. Dockerfile, core/http/auth/middleware.go, core/gallery/backend_library.go 데이터 보존·인증·모델 준비 지연을 설명하는 배포 설계서

학습 범위를 넓힐 때도 core/http/endpoints/openai/chat.go의 호출부터 따라가세요. HTTP 처리는 core/http/endpoints/openai/, 화면은 core/http/react-ui/src/, 인증은 core/http/auth/, 라이브러리 변환은 core/gallery/에 모여 있습니다.

9-1. 6주 뒤 고를 수 있는 심화 갈래

학습 범위는 기능 개수보다 설명할 수 있는 실패 상황의 수로 넓혀 가는 것이 좋습니다. 성공 경로를 읽은 뒤에는 같은 모델의 동시 요청, 잘못된 설정, 끊긴 스트림, 오래된 배포 파일을 차례로 추가해 보세요.

10핵심 키워드 사전

코드를 읽다가 막히는 용어를, 이번 저장소의 역할에 연결하기
용어쉬운 설명
추론이미 학습된 모델에 입력을 넣어 결과를 계산하는 일. 모델을 처음부터 학습하는 것과 다릅니다.
가중치모델이 학습한 숫자들. 다운로드하는 큰 모델 파일의 주요 내용입니다.
백엔드여기서는 실제 추론 기능을 제공하는 엔진 연결 구현. 웹 서비스의 일반적인 서버 쪽이라는 뜻과 구분합니다.
서빙모델을 다른 프로그램이 요청할 수 있는 서비스로 제공하고 관리하는 일입니다.
호환 API기존 클라이언트가 이해하는 요청·응답 형식에 맞춘 인터페이스. 모델의 답변 품질까지 같다는 뜻은 아닙니다.
모달리티텍스트·이미지·음성·영상처럼 입력과 출력이 표현되는 종류입니다.
어댑터한쪽의 형식과 호출 방식을 다른 쪽에 맞게 바꿔 주는 연결 부품입니다.
미들웨어핵심 핸들러 앞뒤에서 인증·해석·기록·제한 등을 처리하는 공통 단계입니다.
gRPC다른 프로세스의 기능을 정해진 메시지 계약으로 부르는 통신 방식입니다.
Protocol Buffers메시지의 필드·번호·타입과 서비스 계약을 정의하는 도구. 이 저장소에서는 여러 언어를 연결합니다.
직렬화프로그램 안의 데이터를 네트워크나 파일로 보낼 수 있는 형태로 바꾸는 일입니다.
SSEHTTP 연결에서 서버가 이벤트를 계속 보내는 방식. 채팅 응답을 조금씩 표시할 때 사용합니다.
토큰모델이 다루는 텍스트의 단위. 한국어 한 글자나 네트워크 조각과 항상 일치하지 않습니다.
토크나이저텍스트와 모델의 토큰 표현을 서로 변환하는 부품입니다.
대화 템플릿역할별 메시지를 모델이 학습한 대화 형식에 맞게 배치하는 규칙입니다.
문맥 길이모델이 한 요청에서 다룰 토큰 범위. 길어지면 메모리와 연산 비용이 달라집니다.
KV 캐시이전 토큰의 계산을 재사용하기 위해 보관하는 키·값 데이터. 모델 파일과 별도로 공간이 필요합니다.
양자화가중치 등의 숫자를 더 적은 비트로 표현하는 방법. 크기·속도·정확도 사이에 선택이 생깁니다.
GGUF가중치와 관련 정보를 담는 파일 형식. 모든 추론 엔진의 공통 입력 형식은 아닙니다.
VRAM / OOMVRAM은 GPU 메모리, OOM은 메모리 부족 상태입니다. 가중치 외의 실행 비용도 원인이 됩니다.
배치계산을 묶어서 처리하는 단위. 물리 배치·논리 배치·동시 요청 슬롯을 같은 숫자로 보지 마세요.
goroutineGo가 관리하는 동시 실행 단위. 실제 모델 로딩을 여러 요청과 조정할 때 등장합니다.
mutex / channelmutex는 공유 상태 변경을 보호하고, channel은 데이터나 완료 신호를 전달합니다.
컨텍스트여기서 Go의 context.Context는 취소·기한 등을 전파하는 수단입니다. 모델 문맥 길이와 다릅니다.
지연 로딩필요해졌을 때 자원을 준비하는 방법. 첫 요청이 느려질 수 있지만 시작 시 비용을 줄입니다.
LRU / 감시자LRU는 오래 사용하지 않은 대상을 우선 정리하는 정책, 감시자는 상태·시간·자원을 추적하는 구성요소입니다.
임베딩 / RAG임베딩은 내용을 숫자 벡터로 표현하는 일, RAG는 관련 자료를 찾아 모델 입력에 더하는 구성 방식입니다.
MCP / 도구 호출MCP는 도구·자원을 연결하는 프로토콜이고, 도구 호출은 모델이 구조화된 실행 요청을 내는 기능입니다. 실제 실행 권한은 별도 문제입니다.
OCI / 다이제스트OCI는 컨테이너 이미지 등을 유통하는 표준 체계입니다. 다이제스트는 특정 배포 내용의 해시 식별자이며 가변 태그와 다릅니다.
상태 확인 / 준비 확인프로세스가 살아 있는지와 실제 요청을 받을 준비가 됐는지는 다릅니다. 최초 모델 준비 시간도 고려해야 합니다.
용어를 연결하는 연습

“문맥 길이를 늘렸더니 로딩 중 OOM이 났다”를 자기 말로 풀어 보세요. “더 긴 입력을 처리하도록 설정하니 모델 가중치 외에 보관하거나 계산할 공간이 늘었고, 사용할 메모리를 넘었을 수 있다”가 됩니다. 용어를 원인과 연결하면 로그가 읽히기 시작합니다.

11참고 링크

공식 안내와 커밋에 고정된 소스를 함께 참고하기

11-1. 공식 저장소 · 문서

11-2. 핵심 소스와 의존성

11-3. 관련 기술 공식 자료

현재 상태 (2026-10-23 확인)

항목상태·출처
저장소 / 선정mudler/LocalAI · GitHub Trending Go Weekly #4
스타 / 포크49,866 / 4,533 · 2026-10-23 GitHub 기준
생성일2023-03-18 · GitHub 기준
주 언어Go · GitHub 분류. UI·백엔드 연결에는 다른 언어도 사용
최신 릴리스v4.12.0 · 2026-10-22 공개 · GitHub 기준
최근 push2026-10-23 · GitHub 기준
최근 커밋f1ff707 · 2026-10-23
feat(addons): list and install backends from a library (#12816)
코드 규모Go 약 79.9만 줄, JavaScript/JSX 약 13.8만 줄. 테스트·자료·생성본을 포함한 확장자별 물리 줄 수는 아래 표 참고
라이선스루트 LICENSE는 MIT. 모델 가중치·엔진·의존성의 라이선스는 각각 확인 필요

11-4. 코드 규모를 숫자 그대로 해석하지 않기

다음은 커밋 f1ff707의 Git 추적 일반 파일을 확장자로 분류한 물리적 줄 수입니다. 주석·빈 줄·테스트·추적된 자료를 포함하며, 심볼릭 링크·서브모듈과 별도로 다운로드하는 모델 가중치·외부 엔진 전체 소스는 제외합니다. 추적된 일반 파일은 총 5,580개입니다.

언어·형식파일 수물리 줄 수
Go2,660799,184
JavaScript / JSX827137,610
Python14033,441
C / C++ / 헤더11330,746
Rust4929
Protocol Buffers21,717
CSS8642,001
HTML7919,967
YAML221283,891

Go 중 _test.go는 1,408개·495,882줄입니다. 특히 tests/e2e-aio/sample_data_test.go 하나가 240,009줄이라 총량을 크게 부풀립니다. 테스트와 swagger/docs.go의 15,246줄을 제외하면 참고값은 288,056줄입니다. 이 값도 엄밀한 실행 코드 줄 수는 아닙니다.

React UI 경로만의 JavaScript/JSX 파일은 616개·108,628줄입니다. Python의 *_pb2.py·*_pb2_grpc.py 생성본은 Git 추적 파일에 없으며, 백엔드 준비 과정에서 생성됩니다. YAML의 큰 비중은 모델·백엔드 목록과 설정 자료의 규모를 보여 줍니다. 파일 확장자로 센 양과 직접 작성한 프로그램 로직의 양을 구분하세요.

쓰기 전에 알아둘 한계
지원·호환·로컬이라는 말에는 설정과 범위가 붙는다

시점: 소스 기준은 커밋 f1ff707입니다. v4.12.0과 그 뒤 개발 상태의 차이, 가변 이미지 태그, 미고정 Python 의존성이 있으므로 다른 설치 결과와 다를 수 있습니다. 트렌딩 순위와 스타는 성능·안정성을 입증하는 지표가 아닙니다.

기능·성능: API 경로와 RPC 정의가 있어도 모델·백엔드가 모두 구현한 것은 아닙니다. 특정 장치의 처리량·메모리·정확도는 해당 조합에서 측정해야 합니다. 취소도 경로별로 읽어야 하며, 로더의 로딩 채널 대기처럼 스트림 취소와 다른 대기 지점이 있습니다.

외부 통신: 로컬 추론은 구성 선택입니다. 클라우드 프록시, 원격 도구, 모델·백엔드 다운로드의 통신 대상은 따로 확인하세요. README의 프라이버시 설명이 모든 부가 기능의 오프라인 동작을 보장하지는 않습니다.

권한·배포: 사용자 인증은 기본 비활성이고, 인증 DB와 정적 API 키가 모두 없으면 인증 미들웨어는 통과합니다. 정적 키는 관리자 권한으로 취급됩니다. 기본 API 바인드는 :8080이며 공개 주소의 무인증 실행을 제한하는 검사도 있지만, 로컬·사설망 바인드까지 인증을 대신해 주지는 않습니다.

학습 범위: 이 글의 중심은 채팅 요청과 엔진 연결입니다. 에이전트의 도구 권한, 분산 상태 일관성, 음성·영상의 실시간 처리 전체를 같은 경로로 일반화하지 마세요. 필요할 때 해당 구현과 공식 문서로 범위를 넓히면 됩니다.