react+星火大模型,构建上下文ai问答页面(可扩展)

前言

最近写的开源项目核心功能跑通了,前两天突发奇想。关于项目可否介入大模型来辅助用户使用平台,就跑去研究了最近比较活火的国内大模型–讯飞星火大模型。

大模型api获取

控制台登录

地址:https://console.xfyun.cn/app/myapp
新建应用后点进去:
image.png

获取api地址和其key

image.png
左侧选用大模型版本,右侧圈起来的地方就是咱api要调用的数据了

如果没有正常的token或key可能没有实名认证,需要先实名认证下!

下面有关于web的调用接口,这是咱们后面要调用的api接口地址:
image.png

技术栈

react hooks + TypeScript + semi-ui(组件库,可选)

下载工具包:

npm i crypto-js base-64 -d

实现

api和key都获取到了,咱就直接开始上代码操作吧!

目录

image.png

utils(工具类)

以下工具类getWebsocketUrl方法,负责构建api的URL地址,具体原因可以查阅对应的官方文档说明

import * as base64 from 'base-64';
import CryptoJs from 'crypto-js';
import { requestObj } from '../config';
export const getWebsocketUrl = () => {return new Promise<string>((resovle, reject) => {let url = 'ws://spark-api.xf-yun.com/v1.1/chat';let host = 'spark-api.xf-yun.com';let apiKeyName = 'api_key';// let date = new Date().toGMTString();let date = new Date().toUTCString();let algorithm = 'hmac-sha256';let headers = 'host date request-line';let signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v1.1/chat HTTP/1.1`;let signatureSha = CryptoJs.HmacSHA256(signatureOrigin, requestObj.APISecret);let signature = CryptoJs.enc.Base64.stringify(signatureSha);let authorizationOrigin = `${apiKeyName}="${requestObj.APIKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;let authorization = base64.encode(authorizationOrigin);// 将空格编码url = `${url}?authorization=${authorization}&date=${encodeURI(date)}&host=${host}`;resovle(url);});
};

config(配置类)

前言说的 key 在此处分别填入即可,Uid无关紧要
image.png

server/AiTool(ws服务工具)

该工具类负责接收父类组件传来的问题,并对相关数据信息进行返回。

  • **forwardRef,useImperativeHandle,props **实现父子通信
  • 通过 **getWebsocketUrl **返回url地址构建ws通信
  • **websocket **返回相关ai回应数据,并对数据加载状态进行实时通信
import { FC, forwardRef, useImperativeHandle, useState } from 'react';
import { requestObj } from '../config';
import { getWebsocketUrl } from '../utils';
interface AiToolProps {isText?: boolean;respondHoodle: (result: string) => void; //关联数据loadHoodle?: (isLoading: boolean) => void; //加载状态errorHoodle?: (isLoading: boolean) => void; //失败调用
}interface CropperRef {submitHoodle: (v: any) => void; //父类调用
}const AiTool = forwardRef<CropperRef, AiToolProps>(function AiTool({ isText, respondHoodle, loadHoodle, errorHoodle },ref
) {let result: string = '';const [historyMessage, setHistoryMessage] = useState<any[]>([{ role: 'user', content: '你是谁' }, //# 用户的历史问题{ role: 'assistant', content: '我是AI助手' }]);useImperativeHandle(ref, () => ({submitHoodle: sendMsg}));const sendMsg = async (questionText: string) => {result = ' ';// 获取请求地址let myUrl = await getWebsocketUrl();// 获取输入框中的内容// 每次发送问题 都是一个新的websocket请求let socket = new WebSocket(myUrl);// 监听websocket的各阶段事件 并做相应处理socket.addEventListener('open', (event) => {if (loadHoodle) loadHoodle(true);// 发送消息let params = {header: {app_id: requestObj.APPID,uid: 'wzz'},parameter: {chat: {domain: 'general',temperature: 0.5,max_tokens: 1024}},payload: {message: {// 如果想获取结合上下文的回答,需要开发者每次将历史问答信息一起传给服务端,如下示例// 注意:text里面的所有content内容加一起的tokens需要控制在8192以内,开发者如有较长对话需求,需要适当裁剪历史信息text: [...historyMessage,// ....... 省略的历史对话{ role: 'user', content: questionText } //# 最新的一条问题,如无需上下文,可只传最新一条问题]}}};socket.send(JSON.stringify(params));});socket.addEventListener('message', (event) => {let data = JSON.parse(event.data);if (!data.payload) {socket.close();return;}result += data.payload.choices.text[0].content;respondHoodle(result);if (data.header.code !== 0) {console.log('出错了', data.header.code, ':', data.header.message);// 出错了"手动关闭连接"socket.close();}if (data.header.code === 0) {// 对话已经完成if (data.payload.choices.text && data.header.status === 2) {setTimeout(() => {// "对话完成,手动关闭连接"socket.close();}, 1000);}}});socket.addEventListener('close', (event) => {setHistoryMessage([...historyMessage,{ role: 'user', content: questionText },{ role: 'assistant', content: result }]);if (loadHoodle) loadHoodle(false);// 对话完成后socket会关闭,将聊天记录换行处理});socket.addEventListener('error', (event) => {if (errorHoodle) errorHoodle(true);console.log('连接发送错误!!', event);});};// return result;return '';
});
export default AiTool;

Chat.tsx(具体组件)

chat组件的具体实现

  • messageList 记录信息数据
  • submit 发送问题函数
  • overRespond 介绍信息函数
  • **moveY **返回底部
import { memo, useRef, useState } from 'react';
//type
import type { FC } from 'react';
import styles from './index.module.scss';
import { Button, Spin } from '@douyinfe/semi-ui';
import AiTool from '@/ai/server/AiTool';
interface IProps {datas?: any[];
}
//user:true代表用户信息,反之ai
interface messageInfo {text: string;user: boolean;
}const Chat: FC<IProps> = () => {const [question, setQuestion] = useState<string>('');// const [result, setResult] = useState<string>('');let result = '';const [isLoading, setIsLoading] = useState<boolean>(false);const [messageList, setMessageList] = useState<messageInfo[]>([]);const ref = useRef<any>(null);const messageContainerRef = useRef<any>(null);const loadingRef = useRef<any>(null);const submit = () => {setQuestion('');console.log(messageList);if (!messageList.length) {setMessageList([{user: true,text: question}]);console.log(messageList);} else {setMessageList([...messageList,{user: true,text: question}]);console.log(messageList);}moveY();if (ref.current) {ref.current.submitHoodle(question);}};const respondHoodle = (respond: string) => {result = respond;loadingRef.current.innerText = result;moveY();// loadingRef.current};const overRespond = (v: boolean) => {if (!v) {setMessageList((prevList) => [...prevList, { user: false, text: result }]);console.log(messageList);moveY();}setIsLoading(v);};const handleSendMessage = (e: any) => {e.preventDefault();};const handleKeyPress = (e: any) => {if (e.keyCode === 13) {submit();}};//返回底部const moveY = () => {const h = messageContainerRef.current.scrollHeight;messageContainerRef.current.scrollTop = h + 20;};return (<div className={styles.chat}><div className={styles.chat__main}><header className={styles.chat__mainHeader}><p>欢迎使用青邮AI助手!</p><div><Button onClick={moveY} style={{ marginRight: 4 }}>返回底部</Button><Button type="danger" theme="solid" onClick={() => setMessageList([])}>清除聊天记录</Button></div></header>{/* 显示你发送消息的内容 */}<div className={styles.message__container} ref={messageContainerRef}>{messageList.map((item, index) => {return item.user ? (<div key={item.user.toString() + index} className={styles.message__chats}><p className={styles.sender__name}>You</p><div className={styles.message__sender}><p>{item.text}</p></div></div>) : (<div className={styles.message__chats}><p>Ai</p><div className={styles.message__recipient}><p>{item.text}</p></div></div>);})}{isLoading ? (<div className={styles.message__chats}><p>Ai</p><div className={styles.message__recipient}><p ref={loadingRef}>{result}</p><Spin /></div></div>) : ('')}</div><div className={styles.chat__footer}><form className="form" onSubmit={handleSendMessage}><inputtype="text"placeholder="编写消息"className={styles.message}value={question}onChange={(e) => setQuestion(e.target.value)}onKeyUp={handleKeyPress}/><Button onClick={submit} type="primary" theme="solid" className={styles.sendBtn}>发送</Button></form></div><AiTool loadHoodle={overRespond} respondHoodle={respondHoodle} ref={ref} /></div></div>);
};export default memo(Chat);

scss就不v了,如需要可以在评论区喊我

结果

效果图

QQ截图20231111211013.jpg

后言

关于使用server的AiTool.tsx工具,其实还能产生不少其他的扩展,接下来着重研究下!加油加油!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/140397.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

迅为龙芯2K1000开发板虚拟机ubuntu启动root用户

作为嵌入式开发人员&#xff0c;系统的所有权限都要为我们打开&#xff0c;所以我们不必像运维那样&#xff0c;对 root 用户非常敏感&#xff0c;所以安装完 ubuntu 系统以后&#xff0c;我们要启用 root 用户。 首先我们打开 ubuntu 控制终端&#xff0c;然后在终端里面输入…

[SOC] MBIST (Memory Built-In Self Test) and Memory Built-in Self Repair (BISR)

存储器构成了 VLSI 电路的很大一部分。存储系统设计的目的 是存储大量数据。[1] 存储器不包括逻辑门和触发器。因此&#xff0c;需要不同的故障模型和测试算法来测试存储器。 MBIST 是一种自测试和修复机制&#xff0c;它通过一组有效的算法来测试存储器&#xff0c;以检测典型…

【阿里云】任务2-OSS对象存储教程(找我参加活动可获得京东卡奖励)

目录 前言说明第一步第二步第三步&#xff1a;开通并使用OSS传输加速三、清理第四步-提交作品第五步-提交记录到小程序 前言 本次任务是阿里云官方发出的&#xff0c;每个任务30软妹币&#xff0c;欢迎大家加入我的活动群&#xff0c;门槛很低&#xff0c;所有人都可以参加&…

代码随想录算法训练营第五十天丨 动态规划part13

300.最长递增子序列 思路 首先通过本题大家要明确什么是子序列&#xff0c;“子序列是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序”。 本题也是代码随想录中子序列问题的第一题&#xff0c;如果没接触过这种…

能跟“猫主子”聊天了!生成式AI最快5年内破译第一种动物语言

image.png ChatGPT用它自己的方式来理解世界&#xff0c;类似的技术是否也能用来学习动物的语言&#xff1f; 所罗门能够与动物交流并不是因为他拥有魔法物品&#xff0c;而是因为他有观察的天赋。 ——康拉德・劳伦兹《所罗门王的指环》 在《狮子王》、《疯狂动物城》等以动…

Java通过JNI技术调用C++动态链接库的helloword测试

JNI调用原理 原理就不细说了&#xff0c;其实就是写个库给Java调&#xff0c;可以百度一下Java JNI&#xff0c;下面是HelloWorld代码测试 编写一个本地测试类 package com.my.study.cpp_jni;/*** 测试Java调用C库* <p>使用命令javac -h . NativeTest.java自动生成C头…

红海云签约湘湖实验室,助力科研机构人力资源数字化全面升级

湘湖实验室&#xff08;农业浙江省实验室&#xff09;是由浙江省农业科学院和萧山区人民政府共同举办的新型研发机构&#xff0c;定位为农业核心种质资源生物制造与生物互作科学问题和核心技术研究&#xff0c;瞄准世界农业科技发展前沿&#xff0c;面向国家重大战略&#xff0…

Ubuntu(WSL2) mysql8.0.31 源码安装

要在 Ubuntu 上使用调试功能安装 MySQL 8.0 的源码&#xff0c;可以按照以下详细步骤进行操作&#xff1a; 1. 更新系统 首先&#xff0c;确保你的 Ubuntu 系统是最新的。运行以下命令更新系统软件包&#xff1a; sudo apt update sudo apt upgrade 2. 下载 MySQL 源码 访…

uniapp公共css

/* 弹性布局 */ .flex{display: flex; } .a-c{align-items: center; } .a-s{align-items: flex-start; } .a-e{align-items: flex-end; } .j-c{justify-content: center; } .j-sb{justify-content: space-between; } .j-s{justify-content: flex-start; } .j-e{justify-content…

Redis 特殊数据类型

目录 1、redis地理空间(GEO) 2、redis基数统计(HyperLogLog) 3、redis位图(bitmap) 4、redis位域bitfield) 5、redis流(Stream) 1、redis地理空间(GEO) Redis 的地理空间数据结构&#xff08;GEO&#xff09;可以用于存储地理位置信息&#xff0c;并支持附近位置搜索等功能…

通讯协议学习之路(实践部分):UART开发实践

通讯协议之路主要分为两部分&#xff0c;第一部分从理论上面讲解各类协议的通讯原理以及通讯格式&#xff0c;第二部分从具体运用上讲解各类通讯协议的具体应用方法。 后续文章会同时发表在个人博客(jason1016.club)、CSDN&#xff1b;视频会发布在bilibili(UID:399951374) 本文…

gpt支持json格式的数据返回(response_format: ‘json_object‘)

Api.h5.chatCreateChatCompletion({model: gpt-3.5-turbo-1106,token: sk-f4fe8b67-fcbe-46fd-8cc9-fd1dac5d6d59,messages: [{role: user,content:使用json格式返回十二生肖&#xff0c;包含中文名和英文名&#xff0c;[{id:"1", enName:"", cnName: &quo…

初始MySQL(二)(表的增删查改)

目录 修改表 CRUD(增删改查) insert语句(表中增加数据) update语句(修改表中的数据) delete删除语句 select语句 修改表 添加列 ALTER TABLE tablename ADD (column datatype [DEFAULT expr] [, column datatype] ...); 修改列 ALTER TABLE tablename MODIFY (column …

JavaScript红宝书第8章:对象、类与面向对象编程(1/4)

原生JavaScript之对象、类与面向对象编程 理解对象属性类型数据属性访问器属性 定义多个属性读取属性特征合并对象对象标识及相等判定对象新语法属性值简写动态给属性命名 简写方法对象解构 小结 理解对象 定义&#xff1a;对象为一组属性的无序集合&#xff0c;严格来说&…

ROS机器人毕业论文数量井喷-数据日期23年11月13日

背景 ROS机器人论文数量在近3年井喷发展&#xff0c;仅硕士论文知网数据库可查阅就已经达到2264篇&#xff0c;实际相关从业者远远远大于这个数值。 按日期排序&#xff0c;每页20篇&#xff0c;23年还未结束&#xff0c;检索本身也不一定完备&#xff0c;就超过200。 相关从业…

语音识别芯片在产品应用上的难点列举

语音识别技术&#xff0c;作为人工智能领域中的一颗璀璨明珠&#xff0c;已经广泛应用于用户交互、智能家居、语音助手等多个领域。它为我们的生活带来了诸多便利&#xff0c;使得我们可以更加高效地与电子设备进行互动。然而&#xff0c;语音识别技术的实现&#xff0c;依赖于…

单例模式的双重检查锁定是什么

单例模式的双重检查锁定是什么&#xff1f; 单例模式是一种常见的设计模式&#xff0c;用于确保一个类只有一个实例&#xff0c;并提供一个全局访问点。双重检查锁定&#xff08;Double-Checked Locking&#xff09;是一种在单例模式中使用的性能优化技术。 在传统的单例模式…

Android超简单的显示网络图片方法

Android显示网络图片的方法如下&#xff1a; 1、首先&#xff0c;需要在AndroidManifest.xml文件中添加网络权限&#xff1a; <uses-permission android:name"android.permission.INTERNET" /> 2、在布局文件中添加ImageView控件&#xff1a; <ImageVie…

强烈建议linux中nvidia 545.29驱动不要升

我之前一直用终端连接我的工作站&#xff08;系统是arch rolling状态&#xff09;&#xff0c;结果昨天回家难得想试试545驱动下的效果。结果一用chrome播放视频就卡&#xff0c;甚至后面进Login界面也会卡住鼠标。 折腾了一晚上用 $sudo downgrade nvidia nvidia-prime nvid…

比尔.盖茨最新思考和宣言, 未来5年AI将彻底改变生活!

人工智能即将彻底改变你使用计算机的方式 并颠覆软件行业。 比尔盖茨 今天我仍然像保罗艾伦和我创办微软时一样热爱软件。但是&#xff0c;尽管从那时起的几十年里它已经有了很大的进步&#xff0c;但在很多方面&#xff0c;软件仍然相当愚蠢。 要在计算机上执行任何任务&am…