AI 聊天界面如何渲染流式 Markdown?代码块没输出完整时怎么办?
下面是一段教学用的模拟面试。
🧑💻 面试官:回答已经正常流式返回,为什么页面里的代码块还会闪来闪去?
🙋♂️ 我:因为每次收到的文字不一定是一段完整的 Markdown。代码围栏、链接和表格可能还没写完。
🧑💻 面试官:那我在每个片段后面补一个代码围栏,再显示,行不行?
🙋♂️ 我:只能作为特定场景里的显示处理,不能把补出的字符存成模型实际回答。
🧑💻 面试官:如果后面又收到真正的结束围栏,或者输出的是危险 HTML 呢?
先分清“模型实际输出”和“当前可以怎样显示”,临时容错不能改变原文,也不能绕过内容安全。
面试速答(60 秒版)
流式 Markdown 的困难在于,前端拿到的是不断增加的内容,而解析器经常需要完整语法。
例如模型刚输出代码块开头,后面的结束围栏还没到。此时可以使用支持不完整语法的渲染器,把当前内容先显示出来,等流结束后再按完整原文校准。
应用要保存未经修改的原始文本,临时补全只服务显示。代码高亮也可以延后或限制频率,避免每个小片段都重新做昂贵处理。
同时,模型输出不能当成可信网页。原始 HTML、链接协议和可执行内容都需要按渲染器的安全规则处理。输出完成,只说明这次流结束了,不说明内容安全。

知识点详解:为什么能逐字收到,还是不能稳定显示?
一个网络片段,不等于一个 Markdown 单元
假设模型正在生成一段 Python 示例。服务端先传来代码围栏的一部分,下一次才传来语言名,再之后才有代码正文。
前端如果只解析当前这一小块,就不知道它属于代码、正文还是链接。它需要结合已经收到的内容,判断此时形成了什么结构。
而且片段边界由生成和传输过程共同决定,并不保证落在一个单词、一行或一个完整语法后面。即使服务端增大缓冲,也不能从根本上保证每次得到完整 Markdown。
所以,接收顺序和文本拼接先要正确,然后才能讨论渲染。
未完成的语法,怎样先显示给用户?
Streamdown 的文档专门讨论了流式 Markdown 中的不完整语法。它属于解决这类问题的一种选择,而不是所有项目都必须采用的唯一方案。
应用也可以根据自己的需求做有限容错。例如判断当前仍处于代码区域,就把已经收到的代码显示在代码容器里;链接目标没完整收到时,先显示普通文字。
注意,这些都是当前显示方式。
假设临时补了结束围栏,原始文本里不能也追加这串字符。否则真正结束围栏到达后,就会多出一份,下载原文与页面内容也可能对不上。
更稳妥的分工是:原始文本保存真实输出,渲染层根据“仍在生成”或“已经结束”的状态决定怎样显示。

为什么代码高亮容易拖慢整个页面?
假设回答已经有上千行代码。如果每收到几个字符,就重新解析整篇内容,再对所有代码重新高亮,新增文字越多,单次刷新要做的工作也越多。
问题不一定出在模型生成慢,也可能是浏览器一直忙着重复处理。
可以先让文字按合理频率更新,延迟处理尚未完成的代码区域,对已经稳定的区域复用结果。是否进一步采用增量解析,要看所选渲染器支持什么,以及实际耗时花在哪里。
先检查解析、高亮、组件重渲染与布局的耗时,别看到卡顿就同时增加一堆缓存。
滚动也要尊重用户。如果用户已经往上翻看内容,新增回答不应强行把他拉回底部。持续跟随与停止跟随,应由页面交互状态决定。

渲染正确,还要限制它能做什么
模型可能输出一个带脚本的 HTML 片段,也可能给出不允许的链接协议。即使语法完全正确,也不应该直接执行。
默认可以关闭原始 HTML 渲染;确实需要时,采用经过核对的清洗与允许规则。链接、图片来源和自定义插件也要检查,不能只防一个 script 标签。
CSP 可以限制执行和加载行为,但它是一层额外防护,不替代对内容和插件的处理。临时显示和最终显示应沿用同一套安全原则,不能因为流结束就切换成未经限制的 HTML。
怎样测试,而不是靠肉眼试一段回答?
准备一份固定的 Markdown,把它按不同位置切开:代码围栏中间、链接地址中间、表格行中间,以及多字节文字附近。
分批送入渲染器,检查过程中有没有丢字或结构持续错乱。全部送完后,再与一次性渲染同一原文的结果比较。
随后加入恶意链接、原始 HTML 和长代码,分别观察安全处理与页面耗时。这样才能知道你的方案解决了哪种问题,哪些内容还需要降级显示。
面试官继续追问
全部生成完再渲染,不是更简单吗?
是,而且某些场景适合这样做。但用户会失去实时反馈。可以先显示安全的纯文本,再在结束时转换,是否值得采用取决于内容长度和交互要求。
临时补全的链接,可以立即允许点击吗?
不宜猜测尚未收到的目标地址。等地址完整、通过允许规则后再开放,比构造一个可能错误的链接更可靠。
前端显示失败,应该重新调用模型吗?
先保留原始输出,用降级视图展示或重新解析。模型已经生成正确文字时,重复调用只会增加成本,而且未必修复渲染问题。
面试速记卡
- 输入边界:流片段不保证是完整 Markdown。
- 状态分开:原始文本保存事实,临时容错只影响显示。
- 性能排查:解析、高亮、渲染与布局分别观察。
- 安全处理:未完成与完成态都不能直接信任模型内容。
- 验证方式:改变切分位置,最后与完整原文渲染结果比较。
公司面试真题
这道题暂未收录可核验的公司真题来源。你可以先阅读本文解析,或浏览已收录的公司面试真题。
浏览公司面试真题 →