本文將介紹如何在使用 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 做演示:
usingMicrosoft.SemanticKernel;
var builder = WebApplication.CreateBuilder(args);
// ··· 略去其他程式碼
// 添加語意內核
builder.Services.AddKernel();
builder.Services.AddOpenAIChatCompletion("gpt-4o", newOpenAIClient(newUri("https://[your-gpt].openai.azure.com/"), newAzure.AzureKeyCredential("[your-key]")));
// ··· 略去其他程式碼
3.2. 控制器中添加 SSE 處理邏輯
在控制器中添加 SSE 處理邏輯,需要使用
Kernel
的
InvokePromptStreamingAsync
或
InvokeStreamingAsync
方法來獲取模型的流式結果輸出,並將輸出推播給客戶端。
範例程式碼如下:
usingSang.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 isnull)
{
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();
returnResults.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