实现用户输入打断大模型流式输出:基于Vue与FastAPI的方案
在大模型交互场景中,流式输出能显著提升用户体验,但当用户进行新输入时,需要立即中断当前输出。本文将详细介绍两种实现方案(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