超视距遥操(Teleop)指南
超视距遥操(Teleop)指南
⚠️ 尚未上线:本能力为 v1.5.0-SNAPSHOT 开发中特性,接口与协议可能调整,正式发布前请勿用于生产环境。
v1.5.0 新增能力。Teleop = 超视距遥操(远端操作员在线接管机器人的语音交互,不受本地视距限制)。它是继「被动交互 / 主动交互」之后的第三条交互路径,走独立的事件族与独立的策略枚举
AgentPolicy.TELEOP。Python 端接口与 Java 版同版本 API 完全等价,仅遵循 PEP8 的snake_case命名。
使用场景
- 机器人在现场无人守卫或光线 / 网络不佳的位置,需要远端操作员对特定请求进行接管应答;
- 二开应用需要旁路 LLM,直接由人工语音接管一段窗口(例如安抚客诉、精确核对下单信息);
- 操作员的音频通过 SDK 上行到网关,网关按
agent_id下发到目标机器人执行。
调用流程
严格顺序:enter → 任意次 start_audio / append_audio_delta / done_audio → exit。
遥操存续期间可任意频次调用 keepalive 探活(推荐每 10 秒一次,见下文 机器人在线检测(keepalive))。
enter exit
┌───────────────────────┐ ┌────────────────────┐
│ TeleopRequest.enter │ │ TeleopRequest.exit │
│ (on_enter_ack) │ │ (on_exit_ack) │
└───────────┬───────────┘ └────────────┬────────┘
│ 任意次 (fire-and-forget) │
▼ │
┌───────────────────────┐ │
│ start_audio(param) │ │
│ append_audio_delta(*)│─────────────────────┘
│ done_audio │
└───────────────────────┘
┌─────────────────────────────┐
│ TeleopRequest.keepalive │ ← 建议每 10 秒一次;code != 0 视为机器人离线
│ (on_keepalive_ack) │
└─────────────────────────────┘
enter/keepalive/exit都有 ACK 回调;相应on_*_ack收到code == 0视为成功;start_audio/append_audio_delta/done_audio是 fire-and-forget,本地不排队等 ACK;- 若发送瞬间 SDK 连接已关闭,
enter/keepalive/exit回调会在原线程同步以code == 1000+msg == "Agentsdk connection is closed"触发一次失败回执。
事件族与协议
Python 端事件类型枚举位于 linksoul_agentsdk.enums.AgentEventType:
| 枚举成员 | 事件类型字符串 | 说明 |
|---|---|---|
AGENTSDK_TELEOP_ENTER | agentsdk.teleop.enter | 进入遥操 |
AGENTSDK_TELEOP_ENTER_ACK | agentsdk.teleop.enter_ack | 进入回执 |
AGENTSDK_TELEOP_AUDIO_START | agentsdk.teleop_audio.start | 音频段开始(可携带 param) |
AGENTSDK_TELEOP_AUDIO_DELTA | agentsdk.teleop_audio.delta | 音频增量(audio 字段为 base64 字符串,可选 gain 为音频增益倍数,默认 1.0) |
AGENTSDK_TELEOP_AUDIO_DONE | agentsdk.teleop_audio.done | 音频段结束 |
AGENTSDK_TELEOP_KEEPALIVE | agentsdk.teleop.keepalive | 机器人在线探活(SDK → 网关) |
AGENTSDK_TELEOP_KEEPALIVE_ACK | agentsdk.teleop.keepalive_ack | 探活回执(网关 → SDK) |
AGENTSDK_TELEOP_EXIT | agentsdk.teleop.exit | 退出遥操 |
AGENTSDK_TELEOP_EXIT_ACK | agentsdk.teleop.exit_ack | 退出回执 |
所有 Teleop 消息 wire 结构:
{
"type": "<event type>",
"agentId": "<目标机器人 agentId>",
"teleopId": "teleop_xxxxxxxxxxxxxxxxxxxxx",
"eventId": "event_xxxxxxxxxxxxxxxxxxxxx",
"agentMode": "teleop"
}
teleopId 由 IdGenerator.generate_teleop_id() 生成;agentMode 恒为 "teleop"(对应 AgentPolicy.TELEOP)。
start_audio 参数
start_audio(param=None) 支持在每一段音频开始时携带一个 AgentParam,主要用于告诉服务端本段音频的音色 / VAD 灵敏度:
| 键 | 类型 | 取值示例 | 说明 |
|---|---|---|---|
role | str | "male" / "female" | 遥操音色选择 |
threshold | int | -14 ~ 14 | VAD 灵敏度阈值,数值越大越不易被截断 |
Python 端构造示例:
start_param = (
AgentParam.create()
.set_string("role", "male")
.set_integer("threshold", 0)
)
event_id = teleop.start_audio(start_param)
wire 上会以 param.extParam.role / param.extParam.threshold 的形式下发(与 Java 版一致)。
机器人在线检测(keepalive)
遥操会话通常持续较长,期间机器人可能因网络中断 / 电量耗尽 / 现场断电等原因掉线。SDK 提供 TeleopRequest.keepalive(...) 一次性探活方法,业务侧应定时调用(建议 每 10 秒一次)判断机器人是否仍在线。
接口与回调
class TeleopKeepaliveCallback:
"""遥操机器人在线探活回调"""
def on_keepalive_ack(self, teleop_id: str, event_id: str, code: int, msg: str) -> None:
raise NotImplementedError
# TeleopRequest 方法
def keepalive(self, keepalive_callback: TeleopKeepaliveCallback, timeout: int) -> str: ...
timeout:本地占位超时(< 50clamp 到 50,语义同enter/exit);- 返回本次消息的
event_id; - 每次调用发送一条
agentsdk.teleop.keepalive,网关按teleop_id转发到目标机器人;机器人上线则回success == true。
code 含义(与 enter / exit 一致)
code | 含义 |
|---|---|
0 | 机器人在线且响应正常 |
-1 | 服务端返回失败,msg 携带 errorMsg——表示机器人已离线,业务侧必须处理该异常 |
1000 | SDK 端 WebSocket 已关闭,本次未真正下发 |
定时探活 + 掉线自动退出
任何一次 code != 0,都应视为机器人已离线——立即停止定时任务、exit 退出遥操、unregister_teleop 释放资源,避免后续 append_audio_delta 继续下发无效音频。
import threading
from linksoul_agentsdk import TeleopKeepaliveCallback, TeleopExitCallback
def start_keepalive(teleop_request, agent_sdk, interval_sec: int = 10):
"""enter 成功后调用;每 interval_sec 秒探活一次"""
state = {"online": True}
def tick():
if not state["online"]:
return
class _Cb(TeleopKeepaliveCallback):
def on_keepalive_ack(self, teleop_id, event_id, code, msg):
if code != 0 and state["online"]:
state["online"] = False
print(f"robot offline => code={code}, msg={msg}")
class _ExitCb(TeleopExitCallback):
def on_exit_ack(self, teleop_id, event_id, code, msg):
agent_sdk.unregister_teleop(teleop_id)
teleop_request.exit(_ExitCb(), 2000)
teleop_request.keepalive(_Cb(), 2000)
if state["online"]:
threading.Timer(interval_sec, tick).start()
threading.Timer(interval_sec, tick).start()
return state # 业务侧可通过 state["online"] = False 主动停止
调用 exit 后应主动设置 state["online"] = False,避免继续发送探活消息。
API 速览
- 请求类:
linksoul_agentsdk.TeleopRequest - 回调基类:
linksoul_agentsdk.TeleopEnterCallback/TeleopKeepaliveCallback/TeleopExitCallback - 注册:
AgentSdk.register_teleop(TeleopRequest)/AgentSdk.unregister_teleop(teleop_id) - ID:
IdGenerator.generate_teleop_id()
完整签名见 Teleop API。可运行示例见 Teleop 示例。
生命周期与清理
AgentSdkTeleopMgr按teleop_id维护活跃 request;调用enter前必须先register_teleop(...);- 每次 request 方法调用会刷新内部
update_ts;超过 7200s 未活动的 request 会被后台清理线程回收; - 调用完
exit或不再需要遥操时,建议调用agent_sdk.unregister_teleop(teleop_id)显式回收; AgentSdk.release()会连带清理该实例名下的所有 Teleop request(按_linksky_client引用批量移除)。
与 Java SDK 对齐
Python 版接口命名遵循 PEP8(snake_case),语义完全对齐 Java:
| Java | Python |
|---|---|
TeleopRequest.enter(param, cb, timeout) | TeleopRequest.enter(param, cb, timeout) |
TeleopRequest.startAudio(param) | TeleopRequest.start_audio(param=None) |
TeleopRequest.appendAudioDelta(eventId, base64) | TeleopRequest.append_audio_delta(event_id, audio_delta) |
TeleopRequest.doneAudio(eventId) | TeleopRequest.done_audio(event_id) |
TeleopRequest.keepalive(cb, timeout) | TeleopRequest.keepalive(cb, timeout) |
TeleopRequest.exit(cb, timeout) | TeleopRequest.exit(cb, timeout) |
TeleopEnterCallback.onEnterAck(...) | TeleopEnterCallback.on_enter_ack(...) |
TeleopKeepaliveCallback.onKeepaliveAck(...) | TeleopKeepaliveCallback.on_keepalive_ack(...) |
TeleopExitCallback.onExitAck(...) | TeleopExitCallback.on_exit_ack(...) |
AgentSdk.registerTeleop(...) | AgentSdk.register_teleop(...) |
AgentSdk.unregisterTeleop(...) | AgentSdk.unregister_teleop(...) |
IdGenerator.generateTeleopId() | IdGenerator.generate_teleop_id() |