把 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 时,改动集中在配置文件而不是散落在代码各处,字段对不上的问题也就不必等到写完才发现。