Skip to main content

CrewAI agents that remember between runs

· 2 min read

CrewAI's built-in memory works inside one kickoff(). When the process exits, context is gone. atulya-crewai implements CrewAI's Storage interface on top of Atulya banks so knowledge compounds across runs.

The problem

Research crews, support crews, planning crews that run daily need memory that survives restarts. SQLite/RAG backends in CrewAI target single-run persistence, not months of accumulated facts.

Architecture

CrewAI Crew
└─ ExternalMemory
└─ AtulyaStorage
├─ save() → retain
├─ search() → recall
└─ reset() → delete_bank + recreate

CrewAI calls save() after tasks and search() before tasks. Atulya extracts facts, entities, and graph links; recall uses multi-strategy retrieval + reranking.

Quick start

pip install atulya-all atulya-crewai
export ATULYA_API_LLM_API_KEY=YOUR_KEY
atulya-api
from atulya_crewai import configure, AtulyaStorage
from crewai.memory.external.external_memory import ExternalMemory
from crewai import Agent, Crew, Task

configure(atulya_api_url="http://localhost:8888")

researcher = Agent(
role="Researcher",
goal="Find accurate information on the topic.",
backstory="Thorough researcher.",
llm="openai/gpt-4o-mini",
)

research_task = Task(
description="Research Rust for CLI tools.",
expected_output="Summary of Rust strengths for CLI.",
agent=researcher,
)

crew = Crew(
agents=[researcher],
tasks=[research_task],
external_memory=ExternalMemory(
storage=AtulyaStorage(
bank_id="research-crew",
mission="Track research findings and comparisons.",
)
),
)

crew.kickoff()

Second run on Go vs Rust: the crew recalls prior Rust research. Third run asking for a recommendation draws on both sessions.

Reflect tool

reflect is not on the Storage interface; expose it as a tool:

from atulya_crewai import AtulyaReflectTool

reflect_tool = AtulyaReflectTool(
bank_id="research-crew",
budget="mid",
reflect_context="Helping a team evaluate languages.",
)

researcher = Agent(..., tools=[reflect_tool], ...)

Per-agent banks

AtulyaStorage(bank_id="research-crew", per_agent_banks=True)
# → research-crew-researcher, research-crew-writer, ...

Or custom:

bank_resolver=lambda base, agent: f"{base}-{agent.lower()}" if agent else base

Pitfalls

  1. Shared bank_id across unrelated crews mixes memory. Use unique IDs per project.
  2. Huge task outputs increase retain latency. Tighten expected_output.
  3. budget: "low" for speed, "high" for depth.
  4. Async loops: use AtulyaStorage / AtulyaReflectTool, not raw client calls from CrewAI's loop.

Next steps