Qwen Intelligence 接入手机应用 / 一次请求的端云分发路径

文章导读
一次请求该在手机上算完还是转出去,判断依据通常不是模型规模,而是这一层请求在当下的可用条件:输入是否落在端侧能保证处理的范围内、端侧能力此刻是否就绪、超时预算够不够、失败之后有没有可走的兜底路径。常见的顺序是应用层先做意图与约束判定,再看端侧能力状态,不满足才交给联网路径。出错时不要从头猜,先看每层日志里同一条请求 ID 断在哪一层。
📋 目录
  1. Ⅰ 画出应用层到端侧能力的调用顺序
  2. Ⅱ 用通用伪代码写出本地优先、联网兜底的分支
  3. Ⅲ 在每层打一条带请求标识的日志
  4. Ⅳ 用断网和超时两种条件验证兜底分支
  5. Ⅴ 按层排查失败归属
A A

一次请求该在手机上算完还是转出去,判断依据通常不是模型规模,而是这一层请求在当下的可用条件:输入是否落在端侧能保证处理的范围内、端侧能力此刻是否就绪、超时预算够不够、失败之后有没有可走的兜底路径。常见的顺序是应用层先做意图与约束判定,再看端侧能力状态,不满足才交给联网路径。出错时不要从头猜,先看每层日志里同一条请求 ID 断在哪一层。

把分发判断放在应用层,端侧只接它能保证处理的输入,联网作为超时与失败后的兜底。落地顺序建议是:先画清层与层之间的调用顺序,再写带超时和降级分支的判断骨架,最后在每层打一条带同一请求 ID 的日志。验证用断网和人为超时两种条件各走一遍,确认降级真的被触发;边界在于端侧拒绝与网络失败必须能分开识别,否则排错会长期卡在同一层。

画出应用层到端侧能力的调用顺序

先确定一次请求会经过哪几层,再谈分发。下面的骨架用占位符表示接口名,接入时替换成你项目里的实际名称;具体命名与调用方式以官方文档为准。

应用层(UI / 业务路由)
  └─ 意图与约束判定        APP_ROUTER.decide(req)
       ├─ 端侧能力层       LOCAL_CAPABILITY.is_ready() / run()
       │     └─ 端侧引擎   LOCAL_ENGINE.run()
       └─ 联网层           CLOUD_GATEWAY.send()
             └─ 远端服务   REMOTE_SERVICE.handle()

关键点是:判定逻辑只放在应用层一处,端侧层不自己做“要不要转云”的决定。这样断点最多只有三个——判定处、端侧调用处、联网调用处,排查面被压小。

用通用伪代码写出本地优先、联网兜底的分支

下面这段不含任何具体 SDK 方法名,只描述结构与分支。LOCAL_TIMEOUT 与 CLOUD_TIMEOUT 需要结合机型和网络环境确认,不建议写死成一个很小的值。

function handle(request):
    request.id = new_request_id()
    log(request.id, 'app', 'start')

    if not APP_ROUTER.decide(request):        # 输入超范围 / 端侧不适用
        return cloud_fallback(request, reason='not_applicable')

    if not LOCAL_CAPABILITY.is_ready():       # 未加载 / 未授权 / 资源不足
        return cloud_fallback(request, reason='local_not_ready')

    result = LOCAL_ENGINE.run(request, timeout=LOCAL_TIMEOUT)
    if result.status == TIMEOUT or result.status == REJECTED:
        return cloud_fallback(request, reason=result.status)
    if result.status == OK:
        return result

function cloud_fallback(request, reason):
    log(request.id, 'fallback', reason)
    try:
        return CLOUD_GATEWAY.send(request, timeout=CLOUD_TIMEOUT)
    except NETWORK_ERROR:
        return degraded_result(request)        # 最后一级降级,不要静默失败

三个分支各自对应一种可观察状态:判定不过、端侧不就绪、端侧超时或被拒。写代码时把 reason 透传到日志里,后面排查就不用靠猜。

Qwen Intelligence 接入手机应用 / 一次请求的端云分发路径

在每层打一条带请求标识的日志

日志的作用是让同一次请求在三个层里串起来。建议固定这些字段:request_id、layer、event、cost_ms、status、reason、net_state。打印位置放在应用层入口、端侧调用前后、兜底触发点、联网返回处。

{'request_id':'r-8f2a','layer':'app','event':'start'}
{'request_id':'r-8f2a','layer':'local','event':'invoke'}
{'request_id':'r-8f2a','layer':'local','event':'done','cost_ms':412,'status':'TIMEOUT'}
{'request_id':'r-8f2a','layer':'fallback','reason':'TIMEOUT'}
{'request_id':'r-8f2a','layer':'cloud','event':'done','cost_ms':980,'status':'OK','net_state':'online'}

cost_ms 用来判断是否踩到超时阈值,net_state 用来区分是链路问题还是本地逻辑问题。字段名可以按你们现有日志规范调整,但同一请求 ID 必须贯穿所有层。

用断网和超时两种条件验证兜底分支

  1. 断网验证:关闭 Wi-Fi 与蜂窝数据后发起一次请求。预期日志出现 fallback,cloud 层返回 NETWORK_ERROR,界面走降级提示或端侧结果;如果该输入本应由端侧处理,则不应出现 fallback 记录。
  2. 超时验证:测试期间把 LOCAL_TIMEOUT 临时改小,让端侧稳定超时。预期本地层 status 为 TIMEOUT,fallback 的 reason 为 TIMEOUT,联网可用时由云端返回 OK。
  3. 区分端侧拒绝与网络失败:端侧拒绝一般发生在 local 层且 status 为 REJECTED,说明输入或权限不满足;网络失败发生在 cloud 层,net_state 为断开或请求根本没发出。测试完成后记得把超时配置改回正常值。

按层排查失败归属

固定一条从应用层到端侧再到网络层的顺序,避免跳层猜原因。同一 request_id 的日志里,最后一条记录所在的 layer 通常就是断点所在层。

  • 应用层:请求 ID 是否生成、decide 的返回是什么、是否进入 fallback、入参是否被截断或改写。判断依据是 app 层日志是否完整。
  • 端侧能力层:is_ready 状态、权限授予情况、模型或资源是否加载完成、本地超时值是否被误改、run 返回的 status。若最后一条是 local + REJECTED,先查输入约束与权限,不要先去动网络配置。
  • 网络层:请求是否真的发出、连接是否建立、返回的 HTTP 状态码、是否命中超时或重试上限。若最后一条是 cloud 且 net_state 断开,才回到链路侧处理。

按这个顺序走一遍,多数情况下十分钟内能定位到层,而不是在应用层反复改逻辑、实际问题却卡在端侧权限或超时阈值上。