Menu Docs
Página inicial do Docs
/ /

Construa um agente de IA com LangGraph.js e o MongoDB Atlas

Você pode integrar o MongoDB Atlas ao LangGraph.js para construir agentes de IA. Este tutorial demonstra como construir um agente com LangGraph.js e MongoDB Vector Search que pode responder a perguntas sobre seus dados.

Especificamente, você executa as seguintes ações:

  1. Configure o ambiente.

  2. Configure seu cluster MongoDB .

  3. Construa o agente, incluindo as ferramentas do agente.

  4. Adicione memória ao agente.

  5. Crie um servidor e teste o agente.

Trabalhe com o código deste tutorial clonando o repositório do Github.

Antes de começar, certifique-se de ter o seguinte:

  • Um dos seguintes tipos de cluster MongoDB :

    • Um Atlas cluster executando o MongoDB versão 6.0.11, 7.0.2 ou posterior. Garanta que seu endereço IP esteja incluído na lista de acessodo seu projeto Atlas.

    • Um sistema local do Atlas criado utilizando o Atlas CLI. Para saber mais, consulte Criar uma implantação de Atlas local.

    • Um cluster MongoDB Community ou Enterprise com Search e Vector Search instalados.

  • npm e Node.js instalados.

  • Uma chave de API do Voyage AI. Para criar uma chave de API, consulte Chaves de API do modelo.

  • Uma chave de API da OpenAI. Você deve ter uma conta da OpenAI com créditos disponíveis para solicitações de API. Para aprender mais sobre como registrar uma conta OpenAI, consulte o website de API OpenAI.

Observação

Este tutorial utiliza modelos de OpenAI e Voyage AI, mas você pode modificar o código para utilizar seus modelos de escolha.

Para configurar o ambiente, conclua as seguintes etapas:

1

Crie um novo diretório de projeto e execute os seguintes comandos no projeto para instalar as dependências necessárias:

npm init -y
npm i -D typescript ts-node @types/express @types/node
npx tsc --init
npm i langchain @langchain/langgraph @langchain/mongodb @langchain/community @langchain/langgraph-checkpoint-mongodb dotenv express mongodb zod

Observação

Seu projeto usa a seguinte estrutura:

├── .env
├── index.ts
├── agent.ts
├── seed-database.ts
├── package.json
├── tsconfig.json
2

Crie um arquivo .env na raiz do seu projeto e adicione suas chaves de API e string de conexão:

OPENAI_API_KEY = "<openai-api-key>"
MONGODB_URI = "<connection-string>"
VOYAGEAI_API_KEY = "<voyage-api-key>"

Substitua <connection-string> pela string de conexão do seu cluster do Atlas ou da implantação local do Atlas.

Sua string de conexão deve usar o seguinte formato:

mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net

Para saber mais, consulte Conectar a um cluster via drivers.

Sua string de conexão deve usar o seguinte formato:

mongodb://localhost:<port-number>/?directConnection=true

Para saber mais, consulte Connection strings.

Você pode seguir este tutorial assistindo ao vídeo.

Duração: 30 minutos

Nesta seção, você configura e ingere dados de amostra em seu cluster MongoDB para habilitar a pesquisa vetorial sobre seus dados.

1

Crie um arquivo index.ts que estabeleça uma conexão com seu cluster MongoDB :

import { MongoClient } from "mongodb";
import 'dotenv/config';
const client = new MongoClient(process.env.MONGODB_URI as string);
async function startServer() {
try {
await client.connect();
await client.db("admin").command({ ping: 1 });
console.log("Pinged your deployment. You successfully connected to MongoDB!");
// ... rest of the server setup
} catch (error) {
console.error("Error connecting to MongoDB:", error);
process.exit(1);
}
}
startServer();
2

Crie um script seed-database.ts para gerar e armazenar registros de amostra de funcionários. Este script executa as seguintes ações:

  • Define um esquema para registros de empregados.

  • Cria uma função para gerar dados de funcionário de amostra usando o LLM.

  • Processa cada registro para criar um resumo de texto a ser usado para incorporações.

  • Usa a integração MongoDB MongoDB MongoDB para inicializar seu agrupamento MongoDB como um armazenamento de vetor. Este componente gera incorporações vetoriais e armazena os documentos em seu namespace do hr_database.employees.

3
npx ts-node seed-database.ts
Pinged your deployment. You successfully connected to MongoDB!
Generating synthetic data...
Successfully added database record: {
acknowledged: true,
insertedId: new ObjectId('685d89d966545cfb242790f0')
}
Successfully added database record: {
acknowledged: true,
insertedId: new ObjectId('685d89d966545cfb242790f1')
}
Successfully added database record: {
acknowledged: true,
insertedId: new ObjectId('685d89da66545cfb242790f2')
}
Successfully added database record: {
acknowledged: true,
insertedId: new ObjectId('685d89da66545cfb242790f3')
}

Dica

Após executar o script, você pode visualizar os dados propagados em seu cluster MongoDB navegando até o namespace hr_database.employees na UI do Atlas.

4

Siga as etapas para criar um índice do MongoDB Vector Search para o namespace hr_database.employees. Dê um nome ao índice vector_index e especifique a seguinte definição de índice:

{
"fields": [
{
"numDimensions": 1024,
"path": "embedding",
"similarity": "cosine",
"type": "vector"
}
]
}

Nesta seção, você cria um grafo para orquestrar o fluxo de trabalho do agente. O grafo define a sequência de etapas que o agente executa para responder a uma query.

1

Crie um novo arquivo chamado agent.ts no seu projeto e adicione o seguinte código para começar a configurar o agente. Você adicionará mais código à função assíncrona nas etapas subsequentes.

import { ChatOpenAI } from "@langchain/openai";
import { AIMessage, BaseMessage, HumanMessage } from "@langchain/core/messages";
import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts";
import { StateGraph } from "@langchain/langgraph";
import { Annotation } from "@langchain/langgraph";
import { tool } from "@langchain/core/tools";
import { ToolNode } from "@langchain/langgraph/prebuilt";
import { MongoDBSaver } from "@langchain/langgraph-checkpoint-mongodb";
import { MongoDBAtlasVectorSearch } from "@langchain/mongodb";
import { MongoClient } from "mongodb";
import { z } from "zod";
import "dotenv/config";
export async function callAgent(client: MongoClient, query: string, thread_id: string) {
// Define the MongoDB database and collection
const dbName = "hr_database";
const db = client.db(dbName);
const collection = db.collection("employees");
// ... (Add rest of code here)
}
2

Adicione o seguinte código ao arquivo para definir o estado do gráfico: state:

const GraphState = Annotation.Root({
messages: Annotation<BaseMessage[]>({
reducer: (x, y) => x.concat(y),
}),
});

O estado define a estrutura de dados que flui pelo fluxo de trabalho do agente . Aqui, o estado rastreia mensagens de conversa, com um redutor que concatena novas mensagens ao histórico de conversa existente.

3

Adicione o seguinte código para definir uma ferramenta e um nó de ferramenta que utiliza a Vector Search do MongoDB para recuperar informações relevantes de funcionário consultando o armazenamento de vetor:

const executeQuery = async (embedding:[], n: number) => {
try {
const client = new MongoClient(process.env.MONGODB_URI as string);
const database = client.db("hr_database");
const coll = database.collection("employees");
const agg = [
{
'$vectorSearch': {
'index': 'vector_index',
'path': 'embedding',
'queryVector': embedding,
'numCandidates': 150,
'limit': n
}
}, {
'$project': {
'_id': 0,
'pageContent': 1,
'score': {
'$meta': 'vectorSearchScore'
}
}
}
];
const result = await coll.aggregate(agg).toArray();
return result
} catch(error) {
console.log("Error while querying:", error)
}
}
const fetchEmbeddings = async (query: string) => {
const apiUrl = "https://ai.mongodb.com/v1/embeddings";
const apiKey = process.env.VOYAGEAI_API_KEY;
const requestBody = {
input: query,
model: "voyage-3.5",
};
try {
const response = await fetch(apiUrl, {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify(requestBody),
});
if (!response.ok) {
throw new Error(`Error: ${response.status} ${response.statusText}`);
}
const data = await response.json();
return data.data[0].embedding;
} catch (error) {
console.error("Error while fetching embedding:", error);
}
};
const employeeLookupTool = tool(
async ({ query, n = 10 }) => {
console.log("Employee lookup tool called");
const embedding = await fetchEmbeddings(query)
const response = await executeQuery(embedding, n)
const result = JSON.stringify(response)
return result;
},
{
name: "employee_lookup",
description: "Gathers employee details from the HR database",
schema: z.object({
query: z.string().describe("The search query"),
n: z.number().optional().default(10).describe("Number of results to return"),
}),
}
);
const tools = [employeeLookupTool];
const toolNode = new ToolNode<typeof GraphState.State>(tools);
4

Adicione o seguinte código ao arquivo para determinar qual modelo usar para o agente. Este exemplo utiliza um modelo do OpenAI, mas você pode modificá-lo para usar seu modelo preferido:

const model = new ChatOpenAI({
model: "gpt-4o"
}).bindTools(tools);
5

Adicione o seguinte código para definir as funções que o agente utiliza para processar mensagens e decidir se deve continuar a conversa:

  1. Esta função configura como o agente usa o LLM:

    • Constrói um modelo de prompt com instruções do sistema e histórico de conversas.

    • Formata o prompt com a hora atual, ferramentas disponíveis e mensagens.

    • Invoca o LLM para gerar a próxima resposta.

    • Retorna a resposta do modelo que deve ser adicionada ao estado da conversa.

    async function callModel(state: typeof GraphState.State) {
    const prompt = ChatPromptTemplate.fromMessages([
    [
    "system",
    `You are a helpful AI assistant, collaborating with other assistants. Use the provided tools to progress towards answering the question. If you are unable to fully answer, that's OK, another assistant with different tools will help where you left off. Execute what you can to make progress. If you or any of the other assistants have the final answer or deliverable, prefix your response with FINAL ANSWER so the team knows to stop. You have access to the following tools: {tool_names}.\n{system_message}\nCurrent time: {time}.`,
    ],
    new MessagesPlaceholder("messages"),
    ]);
    const formattedPrompt = await prompt.formatMessages({
    system_message: "You are helpful HR Chatbot Agent.",
    time: new Date().toISOString(),
    tool_names: tools.map((tool) => tool.name).join(", "),
    messages: state.messages,
    });
    const result = await model.invoke(formattedPrompt);
    return { messages: [result] };
    }
  2. Esta função determina se o agente deve continuar ou terminar a conversa:

    • Se a mensagem contiver chamadas de ferramentas, direcione o fluxo para o nó de ferramentas.

    • Caso contrário, encerre a conversa e retorne a resposta final.

    function shouldContinue(state: typeof GraphState.State) {
    const messages = state.messages;
    const lastMessage = messages[messages.length - 1] as AIMessage;
    if (lastMessage.tool_calls?.length) {
    return "tools";
    }
    return "__end__";
    }
6

Adicione o seguinte código para definir a sequência de etapas que o agente executa para responder a uma query.

const workflow = new StateGraph(GraphState)
.addNode("agent", callModel)
.addNode("tools", toolNode)
.addEdge("__start__", "agent")
.addConditionalEdges("agent", shouldContinue)
.addEdge("tools", "agent");

Especificamente, o agente executa as seguintes etapas:

  1. O agente recebe uma query do usuário.

  2. No nó do agente, o agente processa a consulta e decide se deve utilizar uma ferramenta ou encerrar a conversa.

  3. Caso uma ferramenta seja necessária, o agente redireciona para o nó de ferramentas, onde executa a ferramenta selecionada. Os resultados da ferramenta são enviados de volta para o nó do agente.

  4. O agente interpreta a saída da ferramenta e elabora uma resposta ou decide sobre a próxima ação.

  5. Isso continua até que o agente determine que nenhuma ação adicional é necessária (a função shouldContinue retorna end).

Diagrama que mostra o fluxo de trabalho do agente LangGraph-MongoDB.
clique para ampliar

Para melhorar o desempenho do agente, você pode persistir seu estado usando o MongoDB Checkpointer. A persistência permite que o agente armazene informações sobre interações anteriores, que o agente pode utilizar em interações futuras para oferecer respostas mais relevantes ao contexto.

1

Adicione o seguinte código ao seu arquivo agent.ts para configurar uma camada de persistência para o estado do seu agente:

const checkpointer = new MongoDBSaver({ client, dbName });
const app = workflow.compile({ checkpointer });
2

Por fim, adicione o seguinte código para completar a função do agente para lidar com as consultas:

const finalState = await app.invoke(
{
messages: [new HumanMessage(query)],
},
{ recursionLimit: 15, configurable: { thread_id: thread_id } }
);
console.log(finalState.messages[finalState.messages.length - 1].content);
return finalState.messages[finalState.messages.length - 1].content;

Nesta seção, você cria um servidor para a interação com seu agente e testar sua funcionalidade.

1

Substitua seu arquivo index.ts pelo seguinte código:

import 'dotenv/config';
import express, { Express, Request, Response } from "express";
import { MongoClient } from "mongodb";
import { callAgent } from './agent';
const app: Express = express();
app.use(express.json());
const client = new MongoClient(process.env.MONGODB_URI as string);
async function startServer() {
try {
await client.connect();
await client.db("admin").command({ ping: 1 });
console.log("Pinged your deployment. You successfully connected to MongoDB!");
app.get('/', (req: Request, res: Response) => {
res.send('LangGraph Agent Server');
});
app.post('/chat', async (req: Request, res: Response) => {
const initialMessage = req.body.message;
const threadId = Date.now().toString();
try {
const response = await callAgent(client, initialMessage, threadId);
res.json({ threadId, response });
} catch (error) {
console.error('Error starting conversation:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
app.post('/chat/:threadId', async (req: Request, res: Response) => {
const { threadId } = req.params;
const { message } = req.body;
try {
const response = await callAgent(client, message, threadId);
res.json({ response });
} catch (error) {
console.error('Error in chat:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
} catch (error) {
console.error('Error connecting to MongoDB:', error);
process.exit(1);
}
}
startServer();
2

Execute o seguinte comando para iniciar seu servidor:

npx ts-node index.ts
3

Envie solicitações de amostra para interação com seu agente. Suas respostas variam dependendo dos seus dados e dos modelos que você utiliza.

Observação

A solicitação retorna uma resposta no formato JSON. Você também pode visualizar a saída de texto simples no terminal onde o servidor está em execução.

curl -X POST -H "Content-Type: application/json" -d '{"message": "Build a team to make a web app based on the employee data."}' http://localhost:3000/chat
# Sample response
{"threadId": "1713589087654", "response": "To assemble a web app development team, we ideally need..." (truncated)}
# Plaintext output in the terminal
To assemble a web app development team, we ideally need the following roles:
1. **Software Developer**: To handle the coding and backend.
2. **UI/UX Designer**: To design the application's interface and user experience.
3. **Data Analyst**: For managing, analyzing, and visualizing data if required for the app.
4. **Project Manager**: To coordinate the project tasks and milestones, often providing communication across departments.
### Suitable Team Members for the Project:
#### 1. Software Developer
- **John Doe**
- **Role**: Software Engineer
- **Skills**: Java, Python, AWS
- **Location**: Los Angeles HQ (Remote)
- **Notes**: Highly skilled developer with exceptional reviews (4.8/5), promoted to Senior Engineer in 2018.
#### 2. Data Analyst
- **David Smith**
- **Role**: Data Analyst
- **Skills**: SQL, Tableau, Data Visualization
- **Location**: Denver Office
- **Notes**: Strong technical analysis skills. Can assist with app data integration or dashboards.
#### 3. UI/UX Designer
No specific UI/UX designer was identified in the current search. I will need to query this again or look for a graphic designer with some UI/UX skills.
#### 4. Project Manager
- **Emily Davis**
- **Role**: HR Manager
- **Skills**: Employee Relations, Recruitment, Conflict Resolution
- **Location**: Seattle HQ (Remote)
- **Notes**: Experienced in leadership. Can take on project coordination.
Should I search further for a UI/UX designer, or do you have any other parameters for the team?

Você pode continuar a conversa usando o ID do thread retornado na sua resposta anterior. Por exemplo, para fazer uma pergunta de acompanhamento, use o seguinte comando. Substitua <threadId> pelo ID do thread retornado na resposta anterior.

curl -X POST -H "Content-Type: application/json" -d '{"message": "Who should lead this project?"}' http://localhost:3000/chat/<threadId>
# Sample response
{"response": "For leading this project, a suitable choice would be someone..." (truncated)}
# Plaintext output in the terminal
### Best Candidate for Leadership:
- **Emily Davis**:
- **Role**: HR Manager
- **Skills**: Employee Relations, Recruitment, Conflict Resolution
- **Experience**:
- Demonstrated leadership in complex situations, as evidenced by strong performance reviews (4.7/5).
- Mentored junior associates, indicating capability in guiding a team.
- **Advantages**:
- Remote-friendly, enabling flexible communication across team locations.
- Experience in managing people and processes, which would be crucial for coordinating a diverse team.
**Recommendation:** Emily Davis is the best candidate to lead the project given her proven leadership skills and ability to manage collaboration effectively.
Let me know if you'd like me to prepare a structured proposal or explore alternative options.

Voltar

LangGraph.js

Receber um selo de habilidade

Mestre "Gen AI" de grátis!

Saiba mais

Nesta página