Vercel 的 streamdown 解决了一个概念上简单但极其重要的问题:用 Markdown 渲染 AI 回复,能让内容更美观;而最直观的体验,莫过于看着格式化的 Markdown 在 AI 逐字输出时同步渲染成型。
该项目(https://github.com/vercel/streamdown,作为 Vercel AI SDK 的一部分 https://ai-sdk.dev/elements)非常出色,它支持从传统 Markdown 到代码块、甚至是 Mermaid 图表的一切。更重要的是,即使在内容输出过程中,它看起来也像是完整的 Markdown。它有一个非常棒的特性,当 LLM 的 Markdown 响应还不完整时,它能在客户端智能地补全闭合标签,保证渲染效果。(顺便一提,自 streamdown 发布后,react-markdown 也为流式渲染场景提供了更好的支持)。
然而,目前 streamdown 只支持 React Web。这意味着它无法与 React Native 技术栈原生兼容。原因很简单:streamdown 的依赖项依赖于浏览器 DOM,而这在移动端环境中是不存在的。对于一个 Web 库来说,这是完全合理的设计,但也意味着要支持移动端就必须重构。
这恰恰凸显了一个重要问题:今天的 AI 应用主要是为 Web 而非移动端构建的。为 Web 构建通常更容易,但世界终究是运行在移动设备上的——通过手机与软件互动的人远比通过电脑的多,这一点在新一代的 AI 应用中将继续成立。
为了解决这个问题,我们开发了 streamdown-rn (https://github.com/darkresearch/streamdown-rn)。其设计目标是支持 streamdown 的所有功能,只不过是为 React Native 定制的:
- 内置排版、格式化、GFM
- 交互式代码块
- 数学公式
- Mermaid 图表
与 streamdown 设计为 react-markdown 的直接替代品类似,streamdown-rn 被设计为 react-native-markdown-display 的直接替代品。
超级增强:动态组件注入
在构建过程中,我们决定增加一个独特的附加功能。关于 AI 应用的未来演进,一个重要的观点是:我们将超越「聊天优先」的体验,并最终认为它是一种过时的交互方式。 这个观点主要基于两点:
- 软件 1.0 的演进路径就是:文本 → GUI。 我们从命令行终端开始,逐步走向了更具交互性、沉浸感的视觉体验。
- 动态用户界面在 AI 领域正成为一个热门话题。 Vercel AI SDK 早已支持生成式 UI 组件(Generative UI)。它让 LLM 能够使用工具,将渲染好的 React 组件返回给客户端。
然而,Vercel 的实现方式中,天气工具将数据与视觉展现耦合在了一起。AI 不够灵活,比如它无法使用一个工具获取天气数据,再用另一个机制来展示这些数据。它还被限制在“一次获取、一次展示”的模式中。
我们将动态 UI 组件的想法向前推进了一步,并使其变得通用。
streamdown-rn 中的动态组件注入如何工作?
streamdown-rn 支持一种双大括号语法 {{...}},让你的 AI 能够将任意 React Native 组件直接注入到 Markdown 响应中。例如:
这是一些 **加粗文本** 和一个动态组件:
{{component: "TokenCard", props: {
"tokenSymbol": "BTC",
"tokenPrice": 45000,
"priceChange24h": 2.5
}}}
这瞬间将你的聊天界面变成了一个可编程的 UI 平台,LLM 可以在其中即时组合出复杂的、可交互的界面。
目前,我们限制 streamdown-rn 只能使用开发者预先设置并告知 LLM 的“白名单组件注册表”。但你可以想象,未来这种格式甚至可能允许 LLM 从头开始,即时创建自己的 React 组件来渲染信息。
今天,注册表中的组件通过 JSON Schema 进行验证,并包含各种信息,以帮助 LLM 就何时使用它们做出明智的决策:
import { ComponentRegistry, ComponentDefinition } from 'streamdown-rn';
import { TokenCard } from './components/TokenCard';
import { Chart } from './components/Chart';
// 使用 JSON Schema 定义你的组件以进行验证
const components: ComponentDefinition[] = [
{
name: 'TokenCard',
component: TokenCard,
category: 'dynamic',
description: '用于展示代币信息卡片',
propsSchema: {
type: 'object',
properties: {
tokenSymbol: { type: 'string' },
tokenPrice: { type: 'number' },
priceChange24h: { type: 'number' }
},
required: ['tokenSymbol', 'tokenPrice']
}
},
{
name: 'Chart',
component: Chart,
category: 'dynamic',
description: '渲染一个图表',
propsSchema: {
type: 'object',
properties: {
data: { type: 'array' },
type: { type: 'string', enum: ['line', 'bar', 'pie'] }
},
required: ['data', 'type']
}
}
];
这种设计为 LLM实现了数据与视觉的解耦,我认为这极其强大。它确实依赖于一个更智能的 AI,能够理解何时以及如何使用这些视觉组件,但我认为,现在的模型已经发展到了一个我们可以开始尝试构建这类可靠体验的阶段。