>  기사  >  웹 프론트엔드  >  nodeJS를 사용하여 처음부터 ReAct 에이전트 만들기(wikipedia 검색)

nodeJS를 사용하여 처음부터 ReAct 에이전트 만들기(wikipedia 검색)

DDD
DDD원래의
2024-09-24 10:30:10755검색

Creating a ReAct Agent from the scratch with nodeJS ( wikipedia search )

소개

Wikipedia를 검색하고 찾은 정보를 바탕으로 질문에 답할 수 있는 AI 에이전트를 만들어 보겠습니다. 이 ReAct(Reason and Act) 에이전트는 Google Generative AI API를 사용하여 쿼리를 처리하고 응답을 생성합니다. 우리 대리인은 다음을 수행할 수 있습니다.

  1. 관련 정보는 Wikipedia를 검색하세요.
  2. Wikipedia 페이지에서 특정 섹션을 추출합니다.
  3. 수집된 정보에 대한 이유와 답변을 작성합니다.

[2] ReAct Agent란 무엇인가요?

ReAct Agent는 Reflection-Action 주기를 따르는 특정 유형의 에이전트입니다. 사용 가능한 정보와 수행할 수 있는 작업을 기반으로 현재 작업을 반영한 다음 수행할 작업 또는 작업 종료 여부를 결정합니다.

[3] 에이전트 계획

3.1 필수 도구

  • Node.js
  • HTTP 요청용 Axios 라이브러리
  • Google Generative AI API(gemini-1.5-flash)
  • 위키피디아 API

3.2 에이전트 구조

ReAct 에이전트에는 세 가지 주요 상태가 있습니다.

  1. 생각(반성)
  2. ACTION(실행)
  3. 답변(응답)

[4] 에이전트 구현

각 상태를 강조하면서 ReAct Agent를 단계별로 구축해 보겠습니다.

4.1 초기 설정

먼저 프로젝트를 설정하고 종속 항목을 설치합니다.

mkdir react-agent-project
cd react-agent-project
npm init -y
npm install axios dotenv @google/generative-ai

프로젝트 루트에 .env 파일을 만듭니다.

GOOGLE_AI_API_KEY=your_api_key_here

4.2 Tools.js 파일 생성

다음 콘텐츠로 Tools.js를 만듭니다.

const axios = require("axios");

class Tools {
  static async wikipedia(q) {
    try {
      const response = await axios.get("https://en.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://en.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://en.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://en.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 ReactAgent.js 파일 생성

다음 콘텐츠로 ReactAgent.js를 만듭니다.

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: 2,
    });
  }

  get history() {
    return this._history;
  }

  pushHistory(value) {
    this._history.push(`\n ${value}`);
  }

  async run() {
    this.pushHistory(`**Task: ${this.query} **`);
    try {
      return await this.step();
    } catch (e) {
      if (e.message.includes("exhausted")) {
        return "Sorry, I'm exhausted, I can't process your request anymore. ><";
      }
      return "Unable to process your request, please try again? ><";
    }
  }

  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":
        await this.thought();
        break;
      case "ACTION":
        await this.action();
        break;
      case "ANSWER":
        await this.answer();
        break;
    }
  }

  async promptModel(prompt) {
    const result = await this.model.generateContent(prompt);
    const response = await result.response;
    return response.text();
  }

  async thought() {
    const availableFunctions = JSON.stringify(Array.from(this.functions));
    const historyContext = this.history.join("\n");
    const prompt = `Your task to FullFill ${this.query}.
Context contains all the reflection you made so far and the ActionResult you collected.
AvailableActions are functions you can call whenever you need more data.

Context: "${historyContext}" <<

AvailableActions: "${availableFunctions}" <<

Task: "${this.query}" <<

Reflect uppon Your Task using Context, ActionResult and AvailableActions to find your next_step.
print your next_step with a Thought or FullFill Your Task `;

    const thought = await this.promptModel(prompt);
    this.pushHistory(`\n **${thought.trim()}**`);
    if (
      thought.toLowerCase().includes("fullfill") ||
      thought.toLowerCase().includes("fulfill")
    ) {
      this.state = "ANSWER";
      return await this.step();
    }
    this.state = "ACTION";
    return await this.step();
  }

  async action() {
    const action = await this.decideAction();
    this.pushHistory(`** Action: ${action} **`);
    const result = await this.executeFunctionCall(action);
    this.pushHistory(`** ActionResult: ${result} **`);
    this.state = "THOUGHT";
    return await this.step();
  }

  async decideAction() {
    const availableFunctions = JSON.stringify(Array.from(this.functions));
    const historyContext = this.history;
    const prompt = `Reflect uppon the Thought, Query and AvailableActions

    ${historyContext[historyContext.length - 2]}

    Thought <<< ${historyContext[historyContext.length - 1]}

    Query: "${this.query}"

    AvailableActions: ${availableFunctions}

    output only the function,parametervalues separated by a comma. For example: "wikipedia,ronaldinho gaucho, 1450"`;

    const decision = await this.promptModel(prompt);
    return `${decision.replace(/`/g, "").trim()}`;
  }

  async executeFunctionCall(functionCall) {
    const [functionName, ...args] = functionCall.split(",");
    const func = Tools[functionName.trim()];
    if (func) {
      return await func.call(null, ...args);
    }
    throw new Error(`Function ${functionName} not found`);
  }

  async answer() {
    const historyContext = this.history;
    const prompt = `Based on the following context, provide a complete, detailed and descriptive formated answer for the Following Task: ${this.query} .

Context:
${historyContext}

Task: "${this.query}"`;

    const finalAnswer = await this.promptModel(prompt);
    this.history.push(`Answer: ${this.finalAnswer}`);
    console.log("WE WILL ANSWER >>>>>>>", finalAnswer);
    return finalAnswer;
  }
}

module.exports = ReActAgent;

4.4 에이전트 실행(index.js)

다음 콘텐츠로 index.js를 만듭니다.

const ReActAgent = require("./ReactAgent.js");

async function main() {
  const query = "What does England border with?";
  const functions = [
    [
      "wikipedia",
      "params: query",
      "Semantic Search Wikipedia API for snippets, pageIds and sectionIds >> \n ex: Date brazil has been colonized? \n Brazil was colonized at 1500, pageId, sections : []",
    ],
    [
      "wikipedia_with_pageId",
      "params : pageId, sectionId",
      "Search Wikipedia API for data using a pageId and a sectionIndex as params.  \n ex: 1500, 1234 \n Section information about blablalbal",
    ],
  ];

  const agent = new ReActAgent(query, functions);
  try {
    const result = await agent.run();
    console.log("THE AGENT RETURN THE FOLLOWING >>>", result);
  } catch (e) {
    console.log("FAILED TO RUN T.T", e);
  }
}

main().catch(console.error);

[5] Wikipedia 부분의 작동 방식

Wikipedia와의 상호작용은 두 가지 주요 단계로 이루어집니다.

  1. 초기 검색(위키피디아 기능):

    • Wikipedia 검색 API에 요청합니다.
    • 검색어에 대해 최대 4개의 관련 결과를 반환합니다.
    • 각 결과에 대해 페이지 섹션을 가져옵니다.
  2. 상세 검색(wikipedia_with_pageId 기능):

    • 페이지 ID와 섹션 ID를 사용하여 특정 콘텐츠를 가져옵니다.
    • 요청된 섹션의 텍스트를 반환합니다.

이 프로세스를 통해 상담원은 먼저 검색어와 관련된 주제의 개요를 확인한 다음 필요에 따라 특정 섹션을 더 자세히 살펴볼 수 있습니다.

[6] 실행 흐름 예시

  1. 사용자가 질문을 합니다.
  2. 에이전트가 THOUGHT 상태에 들어가 질문에 대해 반성합니다.
  3. 위키피디아를 검색하기로 결정하고 ACTION 상태로 들어갑니다.
  4. 위키피디아 기능을 실행하고 결과를 얻습니다.
  5. 결과를 반영하기 위해 THOUGHT 상태로 돌아갑니다.
  6. 자세한 내용을 검색하거나 다른 접근 방식을 결정할 수도 있습니다.
  7. 필요에 따라 생각과 행동 주기를 반복합니다.
  8. 정보가 충분하면 ANSWER 상태로 들어갑니다.
  9. 수집된 모든 정보를 바탕으로 최종 답변을 생성합니다.
  10. 위키피디아에 수집할 데이터가 없을 때마다 무한 루프에 들어갑니다. 타이머로 수정하세요 =P

[7] 최종 고려 사항

  • 모듈형 구조를 통해 새로운 도구나 API를 쉽게 추가할 수 있습니다.
  • 무한 루프나 과도한 리소스 사용을 방지하려면 오류 처리 및 시간/반복 제한을 구현하는 것이 중요합니다.
  • 사용온도 : 99999 ㅋㅋ

위 내용은 nodeJS를 사용하여 처음부터 ReAct 에이전트 만들기(wikipedia 검색)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.