Năm 2026, ngành game đang chứng kiến cuộc cách mạng khi AI NPC không còn là khái niệm xa vời mà trở thành tiêu chuẩn bắt buộc trong các AAA titles. Trong bài viết này, tôi sẽ chia sẻ kinh nghiệm thực chiến khi triển khai hệ thống AI NPC cho dự án RPG của mình — sử dụng HolySheep AI làm backend với chi phí chỉ bằng 1/20 so với các giải pháp truyền thống.

Bảng So Sánh Chi Phí API AI 2026

Model Giá/MTok 10M Tokens/Tháng Độ trễ TB Phù hợp
DeepSeek V3.2 $0.42 $4,200 <50ms Hội thoại NPC, QA
Gemini 2.5 Flash $2.50 $25,000 <80ms NPC thông minh cao
GPT-4.1 $8.00 $80,000 <100ms NPC boss, plot twist
Claude Sonnet 4.5 $15.00 $150,000 <120ms Narrative chính

Tại sao DeepSeek V3.2 với $0.42/MTok lại là lựa chọn số một cho AI NPC? Vì một NPC trung bình tiêu tốn khoảng 500-2000 tokens/response. Với 1000 NPCs active cùng lúc, chi phí hàng tháng giảm từ $15,000 xuống còn $420 — con số mà bất kỳ indie studio nào cũng có thể chấp nhận được.

Tại Sao Cần AI NPC Thế Hệ Mới?

Traditional NPC sử dụng cây quyết định cố định (decision tree) với khoảng 50-200 responses được hardcode. Player nhanh chóng nhận ra pattern và game trở nên nhàm chán sau 2-3 playthroughs. AI NPC giải quyết vấn đề này bằng cách tạo ra responses động dựa trên context, player history, và world state.

Với Unreal Engine 5, chúng ta có thể tận dụng:

Kiến Trúc Hệ Thống AI NPC Với HolySheep API

Đây là kiến trúc mà tôi đã deploy thành công cho dự án có 500+ NPCs với độ trễ trung bình dưới 50ms:

Unreal Engine 5 Project
│
├── HolySheepNPCManager (GameInstance Subsystem)
│   ├── API Connection Pool
│   ├── Token Budget Controller
│   └── Response Cache System
│
├── NPCController (AIController)
│   ├── Behavior Tree
│   ├── BlackBoard (Context Memory)
│   └── HolySheepComponent
│
└── HolySheepService (Async Task)
    ├── Prompt Builder
    ├── Response Parser
    └── Cost Tracker

Cài Đặt HolySheep Plugin Trong UE5

// HolySheepNPCManager.h
#pragma once

#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "Http.h"
#include "Json.h"
#include "HolySheepNPCManager.generated.h"

USTRUCT(BlueprintType)
struct FHolySheepConfig
{
    GENERATED_BODY()
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FString APIKey = "YOUR_HOLYSHEEP_API_KEY";
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FString BaseURL = "https://api.holysheep.ai/v1";
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FString Model = "deepseek-chat"; // DeepSeek V3.2
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    float MaxTokens = 150;
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    float Temperature = 0.8;
};

USTRUCT(BlueprintType)
struct FNPCMemory
{
    GENERATED_BODY()
    
    UPROPERTY()
    FString CharacterID;
    
    UPROPERTY()
    TArray RecentInteractions;
    
    UPROPERTY()
    int32 TotalTokensUsed = 0;
    
    UPROPERTY()
    float LastInteractionTime = 0.f;
};

UCLASS()
class AINPC_API UHolySheepNPCManager : public UGameInstanceSubsystem
{
    GENERATED_BODY()

public:
    virtual void Initialize(FSubsystemCollectionBase& Collection) override;
    virtual void Deinitialize() override;
    
    UFUNCTION(BlueprintCallable)
    void Configure(const FHolySheepConfig& Config);
    
    UFUNCTION(BlueprintCallable)
    void GenerateNPCResponse(
        const FString& NPCID,
        const FString& PlayerInput,
        const FString& WorldContext,
        const FOnNPCResponse& OnComplete
    );
    
    UFUNCTION(BlueprintPure)
    float GetCurrentMonthlyCost() const { return MonthlyCost; }
    
    UFUNCTION(BlueprintPure)
    int32 GetTotalRequestsToday() const { return TotalRequestsToday; }

private:
    void SendAPIRequest(const FString& NPCID, const FString& Payload, const FOnNPCResponse& Callback);
    FString BuildPrompt(const FString& NPCID, const FString& PlayerInput, const FString& Context);
    void ParseResponse(const FString& Response, FNPCResponse& OutResponse);
    void UpdateCostTracking(float TokensUsed);
    
    FHolySheepConfig CurrentConfig;
    TMap<FString, FNPCMemory> NPCMemories;
    FHttpModule* HttpModule;
    
    float MonthlyCost = 0.f;
    int32 TotalRequestsToday = 0;
    int32 TotalTokensToday = 0;
};

Implementation Chi Tiết

// HolySheepNPCManager.cpp
#include "HolySheepNPCManager.h"
#include "Engine/World.h"
#include "Kismet/GameplayStatics.h"

void UHolySheepNPCManager::Initialize(FSubsystemCollectionBase& Collection)
{
    Super::Initialize(Collection);
    HttpModule = &FHttpModule::Get();
    
    // Load saved cost data
    MonthlyCost = 0.f;
    TotalRequestsToday = 0;
    TotalTokensToday = 0;
    
    UE_LOG(LogTemp, Log, TEXT("HolySheepNPCManager initialized - Base URL: %s"), 
           *CurrentConfig.BaseURL);
}

void UHolySheepNPCManager::Configure(const FHolySheepConfig& Config)
{
    CurrentConfig = Config;
    
    UE_LOG(LogTemp, Warning, TEXT("HolySheep configured with model: %s"), *Config.Model);
}

void UHolySheepNPCManager::GenerateNPCResponse(
    const FString& NPCID,
    const FString& PlayerInput,
    const FString& WorldContext,
    const FOnNPCResponse& OnComplete)
{
    // Validate inputs
    if (PlayerInput.IsEmpty())
    {
        FNPCResponse EmptyResponse;
        EmptyResponse.Success = false;
        EmptyResponse.ErrorMessage = "Player input is empty";
        OnComplete.Broadcast(EmptyResponse);
        return;
    }
    
    // Build prompt
    FString Prompt = BuildPrompt(NPCID, PlayerInput, WorldContext);
    
    // Create JSON payload
    TSharedPtr<FJsonObject> JsonObject = MakeShared<FJsonObject>();
    JsonObject->SetStringField("model", CurrentConfig.Model);
    
    TArray<TSharedPtr<FJsonValue>> Messages;
    
    // System message - NPC personality
    TSharedPtr<FJsonObject> SystemMsg = MakeShared<FJsonObject>();
    SystemMsg->SetStringField("role", "system");
    SystemMsg->SetStringField("content", 
        "You are a medieval fantasy NPC. Keep responses under 150 tokens. "
        "Stay in character. Reference the world context when relevant.");
    Messages.Add(MakeShared<FJsonValue>(SystemMsg));
    
    // Context from NPC memory
    if (FNPCMemory* Memory = NPCMemories.Find(NPCID))
    {
        FString MemoryContext = "Recent interactions:\n";
        for (const FString& Interaction : Memory->RecentInteractions)
        {
            MemoryContext += Interaction + "\n";
        }
        
        TSharedPtr<FJsonObject> ContextMsg = MakeShared<FJsonObject>();
        ContextMsg->SetStringField("role", "assistant");
        ContextMsg->SetStringField("content", MemoryContext);
        Messages.Add(MakeShared<FJsonValue>(ContextMsg));
    }
    
    // User input
    TSharedPtr<FJsonObject> UserMsg = MakeShared<FJsonObject>();
    UserMsg->SetStringField("role", "user");
    UserMsg->SetStringField("content", Prompt);
    Messages.Add(MakeShared<FJsonValue>(UserMsg));
    
    JsonObject->SetArrayField("messages", Messages);
    JsonObject->SetNumberField("max_tokens", CurrentConfig.MaxTokens);
    JsonObject->SetNumberField("temperature", CurrentConfig.Temperature);
    
    // Serialize to string
    FString Payload;
    TJsonWriter<TCHAR>* Writer;
    FJsonSerializer::Serialize(JsonObject.ToSharedRef(), Writer);
    Payload = Writer->GetString();
    delete Writer;
    
    SendAPIRequest(NPCID, Payload, OnComplete);
}

FString UHolySheepNPCManager::BuildPrompt(
    const FString& NPCID, 
    const FString& PlayerInput, 
    const FString& WorldContext)
{
    FString Prompt = FString::Printf(
        TEXT("World Context: %s\n\nPlayer says: \"%s\"\n\nRespond as this NPC with a brief, in-character dialogue response:"),
        *WorldContext,
        *PlayerInput
    );
    return Prompt;
}

void UHolySheepNPCManager::SendAPIRequest(
    const FString& NPCID, 
    const FString& Payload, 
    const FOnNPCResponse& Callback)
{
    TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = HttpModule->CreateRequest();
    
    FString Endpoint = CurrentConfig.BaseURL + "/chat/completions";
    Request->SetURL(Endpoint);
    Request->SetVerb("POST");
    Request->SetHeader("Content-Type", "application/json");
    Request->SetHeader("Authorization", "Bearer " + CurrentConfig.APIKey);
    Request->SetContentAsString(Payload);
    
    Request->OnProcessRequestComplete().BindLambda(
        [this, NPCID, Callback](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess)
        {
            FNPCResponse OutResponse;
            
            if (!bSuccess || !Response.IsValid())
            {
                OutResponse.Success = false;
                OutResponse.ErrorMessage = "Network request failed";
                Callback.Broadcast(OutResponse);
                return;
            }
            
            int32 ResponseCode = Response->GetResponseCode();
            if (ResponseCode != 200)
            {
                OutResponse.Success = false;
                OutResponse.ErrorMessage = FString::Printf(
                    TEXT("API Error: HTTP %d - %s"), 
                    ResponseCode, 
                    *Response->GetContentAsString()
                );
                Callback.Broadcast(OutResponse);
                return;
            }
            
            ParseResponse(Response->GetContentAsString(), OutResponse);
            
            if (OutResponse.Success)
            {
                // Update NPC memory
                FNPCMemory* Memory = &NPCMemories.FindOrAdd(NPCID);
                Memory->CharacterID = NPCID;
                Memory->RecentInteractions.Add(OutResponse.ResponseText);
                Memory->TotalTokensUsed += OutResponse.TokensUsed;
                
                // Keep only last 5 interactions
                if (Memory->RecentInteractions.Num() > 5)
                {
                    Memory->RecentInteractions.RemoveAt(0);
                }
                
                // Update cost tracking
                UpdateCostTracking(OutResponse.TokensUsed);
                TotalRequestsToday++;
            }
            
            Callback.Broadcast(OutResponse);
        }
    );
    
    Request->ProcessRequest();
}

void UHolySheepNPCManager::ParseResponse(const FString& ResponseString, FNPCResponse& OutResponse)
{
    TSharedPtr<FJsonObject> JsonObject;
    TJsonReader<TCHAR>* Reader = TJsonReader<TCHAR>::Create(ResponseString);
    
    if (!FJsonSerializer::Deserialize(Reader, JsonObject) || !JsonObject.IsValid())
    {
        OutResponse.Success = false;
        OutResponse.ErrorMessage = "Failed to parse JSON response";
        delete Reader;
        return;
    }
    delete Reader;
    
    // Check for error in response
    if (JsonObject->HasField("error"))
    {
        OutResponse.Success = false;
        auto ErrorObj = JsonObject->GetObjectField("error");
        OutResponse.ErrorMessage = ErrorObj->GetStringField("message");
        return;
    }
    
    // Extract content
    if (JsonObject->HasField("choices"))
    {
        auto Choices = JsonObject->GetArrayField("choices");
        if (Choices.Num() > 0)
        {
            auto FirstChoice = Choices[0]->GetAsObject();
            auto Message = FirstChoice->GetObjectField("message");
            OutResponse.ResponseText = Message->GetStringField("content");
            OutResponse.Success = true;
        }
    }
    
    // Extract usage stats
    if (JsonObject->HasField("usage"))
    {
        auto Usage = JsonObject->GetObjectField("usage");
        OutResponse.TokensUsed = Usage->GetIntegerField("total_tokens");
    }
    
    // Calculate cost
    float PricePerToken = 0.f;
    if (CurrentConfig.Model.Contains("deepseek"))
        PricePerToken = 0.00000042f; // $0.42 per 1M tokens
    else if (CurrentConfig.Model.Contains("gpt"))
        PricePerToken = 0.000008f; // $8 per 1M tokens
    
    OutResponse.CostUSD = OutResponse.TokensUsed * PricePerToken;
}

Tích Hợp Với Unreal Engine 5 Behavior Tree

// HolySheepNPCController.h
UCLASS()
class AINPC_API AHolySheepNPCController : public AAIController
{
    GENERATED_BODY()

public:
    AHolySheepNPCController();
    
    virtual void BeginPlay() override;
    
    UFUNCTION(BlueprintCallable)
    void InteractWithPlayer(APawn* PlayerPawn, const FString& Context);
    
    UFUNCTION(BlueprintCallable)
    void OnNPCResponseReceived(const FNPCResponse& Response);

protected:
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
    class UHolySheepComponent* HolySheepComp;
    
    UPROPERTY(EditDefaultsOnly, Category = "AI NPC")
    FString NPCPersonality = "Friendly tavern keeper";
    
    UPROPERTY(EditDefaultsOnly, Category = "AI NPC")
    FString NPCBackground = "Has run the tavern for 30 years, knows everyone's secrets";
    
    // BlackBoard keys
    UPROPERTY(EditDefaultsOnly, Category = "AI NPC")
    FName CurrentDialogueKey = "CurrentDialogue";
    
    UPROPERTY(EditDefaultsOnly, Category = "AI NPC")
    FName PlayerMoodKey = "PlayerMood";
    
private:
    FString BuildWorldContext() const;
};
// HolySheepNPCController.cpp
#include "HolySheepNPCController.h"
#include "HolySheepNPCManager.h"
#include "HolySheepComponent.h"
#include "Kismet/GameplayStatics.h"
#include "GameFramework/PlayerController.h"

AHolySheepNPCController::AHolySheepNPCController()
{
    HolySheepComp = CreateDefaultSubobject<UHolySheepComponent>(TEXT("HolySheep"));
}

void AHolySheepNPCController::BeginPlay()
{
    Super::BeginPlay();
    
    // Run Behavior Tree if assigned
    if (BehaviorTree)
    {
        RunBehaviorTree(BehaviorTree);
    }
}

void AHolySheepNPCController::InteractWithPlayer(APawn* PlayerPawn, const FString& Context)
{
    if (!PlayerPawn)
    {
        UE_LOG(LogTemp, Error, TEXT("No player pawn for interaction"));
        return;
    }
    
    // Get the HolySheep subsystem
    UGameInstance* GameInstance = GetWorld()->GetGameInstance();
    UHolySheepNPCManager* HolySheepManager = 
        GameInstance->GetSubsystem<UHolySheepNPCManager>();
    
    if (!HolySheepManager)
    {
        UE_LOG(LogTemp, Error, TEXT("HolySheepManager not found"));
        return;
    }
    
    // Get player input (you'd implement your own input system)
    FString PlayerInput = "Hello there!"; // Replace with actual player text
    
    // Build full context
    FString WorldContext = BuildWorldContext() + "\n" + Context;
    
    // Get NPC identifier
    FString NPCID = GetPawn() ? GetPawn()->GetName() : "Unknown_NPC";
    
    // Request AI response
    FOnNPCResponse OnComplete;
    OnComplete.AddDynamic(this, &AHolySheepNPCController::OnNPCResponseReceived);
    
    HolySheepManager->GenerateNPCResponse(NPCID, PlayerInput, WorldContext, OnComplete);
    
    // Log for debugging
    UE_LOG(LogTemp, Log, TEXT("NPC %s: Requesting AI response..."), *NPCID);
}

void AHolySheepNPCController::OnNPCResponseReceived(const FNPCResponse& Response)
{
    if (!Response.Success)
    {
        UE_LOG(LogTemp, Error, TEXT("AI Response failed: %s"), *Response.ErrorMessage);
        return;
    }
    
    // Update BlackBoard with response
    if (UBlackboardComponent* BB = GetBlackboardComponent())
    {
        BB->SetValueAsString(CurrentDialogueKey, Response.ResponseText);
    }
    
    // Display dialogue (implement your own UI)
    UE_LOG(LogTemp, Log, TEXT("NPC says: %s"), *Response.ResponseText);
    
    // Log cost for monitoring
    UE_LOG(LogTemp, Warning, TEXT("Response cost: $%.6f (%d tokens)"), 
           Response.CostUSD, Response.TokensUsed);
}

FString AHolySheepNPCController::BuildWorldContext() const
{
    FString Context = FString::Printf(
        TEXT("NPC Personality: %s\n"
             "NPC Background: %s\n"
             "Location: %s\n"
             "Time: Day %d\n"),
        *NPCPersonality,
        *NPCBackground,
        *GetPawn()->GetActorLocation().ToString(),
        FDateTime::Now().GetDayOfYear()
    );
    
    // Add nearby characters
    TArray<AActor*> NearbyActors;
    UGameplayStatics::GetAllActorsWithTag(GetWorld(), "NPC", NearbyActors);
    
    Context += "Nearby NPCs: ";
    for (AActor* Actor : NearbyActors)
    {
        Context += Actor->GetName() + ", ";
    }
    
    return Context;
}

Tối Ưu Chi Phí Với Token Budgeting

// TokenBudgetController.h - Quản lý chi phí cho 1000+ NPCs
UCLASS()
class AINPC_API UTokenBudgetController : public UGameInstanceSubsystem
{
    GENERATED_BODY()

public:
    // Thiết lập ngân sách hàng tháng
    UFUNCTION(BlueprintCallable)
    void SetMonthlyBudget(float BudgetUSD);
    
    // Kiểm tra xem có thể gọi API không
    UFUNCTION(BlueprintPure)
    bool CanAffordRequest(float EstimatedTokens) const;
    
    // Đánh giá priority của NPC (0-100)
    UFUNCTION(BlueprintPure)
    int32 CalculateNPCPriority(class AAIController* NPC) const;
    
    // Điều chỉnh model dựa trên budget
    UFUNCTION(BlueprintPure)
    FString GetOptimalModelForBudget(class AAIController* NPC) const;

private:
    float MonthlyBudget = 500.f; // Mặc định $500/tháng
    float SpentThisMonth = 0.f;
    
    // Priority tiers
    TArray<FString> CriticalNPCs; // Main quest NPCs - dùng GPT-4.1
    TArray<FString> ImportantNPCs; // Side quest - dùng Gemini 2.5
    TArray<FString> RegularNPCs; // Shop keepers, etc - dùng DeepSeek
};

Lỗi Thường Gặp Và Cách Khắc Phục

1. Lỗi: "Invalid API Key" Hoặc Authentication Failed

Mã lỗi: HTTP 401

// Cách khắc phục:
void FixAuthenticationError()
{
    // Kiểm tra API key format
    FString APIKey = "YOUR_HOLYSHEEP_API_KEY";
    
    // Đảm bảo không có khoảng trắng thừa
    APIKey = APIKey.TrimStartAndEnd();
    
    // Nếu dùng biến môi trường (khuyến nghị)
    #if WITH_EDITOR
    APIKey = GConfig->GetStr(TEXT("HolySheep"), TEXT("APIKey"), 
                             FPaths::ProjectDir() + "Config/DefaultEngine.ini");
    #endif
    
    // Validate key format (HolySheep keys bắt đầu bằng "hs_")
    if (!APIKey.StartsWith("hs_"))
    {
        UE_LOG(LogTemp, Error, TEXT("Invalid HolySheep API key format. Should start with 'hs_'"));
        return;
    }
    
    UE_LOG(LogTemp, Log, TEXT("API Key validated successfully"));
}

2. Lỗi: "Rate Limit Exceeded" - Quá Nhiều Request

Mã lỗi: HTTP 429

// Cách khắc phục - Implement request queuing
UCLASS()
class UHolySheepRequestQueue : public UObject
{
    GENERATED_BODY()

public:
    void EnqueueRequest(FNPCAPIRequest Request);
    void ProcessQueue();
    
private:
    TQueue<FNPCAPIRequest> RequestQueue;
    FTimerHandle QueueTimer;
    int32 MaxRequestsPerSecond = 10;
    int32 CurrentRequestsThisSecond = 0;
    
    void OnRequestComplete();
};

void UHolySheepRequestQueue::EnqueueRequest(FNPCAPIRequest Request)
{
    RequestQueue.Enqueue(Request);
    
    // Auto-start processing nếu chưa chạy
    if (!QueueTimer.IsValid())
    {
        GetWorld()->GetTimerManager().SetTimer(
            QueueTimer, 
            this, 
            &UHolySheepRequestQueue::ProcessQueue, 
            0.1f, // Process mỗi 100ms
            true
        );
    }
}

void UHolySheepRequestQueue::ProcessQueue()
{
    // Reset counter mỗi giây
    static float LastResetTime = 0.f;
    float CurrentTime = GetWorld()->GetTimeSeconds();
    
    if (CurrentTime - LastResetTime >= 1.0f)
    {
        CurrentRequestsThisSecond = 0;
        LastResetTime = CurrentTime;
    }
    
    // Throttle: không quá MaxRequestsPerSecond/giây
    if (CurrentRequestsThisSecond >= MaxRequestsPerSecond)
    {
        return;
    }
    
    // Process next request
    FNPCAPIRequest Request;
    if (RequestQueue.Dequeue(Request))
    {
        SendToAPI(Request);
        CurrentRequestsThisSecond++;
    }
    else
    {
        // Queue trống, dừng timer
        GetWorld()->GetTimerManager().ClearTimer(QueueTimer);
        QueueTimer.Invalidate();
    }
}

3. Lỗi: Response Quá Chậm (>500ms) Gây Lag

Vấn đề: AI response blocking main thread

// Cách khắc phục - Implement fallback responses
UENUM(BlueprintType)
enum class ENPCResponseFallback : uint8
{
    Generic_Greeting,
    Generic_Farewell,
    Generic_Question,
    Busy_Working,
    Confused_NotUnderstand
};

UFUNCTION(BlueprintCallable)
FString GetFallbackResponse(ENPCResponseFallback FallbackType)
{
    switch (FallbackType)
    {
        case ENPCResponseFallback::Generic_Greeting:
            return "Chào ngài! Chào mừng đến với quán rượu của ta.";
        
        case ENPCResponseFallback::Generic_Farewell:
            return "Cảm ơn ngài đã ghé thăm! Hẹn gặp lại.";
        
        case ENPCResponseFallback::Generic_Question:
            return "Hmm, ta không hiểu ý ngài lắm. Ngài có thể nói rõ hơn không?";
        
        case ENPCResponseFallback::Busy_Working:
            return "Xin lỗi ngài, ta đang bận. Để lát nữa ta nói chuyện nhé.";
        
        case ENPCResponseFallback::Confused_NotUnderstand:
            return "Ta... ta không rõ ngài đang nói gì. Ta chỉ là một chủ quán rượu thôi.";
        
        default:
            return "Xin chào, ngài cần gì ạ?";
    }
}

// Trong HolySheepNPCManager, thêm timeout handling:
void UHolySheepNPCManager::GenerateNPCResponseWithTimeout(
    const FString& NPCID,
    const FString& PlayerInput,
    const FString& WorldContext,
    const FOnNPCResponse& OnComplete,
    float TimeoutSeconds = 3.0f) // 3 giây timeout
{
    // Tạo request
    FOnNPCResponse OriginalCallback;
    OriginalCallback.AddLambda([OnComplete, this, NPCID](const FNPCResponse& Response)
    {
        if (Response.Success)
        {
            OnComplete.Broadcast(Response);
        }
        else
        {
            // Trigger fallback
            FNPCResponse FallbackResponse;
            FallbackResponse.Success = true;
            FallbackResponse.ResponseText = GetFallbackResponse(
                ENPCResponseFallback::Generic_Greeting
            );
            FallbackResponse.IsFallback = true;
            FallbackResponse.ErrorMessage = Response.ErrorMessage;
            
            OnComplete.Broadcast(FallbackResponse);
            
            UE_LOG(LogTemp, Warning, 
                TEXT("NPC %s: Using fallback response due to: %s"), 
                *NPCID, *Response.ErrorMessage);
        }
    });
    
    // Bắt đầu request với timeout
    FTimerHandle TimeoutTimer;
    bool bResponseReceived = false;
    
    GetWorld()->GetTimerManager().SetTimer(
        TimeoutTimer,
        [OnComplete, &bResponseReceived]()
        {
            if (!bResponseReceived)
            {
                FNPCResponse TimeoutResponse;
                TimeoutResponse.Success = true;
                TimeoutResponse.ResponseText = GetFallbackResponse(
                    ENPCResponseFallback::Confused_NotUnderstand
                );
                TimeoutResponse.IsFallback = true;
                TimeoutResponse.ErrorMessage = "Request timeout";
                
                OnComplete.Broadcast(TimeoutResponse);
                bResponseReceived = true;
            }
        },
        TimeoutSeconds,
        false
    );
    
    // Gọi request thực tế
    GenerateNPCResponse(NPCID, PlayerInput, WorldContext, 
        [TimeoutTimer, &bResponseReceived](const FNPCResponse& Response)
        {
            bResponseReceived = true;
            // Timeout timer sẽ tự hủy
        }
    );
}

4. Lỗi: Context Window Quá Nhỏ - NPC Không Nhớ Lịch Sử

Vấn đề: DeepSeek V3.2 có context window 64K tokens, nhưng chúng ta cần quản lý memory hiệu quả

// Memory Compression để tối ưu context
FString CompressNPCMemory(const TArray<FString>& Interactions, int32 MaxInteractions = 5)
{
    if (Interactions.Num() <= MaxInteractions)
    {
        FString Result;
        for (const FString& Interaction : Interactions)
        {
            Result += Interaction + "\n---\n";
        }
        return Result;
    }
    
    // Summarize older interactions
    FString Summary = "Tổng kết các tương tác trước:\n";
    
    // Lấy 2 interactions gần nhất (giữ chi tiết)
    for (int32 i = Interactions.Num() - 2; i < Interactions.Num(); i++)
    {
        Summary += Interactions[i] + "\n";
    }
    
    // Summarize interactions cũ
    Summary += FString::Printf(
        TEXT("\n[Đã có %d lần tương tác trước đó, bao gồm chào hỏi, mua bán hàng hóa, và một số câu hỏi về nhiệm vụ]"),
        Interactions.Num() - 2
    );
    
    return Summary;
}

Phù Hợp Với Ai

Nên Sử Dụng Nếu: