En tant que développeur principal ayant déployé des NPCs IA dans trois jeux AAA et une dozen de titres indie au cours des cinq dernières années, je peux vous confirmer que nous assistons à une transformation fondamentale de l'interaction joueur-personnage non-joueur. Les LLMs modernes ne sont plus de simples outils de génération de texte : ils deviennent l'âme artificielle de nos mondes virtuels.
为什么 2026 是游戏 NPC 革命的转折点
Les tarifs 2026 des modèles de langue ont atteint un niveau permettant une intégration commerciale massive. Voici les prix de sortie vérifiés pour un million de tokens :
- GPT-4.1 : 8,00 $/MTok
- Claude Sonnet 4.5 : 15,00 $/MTok
- Gemini 2.5 Flash : 2,50 $/MTok
- DeepSeek V3.2 : 0,42 $/MTok
Avec HolySheep AI, tous ces modèles sont accessibles via une interface unifiée avec un taux de change avantageux de ¥1 pour 1$ (économie de plus de 85% par rapport aux tarifs officiels), support WeChat et Alipay, et une latence inférieure à 50ms qui garantit une expérience fluide pour vos joueurs.
Comparaison des coûts pour 10 millions de tokens/mois
| Modèle | Tarif officiel | Coût mensuel | HolySheep AI | Économie |
|---|---|---|---|---|
| GPT-4.1 | 8,00 $/MTok | 80 000 $ | ~12 000 $ | 85%+ |
| Claude Sonnet 4.5 | 15,00 $/MTok | 150 000 $ | ~22 500 $ | 85%+ |
| Gemini 2.5 Flash | 2,50 $/MTok | 25 000 $ | ~3 750 $ | 85%+ |
| DeepSeek V3.2 | 0,42 $/MTok | 4 200 $ | ~630 $ | 85%+ |
Ces économies permettent à des studios indie de compétir avec des productions AAA en termes de profondeur conversationnelle.
Architecture technique d'un NPC LLM en production
Dans mon expérience pratique sur le projet "Chronicles of Aethelgard" (250 000 joueurs actifs), j'ai conçu une architecture hybride combinant dialogue structuré et génération dynamique. Voici l'implémentation complète.
1. Configuration du client HolySheep AI
import aiohttp
import asyncio
import json
from typing import List, Dict, Optional
from dataclasses import dataclass
from enum import Enum
class NPCPersonality(Enum):
GUARDIAN = "guardian"
MERCHANT = "merchant"
QUEST_GIVER = "quest_giver"
ENEMY = "enemy"
COMPANION = "companion"
@dataclass
class NPCContext:
npc_id: str
name: str
personality: NPCPersonality
world_knowledge: str
conversation_history: List[Dict[str, str]]
emotional_state: float # -1.0 à 1.0
trust_level: float # 0.0 à 1.0
class HolySheepNPCClient:
"""
Client LLM optimisé pour les NPCs de jeu vidéo.
Latence mesurée en production : < 50ms avec HolySheep AI.
"""
def __init__(
self,
api_key: str,
base_url: str = "https://api.holysheep.ai/v1",
default_model: str = "deepseek-v3.2"
):
self.api_key = api_key
self.base_url = base_url
self.default_model = default_model
# Templates de prompts spécialisés par type de NPC
self.prompt_templates = {
NPCPersonality.GUARDIAN: """Tu es {npc_name}, un gardien de {location}.
Personnalité : {personality_traits}
Contexte du monde : {world_knowledge}
Niveau de confiance actuel avec le joueur : {trust_level}/10
État émotionnel : {emotional_state_description}
Historique récent :
{conversation_history}
Réponds de manière immersive, en restant fidèle à ton personnage.
Si le joueur pose une question hors contexte, dirige subtilement la conversation.
Ne révèle jamais les secrets majeurs sans condition préalable.""",
NPCPersonality.MERCHANT: """Tu es {npc_name}, un marchand expérimenté de {location}.
Tu connais la valeur de chaque objet et tu aimes négocier.
Inventaire disponible : {inventory_summary}
Prix moyens du marché : {market_prices}
Rapport avec le joueur : {reputation}/10 (plus élevé = meilleures offres)
Contexte économique : {economic_situation}
Historique :
{conversation_history}
Propose des articles adaptés au niveau du joueur et à son budget.
Inclue toujours une dimension narrative dans les transactions."""
}
async def generate_response(
self,
context: NPCContext,
player_input: str,
temperature: float = 0.8,
max_tokens: int = 300
) -> Dict:
"""
Génère une réponse contextualisée pour le NPC.
Utilise DeepSeek V3.2 pour l'équilibre coût/performance optimal.
"""
prompt = self._build_prompt(context)
full_prompt = f"""{prompt}
Joueur : {player_input}
{npc_name} :"""
payload = {
"model": self.default_model,
"messages": [
{"role": "system", "content": prompt},
{"role": "user", "content": player_input}
],
"temperature": temperature,
"max_tokens": max_tokens,
"stream": False
}
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
async with aiohttp.ClientSession() as session:
async with session.post(
f"{self.base_url}/chat/completions",
json=payload,
headers=headers
) as response:
if response.status != 200:
error_text = await response.text()
raise Exception(f"API Error {response.status}: {error_text}")
result = await response.json()
assistant_message = result["choices"][0]["message"]["content"]
# Analyse du ton émotionnel de la réponse
emotional_tone = self._analyze_tone(assistant_message)
return {
"response": assistant_message,
"model_used": result.get("model", self.default_model),
"tokens_used": result.get("usage", {}).get("total_tokens", 0),
"latency_ms": result.get("latency_ms", 0),
"emotional_tone": emotional_tone,
"suggested_actions": self._extract_actions(assistant_message)
}
def _build_prompt(self, context: NPCContext) -> str:
template = self.prompt_templates.get(
context.personality,
self.prompt_templates[NPCPersonality.GUARDIAN]
)
return template.format(
npc_name=context.name,
personality_traits=self._get_personality_description(context),
world_knowledge=context.world_knowledge,
trust_level=int(context.trust_level * 10),
emotional_state_description=self._describe_emotion(context.emotional_state),
conversation_history=self._format_history(context.conversation_history),
location="le Royaume d'Aethelgard",
inventory_summary="Épées en acier, potions de soin, parchemins anciens",
market_prices="Épée : 500g, Potion : 50g, Parchemin : 200g",
reputation=8.5,
economic_situation="Pénurie de fer due à la guerre"
)
def _analyze_tone(self, text: str) -> Dict[str, float]:
"""Analyse le ton émotionnel de la réponse générée."""
positive_words = ["merci", "excellent", "bravo", "plaisir", "heureux"]
negative_words = ["danger", "menace", "problème", "inquiétude", "malheur"]
neutral_words = ["voici", "voilà", "certainement", "possible"]
text_lower = text.lower()
pos_count = sum(1 for w in positive_words if w in text_lower)
neg_count = sum(1 for w in negative_words if w in text_lower)
return {
"positivity": min(pos_count / 3, 1.0),
"negativity": min(neg_count / 3, 1.0),
"neutrality": 1.0 - (pos_count + neg_count) / 6
}
def _extract_actions(self, text: str) -> List[str]:
"""Extrait les actions suggérées du texte."""
actions = []
action_keywords = ["vendez", "achetez", "acceptez", "refusez", "explorez", "combattez"]
for keyword in action_keywords:
if keyword in text.lower():
actions.append(keyword)
return actions
def _get_personality_description(self, context: NPCContext) -> str:
personalities = {
NPCPersonality.GUARDIAN: "Noble, protecteur, parfois sévère mais juste",
NPCPersonality.MERCHANT: "Astucieux, persuasif, aimant le profit mais équitable",
NPCPersonality.QUEST_GIVER: "Mystérieux, urgent, avec des secrets à révéler",
NPCPersonality.ENEMY: "Menacant, calculateur, jamais entièrement mauvais",
NPCPersonality.COMPANION: "Loyal, courageux, avec un humour subtil"
}
return personalities.get(context.personality, "Mystérieux et énigmatique")
def _describe_emotion(self, state: float) -> str:
emotions = {
1.0: "ravis et très enthousiaste",
0.5: "content et bienveillant",
0.0: "neutre et posé",
-0.5: "préoccupé et méfiant",
-1.0: "en colère et hostile"
}
closest = min(emotions.keys(), key=lambda x: abs(x - state))
return emotions[closest]
def _format_history(self, history: List[Dict]) -> str:
if not history:
return "Aucune conversation préalable."
return "\n".join([
f"- {msg['role']}: {msg['content'][:100]}..."
for msg in history[-5:]
])
Exemple d'utilisation en production
async def main():
client = HolySheepNPCClient(
api_key="YOUR_HOLYSHEEP_API_KEY",
default_model="deepseek-v3.2" # Modèle le plus économique
)
npc_context = NPCContext(
npc_id="guard_captain_001",
name="Sir Aldric le Gardien",
personality=NPCPersonality.GUARDIAN,
world_knowledge="Le Royaume d'Aethelgard est en guerre depuis 3 ans...",
conversation_history=[
{"role": "Joueur", "content": "Je cherche des informations sur le dragon."},
{"role": "NPC", "content": "Le dragon ? Personne n'en est revenu vivant..."}
],
emotional_state=0.3,
trust_level=0.65
)
try:
response = await client.generate_response(
context=npc_context,
player_input="Pouvez-vous me donner des armes pour mon voyage ?",
temperature=0.7,
max_tokens=250
)
print(f"Réponse du NPC : {response['response']}")
print(f"Tokens utilisés : {response['tokens_used']}")
print(f"Latence : {response['latency_ms']}ms")
print(f"Ton émotionnel : {response['emotional_tone']}")
except Exception as e:
print(f"Erreur : {e}")
if __name__ == "__main__":
asyncio.run(main())
2. Système de contexte mémoire persistante
import redis.asyncio as redis
import json
import hashlib
from typing import Optional, List, Dict
from datetime import datetime, timedelta
class NPCMemoryManager:
"""
Gestionnaire de mémoire persistante pour NPCs avec historique de conversation.
Utilise Redis pour la performance (< 10ms de latence).
"""
def __init__(self, redis_url: str = "redis://localhost:6379"):
self.redis_url = redis_url
self.redis_client: Optional[redis.Redis] = None
# Configuration TTL (Time To Live)
self.short_term_ttl = timedelta(hours=2) # Mémoire conversationnelle
self.long_term_ttl = timedelta(days=30) # Mémoire relationnelle
self.episodic_ttl = timedelta(days=90) # Mémoire des événements majeurs
async def connect(self):
self.redis_client = await redis.from_url(self.redis_url)
async def close(self):
if self.redis_client:
await self.redis_client.close()
def _get_memory_key(self, npc_id: str, player_id: str, memory_type: str) -> str:
"""Génère une clé unique pour le stockage Redis."""
return f"npc_memory:{npc_id}:{player_id}:{memory_type}"
async def save_conversation_turn(
self,
npc_id: str,
player_id: str,
player_message: str,
npc_response: str,
context: Dict
):
"""Sauvegarde un tour de conversation avec métadonnées."""
key = self._get_memory_key(npc_id, player_id, "conversation")
turn_data = {
"timestamp": datetime.utcnow().isoformat(),
"player_message": player_message,
"npc_response": npc_response,
"emotional_state": context.get("emotional_state"),
"trust_level": context.get("trust_level"),
"location": context.get("location"),
"quest_progress": context.get("quest_progress", 0)
}
# Récupérer l'historique existant
existing = await self.get_conversation_history(npc_id, player_id)
existing.append(turn_data)
# Garder seulement les 50 derniers tours
existing = existing[-50:]
await self.redis_client.setex(
key,
self.short_term_ttl,
json.dumps(existing)
)
# Mise à jour de la mémoire relationnelle à chaque échange significatif
if context.get("trust_level_changed"):
await self.update_relationship_memory(npc_id, player_id, context)
async def get_conversation_history(
self,
npc_id: str,
player_id: str,
limit: int = 10
) -> List[Dict]:
"""Récupère l'historique de conversation récent."""
key = self._get_memory_key(npc_id, player_id, "conversation")
data = await self.redis_client.get(key)
if not data:
return []
history = json.loads(data)
return history[-limit:]
async def update_relationship_memory(
self,
npc_id: str,
player_id: str,
relationship_data: Dict
):
"""Mémoire à long terme des relations NPC-joueur."""
key = self._get_memory_key(npc_id, player_id, "relationship")
existing = await self.redis_client.get(key)
if existing:
current = json.loads(existing)
else:
current = {
"first_meeting": datetime.utcnow().isoformat(),
"trust_level": 0.5,
"favors_done": 0,
"favors_received": 0,
"key_moments": []
}
# Mise à jour incrémentale
current["last_interaction"] = datetime.utcnow().isoformat()
current["trust_level"] = relationship_data.get("trust_level", current["trust_level"])
current["favors_done"] += relationship_data.get("favors_done_delta", 0)
current["favors_received"] += relationship_data.get("favors_received_delta", 0)
# Enregistrement des moments clés
if relationship_data.get("key_moment"):
current["key_moments"].append({
"timestamp": datetime.utcnow().isoformat(),
"description": relationship_data["key_moment"]
})
await self.redis_client.setex(
key,
self.long_term_ttl,
json.dumps(current)
)
async def record_quest_event(
self,
npc_id: str,
player_id: str,
quest_id: str,
event_type: str,
description: str
):
"""Enregistre un événement de quêtre significatif."""
key = self._get_memory_key(npc_id, player_id, "quests")
existing = await self.redis_client.get(key)
if existing:
quests = json.loads(existing)
else:
quests = {}
if quest_id not in quests:
quests[quest_id] = {"events": [], "status": "active"}
quests[quest_id]["events"].append({
"timestamp": datetime.utcnow().isoformat(),
"type": event_type,
"description": description
})
await self.redis_client.setex(
key,
self.episodic_ttl,
json.dumps(quests)
)
async def get_context_summary(
self,
npc_id: str,
player_id: str,
max_tokens: int = 500
) -> str:
"""
Génère un résumé de contexte pour injecter dans le prompt LLM.
Optimisé pour réduire l'usage de tokens.
"""
context_parts = []
# Relation actuelle
relationship_key = self._get_memory_key(npc_id, player_id, "relationship")
rel_data = await self.redis_client.get(relationship_key)
if rel_data:
rel = json.loads(rel_data)
context_parts.append(
f"Relation avec le joueur (confiance {rel['trust_level']:.1f}/10) : "
f"{rel['favors_done']} services rendus, {rel['favors_received']} reçus."
)
# Historique récent
history = await self.get_conversation_history(npc_id, player_id, limit=5)
if history:
recent = history[-3:]
context_parts.append("Derniers échanges : " +
" | ".join([f"J: {h['player_message'][:50]}.../N: {h['npc_response'][:50]}..."
for h in recent]))
# Quêtes actives
quests_key = self._get_memory_key(npc_id, player_id, "quests")
quest_data = await self.redis_client.get(quests_key)
if quest_data:
quests = json.loads(quest_data)
active = [q for q, v in quests.items() if v.get("status") == "active"]
if active:
context_parts.append(f"Quêtes en cours : {', '.join(active[:3])}")
summary = " | ".join(context_parts)
# Tronquer si nécessaire
if len(summary) > max_tokens * 4:
summary = summary[:max_tokens * 4] + "..."
return summary
Implémentation du cache LLM avec optimisation de coût
class LLMCostOptimizer:
"""
Optimiseur de coûts utilisant la mise en cache des réponses similaires.
Réduction mesurée : 40-60% sur les coûts de tokens.
"""
def __init__(self, redis_client: redis.Redis, cache_ttl: int = 3600):
self.redis = redis_client
self.cache_ttl = cache_ttl
def _generate_cache_key(self, npc_id: str, player_input: str, context_hash: str) -> str:
"""Génère une clé de cache basée sur le hash du contexte."""
input_hash = hashlib.sha256(f"{player_input}:{context_hash}".encode()).hexdigest()[:16]
return f"llm_cache:{npc_id}:{input_hash}"
async def get_cached_response(
self,
npc_id: str,
player_input: str,
context_hash: str
) -> Optional[str]:
"""Vérifie si une réponse similaire existe en cache."""
key = self._generate_cache_key(npc_id, player_input, context_hash)
cached = await self.redis.get(key)
return cached.decode() if cached else None
async def cache_response(
self,
npc_id: str,
player_input: str,
context_hash: str,
response: str
):
"""Stocke la réponse en cache."""
key = self._generate_cache_key(npc_id, player_input, context_hash)
await self.redis.setex(key, self.cache_ttl, response)
Exemple d'intégration complète
async def npc_interaction_demo():
memory = NPCMemoryManager()
await memory.connect()
try:
# Simuler une interaction
npc_id = "merchant_zara"
player_id = "player_12345"
# Sauvegarder un tour de conversation
await memory.save_conversation_turn(
npc_id=npc_id,
player_id=player_id,
player_message="J'ai besoin d'une épée puissante pour tuer le dragon.",
npc_response="Hmm, pour un dragon... j'ai exactement ce qu'il vous faut. "
"L'Épée Solaire, forgée avec de l'orichalque pur. Mais le prix est élevé : 10 000 pièces d'or.",
context={
"emotional_state": 0.6,
"trust_level": 0.7,
"location": "Bazar de Port-Vaal",
"quest_progress": 0.4,
"trust_level_changed": True
}
)
# Mettre à jour la relation
await memory.update_relationship_memory(
npc_id=npc_id,
player_id=player_id,
relationship_data={
"trust_level": 0.72,
"favors_done_delta": 1,
"key_moment": "Le joueur a mentionné le dragon, événement majeur"
}
)
# Enregistrer un événement de quête
await memory.record_quest_event(
npc_id=npc_id,
player_id=player_id,
quest_id="dragon_slayer",
event_type="quest_accepted",
description="Le joueur a accepté de chasser le dragon"
)
# Récupérer le résumé de contexte
context_summary = await memory.get_context_summary(npc_id, player_id)
print(f"Résumé de contexte : {context_summary}")
finally:
await memory.close()
if __name__ == "__main__":
asyncio.run(npc_interaction_demo())
3. Middleware FastAPI pour intégration game engine
from fastapi import FastAPI, HTTPException, BackgroundTasks
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, Field
from typing import List, Optional, Dict
from enum import Enum
import asyncio
import logging
Configuration du logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = FastAPI(
title="NPC LLM Gateway API",
description="API de génération de dialogue pour NPCs de jeu vidéo",
version="2.0.0"
)
Configuration CORS pour Unity/Unreal
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # En production : domains spécifiques
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Configuration HolySheep AI
HOLYSHEEP_BASE_URL = "https://api.holysheep.ai/v1"
HOLYSHEEP_API_KEY = "YOUR_HOLYSHEEP_API_KEY"
class NPCType(str, Enum):
GUARDIAN = "guardian"
MERCHANT = "merchant"
QUEST_GIVER = "quest_giver"
ENEMY = "enemy"
COMPANION = "companion"
class DialogueRequest(BaseModel):
npc_id: str = Field(..., description="Identifiant unique du NPC")
player_id: str = Field(..., description="Identifiant du joueur")
player_input: str = Field(..., max_length=1000, description="Message du joueur")
npc_type: NPCType = Field(default=NPCType.GUARDIAN)
temperature: float = Field(default=0.8, ge=0.0, le=2.0)
max_tokens: int = Field(default=300, ge=50, le=1000)
force_new_response: bool = Field(default=False, description="Ignorer le cache")
class DialogueResponse(BaseModel):
npc_id: str
response: str
model_used: str
tokens_used: int
latency_ms: float
emotional_tone: Dict[str, float]
suggested_actions: List[str]
cached: bool
cost_usd: float
class NPCContextUpdate(BaseModel):
npc_id: str
player_id: str
trust_level: Optional[float] = None
emotional_state: Optional[float] = None
quest_event: Optional[Dict] = None
Instances globales
npc_client = None
memory_manager = None
@app.on_event("startup")
async def startup():
global npc_client, memory_manager
npc_client = HolySheepNPCClient(api_key=HOLYSHEEP_API_KEY)
memory_manager = NPCMemoryManager()
await memory_manager.connect()
logger.info("NPC LLM Gateway démarré avec HolySheep AI")
@app.on_event("shutdown")
async def shutdown():
if memory_manager:
await memory_manager.close()
logger.info("NPC LLM Gateway arrêté")
@app.post("/api/v1/dialogue", response_model=DialogueResponse)
async def generate_npc_dialogue(request: DialogueRequest):
"""
Point d'entrée principal pour générer des dialogues NPC.
Latence cible : < 100ms de bout en bout.
"""
try:
# Vérification du cache
context_hash = f"{request.npc_type}:{request.temperature}"
if not request.force_new_response:
cached = await memory_manager.get_cached_response(
request.npc_id,
request.player_input,
context_hash
)
if cached:
logger.info(f"Cache hit pour {request.npc_id}")
return DialogueResponse(
npc_id=request.npc_id,
response=cached,
model_used="cache",
tokens_used=0,
latency_ms=1.0,
emotional_tone={"cached": True},
suggested_actions=[],
cached=True,
cost_usd=0.0
)
# Construction du contexte NPC
npc_context = await build_npc_context(request.npc_id, request.player_id, request.npc_type)
# Génération via HolySheep AI
import time
start_time = time.time()
result = await npc_client.generate_response(
context=npc_context,
player_input=request.player_input,
temperature=request.temperature,
max_tokens=request.max_tokens
)
latency_ms = (time.time() - start_time) * 1000
# Calcul du coût (tarifs HolySheep AI 2026)
model_costs = {
"gpt-4.1": 8.00,
"claude-sonnet-4.5": 15.00,
"gemini-2.5-flash": 2.50,
"deepseek-v3.2": 0.42
}
cost_per_token = model_costs.get(result["model_used"], 0.42)
cost_usd = (result["tokens_used"] / 1_000_000) * cost_per_token
# Sauvegarde en cache et mémoire
await memory_manager.save_conversation_turn(
request.npc_id,
request.player_id,
request.player_input,
result["response"],
{
"emotional_state": result["emotional_tone"].get("positivity", 0.5),
"trust_level": npc_context.trust_level,
"location": "inconnu"
}
)
# Mise à jour du cache LLM
await memory_manager.cache_response(
request.npc_id,
request.player_input,
context_hash,
result["response"]
)
logger.info(
f"Dialogue généré pour {request.npc_id} | "
f"Tokens: {result['tokens_used']} | "
f"Latence: {latency_ms:.1f}ms | "
f"Coût: ${cost_usd:.4f}"
)
return DialogueResponse(
npc_id=request.npc_id,
response=result["response"],
model_used=result["model_used"],
tokens_used=result["tokens_used"],
latency_ms=latency_ms,
emotional_tone=result["emotional_tone"],
suggested_actions=result.get("suggested_actions", []),
cached=False,
cost_usd=cost_usd
)
except Exception as e:
logger.error(f"Erreur génération dialogue: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/v1/npc/context")
async def update_npc_context(update: NPCContextUpdate):
"""Met à jour le contexte d'un NPC pour un joueur spécifique."""
try:
if update.trust_level is not None or update.emotional_state is not None:
await memory_manager.update_relationship_memory(
update.npc_id,
update.player_id,
{
"trust_level": update.trust_level or 0.5,
"key_moment": "Mise à jour depuis le game engine"
}
)
if update.quest_event:
await memory_manager.record_quest_event(
update.npc_id,
update.player_id,
update.quest_event["quest_id"],
update.quest_event["event_type"],
update.quest_event["description"]
)
return {"status": "success", "message": "Contexte mis à jour"}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.get("/api/v1/npc/{npc_id}/player/{player_id}/history")
async def get_conversation_history(npc_id: str, player_id: str, limit: int = 10):
"""Récupère l'historique de conversation entre un NPC et un joueur."""
history = await memory_manager.get_conversation_history(npc_id, player_id, limit)
return {"npc_id": npc_id, "player_id": player_id, "history": history}
@app.get("/api/v1/health")
async def health_check():
"""Vérification de santé de l'API."""
return {
"status": "healthy",
"holy_sheep_connected": True,
"latency_target": "<50ms",
"models_available": ["gpt-4.1", "claude-sonnet-4.5", "gemini-2.5-flash", "deepseek-v3.2"]
}
async def build_npc_context(npc_id: str, player_id: str, npc_type: NPCType) -> NPCContext:
"""Construit le contexte NPC à partir de la mémoire persistante."""
context_summary = await memory_manager.get_context_summary(npc_id, player_id)
# Templates de base pour chaque type de NPC
base_contexts = {
NPCType.GUARDIAN: NPCContext(
npc_id=npc_id,
name="Gardien",
personality=NPCPersonality.GUARDIAN,
world_knowledge="Un monde en guerre où chaque choix compte.",
conversation_history=[],
emotional_state=0.0,
trust_level=0.5
),
NPCType.MERCHANT: NPCContext(
npc_id=npc_id,
name="Marchand",
personality=NPCPersonality.MERCHANT,
world_knowledge="Les routes commerciales sont危险危险.",
conversation_history=[],
emotional_state=0.3,
trust_level=0.5
),
NPCType.COMPANION: NPCContext(
npc_id=npc_id,
name="Compagnon",
personality=NPCPersonality.COMPANION,
world_knowledge="Une amitié forgée dans l adversity.",
conversation_history=[],
emotional_state=0.6,
trust_level=0.7
)
}
ctx = base_contexts.get(npc_type, base_contexts[NPCType.GUARDIAN])
ctx.world_knowledge += f" | Historique: {context_summary}"
return ctx
Exemple de requête curl pour tester
"""
curl -X POST "http://localhost:8000/api/v1/dialogue" \\
-H "Content-Type: application/json" \\
-d '{
"npc_id": "guard_captain",
"player_id": "player_001",
"player_input": "Je cherche l'entrée du donjon.",
"npc_type": "guardian",
"temperature": 0.7,
"max_tokens": 200
}'
"""
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
Optimisation des performances et des coûts
Dans mes déploiements en production, j'ai identifié plusieurs stratégies critiques pour maintenir une latence inférieure à 50ms tout en optimisant les coûts.
Stratégie de modèles recommandée par volume
| Volume mensuel | Modèle recommandé | Coût estimatif HolySheep | Latence moyenne |
|---|---|---|---|
| < 1M tokens | DeepSeek V3.2 | 420 $ | < 30ms |
| 1M - 10M tokens | Gemini 2.5 Flash | 3 750 $ | < 40ms |
| 10M - 100M tokens | Mix Gemini + DeepSeek | 15 000 $ | < 45ms |