OpenAI 호환 엔드포인트 서빙
엔진을 한 번 띄워 챗 창으로 대화하면 장난감입니다. HTTP로, 모두가 이미 말하는 형태로 서빙하면 인프라입니다. 에디터, UI, 에이전트, 당신의 스크립트가 전부 같은 로컬 모델을 몰 수 있습니다. 이 페이지는 로컬 엔드포인트를 띄우고, 이어서 스트리밍, SDK, 보안, 웜 로딩을 다룹니다.
OpenAI 형태가 이긴 이유
chat-completions 요청 포맷 — 모델 이름, {role, content} 메시지 목록, 노브 몇 개 — 은 모든 도구 저자가 가장 먼저 클라이언트를 썼기 때문에 사실상 표준이 되었습니다. 그 회사를 어떻게 생각하든, 형태 자체는 중립입니다. 그냥 POST 위의 JSON입니다. 실용적 귀결: 이것을 흉내 내는 어떤 로컬 서버도 방대한 기존 소프트웨어 더미와 즉시 호환됩니다. 엔진 선택과 클라이언트 선택이 독립적인 결정이 됩니다(엔진 참고).
1분 안에 llama-server
llama-server -m model.gguf -c 8192 --port 8080
# UI는 http://127.0.0.1:8080 — API는 /v1/chat/completions
주석 달린 요청:
curl http://127.0.0.1:8080/v1/chat/completions -H "Content-Type: application/json" -d '{"model":"model","messages":[{"role":"user","content":"Why run local? One sentence."}],"temperature":0.7,"max_tokens":60}'
model— llama-server는 띄운 것을 서빙합니다. 이 필드는 종종 무시되지만, 제정신인 이름을 보내세요.messages— 대화 전체, 시스템 프롬프트가 먼저. 상태 없는 서버이므로 기록은 당신이 쥡니다.max_tokens— 응답의 상한. 폭주하는 생성으로부터 당신을 지킵니다.
응답(다듬은 것)은 답과 함께 공짜 디버깅에 쓸 토큰 회계를 싣습니다:
{
"choices": [
{ "message": { "role": "assistant", "content": "No accounts, no rate limits, and your data never leaves the machine." } }
],
"usage": { "prompt_tokens": 14, "completion_tokens": 15 }
}
usage는 당신의 프롬프트가 실제로 컨텍스트에서 얼마를 쓰는지 알려줍니다 — 모델을 "잊어버렸다"고 탓하기 전에 확인하세요(토큰 참고).
스트리밍
"stream": true를 더하면 서버는 서버 센트 이벤트로 전환합니다. 줄로 구분된 data: 청크, 각각 작은 델타를 싣고, 센티넬로 끝납니다.
data: {"choices":[{"delta":{"content":"No "}}]}
data: {"choices":[{"delta":{"content":"accounts"}}]}
data: [DONE]
클라이언트 쪽: [DONE] 라인까지 delta.content를 쌓으세요. 스트리밍은 생성을 빠르게 하지 않습니다 — 기다림을 견딜 수 있게 만듭니다. 챗 UX에는 같은 것입니다(속도 참고).
Ollama: 서비스 하나, API 둘
Ollama의 네이티브 API는 살짝 더 풍부합니다 — 요청별 옵션과 모델 수명 주기 제어:
curl http://127.0.0.1:11434/api/chat -d '{"model":"qwen3:8b","messages":[{"role":"user","content":"hi"}],"stream":false,"keep_alive":-1}'
/v1/chat/completions의 OpenAI 호환 경로도 함께 서빙하므로, OpenAI 모양의 클라이언트가 수정 없이 Ollama에 동작합니다. keep_alive나 요청별 엔진 옵션을 원하면 네이티브 API, 나머지 전부에는 /v1을 쓰세요.
GPU 서버를 위한 vLLM
vllm serve Qwen/Qwen3-8B --max-model-len 16384 --port 8000
vLLM은 safetensors 가중치를 가져와 한 번 로드하고, 밑에서 연속 배칭이 도는 같은 OpenAI 형태를 서빙합니다 — 동시 사용자가 나타나면 그때의 올바른 도구입니다(동시성 참고). NVIDIA VRAM과 제대로 된 GPU를 원합니다. 1인용 데스크톱에는 손댈 필요 없습니다.
공식 SDK를 localhost로 향하게 하기
공식 OpenAI 라이브러리는 그냥 HTTP 클라이언트입니다. 베이스 URL을 바꾸면 API 키 필드는 장식이 됩니다:
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:8080/v1", api_key="local")
r = client.chat.completions.create(
model="qwen3-8b",
messages=[{"role": "user", "content": "Why run local?"}],
)
print(r.choices[0].message.content)
import OpenAI from "openai";
const client = new OpenAI({ baseURL: "http://127.0.0.1:11434/v1", apiKey: "ollama" });
const r = await client.chat.completions.create({
model: "qwen3:8b",
messages: [{ role: "user", content: "Why run local?" }],
});
서버에 키가 설정되지 않았다면 어떤 문자열이든 동작합니다. 이 베이스-URL 교체가 "로컬 모델과 동작하는" 모든 도구 뒤의 트릭 전부입니다.
이것이 여는 것들
- 에디터와 챗 UI — UI와 통합 참고.
- 당신의 앱: 자기 백엔드 뒤의 로컬 모델, 토큰당 비용 제로.
- 스키마가 보장되는 스크립트 파이프라인 — 구조화 출력 참고.
- 도구를 호출하고 반복하는 에이전트 루프 — 에이전트 참고.
- 이 포럼의 게임 코너: 체스와 장기 에이전트는 위 curl과 똑같이 로컬 chat-completions 엔드포인트를 두드리며 플레이합니다.
문을 잠가세요
모델 서버는 포트에 닿을 수 있는 모든 사람에게 공짜 LLM입니다. 기본값은 안전합니다 — llama-server와 Ollama는 127.0.0.1에 바인딩합니다 — 노트북에서는 그렇게 유지하세요. LAN에 노출하려면: API 키를 설정하고(--api-key는 llama-server와 vLLM에서, 앞단의 어떤 UI든 인증을), TLS에는 리버스 프록시를 선호하세요. 인증 없이 0.0.0.0을 바인딩하지 마세요. 네트워크의 누구나 당신 GPU를 태우고, 컨텍스트를 채우고, 설정을 탐색할 수 있습니다.
따뜻하게 유지하기
콜드 로드 — 디스크에서 VRAM으로 가중치 — 는 TTFT에 수 초에서 수십 초를 치릅니다. llama-server와 vLLM은 시작 시 로드하고 상주하므로 첫 요청만 지불합니다. Ollama는 keep_alive(기본 ~5분) 뒤 언로드합니다. 모델을 무기한 유지하려면 "keep_alive": -1을, 버스트 직후 VRAM을 풀려면 0을 보내세요. 설정이 "처음 느리고 이후 괜찮다"고 느껴지면 그것은 느린 모델이 아니라 콜드 로드입니다 — 그리고 부하 상황에서 로드 폭풍은 실재하는 실패 양상입니다(동시성 참고).