先把输入模态和返回字段对齐——Wan-Streamer v0.2 调用前的三次试跑

文章导读
把 Wan-Streamer v0.2 接进现有流程时,最容易返工的地方通常不是业务逻辑,而是请求体里模态字段与内容字段的对应关系,以及返回体里结果到底落在哪一层。比较省事的做法是:在写正式适配代码之前,先用三次最小试跑——单模态、双模态、空输入——把请求进得去、结果取得出、错误报得清这三件事各验证一遍,再把核对结果写成配置项和字段映射,后续调用就不用再靠临时记忆。下面的骨架里凡是标了占位符的地方
📋 目录
  1. A 用最小 JSON 骨架跑通单模态请求
  2. B 核对返回结构是否按模态分层
  3. C 跑一次双模态与一次空输入
  4. D 把核对结果写成配置项与字段映射
A A

把 Wan-Streamer v0.2 接进现有流程时,最容易返工的地方通常不是业务逻辑,而是请求体里模态字段与内容字段的对应关系,以及返回体里结果到底落在哪一层。比较省事的做法是:在写正式适配代码之前,先用三次最小试跑——单模态、双模态、空输入——把请求进得去、结果取得出、错误报得清这三件事各验证一遍,再把核对结果写成配置项和字段映射,后续调用就不用再靠临时记忆。下面的骨架里凡是标了占位符的地方,都要按你手上实际返回替换,不要直接照抄。

接入 Wan-Streamer v0.2 前建议先跑三次最小请求:单模态确认“模态字段→内容字段→结果路径”的链路,双模态确认多输入并存时字段是否冲突或顺序错位,空输入确认报错文本和错误码落在哪一层。三次试跑都用原始 JSON 发出、原始 JSON 留存,核对结论固化成超时、重试和字段映射配置,再用一次真实请求验证配置生效。做不到这一步的字段,宁可标成待确认,也不要在代码里写死猜测的路径。

用最小 JSON 骨架跑通单模态请求

先只放三个东西进请求体:模态声明、内容容器、流控开关。别急着加业务参数,参数越多,出问题时越难判断是哪个字段错了。下面这份骨架里的字段名是通用写法,实际以你接入环境的说明为准,不确定的先按占位保留。

{
  "model": "wan-streamer-v0.2",
  "modality": ["text"],
  "input": {
    "text": "<一段简短的待处理文本>",
    "image_url": null
  },
  "stream": false,
  "options": {
    "timeout_ms": 30000
  }
}

每个字段先只回答一个问题,不要一次塞满:

  • modality:告诉服务端这次要用哪几路输入。单模态试跑时只填一个值,用来验证“声明了 text,就只读 text 字段”这个假设是否成立。
  • input.text:文本内容本体。试跑时用固定短句,方便在返回里辨认是不是同一份内容。
  • input.image_url:图像类输入的位置。单模态阶段先置空,观察置空是否被接受,这本身就是一次边界探测。
  • stream:先关掉。流式返回会把结果拆成多个分片,字段对齐阶段用非流式更容易看清完整结构。
  • options.timeout_ms:只是占位,实际超时单位是毫秒还是秒、字段名叫什么,需要结合环境确认。

发出去之后,重点看结果字段的路径。常见位置是 data.outputs[0].content 这一类,但也可能是 data.result.text,甚至直接把文本放在 output 下。第一次试跑时把整段返回原样贴进临时文件(比如 resp_single.json),用 jq 逐层看结构,比靠猜路径可靠:

curl -s -X POST "$WAN_ENDPOINT" \
  -H "Content-Type: application/json" \
  -d @payload_single.json | tee resp_single.json

jq 'paths | join(".")' resp_single.json | head -30

把 jq 打印出来的路径和上面骨架里的占位逐个对上,对不上的就标成“待按实际返回填入”,不要直接写进代码。

核对返回结构是否按模态分层

这一步决定的是代码里取值和错误处理写在哪一层。有两种常见分层方式:一是所有模态的结果都放在同一个 outputs 数组里,靠每一条记录的 modality 字段区分;二是文本和图像各占一个固定字段。两种写法对应完全不同的取值代码,写错就要返工。

{
  "code": 0,
  "message": "ok",
  "data": {
    "task_id": "<请求标识,按实际填入>",
    "outputs": [
      {
        "modality": "text",
        "content": "<文本结果>",
        "finish_reason": "<结束原因占位>"
      }
    ],
    "usage": {
      "input_tokens": "<占位>",
      "output_tokens": "<占位>"
    }
  }
}

对比时只看两件事:文本类结果和图像类结果是不是在同一层。如果图像结果多出一层 image_url 或 b64,那取值代码就得按 modality 分支处理;如果两者都统一在 content 里,分页或拼接逻辑反而更简单。判断依据是你自己那份返回原文,不是别人的示例。

缺失字段的表现也要记下来:返回里没有 content 时,是 null、空字符串,还是整个键都不出现。这三种情况在代码里对应 is None、== ""、.get() 三种写法,混用会漏判。另外确认错误信息的位置——是顶层 code/message,还是嵌在 data.error 里。通常建议把“业务结果解析”和“协议错误判断”分成两层函数,顶层先看 code,再看内容,避免把报错当成空结果吞掉。

跑一次双模态与一次空输入

双模态看的是多输入并存时字段会不会互相覆盖、顺序会不会和 modality 数组对不上。建议用同一句文本配一张固定的小图,这样返回里出现什么问题都容易定位。

{
  "model": "wan-streamer-v0.2",
  "modality": ["text", "image"],
  "input": {
    "text": "描述这张图里的主体",
    "image_url": "https://example.invalid/a.png"
  },
  "stream": false
}

返回原文请整段留存,重点记录:outputs 里出现一条还是两条记录;每条记录的 modality 值是否和请求里的顺序一致;图像结果与文本结果是并列还是嵌套。这三点决定后面写遍历还是写固定下标。

{
  "model": "wan-streamer-v0.2",
  "modality": ["text"],
  "input": {
    "text": ""
  },
  "stream": false
}

空输入这次不要怕报错,就是要看报错长什么样。把状态码、code、message 三段原样记下来,例如实际返回里可能是“参数校验失败”这类文本,具体措辞以你环境返回为准,不要凭印象填。如果空字符串被正常接受并返回了空结果,那说明这个接口不替你做非空校验,校验必须写在你自己的调用层。

据此可以写出几条校验规则:模态数组非空;声明了 text 就必须有非空文本(除非实测确实允许空);声明了 image 就必须有可访问的图像地址;返回里 outputs 为空时按错误处理而不是按成功处理;HTTP 200 但顶层 code 非 0 时走错误分支。这些规则都是从上面两次试跑的原文里推出来的,写进代码才有依据。

把核对结果写成配置项与字段映射

试跑结论留在聊天记录或临时脚本里,过一周就忘。建议把它固化成一份配置,代码只读配置,不直接写字段路径。下面是一份骨架,键名可以按你们的配置体系调整。

wan_streamer:
  endpoint: "https://<host>/<path>"
  connect_timeout_ms: 5000
  read_timeout_ms: 30000
  max_retries: 2
  retry_on: ["connect_timeout", "5xx"]
  field_map:
    request_text: "input.text"
    request_image: "input.image_url"
    result_text: "data.outputs[0].content"
    result_modality: "data.outputs[0].modality"
    error_code: "code"
    error_message: "message"
    request_id: "data.task_id"

字段映射表里的值全部来自前三次试跑看到的真实路径,凡是没确认的就先留空并在注释里标“待确认”,不要写一个看起来像的路径上去。超时和重试次数属于保守设置:读超时给得比试跑时观察到的响应时间长一些,重试只对连接超时和 5xx 生效,对参数校验类错误重试没有意义,反而会放大问题。

验证配置是否真的生效,用一个最小的办法:用同一份双模态请求、带上配置文件里的 endpoint 和字段映射跑一次,把原始返回和按映射取出的值并排打印出来,两者对得上才算配置正确。例如:

curl -s -X POST "$WAN_ENDPOINT" \
  -H "Content-Type: application/json" \
  -d @payload_multi.json | tee resp_multi.json

jq -r '.data.outputs[0].content' resp_multi.json

把 jq 取到的值和程序里通过 field_map 取到的值对比,一致说明映射路径没写错;再故意把 text 置空跑一次,看错误是否落进配置里指定的 error_message 路径,这样错误分支也一并验证了。做完这轮,后续调用 Wan-Streamer v0.2 时,改动集中在配置文件而不是散落在代码各处,字段对不上的问题也就不必等到写完才发现。