For the complete documentation index, see llms.txt. This page is also available as Markdown.

实现自定义模块

实现 `IConvaiModule` 以添加自定义运行时行为——它从 SDK 启动开始,访问运行时服务,并响应域事件。

构建一个自定义模块,将其与 Convai 运行时生命周期集成,访问 SDK 服务,并订阅领域事件。在开始之前,请阅读 运行时模块系统 以了解何时模块是合适的工具,以及生命周期状态如何映射到你的实现。

先决条件

  • 一个可正常工作的 Convai 场景,包含一个 ConvaiManager 组件

  • 具备 C# 熟练度,包括 async/await 和 Unity 的 MonoBehaviour 生命周期

  • 熟悉 运行时模块系统

快速入门:最小模块

在阅读完整的接口契约之前,这里是构建一个可工作的模块的最短路径——一个 MonoBehaviour 它会自行注册并订阅一个 SDK 事件:

// MinimalModule.cs
using System;
using System.Collections.Generic;
using System.Threading;
using Convai.Domain.DomainEvents.Runtime;
using Convai.Runtime.Components;
using Convai.Runtime.Core.Modules;
using UnityEngine;

public class MinimalModule : MonoBehaviour, IConvaiModule
{
    public string ModuleId    => "my-project.minimal";
    public string DisplayName => "最小模块";

    public IReadOnlyList<string> RequiredModules  => Array.Empty<string>();
    public IReadOnlyList<Type>   RequiredServices => Array.Empty<Type>();
    public IReadOnlyList<Type>   ProvidedServices => Array.Empty<Type>();
    public bool IsActive { get; private set; }

    private IDisposable _sub;

    private void Awake() => ConvaiManager.ActiveManager?.RegisterModule(this);
    private void OnDestroy() => ConvaiManager.ActiveManager?.UnregisterModule(this);

    public System.Threading.Tasks.ValueTask RegisterAsync(IModuleContext ctx, CancellationToken ct = default)
        => System.Threading.Tasks.ValueTask.CompletedTask;

    public System.Threading.Tasks.ValueTask StartAsync(IModuleContext ctx, CancellationToken ct = default)
    {
        _sub = ctx.Events.Subscribe<CharacterSpeechStateChanged>(e =>
        {
            if (e.IsSpeaking) Debug.Log($"[MinimalModule] Character {e.CharacterId} started speaking.");
        });
        IsActive = true;
        return System.Threading.Tasks.ValueTask.CompletedTask;
    }

    public System.Threading.Tasks.ValueTask PauseAsync(RuntimePauseReason r, CancellationToken ct = default)
    { IsActive = false; return System.Threading.Tasks.ValueTask.CompletedTask; }

    public System.Threading.Tasks.ValueTask ResumeAsync(CancellationToken ct = default)
    { IsActive = true; return System.Threading.Tasks.ValueTask.CompletedTask; }

    public System.Threading.Tasks.ValueTask StopAsync(CancellationToken ct = default)
    { _sub?.Dispose(); IsActive = false; return System.Threading.Tasks.ValueTask.CompletedTask; }
}

将此组件添加到场景中的任意 GameObject 上。完整的接口契约和高级模式如下。

IConvaiModule 接口

生命周期方法参考

方法
何时调用
做什么

RegisterAsync

在运行时构建期间——在任何 StartAsync

通过 context.ProvideModuleService<T>()注册服务。订阅事件。

StartAsync

运行时启动——在所有模块注册之后

启动活动行为:开始处理、初始化硬件、启动协程。

PauseAsync

运行时暂停(应用失去焦点、主动暂停)

停止处理。使用 RuntimePauseReason 来区分原因。

ResumeAsync

运行时恢复

恢复在以下状态中暂停的处理 PauseAsync.

StopAsync

运行时停止或模块被移除

清理:取消订阅、停止协程、释放资源。

IModuleContext 服务

属性
类型
可用性
描述

运行时

ConvaiRuntime

总是

此模块所属的运行时实例。

事件

IEventHub

总是

发布并订阅领域事件。

代理

IAgentRegistry

总是

查询已注册的角色和玩家。

传输

ITransportProvider

可能为 null

平台特定的通信层。

首选项

IRuntimePreferences

可能为 null

可变的运行时首选项。

日志记录器

ILogger

可能为 null

用于诊断的日志记录器。

房间音频

IConvaiRoomAudioService

可能为 null

麦克风和播放服务。

凭据

ICredentialProvider

可能为 null

API 密钥和服务器 URL 解析。

有关可订阅领域事件的完整列表,请参阅 事件系统.

实现一个模块

事件订阅者示例

一个订阅领域事件的模块,当角色说话时触发触觉反馈。

服务提供者和消费者示例

模块在 ProvidedServices中声明其提供的服务,并在 RegisterAsync中注册该实例,而消费模块则通过 TryGetModuleService<T>.

一个消费模块:

注册模块

MonoBehaviour 自注册(推荐)

将模块作为组件附加到任意 GameObject 上。它会通过 ConvaiManagerAwake.

之后 ConvaiManager.Start() 完成后, ConvaiManager.ActiveManager.IsInitialized 返回 true,这表示所有已注册模块都已被发现,且运行时已经启动。

CreateRuntimeBuilder 重写

当你希望将所有自定义集中在一处时,或者当模块不是一个 MonoBehaviour.

使用依赖注入模式

位于 ConvaiCharacterConvaiPlayer GameObject 上的组件可以通过实现 IInjectable<TDependencies>自动接收 SDK 服务。SDK 会在角色或玩家注册到运行时之后注入依赖。

IInjectable<TDependencies>

IConvaiCharacterDependencies

属性
类型
可用性

EventHub

IEventHub

必需

ConnectionService

IConvaiRoomConnectionService

必需

AudioService

IConvaiRoomAudioService

必需

AgentRegistry

IAgentRegistry

可选

日志记录器

ILogger

可选

IConvaiPlayerDependencies

属性
类型
可用性

PlayerInputService

IPlayerInputService

可选

RuntimeSettingsService

IConvaiRuntimeSettingsService

可选

日志记录器

ILogger

可选

编写一个可注入组件

将此组件添加到与 ConvaiCharacter相同的 GameObject 上。SDK 会在角色注册期间自动调用 InjectDependencies

使用示例

示例 1:用于医疗模拟的生物特征关联模块

将角色情绪数据与生物特征传感器读数一起记录,以便进行会后分析。

示例 2:用于工业培训的评估评分模块

根据评分量表跟踪角色触发的动作,并通过以下方式将评分服务暴露给其他模块 ProvideModuleService.

故障排查

症状
可能原因
修复方法

模块的 StartAsync 从未被调用

RegisterAsync 抛出了未处理的异常;运行时会静默中止模块启动

RegisterAsync 主体包裹在 try-catch 中,并明确记录日志。

TryGetModuleService<T> 返回 false 意外地

ProvideModuleService<T> 是在 StartAsync 而不是 RegisterAsync

ProvideModuleService<T>RegisterAsync ——在任何模块的 StartAsync 运行后更新。

模块已启动但错过了早期事件

在中订阅 RegisterAsync 但事件在启动期间于 StartAsync

将订阅移到 StartAsync,或者使用 IsActive 在处理程序中进行检查。

RequiredModules 条目会导致启动错误

列出的模块 ID 在运行时构建之前未注册

请检查模块 ID 字符串是否完全匹配——ID 区分大小写。

InjectDependencies 从未在 IInjectable 组件

组件不在层级结构中的一个 GameObject 上 ConvaiCharacter 层级结构

IInjectable<IConvaiCharacterDependencies> 仅适用于作为角色子级的 GameObject。

ConvaiManager.ActiveManager 在中为 null Awake

管理器的 Awake 尚未在执行顺序 −1100 运行

在中注册模块 Start() ,或者使用 ConvaiManager.ActiveManager?.RegisterModule(this) 具有空安全性。

下一步

日志、指标与重试策略事件系统

最后更新于

这有帮助吗?