Introdução
Vamos criar um agente de IA capaz de pesquisar na Wikipedia e responder perguntas com base nas informações coletadas.
Este Agente ReAct (Raciocínio e Ação) usa a API do Google Generative AI para processar consultas e gerar respostas.
Nosso agente será capaz de:
- Pesquisar informações relevantes na Wikipedia.
- Extrair seções específicas das páginas da Wikipedia.
- Raciocinar sobre as informações coletadas e formular respostas.
[2] O que é um Agente ReAct?
Um Agente ReAct é um tipo específico de agente que segue um ciclo de Reflexão-Ação. Ele reflete sobre a tarefa atual, com base nas informações disponíveis e nas ações que pode realizar, e então decide qual ação tomar ou se deve concluir a tarefa.
[3] Planejando o Agente
3.1 Ferramentas Necessárias
- Node.js
- Biblioteca Axios para requisições HTTP
- API do Google Generative AI (gemini-1.5-flash)
- API da Wikipedia
3.2 Estrutura do Agente
Nosso Agente ReAct terá três estados principais:
- THOUGHT (Reflexão)
- ACTION (Execução)
- ANSWER (Resposta)
3.3 Estado de Pensamento
O estado de pensamento é o momento em que o ReactAgent refletirá sobre as informações coletadas e decidirá qual deve ser o próximo passo.
async thought() {
// ...
}
3.4 Estado de Ação (ACTION)
No estado de ação, o agente executa uma das funções disponíveis com base no Pensamento anterior.
Note que há a ação (execução) e a decisão (qual ação).
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)
}
[4] Implementando o Agente
Vamos construir o Agente ReAct passo a passo, destacando cada estado.
4.1 Configuração Inicial
Primeiro, configure o projeto e instale as dependências:
mkdir projeto-agente-react
cd projeto-agente-react
npm init -y
npm install axios dotenv @google/generative-ai
Crie um arquivo .env
na raiz do projeto:
GOOGLE_AI_API_KEY=sua_chave_api_aqui
Chave de API GRATUITA aqui
4.2 Declaração de Funções
Este arquivo é o arquivo JavaScript que o Node.js usará para executar uma chamada de API para a Wikipedia.
Descrevemos o conteúdo deste arquivo em FunctionDescription.
Crie Tools.js
com o seguinte conteúdo:
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;
4.3 Criando o Arquivo ReactAgent.js
Crie ReactAgent.js
com o seguinte conteúdo:
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;
4.4 Executando o Agente e Explicando as Ferramentas Disponíveis (index.js)
Crie index.js
com o seguinte conteúdo:
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);
Descrição de Função
Ao tentar adicionar uma nova ferramenta ou função, certifique-se de descrevê-la bem.
Em nosso exemplo, isso já está feito e adicionado à nossa classe ReActAgent ao chamar uma nova Instância.
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)
]
];
[5] Como Funciona a Parte da Wikipedia
A interação com a Wikipedia é feita em duas etapas principais:
-
Pesquisa inicial (função wikipedia):
- Faz uma requisição para a API de pesquisa da Wikipedia.
- Retorna até 4 resultados relevantes para a consulta.
- Para cada resultado, busca as seções da página.
-
Pesquisa detalhada (função wikipedia_with_pageId):
- Usa o ID da página e o ID da seção para buscar conteúdo específico.
- Retorna o texto da seção solicitada.
Este processo permite que o agente primeiro obtenha uma visão geral dos tópicos relacionados à consulta e depois se aprofunde em seções específicas conforme necessário.
[6] Exemplo de Fluxo de Execução
- O usuário faz uma pergunta.
- O agente entra no estado THOUGHT e reflete sobre a pergunta.
- Ele decide pesquisar na Wikipedia e entra no estado ACTION.
- Executa a função wikipedia e obtém resultados.
- Retorna ao estado THOUGHT para refletir sobre os resultados.
- Pode decidir buscar mais detalhes ou uma abordagem diferente.
- Repete o ciclo THOUGHT e ACTION conforme necessário.
- Quando tem informações suficientes, entra no estado ANSWER.
- Gera uma resposta final baseada em todas as informações coletadas.
- Entra em loop infinito sempre que a Wikipedia não tiver os dados para coletar. Corrija isso com um temporizador =P
[7] Considerações Finais
- A estrutura modular permite fácil adição de novas ferramentas ou APIs.
- É importante implementar tratamento de erros e limites de tempo/iteração para evitar loops infinitos ou uso excessivo de recursos.
- Este exemplo usa temperatura 2. Quanto menor a temperatura, menos criativo o agente se torna durante as iterações. Experimente para perceber a influência da temperatura nos LLMs.
Top comments (0)