Bigit是一款基于BigDl-LLM和LangChain的Web服务。结合区块链技术,其可以实现针对知识图谱的问答,实现让个人拥有自己的“专用大模型”。
1.前端设计思路
ChatGo前端的主要功能为与后端大模型和链端通讯。具体采用axios与后端python通讯,以太坊Web3.js与链端通讯
- 后端设置本机7860端口的路由,前端通过 axios.get("http://127.0.0.1:7860/predict") 返回后端的对话结果
- 合约编译后,利用Web3.js与合约交互,实现知识图谱的流动、交易功能
2.前端实现
在本项目中,前端的聊天功能的实现参考了Langchain官方的聊天机器人前端模板,并针对项目内容进行了修改,具体的实现如下所示:
import React, { useState, useEffect, useRef } from 'react';
import Head from 'next/head'
import styles from '../styles/Home.module.css'
import Image from 'next/image'
import dynamic from 'next/dynamic';
const ReactMarkdown = dynamic(() => import('react-markdown'));
import CircularProgress from '@mui/material/CircularProgress';
import axios from 'axios';
export default function Home() {
const [userInput, setUserInput] = useState("");
const [history, setHistory] = useState([]);
const [loading, setLoading] = useState(false);
const [messages, setMessages] = useState([
{
"message": "Hi there! How can I help?",
"type": "apiMessage"
}
]);
const messageListRef = useRef(null);
const textAreaRef = useRef(null);
// Auto scroll chat to bottom
useEffect(() => {
const messageList = messageListRef.current;
messageList.scrollTop = messageList.scrollHeight;
}, [messages]);
// Focus on text field on load
useEffect(() => {
textAreaRef.current.focus();
}, []);
// Handle errors
const handleError = () => {
setMessages((prevMessages) => [...prevMessages, { "message": "Oops! There seems to be an error. Please try again.", "type": "apiMessage" }]);
setLoading(false);
setUserInput("");
}
// Handle form submission
const handleSubmit = async(e) => {
e.preventDefault();
if (userInput.trim() === "") {
return;
}
setLoading(true);
setMessages((prevMessages) => [...prevMessages, { "message": userInput, "type": "userMessage" }]);
// Send user question and history to API
const response = await axios.get("http://127.0.0.1:7860/predict", {
params: { message: userInput },
});
// Reset user input
setMessages((prevMessages) => [...prevMessages, { "message": response.data.response, "type": "apiMessage" }]);
setLoading(false);
};
// Prevent blank submissions and allow for multiline input
const handleEnter = (e) => {
if (e.key === "Enter" && userInput) {
if(!e.shiftKey && userInput) {
handleSubmit(e);
}
} else if (e.key === "Enter") {
e.preventDefault();
}
};
// Keep history in sync with messages
useEffect(() => {
if (messages.length >= 3) {
setHistory([[messages[messages.length - 2].message, messages[messages.length - 1].message]]);
}
}, [messages])
return (
<>
<Head>
<title>Chat</title>
<meta name="description" content="Chat to earn" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<div className={styles.topnav}>
<div className = {styles.navlogo}>
<a href="/">Chat Page</a>
</div>
<div className = {styles.navlinks}>
<a href="https://langchain.readthedocs.io/en/latest/" target="_blank">New Chat</a>
<a href="https://langchain.readthedocs.io/en/latest/" target="_blank">Save</a>
<a href="https://chrome.google.com/webstore/detail/foxswap/nnhbgjplnibnbfjgjicfjgjgjgjgjgjg" target="_blank">History</a>
</div>
</div>
<main className={styles.main}>
<div className = {styles.cloud}>
<div ref={messageListRef} className = {styles.messagelist}>
{messages.map((message, index) => {
return (
// The latest message sent by the user will be animated while waiting for a response
<div key = {index} className = {message.type === "userMessage" && loading && index === messages.length - 1 ? styles.usermessagewaiting : message.type === "apiMessage" ? styles.apimessage : styles.usermessage}>
{/* Display the correct icon depending on the message type */}
{message.type === "apiMessage" ? <Image src = "/parroticon.png" alt = "AI: " width = "30" height = "30" className = {styles.boticon} priority = {true} /> : <Image src = "/usericon.png" alt = "Me: " width = "30" height = "30" className = {styles.boticon} priority = {true} />}
<div className = {styles.markdownanswer}>
{/* Messages are being rendered in Markdown format */}
<ReactMarkdown linkTarget = {"_blank"}>{message.message}</ReactMarkdown>
</div>
</div>
)
})}
</div>
</div>
<div className={styles.center}>
<div className = {styles.cloudform}>
<form onSubmit = {handleSubmit}>
<textarea
disabled = {loading}
onKeyDown={handleEnter}
ref = {textAreaRef}
autoFocus = {false}
rows = {1}
maxLength = {512}
type="text"
id="userInput"
name="userInput"
placeholder = {loading? "Waiting for response..." : "Type your question..."}
value = {userInput}
onChange = {e => setUserInput(e.target.value)}
className = {styles.textarea}
/>
<button
type = "submit"
disabled = {loading}
className = {styles.generatebutton}
>
{loading ? <div className = {styles.loadingwheel}><CircularProgress color="inherit" size = {20}/> </div> :
// Send icon SVG in input field
<svg viewBox='0 0 20 20' className={styles.svgicon} xmlns='http://www.w3.org/2000/svg'>
<path d='M10.894 2.553a1 1 0 00-1.788 0l-7 14a1 1 0 001.169 1.409l5-1.429A1 1 0 009 15.571V11a1 1 0 112 0v4.571a1 1 0 00.725.962l5 1.428a1 1 0 001.17-1.408l-7-14z'></path>
</svg>}
</button>
</form>
</div>
<div className = {styles.footer}>
<p>Powered by <a href = "https://github.com/hwchase17/langchain" target="_blank">ChatGo</a> Built by <a href="https://twitter.com/chillzaza_" target="_blank">Ao Wang</a>.</p>
</div>
</div>
</main>
</>
)
}
3.前端使用
1. 进入ChatGo的根目录中,运行
npm run dev