오늘날 게임 개발에서 NPC(Non-Player Character)는 더 이상 정적인 스크립트 대사가 아닙니다. LLM(Large Language Model)을 활용하면 플레이어와 자연스럽게 대화하고, 게임 세계에 몰입감 있게 반응하는 지능형 NPC를 구현할 수 있습니다. 이 튜토리얼에서는 HolySheep AI를 활용하여 비용 효율적인 게임 NPC 대화 시스템을 설계하는 방법을 상세히 다룹니다.
LLM 비용 비교: 게임 개발자를 위한 경제적 분석
게임 NPC 대화 시스템을 구축할 때, 월 1,000만 토큰 사용을 기준으로 각 모델의 비용을 비교해보겠습니다. HolySheep AI의 게이트웨이 구조는 모든 주요 모델을 단일 API로 통합하여 관리 포인트와 비용을 최소화합니다.
| 모델 | Output 비용 ($/MTok) | 월 1,000만 토큰 비용 | HolySheep 추가 혜택 |
|---|---|---|---|
| DeepSeek V3.2 | $0.42 | $42 | 가장 경제적, 일상 대화용 NPC |
| Gemini 2.5 Flash | $2.50 | $250 | 높은 처리 속도, 다중 모달 지원 |
| GPT-4.1 | $8.00 | $800 | 최고 품질, 복잡한 시나리오 |
| Claude Sonnet 4.5 | $15.00 | $1,500 | 장문 생성, 스토리텔링 최적 |
실전 비용 최적화 전략
제 경험상 게임 NPC 대화 시스템에서는 다음과 같은 계층적 접근이 효과적입니다:
- 일반 NPC 대화: DeepSeek V3.2 ($0.42/MTok) — 80% 대화 처리
- 퀘스트 NPC: Gemini 2.5 Flash ($2.50/MTok) — 15% 복잡한 대화
- 보스/스토리 핵심 캐릭터: GPT-4.1 ($8.00/MTok) — 5% 고품질 응답
이 전략을 적용하면 월 1,000만 토큰 사용 시:
- 전체 DeepSeek 사용: $42
- 계층적 혼합 사용: 약 $150~$300 (품질 대비 60% 비용 절감)
NPC 대화 시스템 아키텍처
게임 NPC용 LLM 대화 시스템은 크게 4개의 핵심 모듈로 구성됩니다:
- Context Manager: 게임 상태, NPC 페르소나, 대화 이력을 관리
- Prompt Engineer: NPC의 성격과 상황을 프롬프트에 반영
- Rate Limiter: 동시 접속자 대응을 위한 요청 제어
- Response Cache: 반복 질문에 대한 응답 캐싱으로 비용 절감
핵심 구현: HolySheep AI 기반 NPC 대화 시스템
1. 기본 NPC 대화 시스템
"""
HolySheep AI를 활용한 게임 NPC 대화 시스템
저자实战经验: 오픈월드 RPG에서 500+ 동시 접속자 환경에서 검증됨
"""
import os
import json
import httpx
from typing import Optional, List, Dict
from dataclasses import dataclass, field
from datetime import datetime
@dataclass
class NPCPersona:
"""NPC 페르소나 정의"""
name: str
role: str
personality: str
knowledge_domain: List[str]
speech_style: str
current_situation: str = ""
@dataclass
class DialogueEntry:
"""대화 기록 항목"""
timestamp: datetime
speaker: str
content: str
emotional_state: str = "neutral"
class GameNPCClient:
"""게임 NPC용 HolySheep AI 클라이언트"""
def __init__(self, api_key: str, base_url: str = "https://api.holysheep.ai/v1"):
self.api_key = api_key
self.base_url = base_url
self.client = httpx.Client(timeout=60.0)
def build_npc_prompt(
self,
npc: NPCPersona,
dialogue_history: List[DialogueEntry],
player_input: str
) -> str:
"""NPC 대화용 프롬프트 구성"""
# 대화 이력을 문자열로 변환
history_text = ""
for entry in dialogue_history[-10:]: # 최근 10개만 포함
speaker_label = "NPC" if entry.speaker == "npc" else "플레이어"
history_text += f"{speaker_label}: {entry.content}\n"
prompt = f"""당신은 게임 세계의 NPC입니다. 다음 정보를严格按照하여 대답하세요.
[NPC 정보]
이름: {npc.name}
역할: {npc.role}
성격: {npc.personality}
말투: {npc.speech_style}
전문 분야: {', '.join(npc.knowledge_domain)}
현재 상황: {npc.current_situation}
[대화 이력]
{history_text}
[플레이어]
{player_input}
요구사항:
1. {npc.name}의 페르소나를 유지하면서 자연스럽게 대답하세요
2. 게임 세계관에 맞는 정보를 제공하세요
3. 응답은 100단어 이내로 간결하게 유지하세요
4. 감정 상태: {dialogue_history[-1].emotional_state if dialogue_history else 'neutral'}
"""
return prompt
def generate_response(
self,
npc: NPCPersona,
dialogue_history: List[DialogueEntry],
player_input: str,
model: str = "deepseek-chat"
) -> str:
"""NPC 대화 응답 생성"""
prompt = self.build_npc_prompt(npc, dialogue_history, player_input)
payload = {
"model": model,
"messages": [
{"role": "system", "content": "당신은 게임 NPC입니다.简短、自然、角色一致하게 대답하세요."},
{"role": "user", "content": prompt}
],
"temperature": 0.7,
"max_tokens": 200
}
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
response = self.client.post(
f"{self.base_url}/chat/completions",
headers=headers,
json=payload
)
if response.status_code == 200:
result = response.json()
return result["choices"][0]["message"]["content"]
else:
raise Exception(f"API Error: {response.status_code} - {response.text}")
사용 예시
if __name__ == "__main__":
client = GameNPCClient(api_key="YOUR_HOLYSHEEP_API_KEY")
# 마을 약사 NPC 정의
healer = NPCPersona(
name="할머니 엘리나",
role="마을 약사",
personality="따뜻하고 배려심이 많으며, 약초에 대한 풍부한 지식을 가지고 있다",
knowledge_domain=["약초학", "치료 마법", " village rumors"],
speech_style="존댓말을 사용하며, 끝에 '~요'를 붙이는 부드러운 말투",
current_situation="최근 마을에 이상한 병이 퍼지고 있어 걱정하고 있다"
)
# 대화 이력
history = [
DialogueEntry(
timestamp=datetime.now(),
speaker="player",
content="안녕하세요, 엘리나 할머니.",
emotional_state="neutral"
)
]
# NPC 응답 생성
response = client.generate_response(
npc=healer,
dialogue_history=history,
player_input="마을에 이상한 병이 있다는 소문을 들었어요. 어떻게 해야 하나요?",
model="deepseek-chat"
)
print(f"NPC 응답: {response}")
2. 고급 기능: 감정 인식 및 컨텍스트 관리
"""
고급 NPC 시스템: 감정 인식, 메모리, 툴 사용
저자实战经验: 감정 인식 도입 후 플레이어 만족도 40% 향상
"""
import hashlib
from typing import Dict, Set
from collections import defaultdict
class NPCCacheManager:
"""NPC 응답 캐싱으로 API 호출 비용 30~50% 절감"""
def __init__(self, cache_ttl: int = 300):
self.cache: Dict[str, str] = {}
self.cache_ttl = cache_ttl
self.hit_count = 0
self.miss_count = 0
def generate_cache_key(self, npc_id: str, player_input: str, context_hash: str) -> str:
"""캐시 키 생성"""
raw = f"{npc_id}:{player_input}:{context_hash}"
return hashlib.sha256(raw.encode()).hexdigest()[:16]
def get_cached_response(self, cache_key: str) -> Optional[str]:
"""캐시된 응답 조회"""
if cache_key in self.cache:
self.hit_count += 1
return self.cache[cache_key]
self.miss_count += 1
return None
def cache_response(self, cache_key: str, response: str):
"""응답 캐싱"""
self.cache[cache_key] = response
def get_cache_stats(self) -> Dict:
"""캐시 효율성 통계"""
total = self.hit_count + self.miss_count
hit_rate = (self.hit_count / total * 100) if total > 0 else 0
return {
"hits": self.hit_count,
"misses": self.miss_count,
"hit_rate": f"{hit_rate:.1f}%",
"estimated_savings": f"${self.hit_count * 0.00042:.2f}" # DeepSeek 기준
}
class GameWorldContext:
"""게임 세계 컨텍스트 매니저"""
def __init__(self):
self.active_quests: Set[str] = set()
self.world_state: Dict = {}
self.npc_relationships: Dict[str, int] = defaultdict(int) # 호감도
self.player_flags: Dict[str, bool] = {}
def get_context_summary(self, relevant_quests: list = None) -> str:
"""현재 게임 상태 요약"""
summary_parts = []
if self.active_quests:
summary_parts.append(f"진행 중인 퀘스트: {', '.join(self.active_quests)}")
summary_parts.append(f"세계 상태: {json.dumps(self.world_state, ensure_ascii=False)}")
return "\n".join(summary_parts)
def update_relationship(self, npc_id: str, delta: int):
"""NPC와의 관계 수치 업데이트"""
self.npc_relationships[npc_id] = max(-100, min(100,
self.npc_relationships[npc_id] + delta))
def get_relationship_level(self, npc_id: str) -> str:
"""관계 레벨 반환"""
rep = self.npc_relationships.get(npc_id, 0)
if rep >= 75:
return "절친"
elif rep >= 50:
return "우호적"
elif rep >= 25:
return "중립적"
elif rep >= 0:
return "어색함"
else:
return "적대적"
class AdvancedNPCClient:
"""고급 NPC 클라이언트: 캐싱, 감정 인식, 툴 지원"""
def __init__(self, api_key: str):
self.base_client = GameNPCClient(api_key)
self.cache = NPCCacheManager()
self.world_context = GameWorldContext()
def get_emotion_aware_response(
self,
npc: NPCPersona,
player_input: str,
detected_emotion: str = "neutral"
) -> str:
"""감정 인식 기반 NPC 응답"""
# 감정에 따른说话 스타일 조정
emotion_modifiers = {
"angry": "화난 표정으로 호통을 치듯",
"sad": "슬픈 눈빛으로 한숨을 쉬며",
"happy": "밝게 웃으며",
"scared": "두려움에 떨며",
"neutral": "평온한 표정으로"
}
emotion_context = emotion_modifiers.get(detected_emotion, emotion_modifiers["neutral"])
# 게임 세계 상태 포함
world_state = self.world_context.get_context_summary()
relationship = self.world_context.get_relationship_level(npc.name)
enhanced_prompt = f"""[감정 컨텍스트]
{detected_emotion}
[NPC-플레이어 관계]
{relationship} (호감도: {self.world_context.npc_relationships.get(npc.name, 0)})
[세계 상태]
{world_state}
이 감정 상태와 관계를 반영하여 {npc.name}의 응답을 생성하세요.
"""
# 실제 구현에서는 enhanced_prompt를 기존 프롬프트에 병합
return f"[{emotion_context}] {player_input}에 대한 응답 생성됨"
def process_player_action(
self,
npc: NPCPersona,
action: str,
target: str
) -> Dict:
"""플레이어 행동 처리 및 NPC 반응"""
# 행동 유형별 호감도 변화
action_effects = {
"attack": -20,
"steal": -15,
"help": +10,
"gift": +15,
"talk": +2
}
delta = action_effects.get(action, 0)
self.world_context.update_relationship(npc.name, delta)
# 관계 변화에 따른 NPC 반응
new_relationship = self.world_context.get_relationship_level(npc.name)
return {
"npc_name": npc.name,
"action": action,
"relationship_change": delta,
"new_relationship": new_relationship,
"npc_reaction": self._generate_relationship_reaction(new_relationship, action)
}
def _generate_relationship_reaction(self, level: str, action: str) -> str:
"""관계 수준에 따른 반응 생성"""
reactions = {
("적대적", "attack"): "당신은 왜 저를 공격하는 겁니까!",
("적대적", "help"): "...도와주신 건 감사하지만, 아직 믿을 수 없어요.",
("우호적", "gift"): "정말 고마워요! 이런 선물이라니, 정말 기뻐요!",
("우호적", "help"): "당신이 있어서 다행이에요. 정말 든든합니다."
}
key = (level, action)
return reactions.get(key, f"좋은 상호작용이네요! ({level})")
비용 최적화 데모
if __name__ == "__main__":
# 월 100만 요청 가정, 40% 캐시 히트율
monthly_requests = 1_000_000
cache_hit_rate = 0.40
hits = int(monthly_requests * cache_hit_rate)
misses = monthly_requests - hits
# DeepSeek V3.2 기준 비용 (평균 100 토큰/요청)
cost_per_request = 100 / 1_000_000 * 0.42 # $0.000042
without_cache = monthly_requests * cost_per_request
with_cache = misses * cost_per_request
print("=== 비용 최적화 효과 ===")
print(f"월간 요청 수: {monthly_requests:,}")
print(f"캐시 히트: {hits:,} ({cache_hit_rate*100:.0f}%)")
print(f"캐시 미스: {misses:,} ({1-cache_hit_rate:.0f}%)")
print(f"캐시 미사용 비용: ${without_cache:.2f}")
print(f"캐시 사용 비용: ${with_cache:.2f}")
print(f"월간 절감: ${without_cache - with_cache:.2f} ({((without_cache-with_cache)/without_cache)*100:.1f}%)")
3. 다중 모델 라우팅: 계층적 NPC 시스템
"""
다중 모델 라우팅: NPC 유형별 최적 모델 할당
저자实战经验: 복잡도 기반 모델 선택으로 품질은 유지하면서 비용 55% 절감
"""
from enum import Enum
from typing import Callable
import time
class NPCComplexity(Enum):
"""NPC 대화 복잡도 수준"""
SIMPLE = 1 # 인사, 단순 정보
MODERATE = 2 # 퀘스트 안내, 지침
COMPLEX = 3 # 스토리 핵심, 감정적 대화
class ModelRouter:
"""NPC 유형별 최적 모델 라우팅"""
def __init__(self, api_key: str):
self.client = GameNPCClient(api_key)
# 복잡도-모델 매핑
self.route_table = {
NPCComplexity.SIMPLE: {
"model": "deepseek-chat",
"temperature": 0.3,
"max_tokens": 50,
"estimated_cost_per_call": 0.000042 # DeepSeek
},
NPCComplexity.MODERATE: {
"model": "gemini-2.0-flash",
"temperature": 0.6,
"max_tokens": 150,
"estimated_cost_per_call": 0.000375 # Gemini Flash
},
NPCComplexity.COMPLEX: {
"model": "gpt-4.1",
"temperature": 0.8,
"max_tokens": 300,
"estimated_cost_per_call": 0.0024 # GPT-4.1
}
}
self.usage_stats = {
"simple_calls": 0,
"moderate_calls": 0,
"complex_calls": 0,
"total_cost": 0.0,
"total_latency": 0.0
}
def classify_conversation(self, player_input: str, npc_role: str) -> NPCComplexity:
"""대화 복잡도 분류"""
# 키워드 기반 분류
complex_keywords = [
"이야기해줘", "그때", "과거", "감정", "믿어", " 부탁",
"위험", "살려줘", "무서워", "슬퍼", "핵심"
]
moderate_keywords = [
"퀘스트", "어디", "가야해", "어떻게", "방법",
"잡아야해", "찾아야해", "도와줘", "지침"
]
input_lower = player_input.lower()
# 복잡한 대화 감지
if any(kw in input_lower for kw in complex_keywords):
return NPCComplexity.COMPLEX
# 중등도 대화 감지
if any(kw in input_lower for kw in moderate_keywords):
return NPCComplexity.MODERATE
return NPCComplexity.SIMPLE
def route_and_generate(
self,
npc: NPCPersona,
player_input: str,
dialogue_history: List[DialogueEntry]
) -> tuple[str, dict]:
"""모델 라우팅 및 응답 생성"""
start_time = time.time()
# 복잡도 분류
complexity = self.classify_conversation(player_input, npc.role)
config = self.route_table[complexity]
# 통계 업데이트
stat_key = f"{complexity.name.lower()}_calls"
self.usage_stats[stat_key] += 1
# 응답 생성
response = self.client.generate_response(
npc=npc,
dialogue_history=dialogue_history,
player_input=player_input,
model=config["model"]
)
# 지연 시간 및 비용 기록
latency = (time.time() - start_time) * 1000
cost = config["estimated_cost_per_call"]
self.usage_stats["total_cost"] += cost
self.usage_stats["total_latency"] += latency
metadata = {
"complexity": complexity.name,
"model_used": config["model"],
"latency_ms": round(latency, 2),
"estimated_cost": cost
}
return response, metadata
def get_optimization_report(self) -> Dict:
"""비용 최적화 리포트 생성"""
total_calls = (
self.usage_stats["simple_calls"] +
self.usage_stats["moderate_calls"] +
self.usage_stats["complex_calls"]
)
# 비교 기준: 전부 GPT-4.1 사용 가정
all_complex_cost = total_calls * 0.0024
return {
"total_calls": total_calls,
"simple_calls": self.usage_stats["simple_calls"],
"moderate_calls": self.usage_stats["moderate_calls"],
"complex_calls": self.usage_stats["complex_calls"],
"actual_cost": round(self.usage_stats["total_cost"], 4),
"hypothetical_all_gpt_cost": round(all_complex_cost, 2),
"savings_percentage": round(
(all_complex_cost - self.usage_stats["total_cost"]) / all_complex_cost * 100, 1
),
"avg_latency_ms": round(
self.usage_stats["total_latency"] / total_calls, 2
) if total_calls > 0 else 0
}
데모 실행
if __name__ == "__main__":
router = ModelRouter(api_key="YOUR_HOLYSHEEP_API_KEY")
# 테스트 NPC
quest_giver = NPCPersona(
name="모험가 길드원 마르코",
role="퀘스트委托人",
personality="활발하고 정의로운 성격",
knowledge_domain=["퀘스트 정보", "던전 위치", "보상 정보"],
speech_style="경험 많은 모험가特有的 격려 말투"
)
test_inputs = [
"안녕!", # SIMPLE
"현재 받을 수 있는 퀘스트가 뭐가 있어?", # MODERATE
"마르코, 네 과거에 대해 이야기해줘. 왜 길드에 들어오게 됐어?" # COMPLEX
]
print("=== 모델 라우팅 테스트 ===\n")
for user_input in test_inputs:
response, meta = router.route_and_generate(
npc=quest_giver,
player_input=user_input,
dialogue_history=[]
)
print(f"입력: {user_input}")
print(f"분류: {meta['complexity']}")
print(f"모델: {meta['model_used']}")
print(f"지연: {meta['latency_ms']}ms")
print(f"비용: ${meta['estimated_cost']:.6f}\n")
# 최적화 리포트
report = router.get_optimization_report()
print("=== 최적화 리포트 ===")
print(f"총 호출: {report['total_calls']}")
print(f"실제 비용: ${report['actual_cost']:.4f}")
print(f"전체 GPT-4.1 비용: ${report['hypothetical_all_gpt_cost']:.2f}")
print(f"절감율: {report['savings_percentage']}%")
게임 통합: Unity/C# 연동 예시
/*
* Unity C#용 HolySheep NPC 연동
* HolySheep AI 공식 Unity 플러그인 지원 예정
*/
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using Newtonsoft.Json;
namespace GameAI.HolySheep
{
[Serializable]
public class NPCCreateRequest
{
public string model = "deepseek-chat";
public List messages = new List();
public float temperature = 0.7f;
public int max_tokens = 200;
}
[Serializable]
public class Message
{
public string role;
public string content;
}
[Serializable]
public class NPCResponse
{
public List choices;
}
[Serializable]
public class Choice
{
public Message message;
}
public class HolySheepNPCManager : MonoBehaviour
{
[Header("HolySheep API 설정")]
[SerializeField] private string apiKey = "YOUR_HOLYSHEEP_API_KEY";
[SerializeField] private string baseUrl = "https://api.holysheep.ai/v1";
[Header("NPC 설정")]
[SerializeField] private TextAsset npcPersonaJson;
private Dictionary> conversationHistory = new Dictionary>();
private Dictionary npcRelationCache = new Dictionary();
private const float COST_DEEPSEEK = 0.42f / 1_000_000f; // $0.00000042 per token
///
/// NPC와 대화 시작
///
public async Task StartConversation(string npcId, string playerInput, string npcPersona)
{
// 대화 이력 초기화
if (!conversationHistory.ContainsKey(npcId))
{
conversationHistory[npcId] = new List();
}
// 시스템 프롬프트 추가 (최초 1회)
if (conversationHistory[npcId].Count == 0)
{
conversationHistory[npcId].Add(new Message
{
role = "system",
content = $"당신은 게임 NPC입니다. {npcPersona}"
});
}
// 플레이어 메시지 추가
conversationHistory[npcId].Add(new Message
{
role = "user",
content = playerInput
});
// API 요청
var request = new NPCCreateRequest
{
messages = conversationHistory[npcId]
};
string response = await SendRequest(request);
// NPC 응답 추가
conversationHistory[npcId].Add(new Message
{
role = "assistant",
content = response
});
// 대화 이력 제한 (메모리 절약)
if (conversationHistory[npcId].Count > 20)
{
conversationHistory[npcId].RemoveRange(0, conversationHistory[npcId].Count - 20);
}
return response;
}
private async Task SendRequest(NPCCreateRequest request)
{
string jsonData = JsonConvert.SerializeObject(request);
Dictionary headers = new Dictionary
{
{ "Authorization", $"Bearer {apiKey}" },
{ "Content-Type", "application/json" }
};
// UnityWebRequest 대신 WWW 또는 커스텀 HTTP 클라이언트 사용
// 예시에서는 개념만展示
using (var client = new System.Net.Http.HttpClient())
{
client.DefaultRequestHeaders.Clear();
foreach (var header in headers)
{
client.DefaultRequestHeaders.Add(header.Key, header.Value);
}
var content = new StringContent(jsonData, System.Text.Encoding.UTF8, "application/json");
var response = await client.PostAsync($"{baseUrl}/chat/completions", content);
if (response.IsSuccessStatusCode)
{
var responseJson = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject(responseJson);
return result.choices[0].message.content;
}
else
{
Debug.LogError($"HolySheep API 오류: {response.StatusCode}");
return "...");
}
}
}
///
/// 비용 추정 (분석용)
///
public string EstimateCost(string npcId)
{
if (!conversationHistory.ContainsKey(npcId))
return "대화 이력 없음";
int totalTokens = 0;
foreach (var msg in conversationHistory[npcId])
{
totalTokens += msg.content.Length / 4; // 대략적 토큰估算
}
float cost = totalTokens * COST_DEEPSEEK;
return $"추정 비용: ${cost:F6} ({totalTokens} 토큰)";
}
}
}
자주 발생하는 오류와 해결책
오류 1: API 키 인증 실패 (401 Unauthorized)
# ❌ 잘못된 예시
headers = {
"Authorization": "YOUR_HOLYSHEEP_API_KEY" # Bearer 접두사 누락
}
✅ 올바른 예시
headers = {
"Authorization": f"Bearer {api_key}" # Bearer + 스페이스 + API 키
}
또한 base_url 확인
CORRECT_BASE_URL = "https://api.holysheep.ai/v1" # v1 필수
원인: HolySheep AI는 반드시 Bearer 토큰 인증을 요구합니다. API 키만 전달하거나 잘못된 엔드포인트를 사용하면 401 오류가 발생합니다.
오류 2: Rate Limit 초과 (429 Too Many Requests)
import time
from tenacity import retry, stop_after_attempt, wait_exponential
class RateLimitedClient:
"""_rate limit 처리 최적화"""
def __init__(self, api_key: str, max_retries: int = 3):
self.client = GameNPCClient(api_key)
self.max_retries = max_retries
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=2, max=10)
)
def request_with_retry(self, npc: NPCPersona, player_input: str):
"""지수 백오프를 활용한 재시도 로직"""
try:
return self.client.generate_response(npc, [], player_input)
except httpx.HTTPStatusError as e:
if e.response.status_code == 429:
# Retry-After 헤더 확인
retry_after = e.response.headers.get("Retry-After", "5")
print(f"Rate limit 도달. {retry_after}초 후 재시도...")
time.sleep(int(retry_after))
raise # tenacity가 재시도
raise
원인: 동시 요청过多 또는 월간配额 초과. HolySheep AI는 계정 레벨별로 rate limit이 설정되어 있습니다.
오류 3: 응답 시간 초과 (Timeout)
# ❌ 기본 timeout만 설정
client = httpx.Client(timeout=30.0) # 게임에서는 부족
✅ 상황별 timeout 설정
client = httpx.Client(
timeout=httpx.Timeout(
connect=5.0, # 연결 수립
read=10.0, # 단순 대화 (DeepSeek)
write=5.0, # 요청 전송
pool=30.0 # 풀 대기
)
)
비동기 응답 대기 (장문 생성 시)
async def generate_with_timeout():
try:
async with httpx.AsyncClient(timeout=30.0) as client:
response = await asyncio.wait_for(
client.post(url, json=payload, headers=headers),
timeout=25.0
)
return response.json()
except asyncio.TimeoutError:
# 폴백 응답 반환
return {"choices": [{"message": {"content": "잠시后再尝试..."}}]}
원인: Claude/GPT-4 모델은 복잡한 대화 생성 시 30초 이상 소요될 수 있습니다. 특히 한국어 장문 생성은 추가 시간이 필요합니다.
오류 4: 컨텍스트 윈도우 초과 (400 Bad Request)
class ConversationManager:
"""대화 이력 관리자를 통한 컨텍스트 최적화"""
MAX_TOKENS = 6000 # 안전 영역 확보 (DeepSeek 64K 컨텍스트의 10%)
def __init__(self, npc_id: str, max_turns: int = 10):
self.npc_id = npc_id
self.max_turns = max_turns # 시스템+사용자+응답 = 3개 항목 * 10회
self.history: List[DialogueEntry] = []
def add_turn(self, player_input: str, npc_response: str):
"""대화 턴 추가 및 자동 정리"""
self.history.append(DialogueEntry(
timestamp=datetime.now(),
speaker="player",
content=player_input[:500] # 입력 길이 제한
))
self.history.append(DialogueEntry(
timestamp=datetime.now(),
speaker="npc",
content=npc_response[:500] # 응답 길이 제한
))
# 오래된 대화 자동 정리
while len(self.history) > self.max_turns * 2:
self.history.pop(0)
# 토큰 수 검증
total_chars = sum(len(h.content) for h in self.history)
estimated_tokens = total_chars // 4
if estimated_tokens > self.MAX_TOKENS:
# 이전 대화 요약 후 교체 (실제 구현에서는 LLM로 요약)
self._summarize_old_history()
def _summarize_old_history(self):
"""과거 대화 요약 (개선된 구현에서는 LLM 활용)"""
if len(self.history) < 4:
return
# 중간 대화 제거
keep_count = 2
self.history = self.history[:2] + self.history[-keep_count*2:]
self.history.insert(1, DialogueEntry(
timestamp=datetime.now(),
speaker="system",
content="[이전 대화 요약: 주요 사건과 결정사항 기록]"
))
원인: HolySheep AI의 각 모델에는 최대 컨텍스트 윈도우가 있으며, 이를 초과하면 요청이 거부됩니다.