Rumah  >  Artikel  >  hujung hadapan web  >  Mencipta Ejen ReAct dari awal dengan nodeJS ( carian wikipedia )

Mencipta Ejen ReAct dari awal dengan nodeJS ( carian wikipedia )

DDD
DDDasal
2024-09-24 10:30:10877semak imbas

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

pengenalan

Kami akan mencipta ejen AI yang mampu mencari Wikipedia dan menjawab soalan berdasarkan maklumat yang ditemuinya. Ejen ReAct (Reason and Act) ini menggunakan API AI Generatif Google untuk memproses pertanyaan dan menjana respons. Ejen kami akan dapat:

  1. Cari Wikipedia untuk maklumat yang berkaitan.
  2. Ekstrak bahagian tertentu daripada halaman Wikipedia.
  3. Sebab tentang maklumat yang dikumpul dan rumuskan jawapan.

[2] Apakah itu Ejen ReAct?

Ejen ReAct ialah jenis ejen tertentu yang mengikuti kitaran Refleksi-Tindakan. Ia mencerminkan tugas semasa, berdasarkan maklumat yang tersedia dan tindakan yang boleh dilakukannya, dan kemudian memutuskan tindakan yang perlu diambil atau sama ada untuk menyelesaikan tugasan itu.

[3] Merancang Ejen

3.1 Alat yang Diperlukan

  • Node.js
  • Pustaka Axios untuk permintaan HTTP
  • API AI Generatif Google (gemini-1.5-flash)
  • API Wikipedia

3.2 Struktur Ejen

Ejen ReAct kami akan mempunyai tiga keadaan utama:

  1. FIKIR (Refleksi)
  2. TINDAKAN (Pelaksanaan)
  3. JAWAPAN (Respons)

[4] Pelaksana Ejen

Mari bina Ejen ReAct langkah demi langkah, menyerlahkan setiap keadaan.

4.1 Persediaan Awal

Mula-mula, sediakan projek dan pasang kebergantungan:

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

Buat fail .env pada akar projek:

GOOGLE_AI_API_KEY=your_api_key_here

4.2 Mencipta Fail Tools.js

Buat Tools.js dengan kandungan berikut:

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 Mencipta Fail ReactAgent.js

Buat ReactAgent.js dengan kandungan berikut:

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 Menjalankan ejen (index.js)

Buat index.js dengan kandungan berikut:

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] Bagaimana Bahagian Wikipedia Berfungsi

Interaksi dengan Wikipedia dilakukan dalam dua langkah utama:

  1. Carian awal (fungsi wikipedia):

    • Membuat permintaan kepada API carian Wikipedia.
    • Mengembalikan sehingga 4 hasil carian yang berkaitan untuk pertanyaan.
    • Untuk setiap hasil, ia mengambil bahagian halaman.
  2. Carian terperinci (wikipedia_with_pageId function):

    • Menggunakan ID halaman dan ID bahagian untuk mengambil kandungan tertentu.
    • Mengembalikan teks bahagian yang diminta.

Proses ini membolehkan ejen mendapat gambaran keseluruhan topik yang berkaitan dengan pertanyaan dahulu dan kemudian menyelam lebih dalam ke bahagian tertentu seperti yang diperlukan.

[6] Contoh Aliran Pelaksanaan

  1. Pengguna bertanya soalan.
  2. Ejen memasuki keadaan FIKIR dan memikirkan soalan.
  3. Ia memutuskan untuk mencari Wikipedia dan memasuki keadaan ACTION.
  4. Melaksanakan fungsi wikipedia dan memperoleh hasil.
  5. Kembali ke keadaan FIKIR untuk merenung keputusan.
  6. Boleh memutuskan untuk mencari butiran lanjut atau pendekatan yang berbeza.
  7. Mengulangi kitaran PEMIKIRAN dan TINDAKAN seperti yang diperlukan.
  8. Apabila ia mempunyai maklumat yang mencukupi, ia memasuki keadaan JAWAPAN.
  9. Menjana jawapan akhir berdasarkan semua maklumat yang dikumpul.
  10. Masuk gelung tak terhingga apabila wikipedia tidak mempunyai data untuk dikumpulkan. Betulkan dengan pemasa =P

[7] Pertimbangan Akhir

  • Struktur modular membolehkan penambahan alat atau API baharu dengan mudah.
  • Adalah penting untuk melaksanakan pengendalian ralat dan had masa/lelaran untuk mengelakkan gelung tidak terhingga atau penggunaan sumber yang berlebihan.
  • Gunakan Suhu : 99999 lol

Atas ialah kandungan terperinci Mencipta Ejen ReAct dari awal dengan nodeJS ( carian wikipedia ). Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn