일반적인 프레임워크에서 중계 API 설정: SDK부터 Dify까지
중계 API 연동의 핵심은 세 가지 값입니다: 엔드포인트 주소, API 키, 모델명. 어려운 점은 각 프레임워크가 이 세 가지 값에 서로 다른 이름을 부여한다는 것입니다. 어떤 것은 base_url, 어떤 것은 api_base라고 부르며, Dify에서는 양식 필드가 됩니다. 이 문서에서는 이를 대조하여 나열하며, 모든 코드 예제는 바로 실행 가능합니다. 마지막에는 오류를 단계별로 진단하는 체크리스트를 제공합니다.
핵심 요약
- 주소와 API 키를 환경 변수 API_BASE와 API_KEY에 통일하여 저장하고, 코드 내에서는 평문을 피하세요.
- 엔드포인트 주소는 /v1 접미사를 포함해야 하며, 모델명은 uncensored로 고정해야 합니다.
- LangChain은 base_url을 사용하고, LlamaIndex의 OpenAILike는 api_base를 사용합니다. 이름은 다르지만 의미는 동일합니다.
- Dify에서 'OpenAI-API-compatible' 공급자를 선택하고, 모델명, 주소, 컨텍스트 창 길이를 수동으로 입력하면 됩니다.
세 가지 값을 먼저 준비하세요
어떤 프레임워크를 사용하든, 먼저 이 세 가지 항목을 확보해야 합니다. 이후의 설정은 단순히 값을 채우는 작업입니다.
- 엔드포인트 주소:
https://api.llmzhongzhuan.com/v1. 끝에 있는/v1은 유지해야 하지만,/chat/completions은 중복으로 추가하지 마세요. SDK가 자동으로 연결합니다. - API 키: API 키 발급 페이지에서 이메일과 비밀번호로 회원가입하면 즉시 표시됩니다. 계정당 하나이며, 재설정 시 이전 키는 즉시 무효화됩니다.
- 모델명: 단 하나,
uncensored입니다.GET /v1/models를 통해 직접 확인할 수 있습니다.
추가로 다음 한계를 기억하세요: 컨텍스트 총 길이는 100,000 토큰이며, 단일 max_tokens은 기본값 2048, 최대 32,000입니다. 각 키당 분당 300회 요청이 허용됩니다. 이러한 수치는 이후 매개변수 설정에서 자주 사용됩니다.
환경 변수 작성 방법
API 키를 코드에 하드코딩하는 것은 가장 흔한 사고 원인입니다. 권장 방법은 환경 변수만 읽는 것이며, 배포 시 컨테이너나 키 관리 시스템이 주입하도록 합니다. 아래 예제에서는 API_KEY와 API_BASE 두 변수를 사용하며, 각 프레임워크 예제에서도 이 이름을 따릅니다.
# Linux / macOS:写进 ~/.zshrc 或 .env 加载脚本
export API_KEY="替换为你的密钥"
export API_BASE="https://api.llmzhongzhuan.com/v1"
# Windows PowerShell(仅当前会话)
$env:API_KEY = "替换为你的密钥"
$env:API_BASE = "https://api.llmzhongzhuan.com/v1"
.env 파일을 사용하는 경우, 반드시 .gitignore에 추가하세요. 또한 공식 Python SDK는 기본적으로 OPENAI_API_KEY와 OPENAI_BASE_URL을 읽습니다. 이 변수명을 계속 사용하거나, 아래처럼 생성자에서 명시적으로 매개변수를 전달할 수 있습니다. 명시적 전달의 장점은 시스템에 남아 있는 이전 변수의 간섭을 받지 않아 디버깅 시 변수를 하나 줄일 수 있다는 것입니다.
OpenAI Python SDK 및 Node SDK
Python에는 v1 이상의 openai 패키지가 필요하며, Node에는 v4 이상이 필요합니다. 두 언어의 차이는 구문뿐이며, 클라이언트 생성 시 주소와 API 키를 전달하면 됩니다. 먼저 Python의 비스트리밍 호출 예제를 확인하고, usage를 출력하여 비용 차감을 확인하세요:
import os
from openai import OpenAI
client = OpenAI(
base_url=os.environ.get("API_BASE", "https://api.llmzhongzhuan.com/v1"),
api_key=os.environ["API_KEY"],
)
resp = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": "写一条 20 字以内的发布公告标题。"}],
max_tokens=100,
)
print(resp.choices[0].message.content)
print(resp.usage)
Node 예제는 스트리밍 출력을 사용합니다. 이는 프론트엔드 타이핑 효과에서 가장 흔히 사용되는 방식입니다. 스트리밍 요청이 종료되면 서버는 자동으로 usage가 포함된 청크를 보내므로 추가 매개변수가 필요하지 않습니다:
import OpenAI from "openai";
const client = new OpenAI({
baseURL: process.env.API_BASE ?? "https://api.llmzhongzhuan.com/v1",
apiKey: process.env.API_KEY,
});
const stream = await client.chat.completions.create({
model: "uncensored",
messages: [{ role: "user", content: "列出三个命名服务端日志字段的好习惯。" }],
stream: true,
max_tokens: 300,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
process.stdout.write("\n");
Node 예제는 최상위 await를 사용하므로, .mjs 파일을 사용하거나 package.json에서 "type": "module"을 설정해야 합니다. 런타임이 이를 지원하지 않으면 async 함수로 감싸면 됩니다.
LangChain: ChatOpenAI의 base_url
LangChain에서는 별도의 어댑터가 필요하지 않습니다. langchain_openai 패키지의 ChatOpenAI을 사용하고, base_url을 지정하기만 하면 됩니다.
import os
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="uncensored",
base_url=os.environ.get("API_BASE", "https://api.llmzhongzhuan.com/v1"),
api_key=os.environ["API_KEY"],
temperature=0.7,
max_tokens=500,
timeout=60,
)
print(llm.invoke("把“服务已降级”改写成对用户友好的一句话。").content)
두 가지 작은 팁입니다. 첫째, max_tokens과 timeout은 명시적으로 설정하는 것이 좋습니다. 기본값이 비즈니스 요구사항에 맞지 않을 수 있습니다. 둘째, 체인에서 함수 호출을 사용하는 경우, 본 사이트는 OpenAI 형식의 tools를 지원하므로 LangChain의 bind_tools을 정상적으로 사용할 수 있습니다. 스트리밍 시에는 stream() 내에서 청크 단위로 소비하면 됩니다.
LlamaIndex: OpenAILike
LlamaIndex의 기본 OpenAI 클래스는 모델명이 공식 목록에 있는지 확인하며, 사용자 정의 모델명에서는 오류를 발생시킵니다. 이때에는 llama-index-llms-openai-like의 OpenAILike로 전환해야 합니다. 이 클래스는 검사를 수행하지 않으며, 매개변수 이름도 다릅니다. 주소는 api_base라고 부릅니다.
import os
from llama_index.llms.openai_like import OpenAILike
llm = OpenAILike(
model="uncensored",
api_base=os.environ.get("API_BASE", "https://api.llmzhongzhuan.com/v1"),
api_key=os.environ["API_KEY"],
is_chat_model=True,
context_window=100000,
max_tokens=500,
)
print(llm.complete("用一句话解释什么是幂等请求。"))
is_chat_model=True 설정이 중요합니다. 이는 LlamaIndex가 구식 텍스트 생성이 아닌 대화형 엔드포인트를 사용하도록 합니다. context_window을 100000으로 설정하면 LlamaIndex가 컨텍스트를 분할할 때 문서가 기본 몇천 토큰으로 잘리는 것을 방지합니다.
Dify: OpenAI-API-compatible 공급자
Dify와 같은 시각화 플랫폼은 코드가 아닌 양식을 통해 구성됩니다. 버전마다 UI 텍스트가 약간 다를 수 있지만 단계는 기본적으로 동일합니다:
- '설정'으로 이동하여 '모델 공급자' 페이지를 찾은 후, 목록에서 'OpenAI-API-compatible'을 선택하고 '모델 추가'를 클릭합니다.
- 모델 유형은 'LLM'으로 선택하고, 모델 이름에는
uncensored를 입력합니다. - API Key에는 API 키를 입력하고, API endpoint URL에는
https://api.llmzhongzhuan.com/v1를 입력합니다. - 모델 컨텍스트 창 길이는 100000, 최대 토큰 한도는 32000으로 입력합니다.
- 워크플로우에서 함수 호출을 사용하려면 함수 호출 지원 옵션을 활성화하고, 스트리밍 출력을 켜둡니다.
- 저장 후 간단한 채팅 애플리케이션을 생성하고, 새로 추가한 모델을 선택하여 문장을 입력하여 정상적으로 반환되는지 확인합니다.
플랫폼은 저장 시 탐지 요청을 보낼 수 있습니다. 이 단계가 실패하면 주소에 경로가 중복되어 있거나 API 키 앞뒤에 공백이 있을 가능성이 큽니다. Dify가 컨테이너에서 실행되는 경우, 컨테이너가 외부 도메인에 접근할 수 있는지 확인해야 합니다.
출시 전 확인할 매개변수 및 배포 방법
프레임워크가 작동하는 것은 첫 단계일 뿐입니다. 아래 매개변수들은 출시 전에 하나씩 확인해야 하며, 이는 비용과 실패율에 영향을 미칩니다.
- max_tokens: 기본값은 2048이며, 긴 텍스트 출력이 필요하면 명시적으로 높여야 합니다. 최대 한도는 32,000입니다. 입력과 출력을 합쳐 100,000을 초과하면 400 오류가 발생합니다.
- timeout: 긴 출력 요청은 시간이 더 오래 걸립니다. 스트리밍 상황에서는 읽기 타임아웃을 60초 이상으로 설정하는 것이 좋으며, 비스트리밍의 경우 최대 출력 길이를 기준으로 추정합니다.
- temperature / top_p / stop: 이러한 표준 샘플링 매개변수는 전달되며, 프레임워크에서 설정한 값이 바로 적용되므로 추가 스위치가 필요하지 않습니다.
- 동시 요청: 각 API 키당 분당 300개의 요청이 허용됩니다. 여러 서비스 인스턴스가 동일한 API 키를 공유할 경우 속도 제한은 합산되므로, 각 인스턴스를 300개로 계획하지 마세요.
- 재시도: 프레임워크의 기본 재시도는 주로 네트워크 오류에만 적용됩니다. 429 및 503 오류에는 지수 백오프를 직접 추가해야 합니다. 방법은 '안정성' 문서에서 확인하세요.
컨테이너에 배포할 때도 '이미지에 API 키를 포함하지 않는다'는 원칙은 동일합니다. 오케스트레이션 도구가 시작 시 주입합니다. 아래는 compose 파일의 최소 작성 예시입니다:
# docker-compose.yml 片段
services:
app:
image: your-app:latest
environment:
API_BASE: https://api.llmzhongzhuan.com/v1
API_KEY: ${API_KEY} # 从宿主机环境或 .env 读取,不写进镜像
Kubernetes에서는 Secret 참조로 전환하면 되며 원칙은 동일합니다. 또한 다른 환경별로 다른 계정이나 API 키를 준비하는 것이 좋습니다. 예를 들어 개발, 스테이징, 프로덕션 환경을 각각 분리하면, 한 환경의 API 키 유출이나 잔액 고갈이 프로덕션에 영향을 미치지 않습니다. 각 계정에는 하나의 API 키만 있으므로, 다중 환경에는 여러 계정을 매칭하고 각 계정에 적절한 잔액을 미리 충전하는 것이 좋습니다.
마지막으로 로그입니다. 많은 프레임워크는 디버그 모드에서 전체 요청 헤더를 출력하며, 여기에는 Authorization이 포함됩니다. 프로덕션 환경에서는 이러한 디버그 출력을 반드시 끄거나, 로그 필터에서 API 키를 마스킹해야 합니다. usage 필드를 기록하는 것은 좋은 습관입니다. 이를 통해 청구 내역을 대조할 수 있으며, 특정 기능의 프롬프트가 갑자기 길어지는 것을 조기에 발견할 수 있습니다.
오류 발생 시 진단 순서
연동 단계의 문제의 90%는 몇 가지 지점에 집중되어 있으므로, 아래 순서대로 확인하는 것이 가장 빠릅니다:
- 401: API 키가 비어 있거나, 복사 시 공백이 포함되었거나, 재설정 후에도 이전 키를 사용하고 있습니다.
- 404: 엔드포인트가
/v1없는 루트 경로로 작성되었거나,/chat/completions이 중복으로 추가되었습니다. - 402: 오류 코드가 no_credit인 경우, 잔액이 소진되었거나 무료 체험 크레딧이 만료되었음을 의미합니다. 선불 크레딧을 충전해야 합니다.
- 400: 입력값에
max_tokens이 100,000을 초과하거나 요청 본문이 8 MB를 초과한 것이 일반적인 원인입니다. - 429 / 503: 전자는 분당 300회의 속도 제한을 나타내며, 후자는 upstream_busy 상태입니다. 잠시 후 다시 시도하고, 자세한 방법은 안정성 실전 가이드를 참조하세요.
또 다른 간과하기 쉬운 문제는 네트워크 환경입니다. 사내 네트워크, 프록시 소프트웨어 또는 보안 그룹 규칙으로 인해 도메인 이름은 해결되지만 HTTPS 연결이 수립되지 않을 수 있습니다. 이는 명확한 오류 코드보다는 긴 시간 동안 응답이 없다가 타임아웃으로 이어지는 형태로 나타납니다. 이런 상황이 발생하면 먼저 동일한 머신에서 curl을 사용하여 /v1/models 엔드포인트에 접속해 보세요. 연결이 성공하면 문제가 애플리케이션 계층에 있음을 의미하며, 연결이 실패하면 프록시 및 방화벽 설정을 확인해야 합니다. 이러한 문제 해결 과정을 팀 문서에 기록해 두면, 다음에 신규 구성원이 연동할 때 불필요한 시행착오를 줄일 수 있습니다.
세 값이 모두 정확함에도 오류가 발생하면, 먼저 curl로 직접 요청하여 프레임워크 자체의 문제를 배제하세요. API 엔드포인트가 실제로 무엇을 하는지 먼저 이해하고 싶다면 API 엔드포인트의 작동 원리 문서를 참조하세요.
자주 묻는 질문
base_url에 /v1을 반드시 포함해야 하나요?
포함해야 합니다. https://api.llmzhongzhuan.com/v1으로 설정하면 SDK가 자동으로 뒤에 /chat/completions을 붙이므로, 중복으로 추가하지 마세요.
LlamaIndex에서는 왜 OpenAI 대신 OpenAILike를 사용해야 하나요?
OpenAI 클래스는 모델 이름이 공식 목록에 속하는지 확인하며, 사용자 정의 모델 이름은 오류를 발생시킵니다. OpenAILike는 검증을 수행하지 않으므로 호환되는 엔드포인트 연동에 더 적합합니다.
Dify에서 입력하는 모델 이름은 자유롭게 지어도 되나요?
안 됩니다. 모델명은 반드시 uncensored여야 합니다. 요청 시 해당 ID가 그대로 전달되기 때문입니다. 표시 이름은 별도로 정할 수 있지만, 모델 필드는 정확히 일치해야 합니다.
환경 변수를 수정했는데 왜 적용되지 않나요?
대부분 터미널이나 프로세스가 재시작되지 않아서 발생합니다. 코드에서 base_url과 api_key를 명시적으로 전달하고, 실제로 사용되는 주소를 출력하여 확인하는 것을 권장합니다.
양식을 작성하기만 하면 API 키를 받을 수 있습니다
계정을 생성하고, API 키를 복사한 후 Base URL을 수정하세요. 설정은 매우 간단합니다.