OpenMuse 聊天交互深度体验:发送即停止、消息队列与草稿保留的设计细节
【免费下载链接】openmuseA personal agent with a browser, terminal, files, and work that keeps going built with CopilotKit and AG-UI.项目地址: https://gitcode.com/gh_mirrors/op/openmuse
OpenMuse 是一个自带浏览器、终端和文件的个人智能体应用,基于 CopilotKit 与 AG-UI 构建,支持 iOS、Android 和 Web。这篇文章聚焦它聊天界面里三个不起眼却极影响体验的设计:发送与停止共用同一个按钮、追问消息进入可见队列、点停止后草稿原样保留。🪁
为什么聊天输入框值得细看
很多 AI 聊天应用只回答"怎么发消息",很少处理"消息发出去之后怎么办"。OpenMuse 在这几件事上给出了相当克制、可预期的答案,适合任何做聊天交互的开发者参考:
| 场景 | OpenMuse 的做法 |
|---|---|
| 智能体正在回复,我想打断 | 发送箭头原地变成停止方块,一点即停 |
| 智能体正在回复,我有追问 | 追问进入可见队列,回复结束后按顺序自动发送 |
| 我点了停止 | 正在输入的草稿一字不丢 |
| 某条回复中途失败 | 队列暂停,绝不悄悄重发,由你决定重试 |
完整交互设计说明见 docs/EXPERIENCE.md。
发送按钮变停止按钮:同一个位置,两种状态
聊天输入区是一个圆角"输入胶囊":左侧是+附件按钮,中间是多行输入框,右侧是主操作按钮。按钮位置在整个回复过程中始终不动——智能体空闲时它是向上的发送箭头,一旦开始回复,箭头在同一位置变成实心停止方块,结束后再切回箭头。
这种设计的好处是:你的大拇指不需要换位置,打断回复的心智成本极低。按钮的可访问性标签也会从Send message同步变为Stop reply,对屏幕阅读器同样友好。
相关实现在 chat.tsx:replying状态为真时渲染Square(停止),否则渲染ArrowUp(发送)。Web 端还有个小细节——Enter直接发送,Shift+Enter换行(chat.tsx#L755-L767)。
消息队列:回复还没说完,追问已经排好队
OpenMuse 遵循"一次只跑一个对话回合"的原则,但输入框在你等待回复时从不锁定。这时你点发送,消息不会被丢弃,也不会触发并发请求,而是进入一个可见的消息队列(outbox):
- 输入框上方会出现Up next列表,逐条展示排队中的追问,每条右侧有
X可随时移除; - 当前回复结束后,队列按顺序自动逐条发送,发完一条再发下一条;
- 队列保存在打开中的应用里而非服务端收件箱,所以提示语会提醒你"发完前请保持应用开着"。
队列核心是一个不到 50 行的小类ConversationQueue,源码在 conversation-queue.ts,配合 conversation-run.ts 完成单回合执行。它的行为被三条测试严格约束(conversation-queue.test.ts):
- 回复过程中排入的消息,只发一次、按顺序发;
- 点停止会暂停队列(而不是取消消息),之后可恢复;
- 回复失败时,队列进入暂停态——失败的那条消息已经进入对话记录,绝不自动重发,避免同一请求被悄悄执行两遍。
草稿保留:点停止,不会丢字
stop()的动作只有两步:暂停队列、调用停止智能体(chat.tsx#L308-L315)——它从不触碰输入框里的草稿。也就是说:
- 你正在回复途中输入的新内容,点停止后原封不动留在输入框;
- 停止只是中断本次回复,不会取消已委派的服务端任务——后台的持久任务继续走自己的流程;
- 之后直接点发送,新消息立即继续;如果还有被暂停的排队消息,则会出现Send queued messages按钮,由你显式确认后再继续。
"新发送可直接继续、被暂停的旧队列需显式恢复"——这条区分写得很细:新意图不需要被旧事故拖延,而旧事故不该自动替你冒险。
如何上手体验这三个细节
本地跑通后(需要 Node 24 与 pnpm,克隆仓库git clone https://gitcode.com/gh_mirrors/op/openmuse,然后pnpm install并pnpm dev+pnpm dev:web,打开 localhost:8081),按 docs/DEMO.md 给出的"中断自查"步骤验证:
- 发送一条支持类提示词(如"Check out Hacker News for cool stuff"),等智能体开始回复;
- 回复途中继续输入一段追问,观察发送按钮变成停止方块;
- 点击停止方块,确认输入框里草稿还在;
- 继续输入并再发一条,观察它进入Up next队列;
- 等本轮结束后,看队列消息按顺序发出。
演示视频(38 秒移动端 / 42 秒桌面端)完整展示了发送↔停止的按钮切换:docs/DEMO.md。
关键源码与文档索引 📚
| 文件 | 说明 |
|---|---|
| apps/mobile/src/chat.tsx | 聊天界面:输入胶囊、发送/停止按钮、队列 UI |
| apps/mobile/src/conversation-queue.ts | 消息队列核心:入队、暂停、按序 flush |
| apps/mobile/src/conversation-run.ts | 单回合执行与错误捕获 |
| apps/mobile/test/conversation-queue.test.ts | 队列行为三条核心测试 |
| docs/EXPERIENCE.md | 官方交互设计说明 |
| docs/DEMO.md | 演示录制细节与复现步骤 |
这套"发送即停止 + 可见队列 + 草稿保留 + 失败不静默重试"的组合,本质上是在回答同一个问题:当 AI 的回复速度不可控时,如何让用户始终握有确定感。对于正在打磨聊天体验的团队,这套不到千行的实现是非常值得拆解的范本。✨
【免费下载链接】openmuseA personal agent with a browser, terminal, files, and work that keeps going built with CopilotKit and AG-UI.项目地址: https://gitcode.com/gh_mirrors/op/openmuse
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考