Custom Actions

HyperAgent makes it easy to add custom actions for performing actions that might be too complex for a LLM to perform.

Here is an example to perform a search using exa. It takes 3 parameters

  • type: perform_search

  • actionParams: schema defining what parameters the custom action would require.

  • run: A function taking in a context, and the parameters defined in the actionParams to perform the actual action, in this case search using exa

Code

import "dotenv/config";
import HyperAgent from "@hyperbrowser/agent";
import {
  AgentActionDefinition,
  ActionContext,
  ActionOutput,
} from "@hyperbrowser/agent/types";
import chalk from "chalk";
import { ChatOpenAI } from "@langchain/openai";
import Exa from "exa-js";

import * as z from "zod";

const exaInstance = new Exa(process.env.EXA_API_KEY);

const searchSchema = z
  .object({
    search: z
      .string()
      .describe(
        "The search query for something you want to search about. Keep the search query concise and to-the-point."
      ),
  })
  .describe("Search and return the results for a given query.");

export const RunSearchActionDefinition: AgentActionDefinition = {
  type: "perform_search",
  actionParams: searchSchema,
  run: async function (
    ctx: ActionContext,
    params: z.infer<typeof searchSchema>
  ): Promise<ActionOutput> {
    const results = (await exaInstance.search(params.search, {})).results
      .map(
        (res) =>
          `title: ${res.title} || url: ${res.url} || relevance: ${res.score}`
      )
      .join("\n");

    return {
      success: true,
      message: `Succesfully performed search for query ${params.search}. Got results: \n${results}`,
    };
  },
};

async function runEval() {
  console.log(chalk.cyan.bold("\n===== Running Custom Tool Example ====="));

  const llm = new ChatOpenAI({
    apiKey: process.env.OPENAI_API_KEY,
    model: "gpt-4o",
  });

  const agent = new HyperAgent({
    llm: llm,
    debug: true,
    customActions: [RunSearchActionDefinition],
  });

  const result = await agent.executeTask(
    `Make me a trip plan for Tokyo. 
    Steps:
    
    - Peform search about the place and things to see there using the 'perform_search' tool.
    - Analyze part of the urls provided, filtering results for relevance, and information and collecting a subset of urls that you think warrant further examination.
    - For each page that you've 
        - Navigate to that url
        - Extract information about trip recommendations
        - You must do this in order. Navigate to a single page, and then perform extraction on that page. Do not perform multiple navigations one after another.
    - Narrow down on places based on uniqueness, frequency of recommendation, and whatever else you feel is valuable.
    - Return to me a list of places you recommend, and their details (if any)`,
    {
      debugOnAgentOutput: (agentOutput) => {
        console.log("\n" + chalk.cyan.bold("===== AGENT OUTPUT ====="));
        console.dir(agentOutput, { depth: null, colors: true });
        console.log(chalk.cyan.bold("===============") + "\n");
      },
      onStep: (step) => {
        console.log("\n" + chalk.cyan.bold(`===== STEP ${step.idx} =====`));
        console.dir(step, { depth: null, colors: true });
        console.log(chalk.cyan.bold("===============") + "\n");
      },
    }
  );
  await agent.closeAgent();
  console.log(chalk.green.bold("\nResult:"));
  console.log(chalk.white(result.output));
  return result;
}

(async () => {
  await runEval();
})().catch((error) => {
  console.error(chalk.red("Error:"), error);
  process.exit(1);
});

Last updated