Trong thời đại AI bùng nổ, việc xây dựng một engine kể chuyện tương tác (interactive narrative engine) không còn là đặc quyền của các tập đoàn công nghệ lớn. Bài viết này sẽ hướng dẫn bạn từng bước triển khai hệ thống tương tự AI Dungeon, tích hợp với HolySheep AI — nền tảng API LLM với độ trễ dưới 50ms và chi phí thấp nhất thị trường.

Nghiên cứu điển hình: Startup Game AI tại TP.HCM

Một studio game indie tại TP.HCM chuyên phát triển game nhập vai tiếng Việt đã gặp khó khăn nghiêm trọng khi vận hành engine kể chuyện trên nền tảng cũ. Hệ thống sử dụng API của nhà cung cấp quốc tế với độ trễ trung bình 850ms cho mỗi lượt tương tác — quá chậm khiến người chơi mất hứng thú. Bên cạnh đó, chi phí API hàng tháng lên đến $4,200 cho 2 triệu token, trong khi ngân sách marketing chỉ còn $800.

Sau khi chuyển sang HolySheep AI, đội ngũ đã giảm độ trễ xuống còn 180ms (giảm 79%) và tiết kiệm chi phí API xuống chỉ còn $680/tháng (giảm 84%). Điều này cho phép họ mở rộng hệ thống từ 500 lượt chơi đồng thời lên 5,000 mà không cần tăng ngân sách.

Kiến trúc hệ thống AI Dungeon Engine

Tổng quan kiến trúc

Hệ thống AI Dungeon bao gồm các thành phần chính: Context Manager (quản lý lịch sử hội thoại), Story Engine (xử lý sinh nội dung), State Tracker (theo dõi trạng thái thế giới), và Action Resolver (xử lý hành động người chơi). Dưới đây là kiến trúc mô-đun hoá dễ mở rộng.

# Cấu trúc thư mục dự án
ai-dungeon-engine/
├── src/
│   ├── __init__.py
│   ├── config.py              # Cấu hình HolySheep API
│   ├── story_engine.py        # Engine sinh nội dung
│   ├── context_manager.py     # Quản lý ngữ cảnh
│   ├── state_tracker.py       # Theo dõi trạng thái thế giới
│   ├── action_resolver.py     # Xử lý hành động
│   └── models.py              # Định nghĩa data models
├── tests/
├── requirements.txt
└── main.py                    # Entry point

requirements.txt

openai>=1.12.0 pydantic>=2.5.0 redis>=5.0.0 fasthtml>=0.1.0

Cấu hình kết nối HolySheep AI

Việc cấu hình đúng endpoint và authentication là bước nền tảng quan trọng nhất. HolySheep AI cung cấp endpoint tương thích OpenAI format, giúp việc di chuyển trở nên vô cùng đơn giản.

# src/config.py
import os
from dataclasses import dataclass

@dataclass
class HolySheepConfig:
    """Cấu hình kết nối HolySheep AI - Độ trễ <50ms, giá thấp nhất"""
    
    # Endpoint chuẩn hoá - KHÔNG sử dụng api.openai.com
    base_url: str = "https://api.holysheep.ai/v1"
    
    # API Key từ HolySheep Dashboard
    api_key: str = os.getenv("HOLYSHEEP_API_KEY", "YOUR_HOLYSHEEP_API_KEY")
    
    # Model mapping theo use case
    # DeepSeek V3.2: $0.42/MTok - Tối ưu chi phí cho generation
    # Gemini 2.5 Flash: $2.50/MTok - Cân bằng speed/quality
    # GPT-4.1: $8/MTok - Chất lượng cao nhất
    
    STORY_MODEL: str = "deepseek-v3.2"      # Sinh nội dung chính
    ACTION_MODEL: str = "gemini-2.5-flash"   # Xử lý hành động nhanh
    PLANNING_MODEL: str = "gpt-4.1"          # Lập kế hoạch dài hạn
    
    # Timeout và retry settings
    request_timeout: int = 30
    max_retries: int = 3
    retry_delay: float = 1.0

Singleton instance

config = HolySheepConfig()
# src/story_engine.py
import httpx
from typing import List, Dict, Optional, AsyncIterator
from .config import config

class HolySheepClient:
    """Client tương thích OpenAI cho HolySheep AI"""
    
    def __init__(self):
        self.base_url = config.base_url
        self.api_key = config.api_key
        self._client = httpx.AsyncClient(
            base_url=self.base_url,
            headers={
                "Authorization": f"Bearer {self.api_key}",
                "Content-Type": "application/json"
            },
            timeout=config.request_timeout
        )
    
    async def generate_stream(
        self, 
        messages: List[Dict], 
        model: str,
        temperature: float = 0.8,
        max_tokens: int = 500
    ) -> AsyncIterator[str]:
        """Streaming generation - phản hồi từng chunk cho UX mượt mà"""
        
        payload = {
            "model": model,
            "messages": messages,
            "temperature": temperature,
            "max_tokens": max_tokens,
            "stream": True
        }
        
        async with self._client.stream("POST", "/chat/completions", json=payload) as response:
            response.raise_for_status()
            async for line in response.aiter_lines():
                if line.startswith("data: "):
                    data = line[6:]
                    if data == "[DONE]":
                        break
                    # Parse SSE format
                    chunk = json.loads(data)
                    if chunk.get("choices")[0].get("delta", {}).get("content"):
                        yield chunk["choices"][0]["delta"]["content"]

import json

class StoryEngine:
    """Engine sinh nội dung narrative - trái tim của AI Dungeon"""
    
    SYSTEM_PROMPT = """Bạn là một game master chuyên nghiệp trong game nhập vai.
Nhiệm vụ của bạn:
1. Mô tả cảnh quan, nhân vật, sự kiện một cách sống động
2. Tạo tình huống bất ngờ nhưng logic với cốt truyện
3. Không quá 200 từ cho mỗi đoạn văn
4. Luôn kết thúc bằng lựa chọn cho người chơi
5. Ngôn ngữ: tiếng Việt, giọng văn hấp dẫn"""

    def __init__(self):
        self.client = HolySheepClient()
        self.conversation_history: List[Dict] = []
    
    async def generate_story_segment(
        self, 
        player_action: str,
        world_state: Dict
    ) -> str:
        """Sinh đoạn kể chuyện tiếp theo dựa trên hành động người chơi"""
        
        # Build context với world state
        context_msg = f"Trạng thái thế giới hiện tại: {world_state}"
        messages = [
            {"role": "system", "content": self.SYSTEM_PROMPT},
            {"role": "system", "content": context_msg},
            *self.conversation_history[-10:],  # Giữ 10 message gần nhất
            {"role": "user", "content": player_action}
        ]
        
        # Streaming response - chunks được yield liên tục
        response_chunks = []
        async for chunk in self.client.generate_stream(
            messages=messages,
            model=config.STORY_MODEL,
            temperature=0.8
        ):
            response_chunks.append(chunk)
            yield chunk
        
        full_response = "".join(response_chunks)
        self.conversation_history.append(
            {"role": "user", "content": player_action},
            {"role": "assistant", "content": full_response}
        )
        
        return full_response

Ví dụ sử dụng trong main.py

async def main(): engine = StoryEngine() world_state = { "location": "Rừng sâu với ánh trăng le lói", "health": 85, "inventory": ["kiếm cũ", "bình thuốc"], "enemy_nearby": " sói đói" } print("🎮 Chào mừng đến với AI Dungeon!") print("=" * 50) while True: player_input = input("\n👤 Hành động của bạn: ") if player_input.lower() in ["exit", "quit"]: break print("\n📖 Đang viết...", end="", flush=True) story = "" async for chunk in engine.generate_story_segment(player_input, world_state): print(chunk, end="", flush=True) story += chunk print("\n" + "-" * 50)

Context Manager — Quản lý ngữ cảnh thông minh

Một trong những thách thức lớn nhất của AI Dungeon là duy trì continuity (tính liên tục) của câu chuyện. Context Manager giải quyết bằng cách tóm tắt lịch sử và theo dõi các sự kiện quan trọng.

# src/context_manager.py
from typing import List, Dict, Optional
from dataclasses import dataclass, field
import tiktoken

@dataclass
class ConversationTurn:
    """Một lượt hội thoại"""
    player_action: str
    narrative: str
    timestamp: float
    importance: float = 1.0  # Độ quan trọng (0-1)
    entities_mentioned: List[str] = field(default_factory=list)

class ContextManager:
    """Quản lý ngữ cảnh với chiến lược tóm tắt thông minh"""
    
    MAX_TOKENS = 128000  # Giới hạn context window
    SUMMARY_TRIGGER = 90000  # Trigger summarization khi gần đạt limit
    
    def __init__(self, encoding_model: str = "cl100k_base"):
        self.encoding = tiktoken.get_encoding(encoding_model)
        self.turns: List[ConversationTurn] = []
        self.current_summary: Optional[str] = None
        self.key_events: List[Dict] = []  # Sự kiện quan trọng cần nhớ
    
    def add_turn(self, player_action: str, narrative: str, timestamp: float):
        """Thêm một lượt hội thoại mới"""
        turn = ConversationTurn(
            player_action=player_action,
            narrative=narrative,
            timestamp=timestamp
        )
        self.turns.append(turn)
        self._update_importance()
    
    def _update_importance(self):
        """Tính độ quan trọng dựa trên từ khoá"""
        important_keywords = [
            "chiến đấu", "chết", "yêu", "ghét", "phát hiện", 
            "bí mật", "kho báu", "phép thuật", "nguy hiểm",
            "quyết định", "giao kèo", "biến mất"
        ]
        
        for turn in self.turns:
            text = (turn.player_action + turn.narrative).lower()
            turn.importance = sum(1 for kw in important_keywords if kw in text) / len(important_keywords)
    
    async def summarize_old_context(self) -> str:
        """Tóm tắt các lượt cũ để tiết kiệm context"""
        if not self.turns:
            return ""
        
        # Lấy các lượt ít quan trọng để tóm tắt
        low_importance_turns = [
            t for t in self.turns 
            if t.importance < 0.3
        ]
        
        if not low_importance_turns:
            return ""
        
        # Gọi HolySheep để tạo summary
        old_content = "\n".join([
            f"Player: {t.player_action}\nNarrator: {t.narrative}"
            for t in low_importance_turns
        ])
        
        summary_prompt = f"""Tóm tắt đoạn hội thoại sau thành 3-5 câu, 
        giữ lại thông tin quan trọng về nhân vật, địa điểm, và sự kiện:

        {old_content}"""
        
        # Sử dụng Gemini Flash cho summarization nhanh
        async with HolySheepClient() as client:
            summary = ""
            async for chunk in client.generate_stream(
                messages=[{"role": "user", "content": summary_prompt}],
                model="gemini-2.5-flash"
            ):
                summary += chunk
        
        self.current_summary = summary
        return summary
    
    def get_relevant_context(self, current_location: str) -> str:
        """Lấy ngữ cảnh liên quan cho prompt"""
        parts = []
        
        if self.current_summary:
            parts.append(f"Tổng hợp sự kiện trước: {self.current_summary}")
        
        # Lấy các sự kiện quan trọng gần đây
        important_turns = sorted(
            self.turns[-20:],  # 20 lượt gần nhất
            key=lambda t: (t.importance, t.timestamp),
            reverse=True
        )[:5]
        
        if important_turns:
            parts.append("Các sự kiện quan trọng gần đây:")
            for turn in important_turns:
                parts.append(f"- {turn.narrative[:100]}...")
        
        # Key events liên quan đến location
        relevant_events = [
            e for e in self.key_events 
            if e.get("location") == current_location
        ]
        if relevant_events:
            parts.append(f"Sự kiện tại {current_location}:")
            for event in relevant_events[-3:]:
                parts.append(f"- {event['description']}")
        
        return "\n".join(parts) if parts else "Không có ngữ cảnh trước đó."
    
    def count_tokens(self, text: str) -> int:
        """Đếm số tokens trong text"""
        return len(self.encoding.encode(text))

Integration với Story Engine

class AdvancedStoryEngine(StoryEngine): """Story Engine với Context Management nâng cao""" def __init__(self): super().__init__() self.context = ContextManager() async def generate_with_context( self, player_action: str, world_state: Dict ) -> str: """Generation với context thông minh""" # Kiểm tra và tóm tắt nếu cần total_tokens = self.context.count_tokens( str(self.context.turns) ) if total_tokens > self.context.SUMMARY_TRIGGER: await self.context.summarize_old_context() # Build messages với context context_str = self.context.get_relevant_context( world_state.get("location", "unknown") ) messages = [ {"role": "system", "content": self.SYSTEM_PROMPT}, {"role": "system", "content": f"Ngữ cảnh: {context_str}"}, {"role": "system", "content": f"Trạng thái: {world_state}"}, *self.conversation_history[-6:], {"role": "user", "content": player_action} ] response = "" async for chunk in self.client.generate_stream( messages=messages, model=config.STORY_MODEL ): response += chunk yield chunk # Cập nhật context self.context.add_turn(player_action, response, time.time()) self.conversation_history.extend([ {"role": "user", "content": player_action}, {"role": "assistant", "content": response} ])

State Tracker — Theo dõi trạng thái thế giới

Để narrative engine hoạt động nhất quán, hệ thống cần theo dõi trạng thái thế giới (world state) — bao gồm vị trí, vật phẩm, quan hệ NPC, và các biến số quan trọng. Dưới đây là implementation chi tiết.

# src/state_tracker.py
from typing import Dict, List, Optional, Any
from dataclasses import dataclass, field, asdict
from enum import Enum
import json

class EntityType(Enum):
    PLAYER = "player"
    NPC = "npc"
    ITEM = "item"
    LOCATION = "location"
    EVENT = "event"

@dataclass
class Entity:
    """Đại diện cho một thực thể trong thế giới game"""
    id: str
    type: EntityType
    name: str
    properties: Dict[str, Any] = field(default_factory=dict)
    relationships: Dict[str, List[str]] = field(default_factory=dict)  # type -> [entity_ids]

@dataclass
class WorldState:
    """Trạng thái toàn bộ thế giới game"""
    player: Dict[str, Any]
    current_location: str
    locations: Dict[str, Dict] = field(default_factory=dict)
    entities: Dict[str, Entity] = field(default_factory=dict)
    story_flags: Dict[str, bool] = field(default_factory=dict)
    inventory: List[str] = field(default_factory=list)
    quest_log: List[Dict] = field(default_factory=dict)
    
    def to_dict(self) -> Dict:
        return asdict(self)
    
    def summary(self) -> str:
        """Tạo summary ngắn gọn cho prompt"""
        return f"""
        Vị trí: {self.current_location}
        Mạng: {self.player.get('health', 100)}/100
        Vật phẩm: {', '.join(self.inventory[:5])}
        Nhiệm vụ: {[q['title'] for q in self.quest_log[:2]]}
        """

class StateTracker:
    """Theo dõi và cập nhật trạng thái thế giới"""
    
    def __init__(self):
        self.state = WorldState(