Bigit是一款基于BigDl-LLM和LangChain的Web服务。结合区块链技术,其可以实现针对知识图谱的问答,实现让个人拥有自己的“专用大模型”。
1.链端设计思路
链端的主要功能为实现知识图谱的流通交易,进而实现大模型的流通交易,使个人拥有专用大模型的交易权。具体是结合IPFS实现链上存储图谱、大模型数据,铸造NFT并交易,从而实现知识图谱的流通与交易。
2.链端实现
2.1 聊天机器人NFT铸造
项目中,我们把聊天机器人用到的知识图谱存入IPFS的Pinata云端中,Prompt存入链中,并铸造聊天机器人NFT,具体的实现方式如下
/* eslint-disable @next/next/no-img-element */
import { useState } from 'react'
import { ethers } from 'ethers'
import { useRouter } from 'next/router'
import Web3Modal from 'web3modal'
import axios from 'axios';
import Image from 'next/image';
const apiKey = process.env.NEXT_PUBLIC_PINATA_CLOUD_API_KEY;
const apiSecret = process.env.NEXT_PUBLIC_PINATA_CLOUD_API_SECRET;
import {
marketplaceAddress
} from '../config'
import NFTMarketplace from '../artifacts/contracts/NFTMarketplace.sol/NFTMarketplace.json'
export default function CreateItem() {
const [fileUrl, setFileUrl] = useState(null)
const [formInput, updateFormInput] = useState({ price: '', name: '', description: '' })
const router = useRouter()
async function onChange(e) { //上传文件到IPFS,返回文件url存储在的fileurl中
const file = e.target.files[0];
const formData = new FormData();
formData.append('file', file);
try {
const added = await axios.post('https://api.pinata.cloud/pinning/pinFileToIPFS', formData, {
headers: {
'Content-Type': `multipart/form-data; boundary=${formData._boundary}`,
'pinata_api_key': apiKey,
'pinata_secret_api_key': apiSecret
}
});
const url = `https://gateway.pinata.cloud/ipfs/${added.data.IpfsHash}`
setFileUrl(url)
} catch (error) {
console.log('Error uploading file: ', error)
}
}
async function uploadToIPFS() { //上传文件,返回文件的url
const { name, description, price } = formInput
if (!name || !description || !price || !fileUrl) return
/* first, upload to IPFS */
const data = JSON.stringify({
name, description, kg: fileUrl
});
try {
const added = await axios.post('https://api.pinata.cloud/pinning/pinJSONToIPFS', data, {
headers: {
'Content-Type': `application/json`,
'pinata_api_key': apiKey,
'pinata_secret_api_key': apiSecret
}
});
const url = `https://gateway.pinata.cloud/ipfs/${added.data.IpfsHash}`
/* after file is uploaded to IPFS, return the URL to use it in the transaction */
return url
} catch (error) {
console.log('Error uploading file: ', error)
}
}
async function listNFTForSale() { //创建NFT并上架出售
const url = await uploadToIPFS()
const web3Modal = new Web3Modal()
const connection = await web3Modal.connect()
const provider = new ethers.providers.Web3Provider(connection)
const signer = provider.getSigner()
/* next, create the item */
const price = ethers.utils.parseUnits(formInput.price, 'ether')
let contract = new ethers.Contract(marketplaceAddress, NFTMarketplace.abi, signer)
let listingPrice = await contract.getListingPrice()
listingPrice = listingPrice.toString()
let transaction = await contract.createToken(url, price, { value: listingPrice })
await transaction.wait()
router.push('/')
}
return (
<div className="flex justify-center">
<div className="w-1/2 flex flex-col pb-12">
<input
placeholder="Asset Name"
className="mt-8 border rounded p-4"
onChange={e => updateFormInput({ ...formInput, name: e.target.value })}
/>
<textarea
placeholder="Asset Description"
className="mt-2 border rounded p-4"
onChange={e => updateFormInput({ ...formInput, description: e.target.value })}
/>
<input
placeholder="Asset Price in Eth"
className="mt-2 border rounded p-4"
onChange={e => updateFormInput({ ...formInput, price: e.target.value })}
/>
<input
type="file"
name="Asset"
className="my-4"
onChange={onChange}
/>
{
fileUrl && (
<Image className="rounded mt-4" width={200} height={500} src = "/kg.png" />
)
}
<button onClick={listNFTForSale} className="font-bold mt-4 bg-black text-white rounded p-4 shadow-lg">
Create
</button>
</div>
</div>
)
}
2.2 聊天机器人NFT购买
本项目中,在购买了聊天机器人NFT后,dump文件会被下载到本地,Prompt会被写到项目的example.txt中,以便与后续后端大模型调入。具体的实现方式如下:
import { ethers } from 'ethers'
import { useEffect, useState } from 'react'
import axios from 'axios'
import Web3Modal from 'web3modal'
import Image from 'next/image';
import {
marketplaceAddress
} from '../config'
import NFTMarketplace from '../artifacts/contracts/NFTMarketplace.sol/NFTMarketplace.json'
const rpcEndpointUrl = "http://127.0.0.1:8545";
//const rpcEndpointUrl = "https://rpc-mumbai.maticvigil.com";
export default function Home() {
const [nfts, setNfts] = useState([]) //存储从智能合约中获取的NFT数据
const [loadingState, setLoadingState] = useState('not-loaded') //跟踪数据状态
useEffect(() => {
loadNFTs()
}, [])
async function loadNFTs() {
const provider = new ethers.providers.JsonRpcProvider(rpcEndpointUrl);
const contract = new ethers.Contract(marketplaceAddress, NFTMarketplace.abi, provider)
const data = await contract.fetchMarketItems()
const items = await Promise.all(data.map(async i => {
const tokenUri = await contract.tokenURI(i.tokenId)
const meta = await axios.get(`/api/proxy?url=${encodeURIComponent(tokenUri)}`); //发送HTTP请求获取NFT的元数据
let price = ethers.utils.formatUnits(i.price.toString(), 'ether')
let item = {
price,
tokenId: i.tokenId.toNumber(),
seller: i.seller,
owner: i.owner,
kg: meta.data.kg,
name: meta.data.name,
description: meta.data.description,
}
return item
}))
setNfts(items)
setLoadingState('loaded')
}
async function downloadNFT(nft) {
const response = await fetch(nft.kg);
const blob = await response.blob();
const downloadLink = document.createElement('a');
downloadLink.href = URL.createObjectURL(blob);
downloadLink.download = `${nft.name}.dump`;
downloadLink.click();
}
async function buyNft(nft) {
const web3Modal = new Web3Modal()
const connection = await web3Modal.connect()
const provider = new ethers.providers.Web3Provider(connection)
const signer = provider.getSigner()
const contract = new ethers.Contract(marketplaceAddress, NFTMarketplace.abi, signer)
//交易实例
const price = ethers.utils.parseUnits(nft.price.toString(), 'ether')
const transaction = await contract.createMarketSale(nft.tokenId, {
value: price
})
await downloadNFT(nft);
await axios.post('/api/writeFile', { description: nft.description });
console.log('描述已写入文件!');
await transaction.wait()
loadNFTs()
}
if (loadingState === 'loaded' && !nfts.length) return (<h1 className="px-20 py-10 text-3xl">No items in marketplace</h1>)
return (
<div className="flex justify-center">
<div className="px-4" style={{ maxWidth: '1600px' }}>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 pt-4">
{
nfts.map((nft, i) => (
<div key={i} className="border shadow rounded-xl overflow-hidden">
<Image src= "/kg.png" width={500} height={500} />
<div className="p-4">
<p style={{ height: '64px' }} className="text-2xl font-semibold">{nft.name}</p>
<div style={{ height: '70px', overflow: 'hidden' }}>
<p className="text-gray-400">{nft.description}</p>
</div>
</div>
<div className="p-4 bg-black">
<p className="text-2xl font-bold text-white">{nft.price} ETH</p>
<button style={{ backgroundColor: 'rgb(101, 175, 80)', color: 'white', fontWeight: 'bold', paddingTop: '2px', paddingBottom: '2px', paddingLeft: '12px', paddingRight: '12px', borderRadius: '4px', width: '100%', marginTop: '4px' }} onClick={() => buyNft(nft)}>Buy</button>
</div>
</div>
))
}
</div>
</div>
</div>
)
}
3.链端使用
1. 链端部分我们使用Hardhat框架进行智能合约开发,首先进入/ChatGo文件夹中,安装项目依赖
npm install
2. 依赖安装后,项目启动本地Hardhat节点
npx hardhat node
3. 在本地网络运行时,在单独的终端窗口中将合约部署到本地网络
npx hardhat run scripts/deploy.js --network localhost