记忆
CrewAI 提供了一套统一记忆系统 - 通过一个 Memory 类,替代单独的短期、长期、实体和外部记忆类型,并统一为一个智能 API。Memory 在保存内容时会使用 LLM 分析内容(推断作用域、类别和重要性),并支持自适应深度召回,使用组合评分将语义相似度、时效性和重要性融合在一起。
你可以通过四种方式使用 memory:独立使用(脚本、笔记本)、与 Crews 一起使用、与 Agents 一起使用,或者在 Flows 中使用。
from crewai import Memory
memory = Memory()
# Store -- the LLM infers scope, categories, and importancememory.remember("We decided to use PostgreSQL for the user database.")
# Retrieve -- results ranked by composite score (semantic + recency + importance)matches = memory.recall("What database did we choose?")for m in matches: print(f"[{m.score:.2f}] {m.record.content}")
# Tune scoring for a fast-moving projectmemory = Memory(recency_weight=0.5, recency_half_life_days=7)
# Forgetmemory.forget(scope="/project/old")
# Explore the self-organized scope treeprint(memory.tree())print(memory.info("/"))使用 Memory 的四种方式
Section titled “使用 Memory 的四种方式”在脚本、笔记本、CLI 工具中使用 memory,或者把它作为一个独立的知识库 - 不需要 agents 或 crews。
from crewai import Memory
memory = Memory()
# Build up knowledgememory.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.")
# Later, recall what you needmatches = memory.recall("What are our API limits?", limit=5)for m in matches: print(f"[{m.score:.2f}] {m.record.content}")
# Extract atomic facts from a longer textraw = """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 一起使用
Section titled “与 Crews 一起使用”传入 memory=True 可使用默认设置,也可以传入已配置好的 Memory 实例来定制行为。
from crewai import Crew, Agent, Task, Process, Memory
# Option 1: Default memorycrew = Crew( agents=[researcher, writer], tasks=[research_task, writing_task], process=Process.sequential, memory=True, verbose=True,)
# Option 2: Custom memory with tuned scoringmemory = 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 配置。除非某个 agent 有自己的 memory,否则 crew 中所有 agents 都共享 crew 的 memory。没有自定义 embedder 时,memory 会使用 OpenAI text-embedding-3-large embeddings。
每个任务完成后,crew 会自动从任务输出中提取离散事实并存储它们。每个任务开始前,agent 会从 memory 中召回相关上下文,并把它注入到任务提示中。
与 Agents 一起使用
Section titled “与 Agents 一起使用”Agents 可以使用 crew 共享的 memory(默认),也可以获得一个带作用域的私有上下文视图。
from crewai import Agent, Memory
memory = Memory()
# Researcher gets a private scope -- only sees /agent/researcherresearcher = Agent( role="Researcher", goal="Find and analyze information", backstory="Expert researcher with attention to detail", memory=memory.scope("/agent/researcher"),)
# Writer uses crew shared memory (no agent-level memory set)writer = Agent( role="Writer", goal="Produce clear, well-structured content", backstory="Experienced technical writer", # memory not set -- uses crew._memory when crew has memory enabled)这种模式让 researcher 保留私有发现,而 writer 则从共享的 crew memory 中读取信息。
与 Flows 一起使用
Section titled “与 Flows 一起使用”每个 Flow 都内置了 memory。你可以在任意 flow 方法中使用 self.remember()、self.recall() 和 self.extract_memories()。
from crewai.flow.flow import Flow, listen, start
class 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): # Recall past research to provide context 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 文档 了解 flows 中 memory 的更多内容。
什么是 Scope
Section titled “什么是 Scope”memory 以层级树的方式组织,类似文件系统。每个 scope 都是一个路径,例如 /、/project/alpha 或 /agent/researcher/findings。
/ /company /company/engineering /company/product /project /project/alpha /project/beta /agent /agent/researcher /agent/writerScopes 提供了上下文相关的记忆 - 当你在某个 scope 中召回时,只会搜索树的那个分支,这能同时提升准确率和性能。
Scope 推断如何工作
Section titled “Scope 推断如何工作”当你在 remember() 中不指定 scope 时,LLM 会分析内容和现有 scope 树,然后建议最合适的放置位置。如果没有合适的现有 scope,它会创建一个新的。随着时间推移,scope 树会从内容本身自然生长出来 - 你不需要预先设计 schema。
memory = Memory()
# LLM infers scope from contentmemory.remember("We chose PostgreSQL for the user database.")# -> might be placed under /project/decisions or /engineering/database
# You can also specify scope explicitlymemory.remember("Sprint velocity is 42 points", scope="/team/metrics")可视化 Scope 树
Section titled “可视化 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:子树视图
Section titled “MemoryScope:子树视图”MemoryScope 会将所有操作限制在树的某个分支上。使用它的 agent 或代码只能看到并写入该子树。
memory = Memory()
# Create a scope for a specific agentagent_memory = memory.scope("/agent/researcher")
# Everything is relative to /agent/researcheragent_memory.remember("Found three relevant papers on LLM memory.")# -> stored under /agent/researcher
agent_memory.recall("relevant papers")# -> searches only under /agent/researcher
# Narrow further with subscopeproject_memory = agent_memory.subscope("project-alpha")# -> /agent/researcher/project-alphaScope 设计最佳实践
Section titled “Scope 设计最佳实践”-
先保持扁平,让 LLM 自行组织。 不要一开始就过度设计 scope 层级。先用
memory.remember(content),让 LLM 的 scope 推断随着内容积累自动生成结构。 -
使用
/{entity_type}/{identifier}模式。 像/project/alpha、/agent/researcher、/company/engineering、/customer/acme-corp这样的模式很自然。 -
按关注点分组,而不是按数据类型。 用
/project/alpha/decisions,不要用/decisions/project/alpha。这样相关内容会聚在一起。 -
保持层级浅一些(2-3 层)。 过深的嵌套 scope 会变得过于稀疏。
/project/alpha/architecture是好的;/project/alpha/architecture/decisions/databases/postgresql就太深了。 -
已知时显式指定,不确定时让 LLM 推断。 如果你在保存已知项目决策,请传入
scope="/project/alpha/decisions"。如果你在保存自由形式的 agent 输出,就省略 scope,让 LLM 自己判断。
使用场景示例
Section titled “使用场景示例”多项目团队:
memory = Memory()# Each project gets its own branchmemory.remember("Using microservices architecture", scope="/project/alpha/architecture")memory.remember("GraphQL API for client apps", scope="/project/beta/api")
# Recall across all projectsmemory.recall("API design decisions")
# Or within a specific projectmemory.recall("API design", scope="/project/beta")每个 agent 的私有上下文与共享知识:
memory = Memory()
# Researcher has private findingsresearcher_memory = memory.scope("/agent/researcher")
# Writer can read from both its own scope and shared company knowledgewriter_view = memory.slice( scopes=["/agent/writer", "/company/knowledge"], read_only=True,)客户支持(按客户隔离上下文):
memory = Memory()
# Each customer gets isolated contextmemory.remember("Prefers email communication", scope="/customer/acme-corp")memory.remember("On enterprise plan, 50 seats", scope="/customer/acme-corp")
# Shared product docs are accessible to all agentsmemory.remember("Rate limit is 1000 req/min on enterprise plan", scope="/product/docs")Memory Slices
Section titled “Memory Slices”什么是 Slice
Section titled “什么是 Slice”MemorySlice 是跨越多个、可能互不相交的 scopes 的视图。与 scope(只限制在一个子树)不同,slice 允许同时从多个分支召回内容。
何时使用 Slice 与 Scope
Section titled “何时使用 Slice 与 Scope”- Scope:当某个 agent 或代码块只应限制在单个子树中时使用。例:只看
/agent/researcher的 agent。 - Slice:当你需要把多个分支的上下文合并在一起时使用。例:一个既读取自己的 scope 又读取共享 company knowledge 的 agent。
只读 Slice
Section titled “只读 Slice”最常见的模式是:允许 agent 读取多个分支,但不允许它写入共享区域。
memory = Memory()
# Agent can recall from its own scope AND company knowledge,# but cannot write to company knowledgeagent_view = memory.slice( scopes=["/agent/researcher", "/company/knowledge"], read_only=True,)
matches = agent_view.recall("company security policies", limit=5)# Searches both /agent/researcher and /company/knowledge, merges and ranks results
agent_view.remember("new finding") # Raises PermissionError (read-only)读写 Slice
Section titled “读写 Slice”当关闭只读时,你可以写入所包含的任意 scope,但必须显式指定要写入哪个 scope。
view = memory.slice(scopes=["/team/alpha", "/team/beta"], read_only=False)
# Must specify scope when writingview.remember("Cross-team decision", scope="/team/alpha", categories=["decisions"])召回结果会按三个信号的加权组合进行排序:
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: favor recent memories, short half-lifememory = Memory( recency_weight=0.5, semantic_weight=0.3, importance_weight=0.2, recency_half_life_days=7,)
# Architecture knowledge base: favor important memories, long half-lifememory = 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 分析层
Section titled “LLM 分析层”Memory 会从三个方面使用 LLM:
- 保存时 - 当你省略 scope、categories 或 importance 时,LLM 会分析内容并建议 scope、categories、importance 以及元数据(实体、日期、主题)。
- 召回时 - 对于深度/自动召回,LLM 会分析查询(关键词、时间提示、建议 scopes、复杂度)以引导检索。
- 提取 memories -
extract_memories(content)会把原始文本(例如任务输出)拆成离散的 memory 语句。Agents 会在调用remember()保存每个语句之前先执行这一步,这样原子事实会被存为独立条目,而不是一个大块。
如果 LLM 失败,所有分析都会优雅降级 - 见 Failure Behavior。
Memory Consolidation
Section titled “Memory Consolidation”在保存新内容时,编码管线会自动检查存储中是否存在相似的旧记录。如果相似度高于 consolidation_threshold(默认 0.85),LLM 会决定如何处理:
- keep - 现有记录仍然准确且不重复。
- update - 现有记录需要用新信息更新(LLM 会提供合并后的内容)。
- delete - 现有记录已过时、被替代或被新内容否定。
- insert_new - 是否还应将新内容作为独立记录插入。
这可以防止重复项累积。例如,如果你三次保存 “CrewAI ensures reliable operation”,consolidation 会识别重复并只保留一条记录。
在使用 remember_many() 时,同一批次中的项目会在写入存储前彼此比较。如果两个项目的 cosine similarity >= batch_dedup_threshold(默认 0.98),后者会被静默丢弃。这样无需任何 LLM 调用(纯向量运算)就能捕获单批次中的完全或近似重复项。
# Only 2 records are stored (the third is a near-duplicate of the first)memory.remember_many([ "CrewAI supports complex workflows.", "Python is a great language.", "CrewAI supports complex workflows.", # dropped by intra-batch dedup])remember_many() 是非阻塞的 - 它会把编码管线提交到后台线程并立即返回。这意味着 agent 可以在 memory 正在保存时继续执行下一个任务。
# Returns immediately -- save happens in backgroundmemory.remember_many(["Fact A.", "Fact B.", "Fact C."])
# recall() automatically waits for pending saves before searchingmatches = memory.recall("facts") # sees all 3 records每次 recall() 调用都会在搜索前自动调用 drain_writes(),确保查询总能看到最新的已持久化记录。这是透明的 - 你无需手动思考这一步。
Crew 关闭
Section titled “Crew 关闭”当 crew 结束时,kickoff() 会在其 finally 块中排空所有待处理的 memory 保存,因此即使 crew 在后台保存进行中完成,也不会丢失保存内容。
对于没有 crew 生命周期的脚本或笔记本,请显式调用 drain_writes() 或 close():
memory = Memory()memory.remember_many(["Fact A.", "Fact B."])
# Option 1: Wait for pending savesmemory.drain_writes()
# Option 2: Drain and shut down the background poolmemory.close()每条 memory 记录都可以带有用于溯源追踪的 source 标签,以及用于访问控制的 private 标志。
source 参数用于标识 memory 的来源:
# Tag memories with their originmemory.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 only memories from a specific sourcematches = memory.recall("user preferences", source="user:alice")私有 memories
Section titled “私有 memories”只有当 source 匹配时,私有 memory 才会在召回时可见:
# Store a private memorymemory.remember("Alice's API key is sk-...", source="user:alice", private=True)
# This recall sees the private memory (source matches)matches = memory.recall("API key", source="user:alice")
# This recall does NOT see it (different source)matches = memory.recall("API key", source="user:bob")
# Admin access: see all private records regardless of sourcematches = memory.recall("API key", include_private=True)这在多用户或企业部署中特别有用,因为不同用户的 memory 需要彼此隔离。
RecallFlow(深度召回)
Section titled “RecallFlow(深度召回)”recall() 支持两种深度:
depth="shallow"- 直接进行向量搜索并使用组合评分。速度快(约 200ms),不调用 LLM。depth="deep"(默认) - 运行多步骤 RecallFlow:查询分析、scope 选择、并行向量搜索、基于置信度的路由,以及在置信度较低时的可选递归探索。
智能 LLM 跳过:短于 query_analysis_threshold(默认 200 个字符)的查询会完全跳过 LLM 查询分析,即使在 deep 模式下也是如此。像 “What database do we use?” 这样的短查询本身就已经是不错的搜索短语 - LLM 分析增益不大。这样对于常见短查询,每次 recall 可节省约 1-3 秒。只有较长的查询(例如完整的任务描述)才会通过 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, # Only synthesize when very confident confidence_threshold_low=0.4, # Explore deeper more aggressively exploration_budget=2, # Allow up to 2 exploration rounds query_analysis_threshold=200, # Skip LLM for queries shorter than this)Embedding 配置
Section titled “Embedding 配置”Memory 需要一个 embedding 模型将文本转换为向量,以便进行语义搜索。默认情况下,Memory() 使用 OpenAI text-embedding-3-large embeddings,生成 3072 维向量。请设置 OPENAI_API_KEY 走默认路径,或者用以下三种方式之一配置自定义 embedder。
直接传给 Memory
Section titled “直接传给 Memory”from crewai import Memory
# As a config dictmemory = Memory(embedder={"provider": "openai", "config": {"model_name": "text-embedding-3-large"}})
# As a pre-built callablefrom crewai.rag.embeddings.factory import build_embedderembedder = build_embedder({"provider": "ollama", "config": {"model_name": "mxbai-embed-large"}})memory = Memory(embedder=embedder)通过 Crew 的 Embedder 配置传入
Section titled “通过 Crew 的 Embedder 配置传入”当使用 memory=True 时,crew 的 embedder 配置会被透传:
from crewai import Crew
crew = Crew( agents=[...], tasks=[...], memory=True, embedder={"provider": "openai", "config": {"model_name": "text-embedding-3-large"}},)Provider 示例
Section titled “Provider 示例”OpenAI(默认)
memory = Memory(embedder={ "provider": "openai", "config": { "model_name": "text-embedding-3-large", # "api_key": "sk-...", # or set OPENAI_API_KEY env var },})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": "...", # or set GOOGLE_API_KEY env var },})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": "...", # or set COHERE_API_KEY env var },})VoyageAI
memory = Memory(embedder={ "provider": "voyageai", "config": { "model": "voyage-3", # "api_key": "...", # or set VOYAGE_API_KEY env var },})AWS Bedrock
memory = Memory(embedder={ "provider": "amazon-bedrock", "config": { "model_name": "amazon.titan-embed-text-v1", # Uses default AWS credentials (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": "...", # or set JINA_API_KEY env var },})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
# Pass any callable that takes a list of strings and returns a list of vectorsdef my_embedder(texts: list[str]) -> list[list[float]]: # Your embedding logic here return [[0.1, 0.2, ...] for _ in texts]
memory = Memory(embedder=my_embedder)Provider 参考
Section titled “Provider 参考”| Provider | Key | Typical Model | Notes |
|---|---|---|---|
| OpenAI | openai | text-embedding-3-large | 默认。设置 OPENAI_API_KEY。 |
| Ollama | ollama | mxbai-embed-large | 本地,无需 API key。 |
| Azure OpenAI | azure | text-embedding-3-large | 默认模型。需要 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 配置
Section titled “LLM 配置”Memory 会使用 LLM 进行保存分析(scope、categories、importance 推断)、consolidation 决策,以及 deep recall 的查询分析。你可以配置要使用的模型。
from crewai import Memory, LLM
# Default: gpt-4o-minimemory = Memory()
# Use a different OpenAI modelmemory = Memory(llm="gpt-4o")
# Use Anthropicmemory = Memory(llm="anthropic/claude-3-haiku-20240307")
# Use Ollama for fully local/private analysismemory = Memory(llm="ollama/llama3.2")
# Use Google Geminimemory = Memory(llm="gemini/gemini-2.0-flash")
# Pass a pre-configured LLM instance with custom settingsllm = LLM(model="gpt-4o", temperature=0)memory = Memory(llm=llm)LLM 是延迟初始化的 - 只有在第一次需要时才会创建。这意味着即使 API keys 没有设置,Memory() 在构造阶段也不会失败。只有当 LLM 真正被调用时,错误才会显现(例如在没有显式 scope/categories 时保存,或在 deep recall 期间)。
如果要完全离线/私有运行,请同时为 LLM 和 embedder 使用本地模型:
memory = Memory( llm="ollama/llama3.2", embedder={"provider": "ollama", "config": {"model_name": "mxbai-embed-large"}},)- 默认:LanceDB,存储在
./.crewai/memory下(或者$CREWAI_STORAGE_DIR/memory,如果设置了该环境变量;或者你通过storage="path/to/dir"传入的路径)。 - 自定义后端:实现
StorageBackend协议(见crewai.memory.storage.backend),并将实例传给Memory(storage=your_backend)。
查看 scope 层级、categories 和 records:
memory.tree() # Formatted tree of scopes and record countsmemory.tree("/project", max_depth=2) # Subtree viewmemory.info("/project") # ScopeInfo: record_count, categories, oldest/newestmemory.list_scopes("/") # Immediate child scopesmemory.list_categories() # Category names and countsmemory.list_records(scope="/project/alpha", limit=20) # Records in a scope, newest firstFailure Behavior
Section titled “Failure Behavior”如果 LLM 在分析过程中失败(网络错误、速率限制、无效响应),memory 会优雅降级:
- Save analysis - 会记录警告,并且 memory 仍然会被保存,但会使用默认 scope
/、空 categories 和 importance0.5。 - Extract memories - 整个内容会作为单条 memory 保存,这样不会丢失任何内容。
- Query analysis - recall 会退回到简单的 scope 选择和向量搜索,因此你仍然能得到结果。
这些分析失败不会抛出异常;只有存储或 embedder 失败才会抛错。
memory 内容会发送给你配置的 LLM 进行分析(保存时的 scope/categories/importance、查询分析,以及可选的 deep recall)。如果数据敏感,请使用本地 LLM(例如 Ollama)或确保你的 provider 满足合规要求。
Memory 事件
Section titled “Memory 事件”所有 memory 操作都会发出 source_type="unified_memory" 的事件。你可以监听耗时、错误和内容。
| Event | Description | Key Properties |
|---|---|---|
| MemoryQueryStartedEvent | Query begins | query, limit |
| MemoryQueryCompletedEvent | Query succeeds | query, results, query_time_ms |
| MemoryQueryFailedEvent | Query fails | query, error |
| MemorySaveStartedEvent | Save begins | value, metadata |
| MemorySaveCompletedEvent | Save succeeds | value, save_time_ms |
| MemorySaveFailedEvent | Save fails | value, error |
| MemoryRetrievalStartedEvent | Agent retrieval starts | task_id |
| MemoryRetrievalCompletedEvent | Agent retrieval done | task_id, memory_content, retrieval_time_ms |
示例:监控查询耗时:
from crewai.events import BaseEventListener, MemoryQueryCompletedEvent
class 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(...)。
召回很慢?
- 对常规 agent 上下文使用
depth="shallow"。把depth="deep"留给复杂查询。 - 提高
query_analysis_threshold,让更多查询跳过 LLM 分析。
日志里有 LLM 分析错误?
- Memory 仍会使用安全默认值完成保存/召回。如果想要完整的 LLM 分析,请检查 API keys、速率限制和模型可用性。
日志里有后台保存错误?
- Memory 保存会在后台线程中运行。错误会以
MemorySaveFailedEvent发出,但不会让 agent 崩溃。查看日志以定位根因(通常是 LLM 或 embedder 连接问题)。
Embedding 维度不匹配?
- 现有本地 memory 存储可能是用不同的 embedding 模型创建的。默认 OpenAI memory embedder 现在是
text-embedding-3-large(3072 维),而旧存储通常使用 1536 维 embeddings。进行本地测试时,运行crewai reset-memories -m、删除本地 memory 存储目录,或者显式配置旧的 embedder 模型。
并发写入冲突?
- LanceDB 操作使用共享锁串行化,并会在冲突时自动重试。这可以处理指向同一数据库的多个
Memory实例(例如 agent memory + crew memory)。无需额外操作。
从终端浏览 memory:
crewai memory # Opens the TUI browsercrewai memory --storage-path ./my_memory # Point to a specific directory重置 memory(例如用于测试):
crew.reset_memories(command_type="memory") # Resets unified memory# Or on a Memory instance:memory.reset() # All scopesmemory.reset(scope="/project/old") # Only that subtree所有配置都作为关键字参数传给 Memory(...)。每个参数都有合理的默认值。
| Parameter | Default | Description |
|---|---|---|
llm | "gpt-4o-mini" | 用于分析的 LLM(模型名或 BaseLLM 实例)。 |
storage | "lancedb" | 存储后端("lancedb"、路径字符串,或 StorageBackend 实例)。 |
embedder | None(OpenAI text-embedding-3-large) | embedder(配置字典、可调用对象,或 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 | 未提供且跳过 LLM 分析时使用的重要性。 |
batch_dedup_threshold | 0.98 | remember_many() 批次中丢弃近似重复项的 cosine similarity 阈值。 |
confidence_threshold_high | 0.8 | recall 置信度高于此值时直接返回结果。 |
confidence_threshold_low | 0.5 | recall 置信度低于此值时触发更深的探索。 |
complex_query_threshold | 0.7 | 对于复杂查询,低于此置信度时继续深入探索。 |
exploration_budget | 1 | deep recall 期间允许的 LLM 驱动探索轮数。 |
query_analysis_threshold | 200 | 短于此长度(字符数)的查询在 deep recall 中会跳过 LLM 分析。 |