简单实现一个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);
}
相关推荐
Aric_Jones36 分钟前
lua入门语法,包含安装,注释,变量,循环等
java·开发语言·git·elasticsearch·junit·lua
Akiiiira37 分钟前
【日撸 Java 三百行】Day 12(顺序表(二))
java·开发语言
Chase_Mos5 小时前
Spring 必会之微服务篇(1)
java·spring·微服务
a濯7 小时前
element plus el-table多选框跨页多选保留
javascript·vue.js
小林学习编程7 小时前
SpringBoot校园失物招领信息平台
java·spring boot·后端
撸码到无法自拔7 小时前
docker常见命令
java·spring cloud·docker·容器·eureka
heart000_17 小时前
IDEA 插件推荐:提升编程效率
java·ide·intellij-idea
ŧ榕树先生8 小时前
查看jdk是否安装并且配置成功?(Android studio安装前的准备)
java·jdk
未来的JAVA高级开发工程师8 小时前
适配器模式
java
九月TTS8 小时前
开源分享:TTS-Web-Vue系列:Vue3实现固定顶部与吸顶模式组件
前端·vue.js·开源