让生图和声音进入角色对话
生图和 TTS 作为独立微应用时,用户需要主动打开工作台、准备输入、等待任务完成。它们已经是可用的能力,但还没有真正进入聊天。
这次接入的目标很明确:当一条助手消息完成后,系统可以根据当前对话的模式,为它生成声音或图片。用户继续阅读文字,图片显示在文字下方;需要听的时候,直接点击消息旁边的播放按钮。
这不是给聊天再增加一套模型配置。聊天只负责表达“这条线程是否需要这项能力”,具体使用哪个服务商、哪个模型和哪些详细参数,仍然由 TTS 和生图微应用自己的配置决定。
媒体跟着消息走
聊天媒体和消息内容是绑定的。TTS 生成的音频、生图得到的图片,都关联到具体的助手消息、线程和对应任务。数据库保存的是服务端产物的绝对路径,前端不会直接读取本地文件,而是通过后端受控接口获取内容。
助手消息完成
-> 读取线程媒体开关
-> 创建 TTS / 生图任务
-> 等待任务完成
-> 绑定消息、任务和媒体记录
-> 前端刷新消息媒体状态
媒体任务在文字回复完成之后执行。这样做有两个实际结果:文字不会被生图或语音服务阻塞,RAG 的来源和执行展示也不会被媒体任务改写;同时,媒体结果仍然能够准确回到产生它的那条消息上。
TTS:按钮只负责播放
TTS 的界面保持克制。助手消息操作区提供一个播放按钮,不额外插入一张音频卡片。第一次播放时,如果这条消息还没有成功音频,聊天会调用当前微应用配置的 TTS 服务;生成成功后,音频记录会被保存下来。
之后再次播放,直接使用已经保存的媒体,不重复请求服务商。如果保存的文件已经不存在,系统会把它当作不可用产物,重新发起生成,而不是让用户一直面对一个失效的播放地址。
GPT-SoVITS 也遵守同一条规则。聊天端只使用服务端已经绑定的参考音频 ID,不读取浏览器 IndexedDB 里的临时 ID,也不在聊天侧猜测最近一次任务。参考音频属于 TTS 微应用管理的用户素材,生成后的语音属于消息产物,两者的生命周期彼此独立。
Role:图片自然出现
Role 模式最适合让回复带有视觉表达。当线程选择了角色,并且没有绑定知识库时,图片能力可以自动开启。助手回复完成后,系统根据这条消息发起生图任务,成功结果直接显示在助手文字下方。
图片按钮也只在这个场景出现。用户可以手动触发或重新生成图片,但按钮不会把生图的工作流、尺寸和服务商参数搬进聊天页面。聊天只消费微应用已经准备好的能力配置。
Role + RAG:文字来源优先
角色和知识库同时存在时,图片入口直接不显示,也不会发起生图请求。
这不是把功能按钮置灰,而是表达清楚当前模式的产品边界:当用户要求基于知识库回答时,当前对话优先保证检索来源、引用和执行过程的可解释性,不额外引入角色图片任务。TTS 仍然可以使用,因为声音不会改变 RAG 的检索来源或答案结构。
因此,媒体能力不会参与文本生成、RAG 检索或 Role prompt 编排。它只消费已经完成的助手消息和线程状态,保持聊天核心链路的原有语义。
重新生成必须清理旧产物
媒体是消息的一部分,就必须和消息的生命周期一致。用户重新生成回复时,旧助手消息对应的图片、音频文件和数据库关联会被清理,新的助手消息再建立新的媒体记录。
同样的清理规则也覆盖分支裁剪、删除消息和删除线程。不能只清掉前端状态,否则本地磁盘和数据库会留下用户已经看不到的产物;也不能删除仍被其他消息引用的文件。清理失败时,系统保留明确的失败状态,方便继续处理,而不是静默丢失事实。
生图任务和媒体记录还支持服务重启后恢复。服务重启不会让已经生成的图片和音频变成只能在当前进程里找到的临时对象,刷新线程后,前端仍然可以从后端恢复状态并继续展示或播放。
能力进入聊天,但边界不进入混乱
这次接入没有修改 AgentGraph,没有改 RAG、Chat、Role 的核心逻辑,也没有新增聊天侧的 provider、model 或 voice 配置。新增的只是媒体任务、线程开关、消息关联、受控读取和清理规则。
用户看到的是两个简单动作:图片出现在文字下面,声音通过按钮播放。系统内部则把每个产物绑定到真实消息、真实任务和真实文件生命周期上。
这让生图和 TTS 从“需要进入工作台才能使用的工具”,变成了角色对话里可以自然调用的能力;同时,微应用仍然拥有自己的配置边界,Chat 仍然只负责聊天。