Structured Output
Structured Output is a critical feature for game development that forces the AI to respond in a strict, predictable JSON format. This eliminates the need to parse unreliable free-form text and allows you to directly integrate AI-generated data into your game systems.
Currently Supported Providers
- OpenAI
Why Use Structured Output?
When you ask an AI for game data (like character stats or quest objectives) in plain text, you might get a slightly different format every time. This makes parsing the data brittle and prone to errors. Structured Output solves this by making the AI adhere to a specific JSON Schema you provide.
- Guaranteed Format: The AI’s response is guaranteed to match the JSON structure you define. No more missing fields or incorrect data types.
- Reliable Parsing: You can reliably deserialize the JSON response directly into your C++ or Blueprint data structures.
- Simplified Prompts: You no longer need to write complex prompts begging the AI to format its response correctly. The schema handles the formatting rules.
- Perfect for Game Data: Ideal for generating character stats, quest data, item properties, procedural level layouts, or any other data that needs to fit into your game’s logic.

Blueprint Implementation
The Blueprint workflow involves defining a JSON schema as a string and passing it to the request node. You can then use Unreal’s built-in JSON utility nodes to parse the response.
- Define Your Schema: Create a string variable containing your desired JSON schema. This schema defines the “shape” of the data you want back.
- Request Structured Output: Use the
Request OpenAI Chat Completion
node (or the equivalent for your provider). In theSettings
, set theResponse Format
to your schema string and enable the appropriate JSON mode (if required by the model, like withgpt-4-turbo
). - Parse the Response: On
OnComplete
, take the JSONResponse
string and use a JSON parsing library or Unreal’s built-in nodes (like those from the VaRest or JsonBlueprintUtilities plugins) to convert it into usable data.

C++ Implementation
The C++ implementation offers a robust way to handle structured data, especially when paired with structs that mirror your JSON schema.
#include "Models/OpenAI/GenOAIChat.h"
#include "Data/OpenAI/GenOAIChatStructs.h"
#include "Serialization/JsonSerializer.h" // For JSON parsing
// It's a good practice to have a C++ struct that matches your JSON schema
USTRUCT(BlueprintType)
struct FCharacterStats
{
GENERATED_BODY()
UPROPERTY(BlueprintReadOnly) FString Name;
UPROPERTY(BlueprintReadOnly) int32 Level;
UPROPERTY(BlueprintReadOnly) int32 Health;
UPROPERTY(BlueprintReadOnly) TArray<FString> Skills;
};
void AMyDataManager::RequestCharacterData()
{
// 1. Define the JSON schema as a string.
const FString JSONSchema = TEXT(R"({
"type": "object",
"properties": {
"name": {"type": "string", "description": "The character's name."},
"level": {"type": "integer", "description": "The character's current level."},
"health": {"type": "integer", "description": "The character's health points."},
"skills": {
"type": "array",
"items": {"type": "string"},
"description": "A list of the character's unique skills."
}
},
"required": ["name", "level", "health", "skills"]
})");
// 2. Configure the chat settings for structured output.
FGenOAIChatSettings ChatSettings;
ChatSettings.Model = EOpenAIChatModel::GPT_4o;
ChatSettings.ResponseFormat.JsonObject = JSONSchema; // Set the schema
TArray<FGenChatMessage> Messages;
Messages.Add(FGenChatMessage{EGenChatRole::User, TEXT("Generate a unique fantasy RPG character with a name, level, health, and three skills.")});
TWeakObjectPtr<AMyDataManager> WeakThis(this);
// 3. Send the request.
UGenOAIChat::SendChatRequest(ChatSettings, Messages,
FOnChatCompletionResponse::CreateLambda([WeakThis](const FString& JSONResponse, const FString& Error, bool bSuccess)
{
if (!WeakThis.IsValid() || !bSuccess) return;
// 4. Parse the response on success.
WeakThis->ParseCharacterData(JSONResponse);
})
);
}
void AMyDataManager::ParseCharacterData(const FString& JSONResponse)
{
TSharedPtr<FJsonObject> JsonObject;
const TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JSONResponse);
if (FJsonSerializer::Deserialize(Reader, JsonObject) && JsonObject.IsValid())
{
FCharacterStats NewCharacter;
NewCharacter.Name = JsonObject->GetStringField("name");
NewCharacter.Level = JsonObject->GetIntegerField("level");
NewCharacter.Health = JsonObject->GetIntegerField("health");
const TArray<TSharedPtr<FJsonValue>>* SkillsJsonArray;
if (JsonObject->TryGetArrayField("skills", SkillsJsonArray))
{
for (const auto& SkillValue : *SkillsJsonArray)
{
NewCharacter.Skills.Add(SkillValue->AsString());
}
}
// Now you have a populated FCharacterStats struct to use in your game!
UE_LOG(LogTemp, Log, TEXT("Successfully parsed character: %s, Level %d"), *NewCharacter.Name, NewCharacter.Level);
}
}
Common Use Cases
- Character Generation: Create NPCs with a consistent set of stats (health, mana, strength, etc.).
- Quest System: Generate quests with guaranteed fields like
title
,description
,objectives
(as an array), andreward
. - Item Creation: Procedurally generate weapons or armor with predictable attributes like
damage
,durability
, andspecial_effects
. - Dynamic Dialogue: Generate branching dialogue options for NPCs, where each option is an object with
text
and aconsequence_id
. - World Generation: Define the structure for points of interest on a map, each with a
name
,type
(e.g., “village”, “dungeon”), anddescription
.