多模型响应展示(中文译文)
原始 DeepWiki 页面:https://deepwiki.com/open-webui/open-webui/5.5-multi-model-response-display
翻译时间:2026-06-09T16:08:37.793Z
翻译模型:deepseek-chat
原文字符数:11679
项目:Open WebUI (open-webui)
---
多模型响应展示
相关源文件
以下文件为本 wiki 页面的生成提供了上下文:
src/lib/apis/streaming/index.tssrc/lib/components/chat/Chat.sveltesrc/lib/components/chat/MessageInput.sveltesrc/lib/components/chat/Messages.sveltesrc/lib/components/chat/Messages/Message.sveltesrc/lib/components/chat/Messages/MultiResponseMessages.sveltesrc/lib/components/chat/Messages/RateComment.sveltesrc/lib/components/chat/Messages/ResponseMessage.sveltesrc/lib/components/chat/Messages/ResponseMessage/TaskList.sveltesrc/lib/components/chat/Messages/UserMessage.sveltesrc/lib/components/chat/ModelSelector.sveltesrc/lib/components/chat/ModelSelector/ModelItem.sveltesrc/lib/components/chat/ModelSelector/ModelItemMenu.sveltesrc/lib/components/chat/ModelSelector/Selector.sveltesrc/lib/components/icons/Label.sveltesrc/lib/components/icons/Tag.sveltesrc/lib/utils/index.ts
目的与范围
多模型响应展示系统允许用户同时查询多个 AI 模型,并以对比布局查看它们的响应。当用户一次性向多个模型发送消息时,系统会以标签页界面或并排卡片的形式展示每个模型的响应,方便用户比较不同模型对同一提示的回答。
本文档涵盖实现多模型响应渲染的前端组件和数据结构。关于生成这些响应的后端聊天补全管道,请参阅聊天中间件与请求流程。关于父消息容器组件,请参阅消息渲染。
---
架构概览
当用户消息的 selectedModels 数组中包含多个模型时,多模型响应展示系统被触发。系统检测到该条件后,会通过 MultiResponseMessages.svelte 进行渲染,而非标准的单响应渲染器。
组件层级
标题:多模型展示的组件层级
graph TB
Chat["Chat.svelte<br/>主聊天编排器"]
Messages["Messages.svelte<br/>消息列表容器"]
Message["Message.svelte<br/>单条消息包装器"]
UserMsg["UserMessage.svelte<br/>用户消息渲染器"]
MultiResp["MultiResponseMessages.svelte<br/>多模型响应协调器"]
RespMsg["ResponseMessage.svelte<br/>单模型响应渲染器"]
Chat --> Messages
Messages --> Message
Message --> UserMsg
Message --> MultiResp
Message --> RespMsg
MultiResp --> RespMsg
MultiResp -.->|"N 个实例"| RespMsg
Note1["决策点:<br/>parentMessage.models.length > 1"]
Message --> Note1
Note1 -.-> MultiResp
Note1 -.-> RespMsg
来源:src/lib/components/chat/Messages/Message.svelte:76-128
Message.svelte 中的路由逻辑通过检查父消息(用户提示)上 models 数组的长度来决定使用哪个渲染器:
{#if history.messages[messageId].role === 'user'}
<UserMessage ... />
{:else if (history.messages[history.messages[messageId].parentId]?.models?.length ?? 1) === 1}
<ResponseMessage ... />
{:else}
<MultiResponseMessages ... />
{/if}
来源:src/lib/components/chat/Messages/Message.svelte:55-128
---
消息历史数据结构
多模型响应采用基于树的消息历史组织方式,每个用户消息可以有多个子响应,每个模型对应一个。
历史模式
标题:多模型消息树结构
graph TB
subgraph "history 对象"
messages["messages: Object<messageId, Message>"]
currentId["currentId: string | null"]
end
subgraph "消息结构"
id["id: string (UUID)"]
parentId["parentId: string | null"]
childrenIds["childrenIds: string[]"]
role["role: 'user' | 'assistant'"]
content["content: string"]
model["model: string"]
modelIdx["modelIdx: number"]
models["models: string[]<br/>(仅用户消息包含)"]
end
subgraph "多模型组织"
userMsg["用户消息<br/>models: ['gpt-4o', 'claude-3-5-sonnet']"]
resp1["响应 1<br/>model: 'gpt-4o'<br/>modelIdx: 0"]
resp2["响应 2<br/>model: 'claude-3-5-sonnet'<br/>modelIdx: 1"]
userMsg --> resp1
userMsg --> resp2
end
来源:src/lib/components/chat/Messages/MultiResponseMessages.svelte:64-74,src/lib/utils/index.ts:195-226
分组消息 ID 结构
MultiResponseMessages 按模型索引组织消息,以实现高效导航:
| 变量 | 类型 | 用途 |
|---|---|---|
groupedMessageIds | Object<number, {messageIds: string[]}> | 将所有响应 ID 按对应的模型索引分组。 |
groupedMessageIdsIdx | Object<number, number> | 跟踪每个模型当前可见的响应索引。 |
这种组织方式支持独立导航每个模型的响应历史,因为多次重新生成或编辑可能为每个模型创建多个响应版本。
来源:src/lib/components/chat/Messages/MultiResponseMessages.svelte:59-60,src/lib/components/chat/Messages/MultiResponseMessages.svelte:157-197
---
初始化与消息分组
初始化流程
标题:多模型初始化序列
sequenceDiagram
participant Mount as "onMount"
participant Init as "initHandler"
participant History as "history.messages"
participant Groups as "groupedMessageIds"
Mount->>Init: 触发初始化
Init->>History: 获取 parentMessage
Init->>History: 读取 parentMessage.models
loop 每个模型
Init->>History: 查找匹配 modelIdx 的 childrenIds
Init->>Groups: 按 modelIdx 存储 messageIds
end
Init->>Groups: 计算每个模型的当前索引
Init->>Init: 设置 selectedModelIdx
来源:src/lib/components/chat/Messages/MultiResponseMessages.svelte:148-204,src/lib/components/chat/Messages/MultiResponseMessages.svelte:233-235
initHandler 函数执行按模型分组消息的关键任务:
groupedMessageIds = parentMessage?.models.reduce((a, model, modelIdx) => {
// 查找所有属于父消息子节点且具有相同 modelIdx 的消息
let modelMessageIds = parentMessage?.childrenIds
.map((id) => history.messages[id])
.filter((m) => m?.modelIdx === modelIdx)
.map((m) => m.id);
// 对没有 modelIdx 的消息提供向后兼容支持
if (modelMessageIds.length === 0) {
let modelMessages = parentMessage?.childrenIds
.map((id) => history.messages[id])
.filter((m) => m?.model === model);
modelMessages.forEach((m) => {
m.modelIdx = modelIdx;
});
modelMessageIds = modelMessages.map((m) => m.id);
}
return {
...a,
[modelIdx]: { messageIds: modelMessageIds }
};
}, {});
来源:src/lib/components/chat/Messages/MultiResponseMessages.svelte:157-182
---
展示模式
系统支持两种展示模式,由 $settings?.displayMultiModelResponsesInTabs 设置控制。
标签页展示
当 displayMultiModelResponsesInTabs 为 true 时,响应以标签页界面展示。仅渲染所选模型的响应,在选中多个模型时有助于提升性能。
来源:src/lib/components/chat/Messages/MultiResponseMessages.svelte:253-326
卡片展示
当 displayMultiModelResponsesInTabs 为 false 时,响应以并排卡片形式展示。选中的卡片使用实线边框(border-2),未选中的使用虚线边框。容器使用 CSS 滚动吸附(scroll snap),在移动设备上实现流畅导航。
来源:src/lib/components/chat/Messages/MultiResponseMessages.svelte:329-385
---
响应导航
用户可以使用上一步/下一步控件浏览每个模型的多个响应版本。
导航逻辑
导航函数实现了边界限制以防止越界访问,并向下钻取到所选分支的最深层子节点:
const showPreviousMessage = async (modelIdx) => {
groupedMessageIdsIdx[modelIdx] = Math.max(0, groupedMessageIdsIdx[modelIdx] - 1);
let messageId = groupedMessageIds[modelIdx].messageIds[groupedMessageIdsIdx[modelIdx]];
let messageChildrenIds = history.messages[messageId].childrenIds;
while (messageChildrenIds.length !== 0) {
messageId = messageChildrenIds.at(-1);
messageChildrenIds = history.messages[messageId].childrenIds;
}
history.currentId = messageId;
await tick();
await updateChat();
triggerScroll();
};
来源:src/lib/components/chat/Messages/MultiResponseMessages.svelte:105-123
---
响应合并(混合代理)
系统提供了“合并响应”功能,使用混合代理(Mixture of Agents,MoA)技术将多个模型输出合并为单个统一响应。
合并处理器
合并处理器收集每个模型当前显示的响应,并触发 MoA 补全:
const mergeResponsesHandler = async () => {
const responses = Object.keys(groupedMessageIds).map((modelIdx) => {
const { messageIds } = groupedMessageIds[modelIdx];
const messageId = messageIds[groupedMessageIdsIdx[modelIdx]];
return history.messages[messageId].content;
});
mergeResponses(messageId, responses, chatId);
};
来源:src/lib/components/chat/Messages/MultiResponseMessages.svelte:223-231
合并后的响应在通过 generateMoACompletion 触发生成后,显示在各个模型响应下方。
来源:src/lib/components/chat/Chat.svelte:87,src/lib/components/chat/Messages/MultiResponseMessages.svelte:397-421
---
集成总结
MultiResponseMessages 组件充当并行消息分支的高级协调器:
- 状态同步:使用
history.currentId在模型选择过程中保持全局聊天状态与用户焦点同步。 - 性能:使用
{#key messageId}和{#key history.currentId}确保ResponseMessage等组件在导航过程中正确重新初始化。 - 响应式:根据
$mobile存储适配布局,在卡片展示中切换min-w-full和min-w-80。
来源:src/lib/components/chat/Messages/MultiResponseMessages.svelte:104-146,src/lib/components/chat/Messages/MultiResponseMessages.svelte:339-340