大模型流式调用规范(SSE)

随着大语言模型的广泛应用,如何高效地与其进行接口调用成为一个关键问题。传统的请求-响应模式在面对大模型生成大量文本时存在响应延迟高、用户体验差等问题。流式输出(Streaming)是解决该问题的重要手段。

本文将介绍基于 Server-Sent Events(SSE) 协议的大模型流式调用规范,并结合 Spring Boot 给出一个完整的服务端与客户端调用示例。


一、为什么选择SSE?

在与大模型进行对话时,模型通常会逐字逐句生成内容。如果采用传统的HTTP请求,需要等到模型生成完所有内容再响应给客户端,导致延迟较高。而使用SSE协议,可以做到边生成边推送,极大提升交互性和用户体验。

SSE的优势:

  • 单向连接:服务端主动推送,客户端自动接收;

  • 使用HTTP协议,浏览器原生支持;

  • 实现简单,适用于流式文本输出场景。


二、流式调用接口规范(基于SSE)

请求方式

  • 方法:POST

  • Content-Type:application/json

  • Accept:text/event-stream

请求示例

?
1
2
3
4
5
POST /chat/stream
{
   "prompt" : "你好,请介绍一下三国演义。" ,
   "stream" : true
}

  

响应格式(SSE流)

?
1
2
3
data: { "id" : "msg_001" , "content" : "三国演义是中国古代四大名著之一..." }
data: { "id" : "msg_001" , "content" : "它讲述了东汉末年群雄割据的故事..." }
data: [DONE]
  • 每行以 data: 开头,后接JSON字符串;

  • 最后一行以 data: [DONE] 表示流结束;

  • 客户端需要实时解析接收到的 content 字段并展示。


三、Spring Boot服务端示例

下面是一个基于Spring Boot的SSE流式接口实现示例。

1. Controller层

复制代码
@RestController
@RequestMapping("/chat")
public class ChatController {

    @PostMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter streamChat(@RequestBody ChatRequest request) {
        SseEmitter emitter = new SseEmitter(0L); // 不设置超时时间

        CompletableFuture.runAsync(() -> {
            try {
                // 模拟逐句生成的响应
                List<String> responses = mockModelResponse(request.getPrompt());

                for (String sentence : responses) {
                    Map<String, String> data = new HashMap<>();
                    data.put("id", "msg_001");
                    data.put("content", sentence);

                    emitter.send(SseEmitter.event()
                            .data(new ObjectMapper().writeValueAsString(data)));

                    Thread.sleep(500); // 模拟生成延迟
                }

                emitter.send("data: [DONE]\n\n");
                emitter.complete();
            } catch (Exception e) {
                emitter.completeWithError(e);
            }
        });

        return emitter;
    }

    private List<String> mockModelResponse(String prompt) {
        return List.of(
                "三国演义是中国古代四大名著之一。",
                "它讲述了东汉末年群雄割据的故事。",
                "主要人物包括刘备、关羽、张飞、曹操、孙权等。"
        );
    }
}
复制代码

 


2. 请求类定义

?
1
2
3
4
5
6
public class ChatRequest {
     private String prompt;
     private boolean stream;
 
     // getters and setters
}

  


四、客户端调用示例(Java)

使用Spring WebFlux进行客户端流式接收:

?
1
2
3
4
5
6
7
8
9
10
WebClient client = WebClient.create();
 
client.post()
     .uri( "http://localhost:8080/chat/stream" )
     .header(HttpHeaders.ACCEPT, MediaType.TEXT_EVENT_STREAM_VALUE)
     .bodyValue(Map.of( "prompt" , "介绍一下三国演义" , "stream" , true ))
     .retrieve()
     .bodyToFlux(String. class )
     .doOnNext(System. out ::println)
     .blockLast();

  

 


五、总结与建议

基于SSE的大模型流式调用,能显著提升响应速度与用户体验。在使用时需要注意:

  • SSE适合文字类输出,若涉及音频/图片等内容建议使用WebSocket;

  • 服务端处理时需考虑异常与资源释放;

  • 客户端需具备实时处理与拼接能力。

原创作者: shuchen007 转载于: https://www.cnblogs.com/shuchen007/p/18829409
<think>好的,我现在需要回答用户关于如何在Spring Boot中使用WebClient调用大模型流式接口的问题。首先,我需要回顾一下用户提供的引用内容,特别是引用[3],里面提到了WebFlux和流式调用大模型的方法。用户希望得到一个示例,所以重点应该放在代码实现上。 首先,我应该确认WebClient的基本用法。WebClient是Spring WebFlux的一部分,用于非阻塞的HTTP请求。流式调用通常涉及处理服务器发送的事件(SSE)或分块传输的数据。大模型流式响应可能以SSE形式返回,每个数据块逐步传输。 接下来,我需要考虑如何配置WebClient实例。根据引用[3],需要创建带有响应式支持的WebClient,设置baseUrl,并配置默认的header,比如Content-Type和Authorization,特别是如果大模型API需要认证的话。 然后,处理流式响应。使用WebClient的post方法发送请求,body可能是一个Mono,比如使用BodyInserters.fromValue发送JSON数据。响应应该通过retrieve()获取,然后作为ServerSentEvent的流进行处理。这里需要注意,每个事件的数据可能需要解析,例如JSON对象。 另外,用户可能需要处理每个数据块并最终汇总结果。例如,使用StringBuilder来累积响应内容,并在订阅时触发请求。同时,需要确保主线程不会提前退出,所以可能需要阻塞或等待异步操作完成,这在测试或命令行应用中常见。 还要考虑异常处理,比如在doOnError中记录错误,以及在onComplete中处理完成后的逻辑。此外,引用[3]提到WebFlux的非阻塞特性,确保流式处理不会阻塞主线程,这一点需要在示例中体现出来。 最后,生成相关问题,帮助用户进一步探索,比如如何自定义请求头、处理错误、优化性能等。需要确保所有步骤符合用户的要求,代码语法正确,并且使用中文回答,行内公式用$...$,独立公式用$$...$$,但当前问题不涉及数学公式,所以可能不需要使用。</think>以下是如何在Spring Boot中使用WebClient调用大模型流式接口的详细实现步骤和示例: ### 1. 配置WebClient实例 ```java @Bean public WebClient webClient() { return WebClient.builder() .baseUrl("https://api.largemodel.com/v1/stream") .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .defaultHeader("Authorization", "Bearer your-api-key") .build(); } ``` *注:需在`application.properties`中配置baseUrl和API密钥更安全* ### 2. 流式响应处理方法 ```java public void streamModelResponse(String prompt) { webClient.post() .uri("/chat/completions") .body(BodyInserters.fromValue(Map.of( "prompt", prompt, "temperature", 0.7, "stream", true ))) .retrieve() .bodyToFlux(ServerSentEvent.class) .timeout(Duration.ofSeconds(30)) .doOnError(WebClientResponseException.class, ex -> log.error("API响应错误: {} {}", ex.getRawStatusCode(), ex.getResponseBodyAsString())) .subscribe( event -> { String data = event.data(); if (!"[DONE]".equals(data)) { JsonNode node = new ObjectMapper().readTree(data); System.out.print(node.get("choices").get(0).get("text").asText()); } }, error -> System.err.println("错误: " + error.getMessage()), () -> System.out.println("\n\n流式传输完成") ); } ``` ### 3. 完整控制器示例 ```java @RestController @RequiredArgsConstructor public class StreamController { private final WebClient webClient; private final StringBuilder fullResponse = new StringBuilder(); @GetMapping("/stream-chat") public Flux<String> streamChat(@RequestParam String message) { return webClient.post() .bodyValue(createRequest(message)) .retrieve() .bodyToFlux(DataBuffer.class) .map(dataBuffer -> { String chunk = DataBufferUtils.release(dataBuffer).toString(StandardCharsets.UTF_8); fullResponse.append(chunk); return chunk; }); } private Map<String, Object> createRequest(String message) { return Map.of( "messages", List.of(Map.of("role", "user", "content", message)), "stream", true, "max_tokens", 2000 ); } } ``` ### 关键点说明: 1. **响应式背压控制**:通过`Flux`实现按需处理数据流,自动处理背压[^3] 2. **超时配置**:使用`.timeout()`防止长时间无响应 3. **SSE事件处理**:支持标准的Server-Sent Events格式 4. **数据完整性**:使用StringBuilder累积完整响应 5. **错误处理**:区分网络错误和API业务错误
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值