πŸ“™ Understanding Neuronal Networks presale is now open - 20% off discount!

Multi Agent Systems in LangGraph (JavaScript version) – Part 1

Let’s dive into the process of creating and managing a team of AI agents. That’s right! Multiple agents working together, each with its own goals and tools, all collaborating to achieve a shared objective.

But why use multiple specialized agents instead of one general-purpose agent?

The key is reliability. The more straightforward and clearly defined an agent’s responsibilities, the more dependable it becomes. A study conducted by the LangChain team on using AI agents in production highlighted reliability as the most significant challenge: www.langchain.com/stateofaiagents.

Beyond reliability, there are several other compelling benefits to employing teams of specialized agents:

  • flexibility; each agent can leverage different models tailored to unique tasks and capabilities
  • speed and cost efficiency: specialized agents are optimized for their specific functions, reducing overhead
  • simplified debugging: troubleshooting becomes much easier when issues can be isolated within smaller, focused sub-parts of the system

By splitting tasks among specialized agents, we get a more intelligent, faster, and more reliable way to build cool AI systems.

If interested, you can read more about this in the paper AutoGen Enabling Next-Gen LLM Applications via Multi-Agent Conversation: https://arxiv.org/abs/2308.08155.

What are we going to build

During this chapter, we will build a team of AI agents that will work together to help the user to conduct online research.

The team of agents is made of:

  • the Researcher agent: it can go online and search the web using the Tavily API
  • the Graph Generator agent: it renders data in a chart using simple characters

For a given task, we can use one single agent, or both.

This very small team of agents will be managed by a third agent named the Supervisor. The role of this agent will be to invoke one of the other 2 agents.

This is how an output sample of this multi-agent agent system will look like:

USER PROMPT: What was the GDP of Italy, Japan, and Mexico in 2023?

--------------------------
ITA (2255) | ***********
JAP (4213) | ********************
MEX (1789) | *********
--------------------------

Here is the bar chart representing the GDP of Italy, Japan, and Mexico in 2023:
- **Italy**: $2,254.85 billion USD
- **Japan**: $4,212.95 billion USD
- **Mexico**: $1,788.89 billion USD

This chart helps to compare the economic output of these countries for that year.

For this particular case, both the Researcher agent and the Graph Generator agent were used to build the final output. However, there are cases that can be solved by only one single agent.

Also, remember that the data presented in the chart were scraped from the web, not taken from the training data for the LLM.

This is the file structure of our code:

/multi-agent
 β”œβ”€β”€ /etc
 β”‚   β”œβ”€β”€ utils.js
 β”œβ”€β”€ /agents
 β”‚   β”œβ”€β”€ chartGenerator.js
 β”‚   β”œβ”€β”€ researcher.js
 β”‚   β”œβ”€β”€ supervisor.js
 β”œβ”€β”€ .env
 β”œβ”€β”€ index.js
 β”œβ”€β”€ package-lock.json
 └── package.json

Each code sample will have a starting comment with the file location.

Let’s now get to work!

Building the Researcher Agent

One of the agents that will be part of our team will be the Researcher agent.

The scope of this agent is to use the Tavily search to perform online research.

Tavily is a search engine tailored for AI agents. This tool will allow our AI agents to do real-time, accurate search results tailored for LLMs and RAG.

The first thing we need to do is to generate an API Key for Tavily. You can do that by going to https://app.tavily.com/home, and after you sign in, you can get a free API key from the "API Key" section.

Once the Tavily API Key is generated, be sure to add it to the .env file:

OPENAI_API_KEY= your_key_here
TAVILY_API_KEY = your_key_here

Before getting started, we will create a utility file where we will place the common parts across all files:

// FILE: etc/utils.js

import * as dotenv from "dotenv"
import { ChatOpenAI } from "@langchain/openai"

dotenv.config()

const getLastMessage = ({ messages }) => messages[messages.length - 1]

const buildLLM = () => (new ChatOpenAI({
    modelName: "gpt-4o",
    temperature: 0
}))

export { getLastMessage, buildLLM }

We can build up the actual researcher tool once we have taken care of the utils file and the Tavily API key.

It will use a simple ReACT architecture, similar to the ones in the previous examples:

// FILE: agents/researcher.js

import { getLastMessage, buildLLM } from "../etc/utils.js"
import { TavilySearchResults } from "@langchain/community/tools/tavily_search"
import { SystemMessage } from "@langchain/core/messages"
import { END, START, Annotation, messagesStateReducer, 
    StateGraph } from "@langchain/langgraph"
import { ToolNode } from "@langchain/langgraph/prebuilt"

const llm = buildLLM()

const researcherAnnotation = Annotation.Root({
  messages: Annotation({
    reducer: messagesStateReducer,
    default: () => [
        new SystemMessage("You are a web researcher. You may use the" + 
            " Tavily search engine to search the web for important" +
            " information, so the Chart Generator in your team can" +
            " make useful plots.")
    ]
  })
})

const tavilyTool = new TavilySearchResults()
const tools = [tavilyTool]
const toolNode = new ToolNode(tools)

const callModel = async (state) => {
    const { messages } = state
    const result = await llm.bindTools(tools).invoke(messages)
    return { messages: [result] }
}

const shouldContinue = (state) => {
    const lastMessage = getLastMessage(state)
    const didAICalledAnyTools = lastMessage._getType() === "ai" &&
        lastMessage.tool_calls?.length
    return didAICalledAnyTools ? "tools" : END
}

const researcherGraph = new StateGraph(researcherAnnotation)
    .addNode("agent", callModel)
    .addNode("tools", toolNode)
    .addEdge(START, "agent")
    .addEdge("tools", "agent")
    .addConditionalEdges("agent", shouldContinue, ["tools", END])

const researcherAgent = researcherGraph.compile()

export { researcherAgent }

Let’s give the reseacher agent a test drive:

import { researcherAgent } from "./agents/researcher.js"
import { HumanMessage } from "@langchain/core/messages"
const result  = await researcherAgent.invoke({
    messages: [
        new HumanMessage({
            content: "Who was Abraham Lincoln?"
        })
    ]
})
console.log(result)

// AIMessage {
// "id": "chatcmpl-AbTIx2LnP4QdJsuWVfm75b6GQYWY7",
// "content": "The current exchange rate for EUR/USD is 1.0564 USD."
// "additional_kwargs": {} 

What’s important to note here is the fact that the results given by the agent are real time, based on an actual web search, and not from the model’s initial training data.

πŸ“– Neural Networks from Scratch - Presale

I'm writing a book about the timeless foundational concepts of neural networks for JavaScript developers. Go from if-else to weights and biases by building tiny AI models from scratch!

πŸ“– Neural Networks from Scratch - Presale

I'm writing a book about the timeless foundational concepts of neural networks for JavaScript developers. Go from if-else to weights and biases by building tiny AI models from scratch!


Leave a Reply

Your email address will not be published. Required fields are marked *