主题
记忆
利用 CrewAI 中的统一记忆系统增强智能体能力。
概述
CrewAI 提供统一记忆系统 -- 一个单一的 Memory 类,用一个智能 API 取代了独立的短期记忆、长期记忆、实体记忆和外部记忆类型。记忆在保存时使用 LLM 分析内容(推断作用域、类别和重要性),并支持自适应深度召回,使用融合语义相似度、新近度和重要性的复合评分。
你可以通过四种方式使用记忆:独立使用(脚本、notebooks)、与团队一起使用、与智能体一起使用,或在流程内部使用。
快速开始
python
from crewai import Memory
memory = Memory()
# Store -- the LLM infers scope, categories, and importance
memory.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 project
memory = Memory(recency_weight=0.5, recency_half_life_days=7)
# Forget
memory.forget(scope="/project/old")
# Explore the self-organized scope tree
print(memory.tree())
print(memory.info("/"))使用记忆的四种方式
独立使用
在脚本、notebook、CLI 工具中使用记忆,或将其作为独立知识库 -- 无需智能体或团队。
python
from crewai import Memory
memory = Memory()
# Build up knowledge
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.")
# Later, recall what you need
matches = 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 text
raw = """Meeting notes: We decided to migrate from MySQL to PostgreSQL
next 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)与团队一起使用
传入 memory=True 以使用默认设置,或传入已配置的 Memory 实例以实现自定义行为。
python
from crewai import Crew, Agent, Task, Process, Memory
# Option 1: Default memory
crew = Crew(
agents=[researcher, writer],
tasks=[research_task, writing_task],
process=Process.sequential,
memory=True,
verbose=True,
)
# Option 2: Custom memory with tuned scoring
memory = 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 时,团队会创建一个默认的 Memory(),并自动传入团队的 embedder 配置。团队中的所有智能体共享团队记忆,除非某个智能体拥有自己的记忆。在没有自定义 embedder 的情况下,记忆使用 OpenAI text-embedding-3-large 嵌入。
每个任务完成后,团队会自动从任务输出中提取离散事实并存储。每个任务开始前,智能体会从记忆中召回相关上下文,并将其注入任务提示词。
与智能体一起使用
智能体可以使用团队的共享记忆(默认),或接收一个限定作用域的视图以获取私有上下文。
python
from crewai import Agent, Memory
memory = Memory()
# Researcher gets a private scope -- only sees /agent/researcher
researcher = 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
)这种模式让研究员获得私有的发现,而写作者从团队的共享记忆中读取。
与流程一起使用
每个 Flow 都内置记忆。在任何流程方法中,使用 self.remember()、self.recall() 和 self.extract_memories()。
python
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}"有关流程中记忆的更多信息,请参阅 流程文档。
分层作用域
什么是作用域
记忆被组织成作用域的分层树,类似于文件系统。每个作用域都是一个路径,例如 /、/project/alpha 或 /agent/researcher/findings。
/
/company
/company/engineering
/company/product
/project
/project/alpha
/project/beta
/agent
/agent/researcher
/agent/writer作用域提供上下文相关记忆 -- 当你在某个作用域内召回时,只会搜索树的该分支,从而同时提高精度和性能。
作用域推断如何工作
当你调用 remember() 而未指定作用域时,LLM 会分析内容和现有作用域树,然后建议最佳放置位置。如果没有现有作用域适合,它会创建一个新的作用域。随着时间推移,作用域树会从内容本身有机地生长出来 -- 你不需要预先设计 schema。
python
memory = Memory()
# LLM infers scope from content
memory.remember("We chose PostgreSQL for the user database.")
# -> might be placed under /project/decisions or /engineering/database
# You can also specify scope explicitly
memory.remember("Sprint velocity is 42 points", scope="/team/metrics")可视化作用域树
python
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 将所有操作限制到树的某个分支。使用它的智能体或代码只能在该子树内查看和写入。
python
memory = Memory()
# Create a scope for a specific agent
agent_memory = memory.scope("/agent/researcher")
# Everything is relative to /agent/researcher
agent_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 subscope
project_memory = agent_memory.subscope("project-alpha")
# -> /agent/researcher/project-alpha作用域设计最佳实践
从扁平开始,让 LLM 组织。 不要预先过度设计作用域层级。从
memory.remember(content)开始,让 LLM 的作用域推断在内容积累时创建结构。使用
/{entity_type}/{identifier}模式。 自然层级会从/project/alpha、/agent/researcher、/company/engineering、/customer/acme-corp等模式中浮现。按关注点划分作用域,而不是按数据类型。 使用
/project/alpha/decisions,而不是/decisions/project/alpha。这会让相关内容保持在一起。保持较浅的深度(2-3 层)。 嵌套过深的作用域会变得过于稀疏。
/project/alpha/architecture很好;/project/alpha/architecture/decisions/databases/postgresql则太深。知道时使用显式作用域,不知道时让 LLM 推断。 如果你要存储已知的项目决策,传入
scope="/project/alpha/decisions"。如果你要存储自由形式的智能体输出,省略作用域,让 LLM 自行判断。
用例示例
多项目团队:
python
memory = Memory()
# Each project gets its own branch
memory.remember("Using microservices architecture", scope="/project/alpha/architecture")
memory.remember("GraphQL API for client apps", scope="/project/beta/api")
# Recall across all projects
memory.recall("API design decisions")
# Or within a specific project
memory.recall("API design", scope="/project/beta")每个智能体的私有上下文与共享知识:
python
memory = Memory()
# Researcher has private findings
researcher_memory = memory.scope("/agent/researcher")
# Writer can read from both its own scope and shared company knowledge
writer_view = memory.slice(
scopes=["/agent/writer", "/company/knowledge"],
read_only=True,
)客户支持(按客户划分上下文):
python
memory = Memory()
# Each customer gets isolated context
memory.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 agents
memory.remember("Rate limit is 1000 req/min on enterprise plan", scope="/product/docs")记忆切片
什么是切片
MemorySlice 是跨多个(可能不相交的)作用域的视图。与作用域(限制到一个子树)不同,切片允许你同时从多个分支召回。
何时使用切片与作用域
- 作用域:当智能体或代码块应限制到单个子树时使用。示例:一个只能看到
/agent/researcher的智能体。 - 切片:当你需要组合多个分支的上下文时使用。示例:一个从自身作用域以及共享公司知识中读取的智能体。
只读切片
最常见的模式:授予智能体对多个分支的读取权限,同时不允许其写入共享区域。
python
memory = Memory()
# Agent can recall from its own scope AND company knowledge,
# but cannot write to company knowledge
agent_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)读写切片
当禁用只读时,你可以写入任何包含的作用域,但必须显式指定是哪个作用域。
python
view = memory.slice(scopes=["/team/alpha", "/team/beta"], read_only=False)
# Must specify scope when writing
view.remember("Cross-team decision", scope="/team/alpha", categories=["decisions"])复合评分
召回结果按三个信号的加权组合排序:
composite = semantic_weight * similarity + recency_weight * decay + importance_weight * importance其中:
- 相似度 = 来自向量索引的
1 / (1 + distance)(0 到 1) - 衰减 =
0.5^(age_days / half_life_days)-- 指数衰减(今天为 1.0,半衰期时为 0.5) - 重要性 = 记录的重要性分数(0 到 1),在编码时设置
在 Memory 构造函数上直接配置这些参数:
python
# Sprint retrospective: favor recent memories, short half-life
memory = 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-life
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 分析层
记忆以三种方式使用 LLM:
- 保存时 -- 当你省略作用域、类别或重要性时,LLM 会分析内容并建议作用域、类别、重要性及元数据(实体、日期、主题)。
- 召回时 -- 对于深度/自动召回,LLM 会分析查询(关键词、时间提示、建议作用域、复杂度)以引导检索。
- 提取记忆 --
extract_memories(content)将原始文本(例如任务输出)拆分为离散的记忆陈述。智能体在对每条陈述调用remember()之前会使用它,以便存储原子事实,而不是一个大块。
所有分析在 LLM 失败时都会优雅降级 -- 参见 失败行为。
记忆整合
保存新内容时,编码流水线会自动检查存储中是否存在相似的现有记录。如果相似度高于 consolidation_threshold(默认 0.85),LLM 会决定如何处理:
- keep -- 现有记录仍然准确且不冗余。
- update -- 应使用新信息更新现有记录(LLM 提供合并后的内容)。
- delete -- 现有记录已过时、被取代或与事实矛盾。
- insert_new -- 新内容是否也应作为单独记录插入。
这可以防止重复内容积累。例如,如果你三次保存“CrewAI 确保可靠运行”,整合会识别出重复项,并且只保留一条记录。
批内去重
使用 remember_many() 时,同一批次内的项会在写入存储前相互比较。如果两个项的余弦相似度 >= batch_dedup_threshold(默认 0.98),后一项会被静默丢弃。这可以在单批次内捕获精确或几乎精确的重复项,而无需任何 LLM 调用(纯向量数学)。
python
# 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() 是非阻塞的 -- 它将编码流水线提交到后台线程并立即返回。这意味着智能体可以在保存记忆的同时继续执行下一个任务。
python
# Returns immediately -- save happens in background
memory.remember_many(["Fact A.", "Fact B.", "Fact C."])
# recall() automatically waits for pending saves before searching
matches = memory.recall("facts") # sees all 3 records读屏障
每次 recall() 调用都会在搜索前自动调用 drain_writes(),确保查询始终能看到最新持久化的记录。这是透明的 -- 你永远不需要考虑它。
团队关闭
当团队完成时,kickoff() 会在其 finally 块中排空所有待处理的记忆保存,因此即使团队在后台保存仍在进行时完成,也不会丢失任何保存。
独立使用
对于没有团队生命周期的脚本或 notebook,请显式调用 drain_writes() 或 close():
python
memory = Memory()
memory.remember_many(["Fact A.", "Fact B."])
# Option 1: Wait for pending saves
memory.drain_writes()
# Option 2: Drain and shut down the background pool
memory.close()来源与隐私
每条记忆记录都可以携带用于来源追踪的 source 标签,以及用于访问控制的 private 标志。
来源追踪
source 参数标识记忆的来源:
python
# Tag memories with their origin
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 only memories from a specific source
matches = memory.recall("user preferences", source="user:alice")私有记忆
仅当 source 匹配时,私有记忆才会对召回可见:
python
# Store a private memory
memory.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 source
matches = memory.recall("API key", include_private=True)这在多用户或企业版部署中特别有用,因为不同用户的记忆应当相互隔离。
RecallFlow(深度召回)
recall() 支持两种深度:
depth="shallow"-- 使用复合评分的直接向量搜索。快速(~200ms),无 LLM 调用。depth="deep"(默认) -- 运行多步 RecallFlow:查询分析、作用域选择、并行向量搜索、基于置信度的路由,以及置信度较低时的可选递归探索。
智能 LLM 跳过:短于 query_analysis_threshold(默认 200 个字符)的查询会完全跳过 LLM 查询分析,即使在深度模式下也是如此。诸如“我们使用什么数据库?”这样的短查询本身就是不错的搜索短语——LLM 分析带来的价值很小。对于典型的短查询,这每次回忆可节省 ~1-3s。只有较长的查询(例如完整任务描述)才会经过 LLM 提炼为有针对性的子查询。
python
# Shallow: pure vector search, no LLM
matches = memory.recall("What did we decide?", limit=10, depth="shallow")
# Deep (default): intelligent retrieval with LLM analysis for long queries
matches = memory.recall(
"Summarize all architecture decisions from this quarter",
limit=10,
depth="deep",
)控制 RecallFlow 路由的置信度阈值是可配置的:
python
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
)嵌入器配置
记忆需要一个嵌入模型来将文本转换为向量以进行语义搜索。默认情况下,Memory() 使用 OpenAI text-embedding-3-large 嵌入,生成 3072 维向量。为默认路径设置 OPENAI_API_KEY,或通过以下三种方式之一配置自定义嵌入器。
WARNING
使用 1536 维嵌入(例如 text-embedding-3-small 或 text-embedding-ada-002)创建的现有本地记忆存储,可能与 text-embedding-3-large 默认值不兼容。这同时适用于 OpenAI 和 Azure OpenAI 服务商——Azure 的默认嵌入模型也已从 text-embedding-ada-002 改为 text-embedding-3-large。如果本地测试因嵌入维度不匹配而失败,请使用 crewai reset-memories -m 重置记忆、删除本地记忆存储目录,或在迁移之前显式配置较旧的嵌入器模型。
直接传递给 Memory
python
from crewai import Memory
# As a config dict
memory = Memory(embedder={"provider": "openai", "config": {"model_name": "text-embedding-3-large"}})
# As a pre-built callable
from crewai.rag.embeddings.factory import build_embedder
embedder = build_embedder({"provider": "ollama", "config": {"model_name": "mxbai-embed-large"}})
memory = Memory(embedder=embedder)通过 Crew 嵌入器配置
使用 memory=True 时,团队的 embedder 配置会被透传:
python
from crewai import Crew
crew = Crew(
agents=[...],
tasks=[...],
memory=True,
embedder={"provider": "openai", "config": {"model_name": "text-embedding-3-large"}},
)服务商示例
OpenAI(默认)
python
memory = Memory(embedder={
"provider": "openai",
"config": {
"model_name": "text-embedding-3-large",
# "api_key": "sk-...", # or set OPENAI_API_KEY env var
},
})Ollama(本地、私有)
python
memory = Memory(embedder={
"provider": "ollama",
"config": {
"model_name": "mxbai-embed-large",
"url": "http://localhost:11434/api/embeddings",
},
})Azure OpenAI
python
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
python
memory = Memory(embedder={
"provider": "google-generativeai",
"config": {
"model_name": "gemini-embedding-001",
# "api_key": "...", # or set GOOGLE_API_KEY env var
},
})Google Vertex AI
python
memory = Memory(embedder={
"provider": "google-vertex",
"config": {
"model_name": "gemini-embedding-001",
"project_id": "your-gcp-project-id",
"location": "us-central1",
},
})Cohere
python
memory = Memory(embedder={
"provider": "cohere",
"config": {
"model_name": "embed-english-v3.0",
# "api_key": "...", # or set COHERE_API_KEY env var
},
})VoyageAI
python
memory = Memory(embedder={
"provider": "voyageai",
"config": {
"model": "voyage-3",
# "api_key": "...", # or set VOYAGE_API_KEY env var
},
})AWS Bedrock
python
memory = Memory(embedder={
"provider": "amazon-bedrock",
"config": {
"model_name": "amazon.titan-embed-text-v1",
# Uses default AWS credentials (boto3 session)
},
})Hugging Face
python
memory = Memory(embedder={
"provider": "huggingface",
"config": {
"model_name": "sentence-transformers/all-MiniLM-L6-v2",
},
})Jina
python
memory = Memory(embedder={
"provider": "jina",
"config": {
"model_name": "jina-embeddings-v2-base-en",
# "api_key": "...", # or set JINA_API_KEY env var
},
})IBM WatsonX
python
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",
},
})自定义嵌入器
python
# Pass any callable that takes a list of strings and returns a list of vectors
def 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)服务商参考
| 服务商 | 键 | 典型模型 | 说明 |
|---|---|---|---|
| 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 | -- | 需要 embedding_callable。 |
LLM 配置
记忆使用 LLM 进行保存分析(作用域、类别、重要性推断)、整合决策以及深度回忆查询分析。你可以配置使用哪个模型。
python
from crewai import Memory, LLM
# Default: gpt-4o-mini
memory = Memory()
# Use a different OpenAI model
memory = Memory(llm="gpt-4o")
# Use Anthropic
memory = Memory(llm="anthropic/claude-3-haiku-20240307")
# Use Ollama for fully local/private analysis
memory = Memory(llm="ollama/llama3.2")
# Use Google Gemini
memory = Memory(llm="gemini/gemini-3.7-flash")
# Pass a pre-configured LLM instance with custom settings
llm = LLM(model="gpt-4o", temperature=0)
memory = Memory(llm=llm)LLM 是惰性初始化的——只有在首次需要时才会创建。这意味着即使没有设置 API key,Memory() 在构造时也绝不会失败。错误只会在实际调用 LLM 时才会暴露(例如在未显式指定作用域/类别时保存,或在深度回忆期间)。
如需完全离线/私有运行,可同时为 LLM 和嵌入器使用本地模型:
python
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)。对于生产部署,你可以使用crewai-mongodb-memory包中的MongoDBStorageBackend。了解更多,请参阅 MongoDB 实现教程。
探索
查看作用域层级、类别和记录:
python
memory.tree() # Formatted tree of scopes and record counts
memory.tree("/project", max_depth=2) # Subtree view
memory.info("/project") # ScopeInfo: record_count, categories, oldest/newest
memory.list_scopes("/") # Immediate child scopes
memory.list_categories() # Category names and counts
memory.list_records(scope="/project/alpha", limit=20) # Records in a scope, newest first故障行为
如果 LLM 在分析期间失败(网络错误、限流、无效响应),记忆会优雅降级:
- 保存分析——记录一条警告,记忆仍会以默认作用域
/、空类别和重要性0.5存储。 - 提取记忆——完整内容会作为单条记忆存储,因此不会丢失任何内容。
- 查询分析——回忆会回退到简单的作用域选择和向量搜索,因此你仍能获得结果。
这些分析失败不会抛出异常;只有存储或嵌入器失败才会抛出异常。
隐私说明
记忆内容会被发送到配置的 LLM 进行分析(保存时的作用域/类别/重要性,查询分析以及可选的深度回忆)。对于敏感数据,请使用本地 LLM(例如 Ollama),或确保你的服务商满足你的合规要求。
记忆事件
所有记忆操作都会发出带有 source_type="unified_memory" 的事件。你可以监听耗时、错误和内容。
| 事件 | 描述 | 关键属性 |
|---|---|---|
| MemoryQueryStartedEvent | 查询开始 | query、limit |
| MemoryQueryCompletedEvent | 查询成功 | query、results、query_time_ms |
| MemoryQueryFailedEvent | 查询失败 | query、error |
| MemorySaveStartedEvent | 保存开始 | value、metadata |
| MemorySaveCompletedEvent | 保存成功 | value、save_time_ms |
| MemorySaveFailedEvent | 保存失败 | value、error |
| MemoryRetrievalStartedEvent | 智能体检索开始 | task_id |
| MemoryRetrievalCompletedEvent | 智能体检索完成 | task_id、memory_content、retrieval_time_ms |
示例:监控查询耗时:
python
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")故障排查
记忆没有持久化?
- 确保存储路径可写(默认
./.crewai/memory)。传入storage="./your_path"以使用其他目录,或设置CREWAI_STORAGE_DIR环境变量。 - 使用团队时,确认已设置
memory=True或memory=Memory(...)。
回忆很慢?
- 常规智能体上下文使用
depth="shallow"。将depth="deep"留给复杂查询。 - 提高
query_analysis_threshold,让更多查询跳过 LLM 分析。
日志中出现 LLM 分析错误?
- 记忆仍会以安全的默认值保存/回忆。如果你希望获得完整的 LLM 分析,请检查 API key、限流和模型可用性。
日志中出现后台保存错误?
- 记忆保存运行在后台线程中。错误会以
MemorySaveFailedEvent发出,但不会导致智能体崩溃。检查日志以找出根本原因(通常是 LLM 或嵌入器连接问题)。
嵌入维度不匹配?
- 现有的本地记忆存储可能是用不同的嵌入模型创建的。默认的 OpenAI 记忆嵌入器现在是
text-embedding-3-large(3072 维),而较早的存储通常使用 1536 维嵌入。对于本地测试,请运行crewai reset-memories -m、删除本地记忆存储目录,或显式配置之前的嵌入器模型。
并发写入冲突?
- LanceDB 操作通过共享锁串行化,并在冲突时自动重试。这可以处理多个
Memory实例指向同一数据库的情况(例如智能体记忆 + 团队记忆)。无需任何操作。
从终端浏览记忆:
bash
crewai memory # Opens the TUI browser
crewai memory --storage-path ./my_memory # Point to a specific directory重置记忆(例如用于测试):
python
crew.reset_memories(command_type="memory") # Resets unified memory
# Or on a Memory instance:
memory.reset() # All scopes
memory.reset(scope="/project/old") # Only that subtree配置参考
所有配置都作为关键字参数传给 Memory(...)。每个参数都有合理的默认值。
| 参数 | 默认值 | 描述 |
|---|---|---|
llm | "gpt-4o-mini" | 用于分析的 LLM(模型名称或 BaseLLM 实例)。 |
storage | "lancedb" | 存储后端("lancedb"、路径字符串或 StorageBackend 实例)。 |
embedder | None(OpenAI text-embedding-3-large) | 嵌入器(配置字典、可调用对象,或 None 表示使用默认 OpenAI)。 |
recency_weight | 0.3 | 综合评分中新近度的权重。 |
semantic_weight | 0.5 | 综合评分中语义相似度的权重。 |
importance_weight | 0.2 | 综合评分中重要性的权重。 |
recency_half_life_days | 30 | 新近度分数减半所需的天数(指数衰减)。 |
consolidation_threshold | 0.85 | 相似度高于该值时,在保存时触发合并。设为 1.0 可禁用。 |
consolidation_limit | 5 | 合并过程中最多比较的已有记录数。 |
default_importance | 0.5 | 未提供且跳过 LLM 分析时赋予的重要性。 |
batch_dedup_threshold | 0.98 | 在 remember_many() 批次内丢弃近似重复项的余弦相似度阈值。 |
confidence_threshold_high | 0.8 | 召回置信度高于该值时直接返回结果。 |
confidence_threshold_low | 0.5 | 召回置信度低于该值时触发更深入的探索。 |
complex_query_threshold | 0.7 | 对于复杂查询,置信度低于该值时进行更深入的探索。 |
exploration_budget | 1 | 深度召回期间由 LLM 驱动的探索轮数。 |
query_analysis_threshold | 200 | 短于该值(字符数)的查询在深度召回期间跳过 LLM 分析。 |