autoark_eva_client_sdk 1.0.3
autoark_eva_client_sdk: ^1.0.3 copied to clipboard
EVA 实时语音对话 Flutter SDK,支持 Android/iOS 音频、摄像头、command 与可替换媒体 SPI。
EVA Flutter SDK #
autoark_eva_client_sdk 是 AutoArk AI 提供的 Flutter 语音对话 SDK。它支持实时语音输入、语音播放、文本 turn、camera snapshot、emotion 和 command,并通过 EVA 服务完成 ASR、LLM 与 TTS。
当前版本支持 Android 与 iOS。
安装 #
flutter pub add autoark_eva_client_sdk
flutter pub get
生产应用建议固定一个已经验收过的完整版本,不要使用未锁定的范围依赖。
环境与权限 #
- Flutter:
>=3.3.0;Dart SDK:^3.12.2。 - Android:API 24 及以上,目标设备需要支持
arm64-v8a。 - Android 构建需要 Java 17 toolchain。
- iOS:15.1 及以上,支持 arm64 真机和 Apple Silicon arm64 Simulator;Intel
x86_64Simulator 不支持。 - iOS 工程的 deployment target 必须设为 15.1 或更高。Flutter 会自动完成插件集成,接入方不需要手工添加 EVA 的原生依赖。
- iOS 工程使用 CocoaPods;首次构建前请确保本机 CocoaPods 可用。
SDK 会使用网络连接。Android 的 INTERNET 权限由插件声明;麦克风和摄像头仍需要宿主应用在合适的产品页面中向用户说明用途并申请运行时权限。
iOS 宿主应用需要在 Info.plist 中提供用途说明,并在启用对应能力前取得权限:
<key>NSMicrophoneUsageDescription</key>
<string>用于进行 EVA 语音对话</string>
<key>NSCameraUsageDescription</key>
<string>用于在对话中采集图片</string>
快速开始 #
下面的代码创建一个会话并提交一次文本 turn。示例在 start() 前关闭默认启用的音频输入和 TTS,只验证文本回复;它按提交的 turnId 过滤事件,并等待该 turn 的 reply.final 后再释放资源。
import 'dart:async';
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'),
tts: const EvaTtsConfig(
model: 'ark-tts-flash',
voice: 'zh_en_male_evan',
sampleRate: 44100,
),
),
);
const String turnId = 'quickstart-text-turn';
final Completer<EvaStructuredError?> replyDone = Completer<EvaStructuredError?>();
late final StreamSubscription<EvaAgentEvent> events;
events = agent.events.listen(
(EvaAgentEvent event) {
if (event.turnId != turnId && event.type != EvaAgentEventType.error) {
return;
}
switch (event.type) {
case EvaAgentEventType.transcriptFinal:
print('用户:${event.payload['text']}');
break;
case EvaAgentEventType.replyPartial:
print('EVA:${event.payload['text']}');
break;
case EvaAgentEventType.replyFinal:
print('EVA 完成:${event.payload['text']}');
if (!replyDone.isCompleted) replyDone.complete();
break;
case EvaAgentEventType.error:
if (event.turnId != null && event.turnId != turnId) return;
print('EVA error: ${event.error?.message}');
if (event.error?.fatal == true && !replyDone.isCompleted) {
replyDone.complete(event.error);
}
break;
default:
break;
}
},
);
try {
await agent.setAudioInputEnabled(false);
await agent.setTtsEnabled(false);
await agent.start();
await agent.submitText(
'你好',
options: EvaSubmitTextOptions(turnId: turnId),
);
final EvaStructuredError? error = await replyDone.future;
if (error != null) throw EvaException(error);
} finally {
try {
await agent.stop();
} finally {
await events.cancel();
}
}
}
start() 默认启用音频输入和 TTS,camera 默认关闭;示例使用 created 阶段的 setter 在启动前关闭前两项。需要语音能力时,可在权限流程完成后调用 setAudioInputEnabled(true)、setTtsEnabled(true) 或 setCameraEnabled(true)。stop() 会收束在途工作并释放会话资源,Agent 停止后不能再次启动,需要新建 Agent。
submitText() 返回表示请求已经提交;请继续监听 reply.partial、reply.final 和 error,不要在提交后立即调用 stop()。长连接页面应把 Agent 和事件订阅保存到页面状态,并只在页面销毁时停止和取消订阅。
可选能力 #
| 配置 | 省略后的行为 |
|---|---|
audio input、tts |
start() 默认启用;可在启动前或运行中用对应 setter 关闭。 |
vad |
使用默认语音活动检测参数。 |
transports |
使用 SDK 提供的默认媒体实现。 |
camera |
使用默认图片采集等待时间;摄像头保持关闭,直到显式启用。 |
greeting |
不播放启动问候。 |
history |
turn 之间不携带额外对话历史。 |
bargeIn |
不增加首次播放后的额外保护窗口。 |
emotion |
不执行 emotion 分类。 |
commands |
不注册 command。 |
metadata、systemPrompt |
不附加业务 metadata 或 system instruction。 |
运行期间可以调用:
setAudioInputEnabled():开启或关闭后续麦克风输入。setCameraEnabled():开启或关闭 camera snapshot。setTtsEnabled():开启或关闭后续语音合成与播放。
Agent API #
| 成员 | 用途 |
|---|---|
events |
订阅所有行为事件。 |
start() |
启动会话。 |
submitText(text, options:) |
提交一个非空文本 turn。 |
setAudioInputEnabled(enabled) |
控制麦克风输入。 |
setCameraEnabled(enabled) |
控制 camera snapshot。 |
setTtsEnabled(enabled) |
控制语音播放。 |
getMessages() |
读取当前会话的最终消息快照。 |
stop() |
停止会话并释放资源。 |
配置 #
字段级必填性、默认值、范围和校验规则以对应类型的双语 dartdoc 与 pub.dev API reference 为准。
EvaAgentConfig:会话的顶层配置。EvaAsrConfig:ASR 模型和音频采样率。EvaLlmConfig:LLM 模型和生成参数。EvaTtsConfig:TTS 模型、voice、语速、音高和采样率。EvaVadConfig:语音活动检测参数。EvaGreetingConfig:启动问候策略。EvaHistoryConfig:对话历史长度。EvaCameraConfig:图片采集等待时间。EvaDefaultCameraOptions:默认 camera 实现的图片尺寸和 JPEG 质量。EvaBargeInConfig:播放期间的打断策略。EvaEmotionConfig:emotion 分类设置。EvaCommandsConfig:command 定义和 handler。EvaSubmitTextOptions:单次文本 turn 的 identity 和 metadata。
| 配置 / 入口 | 控制什么 |
|---|---|
EvaAgentConfig.apiKey |
访问 EVA 服务的凭证。 |
EvaAgentConfig.asr |
ASR 模型和目标采样率。 |
EvaAgentConfig.llm |
LLM 模型、生成参数和扩展参数。 |
EvaAgentConfig.tts |
TTS 模型、voice、语速、音高和采样率。 |
EvaAgentConfig.vad |
语音活动检测灵敏度和静音阈值。 |
EvaAgentConfig.systemPrompt |
LLM 使用的 system instruction。 |
EvaAgentConfig.greeting |
启动问候内容或策略。 |
EvaAgentConfig.history |
后续请求携带的完整 turn 数量。 |
EvaAgentConfig.camera |
单次图片采集的等待时间;不会自动打开摄像头。 |
EvaAgentConfig.emotion |
emotion 分类开关和分类设置。 |
EvaAgentConfig.bargeIn |
播放期间的语音打断策略。 |
EvaAgentConfig.commands |
command 定义、handler 和每 turn 限制。 |
EvaAgentConfig.metadata |
附加到事件、消息和 command context 的业务 metadata。 |
EvaAgentConfig.transports |
可选的自定义媒体实现。 |
EvaSubmitTextOptions |
单次文本 turn 的可选 identity 和 metadata。 |
EvaMediaTransports |
音频输入、音频输出、AEC 和可选 camera 的实现集合。 |
createDefaultEvaMediaTransports |
创建 SDK 提供的默认媒体实现集合。 |
model 和 voice 是 EVA 服务目录中的 ID。示例值仅用于说明配置形状,实际可用值取决于租户权限和目标环境。
事件、消息与错误 #
所有行为都通过 agent.events 观察。常用事件包括:
speech.started/speech.stopped:检测到用户开始或结束说话。transcript.partial/transcript.final:中间或最终语音转写。reply.started/reply.partial/reply.final:回复生成进度。playback.started/playback.stopped:语音播放状态。interruption:当前回复被用户语音打断。image.captured、emotion.detected、command.*和error:对应能力的结果或错误。
事件带有 streamId,可能带有 turnId、sequence、partial、final、metadata 和结构化 payload。业务逻辑应按事件类型和这些身份字段处理,不要依赖事件到达时间判断 turn 归属。
getMessages() 只返回最终 user/assistant 消息;中间 transcript/reply 不会进入最终历史。错误通过 EvaStructuredError 提供 source、message、fatal 等信息;非致命错误不一定终止会话,业务代码应同时检查错误的 fatal 和当前操作结果。
Emotion #
Emotion 默认关闭。开启后,结果通过 emotion.detected 事件返回:
final emotion = EvaEmotionConfig(
enabled: true,
labels: <String>['happy', 'sad'],
instructions: '这是儿童陪伴场景,重点区分害怕、难过和开心。',
maxInputChars: 2000,
);
agent.events.listen((EvaAgentEvent event) {
if (event.type == EvaAgentEventType.emotionDetected) {
print(event.emotion?.emotionCode);
}
});
Emotion 与主回复并行,不控制 reply、history、messages 或 TTS。分类失败会产生非致命错误;需要改变配置时创建新的 Agent。
Command #
Command 在构造 Agent 时注册,handler 在 Dart 应用中执行:
final commands = EvaCommandsConfig(
registrations: <EvaCommandRegistration>[
EvaCommandRegistration(
definition: EvaCommandDefinition(
name: 'get_store_hours',
description: '查询门店营业时间',
),
handler: (call, context) async {
return EvaCommandSuccess(message: '门店今天营业到 20:00');
},
),
],
);
handler 收到取消信号后应尽快停止副作用。turn 结束后返回的结果不会重新激活 handler。
Media SPI #
EvaMediaTransports 的四个 role 都是公开 Dart SPI,可以使用默认实现,也可以按 role 替换为应用自己的实现。音频输入、音频输出和 AEC 必须提供;camera 是可选的。不需要 camera 时省略 camera,并保持 setCameraEnabled(false)。
默认实现:
final defaults = createDefaultEvaMediaTransports();
final transports = EvaMediaTransports(
input: defaults.input,
output: defaults.output,
aec: defaults.aec,
camera: defaults.camera,
);
替换其中一个 role 时,只调用一次 factory,并复用同一次 factory 返回的其它 role:
final defaults = createDefaultEvaMediaTransports();
final transports = EvaMediaTransports(
input: defaults.input,
output: defaults.output,
aec: MyAecProcessor(),
camera: defaults.camera,
);
不要把不同 factory 调用返回的默认 role 混合到同一个 EvaMediaTransports,也不要把同一组 transports 同时交给多个 Agent。自定义实现需要遵守对应 SPI 的生命周期、取消和数据格式约束;详见各接口的 dartdoc。
Camera 默认关闭。启用后,SDK 会在语音 turn 中请求图片;权限拒绝、采集失败或超时会通过 error 事件报告。Camera 不使用时可以完全省略对应 role。
平台限制 #
| 平台 | 支持情况 |
|---|---|
Android API 24+、arm64-v8a |
支持 |
| Android 模拟器 | 可用于开发调试;媒体效果请在目标真机验收 |
| iOS 15.1+ arm64 真机 | 支持 |
| Apple Silicon arm64 iOS Simulator | 支持 |
Intel x86_64 iOS Simulator |
不支持 |
麦克风、扬声器、AEC 和 camera 效果会受到设备、音频路由、系统版本和权限状态影响。模拟器不能替代目标 Android/iOS 真机验收。
声学参数与 AEC #
SDK 默认值是省略字段时采用的值;示例值用于说明接入;两者都只能作为待验证起点,不是适用于所有设备的最佳值。业务值需在目标设备、音频路由、音量及安静/噪声、边播边说等场景下验证,记录误触发、漏检、截断与延迟。不要由 AI 或经验直接生成参数并宣称已验证;VAD 调优不能代替 AEC。关闭麦克风、TTS 或改成轮流说话会改变业务能力,不应作为回声或打断问题的默认修复。
默认媒体链路的 AEC 由平台完成:Android 输入在系统能力可用时使用 AcousticEchoCanceler,iOS 使用 Voice Processing 音频链路;SDK 负责媒体路由和生命周期,默认 AEC role 透传,避免重复处理。passthrough 不代表整个链路没有回声消除,当前包也不因此提供独立的主动 software/native AEC 算法。自定义媒体角色需自行负责相应处理和参考对齐;平台可用性和效果仍须在目标设备验收。
凭证、隐私与日志 #
凭证由应用在运行时提供。构建与打包不能依据运行时凭证是否存在来删除 SDK 功能、媒体实现或必要的模型及动态库资产;缺少凭证应在运行时提示配置。不要将生产凭证硬编码进应用或构建参数,编译和混淆不等于秘密存储。生产环境的凭证发放、授权、轮换和滥用防护属于消费方架构责任。
- API key 由接入应用负责安全获取、保存、轮换和撤销;不要写入源码、日志、analytics、crash report 或公开构建参数。
- transcript、message、emotion preview 和业务 metadata 可能包含用户内容;写日志前应遵守应用自己的隐私和留存规则。
- 不要把用户内容或 API key 发送到不必要的日志、分析或错误上报服务。
可调试构建的原生 Core 可能在 non-release 且宿主明确 debuggable 时向受控平台日志写入异常类型和 stack frames;该带外诊断不进入 Dart 事件、channel payload 或 Public API。Release 构建对此路径 fail-closed,不输出原始异常。
External configuration references / 外部配置参考 #
- API Key:EVA 控制台
- 模型和 voice:EVA Models
- 开发工具:EVA Skill、EVA CLI
- 模型页面中的参数请查看 Chat Completions 部分。
托管 ASR/TTS 使用 PCM。ark-tts-flash 示例显式使用 44100 Hz;SDK 的默认采样率不是动态模型默认值,不能依赖省略参数来自动匹配模型。
修改模型、音色、语言、采样率、输出格式或任一依赖参数后,应重新核对同组兼容关系,不混用其他模型的音色或参数。模型和音色目录以官方来源为准,不在 SDK 文档维护完整副本。
- 许可证:LICENSE
- 第三方声明:THIRD_PARTY_NOTICES.md