Join us Sept 17 at .local NYC! Use code WEB50 to save 50% on tickets. Learn more >
MongoDB Event
Docs Menu
Docs Home
/
Atlas
/ / /

LgGraph.js とMongoDB AtlasでAIエージェントを構築

You can integrate MongoDB Atlas with LangGraph.js to build AI agents. This tutorial demonstrates how to build an agent with LangGraph.js and MongoDB Vector Search that can answer questions about your data.

具体的には、次のアクションを実行します。

  1. 環境を設定します。

  2. MongoDBクラスターを構成します。

  3. エージェントツールを含むエージェントをビルドします。

  4. エージェントにメモリを追加します。

  5. サーバーを作成し、エージェントをテストします。

このチュートリアルのコードでは、 GitHubリポジトリをクローンします。

始める前に、以下のものを必ず用意してください。

注意

このチュートリアルでは OpenAI と Vyage AIのモデルを使用していますが、コードを変更して、お好みのモデルを使用することができます。

環境を設定するには、以下の手順を完了します。

1

新しいプロジェクトディレクトリを作成し、プロジェクト内で次のコマンドを実行して必要な依存関係をインストールします。

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

注意

プロジェクトでは、次の構造を使用します。

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

プロジェクトルートに .envファイルを作成し、 APIキーと接続文字列を追加します。

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

<connection-string> を Atlas クラスターまたはローカル Atlas 配置の接続文字列に置き換えます。

接続stringには、次の形式を使用する必要があります。

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

詳しくは、「 ドライバーを使用してクラスターに接続する 」を参照してください。

接続stringには、次の形式を使用する必要があります。

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

詳細については、「接続文字列 」を参照してください。

このチュートリアルは、動画を視聴して進めることができます。

所要時間: 30分

このセクションでは、サンプルデータを構成してMongoDBクラスターに取り込み、データに対してベクトル検索を有効にします。

1

MongoDBクラスターへの接続を確立する index.tsファイルを作成します。

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

サンプルの従業員レコードを生成して格納するseed-database.tsスクリプトを作成してください。このスクリプトは、次のアクションを実行します。

  • 従業員レコードのスキーマを定義します。

  • LM を使用してサンプル従業員データを生成する関数を作成します。

  • 各レコードを処理して、埋め込みに使用するテキストのサマリーを作成します。

  • LgChuin MongoDB統合を使用して、 MongoDBクラスターをベクトルストアとして初期化します。このコンポーネントはベクトル埋め込みを生成し、ドキュメントを 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')
}

Tip

スクリプトを実行中後、Atlas UIの 名前空間に移動すると、 MongoDBクラスター内のシード データを表示できます。hr_database.employees

4

Follow the steps to create an MongoDB Vector Search index for the hr_database.employees namespace. Name the index vector_index and specify the following index definition:

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

このセクションでは、エージェントのワークフローをオーケストレーションするためのグラフを構築します。グラフは、エージェントがクエリに応答するための手順の順序を定義します。

1

プロジェクトに agent.ts という名前の新しいファイルを作成し、次のコードを追加してエージェントの設定を開始してください。次の手順では、非同期関数にさらにコードを追加されます。

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

グラフの状態を定義するには、ファイルに次のコードを追加します。

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

状態は、エージェントのワークフローを通過するデータ構造を定義します。ここでは、 状態は、新しいメッセージを既存の対話履歴に連結する削減値を使用して、対話メッセージを追跡します。

3

Add the following code to define a tool and tool node that uses MongoDB Vector Search to retrieve relevant employee information by querying the vector store:

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://api.voyageai.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

エージェントに使用するモデルを決定するには、ファイルに次のコードを追加します。この例ではOpenAI のモデルを使用していますが、そのモデルを変更して希望のモデルを使用することができます。

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

次のコードを追加して、エージェントがメッセージを処理し、通信を継続するかどうかを判断するために使用する関数を定義します。

  1. この関数は、エージェントがLM を使用する方法を構成します。

    • システム指示とやり取り履歴を含むプロンプト テンプレートを作成します。

    • 現在の時刻、使用可能なツール、メッセージを含むプロンプトの形式を設定します。

    • LLM を呼び出して、次の応答を生成します。

    • 会話状態に追加するモデルの応答を返します。

    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. この関数は、エージェントが会話を続けるべきか、終了するべきかを決定します。

    • メッセージにツール呼び出しが含まれている場合、フローをツールノードにルーティングします。

    • それ以外の場合は、対話を終了し、最終応答を返します。

    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

以下のコードを追加して、エージェントがクエリに応答するための一連のステップを定義します。

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

具体的に言うと、エージェントは以下の手順を実行します。

  1. エージェントは、ユーザーのクエリを受け取ります。

  2. エージェントノードでは、エージェントがクエリを処理し、ツールを使用するか、会話を終了するかを決定します。

  3. ツールが必要な場合、エージェントはツールノードにルーティングし、選択されたツールを実行します。ツールの結果はエージェントノードに返送されます。

  4. エージェントはツールの出力を解釈し、応答を形成するか、次のアクションを決定します。

  5. エージェントがこれ以上の処理は不要であると判断するまでこれが続きます(shouldContinue 関数が end を返すとき)。

LangGraph-MongoDB エージェントのワークフローを示す図。
クリックして拡大します

MongoDB チェックポインターを使用することで、エージェントの状態を永続化し、パフォーマンスを向上させることができます。永続性により、エージェントは以前のインタラクションに関する情報を保存できます。エージェントはこれを今後のインタラクションで使用し、より文脈的に関連性の高い応答を提供できます。

1

次のコードを agent.tsファイルに追加して、エージェントの状態の永続性レイヤーを設定します。

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

最後に、次のコードを追加して、クエリを処理するエージェント関数を完了します。

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;

このセクションでは、エージェントと交流し、その機能をテストするためのサーバーを作成します。

1

index.ts ファイルを次のコードで置き換えてください。

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

次のコマンドを実行して、サーバーを起動します。

npx ts-node index.ts
3

エージェントとやり取りするためのサンプルリクエストを送信してください。使用するデータとモデルによって、応答内容は異なります。

注意

リクエストはJSON形式で応答を返します。サーバーが稼働しているターミナルで、プレーンテキストの出力を表示することもできます。

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?

前の応答で返されたスレッド ID を使用して、会話を続行することができます。例えば、フォローアップの質問をするには、次のコマンドを使用してください。<threadId> を前の応答で返されたスレッド ID に置き換えてください。

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.

戻る

LangGraph.js

ルール バッジを取得する

「生成AI」を無料でマスターします。

詳細

項目一覧