【AI】⭐️搭建一个简单的个人问答网页

目录

🍸前言

🍻一、环境配置

🍹二、具体实现

        2.1 接口改动

        2.2 静态页面

🍸三、测试

        3.1 页面如下

        3.2 问答测试

​🍹 四、章末


🍸前言

        小伙伴们大家好,上次在本地测试了智普大模型提供的免费 api ,并且在控台实现了结果的流式输出,文章链接如下:

【AI】✈️实现一个简单的问答服务-CSDN博客

        但是只看控台还是不太行,费眼,鄙人对 html、css、js 这些也只是略懂一点,但是足以在已有接口的基础上改动下,搭配一个简易的前端页面,实现一个问答服务

🍻一、环境配置

        因为目前后端的接口环境已经有了,所以直接将静态页面跟后端接口放在一起,属于前后端不分离的项目

        后端环境:jdk8、springBoot

        前端使用:html、css、js

        大模型使用的依然是智普,可以看下之前的文章,申请个账号即可,比较简单

🍹二、具体实现

        2.1 接口改动

        接口会返回一个 SseEmitter(Server-Sent Events,服务器推送事件)用于在服务器与客户端之间进行流式数据传输;

        这里使用的 client 实例,可以参考上篇文章,为智普提供的操作api的实例;

        请求进来后,会打印下请求参数,然后新创建一个线程去异步执行,提高响应效率;

    @GetMapping("/testAi2")
    public SseEmitter testAi2(@RequestParam("question") String question) {
        // 创建 SseEmitter 对象,用于流式推送
        SseEmitter emitter = new SseEmitter();
        System.out.println("请求进来了,对应的问题是: " + question);

        // 创建新的线程来处理流式数据
        new Thread(() -> {
            try {
                // 获取流式数据
                List<ChatMessage> messages = new ArrayList<>();
                ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), question);
                messages.add(chatMessage);
                String requestId = UUID.randomUUID().toString();
                ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
                        .model(Constants.ModelChatGLM4)
                        .stream(Boolean.TRUE)
                        .messages(messages)
                        .requestId(requestId)
                        .build();

                ModelApiResponse sseModelApiResp = client.invokeModelApi(chatCompletionRequest);

                if (sseModelApiResp.isSuccess()) {
                    AtomicBoolean isFirst = new AtomicBoolean(true);

                    // 将流数据映射到累加器并推送到前端
                    mapStreamToAccumulator(sseModelApiResp.getFlowable(), chatMessage)
                            .doOnNext(accumulator -> {

                                if (accumulator.getDelta() != null && accumulator.getDelta().getContent() != null) {
                                    // 通过 SSE 推送数据到前端
                                    try {
                                        emitter.send(accumulator.getDelta().getContent(), MediaType.TEXT_PLAIN);
                                    } catch (IOException e) {
                                        System.err.println("发送数据失败: " + e.getMessage());
                                        emitter.completeWithError(e);  // 发生错误时,关闭连接
                                    }
                                }
                            })
                            .doOnComplete(() -> {
                                try {
                                    // 在流完成时,发送 'end' 事件
                                    emitter.send("end", MediaType.TEXT_PLAIN); // 发送 'end' 标志
                                    emitter.complete(); // 完成 SSE 流
                                } catch (IOException e) {
                                    System.err.println("完成流时发送数据失败: " + e.getMessage());
                                    emitter.completeWithError(e);  // 发生错误时,关闭连接
                                }
                            })
                            .blockingLast();
                }
            } catch (Exception e) {
                System.err.println("处理过程中发生错误: " + e.getMessage());
                emitter.completeWithError(e);
            }
        }).start();

        return emitter;
    }
        2.2 静态页面

        css,js 跟 html 代码放到一起的,具体位置要在 resources/static 下面,这样项目启动后可以访问到这些静态资源;代码具体如下:因为对具体的使用不是很清楚,整体的搭建也是在 chatGpt 的帮助下,一步一步调试完善的

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>AI Response</title>
  <style>
    body {
      font-family: 'Arial', sans-serif;
      background-color: #f4f7fc;
      color: #333;
      margin: 0;
      padding: 0;
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
      flex-direction: column;
    }

    h1 {
      font-size: 2.5rem;
      color: #5c6bc0;
      margin-bottom: 20px;
    }

    .container {
      background-color: white;
      border-radius: 8px;
      box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
      padding: 30px;
      width: 100%;
      max-width: 600px;
      text-align: center;
    }

    input[type="text"] {
      width: 80%;
      padding: 12px;
      font-size: 1rem;
      border: 2px solid #ccc;
      border-radius: 4px;
      margin-bottom: 15px;
      transition: border-color 0.3s;
    }

    input[type="text"]:focus {
      border-color: #5c6bc0;
      outline: none;
    }

    button {
      padding: 12px 20px;
      font-size: 1rem;
      color: white;
      background-color: #5c6bc0;
      border: none;
      border-radius: 4px;
      cursor: pointer;
      transition: background-color 0.3s;
    }

    button:hover {
      background-color: #4f5a92;
    }

    #response {
      display: none;
      background-color: #e3f2fd;
      border: 1px solid #90caf9;
      padding: 15px;
      border-radius: 4px;
      margin-top: 20px;
      text-align: left;
      max-height: 400px;
      overflow-y: auto;
      white-space: pre-line; /* 保持换行 */
      font-size: 1rem;
      color: #1e88e5;
    }

    /* 头像样式 */
    #wechat-avatar {
      position: fixed;
      bottom: 20px;
      right: 20px;
      width: 150px;  /* 调整头像大小 */
      height: 150px;
      border-radius: 50%;
      border: 3px solid #fff; /* 给头像加白色边框 */
      box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* 轻微阴影 */
      cursor: pointer;
    }

  </style>
</head>
<body>
<h1>Ask the AI</h1>
<div class="container">
  <input type="text" id="question" placeholder="请输入问题" />
  <button id="askButton">提问</button>
  <div id="response"></div>
</div>

<!-- 微信头像,假设头像文件存放在 'images' 文件夹下 -->
<img id="wechat-avatar" src="uploads/weixinPic.jpg" alt="微信头像" title="点击联系我" onclick="window.open('https://blog.csdn.net/TM007_?spm=1000.2115.3001.5343');" />

<script>
  document.getElementById('askButton').addEventListener('click', function () {
    const question = document.getElementById('question').value.trim();
    if (!question) {
      alert("请输入一个问题!");
      return;
    }

    const responseDiv = document.getElementById('response');
    responseDiv.style.display = 'none'; // 隐藏答案区域,等待新问题的响应

    // 清空之前的回答
    responseDiv.innerHTML = '';

    // 创建 EventSource 对象
    const eventSource = new EventSource(`http://localhost:8888/test/testAi2?question=${encodeURIComponent(question)}`);

    // 监听 SSE 数据流
    eventSource.onmessage = function(event) {
      const data = event.data;
      if (data) {
        responseDiv.textContent += data; // 用 textContent 代替 innerHTML 以避免注入
        responseDiv.style.display = 'block'; // 显示答案区域
      }
    };

    // 监听 SSE 流的结束
    eventSource.addEventListener('end', function(event) {
      responseDiv.textContent += "\n回答完毕";  // 使用 \n 来保证换行
      eventSource.close(); // 关闭 SSE 连接
    });

    // 监听错误事件
    eventSource.onerror = function(event) {
      // 检查是否是正常关闭的连接
      if (eventSource.readyState === EventSource.CLOSED) {
        return;  // 如果连接已经被关闭,就不做任何处理
      }

      console.error("SSE 错误:", event);  // 输出错误日志以便调试
      eventSource.close(); // 关闭 SSE 连接,避免触发重复的错误
    };
  });
</script>
</body>
</html>

🍸三、测试

        启动后端项目,浏览器访问端口号+静态资源,本地地址如下:

http://localhost:8888/index2.html

        3.1 页面如下

       一个问题输入框,可点击的按钮,一个横幅,加上 右下角的图片可以点击跳转,具体跳转到哪的,不明说了!

        3.2 问答测试

         输入问题,点击提问,流式输出的整体速度还好,后端记录也存在

🍹 四、章末

        本地址是简单实现了页面上展示的功能,其中包含的问题还有很多,但是也能使用,留给后面在做更新,文章到这里既结束了~

### 构建个人AI问答知识库 #### 选择合适的技术栈 为了构建一个高效的个人AI问答系统,建议采用先进的自然语言处理(NLP)框架和技术。具体来说,可以考虑使用预训练的语言模型如GPT4o-mini、豆包、文心一言等来实现智能化的回答生成[^1]。 #### 准备基础设施 对于硬件环境而言,一台配置良好且具有稳定网络连接的服务器是必要的;而对于软件方面,则需安装Python解释器及相关依赖库,例如`transformers`用于加载大型语言模型,还有Flask或FastAPI作为Web应用框架以便部署在线服务接口[^4]。 #### 数据收集与整理 创建高质量的内容资源至关重要。可以从公开渠道获取结构化数据集,也可以自行编写常见问题解答(FAQ),并将这些资料转化为适合机器阅读的形式——即JSON文件或其他易于解析的数据格式[^3]。 #### 集成第三方平台和服务 为了让用户体验更加流畅便捷,在开发过程中还可以接入其他实用工具,比如企业微信、钉钉等即时通讯软件,使得员工可以在日常工作中轻松调用内部知识库中的信息。 #### 安全保障措施 考虑到敏感信息的安全性问题,整个项目应遵循严格的信息安全管理标准,像ISO 27001这样的国际认证可以帮助确保系统的安全性得到充分保护。 ```python from transformers import pipeline, AutoModelForQuestionAnswering, AutoTokenizer model_name = "deepset/roberta-base-squad2" tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForQuestionAnswering.from_pretrained(model_name) qa_pipeline = pipeline('question-answering', model=model, tokenizer=tokenizer) def get_answer(question, context): result = qa_pipeline({ 'question': question, 'context': context }) return result['answer'] ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

先锋 Coder

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值