CheatSheet@xyzwps

流式渲染

2025-06-12

背景

事情是这样的:我有一个需求,是使用 AI 为客户制定旅行计划。

这个功能的输入是用户的一段话。比如:

我有7天时间,想从山东德州去北京旅行。请帮我安排一个出行计划

输出是一个有漂亮格式的列表。就像下面这样:

传统的方式是,输出一个 JSON,然后把整个 JSON 用组件渲染出来。 如果 AI 思考过程较长,用户可能要对着屏幕上的旋转菊花相当长的时间,体验不够好。 最好能做成流式的,一边输出,一边渲染。

如果我们用流式输出的话,在把所有 token 输出给前端之前,前端用得到的 token join 出来的 JSON 是不闭合的,或者说不合法的。前端解析这种缺屁股的不合法 JSON 十分麻烦。

这时需要一种输出一部分就可以解析的数据格式。我们把目光投降了 YAML。

YAML

如果流式输出 YAML 的话,也会存在无法解析的问题。 但是大部分情况下,输出的 YAML 是可以解析的。不会出现 JSON 那种不接受全部 token 就完全无法解析的情况。 前端接受到 token 之后,把它们 join 成 YAML 字符串,并对此字符串进行解析。

  • 如果解析成功,就立刻渲染
  • 如果解析失败,就等待下一个 token,来了之后再尝试解析。等待的这段时间内,我们渲染之前成功解析的部分

这样就好了。我们开干。

流式渲染旅行计划

具体过程已经说清楚了,点击下面的按钮观看演示效果吧:

可以看到,演示效果还不错。

存在的问题

不过,这种方案依然存在一些难搞的问题:

  1. 现在的大模型都针对输出 JSON 的场景做过优化,但是没有对输出 YAML 的场景做优化。 输出 YAML 时,可能会出现格式错误。 输出有嵌套层级的 YAML 和带有数组的 YAML 时,出现格式错误的概率会更高。
  2. YAML 标准复杂,解析 YAML 需要引入一个体积较大的库。 如果你在微信小程序这种限制应用包体大小的环境中工作,要慎重考虑项目体积的问题。

    上面的示例中,解析 YAML 的库是我手搓的,对于本示例来说,够用了 😂

  3. YAML 输出一半时,有可能出现这种情况:能够解析成功,但是格式不符合要求。比如:
    - name
    会被解析成一个字符串数组。新 token 加入后,yaml 可能变成这样:
    - name: 刻晴
    这次又变成了对象数组。对于这种情况,要想办法处理。