EVA Flutter SDK

autoark_eva_client_sdkAutoArk AI 提供的 EVA Flutter 端侧 SDK。 它在 Android 原生侧编排媒体、Silero VAD、会话状态、command 与 emotion,并通过固定 EVA Gateway 使用 ASR、LLM 和 TTS。Dart 侧提供稳定 facade、事件流以及可选 media SPI。

当前版本仅支持 Android;iOS 暂不支持。

安装

flutter pub add autoark_eva_client_sdk

Android 运行时已包含在 package 中。Dart facade 与 Android thin shell 以源码交付,原生运行时以经过 R8 加固的编译二进制交付;这种加固用于减少内部实现暴露,不承诺绝对防逆向。接入方只需添加 pub dependency;生产应用应锁定自己验收过的完整 pub version。

环境要求

  • Flutter:>=3.3.0;Dart SDK:^3.12.2
  • Android:minSdk 24,支持 arm64-v8a
  • Android 构建使用 Java 17 toolchain。
  • 默认媒体 helper 使用 AudioRecordAudioTrack、capture-side Android system AEC 与 CameraX。
  • plugin manifest 自动声明普通权限 INTERNET,以及运行时权限 RECORD_AUDIOCAMERA。联网权限不会 弹系统授权框;宿主应用仍负责在调用相应 enable 方法前完成麦克风、相机授权与用户说明。
  • plugin 随包提供 ONNX Runtime JNI 所需的 Android release shrinker rules;宿主无需手工添加 ProGuard/R8 keep 配置。
  • iOS 当前不受支持。

完整 Android 语音对话示例

下面示例使用 SDK 的 Android 默认媒体 helper。applicationManagedApiKey 应由应用自己的安全配置或 后端会话流程注入,不要把真实 AK 写进源码、日志或公开构建参数。

import 'package:autoark_eva_client_sdk/autoark_eva_client_sdk.dart';

Future<void> runEva(String applicationManagedApiKey) async {
  final EvaAgent agent = EvaAgent.create(
    EvaAgentConfig(
      apiKey: applicationManagedApiKey,
      asr: const EvaAsrConfig(
        model: 'ark-asr-plus',
        sampleRate: 16000,
      ),
      llm: EvaLlmConfig(
        model: 'volcengine-doubao-seed-2.0-lite',
        extraParameters: const <String, Object?>{
          'thinking': <String, Object?>{'type': 'disabled'},
        },
      ),
      tts: const EvaTtsConfig(
        model: 'ark-tts-flash',
        voice: 'zh_en_male_evan',
        sampleRate: 44100,
      ),
      vad: const EvaVadConfig(
        sensitivity: 0.6,
        silenceThresholdMs: 400,
      ),
      history: const EvaHistoryConfig(maxTurns: 10),
      camera: const EvaCameraConfig(captureTimeoutMs: 1500),
      emotion: EvaEmotionConfig(enabled: true),
    ),
  );

  final subscription = agent.events.listen((EvaAgentEvent event) {
    switch (event.type) {
      case EvaAgentEventType.replyPartial:
        final Object? text = event.payload['text'];
        if (text is String) print(text);
        break;
      case EvaAgentEventType.error:
        print('EVA error: ${event.error?.message}');
        break;
      default:
        break;
    }
  });

  try {
    await agent.start();
    await agent.setAudioInputEnabled(true);

    // Camera 默认关闭;宿主取得 CAMERA runtime permission 后再显式开启。
    await agent.setCameraEnabled(true);

    await agent.submitText('你好');
  } finally {
    await agent.stop();
    await subscription.cancel();
  }
}

agent.start() 不会自动打开麦克风或摄像头。调用方应在取得对应 runtime permission 后分别调用 setAudioInputEnabled(true)setCameraEnabled(true)stop() 会收束在途 turn 并释放 SDK 持有的 媒体资源。

哪些可以不选

是否必需 省略后的行为
apiKeyasrllmtts Agent 构造必需 不能省略;model、voice 与授权以 EVA Models 和租户为准。
vad 可选 使用 sensitivity=0.5silenceThresholdMs=200
transports 可选 Android 使用 SDK 默认 AudioRecord/AudioTrack/system AEC/CameraX helper。
camera 可选 使用稳定的 1500 ms capture timeout;摄像头仍保持关闭,直到显式 enable。
greetinghistorybargeIn 可选 分别保持无问候、turn 间无额外历史、无首次播放保护窗。
emotion 可选 不执行 emotion 旁路分类。
commandsmetadatasystemPrompt 可选 不注册 command、不附加业务 metadata、使用空 system instruction。

这些能力也可以在 Agent 运行期间开启或关闭:

  • setAudioInputEnabled() 控制后续麦克风输入;
  • setCameraEnabled() 控制持续 camera session 与 turn 图片采集;
  • setTtsEnabled() 控制后续语音合成与播放。

Agent API

EvaAgent.create(config) 返回 EvaAgent。主要公共面包括:

成员 作用
events 单一 Stream<EvaAgentEvent> 行为观察流。
start() 启动 Android 原生 runtime。
submitText(text, options:) 提交一个非空手动文本 turn。
setAudioInputEnabled(enabled) 开启或关闭后续音频输入并等待资源转换。
setCameraEnabled(enabled) 开启或关闭持续 camera session。
setTtsEnabled(enabled) 控制后续 TTS 合成与播放。
getMessages() 读取最终消息的不可变快照。
stop() 收束在途工作并进入终态;重复调用安全。

Agent 停止后进入终态;需要新会话时创建新的 Agent。getMessages() 返回当前最终消息的不可变快照, partial transcript/reply 不进入最终历史。

配置

README 介绍顶层配置及用途。字段级必填性、默认值、范围、单位和校验规则以对应类型的双语 dartdoc 与 pub.dev API reference 为准。

  • EvaAgentConfig:单个 Agent 的顶层托管配置。
  • EvaAsrConfig:ASR model 与目标 PCM 采样率。
  • EvaLlmConfig:LLM model、生成参数与扩展参数。
  • EvaTtsConfig:TTS model、voice、语速、音高与采样率。
  • EvaVadConfig:本地 Silero VAD 调音。
  • EvaGreetingConfig:disabled、static、dynamic 三种启动问候策略的基类。
  • EvaHistoryConfig:有界 LLM 对话历史。
  • EvaCameraConfig:camera capture 时序。
  • EvaBargeInConfig:首次 playback 后的 speech admission 保护窗。
  • EvaEmotionConfig:emotion 旁路分类。
  • EvaCommandsConfig:command 注册与每 turn 预算。
  • EvaSubmitTextOptions:单次手动文本 turn 的 identity 与 metadata。
顶层配置 / 入口 控制什么
EvaAgentConfig.apiKey Gateway 凭证;只用于 SDK 访问 Gateway,不提供 getter 回读。
EvaAgentConfig.asr ASR model 与目标 PCM 采样率。
EvaAgentConfig.llm LLM model、temperature、token 上限与 JSON-compatible 扩展参数。
EvaAgentConfig.tts TTS model、voice、语速、音高与合成采样率。
EvaAgentConfig.vad 本地 Silero VAD sensitivity 与静音阈值。
EvaAgentConfig.systemPrompt 每次 LLM 请求使用的 system instruction。
EvaAgentConfig.greeting disabled、static 或 dynamic 启动问候。
EvaAgentConfig.history 后续 LLM 请求最多携带多少个完整 turn。
EvaAgentConfig.camera 单次图片采集等待时限,不会自动启用摄像头。
EvaAgentConfig.emotion emotion 分类开关、code 目录、业务 instructions 与输入上限。
EvaAgentConfig.bargeIn 首次实际 playback 后的 speech admission 保护窗。
EvaAgentConfig.commands command definitions、Dart handlers 与每 turn raw-call 上限。
EvaAgentConfig.metadata 附加到事件、消息与 command context 的稳定业务 metadata。
EvaAgentConfig.transports 可选的 Dart media SPI 装配;省略时使用 Android 默认 helper。
EvaSubmitTextOptions 单次手动文本 turn 的可选 identity 与 metadata。
EvaMediaTransports 自定义 audio input/output、AEC 与可选 camera 四个 media role。

llm.extraParameters 会直接加入 LLM request body 顶层,但不能覆盖 SDK 管理的字段。可用字段和值域由 Gateway 与所选模型决定。

External configuration references / 外部配置参考

model 与 voice 是服务目录中的不透明 ID。示例值用于说明配置形状,不替代租户权限和目标环境验收。

Emotion

Emotion 是默认关闭的旁路分类能力,需要在构造 Agent 时通过 EvaEmotionConfig 开启:

final EvaEmotionConfig emotion = EvaEmotionConfig(
  enabled: true,
  labels: <String>['happy', 'sad'],
  instructions: '这是儿童陪伴场景,重点区分害怕、难过和开心。',
  maxInputChars: 2000,
);

省略 labels 时使用 EvaEmotionCodes.defaultsneutralhappysadangryanxiousconfusedexcitedfrustratedunknown。custom labels 会完整替换默认业务 code,必须匹配 ^[a-z][a-z0-9_-]{0,63}$ 且不得为空或重复;SDK 会在缺失时补充唯一的 unknowninstructions 只用于 补充场景和业务判断背景,不是一份完整 prompt;maxInputChars 默认为 2000。

分类结果通过 emotion.detected 旁路事件上报:

agent.events.listen((EvaAgentEvent event) {
  if (event.type != EvaAgentEventType.emotionDetected) return;

  final EvaEmotionDetectedPayload? emotion = event.emotion;
  if (emotion == null) return;

  print('${emotion.emotionCode}: ${emotion.confidence}');
});

emotion.source 区分 speech 和 text,emotionCode 始终位于当前有效 code 空间内,无法可靠映射时为 unknownconfidence 若存在则位于 [0,1],它是模型自报值,不是经过校准的概率或准确率承诺; textPreview 最多包含 100 个 Unicode code point,仍属于用户内容。

Emotion 与主回复并行,不参与 reply、history、messages 或 TTS 控制,也不保证在 reply 前或后到达。分类请求 失败会产生非致命错误,不会阻断主对话。当前没有 Emotion runtime toggle;需要改变配置时应创建新的 Agent。

Command

Command 在 Agent 构造时通过 EvaCommandsConfig.registrations 注册。definition 会发送给 Android runtime, handler closure 始终留在 Dart App 进程执行,不经过序列化。

final EvaCommandsConfig commands = EvaCommandsConfig(
  registrations: <EvaCommandRegistration>[
    EvaCommandRegistration(
      definition: EvaCommandDefinition(
        name: 'get_store_hours',
        description: '查询门店营业时间',
      ),
      handler: (EvaCommandCall call, EvaCommandContext context) async {
        return EvaCommandSuccess(message: '门店今天营业到 20:00');
      },
    ),
  ],
);

maxCallsPerTurn 必须为正整数,省略时为 3。handler 收到取消信号后应尽快停止副作用;turn 结束后返回的 结果不会重新激活它。

事件、消息与错误

所有行为观察都来自 agent.events。事件具有稳定的 typestreamId、可选 turnId/sequencepartial/finalEvent、metadata 与结构化 payload;业务代码应按 type 和身份字段归类,不依赖到达时间猜测 turn 归属。

稳定事件目录包括 speech、transcript、interruption、reply、playback、turn latency、image、emotion、 command 与 errorEvaStructuredError.source 区分 sdkprovidergatewaymedia;非致命错误不一定 终止 Agent,调用方应同时检查 fatal 和业务操作结果。

getMessages() 只返回最终 user/assistant 消息。metadata 在构造或提交 turn 时递归复制,后续修改原 Map 不会改变已经进入 SDK 的快照。

Media SPI 与 Android 默认实现

省略 EvaAgentConfig.transports 时,Android 使用 SDK 提供的默认媒体实现:

  • AudioRecord 麦克风采集;
  • AudioTrack TTS 播放;
  • capture-side Android system AEC,并把 TTS playback 同时作为 far-end reference;
  • CameraX 静态图片采集。

音频帧在原生侧闭环,不通过 Dart MethodChannel。需要接入自有设备、已经处理好的音频流或特殊 camera 时, 可以实现 EvaAudioInputSourceEvaAudioOutputSinkEvaAecProcessorEvaCameraSnapshotSource,再通过 EvaMediaTransports 注入。注入后生命周期由 SDK 独占驱动,应用不要并发调用同一 transport 的 start/stop/capture。

Camera 默认关闭;开启期间 SDK 持有一个 session,并在语音 turn 起点请求图片。权限拒绝、capture 失败或 超时会产生结构化 media error;SDK 仍会继续处理可用的文字或语音输入。

平台支持

0.0.1 状态
Android arm64-v8a, API 24+ 支持
Android emulator 可用于功能开发和调试
Android 物理设备 建议在目标设备上验收麦克风、播放、AEC 和 camera 效果
iOS 暂不支持

麦克风、扬声器、AEC 和 camera 效果会受到具体设备、音频路由与权限状态影响。模拟器适合功能调试,不能 替代目标 Android 真机验收。

AK、隐私与日志边界

  • AK 由接入应用负责安全获取、保存、轮换与撤销;不要写入源码、日志、analytics、crash report 或公开构建参数。
  • Gateway endpoint 由 SDK 固定,公共配置不提供 base URL、headers、HTTP transport 或 stage provider 注入。
  • emotion.detected.textPreview、transcript、message 和业务 metadata 可能包含用户内容;写日志前应按应用自己的 隐私与留存规则处理。

许可与 Gateway 服务

本 SDK 是公开下载的专有软件,并非开源软件。SDK 适用 LICENSE 中的 AutoArk AI Proprietary SDK License Agreement;EVA Gateway 是独立服务,适用 GATEWAY_TERMS.md 指向的服务条款。第三方组件适用 THIRD_PARTY_NOTICES.md 中列出的各自许可证。

Libraries

autoark_eva_client_sdk
EVA client SDK for Flutter.