API接口模仿AI大模型进行流式返回

背景
   今天接到个很有意思的需求,本来是前端直接对接AI大模型的接口并流式返回,并没有后端什么事。但是需要对请求做一些额外的组装,所以变成了前端去请求后端API接口,后端对接AI大模型,并且流式返回数据给前端。相当于套了一层娃。如下图所示

变成了


技术方案
   一开始折腾了好久,后面终于找到了这个神器。
.SseEmitter
  SseEmitter是 Spring Framework 提供的一个类,用于实现服务器发送事件(Server-Sent Events,简称 SSE)。SSE 是一种基于 HTTP 协议的单向通信机制,允许服务器向客户端推送实时数据,而不需要客户端频繁地轮询服务器
核心特点
   .SSE 是单向的,仅支持服务器向客户端推送数据。 
   .基于 HTTP:使用标准的 HTTP 协议,兼容性好,无需额外协议支持。
   .自动重连:客户端会在连接断开后自动尝试重新连接。
   .轻量级:相比 WebSocket,SSE 更简单,适合单向推送场景。
使用场景
   .实时通知(如消息提醒)。
   .实时数据更新(如股票价格、新闻推送)。
   .流式数据传输(如日志流、聊天应用)

上代码
附上okhttp相关pom.xml,SseEmitter由于是Spring Framework 的,我的是springboot项目,所以不需要再导包了

<dependency>
   <groupId>com.squareup.okhttp3</groupId>
   <artifactId>okhttp</artifactId>
   <version>4.2.0</version>
</dependency>
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp-sse</artifactId>
    <version>4.2.0</version>
</dependency>
@Data
public class GptRequestDTO {


    private String content;

    private String role;

}
 

private SseEmitter callGPT() {

        SseEmitter sseEmitter = new SseEmitter();

        String prompt="你好";

        

        String gptUrl = "你的ai大模型请求地址";

        String token = "你的ai大模型请求秘钥";

		
		//开始组装大模型的请求参数------可按实际情况修改
        HashMap<String,Object> params = new HashMap<>();
        
        params.put("chatId","xnkj");
        params.put("stream",true);
        params.put("detail",false);

        HashMap<String,Object> variables = new HashMap<>();
        variables.put("uid","asdfadsfasfd2323");
        variables.put("detail","张三");
        params.put("variables",variables);
        
        // 这里我使用了实体类,使用其他形式封装都可以,看个人
        List<GptRequestDTO> messages = new ArrayList<>();
        GptRequestDTO message = new GptRequestDTO();
        message.setRole("user");
        message.setContent(prompt);
        messages.add(message);

        params.put("messages",messages);

        // 转换为 JSON 字符串----我使用的是hutool工具
        String jsonParams = JSONUtil.toJsonStr(params);

        // 使用 OkHttpClient 发送请求
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(50, TimeUnit.SECONDS)
                .readTimeout(10, TimeUnit.MINUTES)
                .build();

        Request request = new Request.Builder()
                .url(gptUrl)
                .addHeader("Authorization", "Bearer " + token)
                .addHeader("Content-Type", "application/json")
                .post(okhttp3.RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),
                        jsonParams))
                .build();

        // 发送请求并处理响应
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                log.error("请求失败", e);
                sseEmitter.completeWithError(e);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (!response.isSuccessful()) {
                    log.error("Unexpected response code: " + response.code());
                    sseEmitter.completeWithError(new IOException("Unexpected response code: " + response.code()));
                    return;
                }

                try (BufferedReader reader = new BufferedReader(new InputStreamReader(response.body().byteStream()))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        if (line.trim().isEmpty()) {
                            continue; // 跳过空行
                        }
                        sseEmitter.send(line); // 将每行数据发送到前端
                    }
                } catch (Exception e) {
                    log.error("推送数据失败", e);
                    sseEmitter.completeWithError(e);
                } finally {
                    sseEmitter.complete(); // 完成 SseEmitter
                }
            }
        });

        return sseEmitter; // 返回 SseEmitter 实例
    }

postman请求接口后返回如下图

前端接受数据的时候,也需要流式接收。

至此,收工,有问题欢迎留言沟通交流。

 

### PHP 实现 AI 流式输出 为了实现在 PHP 中的 AI 流式输出,可以利用 `php://output` 数据流来直接向客户端发送数据。这种方式允许服务器端逐步生成并立即传递部分响应给客户端,而不是等待整个响应构建完成后再一次性发送。 #### 使用 `php://output` 发送流式数据 通过打开指向 `php://output` 的句柄,可以直接写入到当前脚本的标准输出缓冲区: ```php <?php // 关闭输出缓存以确保即时传输 while (@ob_end_flush()); $handle = fopen('php://output', 'w'); if ($handle === false) { http_response_code(500); exit; } // 假设这是来自AI模型的数据源模拟函数 function getAiData() { yield "The"; sleep(1); // 模拟延迟 yield " current"; sleep(1); yield " weather"; sleep(1); yield " in"; sleep(1); yield " San"; sleep(1); yield " Francisco"; sleep(1); yield " is"; sleep(1); yield " partly"; sleep(1); yield " cloudy"; // 更多的实际应用中会从API获取动态内容[^4] } foreach (getAiData() as $chunk) { fwrite($handle, $chunk . "\n"); flush(); // 刷新输出缓冲区以便立刻显示新产生的内容 } fclose($handle); ?> ``` 此代码片段展示了如何创建一个持续更新的信息流,模仿了一个可能由远程AI服务提供的天气预报信息。每次迭代都会暂停一秒(`sleep`)以模拟网络请求的时间消耗,在真实场景下这部分应该替换为实际的服务调用逻辑。 对于更复杂的交互模式,比如与 OpenAI API 或其他机器学习平台集成,则需考虑使用类似 cURL 库发起异步 HTTP 请求,并解析返回结果作为输入提供给上述流程中的 `$chunk` 变量[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值