KO ▾
API 키 받기

LLM API로 채팅봇 만들기: Python 튜토리얼

Abliterated 모델은 안전 거절 메커니즘이 제거된 대규모 언어 모델로, 모델이 콘텐츠 필터에 부딪히지 않고도 논쟁적, 성인 또는 니즈 질문에 답변할 수 있게 합니다. 이 접근 방식은 개발자에게 모델의 어조와 지식 컷오프에 대한 완전한 제어를 제공하며, 표준 가드레일이 유용한 출력을 차단할 수 있는 코딩, 역할극, 창작 글쓰기에 특히 가치 있습니다.

채팅봇 대화 루프를 나타내는 중앙 큐브 주변으로 이동하는 채팅 메시지 블록

주요 포인트

  • API는 상태가 없으므로 코드가 대화 기록을 관리하고 모든 요청에 보냅니다.
  • 스트리밍, 함수 호출 및 JSON 모드는 모두 표준 OpenAI 매개변수를 사용합니다.
  • 100,000토큰 창에 가까워지기 전에 기록을 자르거나 요약하세요.
  • 입력 토큰 2,000개와 출력 토큰 400개로 구성된 1,000회 채팅의 비용은 약 $0.50입니다.

핵심 아이디어: 채팅봇은 루프와 목록입니다

언어 모델은 요청 간에 아무것도 기억하지 않습니다. POST /v1/chat/completions 호출은 모두 독립적입니다. 기억처럼 보이는 것은 프로그램이 이전 메시지를 다시 보내는 것입니다. 각 메시지에는 system(지시사항용), user(사용자 입력용), assistant(이전 모델 응답용) 역할이 부여됩니다.

따라서 전체 아키텍처는 작습니다. 목록을 유지하세요. 사용자 메시지를 추가합니다. 목록을 보냅니다. 응답을 추가합니다. 반복합니다. 스트리밍부터 도구까지 이 튜토리얼의 모든 것은 이 루프의 변형입니다.

여기의 API는 OpenAI 호환이므로 일반 openai 패키지를 사용합니다. Base URL을 https://api.abliterated.cc/v1로 설정하고 키를 사용하며 모델을 uncensored로 설정하세요. API 키 페이지에서 키를 얻으세요. $0.50 체험 크레딧으로 수백 개의 짧은 테스트 메시지를 보낼 수 있습니다.

단계 1: 작동하는 콘솔 채팅봇

pip install openai로 SDK를 설치하고 다음을 실행하세요.

from openai import OpenAI

client = OpenAI(
    base_url="https://api.abliterated.cc/v1",
    api_key="YOUR_KEY",
)

messages = [
    {"role": "system", "content": "You are a concise, friendly support assistant for a bike shop."}
]

while True:
    text = input("you: ")
    if text.strip() in {"quit", "exit"}:
        break
    messages.append({"role": "user", "content": text})
    resp = client.chat.completions.create(
        model="uncensored",
        messages=messages,
        temperature=0.7,
    )
    answer = resp.choices[0].message.content
    messages.append({"role": "assistant", "content": answer})
    print("bot:", answer)

이것은 완전한 채팅봇입니다. 시스템 메시지는 작업을 설정하고, 목록은 기억을 담으며, temperature은 응답의 다양성을 제어합니다. 사실 기반 봇에는 0.2에 가깝게, 창의적인 봇에는 1.0에 가깝게 설정하세요.

작업을 설정하는 시스템 프롬프트 작성

시스템 메시지는 채팅봇 성능을 개선하는 데 가장 비용이 적게 드는 부분입니다. 간결하고 구체적으로 유지하세요. 역할, 대상, 응답 길이와 어조, 그리고 봇이 모르는 내용을 묻는 경우의 처리 방법을 명시하세요. "당신은 자전거 가게의 고객 질문에 답변합니다. 최대 세 문장으로 답변하세요. 수리 가격 관련 질문이 들어오면 정비사가 자전거를 점검해야 한다고 말하세요."라는 프롬프트는 보편적인 원칙을 나열한 한 페이지 분량의 텍스트보다 더 효과적입니다.

두 가지 습관이 도움이 됩니다. 첫째, 태도가 아닌 행동 형태로 규칙을 작성하세요. "도움이 되세요" 대신 "모호한 요청이 있을 때 확인 질문을 하세요". 둘째, 빈 메시지, 매우 긴 붙여넣기, 제품과 무관한 요청과 같은 적대적 입력으로 테스트하세요. 본 결과를 바탕으로 프롬프트를 수정하고, 해당 입력을 회귀 테스트 목록에 보관하세요.

시스템 메시지는 매 턴마다 재전송되므로, 그 안의 모든 단어는 모든 요청에서 토큰으로 청구됩니다. 400개의 토큰으로 구성된 프롬프트가 5턴씩 총 1,000번의 채팅을 거치면 2,000,000개의 입력 토큰이 추가되며, 이는 2 x $0.15 = $0.30에 해당합니다. 이는 작은 금액이지만, 불필요하게 부풀려진 프롬프트가 대량 사용 시 어떻게 누적되는지 보여줍니다.

단계 2: 응답 스트리밍

전체 답변을 기다리면 채팅 창에서 느리게 느껴집니다. stream: true을 설정하고 도착하는 대로 조각을 출력하세요. 스트림은 서버 전송 이벤트를 사용하며 사용량 블록으로 끝나므로 토큰 수를 로깅할 수 있습니다.

from openai import OpenAI

client = OpenAI(base_url="https://api.abliterated.cc/v1", api_key="YOUR_KEY")
messages = [{"role": "user", "content": "Explain tire pressure in two sentences."}]

stream = client.chat.completions.create(
    model="uncensored",
    messages=messages,
    stream=True,
)
reply = ""
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        part = chunk.choices[0].delta.content
        reply += part
        print(part, end="", flush=True)
    if getattr(chunk, "usage", None):
        print("\ntokens:", chunk.usage.prompt_tokens, chunk.usage.completion_tokens)

조립된 reply를 기록에 추가하는 것을 잊지 마세요. 그렇지 않으면 다음 턴에서 봇이 무엇을 말했는지 알 수 없습니다.

단계 3: 컨텍스트가 커지기 전에 관리

창은 프롬프트와 생성된 텍스트(completion) 사이에 공유되며 용량은 100,000 토큰입니다. 대부분의 채팅봇은 이 한도에 훨씬 못 미치지만, 전체 프롬프트에 대해 매 턴마다 지불하므로 대화 기록이 길어질수록 비용이 증가합니다. 다음 간단한 정책으로 둘을 통제하세요:

  • 시스템 메시지는 항상 유지하세요.
  • 마지막 10~20개 메시지를 그대로 유지하세요.
  • 선택한 크기 제한(예: 추정 토큰 8,000개)을 목록이 초과할 때, "이 대화를 150 단어 이내로 요약하세요"라는 지시와 함께 가장 오래된 블록을 모델에 보내고, 해당 블록을 요약으로 대체하고 시스템 메시지로 넣으세요.

영문자 4자당 1토큰의 대략적인 추정으로 트리거를 설정하기에 충분합니다. 각 응답의 사용량 숫자는 사후에 정확한 수치를 제공하므로 한도를 조정할 수 있습니다.

가장 오래된 메시지를 삭제하여 잘라내면, 사용자 및 어시스턴트 쌍으로 삭제하세요. 질문 없는 응답이나 응답 없는 질문은 모델이 예상보다 더 혼란스러워합니다.

단계 4: 봇에 도구 제공

함수 호출은 모델이 주문 조회와 같은 작업을 수행하도록 코드를 요청할 수 있게 합니다. tools로 함수를 설명하고, 모델은 tool_calls 항목으로 응답합니다. 함수를 실행한 후 결과를 role: "tool" 메시지로 보냅니다.

import json
from openai import OpenAI

client = OpenAI(base_url="https://api.abliterated.cc/v1", api_key="YOUR_KEY")

tools = [{
    "type": "function",
    "function": {
        "name": "get_order_status",
        "description": "Look up the status of an order by its id",
        "parameters": {
            "type": "object",
            "properties": {"order_id": {"type": "string"}},
            "required": ["order_id"],
        },
    },
}]

def get_order_status(order_id):
    return {"order_id": order_id, "status": "shipped"}

messages = [{"role": "user", "content": "Where is order A1042?"}]
resp = client.chat.completions.create(
    model="uncensored", messages=messages, tools=tools, tool_choice="auto"
)
msg = resp.choices[0].message

if msg.tool_calls:
    messages.append(msg)
    for call in msg.tool_calls:
        args = json.loads(call.function.arguments)
        result = get_order_status(**args)
        messages.append({
            "role": "tool",
            "tool_call_id": call.id,
            "content": json.dumps(result),
        })
    final = client.chat.completions.create(
        model="uncensored", messages=messages, tools=tools
    )
    print(final.choices[0].message.content)

부수 효과가 있는 작업을 실행하기 전에 항상 인수를 검증하세요. 모델이 호출을 제안하면, 코드가 실행 여부를 결정합니다.

단계 5: JSON 모드로 구조화된 출력

프로그램의 다음 단계가 텍스트가 아닌 데이터(예: 메시지 분류)가 필요하면 response_format과 함께 JSON 모드를 사용하세요. 프롬프트에 JSON 단어와 예상 키도 명시하세요.

curl https://api.abliterated.cc/v1/chat/completions \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "uncensored",
    "response_format": {"type": "json_object"},
    "messages": [
      {"role": "system", "content": "Reply with JSON: {\"intent\": string, \"urgent\": boolean}"},
      {"role": "user", "content": "My brakes squeal and I ride tomorrow morning."}
    ]
  }'

여전히 json.loads를 try 블록으로 감싸세요. 파싱이 실패하면 오류 텍스트를 추가하여 한 번 더 시도하세요. 실패한 요청은 요금이 청구되지 않습니다.

오류, 비용 및 배포 참고사항

오류, 재시도 및 속도 제한 처리

실제 사용자는 부적절한 시점에 요청을 보내며 네트워크는 실패할 수 있습니다. 호출을 감싸서 하나의 잘못된 응답이 대화를 중단하지 않도록 하세요.

import time
from openai import OpenAI, APIError, RateLimitError

client = OpenAI(base_url="https://api.abliterated.cc/v1", api_key="YOUR_KEY")

def chat(messages, tries=3):
    for attempt in range(tries):
        try:
            resp = client.chat.completions.create(
                model="uncensored", messages=messages, temperature=0.7
            )
            return resp.choices[0].message.content
        except RateLimitError:
            time.sleep(2 ** attempt)
        except APIError as exc:
            if attempt == tries - 1:
                raise
            time.sleep(1)
    return "Sorry, the assistant is busy. Please try again in a moment."

일반 키는 최대 8개의 동시 요청과 분당 300개의 요청을 허용하며, 체험 키는 각각 2개와 60개를 허용합니다. 봇이 많은 사용자를 동시에 처리한다면, 재시도에 의존하기보다는 워커 제한을 8 미만으로 설정하여 요청을 큐에 넣으세요. 실패한 요청은 요금이 청구되지 않으므로 재시도 시 토큰 비용이 발생하지 않습니다. 실제 장애 시 빠르게 실패하도록 재시도 횟수를 작게 유지하세요.

또한 대화 턴마다 X-Request-Id 헤더를 로깅하세요. 응답이 이상해 보일 때, 이 ID를 통해 나중에 정확한 요청을 찾을 수 있습니다.

비용 및 배포

가격은 입력 토큰 100만 개당 $0.15, 출력 토큰 100만 개당 $0.50입니다. 지원 채팅이 총 2,000 입력 토큰과 400 출력 토큰을 사용한다고 가정해 봅시다.

  • 입력: 0.002M x $0.15 = $0.0003
  • 출력: 0.0004M x $0.50 = $0.0002
  • 채팅 1회: $0.0005, 따라서 1,000회 채팅 비용은 $0.50

현재 요금과 충전 보너스는 요금제 페이지에서 확인하세요. 프로덕션에서는 키를 브라우저 코드가 아닌 서버에 보관하고, 크레딧이 떨어지기 전에 알리기 위해 X-Balance-USD 응답 헤더를 모니터링하세요. 일반 키는 분당 300개의 요청과 최대 8개의 동시 요청을 허용하므로, 한 번에 모두 전송하기보다는 버스트를 큐에 넣으세요. 문서에는 지원되는 모든 파라미터가 나열되어 있습니다.

질문과 답변

채팅봇을 만들려면 프레임워크가 필요합니까?

아니요. OpenAI SDK와 메시지 목록만 있으면 작동하는 봇을 만들 수 있습니다. 프레임워크의 추가 기능이 필요할 때만 나중에 추가하세요.

API는 이전 메시지를 기억합니까?

아니요. 각 요청은 독립적이므로 코드는 매번 기록을 다시 전송해야 합니다.

어떻게 하면 기록이 너무 길어지는 것을 방지할 수 있나요?

시스템 메시지와 최근 대화 내용을 유지하고, 목록이 설정한 크기를 초과하면 이전 내용을 짧은 메모로 요약하세요.

봇이 내 함수를 호출할 수 있습니까?

네. 도구와 tool_choice를 전달하고, 응답에 tool_calls가 포함된 경우 함수를 실행한 후 결과를 tool 메시지로 반환하세요.

키는 양식 하나만 작성하면 받을 수 있습니다

계정을 생성하고 키를 복사한 후 Base URL을 변경하세요. 설정은 이것으로 끝입니다.