本文将介绍如何在使用 Semantic Kernel 框架的 ASP.NET 项目中使用流式输出 SSE(Server-Sent Events),并展示如何在Vue3前端应用中接收这些数据。并介绍了如何使用
@microsoft/fetch-event-source
库使用 POST 方法来接收 SSE 数据。
1. 背景
在大模型的应用场景中,用户经常需要与模型进行实时交互,例如,生成文本、回答问题等。这些场景要求数据传输能够快速且连续,以提供流畅的用户体验。SSE作为一种基于HTTP的标准协议,允许服务器向客户端推送实时数据,非常适合于此类应用。
2. 什么是 SSE
SSE 并不是一种新的技术,但是随着 ChatGPT 的火热,这种技术又重新受到了关注。
SSE(Server-Sent Events)是一种基于 HTTP 的服务器推送技术,允许服务器实时向客户端推送数据。与 WebSocket 不同,SSE 是单向通信,只能由服务器向客户端推送数据,而客户端无法向服务器发送数据。SSE 使用简单,易于实现,适用于需要实时数据推送的场景。
SSE 的工作原理是,客户端通过 EventSource 对象与服务器建立连接,服务器端通过发送特定格式的数据(如 data: Hello, world!\n\n
)来推送消息给客户端。客户端收到消息后,可以通过监听 message 事件来处理数据。
3. 在 Semantic Kernel 中使用 SSE
如果要使用 SSE,首先需要在 ASP.NET 项目正确的引入 Semantic Kernel,并在控制器中添加 SSE 的处理逻辑。
3.1. 引入 Semantic Kernel
以下是 Program.cs
中引入 Semantic Kernel 的相关代码,这里以 Azure OpenAI 做演示:
using Microsoft.SemanticKernel;
var builder = WebApplication.CreateBuilder(args);
// ··· 略去其他代码
// 添加语义内核
builder.Services.AddKernel();
builder.Services.AddOpenAIChatCompletion("gpt-4o", new OpenAIClient(new Uri("https://[your-gpt].openai.azure.com/"), new Azure.AzureKeyCredential("[your-key]")));
// ··· 略去其他代码
3.2. 控制器中添加 SSE 处理逻辑
在控制器中添加 SSE 处理逻辑,需要使用 Kernel
的 InvokePromptStreamingAsync
或 InvokeStreamingAsync
方法来获取模型的流式结果输出,并将输出推送给客户端。
示例代码如下:
using Sang.AspNetCore.CommonLibraries.Models;
[HttpPost("test")]
[Produces("text/event-stream")]
public async Task<IResult> SSETest()
{
var content = _kernel.InvokePromptStreamingAsync("什么是快乐星球?");
Response.Headers.ContentType = "text/event-stream";
Response.Headers.CacheControl = "no-cache";
await Response.Body.FlushAsync();
if (content is )
{
var error = JsonSerializer.Serialize(MessageModel<string>.Fail("生成失败"), _jsonSerializerOptions);
await Response.WriteAsync($"data: {error}\n\n");
await Response.Body.FlushAsync();
}
else
{
await foreach (var item in content)
{
await Response.WriteAsync($"data: {MessageModel<string>.Ok(item.ToString())}\n\n");
await Response.Body.FlushAsync();
}
}
// 结束标记
await Response.WriteAsync($"data: [DONE]\n\n");
await Response.Body.FlushAsync();
return Results.Empty;
}
在上面额代码中,我们使用 InvokePromptStreamingAsync
方法获取模型的流式输出,并通过 Response.WriteAsync
方法将输出推送给客户端。每个完整数据包后跟随两个换行符(\n\n
),这是SSE协议的要求。在客户端接收到 [DONE]
标记后,表示数据传输结束。这里的 MessageModel
是一个自定义的消息模型,可以安装 Sang.AspNetCore.CommonLibraries
包来使用,每个数据包(message)都是一个完整的 json
数据,方便解析。下图是测试结果:
4. 前端接收
在Vue3应用中,我们可以使用 EventSource
接口或者第三方库来接收SSE数据。对于原生使用和 EventSource
的更多信息,请参考 MDN 文档[1]。
这里,我们将使用 @microsoft/fetch-event-source
库来演示如何接收服务器发送的数据。
首先,安装 @microsoft/fetch-event-source
库:
npm install @microsoft/fetch-event-source
然后,在Vue组件中,我们可以这样接收数据:
import { ref } from 'vue';
import { fetchEventSource } from '@microsoft/fetch-event-source';
const dataStream = ref('');
const fetchDataStream = () => {
fetchEventSource('/test', {
method: 'POST',
headers: {
"Content-Type": 'application/json',
},
body: JSON.stringify({ /* 请求体 */ }),
onmessage(event) {
if (event.data === '[DONE]') {
console.log('Stream ended');
return;
}
let data = JSON.parse(event.data);
dataStream.value += data.data;
// 更新UI等操作
},
onerror(error) {
console.error('Stream encountered error:', error);
}
});
};
在上面的代码中,我们使用fetchEventSource
方法订阅了服务器端的SSE。每当服务器发送新的数据时,onmessage
回调就会被触发,我们可以在这里更新Vue组件的状态,以实现数据的实时展示。
注意,这里演示使用的是 POST 请求,在这个案例中直接将服务端和接收端使用 GET 请求也是可以的。但是默认浏览器 EventSource API
对允许发出的请求类型施加了一些限制作,其中就包括不允许使用 POST
请求。因此,如果需要使用 POST
请求,可以使用 @microsoft/fetch-event-source
库。具体的更多信息可以参考 GitHub 仓库 Azure/fetch-event-source[2]。
5. 结语
SSE提供了一种高效、简单的方法,允许服务器向客户端推送实时数据。通过结合Semantic Kernel和Vue3,我们可以构建出能够实时响应大模型推理结果的Web应用,从而提供更加流畅和动态的用户体验。希望本文所介绍的内容能够帮助到你,欢迎留言讨论。
References
[1]
MDN 文档: https://developer.mozilla.org/zh-CN/docs/Web/API/Server-sent_events/Using_server-sent_events[2]
GitHub 仓库 Azure/fetch-event-source: https://github.com/Azure/fetch-event-source?wt.mc_id=DT-MVP-5005195