Atomic Integrated Analysis with LiteRTDemo
English
运行解剖
Analyze By Demo
本章将通过“图文码”一体化的原子驱动,拆解 LiteRTDemo 项目中每一个真实的插件交互触点。
Topology Step 1
图 1: LiteRTDemo 插件交互拓扑 (原子触点映射)
Architecture Step 2
图 2: 系统原子运行全流程 (初始化流程 | 使用原子时序 | 物理销毁)
1. 初始化物理链路 (Startup Logic)
Bootstrapper
资产加载
人格注入
2. 使用原子时序 (Usage Timing & Sequence)
T1
意图捕获 (Input)→ 用户按下回车
T2
指令派遣 (Dispatch)→ UI 触发组件调用
T3
指令注入 (Push)→ API 注入后台计算线程
T4
流式渲染 (Output)→ 物理回灌 UMG 像素
3. 物理销毁与回收 (Cleanup)
断开代理
Session 回收
Practical Step 3
开发者集成指南:学习 Demo 中驱动插件的方法
本节将 Demo 项目中所有非插件、非引擎的自定义代码按生命周期拆解。写全准则:除底层 API 外,所有应用层逻辑必须在位。
Phase 01
资产初始化与物理加载
提示:你需要学习如何初始化一个 AI 并且在你的项目合适的地方让其进行一次唯一的初始化。
Build.cs
Provider
Plugin API
静态模块挂载
EnsureModelLoadedAsync
LoadModel(Config)
OnComplete(bSuccess)
1.1 模块依赖 | LiteRTDemo.Build.cs
PublicDependencyModuleNames.AddRange(new string[] {
"Core", "CoreUObject", "Engine",
"LiteRTLMUnreal" // 插件核心二进制包
});
1.2 物理加载驱动 | UmgMcpLiteRtLmAiProvider.cpp
void FUmgMcpLiteRtLmAiProvider::EnsureModelLoadedAsync(const FString& AbsPath, TFunction<void(bool)> OnComplete) {
FLiteRtLmConfig Config = FLiteRtLmUnrealApi::GetAutoConfig();
Config.ModelPath = AbsPath;
Config.Backend = TEXT("gpu");
AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [Config, OnComplete]() {
bool bSuccess = FLiteRtLmUnrealApi::LoadModel(Config);
AsyncTask(ENamedThreads::GameThread, [bSuccess, OnComplete]() {
if (OnComplete) OnComplete(bSuccess);
});
});
}
Phase 02
指令下行:从指尖到插件
提示:你需要学习如何向 AI 发起对话请求,特别的这会要求你接下来需要异步同步的方法更新,这是用户使用的核心输入。
Widget
Subsystem
Provider
Plugin API
ExecuteSendMessage()
RequestQuestion()
Send(Messages)
SendChatRequest(Agent, Msg, Tools...)
一级派遣:UI 意图捕获 | UmgMcpActiveMessageSubsystem.cpp
void UUmgMcpActiveMessageSubsystem::ExecuteSendMessage() {
auto ChatInput = RegisteredChatInput.Pin();
if (!ChatInput.IsValid()) return;
FText InputText = ChatInput->GetText();
if (!InputText.IsEmpty()) {
RequestQuestion(InputText.ToString());
ChatInput->ClearText();
}
}
二级派遣:中枢协议封装 | UmgMcpActiveMessageSubsystem.cpp
void UUmgMcpActiveMessageSubsystem::RequestQuestion(const FString& QuestionText) {
bIsGenerating = true;
TSharedPtr<FUmgMcpAgent> ActiveAgent = SessionSubsystem->GetMainAgent();
if (ActiveAgent.IsValid()) {
UpdateActiveMessageMeta(ActiveAgent->Name, LOCTEXT("Thinking", "Thinking..."), true);
TSharedPtr<FJsonObject> RequestObj = MakeShared<FJsonObject>();
RequestObj->SetStringField(TEXT("role"), TEXT("user"));
RequestObj->SetStringField(TEXT("content"), QuestionText);
ActiveAgent->Answer(RequestObj);
}
}
三级派遣:插件 API 注入 | FUmgMcpLiteRtLmAiProvider.cpp
void FUmgMcpLiteRtLmAiProvider::Send(const TArray<FUmgMcpChatMessage>& Messages, ...) {
FLiteRtLmUnrealApi::SendChatRequest(
AgentPtr, Messages, ToolsJson, OnChunk, OnDone, Params
);
}
Phase 03
流式回灌:从碎片到像素
提示:你需要学习如何从 AI 中取得结果并且最好实时渲染。这里你需要区分两个概念:流式输出和回答完成。流式输出是增量输出,用于增强体验;回答完成是存量输出,用于实现业务逻辑。
Plugin
Provider
Subsystem
Widget
Streaming Loop (Multiple Tokens)
OnChunk(FString)
AppendActiveMessageText
AppendToTextOutputBlock
OnDone(Result)
一级回灌:线程切换与中转 | UmgMcpLiteRtLmAiProvider.cpp
FLiteRtLmChunkCallback OnChunk = [this](const FString& Chunk) {
AsyncTask(ENamedThreads::GameThread, [this, Chunk]() {
ActiveSubsystem->AppendActiveMessageText(Chunk);
});
};
二级回灌:活跃槽位调度 | UmgMcpActiveMessageSubsystem.cpp
void UUmgMcpActiveMessageSubsystem::AppendActiveMessageText(const FString& PartialText) {
if (ActiveMessageWidget.IsValid()) {
ActiveMessageWidget->AppendToCurrentTextOutputBlock(PartialText);
if (RegisteredHub.IsValid()) RegisteredHub.Pin()->RefreshVisibility();
}
}
三级回灌:像素刷新 | SUmgMcpAgentResponseGroup.cpp
void SUmgMcpAgentResponseGroup::AppendToCurrentTextOutputBlock(const FString& Text) {
CurrentFullText += Text;
InternalTextWidget->SetText(FText::FromString(CurrentFullText));
}
Phase 04
资源卸载与物理回收
Widget
Provider
Plugin API
RemoveAll(this)
UnloadModel()
4.1 显存物理释放 | UmgMcpLiteRtLmAiProvider.cpp
void FUmgMcpLiteRtLmAiProvider::UnloadModel() {
FLiteRtLmUnrealApi::UnloadModel();
}
4.2 代理断路逻辑 | WinyunqDialogueWidget.cpp
void UWinyunqDialogueWidget::SetTargetNPC(ULiteRtLmComponent* InNPC) {
if (CurrentNPC) CurrentNPC->OnTextChunkReceived.RemoveAll(this);
}