react+星火大模型,构建上下文ai问答页面(可扩展)
前言
最近写的开源项目核心功能跑通了,前两天突发奇想。关于项目可否介入大模型来辅助用户使用平台,就跑去研究了最近比较活火的国内大模型–讯飞星火大模型。
大模型api获取
控制台登录
地址:https://console.xfyun.cn/app/myapp
新建应用后点进去:

获取api地址和其key

左侧选用大模型版本,右侧圈起来的地方就是咱api要调用的数据了
如果没有正常的token或key可能没有实名认证,需要先实名认证下!
下面有关于web的调用接口,这是咱们后面要调用的api接口地址:

技术栈
react hooks + TypeScript + semi-ui(组件库,可选)
下载工具包:
npm i crypto-js base-64 -d
实现
api和key都获取到了,咱就直接开始上代码操作吧!
目录

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无关紧要

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了,如需要可以在评论区喊我
结果
效果图

后言
关于使用server的AiTool.tsx工具,其实还能产生不少其他的扩展,接下来着重研究下!加油加油!
相关文章:
react+星火大模型,构建上下文ai问答页面(可扩展)
前言 最近写的开源项目核心功能跑通了,前两天突发奇想。关于项目可否介入大模型来辅助用户使用平台,就跑去研究了最近比较活火的国内大模型–讯飞星火大模型。 大模型api获取 控制台登录 地址:https://console.xfyun.cn/app/myapp 新建应…...
python---设计模式
python中设计模式-单例模式 基于__new__方法实现 第一个设计: class MySingleton:def __init__(self):passdef __new__(cls, *args, **kwargs):passmysingleton1 MySingleton() mysingleton2 MySingleton() print(mysingleton1) print(mysingleton2) print(id(…...
Java编写xml文件时,文件中特殊字符如何解决?
有一个使用Java创建XML文件的需求,但标签里面有以下特殊字符<、>、&等 在未解决之前,创建出的XML是这样的 <?xml version"1.0" encoding"UTF-8"?><actionlist><update><jobno>1111</jobno&…...
vue3 ts pinia openapi vue-query pnpm docker前端架构小记
1.引言 开发中,我们是否经常遇到以下痛点: 项目越大,启动和热更新越来越慢,启动都要花个3-5分钟以上没有类型保障,接口返回的Object不拿到真实数据都不知道有哪些字段,接手别人js项目(无类型)很痛苦需要手…...
ARM day4
LED灯亮灭控制 .text .global _start _start: 1ldr r0,0x50000a28ldr r1,[r0]orr r1,r1,#(0x3<<4)str r1,[r0] 2ldr r0,0x50006000ldr r1,[r0]bic r1,r1,#(0x3<<20)orr r1,r1,#(0x1<<20)bic r1,r1,#(0x3<<16)orr r1,r1,#(0x1<<16)str r1,[r0]…...
3.30每日一题(多元函数微分学)
1、判断连续:再分界点的极限值等于该点的函数值; 如何求极限值: 初步判断:分母都为二次幂开根号,所以分母为一次幂;分子为二次,一般来说整体为0; 如何说明极限为零(常用…...
《OSTEP》条件变量(chap30)
〇、前言 本文是对《OSTEP》第三十章的实践与总结。 一、条件变量 #include <pthread.h> #include <stdio.h> #include <assert.h>int buffer; int count 0; // 资源为空// 生产,在 buffer 中放入一个值 void put(int value) {assert(count 0);count 1…...
MySQL的索引和复合索引
由于MySQL自动将主键加入到二级索引(自行建立的index)里,所以当select的是主键或二级索引就会很快,select *就会慢。因为有些列是没在索引里的 假设CA有1kw人咋整,那我这个索引只起了前一半作用。 所以用复合索引&am…...
关于mac下pycharm旧版本没删除的情况下新版本2023安装之后闪退
先说结论,我用的app cleaner 重新删除的pycharm ,再重新安装即可。在此记录一下 之前安装的旧版的2020的pycharm,因为装不了新的插件,没办法就升级了。新装2023打开之后闪退,重启系统也不行,怀疑是一起破解…...
Django中如何让DRF的接口针对前后台返回不同的字段
在Django中,使用Django Rest Framework(DRF)时,可以通过序列化器(Serializer)和视图(View)的组合来实现前后台返回不同的字段。这通常是因为前后台对数据的需求不同,或者…...
【机器学习】Kmeans聚类算法
一、聚类简介 Clustering (聚类)是常见的unsupervised learning (无监督学习)方法,简单地说就是把相似的数据样本分到一组(簇),聚类的过程,我们并不清楚某一类是什么(通常无标签信息)࿰…...
getid3 获取视频时长
1、首先,我们需要先下载一份PHP类—getid3https://codeload.github.com/JamesHeinrich/getID3/zip/master 2.我在laravel6.0 中使用 需要在composer.json 自动加载 否则系统访问不到 在命令行 执行 composer dump-autoload $getID3 new \getID3();//视频文件需要放…...
如何知道一个程序为哪些信号注册了哪些信号处理函数?
https://unix.stackexchange.com/questions/379694/is-there-a-way-to-know-if-signals-are-present-in-your-application-and-which-sign 使用 strace...
34 mysql limit 的实现
前言 这里来看一下 我们常见的 mysql 分页的 limit 的相的处理 这个问题的主要是来自于 之前有一个需要处理 大数据量的数据表的信息, 将数据转移到 es 中 然后就是用了最简单的 “select * from tz_test limit $pageOffset, $pageSize ” 来分页处理 但是由于 数据表的数…...
jbase实现申明式事务
对有反射的语言,申明式事务肯定不可少。没必要没个人都try,catch写事务,写的不好的话还经常容易锁表,为此给框架引入申明式事务。申明式既字面意思,在需要事务的方法前面加一个申明,那么框架保证事务。 首…...
如何在在线Excel文档中规范单元格输入
在日常的工作中,我们常常需要处理大量的数据。为了确保数据的准确性和可靠性。我们需要对输入的数据进行规范化和验证。其中一个重要的方面是规范单元格输入。而数据验证作为Excel中一种非常实用的功能,它可以帮助用户规范单元格的输入,从而提…...
力扣138:随机链表的复制
力扣138:随机链表的复制 题目描述: 给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成ÿ…...
C语言左移与右移学习
在学习左移与右移之前,我们首先要学习两种移位运算:逻辑移位和算数移位。 逻辑位移:移出去的位丢弃,空缺位用0补充。 算数位移:移出去的位丢弃,空缺位用符号位补充。 左移 左移是高位溢出,低…...
asp.net core mvc之 视图
一、在控制器中找到匹配视图,然后渲染成 HTML 代码返回给用户 public class HomeController : Controller {public IActionResult Index(){return View(); //默认找 Views/Home/Index.cshtml ,呈现给用户} } 二、指定视图 1、控制器 publ…...
ChatGLM3 tool_registry.py 代码解析
ChatGLM3 tool_registry.py 代码解析 0. 背景1. tool_registry.py 0. 背景 学习 ChatGLM3 的项目内容,过程中使用 AI 代码工具,对代码进行解释,帮助自己快速理解代码。这篇文章记录 ChatGLM3 tool_registry.py 的代码解析内容。 1. tool_re…...
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
