10 min de lecture
Votre flux Quicknode peut désormais diffuser vers plusieurs destinations simultanément à partir d'un seul pipeline. Pas de flux en double, pas de divergence de configuration et pas de frais supplémentaires par destination. Les destinations disponibles par flux varient selon la formule choisie. En savoir plus.
Aperçu​
Les agents IA modernes intégrant la technologie blockchain deviennent de plus en plus puissants et polyvalents, qu'il s'agisse de récupérer des données sur la blockchain ou d'effectuer des échanges de jetons. Dans ce guide, découvrez comment créer un agent IA pour Discord à l'aide de JavaScript et de l'API OpenAI.
Nous récupérerons les données métriques par bloc depuis Quicknode Streams et les stockerons dans une instance PostgreSQL. Ensuite, un code JavaScript gérera les interactions entre les utilisateurs sur Discord et notre agent IA.
Ce dont vous aurez besoin​
- Un compte Quicknode (créez-en un ici)
- Une clé API OpenAPI
Ce que vous ferez​
- En savoir plus sur les agents IA
- Configurer un flux Quicknode
- Configurer l'agent IA avec Discord
- Lancer l'agent et récupérer les données de métriques par bloc depuis Quicknode Streams vers une instance PostgreSQL
| Dépendance | Version |
|---|---|
| nœud | 23.3.0 |
| axios | ^1.7.9 |
| discord.js | ^14.18.0 |
| dotenv | ^16.4.7 |
| openai | ^4.83.0 |
| pg | ^8.13.1 |
Configuration du flux de données​
Nous allons configurer un flux Quicknode afin d'obtenir des données de blocs filtrées. Consultez le code de filtrage des flux ci-dessous pour obtenir des métriques de blocs filtrées.
function main(stream) {
const data = stream.data ? stream.data : stream;
const block = Array.isArray(data) ? data[0] : data;
if (!block || !block.transactions) {
throw new Error('Invalid block data structure');
}
const metadata = stream.metadata || {};
const dataset = metadata.dataset;
const network = metadata.network;
const transactions = block.transactions;
const blockNumber = parseInt(block.number, 16);
const blockTimestampHex = block.timestamp;
const blockTimestamp = new Date(parseInt(blockTimestampHex, 16) * 1000).toISOString();
let totalGasPrice = BigInt(0);
let totalEthTransferred = BigInt(0);
let contractCreations = 0;
let largestTxValue = BigInt(0);
let largestTxHash = null;
const activeAddresses = new Set();
const uniqueTokens = new Set();
for (const tx of transactions) {
const gasPriceInWei = BigInt(tx.gasPrice);
totalGasPrice += gasPriceInWei;
const valueInWei = BigInt(tx.value);
totalEthTransferred += valueInWei;
if (valueInWei > largestTxValue) {
largestTxValue = valueInWei;
largestTxHash = tx.hash;
}
if (tx.from) activeAddresses.add(tx.from.toLowerCase());
if (tx.to) activeAddresses.add(tx.to.toLowerCase());
if (!tx.to) contractCreations++;
if (tx.to) uniqueTokens.add(tx.to.toLowerCase());
}
const averageGasPriceInWei = totalGasPrice / BigInt(transactions.length);
const averageGasPriceInGwei = Number(averageGasPriceInWei) / 1e9;
return {
blockNumber,
blockTimestamp,
dataset,
network,
transactionCount: transactions.length,
averageGasPriceInWei: averageGasPriceInWei.toString(),
averageGasPriceInGwei,
totalEthTransferred: (Number(totalEthTransferred) / 1e18).toString(),
activeAddressCount: activeAddresses.size,
contractCreations,
uniqueTokensInteracted: uniqueTokens.size,
largestTx: {
value: (Number(largestTxValue) / 1e18).toString(),
hash: largestTxHash,
},
};
}
Notre flux sera dirigé vers une base de données PostgreSQL ; Tembo est le fournisseur PostgreSQL utilisé dans la vidéo.
Configuration de l'agent IA avec Discord​
Notre agent IA fonctionnera comme un bot Discord ; vous pouvez créer votre bot Discord sur le portail des développeurs de Discord.
Créons un répertoire pour notre application JavaScript et définissons-le comme répertoire de travail :
mkdir BLOCK-METRICS-BOT
cd BLOCK-METRICS-BOT
Pour notre application JavaScript, nous devons tout d'abord installer plusieurs bibliothèques pour Discord, OpenAI, PostgreSQL et la gestion des variables d'environnement :
npm i axios discord.js openai pg dotenv
Créez ensuite les fichiers suivants dans votre répertoire racine :
BLOCK-METRICS-BOT/
├── .env
├── main.js
└── actions/
├── getMetrics.js
└── openaiHelper.js
Commençons par ajouter du code à nos fichiers ; vous pouvez également retrouver l'intégralité du code dans le monorepo des applications d'exemple.
DISCORD_TOKEN=votre_jeton_de_bot_Discord
OPENAI_API_KEY=clé_API_OpenAI
URL_DE_LA_BASE_DE_DONNÉES=postgres_sql_database_url
Remplacez les valeurs de chaque variable d'environnement comme il se doit ; regardez la vidéo pour savoir comment obtenir chaque valeur.
const { Pool } = require('pg');
// Initialize PostgreSQL connection
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: {
rejectUnauthorized: false, // Allow self-signed certificates
},
});
module.exports = async (blockNumber) => {
const query = `
SELECT data
FROM "block-metrics"
WHERE (data->>'blockNumber')::BIGINT = $1
`;
try {
const result = await pool.query(query, [blockNumber]);
if (result.rows.length > 0) {
return result.rows[0].data; // Data is already parsed JSON
} else {
return null;
}
} catch (err) {
console.error("Database query error:", err.message);
throw new Error("Failed to fetch block metrics.");
}
};
Le script ci-dessus permettra d'extraire des données de la base de données PostgreSQL.
const axios = require('axios');
const openaiEndpoint = 'https://api.openai.com/v1/chat/completions';
module.exports = async (prompt) => {
const MAX_RETRIES = 5; // Maximum retries for handling rate limits
let retryDelay = 1000; // Initial retry delay (in milliseconds)
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
try {
const response = await axios.post(
openaiEndpoint,
{
model: "gpt-4", // Use GPT-4 for higher quality responses
messages: [{ role: "user", content: prompt }],
max_tokens: 200, // Limit the response length to 200 tokens
},
{
headers: {
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
"Content-Type": "application/json",
},
}
);
// Log only rate limit details
console.log("Rate Limit Details:", {
remainingRequests: response.headers['x-ratelimit-remaining-requests'],
resetTime: response.headers['x-ratelimit-reset-requests'],
});
return response.data.choices[0].message.content.trim();
} catch (err) {
if (err.response?.status === 429) {
console.warn(`Rate limit hit. Retrying in ${retryDelay / 1000}s... (Attempt ${attempt}/${MAX_RETRIES})`);
await new Promise((resolve) => setTimeout(resolve, retryDelay));
retryDelay *= 2; // Increase delay exponentially
} else {
console.error("OpenAI API Error:", err.response?.data || err.message);
throw new Error("Failed to generate response.");
}
}
}
return "I'm currently experiencing high demand and cannot process your request. Please try again later.";
};
Ce script vous aidera Ă interagir avec l'API OpenAI et Ă traiter les requĂŞtes.
require('dotenv').config();
const { Client, GatewayIntentBits } = require('discord.js');
const getMetrics = require('./actions/getMetrics');
const openaiHelper = require('./actions/openaiHelper');
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent, // Required to read message content
],
});
// Thread-specific context storage
const THREAD_CONTEXT = new Map();
client.once('ready', () => {
console.log(`Logged in as ${client.user.tag}`);
});
client.on('messageCreate', async (message) => {
if (message.author.bot) return; // Ignore bot messages
// Handle follow-up queries in threads
if (message.channel.isThread()) {
const context = THREAD_CONTEXT.get(message.channel.id);
if (!context) {
message.channel.send("This thread has no active context. Please start a new query.");
return;
}
const includeTimestamp = /time|date|when|confirmed|timestamp/i.test(message.content);
const prompt = `You are Michael Scott, the quirky and often inappropriate boss from The Office.
You are answering questions about Ethereum block ${context.blockNumber}.
Here is the known data for block ${context.blockNumber}:
${JSON.stringify(context.blockData, null, 2)}
User's query: "${message.content}"
Respond as Michael Scott would: provide an accurate answer first, and then add a humorous remark in Michael's style.
${includeTimestamp ? `Mention the block timestamp (${context.blockData.blockTimestamp}) as part of the response.` : 'Do not mention the block timestamp unless explicitly asked.'}
Keep your response under 150 tokens.`;
try {
const response = await openaiHelper(prompt);
await message.reply(response);
} catch (error) {
console.error("OpenAI Error:", error.message);
message.reply("Uh-oh, looks like something went wrong. Classic Michael mistake!");
}
return;
}
const blockNumberMatch = message.content.match(/block(?:\s*number)?\s*(\d+)/i);
if (blockNumberMatch) {
const blockNumber = parseInt(blockNumberMatch[1], 10);
try {
const blockData = await getMetrics(blockNumber);
if (!blockData) {
message.channel.send(`No data found for block ${blockNumber}. That's what she said!`);
return;
}
const thread = await message.startThread({
name: `Block ${blockNumber} Query`,
autoArchiveDuration: 60,
});
THREAD_CONTEXT.set(thread.id, { blockNumber, blockData });
const includeTimestamp = /time|date|when|confirmed|timestamp/i.test(message.content);
const prompt = `You are Michael Scott, the quirky and often inappropriate boss from The Office.
You are answering questions about Ethereum block ${blockNumber}.
Here is the known data for block ${blockNumber}:
${JSON.stringify(blockData, null, 2)}
User's query: "${message.content}"
Respond as Michael Scott would: provide an accurate answer first, and then add a humorous remark in Michael's style.
${includeTimestamp ? `Mention the block timestamp (${blockData.blockTimestamp}) as part of the response.` : 'Do not mention the block timestamp unless explicitly asked.'}
Keep your response under 150 tokens.`;
const response = await openaiHelper(prompt);
await thread.send(response);
} catch (error) {
console.error("Error:", error.message);
message.channel.send(`I couldn't process your query for block ${blockNumber}.`);
}
} else {
const funnyResponses = [
"I'm sorry, I can't read your mind. Do you know how many times I've been asked to do that in the office? Just give me a block number!",
"This feels like a setup for 'that's what she said.' Anyway, I need a block number to work with.",
"No block number? That’s okay, I’ll just sit here awkwardly until you give me one.",
"Imagine I'm your assistant... but I need details. Which block are we talking about?",
"You’re lucky I’m not Dwight, or I’d make you fill out a block request form. Just give me the number!"
];
const followUp = "Could you please specify the block number you'd like to know about?";
message.channel.send(funnyResponses[Math.floor(Math.random() * funnyResponses.length)]);
setTimeout(() => {
message.channel.send(followUp);
}, 2000);
}
});
// Login to Discord
client.login(process.env.DISCORD_TOKEN);
Il s'agit du script principal qui gère les entrées de l'utilisateur sur Discord et traite les résultats à l'aide des scripts contenus dans actions/ répertoire.
Pour lancer l'agent, il suffit d'exécuter la commande main.js fichier.
node main.js
Une fois cette opération réussie, votre agent sur Discord devrait être prêt à répondre à vos questions concernant les indicateurs de blocs Ethereum.
Nous ❤️ les commentaires !
Faites-nous savoir si vous avez des commentaires ou des suggestions de nouveaux sujets. Nous serions ravis de vous entendre.
