🌳多平台技术论坛专家博主,全网11W+粉丝

✈️公众号 | 乡下小哥编程 。回复 Java全套视频教程 或 前端全套视频教程 即可获取 300G+ 教程资料及项目实战案例 

⭐职场开发经验干货分享、开源项目源码分享

文末有实现对话效果演示~

前言

       大模型出现很长一段时间了,自己在工作中也已经使用到了。公司中用到的是自己训练的模型,然后我就想能不能在以往的项目中引用大模型接口实现对话呢?在本地跑模型需要的配置挺高的,用在线接口的形式实现是简单方便的。在最近开发的一个项目微信小程序书籍借阅系统添加一个AI自动对话功能,也算是一个亮点。其它的项目也可以借阅相同的实现来集成这个AI自动对话功能~

具体实现过程

1、申请大模型使用

我这里使用的是讯飞大模型,新用户可以免费申请200万Token。自动注册即可,注册过程省略,根据官网一步一步注册即可。

https://xinghuo.xfyun.cn/sparkapi?scr=price

2、开发文档地址

官方文档说明,可以参考文档来具体实现。里边有不同语言的调用实例,比如:JS、Java、Python、小程序等。

https://www.xfyun.cn/doc/spark/Web.html#_1-接口说明

3、项目集成

3.1 基本说明

      说明:这里和大模型通信是通过Websocket的方式来实现,主要是为了数据的实时返回、持续对话、双向通信等能力。几个重要过程:1、鉴权;2、构造请求报文;3、报文发送、响应处理。

3.2 pom文件引入依赖

   由于需要使用到websocket和构造json格式的报文体,所以引入如下两个依赖


<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.11.0</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.60</version>
</dependency>

3.3 配置文件

  这里的配置在登录讯飞平台后,可以看到。将相关配置写入即可、确保参数写对。

3.5 核心代码部分(报文发送及响应处理)

注意:由于源码长度较长,篇幅有限。这里只给出部分核心源码,完整的前后端项目代码已经上传到Github 、可自行查看。后端核心部分处理,和大模型交互的处理。

    public String answer(String text, String uid) {
        try {
            String authUrl = getAuthUrl();
            if (authUrl == null || authUrl.isEmpty()) {
                logger.error("authUrl为空,无法建立WebSocket连接");
                return "出错啦,请稍后再试...";
            }

            authUrl = authUrl.replace("http://", "ws://").replace("https://", "wss://");
            logger.info("最终 WebSocket URL: {}", authUrl);

            Request request = new Request.Builder().url(authUrl).build();
            OkHttpClient client = new OkHttpClient.Builder().build();
            StringBuilder sb = new StringBuilder();
            CompletableFuture<String> messageReceived = new CompletableFuture<>();
            String body = buildBody(text, uid);
            logger.info("发送消息体: {}", body);

            WebSocket webSocket = client.newWebSocket(request, new WebSocketListener() {
                @Override
                public void onOpen(WebSocket webSocket, Response response) {
                    logger.info("WebSocket 已连接");
                    webSocket.send(body);
                }

                @Override
                public void onMessage(WebSocket webSocket, String msg) {
                    logger.info("收到原始消息: {}", msg);
                    try {
                        JSONObject obj = JSON.parseObject(msg);
                        JSONObject header = obj.getJSONObject("header");

                        // 服务端返回错误处理
                        if (header.getInteger("code") != 0) {
                            logger.error("服务端返回错误: {}", obj.toJSONString());
                            messageReceived.completeExceptionally(
                                    new RuntimeException(header.getString("message"))
                            );
                            webSocket.close(1001, "Error");
                            return;
                        }

                        // 处理 payload 内容
                        JSONObject payload = obj.getJSONObject("payload");
                        if (payload != null) {
                            JSONObject choices = payload.getJSONObject("choices");
                            if (choices != null && choices.getJSONArray("text") != null
                                    && !choices.getJSONArray("text").isEmpty()) {

                                // 遍历每条 text
                                for (int i = 0; i < choices.getJSONArray("text").size(); i++) {
                                    JSONObject t = choices.getJSONArray("text").getJSONObject(i);

                                    // 优先拼接 content
                                    String content = t.getString("content");
                                    if (content != null && !content.trim().isEmpty()) {
                                        sb.append(content);
                                    }

                                    // 如果想保留 reasoning_content 可另存,不拼接到 sb
                                    // String reasoning = t.getString("reasoning_content");
                                }
                            }
                        }

                        // status==2 表示完整返回,结束 WebSocket
                        if (header.getLong("status") != null && header.getLong("status") == 2) {
                            webSocket.close(1000, "Closing WebSocket connection");
                            messageReceived.complete(sb.toString()); // 返回拼接后的完整文本
                        }
                    } catch (Exception e) {
                        logger.error("解析返回消息异常: {}", e.getMessage(), e);
                        messageReceived.completeExceptionally(e);
                        try {
                            webSocket.close(1011, "Exception");
                        } catch (Exception ex) {
                            logger.warn("关闭 WebSocket 异常: {}", ex.getMessage(), ex);
                        }
                    }
                }

                @Override
                public void onFailure(WebSocket webSocket, Throwable t, Response response) {
                    logger.error("WebSocket连接失败", t);
                    if (response != null) {
                        logger.error("失败响应: {}", response);
                    }
                    messageReceived.completeExceptionally(t);
                }

                @Override
                public void onClosed(WebSocket webSocket, int code, String reason) {
                    logger.info("WebSocket已关闭: {} - {}", code, reason);
                }

                @Override
                public void onClosing(WebSocket webSocket, int code, String reason) {
                    logger.info("WebSocket正在关闭: {} - {}", code, reason);
                }
            });

            // 阻塞等待消息返回,带超时
            messageReceived.get(30, TimeUnit.SECONDS);
            webSocket.close(1000, "Closing WebSocket connection");
            return sb.toString();
        } catch (Exception e) {
            logger.error("错误信息:", e);
            return "出错啦,请稍后再试...";
        }
    }

前端小程序和后端接口交互

    这里可以自己根据前端页面的输入处理,后端接口返回的数据,不同处理实现来取值。这里只是一个简单的演示~ 对应的接口也可以封装处理。

  sendMessage() {
    const text = this.data.inputValue.trim();
    if (!text) return;

    // 添加用户消息
    let newMessages = [...this.data.messages, {
      role: 'user',
      text
    }];
    this.setData({
      messages: newMessages,
      inputValue: '',
      toView: 'msg' + (newMessages.length - 1)
    });

    // 构造 CognitiveMode 需要的 JSON
    const requestData = {
      message: {
        text: [{
          role: 'user',
          content: text
        }]
      }
    };

    wx.request({
      url: 'http://localhost:8282/ai/ask', // 你的 Spring Boot 接口
      method: 'POST',
      data: {
        text: JSON.stringify(requestData),
        uid: 'wx-user-001'
      },
      header: {
        'Content-Type': 'application/json'
      },
      success: (res) => {
        try {
          console.error("对话返回数据:", res)
          // 如果 res.data 是字符串,先转对象
          let data = res.data;
          if (typeof data === "string") {
            try {
              data = JSON.parse(data);
            } catch (e) {
              console.error("返回数据不是 JSON 格式:", data);
              return;
            }
          }

          // 健壮取值
          const text = data?.data?.returnText ?? "抱歉,未获取到回复";
          console.error("对话text返回数据:", text)
          const reply = text || '抱歉,我暂时无法回答';
          const newMessages = [
            ...this.data.messages,
            {
              role: 'ai',
              text: reply
            }
          ];

          this.setData({
            messages: newMessages,
            toView: 'msg' + (newMessages.length - 1)
          });
        } catch (err) {
          console.error("success 回调异常:", err);
        }
      },

      fail: (res) => {
        console.error("对话异常:", res)
        newMessages = [...this.data.messages, {
          role: 'ai',
          text: '网络错误,请稍后再试'
        }];
        this.setData({
          messages: newMessages,
          toView: 'msg' + (newMessages.length - 1)
        });
      }
    });
  },

3.6 实现效果

     这里用户在下方输入内容,然后调用后台接口,将数据发送到后端,后端接口处理和大模型的交互。然后将大模型处理的结果,返回给前端。前端再将结果渲染到页面。

这里有一些和大模型交互的信息,可以看到相关交互信息。

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐