이 튜토리얼에서는 crewai-mongodb-memory 패키지를 사용하여 CrewAI 에이전트에 MongoDB Atlas에 지속되는 지속형 장기 메모리를 제공합니다. 에이전트는 사용자 기록을 메모리 기록으로 저장하고 MongoDB 벡터 검색으로 의미론적으로 회상하므로 이전 세션에서 학습한 내용을 검색하여 새로운 크루는 다음 세션에서 정확하게 답변할 수 있습니다.
MongoDB CrewAI 통합에 대해 자세히 학습하려면 MongoDB 와 CrewAI 통합을 참조하세요.
전제 조건
이 튜토리얼을 완료하려면 다음 조건을 충족해야 합니다.
Python 3.10 이상.
다음 MongoDB cluster 유형 중 하나입니다.
MongoDB 6.0.11 버전, 이상을 실행 Atlas cluster7.0.2. 사용자의 IP 주소 가 Atlas 프로젝트의 액세스 목록에 포함되어 있는지 확인하세요.
Atlas CLI 사용하여 생성된 로컬 Atlas 배포서버 입니다. 자세히 학습 로컬 Atlas 배포 만들기를 참조하세요.
검색 및 벡터 검색이 설치된 MongoDB Community 클러스터.
Voyage AI API 키입니다. API 키를 만들려면 모델 API 키를 참조하세요.
에이전트의 추론을 지원하는 Anthropic 또는 OpenAI 등의 대규모 언어 모델(LLM) API 키입니다.
MongoDB에서 에이전트 메모리가 작동하는 방법
crewai-mongodb-memory 백엔드는 CrewAI의 StorageBackend 프로토콜을 구현합니다. 각 메모리를 MongoDB 컬렉션의 기록으로 저장하고 MongoDB 벡터 검색을 사용하여 관련 기록을 의미론적으로 검색합니다. 백엔드는 다음 구성 요소를 사용합니다.
MongoDBStorageBackend: Atlas 클러스터에 연결하고, 메모리 기록을 저장하고,$vectorSearch쿼리를 실행하여 다시 호출합니다.MemoryRecord: 텍스트 콘텐츠,/users/alex/preferences등의 계층적 범위, 선택 사항인 카테고리 및 벡터 임베딩을 포함하여 단일 메모리를 나타냅니다.embed_text(): 저장된 문서와 쿼리 모두에 대해 Voyage AIvoyage-4모델을 사용하여 1024차원 임베딩을 생성합니다.
메모리가 승무원의 인프로세스 컨텍스트 대신 Atlas에 있기 때문에 동일한 클러스터와 스코프에 연결되는 승무원은 저장된 메모리를 회상할 수 있습니다.
{ "_id": "cff7db8d-aa2e-4d82-8234-e4b8f1ccccb6", "content": "The user always prefers window seats on flights.", "scope": "/users/alex/preferences", "categories": [ "preference" ], "metadata": {}, "importance": 0.5, "created_at": { "$date": "2026-07-14T22:06:59.162Z" }, "last_accessed": { "$date": "2026-07-14T22:06:59.162Z" }, "source": null, "private": false, "embedding": [ 0.03147803992033005, 0.015906454995274544, -0.007409059442579746, 0.028631621971726418, ... ], "scope_ancestors": [ "/users", "/users/alex", "/users/alex/preferences" ] }
에이전트 빌드 및 실행
다음 단계를 완료하여 사용자 설정을 저장하고 호출하는 에이전트를 빌드하고 실행합니다.
환경을 설정합니다.
터미널에서 다음 명령을 실행하여
crewai-memory-project라는 새 디렉토리 만들고 필요한 종속성을 설치합니다.mkdir crewai-memory-project cd crewai-memory-project pip install crewai-mongodb-memory crewai "crewai[google-genai]" voyageai python-dotenv 프로젝트 에서
.env파일 만들고 다음 줄을 추가합니다.ATLAS_URI="<connection-string>" VOYAGE_API_KEY="<voyage-api-key>" ANTHROPIC_API_KEY="<anthropic-api-key>" MODEL="<anthropic-model>" 참고
<connection-string>을 Atlas 클러스터 또는 로컬 Atlas 배포서버의 연결 문자열로 교체합니다.연결 문자열은 다음 형식을 사용해야 합니다.
mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net 자세한 학습은 클라이언트 라이브러리를 통해 클러스터에 연결을 참조하세요.
연결 문자열은 다음 형식을 사용해야 합니다.
mongodb://localhost:<port-number>/?directConnection=true 학습 내용은 연결 문자열을 참조하세요.
에이전트를 빌드합니다.
프로젝트 에 main.py 라는 파일 만들고 다음 코드를 붙여넣습니다.
from __future__ import annotations import os import sys import time import warnings from dotenv import load_dotenv load_dotenv() warnings.filterwarnings("ignore") from crewai import Agent, Crew, Task from crewai.tools import tool from crewai_mongodb_memory import MemoryRecord, MongoDBStorageBackend, embed_text from crewai.events import CrewKickoffStartedEvent, CrewKickoffCompletedEvent, AgentExecutionCompletedEvent from crewai.events import BaseEventListener DEMO_DB = "crewai_mem_agent_demo" SCOPE = "/users/alex/preferences" MODEL = os.environ.get("ANTHROPIC_MODEL", "anthropic/claude-3-5-sonnet-latest") EMBEDDING_MODEL = os.environ.get("VOYAGE_MODEL", "voyage-4") # Module-level backend handle so the function tools can reach it. _BACKEND: MongoDBStorageBackend | None = None _SESSION_LABEL = "" # Custom event listener for logging Crew and Agent events. class EventListener(BaseEventListener): def __init__(self): super().__init__() def setup_listeners(self, crewai_event_bus): def on_crew_started(source, event): print(f"Crew '{event.crew_name}' has started execution!") def on_crew_completed(source, event): print(f"Crew '{event.crew_name}' has completed execution!") print(f"Output: {event.output}") def on_agent_execution_completed(source, event): print(f"Agent '{event.agent.role}' completed task") print(f"Output: {event.output}") def banner(title: str) -> None: print(f"\n{'=' * 70}\n{title}\n{'=' * 70}") def embed(text: str, input_type: str) -> list[float]: """Generate embeddings with an explicit Voyage model.""" try: return embed_text(text, input_type=input_type, model=EMBEDDING_MODEL) except TypeError: # Backward compatibility for older crewai-mongodb-memory versions. return embed_text(text, input_type=input_type) def remember_preference(fact: str) -> str: """Store a durable user preference in MongoDB Atlas long-term memory.""" assert _BACKEND is not None rec = MemoryRecord( content=fact, scope=SCOPE, categories=["preference"], embedding=embed(fact, input_type="document"), ) _BACKEND.save([rec]) return f"Stored preference: {fact}" def recall_preferences(query: str) -> str: """Retrieve relevant user preferences from Atlas via $vectorSearch.""" assert _BACKEND is not None qv = embed(query, input_type="query") hits = _BACKEND.search(qv, scope_prefix=SCOPE, limit=3) if not hits: return "No relevant preferences found." for rec, score in hits: print(f" - [{score:.3f}] {rec.content}") return "\n".join(f"- {rec.content} (score={score:.3f})" for rec, score in hits) def build_agent() -> Agent: return Agent( role="Personal Concierge", goal="Help the user with durable long-term memory stored in MongoDB Atlas.", backstory=( """You remember user preferences across sessions. When the user shares a durable preference, call remember_preference. When a request may depend on what you know about them, call recall_preferences first and use the results.""" ), tools=[remember_preference, recall_preferences], llm=MODEL, verbose=True, ) def run_task(description: str, expected_output: str) -> str: """Run a single-task crew (a fresh crew each call is a fresh session).""" agent = build_agent() task = Task(description=description, expected_output=expected_output, agent=agent) crew = Crew(agent, [task]) return str(crew.kickoff()) def main() -> None: global _BACKEND, _SESSION_LABEL uri = os.environ.get("ATLAS_URI") # Fast fail if the required environment variables are not set. if not all([uri, os.environ.get("ANTHROPIC_API_KEY"), os.environ.get("VOYAGE_API_KEY")]): print("This script needs ATLAS_URI, ANTHROPIC_API_KEY, and VOYAGE_API_KEY.") sys.exit(1) print(f"=== CrewAI and MongoDB Atlas long-term memory (model={MODEL}) ===") _BACKEND = MongoDBStorageBackend(uri, database_name=DEMO_DB) _BACKEND.delete(scope_prefix=SCOPE) # clean slate for a repeatable run print("Ensuring Atlas Vector Search index (first build can take ~1 min)...") if not _BACKEND.ensure_vector_index(wait=True): print("Vector index did not become queryable in time.") sys.exit(1) print("Index queryable.") # Initialize the event listener for the agent. listener = EventListener() # Session 1: the agent learns and stores preferences. banner("SESSION 1 - agent stores durable preferences in Atlas") session_1_input = ( """The user says: 'I'm vegetarian, I avoid dairy, and I always prefer window seats on flights.' Store each durable preference.""" ) _SESSION_LABEL = "SESSION 1" print(f" {session_1_input}") out1 = run_task( description=session_1_input, expected_output="A short confirmation of what was stored.", ) print(f"\nAgent (session 1): {out1}") # Atlas indexing is asynchronous, so wait until the facts are searchable. print("\nWaiting for Atlas to index the new memories...") for _ in range(20): if _BACKEND.search(embed("food", input_type="query"), scope_prefix=SCOPE, limit=1): break time.sleep(2) print("Memories searchable.") # Session 2: a brand-new crew with no shared state recalls from Atlas. banner("SESSION 2 - fresh crew answers via recall_preferences") session_2_input = ( """The user is booking a long flight and pre-ordering an in-flight meal. Recommend a seat and a meal that fit their preferences.""" ) _SESSION_LABEL = "SESSION 2" print(f" {session_2_input}") out2 = run_task( description=session_2_input, expected_output="A seat and meal recommendation grounded in recalled preferences.", ) print(f"\nAgent (session 2, brand-new crew): {out2}") banner("Proof: direct $vectorSearch recall over stored preferences") hits = _BACKEND.search( embed("dietary and seating preferences", input_type="query"), scope_prefix=SCOPE, limit=5, ) for rec, score in hits: print(f" - [{score:.3f}] {rec.content}") _BACKEND.delete(scope_prefix=SCOPE) _BACKEND.close() print("\nDone - CrewAI stored and recalled preferences via MongoDB Vector Search.") if __name__ == "__main__": main()
이 스크립트 다음을 수행합니다.
파일 실행합니다.
다음 명령을 실행하여 스크립트 실행합니다.
python main.py
====================================================================== SESSION 1 - agent stores durable preferences in Atlas ====================================================================== The user says: 'I'm vegetarian, I avoid dairy, and I always prefer window seats on flights.' Store each durable preference. Crew 'crew' has started execution! ╭────────────────────────────── 🤖 Agent Started ──────────────────────────────╮ │ │ │ Agent: Personal Concierge │ │ │ │ Task: The user says: 'I'm vegetarian, I avoid dairy, and I always prefer │ │ window seats on flights.' Store each durable preference. │ │ │ ╰──────────────────────────────────────────────────────────────────────────────╯ Tool remember_preference executed with result: Stored preference: The user is vegetarian.... Tool remember_preference executed with result: Stored preference: The user avoids dairy.... Tool remember_preference executed with result: Stored preference: The user always prefers window seats on flights.... [Finalize] todos_count=0, todos_with_results=0 ╭─────────────────────────── ✅ Agent Final Answer ────────────────────────────╮ │ │ │ Agent: Personal Concierge │ │ │ │ Final Answer: │ │ Stored: │ │ - The user is vegetarian. │ │ - The user avoids dairy. │ │ - The user always prefers window seats on flights. │ │ │ ╰──────────────────────────────────────────────────────────────────────────────╯ Agent 'Personal Concierge' completed task Output: Stored: - The user is vegetarian. - The user avoids dairy. - The user always prefers window seats on flights. Crew 'crew' has completed execution! Output: Stored: - The user is vegetarian. - The user avoids dairy. - The user always prefers window seats on flights. Agent (session 1): Stored: - The user is vegetarian. - The user avoids dairy. - The user always prefers window seats on flights. Waiting for Atlas to index the new memories... Memories searchable. ====================================================================== SESSION 2 - fresh crew answers via recall_preferences ====================================================================== The user is booking a long flight and pre-ordering an in-flight meal. Recommend a seat and a meal that fit their preferences. Crew 'crew' has started execution! ╭────────────────────────────── 🤖 Agent Started ──────────────────────────────╮ │ │ │ Agent: Personal Concierge │ │ │ │ Task: The user is booking a long flight and pre-ordering an in-flight │ │ meal. │ │ Recommend a seat and a meal that fit their preferences. │ │ │ ╰──────────────────────────────────────────────────────────────────────────────╯ - [0.794] The user always prefers window seats on flights. - [0.748] The user is vegetarian. - [0.739] The user avoids dairy. Tool recall_preferences executed with result: - The user always prefers window seats on flights. (score=0.794) - The user is vegetarian. (score=0.748) - The user avoids dairy. (score=0.739)... [Finalize] todos_count=0, todos_with_results=0 ╭─────────────────────────── ✅ Agent Final Answer ────────────────────────────╮ │ │ │ Agent: Personal Concierge │ │ │ │ Final Answer: │ │ I recommend booking a **window seat** for the long flight, since you │ │ prefer window seats. │ │ │ │ For your in-flight meal, choose a **vegetarian dairy-free meal**. If the │ │ airline offers specific meal codes, look for something like **VGML / vegan │ │ meal**, since that will typically meet both preferences: vegetarian and no │ │ dairy. │ │ │ ╰──────────────────────────────────────────────────────────────────────────────╯ Agent 'Personal Concierge' completed task Output: I recommend booking a **window seat** for the long flight, since you prefer window seats. For your in-flight meal, choose a **vegetarian dairy-free meal**. If the airline offers specific meal codes, look for something like **VGML / vegan meal**, since that will typically meet both preferences: vegetarian and no dairy. Crew 'crew' has completed execution! Output: I recommend booking a **window seat** for the long flight, since you prefer window seats. For your in-flight meal, choose a **vegetarian dairy-free meal**. If the airline offers specific meal codes, look for something like **VGML / vegan meal**, since that will typically meet both preferences: vegetarian and no dairy. Agent (session 2, brand-new crew): I recommend booking a **window seat** for the long flight, since you prefer window seats. For your in-flight meal, choose a **vegetarian dairy-free meal**. If the airline offers specific meal codes, look for something like **VGML / vegan meal**, since that will typically meet both preferences: vegetarian and no dairy. ====================================================================== Proof: direct $vectorSearch recall over stored preferences ====================================================================== - [0.770] The user always prefers window seats on flights. - [0.768] The user is vegetarian. - [0.754] The user avoids dairy. Done - CrewAI stored and recalled preferences via MongoDB Vector Search.
참고
LLM 응답과 벡터 검색 점수는 실행 마다 다를 수 있으므로 출력이 다를 수 있습니다.