在大模型交互场景中,流式输出能显著提升用户体验,但当用户进行新输入时,需要立即中断当前输出。本文将详细介绍两种实现方案(SSE协议与Fetch API),基于Vue前端和FastAPI后端,实现"用户输入打断大模型流式输出"的完整功能。
技术原理概述
大模型的流式输出本质是服务器采用分块传输(Chunked Transfer Encoding) 逐步返回数据。中断机制的核心是:
- 前端监测到用户新输入时,立即终止当前连接/请求
- 后端检测到连接中断后,停止模型生成并释放资源
- 建立新连接处理新输入
两种主流实现方式各有优势:
- SSE(Server-Sent Events):基于HTTP长连接的服务器推送协议,原生支持流式传输
- Fetch API:现代AJAX方案,通过
ReadableStream
处理分块响应,中断机制更灵活
前端实现(Vue)
方案一:基于SSE的实现
SSE通过EventSource
对象实现,适合简单的单向流式传输场景:
<template>
<div class="chat-container">
<div class="message-list" v-html="messageContent"></div>
<div class="input-area">
<input
v-model="userInput"
@input="handleUserInput"
placeholder="输入内容..."
>
<button @click="sendMessage">发送</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
userInput: '',
messageContent: '',
eventSource: null, // SSE连接实例
debounceTimer: null
}
},
beforeUnmount() {
this.closeSSEConnection();
},
methods: {
handleUserInput() {
if (this.debounceTimer) clearTimeout(this.debounceTimer);
this.closeSSEConnection(); // 中断当前连接
this.debounceTimer = setTimeout(() => {
if (this.userInput.trim()) this.sendMessage();
}, 300);
},
sendMessage() {
const query = encodeURIComponent(this.userInput.trim());
this.messageContent = '思考中...';
// 建立SSE连接
this.eventSource = new EventSource(`/api/sse-stream?query=${query}`);
this.eventSource.onmessage = (event) => {
this.messageContent = event.data; // 实时更新内容
};
this.eventSource.onerror = () => this.closeSSEConnection();
},
closeSSEConnection() {
if (this.eventSource) {
this.eventSource.close(); // 中断SSE连接
this.eventSource = null;
}
}
}
};
</script>
方案二:基于Fetch API的实现
Fetch通过ReadableStream
处理流式响应,中断机制更灵活,适合复杂交互场景:
<template>
<div class="chat-container">
<div class="message-list" v-html="messageContent"></div>
<div class="input-area">
<input
v-model="userInput"
@input="handleUserInput"
placeholder="输入内容..."
>
<button @click="sendMessage">发送</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
userInput: '',
messageContent: '',
abortController: null, // Fetch中断控制器
debounceTimer: null,
reader: null // 流读取器
}
},
beforeUnmount() {
this.abortCurrentRequest();
},
methods: {
handleUserInput() {
if (this.debounceTimer) clearTimeout(this.debounceTimer);
this.abortCurrentRequest(); // 中断当前请求
this.debounceTimer = setTimeout(() => {
if (this.userInput.trim()) this.sendMessage();
}, 300);
},
async sendMessage() {
const query = encodeURIComponent(this.userInput.trim());
this.messageContent = '思考中...';
// 创建新的中断控制器
this.abortController = new AbortController();
const { signal } = this.abortController;
try {
const response = await fetch(`/api/fetch-stream?query=${query}`, { sig