Memory
利用 CrewAI 中统一的 memory 系统来增强 agent 能力。
概览
CrewAI 提供了一个 统一的 memory 系统 —— 一个单一的 Memory 类,用来替代原本独立的短期记忆、长期记忆、实体记忆和外部记忆类型,并通过一个智能 API 统一管理。Memory 在保存内容时会使用 LLM 进行分析(推断 scope、categories 和 importance),并支持具有自适应深度的 recall,使用复合评分机制综合语义相似度、时间新近度和重要性进行排序。
你可以通过四种方式使用 memory:独立使用(脚本、notebook)、与 Crews 一起使用、与 Agents 一起使用,或者 在 Flows 内部使用。
快速开始
from crewai import Memorymemory = Memory()# 存储 —— LLM 会推断 scope、categories 和 importancememory.remember("We decided to use PostgreSQL for the user database.")# 检索 —— 结果按复合评分排序(语义 + 新近度 + 重要性)matches = memory.recall("What database did we choose?")for m in matches:print(f"[{m.score:.2f}] {m.record.content}")# 为一个变化很快的项目调整评分权重memory = Memory(recency_weight=0.5, recency_half_life_days=7)# 忘记memory.forget(scope="/project/old")# 查看自组织的 scope 树print(memory.tree())print(memory.info("/"))
使用 Memory 的四种方式
独立使用
你可以在脚本、notebook、CLI 工具中使用 memory,或者把它当作一个独立知识库来用 —— 不需要 agents 或 crews。
from crewai import Memorymemory = Memory()# 累积知识memory.remember("The API rate limit is 1000 requests per minute.")memory.remember("Our staging environment uses port 8080.")memory.remember("The team agreed to use feature flags for all new releases.")# 之后按需检索matches = memory.recall("What are our API limits?", limit=5)for m in matches:print(f"[{m.score:.2f}] {m.record.content}")# 从较长文本中提取原子事实raw = """Meeting notes: We decided to migrate from MySQL to PostgreSQLnext quarter. The budget is $50k. Sarah will lead the migration."""facts = memory.extract_memories(raw)# ["Migration from MySQL to PostgreSQL planned for next quarter",# "Database migration budget is $50k",# "Sarah will lead the database migration"]for fact in facts:memory.remember(fact)
与 Crews 一起使用
传入 memory=True 使用默认设置,或者传入一个已配置好的 Memory 实例来自定义行为。
from crewai import Crew, Agent, Task, Process, Memory# 方式 1:默认 memorycrew = Crew(agents=[researcher, writer],tasks=[research_task, writing_task],process=Process.sequential,memory=True,verbose=True,)# 方式 2:带评分调优的自定义 memorymemory = Memory(recency_weight=0.4,semantic_weight=0.4,importance_weight=0.2,recency_half_life_days=14,)crew = Crew(agents=[researcher, writer],tasks=[research_task, writing_task],memory=memory,)
当 memory=True 时,crew 会创建一个默认的 Memory(),并自动传递 crew 的 embedder 配置。crew 中所有 agents 默认共享 crew 的 memory,除非某个 agent 自己定义了独立 memory。
每个 task 完成后,crew 会自动从任务输出中提取离散事实并存储。每个 task 开始前,agent 会先从 memory 中回忆相关上下文,并将其注入到任务 prompt 中。
与 Agents 一起使用
Agents 可以使用 crew 的共享 memory(默认),也可以接收一个带 scope 的视图,用作私有上下文。
from crewai import Agent, Memorymemory = Memory()# Researcher 获得一个私有 scope —— 只能看到 /agent/researcherresearcher = Agent(role="Researcher",goal="Find and analyze information",backstory="Expert researcher with attention to detail",memory=memory.scope("/agent/researcher"),)# Writer 使用 crew 共享 memory(未设置 agent 级 memory)writer = Agent(role="Writer",goal="Produce clear, well-structured content",backstory="Experienced technical writer",# 未设置 memory —— 当 crew 启用了 memory 时,使用 crew._memory)
这种模式可以让 researcher 保留私有发现,而 writer 则从 crew 共享 memory 中读取内容。
在 Flows 中使用
每个 Flow 都内置了 memory。你可以在任意 flow 方法中使用 self.remember()、self.recall() 和 self.extract_memories()。
from crewai.flow.flow import Flow, listen, startclass ResearchFlow(Flow):@start()def gather_data(self):findings = "PostgreSQL handles 10k concurrent connections. MySQL caps at 5k."self.remember(findings, scope="/research/databases")return findings@listen(gather_data)def write_report(self, findings):# 回忆过去的研究,为当前内容提供上下文past = self.recall("database performance benchmarks")context = "\n".join(f"- {m.record.content}" for m in past)return f"Report:\nNew findings: {findings}\nPrevious context:\n{context}"
关于 Flows 中的 memory,参见 Flows documentation。
分层 Scopes
什么是 Scope
Memories 被组织在一个分层的 scope 树中,类似文件系统。每个 scope 都是一个路径,例如 /、/project/alpha 或 /agent/researcher/findings。
//company/company/engineering/company/product/project/project/alpha/project/beta/agent/agent/researcher/agent/writer
Scopes 提供了 上下文相关的 memory —— 当你在某个 scope 内 recall 时,只会搜索该树枝,从而同时提升精度和性能。
Scope 推断是如何工作的
当你调用 remember() 却没有指定 scope 时,LLM 会分析内容和已有的 scope 树,然后建议最合适的放置位置。如果没有合适的现有 scope,它会创建一个新的。随着时间推移,scope 树会根据内容自然生长 —— 你无需预先设计 schema。
memory = Memory()# LLM 根据内容推断 scopememory.remember("We chose PostgreSQL for the user database.")# -> 可能被放到 /project/decisions 或 /engineering/database# 你也可以显式指定 scopememory.remember("Sprint velocity is 42 points", scope="/team/metrics")
可视化 Scope 树
print(memory.tree())# / (15 records)# /project (8 records)# /project/alpha (5 records)# /project/beta (3 records)# /agent (7 records)# /agent/researcher (4 records)# /agent/writer (3 records)print(memory.info("/project/alpha"))# ScopeInfo(path='/project/alpha', record_count=5,# categories=['architecture', 'database'],# oldest_record=datetime(...), newest_record=datetime(...),# child_scopes=[])
MemoryScope:子树视图
MemoryScope 会将所有操作限制在树的某个分支内。使用它的 agent 或代码只能在该子树中查看和写入。
memory = Memory()# 为特定 agent 创建一个 scopeagent_memory = memory.scope("/agent/researcher")# 所有操作都相对于 /agent/researcheragent_memory.remember("Found three relevant papers on LLM memory.")# -> 存储在 /agent/researcheragent_memory.recall("relevant papers")# -> 只搜索 /agent/researcher# 进一步缩小到 subscopeproject_memory = agent_memory.subscope("project-alpha")# -> /agent/researcher/project-alpha
Scope 设计最佳实践
- 从扁平开始,让 LLM 组织结构。 不要一开始就过度设计 scope 层级。先使用
memory.remember(content),让 LLM 的 scope 推断随着内容积累自然建立结构。 - 使用
/{entity_type}/{identifier}模式。 自然的层级结构通常会来自诸如/project/alpha、/agent/researcher、/company/engineering、/customer/acme-corp这样的模式。 - 按关注点而不是按数据类型划分 scope。 使用
/project/alpha/decisions,而不是/decisions/project/alpha。这样可以让相关内容保持在一起。 - 保持较浅层级(2 到 3 层)。 过深嵌套的 scopes 会变得太稀疏。
/project/alpha/architecture很合适;/project/alpha/architecture/decisions/databases/postgresql就太深了。 - 已知时显式指定 scope,不确定时让 LLM 推断。 如果你存储的是一个明确的项目决策,可以传
scope="/project/alpha/decisions"。如果你存储的是自由形式的 agent 输出,就可以省略 scope,让 LLM 自动判断。
使用场景示例
多项目团队:
memory = Memory()# 每个项目都有自己的分支memory.remember("Using microservices architecture", scope="/project/alpha/architecture")memory.remember("GraphQL API for client apps", scope="/project/beta/api")# 跨所有项目 recallmemory.recall("API design decisions")# 或在特定项目内 recallmemory.recall("API design", scope="/project/beta")
每个 agent 拥有私有上下文,同时共享知识:
memory = Memory()# Researcher 有私有发现researcher_memory = memory.scope("/agent/researcher")# Writer 可以读取自己的 scope 和共享公司知识writer_view = memory.slice(scopes=["/agent/writer", "/company/knowledge"],read_only=True,)
客户支持(按客户隔离上下文):
memory = Memory()# 每个客户都有独立上下文memory.remember("Prefers email communication", scope="/customer/acme-corp")memory.remember("On enterprise plan, 50 seats", scope="/customer/acme-corp")# 共享产品文档可供所有 agents 访问memory.remember("Rate limit is 1000 req/min on enterprise plan", scope="/product/docs")
Memory Slices
什么是 Slice
MemorySlice 是跨多个、可能互不相连的 scopes 的一个视图。与 scope(限制在单棵子树)不同,slice 允许你同时从多个分支 recall。
什么时候使用 Slice,什么时候使用 Scope
- Scope:当某个 agent 或代码块应被限制在单一子树中时使用。例如:一个只能看到
/agent/researcher的 agent。 - Slice:当你需要组合来自多个分支的上下文时使用。例如:一个同时读取自己 scope 和公司共享知识的 agent。
只读 Slice
最常见的模式:给 agent 多个分支的读取权限,但不允许它写入共享区域。
memory = Memory()# Agent 可以从自己的 scope 和公司知识中 recall,# 但不能写入公司知识agent_view = memory.slice(scopes=["/agent/researcher", "/company/knowledge"],read_only=True,)matches = agent_view.recall("company security policies", limit=5)# 搜索 /agent/researcher 和 /company/knowledge,并合并排序结果agent_view.remember("new finding") # 抛出 PermissionError(只读)
可读写 Slice
当关闭只读时,你可以写入任意一个包含的 scope,但必须显式指定写入哪个 scope。
view = memory.slice(scopes=["/team/alpha", "/team/beta"], read_only=False)# 写入时必须指定 scopeview.remember("Cross-team decision", scope="/team/alpha", categories=["decisions"])
复合评分
Recall 结果通过以下三个信号的加权组合来排序:
composite = semantic_weight * similarity + recency_weight * decay + importance_weight * importance
其中:
- similarity = 向量索引中的
1 / (1 + distance)(范围 0 到 1) - decay =
0.5^(age_days / half_life_days)—— 指数衰减(当天为 1.0,半衰期时为 0.5) - importance = 记录的重要性分数(范围 0 到 1),在编码时设置
你可以直接在 Memory 构造函数中配置这些参数:
# Sprint retrospective:偏重新近记忆,短半衰期memory = Memory(recency_weight=0.5,semantic_weight=0.3,importance_weight=0.2,recency_half_life_days=7,)# Architecture knowledge base:偏重重要记忆,长半衰期memory = Memory(recency_weight=0.1,semantic_weight=0.5,importance_weight=0.4,recency_half_life_days=180,)
每个 MemoryMatch 都包含一个 match_reasons 列表,方便你查看结果为何会排在那个位置(例如 ["semantic", "recency", "importance"])。
LLM 分析层
Memory 以三种方式使用 LLM:
- 保存时 —— 当你没有提供 scope、categories 或 importance 时,LLM 会分析内容,并建议 scope、categories、importance 以及 metadata(entities、dates、topics)。
- 检索时 —— 在 deep / auto recall 中,LLM 会分析查询(关键词、时间提示、建议 scopes、复杂度),以指导检索过程。
- 提取 memories ——
extract_memories(content)会将原始文本(例如 task 输出)拆分为离散的 memory 语句。Agents 在对每条语句调用remember()之前会先做这一步,以便存储的是原子事实,而不是一个很大的文本块。
所有分析都具备优雅降级能力 —— 详见 Failure Behavior。
Memory Consolidation
当保存新内容时,编码流水线会自动检查存储中是否已有相似记录。如果相似度高于 consolidation_threshold(默认 0.85),LLM 会决定如何处理:
- keep —— 现有记录仍然准确,不冗余。
- update —— 现有记录应根据新信息更新(LLM 会提供合并后的内容)。
- delete —— 现有记录已过时、被替代,或与新信息相冲突。
- insert_new —— 是否也要将新内容作为一条独立记录插入。
这可以防止重复记录不断积累。例如,如果你连续三次保存 “CrewAI ensures reliable operation”,consolidation 会识别出重复,只保留一条记录。
批内去重
当使用 remember_many() 时,同一批次中的条目会先彼此比较,然后才进入存储层。如果两条内容的余弦相似度大于等于 batch_dedup_threshold(默认 0.98),后面那条会被静默丢弃。这样可以在不调用 LLM 的情况下,仅靠向量计算捕捉单批次中的完全重复或近似重复内容。
# 最终只会存储 2 条记录(第三条与第一条几乎重复)memory.remember_many(["CrewAI supports complex workflows.","Python is a great language.","CrewAI supports complex workflows.", # 被批内去重丢弃])
非阻塞保存
remember_many() 是 非阻塞 的 —— 它会将编码流水线提交到后台线程并立即返回。这意味着 agent 可以继续执行下一个 task,而 memory 保存会在后台进行。
# 立即返回 —— 保存会在后台执行memory.remember_many(["Fact A.", "Fact B.", "Fact C."])# recall() 在搜索前会自动等待待处理写入完成matches = memory.recall("facts") # 能看到全部 3 条记录
读屏障
每次 recall() 调用都会先自动调用 drain_writes(),确保查询总是能看到最新持久化的记录。这个过程是透明的 —— 你不需要手动考虑它。
Crew 结束时
当 crew 完成时,kickoff() 会在其 finally 块中清空所有待处理的 memory 保存,因此即使 crew 结束时后台保存仍在进行,也不会丢失任何保存操作。
独立使用场景
在脚本或 notebook 中,由于不存在 crew 生命周期,需要你显式调用 drain_writes() 或 close():
memory = Memory()memory.remember_many(["Fact A.", "Fact B."])# 方式 1:等待待处理保存完成memory.drain_writes()# 方式 2:清空待处理保存并关闭后台线程池memory.close()
Source 与隐私
每条 memory record 都可以携带一个 source 标签用于来源追踪,并通过 private 标志进行访问控制。
来源追踪
source 参数用于标识一条 memory 来自哪里:
# 给 memories 打上来源标签memory.remember("User prefers dark mode", source="user:alice")memory.remember("System config updated", source="admin")memory.remember("Agent found a bug", source="agent:debugger")# 只 recall 来自指定 source 的 memoriesmatches = memory.recall("user preferences", source="user:alice")
私有 Memories
私有 memory 只有在 source 匹配时才可被 recall 看到:
# 存储私有 memorymemory.remember("Alice's API key is sk-...", source="user:alice", private=True)# 这个 recall 能看到私有 memory(source 匹配)matches = memory.recall("API key", source="user:alice")# 这个 recall 看不到(source 不同)matches = memory.recall("API key", source="user:bob")# 管理员访问:无论 source 是什么,都能看到所有私有记录matches = memory.recall("API key", include_private=True)
这在多用户或企业部署中特别有用,因为不同用户的 memories 需要相互隔离。
RecallFlow(深度 Recall)
recall() 支持两种深度:
depth="shallow"—— 直接向量搜索 + 复合评分。速度快(约 200ms),不调用 LLM。depth="deep"(默认) —— 运行一个多步骤 RecallFlow:查询分析、scope 选择、并行向量搜索、基于置信度的路由,以及当置信度较低时可选的递归探索。
智能跳过 LLM:即使是 deep 模式,短于 query_analysis_threshold(默认 200 个字符)的查询也会跳过 LLM 查询分析。像 “What database do we use?” 这样的短查询,本身已经是很好的搜索短语 —— LLM 分析不会带来太多额外价值。这样通常可为典型短查询节省约 1 到 3 秒。只有更长的查询(例如完整 task 描述)才会经过 LLM 提炼为更有针对性的子查询。
# Shallow:纯向量搜索,不调用 LLMmatches = memory.recall("What did we decide?", limit=10, depth="shallow")# Deep(默认):对长查询进行带 LLM 分析的智能检索matches = memory.recall("Summarize all architecture decisions from this quarter",limit=10,depth="deep",)
控制 RecallFlow 路由器的置信度阈值也可以配置:
memory = Memory(confidence_threshold_high=0.9, # 只有非常有把握时才直接 synthesizeconfidence_threshold_low=0.4, # 更积极地进行深入探索exploration_budget=2, # 最多允许 2 轮探索query_analysis_threshold=200, # 短于此长度的查询跳过 LLM)
Embedder 配置
Memory 需要一个 embedding 模型,将文本转换为向量,以支持语义搜索。你可以通过三种方式配置它。
直接传给 Memory
from crewai import Memory# 作为配置字典传入memory = Memory(embedder={"provider": "openai", "config": {"model_name": "text-embedding-3-small"}})# 作为预构建 callable 传入from crewai.rag.embeddings.factory import build_embedderembedder = build_embedder({"provider": "ollama", "config": {"model_name": "mxbai-embed-large"}})memory = Memory(embedder=embedder)
通过 Crew 的 Embedder 配置
当使用 memory=True 时,crew 的 embedder 配置会自动传递进去:
from crewai import Crewcrew = Crew(agents=[...],tasks=[...],memory=True,embedder={"provider": "openai", "config": {"model_name": "text-embedding-3-small"}},)
Provider 示例
OpenAI(默认)
memory = Memory(embedder={"provider": "openai","config": {"model_name": "text-embedding-3-small",# "api_key": "sk-...", # 或设置 OPENAI_API_KEY 环境变量},})
Ollama(本地、私有)
memory = Memory(embedder={"provider": "ollama","config": {"model_name": "mxbai-embed-large","url": "http://localhost:11434/api/embeddings",},})
Azure OpenAI
memory = Memory(embedder={"provider": "azure","config": {"deployment_id": "your-embedding-deployment","api_key": "your-azure-api-key","api_base": "https://your-resource.openai.azure.com","api_version": "2024-02-01",},})
Google AI
memory = Memory(embedder={"provider": "google-generativeai","config": {"model_name": "gemini-embedding-001",# "api_key": "...", # 或设置 GOOGLE_API_KEY 环境变量},})
Google Vertex AI
memory = Memory(embedder={"provider": "google-vertex","config": {"model_name": "gemini-embedding-001","project_id": "your-gcp-project-id","location": "us-central1",},})
Cohere
memory = Memory(embedder={"provider": "cohere","config": {"model_name": "embed-english-v3.0",# "api_key": "...", # 或设置 COHERE_API_KEY 环境变量},})
VoyageAI
memory = Memory(embedder={"provider": "voyageai","config": {"model": "voyage-3",# "api_key": "...", # 或设置 VOYAGE_API_KEY 环境变量},})
AWS Bedrock
memory = Memory(embedder={"provider": "amazon-bedrock","config": {"model_name": "amazon.titan-embed-text-v1",# 使用默认 AWS 凭据(boto3 session)},})
Hugging Face
memory = Memory(embedder={"provider": "huggingface","config": {"model_name": "sentence-transformers/all-MiniLM-L6-v2",},})
Jina
memory = Memory(embedder={"provider": "jina","config": {"model_name": "jina-embeddings-v2-base-en",# "api_key": "...", # 或设置 JINA_API_KEY 环境变量},})
IBM WatsonX
memory = Memory(embedder={"provider": "watsonx","config": {"model_id": "ibm/slate-30m-english-rtrvr","api_key": "your-watsonx-api-key","project_id": "your-project-id","url": "https://us-south.ml.cloud.ibm.com",},})
自定义 Embedder
# 传入任意 callable,它接收一个字符串列表并返回一个向量列表def my_embedder(texts: list[str]) -> list[list[float]]:# 你的 embedding 逻辑return [[0.1, 0.2, ...] for _ in texts]memory = Memory(embedder=my_embedder)
Provider 参考表
| Provider | Key | 常见模型 | 说明 |
|---|---|---|---|
| OpenAI | openai |
text-embedding-3-small |
默认值。设置 OPENAI_API_KEY。 |
| Ollama | ollama |
mxbai-embed-large |
本地运行,无需 API key。 |
| Azure OpenAI | azure |
text-embedding-ada-002 |
需要 deployment_id。 |
| Google AI | google-generativeai |
gemini-embedding-001 |
设置 GOOGLE_API_KEY。 |
| Google Vertex | google-vertex |
gemini-embedding-001 |
需要 project_id。 |
| Cohere | cohere |
embed-english-v3.0 |
多语言支持较强。 |
| VoyageAI | voyageai |
voyage-3 |
针对检索优化。 |
| AWS Bedrock | amazon-bedrock |
amazon.titan-embed-text-v1 |
使用 boto3 凭据。 |
| Hugging Face | huggingface |
all-MiniLM-L6-v2 |
本地 sentence-transformers。 |
| Jina | jina |
jina-embeddings-v2-base-en |
设置 JINA_API_KEY。 |
| IBM WatsonX | watsonx |
ibm/slate-30m-english-rtrvr |
需要 project_id。 |
| Sentence Transformer | sentence-transformer |
all-MiniLM-L6-v2 |
本地运行,无需 API key。 |
| Custom | custom |
— | 需要 embedding_callable。 |
LLM 配置
Memory 会使用一个 LLM 进行保存分析(推断 scope / categories / importance)、consolidation 决策,以及深度 recall 的查询分析。你可以配置要使用的模型。
from crewai import Memory, LLM# 默认:gpt-4o-minimemory = Memory()# 使用不同的 OpenAI 模型memory = Memory(llm="gpt-4o")# 使用 Anthropicmemory = Memory(llm="anthropic/claude-3-haiku-20240307")# 使用 Ollama 实现完全本地 / 私有分析memory = Memory(llm="ollama/llama3.2")# 使用 Google Geminimemory = Memory(llm="gemini/gemini-2.0-flash")# 传入一个带自定义设置的预配置 LLM 实例llm = LLM(model="gpt-4o", temperature=0)memory = Memory(llm=llm)
LLM 是 惰性初始化 的 —— 只有在第一次真正需要时才会创建。这意味着即使没有设置 API key,Memory() 在构造时也不会失败。只有当 LLM 真正被调用时(例如保存时没有显式提供 scope / categories,或者执行深度 recall)才会暴露错误。
如果你希望完全离线 / 私有运行,可以同时为 LLM 和 embedder 使用本地模型:
memory = Memory(llm="ollama/llama3.2",embedder={"provider": "ollama", "config": {"model_name": "mxbai-embed-large"}},)
存储后端
- 默认值:LanceDB,存储在
./.crewai/memory下(如果设置了$CREWAI_STORAGE_DIR环境变量,则存到$CREWAI_STORAGE_DIR/memory,或者使用你通过storage="path/to/dir"传入的路径)。 - 自定义后端:实现
StorageBackend协议(见crewai.memory.storage.backend),然后通过Memory(storage=your_backend)传入一个实例。
发现与查看
你可以查看 scope 层级、categories 和 records:
memory.tree() # 格式化的 scope 树和记录数量memory.tree("/project", max_depth=2) # 子树视图memory.info("/project") # ScopeInfo:record_count、categories、oldest/newestmemory.list_scopes("/") # 直接子 scopememory.list_categories() # category 名称与数量memory.list_records(scope="/project/alpha", limit=20) # 某个 scope 下的记录,按时间从新到旧
失败行为
如果 LLM 在分析过程中失败(网络错误、速率限制、响应无效),memory 会优雅降级:
- 保存分析 —— 会记录一个 warning,但 memory 仍会被存储,默认使用 scope
/、空 categories,以及 importance0.5。 - 提取 memories —— 整段内容会作为单条 memory 存储,确保没有内容丢失。
- 查询分析 —— recall 会退回到简单的 scope 选择和向量搜索,因此你仍然能得到结果。
这些分析失败不会抛异常;只有存储层或 embedder 层失败才会抛出异常。
隐私说明
Memory 内容会被发送给配置的 LLM 进行分析(保存时推断 scope / categories / importance,检索时做查询分析,以及可选的深度 recall)。如果数据敏感,请使用本地 LLM(例如 Ollama),或者确保你的 provider 满足合规要求。
Memory Events
所有 memory 操作都会发出事件,并带有 source_type="unified_memory"。你可以监听耗时、错误和内容相关事件。
| Event | 描述 | 关键属性 |
|---|---|---|
| MemoryQueryStartedEvent | 查询开始 | query, limit |
| MemoryQueryCompletedEvent | 查询成功 | query, results, query_time_ms |
| MemoryQueryFailedEvent | 查询失败 | query, error |
| MemorySaveStartedEvent | 保存开始 | value, metadata |
| MemorySaveCompletedEvent | 保存成功 | value, save_time_ms |
| MemorySaveFailedEvent | 保存失败 | value, error |
| MemoryRetrievalStartedEvent | Agent 检索开始 | task_id |
| MemoryRetrievalCompletedEvent | Agent 检索完成 | task_id, memory_content, retrieval_time_ms |
示例:监控查询耗时:
from crewai.events import BaseEventListener, MemoryQueryCompletedEventclass MemoryMonitor(BaseEventListener):def setup_listeners(self, crewai_event_bus):@crewai_event_bus.on(MemoryQueryCompletedEvent)def on_done(source, event):if getattr(event, "source_type", None) == "unified_memory":print(f"Query '{event.query}' completed in {event.query_time_ms:.0f}ms")
故障排查
Memory 没有持久化?
- 确保存储路径可写(默认是
./.crewai/memory)。你可以通过storage="./your_path"使用其他目录,或者设置CREWAI_STORAGE_DIR环境变量。 - 如果是在 crew 中使用,请确认设置了
memory=True或memory=Memory(...)。
Recall 很慢?
- 对常规 agent 上下文使用
depth="shallow"。只在复杂查询中使用depth="deep"。 - 提高
query_analysis_threshold,让更多查询跳过 LLM 分析。
日志中出现 LLM 分析错误?
- Memory 仍会以安全默认值保存 / recall。如果你想恢复完整的 LLM 分析,请检查 API key、速率限制和模型可用性。
日志中出现后台保存错误?
- Memory 保存运行在后台线程中。错误会以
MemorySaveFailedEvent发出,但不会导致 agent 崩溃。请查看日志中的根本原因(通常是 LLM 或 embedder 连接问题)。
并发写入冲突?
- LanceDB 操作会通过共享锁串行化,并在冲突时自动重试。这能处理多个
Memory实例同时指向同一数据库的情况(例如 agent memory + crew memory)。无需手动处理。
在终端中浏览 memory:
crewai memory # 打开 TUI 浏览器crewai memory --storage-path ./my_memory # 指向指定目录
重置 memory(例如用于测试):
crew.reset_memories(command_type="memory") # 重置统一 memory# 或在 Memory 实例上:memory.reset() # 所有 scopesmemory.reset(scope="/project/old") # 仅该子树
配置参考
所有配置都通过 Memory(...) 的关键字参数传入。每个参数都有合理默认值。
| 参数 | 默认值 | 描述 |
|---|---|---|
llm |
"gpt-4o-mini" |
用于分析的 LLM(模型名或 BaseLLM 实例)。 |
storage |
"lancedb" |
存储后端("lancedb"、路径字符串,或 StorageBackend 实例)。 |
embedder |
None(默认 OpenAI) |
Embedder(配置字典、callable,或 None 表示使用默认 OpenAI)。 |
recency_weight |
0.3 |
复合评分中新近度的权重。 |
semantic_weight |
0.5 |
复合评分中语义相似度的权重。 |
importance_weight |
0.2 |
复合评分中重要性的权重。 |
recency_half_life_days |
30 |
新近度分数减半所需天数(指数衰减)。 |
consolidation_threshold |
0.85 |
保存时触发 consolidation 的相似度阈值。设为 1.0 可禁用。 |
consolidation_limit |
5 |
consolidation 时最多比较的现有记录数。 |
default_importance |
0.5 |
未提供 importance 且跳过 LLM 分析时使用的默认值。 |
batch_dedup_threshold |
0.98 |
remember_many() 批内近似重复去重的余弦相似度阈值。 |
confidence_threshold_high |
0.8 |
Recall 置信度高于此值时直接返回结果。 |
confidence_threshold_low |
0.5 |
Recall 置信度低于此值时触发更深入探索。 |
complex_query_threshold |
0.7 |
对复杂查询而言,低于此置信度时进行更深探索。 |
exploration_budget |
1 |
深度 recall 中 LLM 驱动探索的轮数。 |
query_analysis_threshold |
200 |
深度 recall 时,短于此长度(字符数)的查询会跳过 LLM 分析。 |
使用 Mintlify 构建。
