消息格式

版本变更

版本号 变更时间 变更描述
1.9 2023-10-13 user-agent增加ssn字段, 用于标记设备是否出厂时在eFuse写入过防SN、mac等篡改标记位
1.8 2023-01-10 user-agent增加sbc字段, 用于标记设备是否出厂时在eFuse写入过安全启动相关标记位
1.7 2022-09-22 user-agent增加keyPubV字段, 用于标识车联网加密音乐nlu密钥版本号
1.6 2022-01-04 headers中添加client-id, 用于主干链路标识产品类型
1.5 2021-12-20 headers中添加duertoken, duertoken表示小度设备认证的凭据
1.4 2021-02-24 headers中增加firmware-auth-token,firmware-auth-id,用于第三方蓝牙设备固件鉴权
1.3 2021-02-05 headers中增加didp-token,用于端didp实验配置还原、求diff
1.2 2020-01-08 headers中增加device-tag,用于行业版功能判断
1.1 2019-11-06 user-agent中增加了渠道ch和功能集fs,用于运营商版本云端的功能判断,避免和版本号的隐式耦合
1.0 2018-11-19 user-agent中增加了os和model,用于更详细的统计和问题定位

摘要

DCS协议基于HTTP的请求和响应,在Message Body中传递JSON消息,必要时一个HTTP请求/响应中会有多个Message Body(即multipart的HTTP请求/响应),除了JSON消息外附带额外的附件数据(如音频数据、HTML展示数据等等)。设备端请求中的JSON消息由端状态(clientContext)和事件(event)两个部分组成,服务端响应的JSON消息由指令(directive)一个部分组成。本页面详细介绍请求/响应及JSON消息的格式。

设备端请求

设备端向服务端发出的请求,格式如下:

:method = POST
:scheme = https
:path = /dcs/v1/events
authorization = Bearer {{ACCESS_TOKEN}}
content-type = multipart/form-data; boundary=this-is-a-boundary
dueros-device-id = {{DEVICE_UNIQUE_ID}}
baidu-mobile-id = {{BAIDU_MOBILE_ID}}
user-agent = version/{{PRODUCT_VERSION}} didp/{{PRODUCT_VERSION}} app/{{APP_VERSION}} DcsSdk/{{DCS_SDK_VERSION}} os/{{OS_VERSION}} model/{{DEVICE_MODEL}} from/{{INPUT_FROM}} ch/{{CHANNEL}} fs/{{FEATURE_SET}}  sbc/{{SECURE_BOOT_CONTROL}} ssn/{{SECURE_SERIAL_NUMBER}}
device-tag = {{DEVICE_TAG}}
accept-encoding = {{ACCEPT_ENCODING}}
didp-token = {{DIDP_TOKEN}}
firmware-auth-id = {{FIRMWARE_AUTH_ID}}
firmware-auth-token = {{FIRMWARE_AUTH_TOKEN}}
duertoken = {{DUERTOKEN}}
client-id = {{CLIENT_ID}}

--this-is-a-boundary
Content-Disposition: form-data; name="metadata"
Content-Type: application/json; charset=UTF-8

{
    "clientContext": [
        {{CLIENT_CONTEXT_1}},
        {{CLIENT_CONTEXT_2}},
        ...
    ],

    "event": {{EVENT}}
}

--this-is-a-boundary
Content-Disposition: form-data; name="audio"
Content-Type: application/octet-stream

{{binary audio data}}
--this-is-a-boundary--

下面详细介绍其中的内容。

HTTP/2 method/scheme/path

:method = POST
:scheme = https
:path = /dcs/v1/events
  • method
    • 上报event都使用POST请求。
  • scheme
    • 全部使用带加密的https。
  • path
    • 上报交互事件应该把请求发到/dcs/v1/events,其中v1是协议版本号。一次设备端请求只允许上报一个交互事件。
    • 上报统计事件应该把请求发到/dcs/v2/events,其中v2是协议版本号。 一次设备端请求允许上报一个或者多个统计事件。
    • 由于历史的原因,统计事件和交互事件未加区分,所有事件都通过/dcs/v1/events上报,因此/dcs/v1/events对应的服务端同时具备处理统计事件和交互事件的能力,而/dcs/v2/events对应的服务端只具备处理统计事件的能力;随着设备端支持统计事件和交互事件区分的能力升级,升级后的设备端统计事件只允许通过/dcs/v2/events上报,未升级的设备端统计事件依然可以通过/dcs/v1/events上报。

HTTP/2 Headers

authorization = Bearer {{ACCESS_TOKEN}}
content-type = multipart/form-data; boundary=this-is-a-boundary
dueros-device-id = {{DEVICE_UNIQUE_ID}}
baidu-mobile-id = {{BAIDU_MOBILE_ID}}
user-agent = version/{{PRODUCT_VERSION}} didp/{{PRODUCT_VERSION}} app/{{APP_VERSION}} DcsSdk/{{DCS_SDK_VERSION}} os/{{OS_VERSION}} model/{{DEVICE_MODEL}} from/{{INPUT_FROM}} ch/{{CHANNEL}} fs/{{FEATURE_SET}}
device-tag = {{DEVICE_TAG}}
accept-encoding = {{ACCEPT_ENCODING}}
didp-token = {{DIDP_TOKEN}}
firmware-auth-id = {{FIRMWARE_AUTH_ID}}
firmware-auth-token = {{FIRMWARE_AUTH_TOKEN}}
duertoken = {{DUERTOKEN}}
client-id = {{CLIENT_ID}}
  • authorization
    • 用户身份验证和授权的凭证,需要提供从认证服务器获取的Access Token。
    • 如何获取Access Token,详情见认证与授权文档
  • content-type
    • DCS设备端的请求是multipart的HTTP请求,即请求中可以有多个消息(message),以boundary隔开。
  • dueros-device-id
    • 设备的唯一ID。比如在手机上,可以使用IMEI。
    • 该ID不超过64个字符(含64个)。构成改ID的字符集仅限于
      • 大写字母: A-Z
      • 小写字母: a-z
      • 数字: 0-9
      • 其他字符: -_
  • (optional) baidu-mobile-id
    • 百度各手机产品线使用统一ID。
    • 该ID使用imei等信息生成,用于百度各个手机产品线之间统一标识一台设备,生成方法参加《百度CUID使用规范》、百度CUID SDK。
  • user-agent
    • 设备端应用程序和配置的版本信息,包括didp配置的版本信息,设备的app版本信息,DcsSdk的版本信息,操作系统版本信息,设备型号信息。服务端只能用于统计和问题定位与追查,不可作为其他具体功能相关的用途使用。
      • version/{{PRODUCT_VERSION}} didp/{{PRODUCT_VERSION}},version和didp都表示didp配置的版本信息(兼容原来使用version),version和didp表示意义相同,推荐使用didp,如果两者同时存在,以didp为准,{{PRODUCT_VERSION}}代表didp配置的版本号,是一个正整数。
      • app/{{APP_VERSION}},表示设备端和服务端通讯所使用的应用程序的版本信息,{{APP_VERSION}}取值为大小写字母、数字、下划线、中划线构成的字符串。
      • DcsSdk/{{DCS_SDK_VERSION}},如果设备端使用了DuerOS提供的DcsSdk,需要填写DcsSdk的版本号,{{DCS_SDK_VERSION}}取值为大小写字母、数字、下划线、中划线构成的字符串。
      • os/{{OS_VERSION}},{{OSVERSION}}的取值为```{操作系统类型}{版本号}```,如"android_8.1.0"或"ios_12.1.0"。
      • model/{{DEVICE_MODEL}},设备型号。android下取android.os.Build.MODEL,ios下取utsname().machine
      • from/{{INPUT_FROM}},optional,同一设备存在多个请求入口时,用作入口的标记。{{INPUT_FROM}}取值为大小写字母、数字、下划线、中划线构成的字符串。
      • ch/{{CHANNEL}},optional,渠道标志。运营商版本需要提供渠道信息。{{CHANNEL}}取值为大小写字母、数字、下划线、中划线构成的字符串,长度不超过64个字符。现有渠道:CMCC(中国移动)、CUCC(中国联通)、CTCC(中国电信)
      • fs/{{FEATURE_SET}},optional,功能集(设备端功能的集合)标志。{{FEATURE_SET}}取值为大小写字母、数字、下划线、中划线构成的字符串,长度不超过64个字符。运营商版本需要提供功能集信息,如CM01、CM03、CT01、CT05、CU01等。
      • keyPubV/{{PUBLIC_KEY_VERSION}}, optional, 加密车联网音乐nlu的RSA密钥版本号。{{PUBLIC_KEY_VERSION}}取值为大小写字母、数字、下划线、中划线构成的字符串,长度不超过64个字符。初始版本号为: 1.0.0
      • sbc/{{SECURE_BOOT_CONTROL}}, optional, 用于标记设备出厂时是否在eFuse中写入安全启动标记位。{{SECURE_BOOT_CONTROL}}取值为0(未写入)或 非0(已写入,需要触发安全启动,由于该字段硬件可能用来做多个不同标记,但只要是非0,就一定表示开启安全启动)。默认为0
      • ssn/{{SECURE_SERIAL_NUMBER}}, optional, 用于标记设备出厂时是否在eFuse中写入防SN篡改标记位(写入后只要sn、mac、cmei、cuei、ctei中有任何一个被改写都视为非法设备,会在sn后缀带上_invalid) {{SECURE_SERIAL_NUMBER}}取值为0(未写入)或 1(已写入,需要触发防SN篡改校验)。默认为0
      • 例如: user-agent: didp/2 app/1.0.1 DcsSdk/1.0.2 os/ios_12.1.0 model/iPhone10,3 from/fyp ch/CMCC fs/CM03 keyPubV/1.0.0 sbc/3 ssn/1 表示端当前的didp配置的版本是2,app版本是1.0.1,DcsSdk版本是1.0.2, 系统为ios 12.1.0,设备型号为iPhone10,3(iPhone X),请求入口是手机负一屏,渠道是中国移动,功能集是CM03,加密nlu的RSA密钥版本号为1.0.0,安全启动标记位为已写入状态,防SN篡改校验标记位为已写入状态
  • (optional) device-tag
    • 数据格式为querystring样式,value经过urlencode编码,型如A=B&C=D
    • 云端会通过特定指令向设备端设置和删除指定tag,设备端以key/value的集合维护所有tag,当请求云端时,转为querystring形式的字符串放入device-tag
  • (optional) didp-token
    • 从SetDeviceDefaultConfig指令payload中读取didp_token。如果有值(包括空串),则在后续的请求带上,如果没有值(没有键名),则后续请求不要携带该值。
    • 取值为大小写字母、数字、下划线、中划线构成的字符串,长度不超过64个字符
  • (optional) firmware-auth-id
    • 对于第三方的蓝牙设备,要对他们收取licences费,需要先在我们这里购买三元组(product_id, triad_id, secret),在生产设备时会把三元组信息烧录到硬件里面。硬件在使用的时候,我们的固件sdk会拿到这些信息,在调用度秘云端服务的时候,会带上相关信息供云端做鉴权(方案详细文档)。firmware-auth-id字段为三元组中的triad_id。
  • (optional) firmware-auth-token
    • 三元组授权过的固件生成的用于设备鉴权的token
  • (optional) duertoken
    • 小度设备认证的凭据, 用于认证用户、设备请求的合法性
    • 字符串类型,在设备绑定或者刷新时创建并返回给设备, 由设备上存储和携带. 在DCS及其旁路请求中上报, 作为请求认证的一种方式
  • (optional) client-id
    • 设备client-id, 字符串类型, 小度设备在访问DCS新事件链路dcs/v3/events时上报,用于标识一个产品类型
  • (optional) accept-encoding
    • 客户端支持的内容压缩格式(multipart协议,每一个part支持的Content-Encoding)
    • 取值:
      • gzip
      • deflate
  • (optional) extension
    • 可选头,云端兼容解析带不带该头两种情况。云端做性能优化使用,比如 name=="ProgressReportIntervalElapsed"时,度秘DCS服务收到该事件扩展信息后,针对大部分事件,云端接入层DCS服务是不需要解析的,直接拦截或透传到下游UI进行解析和处理。能减少DCS服务事件、端状态的解析所花费耗时和CPU消耗。
    • 取值:namespace,name,token
      • 数据格式为querystring样式,value经过urlencode编码,如namespace=ai.dueros.device_interface.audio_player&name=ProgressReportIntervalElapsed&token={{STRING}}
      • 获取不到token的情况时,可以只上报namespace和name。云端根据具体事件进行判断。
  • submodel
    • 系统属性{{ro.product.submodel}}的值,用于区分不同散热方案的设备。
    • 已量产设备中工厂默认写入值为0000。

Message 1 Headers

Content-Disposition: form-data; name="metadata"
Content-Type: {{CONTENT_TYPE}}; charset=UTF-8
Content-Encoding: {{CONTENT_ENCODING}}
  • CONTENT_TYPE
    • 取值:
      • application/json
      • application/pb: proto3格式
  • (optional) CONTENT_ENCODING
    • 取值:
      • gzip
      • deflate

上报事件时,事件消息的Header,其中Content-Type需要声明为application/json,编码使用UTF-8。

Message 1 Body

{
    "clientContext": [
        {{CLIENT_CONTEXT_1}},
        {{CLIENT_CONTEXT_2}},
        ...
    ],

    "event": {{EVENT}}
}

这是设备端响服务端上报事件的JSON消息,主要有2个部分组成:

  • clientContext
    • 端状态:服务端在处理某些事件时,需要了解在请求当时设备端各模块所处的状态。比如端上是否正在播放音乐,是否有闹钟在响,是否正在播报等等。
    • 只有部分事件上报需要附带设备端状态,一个事件是否需要携带设备端状态,需要携带哪些状态,详见事件的说明文档。
  • event
    • 事件:设备端上发生任何事情,需要通过事件来通知服务端。比如开始播放音乐了,音乐播放结束了,闹铃响了,用户开始了语音请求等等,都对应一个个事件。

Message 2 Headers & Body

Content-Disposition: form-data; name="audio"
Content-Type: application/octet-stream

{{BIANRY AUDIO DATA}}

有些事件的上报,需要在请求中附带额外的二进制附件。比如用户开始语音请求,设备端开始“倾听”是要上报ListenStarted事件,此时请求中需要附带录制的语音数据。

在JSON消息之后,用boundary隔开,可以附上下一个HTTP消息,此消息为application/octet-stream格式,即二进制格式,消息内容为所需要附上的二进制数据。

服务端响应

服务端的响应,格式如下:

:status = 200
content-type = multipart/related; boundary=this-is-a-boundary; type="application/json"

--this-is-a-boundary
Content-Disposition: form-data; name="metadata"
Content-Type: application/json; charset=UTF-8

{
    "directive": {{DIRECTIVE}}
}

--this-is-a-boundary
Content-Disposition: form-data; name="audio"
Content-Type: application/octet-stream
Content-ID: <1234>

{{BINARY AUDIO DATA}}
--this-is-a-boundary
Content-Disposition: form-data; name="attachment"
Content-Type: text/plain
Content-ID: <5678>

{{RAW ATTACHMENT DATA}}
--this-is-a-boundary--

下面详细介绍其中内容。

HTTP/2 status & Headers

:status = 200
content-type = multipart/related; boundary=this-is-a-boundary; type="application/json"
  • status
    • HTTP响应代码,正常返回200或204

Message 1

Content-Disposition: form-data; name="metadata"
Content-Type: application/json; charset=UTF-8
Dcs-Info: name={{ENUM_DIRECTIVE_NAME}};
Content-Encoding: {{CONTENT_ENCODING}}
Extension: {{EXTENSION}}
Predict:dialogRequestId={{STRING}}&index={{INTEGER}}&sessionId={{STRING}}&type={{INTEGER}}&query={{STRING}}
{
    "directive": {{DIRECTIVE}}
}
  • (optional) Dcs-Info
    • 可选头,在指令body比较大的时候,可供客户端做性能优化。比如 name=="RenderSwanView"时,端上可以在收指令的过程中,提前开始启动界面。name是一个枚举值,对应dcs指令,目前可选的取值如下
      • RENDER_SWAN_VIEW: ai.dueros.device_interface.screen.RenderSwanView
      • RENDER_SWAN_DIALOG: ai.dueros.device_interface.screen.RenderSwanDialog
      • EXECUTE_SWAN_CODE: ai.dueros.device_interface.screen.ExecuteSwanCode
      • RENDER_AUDIO_LIST: ai.dueros.device_interface.screen_extended_card.RenderAudioList
      • RENDER_VIDEO_LIST: ai.dueros.device_interface.screen_extended_card.RenderVideoList
      • RENDER_ALBUM_LIST: ai.dueros.device_interface.screen_extended_card.RenderAlbumList
  • (optional) CONTENT_ENCODING
    • 取值:
      • gzip
      • deflate
  • (optional) Extension
    • 可选头,端上能兼容解析带不带该头两种情况。服务器根据didp配置信息决定是否下发该头,供客户端做性能优化。比如 name=="RenderSwanView"时,DCS-SDK收到该指令后,检查是否需要解析,针对大部分的渲染指令,DCS-SDK是不需要解析的,直接透传到launcher进行解析和处理。能减少DCS-SDK一半以上信令的耗时和CPU消耗。
    • 取值:原来directive的header内容,包含namespace,name,dialogRequestId,messageId。
      • 数据格式为querystring样式,value经过urlencode编码,如namespace=ai.dueros.device_interface.voice_output&name=Speak&dialogRequestId={{STRING}}&messageId={{STRING}}
  • directive
    • 如果包含Extension头,directive的header部分去掉,Extension头已经包含头部信息。
    • 指令:服务端下发给设备端的指令,需要设备端执行。比如播放一个语音,设置一个闹钟,播放一个音乐等等。
  • (optional) Predict
    • 可选头,端上能兼容解析带不带该头两种情况,支持预测预取结果到端功能,需要带上该header信息。服务器根据didp配置信息决定是否下发该头,端到端做性能优化使用。支持预测预取结果到端时,云端会把预测预取的中间结果发到端上,DCS-SDK收到该指令集后,根据dialogRequestId和index标识唯一的预测中间结果指令集,并在端上进行预测Query对应指令集的缓存,当最终识别文本到达时,根据最终文本和预测文本进行命中判断,命中后,更新本次结果到云端,并直接执行指令,从而加速端到端的速度。
    • 取值:包含dialogRequestId、index、sessionId、type、query。
      • 数据格式为querystring样式,value经过urlencode编码,如dialogRequestId={{STRING}}&index={{INTEGER}}&sessionId={{STRING}}&type={{INTEGER}}&query={{STRING}}
      • dialogRequestId 语音交互会话id
      • index 一次语音对话序列号,从0开始递增计数,0不一定返回且不保证连续(不是每个语音中间包都返回结果)
      • sessionId 云端记录id
      • type 取值范围为0、1、2。0表示中间包结果非最后一条指令,1表示中间结果最后一条指令,2表示最终包指令
      • query 云端预测query

Message 2

Content-Disposition: form-data; name="audio"
Content-Type: application/octet-stream
Content-ID: <1234>

{{BINARY AUDIO DATA}}

有些指令需要附带额外的附件。比如播报需要附带播报语音,屏幕展示指令需要附带所需要展示的内容。此时,附件作为JSON消息之后下一个HTTP消息返回,消息Header中的Content-ID为附件的ID,一般对应指令中的cid:<1234>。附带的播报语音通过name="audio"与其他类型附件区分开,端上可以先行解码音频。

Message 3

Content-Disposition: form-data; name="attachment"
Content-Type: text/plain
Content-ID: <5678>

{{RAW ATTACHMENT DATA}}

有些指令需要附带额外的附件。比如屏幕展示指令需要附带所需要展示的内容。此时,附件作为JSON消息之后下一个HTTP消息返回,消息Header中的Content-ID为附件的ID,一般对应指令中的cid:<5678>。Content-Type标识附件内容的格式。普通的附件通过name="attachment"与播报语音区分开。

JSON消息格式

所有的directive / event / clientContext都遵循同一种格式。

以Speak指令为例,格式如下:

"directive": {
    "header": {
        "namespace": "ai.dueros.device_interface.voice_input",
        "name": "Speak",
        "messageId": "messageId-1234"
    },
    "payload": {
        "url": "cid:1234",
        "format": "AUDIO_MPEG",
        "token": "token-1234"
    }
}

header中的namespace和name确定了这是什么指令(在本例子中这是Speak指令),确定了指令,就知道怎么解读payload里的数据了。

以Java为例,在实际实现中,namespace对应包,name对应类,payload对应类成员。比如这里Speak是一个类,类成员包括url/format/token三个。