샘플

키를 넣고 아래 순서대로 실행해 보면 API가 어떻게 답하는지 바로 볼 수 있다. 요청은 이 브라우저에서 llm.kephas.kr로 바로 가고, 넣은 키는 이 브라우저에만 저장된다.

각 단계는 하는 일의 설명, 같은 요청을 터미널에서 보내는 curl 명령, 이 페이지에서 실행하는 칸, 그 결과 순서로 놓여 있다. 이 페이지에서 보낸 요청도 프로그램에서 보낸 요청과 똑같이 처리되고, 그 키의 사용 기록(요청 수·토큰 수·모델·시각)에 남는다. 서버는 프롬프트와 응답 본문을 저장하지 않는다.

1. 키 넣기

키는 sk-로 시작하는 문자열이다. 키가 없으면 사내망에 연결된 PC에서 키 발급·관리 페이지를 열어 직접 발급한다. 이름(용도)에 쓰는 사람과 용도(예: "홍길동 RAG 실험")를 적고, 허용 범위(보통 member)와 만료(기본 90일)를 정해 [발급]을 누르면 키 전체가 한 번만 보인다. 그 키를 바로 복사해 안전한 곳에 옮겨 두고 아래 칸에 넣는다. 공인 주소로 연 키 발급·관리 페이지에서는 발급이 막히고, 사내에서 직접 여는 주소가 안내된다. 발급한 키는 사내든 외부든 이 페이지에서 그대로 쓴다. 자세한 발급 순서는 API 설명의 「받는 법」에 있다.

아래 칸에 키를 넣으면 이 브라우저의 저장소(localStorage)에 저장되어, 다음에 이 페이지를 열어도 그대로 남아 있다. 키는 이 페이지가 보내는 요청의 Authorization: Bearer 헤더로만 서버에 간다. 칸을 비우면 저장된 키도 지워진다. 여러 사람이 함께 쓰는 컴퓨터라면 다 쓴 뒤 칸을 비운다.

터미널에서 따라 하려면 먼저 주소와 키를 환경변수로 둔다. 아래 curl 명령은 모두 bash 기준이다(Windows에서는 Git Bash나 WSL에서 그대로 쓸 수 있다). openai 라이브러리(Python·Node)도 같은 두 환경변수를 읽는다.

export OPENAI_BASE_URL=https://llm.kephas.kr/v1
export OPENAI_API_KEY=<키>

2. 모델 목록

GET /v1/models는 이 키로 쓸 수 있는 모델만 돌려준다. 키의 허용 범위(member, developer, 모델 직접 지정)에 따라 목록이 다르다. 각 모델에는 OpenAI 형식의 필드(id, object, created, owned_by)에 더해 등급(grade), 최대 컨텍스트 토큰 수(ctx), 임베딩 차원(dim, 임베딩 모델만), 도구 호출 지원 여부(tools), 언어(langs), 짧은 설명(note)이 붙는다. 같은 모델이 여러 서버에 있어도 목록에는 한 번만 나온다. 아래 표는 이름과 덧붙은 필드를 보여 준다.

키가 틀리면 401과 함께 키가 유효하지 않다가 온다. 정지된 키는 메시지에 Key is blocked가, 만료된 키는 Expired Key가 함께 들어 있다. 목록이 비어 있으면 이 키로 쓸 수 있는 모델이 없다는 뜻이다. 사내망에서 연 키 발급·관리 페이지의 목록에서 이 키의 허용 범위를 확인하고, 필요하면 맞는 범위로 새 키를 발급한다.

curl $OPENAI_BASE_URL/models -H "Authorization: Bearer $OPENAI_API_KEY"
등급이름ctxdimtoolslangsnote
아직 불러오지 않았다.

3. 등급으로 고르기

모델 이름을 몰라도 등급으로 고를 수 있다. 등급은 종류 한 글자와 품질 한 글자로 되어 있다. 종류는 C(대화·추론·문서 작성), K(코드 생성·수정), E(임베딩, 검색·RAG), V(이미지 입력)이고, 품질은 1(사내 최상, 느릴 수 있음, 긴 컨텍스트), 2(범용, 일상 업무의 기본값), 3(경량, 분류·추출·테스트용, 복잡한 추론에는 맞지 않음), X(실험, 검증 전이라 예고 없이 바뀌거나 사라질 수 있음)다. 품질 숫자는 내부 성능 측정으로 정하며, 지금 붙어 있는 등급은 측정 전 값이라 바뀔 수 있다.

GET /v1/pick은 조건에 맞는 모델 하나를 골라 이름과 제원을 돌려준다. 아래 칸이 보내는 인자는 다음과 같다.

고르는 순서는 이렇다. 조건에 맞는 모델을 모으고, 그중 이 키로 쓸 수 있는 것만 남기고, 품질 숫자가 작은 것을 앞에 두고, 품질이 같으면 레지스트리의 기본(default) 모델을, 그다음은 등록된 순서를 따른다. 응답의 reason은 그 품질의 후보가 하나뿐이었으면 best-quality, 기본 모델이라 골랐으면 default, 등록 순서로 골랐으면 first-registered다. 고른 모델의 이름은 아래 4. 대화의 model 칸에 자동으로 들어간다.

조건에 맞는 모델이 하나도 없으면 404, 맞는 모델은 있지만 이 키로 쓸 수 없으면 403이 온다. 둘 다 type은 model_pick_failed다. 인자가 틀리면(예: max_quality=5) 400과 함께 무엇이 틀렸는지 알려 준다.

curl "$OPENAI_BASE_URL/pick?grade=C&max_quality=2" -H "Authorization: Bearer $OPENAI_API_KEY"

4. 대화

POST /v1/chat/completions는 OpenAI의 Chat Completions와 같다. messages, stream, max_tokens, temperature 같은 인자를 그대로 쓴다. model에는 실제 모델 이름(예: qwen3-8b)이나 등급(grade:C2)을 쓴다. 등급을 쓰면 호출할 때마다 3.과 같은 규칙으로 모델을 골라 부르고, 고른 모델을 응답 헤더 x-kephas-picked-model과 x-kephas-picked-grade로 알려 준다. 응답 본문의 model에도 실제 모델 이름이 들어 있다. 조건은 grade:C?max_quality=2&tools=true처럼 뒤에 붙인다.

이 페이지는 "stream": true로 보낸다. 그러면 응답은 data: {...} 줄이 이어지는 SSE(server-sent events)로 오고, 마지막 줄은 data: [DONE]이다. 이 페이지는 그 줄들을 읽어 본문을 이어 붙인다. 생각(reasoning) 모델(예: gpt-oss-120b, qwen3-8b)은 본문 앞에 생각 과정을 reasoning_content로 먼저 보내는데, 결과에는 옅은 글씨로 따로 보인다. 응답을 받는 중에 [중단]을 누르면 연결을 끊는다. 결과 맨 끝에는 받은 본문 조각 수와 걸린 시간이 나온다. curl에서는 -N을 붙여야 줄이 오는 대로 보인다. model 칸과 프롬프트는 이 브라우저에 저장된다.

한동안 쓰이지 않은 모델은 처음 부를 때 서버가 메모리에 올리느라 첫 응답까지 수십 초 걸릴 수 있고, 이어지는 호출은 빠르다. 점검이나 엔진 전환 중에는 일부 모델이 503을 돌려주니 잠시 뒤 다시 보낸다. 다른 모델로 몰래 바꿔 부르지 않는다. 응답이 200으로 시작한 뒤 모델 쪽에서 오류가 나면 스트림 안에 error가 오고, 결과에 빨간 글씨로 보인다. 본문 없이 생각만 하다 끝났다면 max_tokens가 작은 것이니 넉넉히 주거나 지정하지 않는다.

curl -N $OPENAI_BASE_URL/chat/completions \
  -H "Authorization: Bearer $OPENAI_API_KEY" -H "Content-Type: application/json" \
  -d '{"model":"grade:C2","stream":true,"messages":[{"role":"user","content":"안녕하세요. 한 문장으로 자기소개를 해 주세요."}]}'

5. 임베딩

POST /v1/embeddings는 문장을 숫자 벡터로 바꾼다. 검색이나 RAG에서 문장 사이의 거리를 잴 때 쓴다. input은 문자열 하나 또는 문자열 배열이고, 응답의 data[].embedding에 벡터가 담긴다. 벡터의 길이(차원)는 모델마다 정해져 있고 2. 모델 목록의 dim과 같다. 이 페이지는 model로 grade:E를 보내 이 키로 쓸 수 있는 임베딩 모델 중 품질이 가장 좋은 것을 고르게 하고, 응답의 model, 차원, 벡터의 앞 8개 값을 보여 준다.

임베딩에는 E 등급만 쓸 수 있다. 예를 들어 grade:C를 보내면 400과 함께 임베딩에는 E 등급만 쓸 수 있다(요청: grade:C)가 온다. 입력 길이는 그 모델의 ctx 이하여야 한다.

curl $OPENAI_BASE_URL/embeddings \
  -H "Authorization: Bearer $OPENAI_API_KEY" -H "Content-Type: application/json" \
  -d '{"model":"grade:E","input":"사내 LLM 레지스트리"}'

6. 같은 호출을 코드로

위에서 해 본 호출을 프로그램에서 하는 코드다. openai 라이브러리(Python·Node)와 LangChain(langchain-openai)은 Base URL과 키만 바꾸면 그대로 쓴다. 전용 SDK는 아직 없다. 아래 코드에는 4. 대화의 model 칸 값이 들어가고, 칸을 바꾸면 코드도 따라 바뀐다. <키> 자리에 키를 넣거나, 1.에서처럼 환경변수 OPENAI_BASE_URL·OPENAI_API_KEY를 두고 코드에서는 빼도 된다.

서비스 코드에서는 호출마다 grade:를 쓰기보다, 시작할 때 3.의 pick으로 한 번 골라 그 이름을 로그에 남기고 그 이름으로 부르기를 권한다. 등급으로 부르면 호출마다 다시 고르므로 도중에 모델이 바뀔 수 있다. 키는 코드·저장소·문서에 넣지 말고 환경변수로 준다.


엔드포인트별 인자와 응답, 오류의 정확한 명세는 API 레퍼런스에 있고, 클라이언트 설정과 오류가 났을 때 할 일은 API 설명에 있다.