AI 에이전트의 경우: 문서 인덱스는 https://www.mongodb.com/ko-kr/docs/llms.txt에서 사용할 수 있으며, 모든 페이지의 마크다운 버전은 어떤 URL 경로에 .md를 추가하여 사용할 수 있습니다.
Docs Menu

CrewAI 에이전트에 장기 텀 기억 추가

이 튜토리얼에서는 crewai-mongodb-memory 패키지를 사용하여 CrewAI 에이전트에 MongoDB Atlas에 지속되는 지속형 장기 메모리를 제공합니다. 에이전트는 사용자 기록을 메모리 기록으로 저장하고 MongoDB 벡터 검색으로 의미론적으로 회상하므로 이전 세션에서 학습한 내용을 검색하여 새로운 크루는 다음 세션에서 정확하게 답변할 수 있습니다.

MongoDB CrewAI 통합에 대해 자세히 학습하려면 MongoDB 와 CrewAI 통합을 참조하세요.

이 튜토리얼을 완료하려면 다음 조건을 충족해야 합니다.

crewai-mongodb-memory 백엔드는 CrewAI의 StorageBackend 프로토콜을 구현합니다. 각 메모리를 MongoDB 컬렉션의 기록으로 저장하고 MongoDB 벡터 검색을 사용하여 관련 기록을 의미론적으로 검색합니다. 백엔드는 다음 구성 요소를 사용합니다.

  • MongoDBStorageBackend: Atlas 클러스터에 연결하고, 메모리 기록을 저장하고, $vectorSearch 쿼리를 실행하여 다시 호출합니다.

  • MemoryRecord: 텍스트 콘텐츠, /users/alex/preferences 등의 계층적 범위, 선택 사항인 카테고리 및 벡터 임베딩을 포함하여 단일 메모리를 나타냅니다.

  • embed_text(): 저장된 문서와 쿼리 모두에 대해 Voyage AI voyage-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"
]
}

다음 단계를 완료하여 사용자 설정을 저장하고 호출하는 에이전트를 빌드하고 실행합니다.

1
  1. 터미널에서 다음 명령을 실행하여 crewai-memory-project 라는 새 디렉토리 만들고 필요한 종속성을 설치합니다.

    mkdir crewai-memory-project
    cd crewai-memory-project
    pip install crewai-mongodb-memory crewai "crewai[google-genai]" voyageai python-dotenv
  2. 프로젝트 에서 .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

    학습 내용은 연결 문자열을 참조하세요.

2

프로젝트 에 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):
@crewai_event_bus.on(CrewKickoffStartedEvent)
def on_crew_started(source, event):
print(f"Crew '{event.crew_name}' has started execution!")
@crewai_event_bus.on(CrewKickoffCompletedEvent)
def on_crew_completed(source, event):
print(f"Crew '{event.crew_name}' has completed execution!")
print(f"Output: {event.output}")
@crewai_event_bus.on(AgentExecutionCompletedEvent)
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)
@tool("remember_preference")
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}"
@tool("recall_preferences")
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()

이 스크립트 다음을 수행합니다.

  • MongoDBStorageBackend 로 Atlas 클러스터에 연결하고 메모리 컬렉션에 MongoDB 벡터 검색 인덱스가 있는지 확인합니다.

  • 에이전트가 메모리를 저장하고 조회하기 위해 호출하는 두 개의 도구( remember_preference, recall_preferences)를 정의합니다.

  • 메모리 도구를 사용하는 CrewAI 에이전트 를 정의한 다음 사용자의 기호를 저장하는 하나의 크루를 실행합니다.

  • Atlas에서 저장된 기본 설정을 호출하고 이를 기반으로 권장사항을 제시하는 새로운 클러스터를 실행합니다.

3

다음 명령을 실행하여 스크립트 실행합니다.

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 응답과 벡터 검색 점수는 실행 마다 다를 수 있으므로 출력이 다를 수 있습니다.