简单实现一个DEEPSEEK前端+后端

1、效果展示

项目链接:beite-ai-ui

2、运行环境

text 复制代码
版本介绍
vue:3.5.13
node:18.20.7

运行:
npm i
npm run dev
http://localhost:5173/stream

3、遇到的问题

  • 处理deepseek返回的数据直接展示格式的问题

在deepseek返回的数据消息看到,数据是一个md形式的返回,以及代码高亮,因为我也不是专业的前端,参考了若依大神开发的项目,项目地址:ruoyi-vue-pro

  • 流式返回对话不显示问题

因为是使用自己后端跟deepseek对接的,一开始我是直接前端直接访问,是可以的,后面切换成访问自己的后端,发现不行,那我就观察了一下deepseek是怎么返回的,发现我的流返回的不对,折腾了一天,后面去访问各大大神,最终解决,这里附上代码 原来的代码,如果有大神知道是哪里的问题,望提出交流交流

java 复制代码
@PostMapping(value = "/deep_seek/chat_stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter deepSeekChatSteam(@Validated @RequestBody DeepSeekQueryDTO deepSeekQueryDTO) {
    DeepSeekModelDTO modelDTO = deepSeekModelConverter.queryDtoToDTO(deepSeekQueryDTO);

    modelDTO.setTemperature(0.7);
    modelDTO.setMaxTokens(2000);
    modelDTO.setTopP(1.0);
    modelDTO.setStream(Objects.nonNull(deepSeekQueryDTO.getStream()) ? deepSeekQueryDTO.getStream() : Boolean.FALSE);

    // 流式返回
    SseEmitter emitter = new SseEmitter();

    // 调用 Service 层的 streamChat 方法
    objectProvider.stream().forEach(item -> {
        if (item.support(AiModelOptions.DEEP_SEEK)) {
            ExecutorService executor = Executors.newSingleThreadExecutor();
            executor.execute(() -> {
                item.streamChat(modelDTO, new EventSourceListener() {
                    @Override
                    public void onEvent(@NotNull EventSource eventSource, String id, String type, @NotNull String data) {
                        System.out.println(JSON.isValid(data));
                        System.out.println(data);
                        try {
                            emitter.send(SseEmitter.event()
                                    .name("message") // 事件名称(前端可通过 addEventListener 监听)
                                    .data(data));
                        } catch (IOException e) {
                            emitter.completeWithError(e);
                        } finally {
                            emitter.complete();
                            executor.shutdown();
                        }
                    }

                    @Override
                    public void onClosed(@NotNull EventSource eventSource) {
                        emitter.complete();
                    }

                    @Override
                    public void onFailure(@NotNull EventSource eventSource, Throwable t, okhttp3.Response response) {
                        emitter.completeWithError(t); // 处理失败
                    }
                });
            });

        }
    });

    return emitter;
}

后来改成这个可以了:

java 复制代码
@PostMapping(value = "/deep_seek/chat_stream")
public void deepSeekChatStream(
        @Validated @RequestBody DeepSeekQueryDTO deepSeekQueryDTO,
        HttpServletResponse httpServletResponse) throws IOException, InterruptedException {

    httpServletResponse.setContentType("text/event-stream");
    httpServletResponse.setCharacterEncoding("utf-8");

    DeepSeekModelDTO modelDTO = deepSeekModelConverter.queryDtoToDTO(deepSeekQueryDTO);
    modelDTO.setTemperature(0.7);
    modelDTO.setMaxTokens(2000);
    modelDTO.setTopP(1.0);
    modelDTO.setStream(Objects.nonNull(deepSeekQueryDTO.getStream()) ? deepSeekQueryDTO.getStream() : Boolean.FALSE);

    // 使用 CountDownLatch 同步流式输出完成
    CountDownLatch completionLatch = new CountDownLatch(1);
    PrintWriter pw = httpServletResponse.getWriter(); // 提前获取 Writer

    objectProvider.stream().forEach(item -> {
        if (item.support(AiModelOptions.DEEP_SEEK)) {
            item.streamChat(modelDTO, new EventSourceListener() {
                @Override
                public void onEvent(
                        @NotNull EventSource eventSource,
                        String id,
                        String type,
                        @NotNull String data) {
                    String sendData = "data: " + data + "\n\n";
                    pw.println(sendData);
                    pw.flush();
                }

                @Override
                public void onClosed(@NotNull EventSource eventSource) {
                    completionLatch.countDown(); // 流关闭时释放锁
                }

                @Override
                public void onFailure(
                        @NotNull EventSource eventSource,
                        Throwable t,
                        okhttp3.Response response) {
                    completionLatch.countDown(); // 出错时也释放锁
                    // 可选:记录错误或发送错误信息到客户端
                    try {
                        pw.println("data: [ERROR] " + t.getMessage() + "\n\n");
                        pw.flush();
                    } catch (Exception e) {
                        // 处理异常
                    }
                }
            });
        }
    });

    // 等待流完成(超时时间根据业务需求设置)
    completionLatch.await(1000, TimeUnit.SECONDS);
}
相关推荐
coderSong25682 小时前
Java高级 |【实验八】springboot 使用Websocket
java·spring boot·后端·websocket
Mr_Air_Boy3 小时前
SpringBoot使用dynamic配置多数据源时使用@Transactional事务在非primary的数据源上遇到的问题
java·spring boot·后端
豆沙沙包?3 小时前
2025年- H77-Lc185--45.跳跃游戏II(贪心)--Java版
java·开发语言·游戏
年老体衰按不动键盘4 小时前
快速部署和启动Vue3项目
java·javascript·vue
咖啡啡不加糖4 小时前
Redis大key产生、排查与优化实践
java·数据库·redis·后端·缓存
liuyang-neu4 小时前
java内存模型JMM
java·开发语言
UFIT4 小时前
NoSQL之redis哨兵
java·前端·算法
刘 大 望4 小时前
数据库-联合查询(内连接外连接),子查询,合并查询
java·数据库·sql·mysql
怀旧,5 小时前
【数据结构】6. 时间与空间复杂度
java·数据结构·算法
大春儿的试验田5 小时前
Parameter ‘XXX‘ not found. Available parameters are [list, param1]
java