中文 | Bahasa inggris
Pipa untuk membuat obrolan online dan offline di Unity.

Dengan rilis Unity.Sentis , kita dapat menggunakan beberapa model jaringan saraf saat runtime, termasuk model embedding teks untuk pemrosesan bahasa alami.
Meskipun mengobrol dengan AI bukanlah hal baru, dalam permainan, bagaimana merancang percakapan yang tidak menyimpang dari ide -ide pengembang tetapi lebih fleksibel adalah poin yang sulit.
UniChat didasarkan pada Unity.Sentis dan teknologi embedding vektor teks, yang memungkinkan mode offline untuk mencari konten teks berdasarkan database vektor.
Tentu saja, jika Anda menggunakan mode online, UniChat juga menyertakan toolkit rantai berdasarkan langchain untuk dengan cepat menyematkan LLM dan agen dalam permainan.
Berikut ini adalah bagan aliran Unichat. Di kotak Local Inference adalah fungsi yang dapat digunakan secara offline:

manifest.json : {
"dependencies" : {
"com.cysharp.unitask" : " https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask " ,
"com.huggingface.sharp-transformers" : " https://github.com/huggingface/sharp-transformers.git " ,
"com.unity.addressables" : " 1.21.20 " ,
"com.unity.burst" : " 1.8.13 " ,
"com.unity.collections" : " 2.2.1 " ,
"com.unity.nuget.newtonsoft-json" : " 3.2.1 " ,
"com.unity.sentis" : " 1.3.0-pre.3 " ,
"com.whisper.unity" : " https://github.com/Macoron/whisper.unity.git?path=Packages/com.whisper.unity "
}
}Unity Package Manager menggunakan url git https://github.com/AkiKurisu/UniChat.git public void CreatePipelineCtrl ( )
{
//1. New chat model file (embedding database + text table + config)
ChatPipelineCtrl PipelineCtrl = new ( new ChatModelFile ( ) { fileName = $ "ChatModel_ { Guid . NewGuid ( ) . ToString ( ) [ 0 .. 6 ] } " } ) ;
//2. Load from filePath
PipelineCtrl = new ( JsonConvert . DeserializeObject < ChatModelFile > ( File . ReadAllText ( filePath ) ) )
} public bool RunPipeline ( )
{
string input = "Hello!" ;
var context = await PipelineCtrl . RunPipeline ( "Hello!" ) ;
if ( ( context . flag & ( 1 << 1 ) ) != 0 )
{
//Get pipeline output
string output = context . CastStringValue ( ) ;
//Update history
PipelineCtrl . History . AppendUserMessage ( input ) ;
PipelineCtrl . History . AppendBotMessage ( output ) ;
return true ;
}
} pubic void Save ( )
{
//PC save to {ApplicationPath}//UserData//{ModelName}
//Android save to {Application.persistentDataPath}//UserData//{ModelName}
PipelineCtrl . SaveModel ( ) ;
} Model embedding digunakan BAAI/bge-small-zh-v1.5 secara default dan menempati memori video terkecil. Ini dapat diunduh dalam rilis, namun hanya mendukung bahasa Cina. Anda dapat mengunduh model yang sama dari HuggingFaceHub dan mengonversinya ke format ONNX.
Mode pemuatan adalah UserDataProvider opsional, StreamingAssetsProvider dan ResourcesProvider , jika terpasang Unity.Addressables , Opsional AddressableProvider .
Jalur file UserDataProvider adalah sebagai berikut:

ResourcesProvider menempatkan file di folder model di folder sumber daya.
StreamingAssetsProvider Tempatkan file di folder model di folder streamingassets.
Alamat AddressablesProvider IS sebagai berikut:

Unichat didasarkan pada Langchain C# menggunakan struktur rantai untuk menghubungkan komponen secara seri.
Anda dapat melihat sampel dalam contoh repo.
Penggunaan sederhana adalah sebagai berikut:
public class LLM_Chain_Example : MonoBehaviour
{
public LLMSettingsAsset settingsAsset ;
public AudioSource audioSource ;
public async void Start ( )
{
var chatPrompt = @"
You are an AI assistant that greets the world.
User: Hello!
Assistant:" ;
var llm = LLMFactory . Create ( LLMType . ChatGPT , settingsAsset ) ;
//Create chain
var chain =
Chain . Set ( chatPrompt , outputKey : "prompt" )
| Chain . LLM ( llm , inputKey : "prompt" , outputKey : "chatResponse" ) ;
//Run chain
string result = await chain . Run < string > ( "chatResponse" ) ;
Debug . Log ( result ) ;
}
} Contoh di atas menggunakan Chain untuk memanggil LLM secara langsung, tetapi untuk menyederhanakan pencarian database dan memfasilitasi rekayasa, disarankan untuk menggunakan ChatPipelineCtrl sebagai awal rantai.
Jika Anda menjalankan contoh berikut, pertama kali Anda menelepon LLM dan kedua kalinya Anda membalas langsung dari database.
public async void Start ( )
{
//Create new chat model file with empty memory and embedding db
var chatModelFile = new ChatModelFile ( ) { fileName = "NewChatFile" , modelProvider = ModelProvider . AddressableProvider } ;
//Create an pipeline ctrl to run it
var pipelineCtrl = new ChatPipelineCtrl ( chatModelFile , settingsAsset ) ;
pipelineCtrl . SwitchGenerator ( ChatGeneratorIds . ChatGPT ) ;
//Init pipeline, set verbose to log status
await pipelineCtrl . InitializePipeline ( new PipelineConfig { verbose = true } ) ;
//Add system prompt
pipelineCtrl . Memory . Context = "You are my personal assistant, you should answer my questions." ;
//Create chain
var chain = pipelineCtrl . ToChain ( ) . Input ( "Hello assistant!" ) . CastStringValue ( outputKey : "text" ) ;
//Run chain
string result = await chain . Run < string > ( "text" ) ;
//Save chat model
pipelineCtrl . SaveModel ( ) ;
} Anda dapat melacak rantai menggunakan metode Trace() , atau menambahkan simbol skrip UNICHAT_ALWAYS_TRACE_CHAIN dalam pengaturan proyek.
| Nama metode | Tipe pengembalian | Keterangan |
|---|---|---|
Trace(stackTrace, applyToContext) | void | Rantai jejak |
stackTrace: bool | Mengaktifkan penelusuran tumpukan | |
applyToContext: bool | Berlaku untuk semua subchains |

Jika Anda memiliki solusi sintesis ucapan, Anda dapat merujuk ke VitsClient implementasi komponen TTS?.
Anda dapat menggunakan AudioCache untuk menyimpan pidato sehingga dapat dimainkan saat Anda mengambil jawaban dari database dalam mode offline.
public class LLM_TTS_Chain_Example : MonoBehaviour
{
public LLMSettingsAsset settingsAsset ;
public AudioSource audioSource ;
public async void Start ( )
{
//Create new chat model file with empty memory and embedding db
var chatModelFile = new ChatModelFile ( ) { fileName = "NewChatFile" , modelProvider = ModelProvider . AddressableProvider } ;
//Create an pipeline ctrl to run it
var pipelineCtrl = new ChatPipelineCtrl ( chatModelFile , settingsAsset ) ;
pipelineCtrl . SwitchGenerator ( ChatGeneratorIds . ChatGPT , true ) ;
//Init pipeline, set verbose to log status
await pipelineCtrl . InitializePipeline ( new PipelineConfig { verbose = true } ) ;
var vits = new VITSModel ( lang : "ja" ) ;
//Add system prompt
pipelineCtrl . Memory . Context = "You are my personal assistant, you should answer my questions." ;
//Create cache to cache audioClips and translated texts
var audioCache = AudioCache . CreateCache ( chatModelFile . DirectoryPath ) ;
var textCache = TextMemoryCache . CreateCache ( chatModelFile . DirectoryPath ) ;
//Create chain
var chain = pipelineCtrl . ToChain ( ) . Input ( "Hello assistant!" ) . CastStringValue ( outputKey : "text" )
//Translate to japanese
| Chain . Translate ( new GoogleTranslator ( "en" , "ja" ) ) . UseCache ( textCache )
//Split them
| Chain . Split ( new RegexSplitter ( @"(?<=[。!?! ?])" ) , inputKey : "translated_text" )
//Auto batched
| Chain . TTS ( vits , inputKey : "splitted_text" ) . UseCache ( audioCache ) . Verbose ( true ) ;
//Run chain
( IReadOnlyList < string > segments , IReadOnlyList < AudioClip > audioClips )
= await chain . Run < IReadOnlyList < string > , IReadOnlyList < AudioClip > > ( "splitted_text" , "audio" ) ;
//Play audios
for ( int i = 0 ; i < audioClips . Count ; ++ i )
{
Debug . Log ( segments [ i ] ) ;
audioSource . clip = audioClips [ i ] ;
audioSource . Play ( ) ;
await UniTask . WaitUntil ( ( ) => ! audioSource . isPlaying ) ;
}
}
}Anda dapat menggunakan layanan ucapan-ke-teks seperti Whisper.unity for Local Inference?.
public void RunSTTChain ( AudioClip audioClip )
{
WhisperModel whisperModel = await WhisperModel . FromPath ( modelPath ) ;
var chain = Chain . Set ( audioClip , "audio" )
| Chain . STT ( whisperModel , new WhisperSettings ( ) {
language = "en" ,
initialPrompt = "The following is a paragraph in English."
} ) ;
Debug . Log ( await chain . Run ( "text" ) ) ;
}Anda dapat mengurangi ketergantungan pada LLM dengan melatih classifier hilir berdasarkan model tertanam untuk menyelesaikan beberapa tugas pengakuan dalam permainan (seperti classifier ekspresi?).
Melihat
1. Anda perlu membuat komponen di lingkungan Python.
2. Saat ini, Sentis masih mengharuskan Anda untuk mengekspor secara manual ke format ONNX
Praktik Terbaik: Gunakan model tertanam untuk menghasilkan sifat -sifat dari data pelatihan Anda sebelum pelatihan. Hanya model hilir yang perlu diekspor sesudahnya.
Berikut ini adalah contoh shape=(512,768,20) dari classifier multi-layer perceptron dengan ukuran ekspor hanya 1,5MB:
class SubClassifier ( nn . Module ):
#input_dim is the output dim of your embedding model
def __init__ ( self , input_dim , hidden_dim , output_dim ):
super ( CustomClassifier , self ). __init__ ()
self . fc1 = nn . Linear ( input_dim , hidden_dim )
self . relu = nn . ReLU ()
self . dropout = nn . Dropout ( p = 0.1 )
self . fc2 = nn . Linear ( hidden_dim , output_dim )
def forward ( self , x ):
x = self . fc1 ( x )
x = self . relu ( x )
x = self . dropout ( x )
x = self . fc2 ( x )
return x Komponen game adalah berbagai alat yang dikombinasikan dengan fungsi dialog sesuai dengan mekanisme permainan tertentu.
Statemachine yang beralih menyatakan sesuai dengan konten obrolan. Statemachine Nesting (Substatemachine) saat ini tidak didukung. Bergantung pada percakapan, Anda dapat melompat ke negara bagian yang berbeda dan menjalankan serangkaian perilaku yang sesuai, mirip dengan mesin status animasi Unity.
public void BuildStateMachine ( )
{
chatStateMachine = new ChatStateMachine ( dim : 512 ) ;
chatStateMachineCtrl = new ChatStateMachineCtrl (
TextEncoder : encoder ,
//Input a host Unity.Object
hostObject : gameObject ,
layer : 1
) ;
chatStateMachine . AddState ( "Stand" ) ;
chatStateMachine . AddState ( "Sit" ) ;
chatStateMachine . states [ 0 ] . AddBehavior < StandBehavior > ( ) ;
chatStateMachine . states [ 0 ] . AddTransition ( new LazyStateReference ( "Sit" ) ) ;
// Add a conversion directive and set scoring thresholds and conditions
chatStateMachine . states [ 0 ] . transitions [ 0 ] . AddCondition ( ChatConditionMode . Greater , 0.6f , "I sit down" ) ;
chatStateMachine . states [ 0 ] . transitions [ 0 ] . AddCondition ( ChatConditionMode . Greater , 0.6f , "I want to have a rest on chair" ) ;
chatStateMachine . states [ 1 ] . AddBehavior < SitBehavior > ( ) ;
chatStateMachine . states [ 1 ] . AddTransition ( new LazyStateReference ( "Stand" ) ) ;
chatStateMachine . states [ 1 ] . transitions [ 0 ] . AddCondition ( ChatConditionMode . Greater , 0.6f , "I'm well rested" ) ;
chatStateMachineCtrl . SetStateMachine ( 0 , chatStateMachine ) ;
}
public void LoadFromBytes ( string bytesFilePath )
{
chatStateMachineCtrl . Load ( bytesFilePath ) ;
} public class CustomChatBehavior : ChatStateMachineBehavior
{
private GameObject hostGameObject ;
public override void OnStateMachineEnter ( UnityEngine . Object hostObject )
{
//Get host Unity.Object
hostGameObject = hostObject as GameObject ;
}
public override void OnStateEnter ( )
{
//Do something
}
public override void OnStateUpdate ( )
{
//Do something
}
public override void OnStateExit ( )
{
//Do something
}
} private void RunStateMachineAfterPipeline ( )
{
var chain = PipelineCtrl . ToChain ( ) . Input ( "Your question." ) . CastStringValue ( "stringValue" )
| new StateMachineChain ( chatStateMachineCtrl , "stringValue" ) ;
await chain . Run ( ) ;
}Invoke Tools Berdasarkan Alur Kerja Reactagent.
Inilah contohnya:
var userCommand = @"I want to watch a dance video." ;
var llm = LLMFactory . Create ( LLMType . ChatGPT , settingsAsset ) as OpenAIClient ;
llm . StopWords = new ( ) { " n Observation:" , " n t Observation:" } ;
//Create agent with muti-tools
var chain =
Chain . Set ( userCommand )
| Chain . ReActAgentExecutor ( llm )
. UseTool ( new AgentLambdaTool (
"Play random dance video" ,
@"A wrapper to select random dance video and play it. Input should be 'None'." ,
( e ) =>
{
PlayRandomDanceVideo ( ) ;
//Notice agent it finished its work
return UniTask . FromResult ( "Dance video 'Queencard' is playing now." ) ;
} ) )
. UseTool ( new AgentLambdaTool (
"Sleep" ,
@"A wrapper to sleep." ,
( e ) =>
{
return UniTask . FromResult ( "You are now sleeping." ) ;
} ) )
. Verbose ( true ) ;
//Run chain
Debug . Log ( await chain . Run ( "text" ) ) ; Berikut adalah beberapa aplikasi yang saya buat. Karena mereka termasuk beberapa plugin komersial, hanya versi build yang tersedia.
Lihat halaman rilis
Berdasarkan Unichat untuk membuat aplikasi serupa di Unity
Versi repositori yang disinkronkan adalah
V0.0.1-alpha, demo menunggu untuk diperbarui.

Lihat halaman rilis

Ini berisi komponen perilaku dan suara dan belum tersedia.
Demo menggunakan TavernAI struktur data karakter, dan kita dapat menulis kepribadian karakter, sampel percakapan, dan skenario obrolan menjadi gambar.

Jika Anda menggunakan kartu karakter TavernAI , kata isyarat di atas ditimpa.
https://www.akikurisu.com/blog/posts/create-ratbox-in-unity-2024-03-19/
https://www.akikurisu.com/blog/posts/use-nlp-in-unity-2024-04-03/