本文转载自 SparkDesign:Spark Chat,创造工具的工具。项目仓库为 agentscope-ai/agentscope-spark-design,页面内容来自公开开源项目;转载时保留原文链接与素材来源,图片已保存到本地博客目录。
楔子
某一天,我和一个朋友聊天的时候,突发奇想到一个 Idea——很多女孩子都喜欢个性化的美甲💅🏻图案,是不是可以靠 AIGC 来生成,在去线下店做之前可不可以有这么一个产品就可以把 图案生成、上手预览、图案比较 都给处理妥当了。
那么作为一个产品开发爱好者,我要怎么去实现我的想法快速变成一个可用的 MVP 产品呢?
我需要去设计一个和大模型交互的界面,它能根据和用户的聊天内容生成美甲领域的图案,然后还得有一个图案在指甲上的效果图的展示,这样使用者才能有体感并采纳这个设计到自己的手上,这个界面包含了Web或者Mobile的界面开发,需要实现或者基于一个市面上已有的什么框架去改造,还要支持一个手部的模型,使用者可以在下面像试衣服一样切换不同的图案...这已经是个完整的类 ChatGPT 应用了,开发出来并且体验优秀的难度我想不亚于去做这么一个 AI Chat 产品。
这个过程其实是一件相当麻烦的事情,尤其是我并非一名专业的开发者。但是在这个时代,我们又是幸运的,LLM 无疑放大了普通人的创造力,我可以借由 V0、Lovable、或者 Cursor、Windsuf 来快速的通过自然语言描述实现我的这个 Idea 的雏形。然而非常遗憾的是,我是一个业务开发者,与此同时还是一名专业的体验设计师,现如今 AI 直接通过 Prompt 生成的东西充其量只是一个“玩具”或者“手工艺品”,达不到我的要求。
我想要的是什么呢?
有一个契合我需求的“雏形”,它是优质、逻辑完满、体验精良并且视觉品质高的,然后在此之上融合了我的需求进去,并对我的用户来说足够方便使用。用一个更通俗的表达就是,我想要一个 NailGPT,在我输入 Prompt 后,可以以一个 Three.js 的形式展示出来,并且调用了 4o 或者其他什么模型的生图链路来可以生成并更换指甲的图案,同时,它拥有与 ChatGPT 一般自然、流畅的对话与交互体验。简单来说,这个雏形的大部分内容我是不在乎的,和ChatGPT差不多就行,我真正在乎的就是核心关于手模和图案预览的这一部分功能,但是我自己去做却可能要花大量的精力在这些部分上...
Spark Chat 就是为了解决这样的问题而诞生的。
Chat,组织内容的形式
我们可以来对比以下几个产品:


可以发现,他们都是以对话交互为核心,结合了具体的场景发展出了既类似、又各有特点的领域内产品形态,解决了细分场景下用户的问题,刚刚我们设想的NailGPT不也是如此么?我们将 Chat 视为构建更复杂 AI 交互的基础设施,就像 HTML 是构建 Web 体验的基础。
Spark Chat 就是帮你做好与模型对话的这部分工作,让你专注于你自己的用户场景。
Spark Chat,创造工具的工具
我个人喜欢 Notion,不仅因为它功能强大,还因为它承载了一种“软件作为思维媒介”的精神特质。这种精神,也深刻启发了我们在构建 Spark Chat 的过程。我们无意于再造一个 OpenWebUI、LangUI 或者 AntDX 这些致力于构造类 GPT 界面的前端组件工具,也不做一个样板化的 Chat 产品接口,而是提出了一个以“Card”为核心的 Chat 组织形式,并为它命名为一个有趣的名字——Portkey。
Portkey 是 Spark Chat 框架中的核心构想之一。在哈利波特的故事中,魔法师们通过 Portkey(门钥匙)穿梭于不同的物理空间,门钥匙(Portkey)看似平凡无奇——或许是一只破旧的靴子,一本褪色的书籍,亦或一个古老的杯子,——却能在触碰的刹那,将人从熟悉的此地传送至遥远的彼方而无需施展复杂的咒语。
区别于等常见的“纯文本块流(Text-based Stream)”去构造对话,在 Spark Chat 的设计中,我们借鉴了 Notion 的“乐高哲学”,通过标准化的构建块创造更多可能。这些不仅仅是是信息交换的通道或 UI 组件,而是交互的语义单元,每一个都承载特定的交互意图和信息结构。
Portkey 哲学:从结构出发理解“对话”
Spark Chat 所有对话都基于一个统一模型:
- 每条消息 = 一个 Bubble
- 每个 Bubble 包含多个 portkeys []
- 每张卡片由 { code, data } 构成
Portkey 结构检查
- 语义单元每张卡片是否表达一个清晰意图,而不是只承担样式容器。
- 数据边界
code定义组件类型,data只承载结构化语义内容。 - 渲染协议所有卡片走统一渲染管线,减少一次性 UI 特例。
- 扩展机制新增业务卡片时,只注册新的 Portkey,而不改动整体对话框架。
这意味着:一切消息、内容、交互行为,都是一张“Portkey”,是结构化的,这与 Notion 的 Block-based 结构几乎一脉相承:

你可以将文本、图表、推荐、流程、轮播、统计数据以Portkey的形式组织,你也可以让每次模型回答都变成一组“可被理解与操作的块”,这种结构具有两个重要术价值:
- 统一渲染引擎:所有对话内容都走统一的 Portkey 渲染管线,避免 UI 特例和嵌套分支逻辑
- 语义隔离:每种Portkey封装自己的样式、逻辑、状态,无副作用,利于测试和协同
在数据层面,它实现了从“语言-界面”之间的解耦:开发者可以仅通过结构数据来操控 UI 呈现,让人机交互从“流式对话”走向“结构对话”。
用模型来“拼装”界面
Spark Chat 不再要求模型吐出自然语言,再由 UI “包裹”这些文字。我们拒绝“钉死”的输入输出组件,而是选择用“乐高式”语法描述整个界面状态,于是我们设计了一种对大模型友好的 UI 协议:
{
"portkeys": [
{ "code": "Text", "data": { "content": "什么是 City Pop" } },
{ "code": "MyPortkey", "data": [["活跃用户", 128000], ["账户余额", 54000]] }
]
}
这种结构被自动解析为对应的 UI 卡片,就像 Notion 把/table 命令转化为可操作表格一样。我们构建的是一种 模型-界面共通语言。模型输出结构即视图定义,这种架构在技术上具有以下优势:
- 前后分离:UI 渲染与数据生成职责清晰,便于模型迭代与缓存优化
- 安全性更强:与直接拼 HTML 相比,结构协议可控、安全
- 多模态融合:图文、表格、动画、组件可组合嵌套,不局限于文本流
在 Spark Chat 中,code 是组件类型,data 是语义内容,这本质上就是一门 UI 语言。这门语言有如下特性:
- 声明式:开发者只需描述“Portkey组合”,无需控制流程
- 数据驱动:支持 LLM 直接生成结构化输出
- 可扩展:任意注册
CustomPortkeys,定义新的语义单元
这类语言也许是未来 agent 接口的基础 —— 一套可以被模型理解,也可以被人类组织的 UI 语法
其背后是一种新的合作范式:模型不再是“内容提供者”,而是“结构组织者”;前端不再是“显示模板”,而是“表达引擎”。
我们让模型控制界面,像写 JSON 那样构造多模态对话体验
我们构建的是一种“对话式操作系统”,让用户的应用长在其上
Spark Chat 更像是一套“AI UI Runtime”。它可以承载一切基于语义驱动的用户交互需求:
- 问答类产品
- agent 控制类产品
- 数据报告生成类产品
- ...
其内部架构特性包括:
- 面向会话的 UI 状态容器(Bubble)
- 跨内容类型的通用渲染协议(Portkey)
- 自定义组件注入与状态逻辑复用(CustomPortkeysProvider )
这使得 Spark Chat 更像一个语义可编程的 UI 操作系统,每一次的生成与回复,不是简单的“文本输出”,而是“界面重构”
自定义不是“可选项”,而是核心理念
Spark Chat 拥有强类型的卡片注册机制:开发者通过 CustomPortkeysProvider注入所有组件。 所有自定义Portkey遵循统一约定:
function MyPortkey(props) {
return <Portkey title="...">{...props.data}</Portkey>
}
你可以创建任意的:
- 数据可视化卡(图表、指标)
- 富媒体卡(音视频、轮播)
- 操作卡(表单、按钮、评分)
- 外部组件封装卡(日历、文件列表、代码块)
卡片注册是一种软约束机制。它将技术与表达统一在一个接口层中:你在设计“Portkey”,本质上是在设计语言扩展。
用户体验设计视角下的 Spark Chat
Spark Chat 在 用户体验维度上有独特坚持:
- 模块性:每张Portkey具备清晰边界与可重用性
- 语义性:内容与表现分离,组件不绑死特定数据结构
- 响应性:支持在移动、桌面、自适应容器中灵活展示
- 过渡性:支持Portkey过渡动画与状态装载(如 loading 状态、展开展示)
- 交互性:通过各种Portkey实现复杂业务场景
我们鼓励在 Portkey 层级上思考交互,而不是在整体 UI 上堆叠特例。Portkey本身应具备最小功能闭环,并支持组合与联动。
同时我们强调界面体验的统一感:每种Portkey都有默认行为与样式,统一的边距、圆角、阴影、字体规范保证了视觉一致性。
Spark Chat 是设计系统的对话化延伸,它试图定义“对话式 Design Token”的基本单位
我们用结构定义界面,用卡片组织意义,用语义建立连接
我们不希望被归类为某一种类型,而是希望成为开发者、设计师与模型之间的协作空间。它是一种对话表达语言,是一种 UI 编排协议,是一种人机共创的语法实验。我们希望你不是在“使用 Spark Chat”,而是在用它:
- 构建你理想中的 Agent 界面;
- 组织你与模型的协作流程;
- 进行一次探讨 AI+UI 未来的尝试。
我们不是在开发一个框架,而是在实践一种表达方式的可能性。Spark Chat的根本定位是一种"元工具"(meta-tool)——我们做出了限制同时又释放了可能性,以达成平衡自由和高质量的标准。我们构建了积木(Portkey 协议),但我们同时也在构建盒子(Agent UI Templates),因为大部分用户需要的是:
- 一个能直接用的解决方案
- 一种语义层上就能理解的结构组合
值得一提的是,Spark 将Chat视为基础设施并不意味着它是唯一的交互范式。正如ComfyUI等工具在特定领域成功采用节点式界面,AI交互生态正经历着多范式共存的转型期。Chat作为基础设施的价值不在于消除这种多样性,而在于为最普遍、最自然的交互入口提供结构化框架。随着技术成熟,我们可能会看到这些看似独立的范式逐渐融合,形成能够根据任务复杂度和用户专业度自适应切换的混合模式。
“我们塑造工具,而后工具重塑我们” ——马歇尔·麦克卢汉