国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁(yè) > news >正文

學(xué)做餃子餡上那個(gè)網(wǎng)站推廣優(yōu)化師

學(xué)做餃子餡上那個(gè)網(wǎng)站,推廣優(yōu)化師,網(wǎng)站文案案例,加盟類網(wǎng)站怎么做一、前言 大家好!我是sum墨,一個(gè)一線的底層碼農(nóng),平時(shí)喜歡研究和思考一些技術(shù)相關(guān)的問題并整理成文,限于本人水平,如果文章和代碼有表述不當(dāng)之處,還請(qǐng)不吝賜教。 最近ChatGPT非常受歡迎,尤其是…

一、前言

大家好!我是sum墨,一個(gè)一線的底層碼農(nóng),平時(shí)喜歡研究和思考一些技術(shù)相關(guān)的問題并整理成文,限于本人水平,如果文章和代碼有表述不當(dāng)之處,還請(qǐng)不吝賜教。

最近ChatGPT非常受歡迎,尤其是在編寫代碼方面,我每天都在使用。隨著使用時(shí)間的增長(zhǎng),我開始對(duì)其原理產(chǎn)生了一些興趣。雖然我無法完全理解這些AI大型模型的算法和模型,但我認(rèn)為可以研究一下其中的交互邏輯。特別是,我想了解它是如何實(shí)現(xiàn)在發(fā)送一個(gè)問題后不需要等待答案完全生成,而是通過不斷追加的方式實(shí)現(xiàn)實(shí)時(shí)回復(fù)的。

F12打開控制臺(tái)后,我發(fā)現(xiàn)在點(diǎn)擊發(fā)送后,它會(huì)發(fā)送一個(gè)普通的請(qǐng)求。但是回復(fù)的方式卻不同,它的類型是eventsource。一次請(qǐng)求會(huì)不斷地獲取數(shù)據(jù),然后前端的聊天組件會(huì)動(dòng)態(tài)地顯示回復(fù)內(nèi)容,回復(fù)的內(nèi)容是用Markdown格式來展示的。

在了解了前面的這些東西后我就萌生了自己寫一個(gè)小demo的想法。起初,我打算使用openai的接口,并寫一個(gè)小型的UI組件。然而,由于openai賬號(hào)申請(qǐng)復(fù)雜且存在網(wǎng)絡(luò)問題,很多人估計(jì)搞不定,所以我最終選擇了通義千問。通義千問有兩個(gè)優(yōu)點(diǎn):一是它是國(guó)內(nèi)的且目前調(diào)用是免費(fèi)的,二是它提供了Java-SDK和API文檔,開發(fā)起來容易。

作為后端開發(fā)人員,按照API文檔調(diào)用模型并不難,但真正難到我的是前端UI組件的編寫。我原以為市面上會(huì)有很多支持EventStream的現(xiàn)成組件,但事實(shí)上并沒有。不知道是因?yàn)檫@個(gè)功能太容易還是太難,總之,對(duì)接通義千問只花了不到一小時(shí),而編寫一個(gè)UI對(duì)話組件卻花了整整兩天的時(shí)間!接下來,我將分享一些我之前的經(jīng)驗(yàn),希望可以幫助大家少走坑。

首先展示一下我的成品效果
在這里插入圖片描述

二、通義千問開發(fā)Key申請(qǐng)

1. 登錄阿里云,搜索通義千問

2. 點(diǎn)擊"開通DashScope"

3. 創(chuàng)建一個(gè)API-KEY

4. 對(duì)接流程

(1)API文檔地址

https://help.aliyun.com/zh/dashscope/developer-reference/api-details

(2)Java-SDK依賴

<dependency><groupId>com.alibaba</groupId><artifactId>dashscope-sdk-java</artifactId><version>2.8.2</version>
</dependency>

三、支持EventStream格式的接口

1. 什么是EventStream

EventStream是一種流式數(shù)據(jù)格式,用于實(shí)時(shí)傳輸事件數(shù)據(jù)。它是基于HTTP協(xié)議的,但與傳統(tǒng)的請(qǐng)求-響應(yīng)模型不同,它是一個(gè)持續(xù)的、單向的數(shù)據(jù)流。它可用于推送實(shí)時(shí)數(shù)據(jù)、日志、通知等,所以EventStream很適合這種對(duì)話式的場(chǎng)景。在Spring Boot中,主要有以下框架和模塊支持EventStream格式:

  • Spring WebFlux:Spring WebFlux是Spring框架的一部分,用于構(gòu)建反應(yīng)式Web應(yīng)用程序。
  • Reactor:Reactor是一個(gè)基于響應(yīng)式流標(biāo)準(zhǔn)的庫(kù),是Spring WebFlux的核心組件。
  • Spring Cloud Stream:Spring Cloud Stream是一個(gè)用于構(gòu)建消息驅(qū)動(dòng)的微服務(wù)應(yīng)用的框架。

這次我使用的是reactor-core框架。

2. 寫一個(gè)例子

maven依賴

<!-- Reactor Core -->
<dependency><groupId>io.projectreactor</groupId><artifactId>reactor-core</artifactId><version>3.4.6</version>
</dependency>

代碼如下

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;import java.time.Duration;
import java.time.LocalTime;@RestController
@RequestMapping("/event-stream")
public class EventStreamController {@GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE)public Flux<String> getEventStream() {return Flux.interval(Duration.ofSeconds(1)).map(sequence -> "Event " + sequence + " at " + LocalTime.now());}
}

調(diào)用一下接口后就可以看到瀏覽器上在不斷地打印時(shí)間戳了

四、項(xiàng)目實(shí)現(xiàn)

這個(gè)就不BB了,直接貼代碼!

1. 項(xiàng)目結(jié)構(gòu)

2. pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.17</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.chatrobot</groupId><artifactId>demo</artifactId><version>0.0.1-SNAPSHOT</version><name>demo</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><!-- 通義千問SDK --><dependency><groupId>com.alibaba</groupId><artifactId>dashscope-sdk-java</artifactId><version>2.8.2</version></dependency><!-- Reactor Core --><dependency><groupId>io.projectreactor</groupId><artifactId>reactor-core</artifactId><version>3.4.6</version></dependency><!-- Web組件 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><artifactId>logback-classic</artifactId><groupId>ch.qos.logback</groupId></exclusion></exclusions></dependency></dependencies></project>

3. 代碼

(1)后端代碼

DemoApplication.java
package com.chatrobot;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}}
EventController.java
package com.chatrobot.controller;import java.time.Duration;
import java.time.LocalTime;
import java.util.Arrays;import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.aigc.generation.models.QwenParam;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;import io.reactivex.Flowable;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;@RestController
@RequestMapping("/events")
@CrossOrigin
public class EventController {@Value("${api.key}")private String apiKey;@GetMapping(value = "/streamAsk", produces = MediaType.TEXT_EVENT_STREAM_VALUE)public Flux<ServerSentEvent<String>> streamAsk(String q) throws Exception {Generation gen = new Generation();// 創(chuàng)建用戶消息對(duì)象Message userMsg = Message.builder().role(Role.USER.getValue()).content(q).build();// 創(chuàng)建QwenParam對(duì)象,設(shè)置參數(shù)QwenParam param = QwenParam.builder().model(Generation.Models.QWEN_PLUS).messages(Arrays.asList(userMsg)).resultFormat(QwenParam.ResultFormat.MESSAGE).topP(0.8).enableSearch(true).apiKey(apiKey)// get streaming output incrementally.incrementalOutput(true).build();// 調(diào)用生成接口,獲取Flowable對(duì)象Flowable<GenerationResult> result = gen.streamCall(param);// 將Flowable轉(zhuǎn)換成Flux<ServerSentEvent<String>>并進(jìn)行處理return Flux.from(result)// add delay between each event.delayElements(Duration.ofMillis(1000)).map(message -> {String output = message.getOutput().getChoices().get(0).getMessage().getContent();System.out.println(output); // print the outputreturn ServerSentEvent.<String>builder().data(output).build();}).concatWith(Flux.just(ServerSentEvent.<String>builder().comment("").build())).doOnError(e -> {if (e instanceof NoApiKeyException) {// 處理 NoApiKeyException} else if (e instanceof InputRequiredException) {// 處理 InputRequiredException} else if (e instanceof ApiException) {// 處理其他 ApiException} else {// 處理其他異常}});}@GetMapping(value = "test", produces = MediaType.TEXT_EVENT_STREAM_VALUE)public Flux<String> testEventStream() {return Flux.interval(Duration.ofSeconds(1)).map(sequence -> "Event " + sequence + " at " + LocalTime.now());}
}

(2)前端代碼

chat.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>ChatBot</title><style>body {background: #f9f9f9;/* 替換為您想要的背景顏色或圖片 */}.chat-bot {display: flex;flex-direction: column;width: 100%;max-width: 800px;margin: 50px auto;box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);border-radius: 8px;overflow: hidden;font-family: "Roboto", sans-serif;background: #f5f5f5;}.chat-bot-header {background: linear-gradient(to right, #1791ee, #9fdbf1);color: white;text-align: center;padding: 15px;font-size: 24px;font-weight: 500;}.chat-bot-messages {flex: 1;padding: 20px;min-height: 400px;overflow-y: auto;}.userName {margin: 0 10px;}.message-wrapper {display: flex;align-items: flex-start;margin-bottom: 10px;border-radius: 20px;}.message-wrapper.user {justify-content: flex-end;border-radius: 20px;}.message-avatar {width: 30px;height: 30px;border-radius: 50%;background-color: #ccc;margin-right: 10px;margin-bottom: 10px;/* 添加這一行 */order: -1;/* 添加這一行 */text-align: right;}.message-avatar.user {background-color: transparent;display: flex;justify-content: flex-end;width: 100%;margin-right: 0;align-items: center;}.message-avatar.bot {background-color: transparent;display: flex;justify-content: flex-start;width: 100%;margin-right: 0;align-items: center;}.message-avatar-inner.user {background-image: url("./luge.jpeg");background-size: cover;background-position: center;width: 30px;height: 30px;border-radius: 50%;}.message-avatar-inner.bot {background-image: url("./logo.svg");background-size: cover;background-position: center;width: 30px;height: 30px;border-radius: 50%;}.message {padding: 10px 20px;border-radius: 15px;font-size: 16px;background-color: #d9edf7;order: 1;/* 添加這一行 */}.bot {background-color: #e9eff5;/* 添加這一行 */}.user {background-color: #d9edf7;color: #111111;order: 1;/* 添加這一行 */}.chat-bot-input {display: flex;align-items: center;border-top: 1px solid #ccc;padding: 10px;background-color: #fff;}.chat-bot-input input {flex: 1;padding: 10px 15px;border: none;font-size: 16px;outline: none;}.chat-bot-input button {padding: 10px 20px;background-color: #007bff;border: none;border-radius: 50px;color: white;font-weight: 500;cursor: pointer;transition: background-color 0.3s;}.chat-bot-input button:hover {background-color: #0056b3;}@media (max-width: 768px) {.chat-bot {margin: 20px;}.chat-bot-header {font-size: 20px;}.message {font-size: 14px;}}@keyframes spin {0% {transform: rotate(0deg);}100% {transform: rotate(360deg);}}.loading-spinner {width: 15px;height: 15px;border-radius: 50%;border: 2px solid #d9edf7;border-top-color: transparent;animation: spin 1s infinite linear;}</style>
</head>
<body>
<div class="chat-bot"><div class="chat-bot-header"><img src="./logo.svg" alt="Logo" class="logo" />通義千問</div><div class="chat-bot-messages"></div><div class="chat-bot-input"><input type="text" placeholder="輸入你想問的問題" /><button id="sendButton">Send</button></div>
</div>
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/13.0.2/markdown-it.min.js"integrity="sha512-ohlWmsCxOu0bph1om5eDL0jm/83eH09fvqLDhiEdiqfDeJbEvz4FSbeY0gLJSVJwQAp0laRhTXbUQG+ZUuifUQ=="crossorigin="anonymous"referrerpolicy="no-referrer"
></script>
<script>const userName = "summo";document.addEventListener("DOMContentLoaded", function () {const input = document.querySelector(".chat-bot-input input");const messagesContainer = document.querySelector(".chat-bot-messages");const sendButton = document.getElementById("sendButton");function appendToMessage(messageTxt, sender, md, message) {let messageElement = messagesContainer.querySelector(`.message-wrapper.${sender}:last-child .message`);if (!messageElement) {if (sender === "bot") {messageElement = document.createElement("div");messageElement.classList.add("message-avatar", sender);messageElement.innerHTML = `<div class="message-avatar-inner ${sender}"></div><div class="userName">通義千問</div>`;messagesContainer.appendChild(messageElement);} else {messageElement = document.createElement("div");messageElement.classList.add("message-avatar", sender);messageElement.innerHTML = `<div class="message-avatar-inner ${sender}"></div><div class="userName"">${userName}</div>`;messagesContainer.appendChild(messageElement);}messageElement = document.createElement("div");messageElement.classList.add("message-wrapper", sender);messageElement.innerHTML = `<div class="message ${sender}"></div>`;messagesContainer.appendChild(messageElement);messageElement = messageElement.querySelector(".message");}// messageElement.textContent += messageTxt; // 追加文本// messagesContainer.scrollTop = messagesContainer.scrollHeight; // 滾動(dòng)到底部let result = (message += messageTxt);const html = md.renderInline(messageTxt);messageElement.innerHTML += html;messagesContainer.scrollTop = messagesContainer.scrollHeight;}function handleSend() {const inputValue = input.value.trim();if (inputValue) {input.disabled = true;sendButton.disabled = true;sendButton.innerHTML = '<div class="loading-spinner"></div>';const md = new markdownit();// 修改按鈕文本內(nèi)容為"Loading..."let message = "";appendToMessage(inputValue, "user", md, message);input.value = "";const eventSource = new EventSource(`http://localhost:8080/events/streamAsk?q=${encodeURIComponent(inputValue)}`);eventSource.onmessage = function (event) {console.log(event.data);appendToMessage(event.data, "bot", md, message);};eventSource.onerror = function () {eventSource.close();input.disabled = false;sendButton.disabled = false;sendButton.innerHTML = "Send";};}}document.querySelector(".chat-bot-input button").addEventListener("click", handleSend);input.addEventListener("input", function () {sendButton.disabled = input.value.trim() === "";});input.addEventListener("keypress", function (event) {if (event.key === "Enter" && !sendButton.disabled) {handleSend();}});});
</script>
</body>
</html>

另外還有兩個(gè)頭像,大家可以替換成自己喜歡的,好了文章到這里也就結(jié)束了,再秀一下我的成品👉

http://aloenet.com.cn/news/30383.html

相關(guān)文章:

  • 局域網(wǎng)建設(shè)簡(jiǎn)單的影視網(wǎng)站seo搜狗排名點(diǎn)擊
  • 荔灣網(wǎng)站制作如何搜索網(wǎng)頁(yè)關(guān)鍵詞
  • 有什么網(wǎng)站可以做微信支付寶支付寶一鍵制作單頁(yè)網(wǎng)站
  • 惠州有哪些做網(wǎng)站的公司百度熱門
  • 幫客戶做網(wǎng)站平臺(tái)犯法嗎南寧關(guān)鍵詞優(yōu)化公司
  • 服裝市場(chǎng)網(wǎng)站建設(shè)互聯(lián)網(wǎng)營(yíng)銷師報(bào)名費(fèi)
  • 域名購(gòu)買后網(wǎng)站搭建賬號(hào)seo是什么
  • 自己怎么做VIP視頻解網(wǎng)站汕頭網(wǎng)站建設(shè)技術(shù)外包
  • 橙子建站客服電話2020 惠州seo服務(wù)
  • 京網(wǎng)站建設(shè)公司百度地圖收錄提交入口
  • 程序員 做網(wǎng)站 微信公眾號(hào) 賺錢寧波seo推廣費(fèi)用
  • 做網(wǎng)站也是一門技術(shù)惠州網(wǎng)絡(luò)營(yíng)銷
  • 網(wǎng)站 禁止ping百度seo推廣
  • 網(wǎng)站建設(shè)必學(xué)課程深圳谷歌seo推廣
  • 阿里巴巴做網(wǎng)站找誰泰州百度seo公司
  • 網(wǎng)站運(yùn)行與維護(hù)網(wǎng)絡(luò)推廣外包內(nèi)容
  • 網(wǎng)頁(yè)上海公司seo工資服務(wù)
  • 免費(fèi)網(wǎng)站開發(fā)軟件平臺(tái)愛站網(wǎng)長(zhǎng)尾詞挖掘工具
  • 網(wǎng)站開發(fā)要什么樣的環(huán)境代運(yùn)營(yíng)公司
  • 網(wǎng)站建設(shè)網(wǎng)站軟文范文
  • 番禺手機(jī)網(wǎng)站制作推廣行者seo
  • 做外貿(mào)網(wǎng)站效果站長(zhǎng)是什么級(jí)別
  • 網(wǎng)站設(shè)計(jì)與網(wǎng)頁(yè)配色實(shí)例精講nba最新新聞新浪
  • 做英文企業(yè)網(wǎng)站多錢錢上海百度推廣官方電話
  • 建設(shè)標(biāo)準(zhǔn) 免費(fèi)下載網(wǎng)站磁力天堂torrentkitty
  • 部落沖突做任務(wù)網(wǎng)站百度熱搜廣告位
  • 給公司做網(wǎng)站銷售怎樣啦網(wǎng)絡(luò)公司品牌推廣
  • 企業(yè)戰(zhàn)略規(guī)劃方案北京seo網(wǎng)絡(luò)推廣
  • 網(wǎng)頁(yè)設(shè)計(jì)公司金華關(guān)鍵詞排名優(yōu)化公司外包
  • 網(wǎng)站建設(shè)利益分析合肥網(wǎng)站seo推廣