跳到主要内容

记忆、知识与可靠性外围

30 秒导读: 前五章讲的是 PraisonAI 怎么"想"和"跑"——Agent 单体、工具、LLM 层、多智能体编排、 确定性工作流。这一章讲的是托住它们的支撑层:让 agent 记得住(记忆)、查得到(知识/RAG)、 跑得稳(可靠性/安全)。这三组子系统不在主执行线上,而是被主链路"按需取用"的公共货架。 本章是导航式讲解——告诉你每个子系统解决什么问题、入口符号叫什么、在哪个文件,你可以按需下钻源码。

本章与兄弟章互补、不重复它们的内容:

源码位置:全部在 src/praisonai-agents/praisonaiagents/ 下。下文所有 file:line 均相对该目录 (例:memory/memory.py:38)。


1. 这是什么(零基础也能懂)

一句话定义: 这一章讲 PraisonAI 里让 agent"脑能记、事能查、险能拦"的三组基础设施。

先用一个类比建立直觉。 把一个裸 LLM 想成一个很聪明、但有严重健忘症、还被关在小黑屋里的人。 主链路负责让它"开口说话",而本章这三层负责补它天生缺的能力:

它缺什么这一层补什么对应目录
记性(说完就忘,跨轮/跨会话记不住)记忆:短期滑动上下文 + 长期可检索存储 + 质量打分决定"什么值得长期记"memory/
查资料的能力(不知道你私有文档里写了啥)知识 / RAG:把大文档切块、嵌入、按相似度召回、附引用喂回模型knowledge/ + rag/
危险动作的刹车与"卡死自救"可靠性 / 安全:输出校验、死循环检测、快照回滚、代码沙箱、策略与人工审批guardrails/ escalation/ snapshot/ checkpoints/ sandbox/ policy/ approval/ telemetry/

它们各自能做什么:

  • 记忆:把一次对话里"值得留下的"信息按质量分数筛进长期库,下次任务开始时自动召回相关片段拼进上下文; 后端可换(内存 / SQLite / Mongo / mem0 / chroma),甚至支持图记忆(Neo4j/Memgraph)。
  • 知识 / RAG:把任意文件读入→切块→嵌入→索引,查询时召回 + 重排 + 生成带引用编号的答案。
  • 可靠性:LLM 输出过一道 guardrail 校验;检测"反复做同一件事"的 doom loop 并触发恢复; agent 改文件前打快照,可 undo/redo/diff;要执行的代码先过静态危险扫描 + 沙箱隔离; 高危工具调用被 policy 拦下、走 approval 人工审批。

一句话直觉: 记忆是"上下文当内存、数据库当磁盘";RAG 是"给 agent 配了个可搜索、会标出处的图书馆"; 可靠性层是"在流水线上装了校验器、断路器、后悔药和安全阀"。

本节不出现底层代码。目标:完全不懂的人读完知道"这三层是干嘛的"。


2. 顶层全景(它大概怎么转)

怎么读这张图: 中间竖线是 Agent 主执行线(第 1 章),两侧/下方是本章三组支撑货架; 箭头表示"主线在某个时机取用某个货架"。三组彼此独立,可单独开关。

┌───────────────────────────────┐
│ Agent 主执行线 (ch.01) │
│ 收到任务 → 想 → 调工具 → 回答 │
└───┬───────────┬───────────┬────┘
①任务开始:召回记忆 │ │ │ ③改文件/调工具前后:安全闸
⑤任务结束:写回记忆 │ │ ②需要外部知识
▼ ▼ ▼
┌──────────────┐ ┌──────────┐ ┌──────────────────────────┐
│ 记忆 memory │ │ 知识/RAG │ │ 可靠性 / 安全外围 │
│ 短期 + 长期 │ │ knowledge│ │ guardrail 校输出 │
│ 质量打分筛选 │ │ + rag │ │ doom-loop 断路器 │
│ 多后端适配 │ │ 切块/召回 │ │ snapshot/checkpoint 后悔药 │
│ │ │ 重排/引用 │ │ sandbox/policy/approval 闸 │
└──────┬───────┘ └────┬─────┘ └───────────┬──────────────┘
│ 换后端 │ 换向量库 │
▼ ▼ ▼
in-mem/SQLite/ InMemoryVector/ 静态扫描 + 沙箱 +
Mongo/mem0/chroma 外部向量库 策略 + 人工审批

各货架一句话职责与入口:

货架干什么入口符号文件
记忆短/长期分层存取 + 质量打分 + 上下文拼装Memorymemory/memory.py:38
记忆后端把存储抽象成可注册的适配器register_memory_adaptermemory/adapters/registry.py:44
知识文件→切块→嵌入→索引→检索的门面Knowledgeknowledge/knowledge.py:53
RAG检索 + 生成带引用的答案RAG / RAGConfigrag/pipeline.py:77 / rag/models.py:188
输出校验对 LLM 输出做 guardrail 判定GuardrailResult / LLMGuardrailguardrails/guardrail_result.py:13
断路器检测死循环并给恢复动作DoomLoopDetector / _is_doom_loopescalation/doom_loop.py:87 / agent/agent.py:3070
后悔药文件快照的 undo/redo/diffundo/redo/diffagent/agent.py:2935
沙箱代码静态危险扫描 + 隔离执行check_code_safety / SandboxManagersandbox/security.py:82 / sandbox/manager.py:23
策略/审批高危工具拦截 + 人工审批PolicyEngine / ApprovalRegistrypolicy/engine.py:17 / approval/registry.py:81
遥测匿名使用统计MinimalTelemetrytelemetry/telemetry.py:78

3. 记忆:记得住

这节讲什么: 一次对话里哪些信息值得留下、怎么打分、存哪、下次怎么召回。核心入口是一个 "单文件记忆管理器"类:

# 真实:memory/memory.py:38
class Memory(SearchMixin, MemoryCoreMixin):
"""A single-file memory manager covering STM/LTM/entity/user/quality-score/graph ..."""

Memory 用多重继承拼装:自身文件 memory.py 定义主实现,SearchMixin(memory/search.py:20)和 MemoryCoreMixin(memory/core.py:20)是为向后兼容而拆出的同名方法。注意 MRO:Memory 自己定义的 store_short_term/compute_quality_score会覆盖 mixin 版本,所以读主逻辑看 memory.py 即可, mixin 是历史分解的产物。

3.1 短期 vs 长期分层

  • 短期记忆(STM):一次会话内的临时上下文,写入 store_short_term(memory/memory.py:509), 按 search_short_term(memory/memory.py:581)召回。
  • 长期记忆(LTM):跨会话的持久知识,store_long_term(memory/memory.py:770)/ search_long_term(memory/memory.py:907)。
  • 另有两类专用记忆:实体记忆(命名实体的结构化信息)与用户记忆(每个用户的偏好/历史), 在类 docstring(memory/memory.py:39-47)里列出。

这条分层的价值:不是所有东西都值得进长期库——STM 便宜可丢,LTM 要挑过。挑选的依据就是"质量分"。

3.2 质量打分:决定"什么值得长期记"

要解决的小问题: 记忆库会被垃圾撑爆。需要一个 0–1 的分数,给每条记忆定"含金量", 低于阈值的不进长期库、检索时也过滤掉。

打分公式(简化直觉,非源码):

# 示意,非源码 —— 四个子指标的加权平均
score = (completeness * 0.25 # 信息完整吗
+ relevance * 0.25 # 和任务相关吗
+ clarity * 0.25 # 表述清楚吗
+ accuracy * 0.25) # 准确吗

真实实现: compute_quality_score(memory/memory.py:471)就是这个加权平均,默认四项各 0.25、 结果 round(total, 3)。写入前由 _process_quality_metrics(memory/memory.py:1742)把分数塞进 metadata;若外部评估器直接给了 evaluator_quality,则跳过子指标直接用它。

分数怎么起作用: 检索时 search_short_term/search_long_term 接受 min_qualityrelevance_cutoff 两个门槛(memory/memory.py:585-586911-912),低于门槛的记录被过滤—— 质量分在这一侧发挥闸门作用。

3.3 上下文拼装:召回后怎么喂回 agent

一次任务开始时,build_context_for_task(memory/memory.py:1542)把短期 + 长期 + 实体 + 用户 四路记忆合并、去重、清洗成一段文本块,拼进 agent 的上下文。参数 include_in_output 控制这段记忆 是否真进输出(默认仅在 debug 日志开启时包含),避免污染正式回答。

3.4 多后端适配:换存储不改上层

要解决的小问题: 有人只想要内存里跑,有人要 SQLite 单机持久,有人要 Mongo/云向量, 有人要接 mem0 或 chroma。上层 Memory 不该被绑死在某个存储上。

做法是"注册表 + 适配器"。后端各实现统一协议 MemoryProtocol(memory/protocols.py), 通过注册表登记后按名字取用:

后端注册方式位置
in_memoryregister_memory_adapter("in_memory", InMemoryAdapter)memory/adapters/__init__.py:37
sqliteregister_memory_adapter("sqlite", SqliteMemoryAdapter)memory/adapters/__init__.py:36
mem0register_memory_factory("mem0", ...)memory/adapters/__init__.py:40
chromaregister_memory_factory("chroma", ...)memory/adapters/__init__.py:41
mongodbregister_memory_factory("mongodb", ...)memory/adapters/__init__.py:42

注册表本体 MemoryAdapterRegistry(memory/adapters/registry.py:15)提供 register_memory_adapter(:44)、register_memory_factory(:49)、get_memory_adapter(:54)。 "适配器"直接给类、"工厂"给构造函数(mem0/chroma/mongo 这类需要外部连接的用工厂延迟构造)。

自动记忆抽取: memory/__init__.py 里以懒加载方式导出 AutoMemory / AutoMemoryExtractor (memory/__init__.py:85,实现在 memory/auto_memory.py)——从对话中自动抽取值得记的条目, 省去手工调用 store_*

诚实边界: 图记忆(Neo4j/Memgraph)在 Memory 的 config 里以 graph_store 声明 (memory/memory.py:70-77),但走的是 mem0 通道,本章不下钻其实现。


4. 知识与 RAG:查得到

这节讲什么: 把你的私有文档变成 agent 能搜的知识库。PraisonAI 把这件事拆成两层: knowledge/(索引与检索的积木)和 rag/(把检索结果组织成带引用的答案的管线)。

4.1 knowledge:文件到可检索索引

门面类 Knowledge(knowledge/knowledge.py:53)对外只暴露三个动词:

动作方法位置
把文件加入知识库addknowledge/knowledge.py:410
存一段内容storeknowledge/knowledge.py:305
按查询检索searchknowledge/knowledge.py:347

底下的处理流水线(数据流向,从左到右):

文件 ──▶ 切块 ──▶ 嵌入 ──▶ 向量库 ──▶ 检索 ──▶ 重排 ──▶ 命中片段
chunking (embed) vector_store retrieval rerankers

流水线各节点的入口符号(都在 knowledge/ 下):

环节关键符号文件
切块Chunking / chunkchunking.py:5 / :194
语料统计与索引结果CorpusStats / IndexResult / IgnoreMatcherindexing.py:42 / :139 / :179
增量索引追踪(文件哈希/mtime)FileTrackerindexing.py:326
向量存储VectorStoreProtocol / VectorStoreRegistry / InMemoryVectorStorevector_store.py:57 / :164 / :239
检索策略RetrievalStrategy / RetrieverRegistry / RetrievalResultretrieval.py:18 / :97 / :27
查询引擎SimpleQueryEngine / SubQuestionEnginequery_engine.py:210 / :248
重排RerankerRegistry / SimpleReranker / RerankResultrerankers.py:79 / :126 / :18

设计暗线:向量库、检索器、查询引擎、重排器全走"协议 + 注册表"(每个文件都有 XxxProtocol + XxxRegistry),和记忆的适配器思路一致——都是"接口固定、实现可插拔"。 SubQuestionEngine(query_engine.py:248)值得注意:它能把一个复杂问题拆成子问题分别检索再合并。

indexing.py 里的四个类各管一段: CorpusStats(:42)记语料统计、IndexResult(:139) 记一次索引的结果、IgnoreMatcher(:179)管忽略规则,而 FileTracker(:326)按文件哈希 + mtime 做增量索引追踪(状态持久化到 JSON,跨会话复用),避免每次全量重建。四者并列、各司其职。

4.2 rag:把检索结果变成带引用的答案

knowledge/ 给的是"召回哪些片段",rag/ 负责"用这些片段生成答案、并标清每句话的出处"。

  • 管线入口 RAG(rag/pipeline.py:77):串起检索→压缩→生成的完整 RAG 流程 (同目录还有 retriever.py/compressor.py/summarizer.py/budget.py 等分步实现)。
  • 核心数据模型(rag/models.py):
    • Citation(:21)——一条引用(出处 + 片段),是"可追溯"的最小单元。
    • ContextPack(:78)——喂给 LLM 的上下文包,内含一组 Citation
    • RAGResult(:131)——最终答案 + 其引用列表。
    • RAGConfig(:188)——RAG 行为配置。
  • 检索行为配置 RetrievalConfig(rag/retrieval_config.py:28):用 RetrievalPolicy(:13) 决定何时检索、CitationsMode(:20,默认 APPEND)决定引用怎么附到答案上。

一句话记住 knowledge 与 rag 的分工: knowledge = "图书馆的检索台"(找到书页); rag = "研究助理"(读完书页写出带脚注的答复)。


5. 可靠性与安全外围:跑得稳

这节讲什么: 让 agent 少犯错、犯了错能回头、危险动作有闸门。这里的子系统各管一个失败模式, 下面按"输出对不对 → 会不会卡死 → 改坏了能不能回滚 → 执行安不安全 → 该不该放行"的顺序过一遍。

5.1 guardrails:LLM 输出的校验器

失败模式: LLM 输出格式错、含敏感内容、不达标。

GuardrailResult(guardrails/guardrail_result.py:13,一个 pydantic BaseModel)是统一的判定结果, from_tuple(:21)允许把简单的 (bool, value) 返回值升级成结构化结果。LLMGuardrail (guardrails/llm_guardrail.py:15)是"用另一个 LLM 来判定输出合不合格"的校验器。 多道 guardrail 可用 guardrails/chain.py 串成链。

5.2 doom-loop:死循环断路器

失败模式: agent 反复调同一个工具、连续失败、原地打转,烧钱不前进。

要注意这里有两套实现,别混:

实现角色位置
DoomLoopDetectorescalation/ 子系统里独立、功能更全的检测器(重复动作/相似动作/连续失败/无进展 + 恢复动作)escalation/doom_loop.py:87
DoomLoopTracker实际被 Agent 装配的轻量追踪器agent/autonomy.py:316

Agent 侧的接线:初始化自治特性时构造 DoomLoopTracker(agent/agent.py:2903), 主循环里每步 _record_action 记录动作、_is_doom_loop(agent/agent.py:3070)判定是否打转、 命中后 _get_doom_recovery(agent/agent.py:3132)取恢复动作、_reset_doom_loop(:3080)复位。

# 真实:agent/agent.py:3070
def _is_doom_loop(self) -> bool:
if self._doom_loop_tracker is None:
return False
return self._doom_loop_tracker.is_doom_loop()

一句话:_is_doom_loop 只是把判断委托给追踪器;检测逻辑本体在追踪器/检测器里 (记录动作历史 → 匹配"重复/失败/无进展"模式)。

5.3 snapshot / checkpoints:改坏了的后悔药

失败模式: agent 把文件改错,想回到改之前。

两套粒度:

  • 文件级快照(挂在 Agent 上):agent/agent.py 维护 _file_snapshot(一个 FileSnapshot)和 _snapshot_stack(agent/agent.py:2908,undo/redo 的哈希栈), 对外暴露 undo(:2935)、redo(:2968)、diff(:2993)。快照本体 FileSnapshot(snapshot/snapshot.py:92)提供 restore(:390)、get_current_hash(:494), 配套 FileDiff(:26)、SnapshotInfo(:55)。
  • 会话级检查点:CheckpointService(checkpoints/service.py:42)的 save(:252)/ restore(:313)——保存/恢复整个会话状态,比单纯文件快照更高层。

心智模型:snapshot 管"文件改动的撤销栈",checkpoint 管"整局存档读档"。

5.4 sandbox:执行不受信代码的安全层

失败模式: agent 生成的代码含 os.system("rm -rf") 这类危险操作。

两道防线:

  1. 静态危险扫描(best-effort 提示):check_code_safety(sandbox/security.py:82)先用 正则 DANGEROUS_PATTERNS(:29)扫,再用 AST 遍历器 DangerousASTVisitor(:210, visit_Call:216 / visit_Import:256)查危险调用/导入,返回一组 SecurityWarning。 源码明确标注这只是警告,真正隔离靠沙箱(sandbox/security.py:91-93 docstring)。
  2. 真隔离:SandboxManager(sandbox/manager.py:23)按 SecurityPolicy/SandboxConfig (sandbox/config.py:14/:92)在隔离环境里执行。

5.5 policy / approval:该不该放行

失败模式: 高危工具(删文件、发消息、花钱)不该无人值守地跑。

  • 策略引擎 PolicyEngine(policy/engine.py:17):check(:110)/check_tool(:149)/ check_file(:169)——按规则判定一个工具调用或文件操作是否放行。
  • 审批注册表 ApprovalRegistry(approval/registry.py:81):登记哪些工具需要人工批准 (add_requirement:150 / is_required:158 / get_risk_level:161),并把批准动作路由给 可插拔的后端(set_backend:119,CLI/回调等,见 approval/backends.py)。

两者配合就是第 2 章工具执行链上的闸门:policy 判"要不要拦",approval 管"拦下后谁来批"。

5.6 telemetry:匿名遥测

MinimalTelemetry(telemetry/telemetry.py:78)做匿名使用统计——track_agent_execution(:206)/ track_task_completion(:262)/track_tool_usage(:290)。可被环境变量关掉 (_is_monitoring_disabled,telemetry/telemetry.py:39),不采集用户内容,只记成败与计数。


6. 巧妙之处(可带走的设计)

  • "协议 + 注册表"统一贯穿三层。 记忆后端(memory/adapters/registry.py:44)、向量库/检索器/ 重排器(knowledge/*.py 每个都是 XxxProtocol + XxxRegistry)、审批后端 (approval/registry.py:119)全用同一模式:接口固定、实现按名注册、运行时取用。想加一种新存储/ 新向量库,写个类注册进去即可,上层零改动。
  • 质量分在"读"侧当闸门,而非只在写侧。 min_quality 作用于 search_* (memory/memory.py:585/911),意味着同一批数据可以按不同门槛检索——低门槛拿全量、高门槛拿精华。
  • doom-loop 用委托解耦。 Agent 只持有一个 tracker、_is_doom_loop 一行委托 (agent/agent.py:3070),检测策略可整体替换(轻量 DoomLoopTracker ↔ 全功能 DoomLoopDetector) 而不动主循环。
  • 安全分"提示"与"隔离"两层且诚实标注。 静态扫描自称 best-effort、真隔离靠沙箱 (sandbox/security.py:91-93)——不把静态扫描伪装成安全边界,值得学。

7. 边界与局限(诚实)

  • 本章只做导航。 每个子系统的深实现(mem0 的图记忆通道、rag 的压缩/预算策略、沙箱的具体隔离机制) 留给源码,本章不逐行走读。
  • 记忆的 mixin 是历史包袱。 SearchMixin/MemoryCoreMixinMemory 自身存在同名方法, 真正生效的是 memory.py 里的定义(MRO 覆盖)。读代码别被 core.py/search.py 的同名方法带偏。
  • 同名/多实现处认准接线的那个。 Agent 实际装配的死循环追踪器是 agent/autonomy.py:316DoomLoopTracker,而非 escalation/doom_loop.pyDoomLoopDetector (后者是独立、更完整但未直接接线的实现)。
  • 图记忆 / mem0 / chroma 等外部后端依赖第三方服务,行为不在本仓库源码内,本章不下钻。

8. 代码地图(导航索引)

按"主题 | 文件路径 | 符号名"三列,便于用符号名 grep 定位(比行号抗漂移)。

记忆

主题文件符号
记忆管理器主入口memory/memory.pyMemory
质量打分memory/memory.pycompute_quality_score / _process_quality_metrics
短期存/取memory/memory.pystore_short_term / search_short_term
长期存/取memory/memory.pystore_long_term / search_long_term
上下文拼装memory/memory.pybuild_context_for_task
后向兼容 mixinmemory/search.py / memory/core.pySearchMixin / MemoryCoreMixin
后端注册表memory/adapters/registry.pyMemoryAdapterRegistry / register_memory_adapter / register_memory_factory
内置后端memory/adapters/InMemoryAdapter / SqliteMemoryAdapter(+ mem0/chroma/mongo 工厂)
自动抽取memory/auto_memory.pyAutoMemory / AutoMemoryExtractor

知识与 RAG

主题文件符号
知识门面knowledge/knowledge.pyKnowledge(add/store/search)
切块knowledge/chunking.pyChunking
索引/语料统计knowledge/indexing.pyCorpusStats / IndexResult / IgnoreMatcher / FileTracker
向量库knowledge/vector_store.pyVectorStoreProtocol / VectorStoreRegistry / InMemoryVectorStore
检索knowledge/retrieval.pyRetrieverRegistry / RetrievalStrategy / RetrievalResult
查询引擎knowledge/query_engine.pySimpleQueryEngine / SubQuestionEngine
重排knowledge/rerankers.pyRerankerRegistry / SimpleReranker
RAG 管线rag/pipeline.pyRAG
RAG 数据模型rag/models.pyCitation / ContextPack / RAGResult / RAGConfig
检索配置rag/retrieval_config.pyRetrievalConfig / RetrievalPolicy / CitationsMode

可靠性与安全

主题文件符号
输出校验guardrails/guardrail_result.py / guardrails/llm_guardrail.pyGuardrailResult / LLMGuardrail
死循环检测(独立)escalation/doom_loop.pyDoomLoopDetector
死循环追踪(接线)agent/autonomy.py / agent/agent.pyDoomLoopTracker / _is_doom_loop
文件快照撤销agent/agent.py / snapshot/snapshot.pyundo/redo/diff / FileSnapshot
会话检查点checkpoints/service.pyCheckpointService(save/restore)
代码危险扫描sandbox/security.pycheck_code_safety / DangerousASTVisitor / DANGEROUS_PATTERNS
沙箱执行sandbox/manager.py / sandbox/config.pySandboxManager / SecurityPolicy / SandboxConfig
策略引擎policy/engine.pyPolicyEngine(check/check_tool/check_file)
审批注册表approval/registry.pyApprovalRegistry(add_requirement/is_required)
遥测telemetry/telemetry.pyMinimalTelemetry