Home >Web Front-end >JS Tutorial >Creating a ReAct AI Agent with Node.js (Wikipedia search) en
We will create an AI agent capable of searching Wikipedia and answering questions based on the information collected.
This ReAct (Reasoning and Action) Agent uses the Google Generative AI API to process queries and generate responses.
Our agent will be able to:
A ReAct Agent is a specific type of agent that follows a Reflection-Action cycle. It reflects on the current task, based on the information available and the actions it can take, and then decides what action to take or whether to complete the task.
Our ReAct Agent will have three main states:
The thinking state is the moment in which ReactAgent will reflect on the information collected and decide what the next step should be.
async thought() { // ... }
In the action state, the agent performs one of the available functions based on the previous Thought.
Note that there is the action (execution) and the decision (which action).
async action() { // chama a decisão // executa a ação e retorna um ActionResult } async decideAction() { // Chama o LLM com base no Pensamento (reflexão) para formatar e adequar a chamada de função. // Procure por um modo de função-ferramenta na [documentação da API do Google](https://ai.google.dev/gemini-api/docs/function-calling) }
Let's build the ReAct Agent step by step, highlighting each state.
First, configure the project and install the dependencies:
mkdir projeto-agente-react cd projeto-agente-react npm init -y npm install axios dotenv @google/generative-ai
Create a .env file in the project root:
GOOGLE_AI_API_KEY=sua_chave_api_aqui
FREE API Key here
This file is the JavaScript file that Node.js will use to perform an API call to Wikipedia.
We describe the contents of this file in FunctionDescription.
Create Tools.js with the following content:
const axios = require("axios"); class Tools { static async wikipedia(q) { try { const response = await axios.get("https://pt.wikipedia.org/w/api.php", { params: { action: "query", list: "search", srsearch: q, srwhat: "text", format: "json", srlimit: 4, }, }); const results = await Promise.all( response.data.query.search.map(async (searchResult) => { const sectionResponse = await axios.get( "https://pt.wikipedia.org/w/api.php", { params: { action: "parse", pageid: searchResult.pageid, prop: "sections", format: "json", }, }, ); const sections = Object.values( sectionResponse.data.parse.sections, ).map((section) => `${section.index}, ${section.line}`); return { pageTitle: searchResult.title, snippet: searchResult.snippet, pageId: searchResult.pageid, sections: sections, }; }), ); return results .map( (result) => `Snippet: ${result.snippet}\nPageId: ${result.pageId}\nSections: ${JSON.stringify(result.sections)}`, ) .join("\n\n"); } catch (error) { console.error("Error fetching from Wikipedia:", error); return "Error fetching data from Wikipedia"; } } static async wikipedia_with_pageId(pageId, sectionId) { if (sectionId) { const response = await axios.get("https://pt.wikipedia.org/w/api.php", { params: { action: "parse", format: "json", pageid: parseInt(pageId), prop: "wikitext", section: parseInt(sectionId), disabletoc: 1, }, }); return Object.values(response.data.parse?.wikitext ?? {})[0]?.substring( 0, 25000, ); } else { const response = await axios.get("https://pt.wikipedia.org/w/api.php", { params: { action: "query", pageids: parseInt(pageId), prop: "extracts", exintro: true, explaintext: true, format: "json", }, }); return Object.values(response.data?.query.pages)[0]?.extract; } } } module.exports = Tools;
Create ReactAgent.js with the following content:
require("dotenv").config(); const { GoogleGenerativeAI } = require("@google/generative-ai"); const Tools = require("./Tools"); const genAI = new GoogleGenerativeAI(process.env.GOOGLE_AI_API_KEY); class ReactAgent { constructor(query, functions) { this.query = query; this.functions = new Set(functions); this.state = "THOUGHT"; this._history = []; this.model = genAI.getGenerativeModel({ model: "gemini-1.5-flash", temperature: 1.8, }); } async run() { this.pushHistory(`**Tarefa: ${this.query} **`); try { return await this.step(); } catch (e) { console.error("Erro durante a execução:", e); return "Desculpe, não consegui processar sua solicitação."; } } async step() { const colors = { reset: "\x1b[0m", yellow: "\x1b[33m", red: "\x1b[31m", cyan: "\x1b[36m", }; console.log("===================================="); console.log( `Next Movement: ${ this.state === "THOUGHT" ? colors.yellow : this.state === "ACTION" ? colors.red : this.state === "ANSWER" ? colors.cyan : colors.reset }${this.state}${colors.reset}`, ); console.log(`Last Movement: ${this.history[this.history.length - 1]}`); console.log("===================================="); switch (this.state) { case "THOUGHT": return await this.thought(); break; case "ACTION": return await this.action(); break; case "ANSWER": return await this.answer(); } } async thought() { const funcoesDisponiveis = JSON.stringify(Array.from(this.functions)); const contextoHistorico = this.history.join("\n"); const prompt = `Sua Tarefa é ${this.consulta} O Contexto posui todas as reflexões que você fez até agora e os ResultadoAção que coletou. AçõesDisponíveis são funções que você pode chamar sempre que precisar de mais dados. Contexto: "${contextoHistorico}" << AçõesDisponíveis: "${funcoesDisponiveis}" << Tarefa: "${this.consulta}" << Reflita sobre Sua Tarefa usando o Contexto, ResultadoAção e AçõesDisponíveis para encontrar seu próximo_passo. Imprima seu próximo_passo com um Pensamento ou Finalize Cumprindo Sua Tarefa caso tenha as informações disponíveis`; const thought = await this.promptModel(prompt); this.pushHistory(`\n **${thought.trim()}**`); if ( thought.toLowerCase().includes("cumprida") || thought.toLowerCase().includes("cumpra") || thought.toLowerCase().includes("cumprindo") || thought.toLowerCase().includes("finalizar") || thought.toLowerCase().includes("finalizando") || thought.toLowerCase().includes("finalize") || thought.toLowerCase().includes("concluída") ) { this.state = "ANSWER"; } else { this.state = "ACTION"; } return this.step(); } async action() { const action = await this.decideAction(); this.pushHistory(`** Ação: ${action} **`); const result = await this.executeFunctionCall(action); this.pushHistory(`** ResultadoAção: ${result} **`); this.state = "THOUGHT"; return this.step(); } async decideAction() { const availableFunctions = JSON.stringify(Array.from(this.functions)); const historyContext = this.history; const prompt = `Reflita sobre o Pensamento, Consulta e Ações Disponíveis ${historyContext[historyContext.length - 2]} Pensamento <<< ${historyContext[historyContext.length - 1]} Consulta: "${this.query}" Ações Disponíveis: ${availableFunctions} Retorne apenas a função,parâmetros separados por vírgula. Exemplo: "wikipedia,ronaldinho gaucho,1450"`; const decision = await this.promptModel(prompt); return decision.replace(/`/g, "").trim(); } async answer() { const historyContext = this.history.join("\n"); const prompt = `Com base no seguinte contexto, forneça uma resposta completa e detalhada para a tarefa: ${this.query}. Contexto: ${historyContext} Tarefa: "${this.query}"`; const finalAnswer = await this.promptModel(prompt); return finalAnswer; } async promptModel(prompt) { const result = await this.model.generateContent(prompt); const response = await result.response; return response.text(); } async executeFunctionCall(functionCall) { const [functionName, ...args] = functionCall.split(","); const func = Tools[functionName.trim()]; if (func) { return await func.call(null, ...args); } throw new Error(`Função ${functionName} não encontrada`); } pushHistory(value) { this._history.push(value); } get history() { return this._history; } } module.exports = ReactAgent;
Create index.js with the following content:
const ReactAgent = require("./ReactAgentPTBR.js"); async function main() { const query = "Que clubes ronaldinho gaúcho jogou para?"; // const query = "Quais os bairros de Joinville?"; // const query = "Qual a capital da frança?"; const functions = [ [ "wikipedia", "params: query", "Busca semântica na Wikipedia API por pageId e sectionIds >> \n ex: Pontos turísticos de são paulo \n São Paulo é uma cidade com muitos pontos turísticos, pageId, sections : []", ], [ "wikipedia_with_pageId", "params: pageId, sectionId", "Busca na Wikipedia API usando pageId e sectionIndex como parametros. \n ex: 1500,1234 \n Informações sobre a seção blablalbal", ], ]; const agent = new ReactAgent(query, functions); const result = await agent.run(); console.log("Resultado do Agente:", result); } main().catch(console.error);
When trying to add a new tool or function, be sure to describe it well.
In our example, this is already done and added to our ReActAgent class when calling a new Instance.
const functions = [ [ "google", // nomeDaFuncao "params: query", // NomeDoParâmetroLocal "Pesquisa semântica na API da Wikipedia por snippets, pageIds e sectionIds >> \n ex: Quando o Brasil foi colonizado? \n O Brasil foi colonizado em 1500, pageId, sections : []", // breve explicação e exemplo (isso será encaminhado para o LLM) ] ];
Interaction with Wikipedia is done in two main steps:
Initial search (wikipedia function):
Detailed search (wikipedia_with_pageId function):
This process allows the agent to first get an overview of topics related to the query and then drill down into specific sections as needed.
The above is the detailed content of Creating a ReAct AI Agent with Node.js (Wikipedia search) en. For more information, please follow other related articles on the PHP Chinese website!