简单实现一个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);
}
相关推荐
二两小咸鱼儿1 小时前
Java Demo - JUnit :Unit Test(Assert Methods)
java·后端·junit
字节源流1 小时前
【spring】配置类和整合Junit
java·后端·spring
跪在镜子前喊帅2 小时前
【面试】Java 多线程
java·面试
鱼樱前端2 小时前
Vue3 + TypeScript 整合 MeScroll.js 组件
前端·vue.js
好看资源平台2 小时前
Java/Kotlin逆向基础与Smali语法精解
java·开发语言·kotlin
zimoyin2 小时前
解决 Java/Kotlin 资源加载问题
java·python·kotlin
拉不动的猪2 小时前
刷刷题29
前端·vue.js·面试
武昌库里写JAVA2 小时前
原生iOS集成react-native (react-native 0.65+)
vue.js·spring boot·毕业设计·layui·课程设计
野生的程序媛2 小时前
重生之我在学Vue--第5天 Vue 3 路由管理(Vue Router)
前端·javascript·vue.js
鱼樱前端2 小时前
Vue 2 与 Vue 3 响应式原理详细对比
javascript·vue.js