内容
### 追溯性丧失资格:`slash(ids)` (0.4.4)
`credit()` 是只能追加的,因此一个 **患者 "睡眠者"(patient "sleeper")** 通过许多良性记忆在一个来源下积累良好结果,即使遇到一次坏结果也不会受到影响(好=50,坏=1 仍可信任)—— 对结果地位的残余攻击是一种缓慢的、领域内的累积器,而不是一次性的。`slash(ids, scope='source')` 是责任杠杆:当一个记忆被 **抓住** 导致不良结果时,它会丧失该来源的 **全部累计地位** —— 每个共享其规范来源的活跃记忆都会变成负值,并失去任何渐进式 → 语义提升,因此该来源立即失败影响门槛。累计信誉 *就是* 债券;一旦被抓住,攻击者的耐心就会变成其最大的暴露赌注。 不像 `forget()` 它不会删除任何东西(记录仍可通过 `meta['slashed']` 回忆和审计);不像 `credit(bad)` 它不能被抵消。这使得腐败成本随着累计地位 × 可检测性(经典预期惩罚结果——惩罚必须超过收益 / P(被抓住))而扩大,这是对时间丰富的攻击者施加的每动作上限仅让他分摊。
收据:[`probes/triad_attacker_split.py`](probes/triad_attacker_split.py),[`probes/reversibility_gate_frontier.py`](probes/reversibility_gate_frontier.py)。
由于检测是不完美的 —— 自我评级 / MINJA 风格的预言机可以被 **欺骗** 以标记一个合法来源,因此 `slash()` 可以被 **用作武器** 以击倒竞争对手的记忆 —— 丧失资格是可逆的:`restore(ids, scope='source')` 恢复 **确切** 的 slash 之前的状态(保存在 `meta['pre_slash']`),或者如果没有记录则恢复到干净的状态。惩罚很重,但申诉很便宜 —— 否则 `slash()` 本身成为攻击面。
**通过转换保存出处:`remember(..., derived_from=[ids])` (0.4.6).** 上述所有内容 —— `slash`、每个来源的影响预算、来源级别的责任 —— 在记忆 **转换** 时都是静默不可计数的:应用程序对五个来源记忆的总结是一个新鲜的记录,没有来源,因此 `slash(source)` 无法触及它,累计上限无法归因于它的切片。`mnemo` 自己的合并永远不会失去出处(它链接,而不是合并文本),但 LLM 总结/重写会。`remember(text, derived_from=[parent_ids])` 关闭了这个漏洞:新记录 **继承** 其父节点的规范来源的联合作为 **污染** (传递性 —— 摘要的摘要仍然携带原始出处),并且 `slash(scope='source')` 与 **自身来源或继承污染** 匹配,因此丧失某个来源的资格也会烧毁它所馈送的每个派生摘要。诚实的边界:应用程序必须在转换步骤中 **声明** 派生 —— `mnemo` 可以带上污染,但它无法恢复不透明的总结所丢弃的出处。这是所有其他内容的基石。
**触发 slash 所需的累计触发器:`monitor()` (0.4.7, hardened 0.4.8).** 追溯性 `slash()` *不能* 对抗缓慢的 salami 攻击者触发每个切片:每个切片的 `P(detected) ≈ 0`,而威慑债券随着 `1/P(detected)` 缩放,因此惩罚在你担心的攻击上恰好爆炸。所以触发器必须是 **累计的**。`monitor(ids, outcome)` 是 `credit()` 的替代品,它在每个归因来源的不良率上运行单边 **CUSUM-type** 检测器超过良性参考 `k`;在 `h` 违规时,它为该来源 **引发一个案件**,并通过 `derived_from` 污染携带归因,因此稍后总结的切片仍然会累积对抗其来源。`h` 设置误报率(`ARL ~ exp(h)`)和检测延迟 `~ h/(rate−k)` —— Lorden 下限没有门缩小。状态持续到一个侧文件,因此一次滴漏不能在会话之间重置检测器。
三个诚实的限制(来自完整的对抗性审查 —— 这并不 "解决" 投毒):
(1) 它是 **CUSUM-type** (高斯均值偏移 `x−k`),不是最佳的伯努利对数似然形式;
(2) **`k` 是一个容忍率 *价格*,不是墙** —— 攻击者将其不良率保持在 `k` 以下/等于 `k` 会使统计数据漂移到零,并且 **可证明** 是不可检测的,因此这会捕获粗心的投毒者,而患者会获得有界的 `k × 暴露` 残留量(延迟下限移动到 `k`,而不是关闭);降低 `k` 只会增加对诚实来源的误报;
(3) **不要自动触发不可逆的惩罚** —— `auto_slash` **默认关闭**。七十年的自动惩罚(SPC → 欺诈 → 内容审核,例如 Knight Capital,无飞行名单)趋同于自动 **检测** + 可逆转的人工审核惩罚,因为漂移的基本速率保证误报,单个误报会摧毁整个受污染的下游树(通过链接的有罪推定),并且如果结果是攻击者影响的(MINJA),自动触发器就变成 **构陷武器**(供给不良结果归因于竞争对手 → 自动削减竞争对手;参见 RepTrap / 坏口碑)。推荐:对于某个案例,限制/冻结来源的前向影响(可逆转),并排队一个 **人工** 审查;通过人工确认 `slash()`;保持 `restore()` 在一步之遥。`auto_slash=True` 是一个显式选择,仅适用于高完整性、不可自我评级的预言机。
收据:[`probes/triad_attacker_split.py`](probes/triad_attacker_split.py),[`probes/reversibility_gate_frontier.py`](probes/reversibility_gate_frontier.py)。
**限制检测器无法实现的剩余部分:`spend_irreversible()` (0.4.9).** `monitor` 的 `k` 是一个容忍的 **比率**,因此 **严格低于 `k`** 的攻击者给出 CUSUM 负漂移 → 没有检测能力超过基线误报率,在净 `k × 暴露` 处获利 —— 但暴露是 **每窗口**,因此在无限范围内,其总不可逆转的破坏增长无界。限制比率永远不会限制积分。`spend_irreversible(ids, amount, budget)` 直接限制积分:一个来源的 **生命周期预算不可逆转的影响**,通过 `derived_from` 污染收费(最严格的贡献来源绑定 —— 一个行动被污染的来源耗尽,拒绝执行)。仅在 **不可逆转的尾部** 计费:可逆转的影响永远不会被收费,因此真正好的来源保持 **无限** 的可逆转影响,只有其不可逆转的尾部是有限的。它 **不被`restore()` 退款** —— 不可逆转的行动无法撤销,因此其积分是永久的(通过人工提高审查来源的天花板,而不是通过廉价的自动退款)。诚实的阅读:你不移除残留部分,你 **定价(`k`)**,限制(这个生命周期预算),并在不可逆转的尾部设置可逆转的人工门槛(`slash`/`restore`)—— 但这 **不是一个封闭的分解**,因为它们都依赖于第四个,无限的轴:**健全的归因** (正确地将一个行动归因于其来源并标记其不可逆转性)。测量:[`probes/lifetime_budget_bound.py`](probes/lifetime_budget_bound.py) 显示相同的不可检测的低于 `k` 攻击者的总破坏从无界(50→800 在整个范围内)变为 **预算上限**,而一个好的来源保持 10,000 个免费的可逆转使用。
诚实的限制(这 **重新定位** 残留部分,而不是关闭它):(1)预算是 **每个来源的**,因此它 **不会逃避 Sybil** —— 一个白洗涤攻击者为新鲜的廉价身份铸造一个全新的预算,因此系统范围内的破坏是 `budget × (#identities)`;身份计数由 **不同的轴** 界定(与地位无关的能力上限/身份成本;Douceur 2002,Friedman–Resnick 2001),因此这限制了一个 **已知的** 来源,而不是一个可以铸造新身份的对手;(2)**无退款的丧失是 griefing 矢量** —— 获得不良的不可逆转的行为归因于或污染链接到竞争对手的来源,你永久耗尽其预算(RepTrap 在预算轴上),与 `auto_slash`-off 默认避免的相同问题相同,因此调整 `budget` 和可逆转/不可逆转的分割保守;(3)它假设应用程序可以标记一个动作的 **不可逆转性和破坏金额** 在花费时间;`amount` 由调用者提供,一个 `budget` 大小的动作耗尽一个来源(保证是“≤ `budget` per source”,`k` 不进入它),如果分类器是攻击者影响的,则计量器会泄漏。
**加强其他三个立足点的基础:`verify_attribution()` (0.5.0).** `k`、影响预算、影响门和 `slash` 都以记忆的规范 **来源 ID** 为键。因此,归因不是第四个轴 —— 它是其他三个立足的基础,并且是唯一一个 **不自我认证** 的:一个单一的 **事后重标签** (重写记录的来源,或剥夺总结的继承 `derived_from` 污染以清洗中毒来源)不会 **降低** 其他三个,而是 **一次性静默取消所有**,没有内部层可以上诉。因此,将归因绑定到防篡改的写收据链中(启用 `receipts=True` / `receipt_key=…`):收据现在承诺每个写入的规范来源,并且 `verify_attribution()` 报告任何当前来源与提交的内容不匹配的活动记忆。**重标签变得响亮,而不是沉默的。**
诚实的限制(这 **不是防篡改,而是防篡改证据**):(1)**防篡改证据 ≠ 正确性**。一个在写入时 **错误** 的来源 —— 一个攻击者控制标记通道(MINJA 样式)并断言一个良性来源 —— 被忠实地提交,并且 `verify_attribution()` **无法** 告诉它错了。那是真正的 **预言机问题**,不受影响。(2)**如果没有外部锚定或签名,链条仅防篡改证据**。没有签名(默认),攻击者可以静默重标签 `rec["source"]`,同样可以重新计算整个边车收据链并通过检查。防篡改证据 ≠ 正确性。
**使佐证计数成为一个独立的 **验证密钥**,而不是独立的字符串:`strict_corroboration` + `attestation` (0.5.2).** 佐证门槛(渐进→语义提升和 `recall(influence_only=True)`)需要“≥2 个不同的来源”。默认情况下,来源是一个规范的 **字符串** —— 实体解析会折叠诚实的 Sybil 变体(`Wikipedia`/`wikipedia.org`/一个 URL → 一个),但攻击者可以仍然提供两个 **无关** 的来源字符串并制造“独立”的佐证。设置 `m.strict_corroboration = True` 并且佐证链接仅在它携带 **验证密钥** 时才计入:一个来源对其编写的声明进行签名(`sig = mnemo.attest(text, source_sk, source_doc)`;写入 `remember(..., attestation=(source_pubkey, sig))`),签名在写入时在 **相同的声明 + 规范来源** 上进行验证(伪造或重放的佐证被 **拒绝**,而不是静默丢弃),并且记录携带 `attested_key`。然后独立性由 **不同的Ed25519公钥** 衡量,攻击者无法伪造 —— N 个 Sybil 变体之一折叠为一个证人,除非攻击者拥有 N 个不同的密钥(一个昂贵的身份;Douceur 2002)。这是 **外源信任根**,归因问题归结为:“我能信任标签”变成“我能信任根”,即身份轴。测量:[`probes/attribution_verified_key.py`](probes/attribution_verified_key.py) — 一个两个字符串的欺骗通过默认门槛被 **拒绝**;两个不同的签名证人通过;使用相同的密钥两次折叠为一个;伪造和重放的证人在写入时被拒绝。
### 证据级别的棘轮:`grade()` + `ratify()` (0.6.0)
**声明的状态是它 **赚取** 的,而不是作者自我分配的标签。** 两个轴基于现有的基板,只能在来自 **其他方** 的事件上向上移动:**置信度级别**(`claimed → corroborated → verified → settled`)和单独的 **新颖性** 标志(`novel` 仅当外部 **先前艺术搜索返回空**)。`remember()` 不能设置两者;`grade(id)` 是仅取决于 **ratifications + corroboration + `credit()` 结果的纯函数,因此没有可以伪造的内容。`ratify(id, kind, by_key, lens=)` 记录外部事件(`independent_witness` / `reproduction` / `prior_art_empty` / `audit`);如果 `by_key` 是声明的作者自己的,则 **拒绝**,而重复的 `(by_key, kind, lens)` 不会堆叠,因此相关或重复的审计器不会添加任何内容。最高的评级需要 **复制** 加上 **两个不同的镜头** —— 相关审计器防护。收据:[`probes/evidence_grade_ratchet.py`](probes/evidence_grade_ratchet.py) 显示(1)棘轮保持(生成器升级自己的声明不会做任何事情),(2)伪造成本——一个身份卡在 `claimed` 处,每次提升都需要另一个不同的密钥(Douceur;与 `attestation` 配对以使这些密钥不可伪造),并且(3)我们自己的 32 个对抗性审计帖子通过棘轮重现审计的 **免费** 头条:**0/32 reach `novel`**(没有一个有空的先前艺术搜索)和 11 个实质性错误的一个停留在 `claimed` 处,而 21 个复制的达到 `verified` 处。过度标记不会在事后被捕获 —— 它在结构上不可断言。 **诚实的限制**:这限制了 **谁** 可以将标签提升到 **不同的身份**,而不是真理 —— 错误的声明带有真实的复制品仍然会升级;而 `by_key` 可以伪造,除非与 `attestation` 搭配(那么每个身份都是 Douceur 昂贵的)。证据级别/阶段式提升是教科论证和 KR 证明级别,阶段式审查;新的位点是一个可运行的内存基元,使该级别由构造外部棘轮。选择加入;默认行为不变。
### 独立于时间,而不是仅仅独立于来源:`temporal_gate` (0.6.5)
佐证链接证明 **来源** 的独立性,但永远不会 **时间** 的独立性 —— 真正独立的来源很少在彼此 **几秒内** 编写,但协调的伪造会在 **突发** 中编写证人。 `temporal_gate`(选择加入,`m.temporal_gate = 60.0` 秒;默认 `None` → **行为不变**,由 **hannune** 在 r/RAG 建议)会将 **同时到达** 的佐证链接(彼此在窗口内的时间戳)折叠为 **一个锚点**,在 `≥2-distinct-source` 计数之前 —— 与源规范化折叠 `Wikipedia`/`wikipedia.org` 到一个类似,但 **时间** 上。测量:[`probes/temporal_gate_demo.py`](probes/temporal_gate_demo.py):一个真正的恢复,其证人随时间分散, **不会受到影响**;**同时到达的突发被阻止**,而 **诚实的限制** 是 **患者** 攻击者将伪造的写入间隔超过窗口仍然通过(定时信号无法捕获耐心;再次是睡者)。这是 **解耦** 层 —— 时间与来源计数和内容一致性正交,因此其价值正是攻击者留下的解耦。 是 **教科布斯特 / Sybil 时间检测** 的教科工具箱,不作为墙。
### 相关性验证:`coherence_gate`(0.6.4)
相关性验证链接证明了来源的独立性,但从未证明证人是否与主张相关——因此,伪造的2个来源的毒药,其“证人”是无关填充内容,仍然可以达到`≥2-distinct-source`的门槛。`coherence_gate`(可选,`m.coherence_gate = 0.18`;默认为`None` → **无行为变化**)使链接仅在证人**与主张一致**时才计入该门槛——如果您传递了`embed`函数,则使用嵌入余弦相似度,否则使用词法token-Jaccard相似度——高于阈值。测量([`probes/coherence_gate_demo.py`](probes/coherence_gate_demo.py)):真正的与主题相关的恢复不受影响(无错误拒绝),**懒惰的无关伪造被阻止**,而**复杂的伪造**带有**与主题相关的**证人仍然通过。所以,这**提高了伪造者的门槛**,从“2个不同的源字符串”提高到“2个不同的源字符串 + 与主题相关的证人文本”;它**不**关闭残余风险。这是教科书中的**自适应攻击 / 共同模式**领域(Carlini & Wagner 2017; Knight & Leveson 1986; PoisonedRAG)——一个深度防御层,而不是一个墙。
### LLM重写后仍然存活的出处:自动标记的 lineage(0.6.3)
0.6.2中的撤回操作依赖于`derived_from`污染——但应用侧的总结/合并步骤(一个不受信任的LLM重写)通常会**丢弃**该链接,使总结成为孤立的,从而无法撤回。0.6.3在**转换边界**处关闭了该漏洞,并且——因为我们首先对主张进行了完整的 多镜头审查 + 引用检查——所以它**诚实地**这样做。源字符串默认拒绝(降低任何没有源的写入)是教科书中的 **Biba(1977)**:它验证*来源,而不是真实性*,调用者可以伪造源,并且它不影响带有有效出处的毒药(MINJA,[arXiv:2503.03704](https://arxiv.org/abs/2503.03704),NeurIPS 2025)或攻击检索几何(AgentPoison,NeurIPS 2024)。实际**衡量**的是**存储携带的 lineage**:`recall()`记录它所检索的内容,`remember(..., derived=True)`在没有显式父节点的情况下**自动标记`derived_from`来源**,因此,在存储中,召回后立即写入的总结继承了其祖先的污染——不受信任的LLM仅提供文本,而不持有开关。测量([`probes/autostamp_lineage.py`](probes/autostamp_lineage.py)):清洗后的总结继承了根节点的污染,不是孤立的,并且在**对根节点进行`slash()`操作时被回收**(可逆转);一个没有前置召回的派生写入仍然是**孤立的**(失败关闭)。这与 **MemLineage** ([arXiv:2605.14421](https://arxiv.org/abs/2605.14421))一致:仅签名6/6攻击→0/6,一旦祖先 lineage 传播。同时提供`remember(derived=True)`(声明转换输出)和存储级别的`strict_provenance`标志(需要显示的源**或**可解的父节点)。**诚实范围**:这是Biba风格的完整性/污染跟踪*应用程序*(不是新颖的),它关闭了**清洗后的总结**路径;它**不**阻止携带出处的毒药——这需要内容审查+信任衰减检索。所有操作均为可选;`derived=False`默认值→零行为变化。
### 落地的撤回在每条路径上都获胜:`slash()` → 0 load-bearing(0.6.2)
佐证只能提高*置信度*,而不能确立*真相*——因此,经过身份验证但虚假的主张**将**被允许通过。实际持有的属性是暂时的:*任何错误的内容在正确的信号落地后都不会继续具有负载承载能力*(有界的爆炸半径+可逆转的传播)。`slash(ids, scope='source')` 落地该信号——当错误结果最终归因于某个源时,它会失去立足点,并且通过`derived_from`污染传播到整个总结/合并的传递子树;`restore()`是精确的,因此错误的或被用作武器的撤回是可以撤销的。**0.6.2 关闭了最后一个漏洞:** 一个被捕获的记录,如果它独立通过了≥2-distinct-source门槛,曾经**存活**(撤回记录会损害 accountability 但不会剥夺佐证链接),因此 `_is_corroborated()`——召回影响门控**和**晋升门槛——现在对**每个**路径(信用、晋升和基于链接的佐证)都返回`False`,对于任何被slash的记录。测量:一次slash撤销了5/5个已达到出处的后代(包括深度2的汇总和链接佐证的那个),恢复恢复了5/5;唯一的幸存者是一个 lineage-stripped的孤立节点(通过总结保留`derived_from`——使用要求,而不是存储错误)。可运行的收据:[`mnemo/probes/retraction_propagation.py`](https://github.com/DanceNitra/agora/blob/main/mnemo/probes/retraction_propagation.py)。信用:jacksonxly(不变性)+ marintkael(经过身份验证但虚假)。
### 收敛性支持的状态:`convergence_report()` + `recall(with_status=)`(0.6.1)
**佐证衡量来源的独立性,从未衡量正确性**——因此,真正独立的来源可以**收敛**到一个**虚假**的主张(“认证但虚假”),而记录内容中的任何内容都无法检测到它。此次升级(由一次尖锐的R/RAG交流引发)使内存层诚实地携带该信息,而不是将收敛性提升为“真实”。`convergence_report(id)`返回一个**基于收敛性的**状态(来源同意,*不是*仲裁为真)vs **仲裁**(通过`ratify(kind='reproduction'/'audit')`从*不同*身份进行外部检查——一种不同的故障模式);它标记**低_source_diversity**(≥2个佐证链接解析为≤1个不同来源——来自少数来源的一致同意应该*提高*怀疑,因为当来源共享一个基质时,错误相关联);并且它报告一个**lineage_grade**,在派生内存时以其最弱的父节点为上限(信任污染传播,不只是来源污染)。`recall(with_status=True)`在使用的节点处携带状态。该机制是教科书式的——冗余仅在故障模式独立时恢复错误的共识(Knight & Leveson 1986; Condorcet/Ladha 1992; Campbell & Fiske 1959);新增的是一个可运行的内存基元,它命名了它。所有操作均为可选;默认行为不变。
### 软元数据过滤器:`recall(prefer=..., prefer_trust=...)`(0.4.1)
硬元数据过滤器(`where={"speaker": x}`)删除不匹配的记忆——当过滤器正确时效果很好,但当提取器猜测错误的值时,它会**硬删除答案**。软版本仅**提升**匹配的记忆,根据您对该调用的信任程度加权,并留下所有其他可排名的:`recall(q, prefer={"speaker": x}, prefer_trust=t)`,`t∈[0,1]`(0 = 无过滤,1 = 强偏好)。当匹配较弱/模糊时,传递一个**低**`prefer_trust`,使过滤器倾向于普通的召回。重点是根据**提取的先验可靠性**(例如别名匹配强度:精确名称命中→〜1.0,无名称/模糊猜测→〜0.0)进行加权,**不是**根据提取器模型自己的自报告置信度(它在错误时恰好被破坏)。通过`recall()`在LoCoMo上进行端到端测量(收据:[`probes/locomo_soft_prefer_filter.py`](probes/locomo_soft_prefer_filter.py)):使用在精确名称问题上可靠(5%错误)但在模糊问题上猜测(67%错误)的提取器,基于别名强度加权的`prefer`分数**recall@20 0.718(+0.144超过无过滤器,所有10/10对话中的最佳)**,并且在提取器选择错误说话者的子集上恢复到**0.315,而硬过滤器为0.110**(它会删除正确答案)。软`prefer`提供了过滤器的优势而没有硬过滤器的劣势。可逆转:`prefer=None` = 传统召回。
### 组合多个软提示:多维`prefer`(0.4.2)
将`prefer`作为**(cond, trust)**元组的**列表**(或`{"cond":…, "trust":…}`字典)传递,以一次加权多个提示——例如,一个已解析的时间窗口**和**一个命名说话者:`recall(q, prefer=[({"year": 2023}, 0.9), ({"speaker": x}, 0.7)])`。匹配提示**作为中性因子为1.0的乘积**组合,因此匹配两个提示的记忆比匹配单个提示的记忆提升更多,而不匹配的提示是惰性。 使用`prefer_max_boost`限制(一个上限乘积,如Elasticsearch `function_score`的`max_boost`)。单个`dict` + 标量`prefer_trust`是一维情况,不变。 测量(收据:[`probes/locomo_composed_soft_filters.py`](probes/locomo_composed_soft_filters.py),自检0/1568与已发布的路径):在LoCoMo问题中携带两个独立提示(n = 183),提示的乘积组合得分**recall@20 0.865 vs 0.755 for best single cue(+0.110,bootstrap CI不包含0)**,而在单个维度信任上限的加权组合中,提示的得分降低(-0.053 - 限制因素使联合证据趋于平缓,这是经典的“在饱和形式之外组合”失败,BM25F/Robertson等,CIKM 2004)。因此:以**乘积**组合,如果您限制,请限制乘积——这是Elasticsearch的生产搜索所做的选择(Elasticsearch默认为`score_mode=multiply`)。诚实范围:一个基准,一个嵌入器,近正交提示。可逆转:单个字典/`None`的行为与以前完全相同。
**仅组合您信任的提示**(收据:[`probes/locomo_correlated_cue_composition.py`](probes/locomo_correlated_cue_composition.py))。乘积继承了乘积专家组的**否决权**(Hinton 2002):一个接近零的因素否决,因此目标错过**任一**提示,大大低于加法总和或受信任的提示值。解决方法是每个提示的`trust`,您已经传递:降低不受信任的提示,恢复乘积趋向总和。有趣的是,这不是相关性影响——差距在提示**正交**时最大,并且**缩小**提示相关时(冗余副本只要真实提示命中就不能错过)。经验法则:仅当第二个提示**独立适用于查询**时,才组合第二个提示,并根据该可靠性加权。
### 连续状态提示:`recall(near=...)`(0.6.6)
`prefer`匹配**分类**元数据(`theme == "identity"`)。对于**连续**状态向量——一个TAT样式的5-D块,或存储在元数据中的任何类似嵌入的特征——您希望在数值子空间中进行最近邻居,而不是精确匹配。`recall(query, k, near={"target": {"theme": 0.29, "role": 0.33, ...}, "trust": 0.7, "half": 0.2})`通过目标的数值维度**提升**每个记录`1 + trust*(coverage)*exp(-distance/half)`。软(从不硬删除;缺少维度→中性),与文本相似度和`prefer`组合,`near=None` = 字节相同的旧版本。 在真实的TAT 5-D状态轨迹上测量:**制度相关性精度@5 **0.984(near)vs 0.758(纯文本)**。它重新排序召回池——不是向量索引。收据:`mnemo/probes/continuous_chunk_recall_probe.py`。
### 使不主张可见:`recall(with_warrant=True)` + `spend_irreversible(provenance_lo=...)`(0.6.6)
“无独立渠道”的低分数静默衰减为**“未经验证但存在”**——下游读取静默作为软肯定,您又回到了带有额外步骤的共识-毒药。因此,**放弃**是一种一流的、可分支的**状态**:`recall(with_warrant=True)`将每个命中标记为`earned` / `corroborated` / `unwarranted`,而消费者规则是**永远不要让`unwarranted`**驱动后果决策。与其配套的是`spend_irreversible(ids, amount, budget, provenance_lo=0.15)`,上限是**没有佐证贡献记录**的来源,具有小的`provenance_lo`而不是完整的预算——一个低出处内存召回为不可逆操作绑定该操作的预算**针对自身**,限定硬地板到后果切片而不是整个存储。两者均为可选(`with_warrant=False` / `provenance_lo=None` = 旧版)。收据:`mnemo/probes/legible_warrant_scoped_budget_probe.py`。
### 要求不可逆转尾部获得收益结果:`spend_irreversible(require_earned=True)`(0.6.7)
默认情况下,`spend_irreversible(provenance_lo=...)`向所有**佐证**来源授予完整的不可逆转预算——而在默认的(非严格)配置中,佐证接受≥2个不同的**源字符串**,攻击者设置,因此伪造源的女巫毒药可以为不可逆转的操作赚取完整的预算。`require_earned=True`将完整的预算授予具有**收益结果**的来源(`good>0`和`good>=bad`,由`credit()`在实际下游成功时设置)——这是女巫无法伪造的信号(伪造或有保证的≥2-证人女巫清除佐证但不通过)。成本:任何尚未获得收益的合法来源也被限制在`provenance_lo`,因此对于高风险部署它是可选的;默认`False`是字节相同的旧路径。收据:`mnemo/probes/spend_irreversible_require_earned_probe.py`。
### 修正事实的近似重排序:`recall(tie_recent=eps)`(0.6.8)
当事实在**自由文本中被纠正**时,SRO超额不会触发,而过时值可以**出排名**的新鲜值:在MemBench(ACL 2025 Findings)知识更新问题上,**过时值在rank-1中的32.7%**的情况中胜出——同样适用于原始余弦和mnemo的语义召回(收据:`mnemo/probes/membench_recall_probe_v2.py`)。`tie_recent=eps`将相关性在`eps`范围内的候选者重新排序为**最新**(按`valid_from`,回退到`ts`);低于频带的所有内容保持其分数顺序。 测量扫描(222个问题包括3个非更新分割点收据:`mnemo/probes/membench_recency_tiebreak_probe.py`):`tie_recent=0.05`在集中余弦上削减旧比新鲜**0.327 → 0.109(3×)≈零命中率在控制分割点**,而线性位置奖励被测量为无用(之前它损害控制)。诚实范围:基准的纠正始终在原始提及之后(通过构造;控制分割点成本是公平性检查),而**对抗性**回声**回声攻击**的旧值重述不会被推广——不要在敌对输入使用时使用它(与`influence_only`结合)。
### 已纠正事实的回声攻击防护:`m.echo_guard = True` + `remember(object=...)`(0.6.9)
事实被纠正(旧值 → 取代);稍后旧值被**重述**——一个无辜的重述或攻击者重注入。在一个普通的**时间顺序** / **双时间** / **最后写入获胜**存储中,重述携带一个更新的时间戳并**复活**过时的值。 测量MemBench的回声装置([`mnemo/probes/echo_attack_probe_v2.py`](https://github.com/DanceNitra/agora/blob/main/mnemo/probes/echo_attack_probe_v2.py),检索级别的旧答案率,43个纠正的事实案例;回声用deepseek/kimi/glm paraphrased交叉家族):时间顺序,一个mem0-v1-faithful **添加/更新/删除**策略和一个**双时间Graphiti-faithful**策略都达到**0.21 → 不可用**在**逐字**和**意译**回声下;一个逐字哈希策略(MemStrata样式)抵抗逐字(0.21),但被**意译**摧毁(1.00)。
mnemo自己的基于键的超额订阅也容易受到影响(端到端`echo_guard_e2e_probe.py`:**1.00**在**逐字**和**意译**回声下)。
设置`echo_guard=True`并将断言值作为`remember(text, key=..., object=...)`:一个键值写入,如果`object`与一个键**已经为该键超额订阅**的值匹配,则该写入被视为超额订阅的重新声明——在到达时被退休,当前值保留。端到端,这将旧速率保持在接近无回声的基线(〜0.28)**在**逐字和意译回声下(攻击Δ≈0,而没有防护措施为+0.65)。
**负载能力限制(已测量,非假设)**:意译抗性仅来自`object`的**值保留**。嵌入近似重复**无法**将相同值的意译(cos≈0.95)与不同值的更正(≈0.84)区分开来——它们重叠(〜42%错误阻止在0.9)。因此,防护措施基于`object`的文本/值,而非相似性。当**模糊**值时,回声攻击防护措施会失败,而没有`object`,防护措施会恢复到标准化文本(仅逐字,MemStrata等价)。真正的**反转**回纠正的值需要`remember(..., reaffirm=True)`(防护措施无法自行取消)。可选;`echo_guard=False`(默认)= 字节相同的旧版键控超额订阅。
### 封闭检索循环:`propagate_outcome()` (0.6.10)
影响门和`echo_guard`依赖于不可自我提升的赚取结果信号(`credit()`),但在实时存储中,我们测量检索→赚取**转化率仅为~28%**(8个代理中16-62%;`mnemo/probes/retrieval_exposure_coverage_probe.py`)。这个差距是一个**归因**问题,而不是上限:应用程序仅手动为某些已采取的回忆提供积分,因此大多数检索并使用的记忆从未赚取其信号。`propagate_outcome(outcome)`在动作被评分时自动为上一次回忆的**决策驱动**子集授予积分,因此覆盖率接近应用程序的评分动作率,而无需手动将ID线程到`credit()`中。
通过`mnemo/probes/outcome_propagation_probe.py`测量:转化率从手动归因限制提升到评分动作率,并且**召回集中的非驱动因素在默认`driving_only`模式下赚取0%**(与`driving_only=False`时**50%**相比)- 因此封闭循环**不会**打开召回集归因毒药面。负载承载限制:`driving_only=True, ids=None`具有冷启动(尚未证实的最新记忆赚取不到任何积分)- 传递显式驱动程序ID以获得首次使用积分;显式路径的毒药安全性与选择驱动程序的回忆相同(使用`recall(..., influence_only=True)`进行高风险)。
### 撤销更正的事实:`revert(key)` + 无对象覆盖保护 (0.6.12 / 0.6.13)
`revert(key)`恢复键值在上一次键控超会话之前的值 - 根据超会话账本确定性地解决并重新确认追加-仅,通过编辑行永远不会。 这是键控值的控制平面撤销,作为MCP工具公开。 除此之外,**无对象覆盖保护**:在具有显式`object=`值的键控(值账本)上,不再携带任何对象的键控写入不能再取代真实值 - 这是我们自己的试点发现的一个漏洞,其中无值的恢复语句(“返回旧值”)用垃圾文本取代了真实值(`mnemo/probes/revert_by_reference_probe.py`,阻力**0.00 → 1.00**)。 与仅内容存储的判别间隙1.0。 更改账本值现在需要显式对象、`reaffirm=True`或`revert()`。
### 血统感知的纠正:`retract_lineage(subject)` (0.7.16)
当一个事实已经被纠正**之后**它播种了派生回写(一个代理存储了“我们使用MongoDB”,然后写入了“MongoDB连接字符串在配置中”),纯值的纠正会使这些派生记录保持活动 - 知识编辑的**涟漪效应**(Cohen等,RippleEdits,TACL 2024)。 `retract_lineage`降级主题**和**通过`derived_from`污染继承的一切到`superseded` - 从默认回忆中消失,但**保留**(用`include_superseded`回忆,标记`needs_rederivation`)以便您可以重新派生与纠正的根源而不是硬删除有效载荷(如`forget_subject`)。
这是经典的 retract-and-retain 来自 Truth-Maintenance(Doyle 1979)和双时态失效,最近移植到LLM代理内存([TOKI](https://arxiv.org/abs/2606.06240),[MemLineage](https://arxiv.org/abs/2605.14421));mnemo的唯一转折是它依赖与`forget_subject`相同的`derived_from`污染,因此它不需要单独的图。 它只能在实际记录的链接上进行级联。
### 再生降级有效载荷:`rederive(subject)` (0.7.17)
`retract_lineage`停车派生事实;`rederive`使它们恢复。 在您写入纠正后,它获取每个标记为`needs_rederivation`的记录,重写其文本以对抗纠正的根源(默认:确定性逐字值替换 - 跳过不包含旧值逐字的释义事实;传递`rewrite=`以获取LLM支持的重写器),并使用`derived_from` -> 纠正的根源重新记忆它,以便未来的纠正可以再次级联。 通过`recovery_halflife_pilot.py`测量(k=3):派生有效载荷恢复ACTIVE以确认纠正的值(3/3),而天真纠正(污染的有效载荷保持活动,损害0.98),硬删除(有效载荷丢失)或降级单独(有效载荷停放)。 corrupt -> 洗涤 -> 纠正 -> `retract_lineage` -> `rederive`是完整的纠正生命周期。
### 单次调用擦除和证明:`governance_report()` (0.7.18)
删除权请求(GDPR第17条)是代理内存获得法律认可的一个地方:您必须删除主题的数据**和**保留该操作的审计记录(第30条),而删除操作不能看起来像篡改。 mnemo已经有这些部分 - `forget_subject(subject, request_id=...)`硬删除主题**及其`derived_from`行谱**(从该主题的数据中构建的摘要也会被删除),并写入哈希链, 可选地使用Ed25519签名的删除墓碑;`verify_writes()`然后证明写入接收链和墓碑链都是完整的,因此实际删除被视为**已记账**,而隐蔽的带外删除仍然会触发验证器。 `governance_report(expected_pubkey=...)`将这些缝合到一个面向审计师的界面:删除总数,每个`request_id`的明细,以及篡改证据判决。
```python
m.forget_subject("user-42", request_id="dsr-2026-07-12-0001")
m.governance_report(expected_pubkey=pk)
# -> {erasures_total, by_request:{"dsr-...":{erased, memory_ids}}, proof:{verified:True, all_signed:True, ...}, scope}
```
**诚实范围**(在内部声明,因为这里的过度声明是失败模式):删除仅在*此*mnemo存储中 - 不包括您的矢量存储,提示日志或备份 - 以及墓碑证明删除**操作**,永远不会内容(PII的哈希仍然是PII)。 签名仅在负载上对抗没有持有`receipt_key`的一方; 为操作员对抗审计,将链头锚定在外部。
### 按时间点/双时态读取:`as_of()` + `history()` (0.6.14)
每个键控写入已经携带一个`[valid_from, invalidated_at)`间隔,因此时间线可以**无需图数据库**重建。 `as_of(key, when)`返回在事件时间`when`时有效的当前值;`history(key)`返回整个有效时间线(键控的每个值,其间隔,其状态和 - 自0.6.18以来 - 退休策略)。 关闭具有现有间隔的双时态图存储的唯一实时边缘。 诚实限制:脱机后填补通过事件时间(`valid_from`)解决,而不是摄入顺序。
### 在生产中运行有界:`Mnemo(capacity=N)` 双层驱逐 (0.6.15)
追加-仅适用于无界限;生产内存并非如此。 `Mnemo(capacity=N)`通过验证值保护 + 近期老化规则硬驱逐最低价值**活动**记录超过`N`(`protect_frac`的上限是近期免疫的,因此罕见但关键的内存在一个罕见但关键的内存中幸存下来;其余部分通过衰减加权值填充,以便陈旧的高价值内存无法挤出新的内存)。 超时历史不被计算或驱逐(它很便宜并保留`as_of`)。 默认`None` = 无界限传统,字节相同。 (`mnemo/probes/` 实验室29992a。)
### 将昂贵的重组延迟到空闲:`sleep()` (0.6.16)
合并(集群合并,保持预算,容量)是O(n);在写入路径上执行此操作会使每个`remember()`的成本增加。 `sleep()`将其延迟到主机调度的空闲调用(“睡眠时间计算”通道)- 写入路径保持快速,`sleep()`在没有成熟的调用时为无操作,幂等且召回安全。 作为`sleep` MCP工具公开。 纯库基元:无需代理循环,无图数据库,无主机。
### 抗女巫共证:种子锚定流信任`trust_seeds` (0.6.17)
通过“≥2个不同来源”(或使用`strict_corroboration`,≥2个不同的Ed25519密钥)的共证是**对称的** - 并且不同的密钥可以免费铸造,因此一个有决心的女巫可以清除门槛(Douceur 2002; Cheng-Friedman 2005仅证明**非对称**,基于流的信任是女巫证明的)。 `trust_seeds`添加了该锚点:只有当来源位于从应用程序种子根源通过推荐边(TrustRank/Advogato; Gyöngyi等,2004年)生长的信任闭包中时,共证见证才算数,直到`trust_hops`。 未推荐的自我铸造源贡献**零**受信任的见证(`mnemo/probes/seed_anchored_trust_probe.py`,4/4)。 默认空集=字节相同的旧版。 诚实限制:它将剩余风险转移到“获得**一个**种子认可”并假设健全的种子+归因 - 赚取结果路径(`credit()`)保持正交的不可伪造通道。
### 哪个解析器退休了每个事实:`superseded_by_policy` + `supersession_report()` (0.6.18)
存储的历史记录了**什么**被退休,但没有**为什么**。 每个超会话路径现在都标记`meta['superseded_by_policy']`(`keyed_lww` / `keyed_lww_backfill` / `keyed_reaffirm` / `echo_guard` / `objectless_guard` / `state_toggle` / `toggle_corroborated` / `toggle_persistence` / `keep_budget`);`history()`按行公开它,而`supersession_report()`按策略聚合计数 - 写入时的判断日志是大多数内存系统省略的(cf. TOKI,arXiv:2606.06240)。 仅附加元数据;没有解决决策更改(`mnemo/probes/supersession_policy_stamp_probe.py`,10/10)。
### 删除后保留审计轨迹的删除权:`forget_subject()` + 删除墓碑 (0.6.19+)
`forget()`真正删除内容 - 但硬删除会使`verify_writes()`报告现在丢失的记录为“带外删除”,因此合法的删除与篡改无法区分。 `forget_subject(subject, request_id=…)`删除归因于数据主题的**所有**内存**及其`derived_from`行谱**(还包括从主题数据构建的摘要,因此天真文本匹配删除会错过),然后为每个记录追加签名的哈希链**删除墓碑**。 墓碑提交记录的随机替代ID + 时间戳 + 不透明的`request_id` **和** **没有内容派生**(PII的哈希仍然是PII),因此`verify_writes()`现在报告删除为**已记账**(链完整,有效删除)而记录缺失**没有**墓碑仍然标记为篡改 - 而伪造的墓碑被相同的检查捕获。 `erasure_report()`是无内容的删除证明轨迹。
**诚实范围**:这在*此*mnemo存储中删除+证明操作**仅限于此**(不是您的矢量存储,提示日志或备份);它是一个完整性基元,**不是**合规认证,并且签名仅在负载上对抗没有持有`receipt_key`的一方。 先前技术:加密粉碎;Cassandra /事件源墓碑;GDPR第30条删除日志;Crosby-Wallach / Certificate-Transparency篡改日志。 接收:`mnemo/probes/forget_subject_tombstone_probe.py`(8/8)。
### OpenAI Agents SDK的内存替换:`MnemoSession` (0.6.20+)
`mnemo.integrations.openai_agents.MnemoSession`是一个持久的[`Session`](https://openai.github.io/openai-agents-python/sessions/)后端 - 与`SQLiteSession`/`RedisSession`填充相同的插槽 - 因此代理对话在重启后存活:
```python
from agents import Agent, Runner
from mnemo.integrations.openai_agents import MnemoSession
session = MnemoSession("user-42", path="sessions.json") # 一个存储可以保存许多会话
Runner.run_sync(agent, "hi", session=session)
```
它忠实地实现了协议(`get_items`/`add_items`/`pop_item`/`clear_session`,逐字项目,`limit`=最新-N,多会话隔离),并且**没有依赖** - SDK在结构上匹配,从不导入。 **诚实范围**:`Session`是一个逐字日志,因此mnemo的超会话/echo_guard(键控**事实**)不会自动清理重放消息 - 用于防毒事实内存使用mnemo的核心`remember(key=…)`/`recall()`。 它**免费**为用户提供额外的功能:用户回合的删除带有签名,无内容删除墓碑(`session.forget_subject()`),以及防篡改历史(`store.verify_writes()`启用收据)。 接收:`mnemo/probes/mnemo_session_adapter_probe.py`(11/11)。 适配器位于`mnemo.integrations`(可选额外功能)。
### AutoGen的当前真相内存:`MnemoMemory` (0.7.0+)
`mnemo.integrations.autogen.MnemoMemory`实现了AutoGen的[`Memory`](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/memory.html)
协议(`add`/`query`/`update_context`/`clear`/`close`)- 而这里mnemo的价值是偶然的。 与逐字`Session`不同,AutoGen `Memory`检索事实并在每个回合注入它们,因此 **`recall()`隐藏已过时的值意味着代理基于当前真相,而不是过时的值**:
```python
from autogen_agentchat.agents import AssistantAgent
from mnemo.integrations.autogen import MnemoMemory
mem = MnemoMemory(path="mem.json")
agent = AssistantAgent("assistant", model_client=..., memory=[mem])
```
传递稳定的`key`(+ `object`)到内存的`metadata`以驱动确定性超会话 - 后续`key="user::timezone", object="PST"`退休早期的`UTC`,而`update_context`只注入`PST`。 通过`mnemo/probes/mnemo_autogen_adapter_probe.py`验证端到端(7/7,包括“已过时值未重新注入”)。 零依赖核心:AutoGen被延迟加载到适配器中,从不通过`import mnemo`。
### 用于LangGraph的可查询历史:`MnemoStore` (0.7.1+)
`mnemo.integrations.langgraph.MnemoStore`是LangGraph的[`BaseStore`](https://langchain-ai.github.io/langgraph/reference/store/)
(忠实的`put`/`get`/`search`/`delete`/`list_namespaces` + `batch`/`abatch`)- 并且由于LangMem位于任何BaseStore,一个接口适配器可达两个。 与内置的`InMemoryStore`具有相同的最后写入获胜语义, 而它会丢弃的是:**历史记录**。 在`InMemoryStore`中,键控写入覆盖键值,在`InMemoryStore`中旧值消失;`MnemoStore`保留它在mnemo的超会话账本中,因此`store.history(namespace, key)`返回键控的所有值 - 以及时间点读取,防篡改收据和`forget_subject`擦除。
```python
from mnemo.integrations.langgraph import MnemoStore
store = MnemoStore(path="lg.json")
store.put(("user","42"), "timezone", {"tz": "UTC"}); store.put(("user","42"), "timezone", {"tz": "PST"})
store.get(("user","42"), "timezone").value # {"tz": "PST"} (像InMemoryStore)
store.history(("user","42"), "timezone") # [{"tz":"UTC"}, {"tz":"PST"}] (仅mnemo)
```
通过`mnemo/probes/mnemo_langgraph_adapter_probe.py`验证端到端(9/9,包括“InMemoryStore无历史”对比)。 继承自BaseStore,因此导入此模块会导入LangGraph(可选额外功能);`import mnemo`仍然是零依赖。
### 在信任之前标记冲突:`check_conflict()` (0.7.2+)
从业者不断发现同样的招数:停止信任写入路径,检查每个新事实与已存储的事实是否冲突,并在提交前标记冲突。 `check_conflict(text, key=…, object=…)`执行此操作, 只读且无需LLM:它返回新的事实将与之冲突的活动内存 - 键控`key`的值变化,或具有类似内存的数值/否定冲突 - 因此您可以控制、审查或拒绝写入`remember()`之前。
信号是值/否定冲突,**不是**余弦相似度 - 这就是它的意义所在:修正后的值通常与原始值更相似,而不是重述(AUROC ~0.59 区分它们),因此一个“过于相似,肯定是重复项”的门户会默默地吞噬矛盾。 传递`incompatible(a, b) -> bool`(例如LLM裁判)以捕获具有无数值/否定标记的纯语义矛盾。 机制是教科书式的(数据库CHECK-constraint验证写入;TMS断言矛盾,Doyle 1979)- 这里它是本机、零依赖的基元。 也通过`check_conflict` MCP工具公开。 接收:`mnemo/probes/check_conflict_probe.py`(8/8)。
### 用于LlamaIndex的当前真相长期内存:`MnemoMemoryBlock` (0.7.1+)
`mnemo.integrations.llamaindex.MnemoMemoryBlock`是LlamaIndex的长期 [`BaseMemoryBlock`](https://developers.llamaindex.ai/python/framework/module_guides/deploying/agents/memory/)
(异步 `_aget`/`_aput`),因此它与内置的静态/事实提取/矢量块一起位于`Memory`上:
```python
from llama_index.core.memory import Memory
from mnemo.integrations.llamaindex import MnemoMemoryBlock
memory = Memory.from_defaults(session_id="s1", token_limit=40000,
memory_blocks=[MnemoMemoryBlock(name="mnemo", path="mem.json", k=5)])
```
与AutoGen块的区分度相同:`_aget`通过mnemo的`recall()`检索,因此一旦事实被纠正(通过键控写入),块就不会将过时的值注入提示中。 通过`mnemo/probes/mnemo_llamaindex_adapter_probe.py`验证端到端(4/4,包括“纠正的值未被重新注入”)。 继承自`BaseMemoryBlock`,因此导入它会导入LlamaIndex(可选额外功能);`import mnemo`仍然是零依赖。
### Google ADK 的持久内存:`MnemoMemoryService` (0.7.4+)
`mnemo.integrations.google_adk.MnemoMemoryService` 是一个可直接替换的 Google ADK [`BaseMemoryService`](https://google.github.io/adk-docs/sessions/memory/)(`add_session_to_memory` / `search_memory`),由 mnemo 存储支持,因此内存持久存在,并且检索是基于词汇和语义的排名,而不是内置的单词重叠:
```python
from google.adk.runners import Runner
from mnemo.integrations.google_adk import MnemoMemoryService
runner = Runner(agent=agent, app_name="app", session_service=...,
memory_service=MnemoMemoryService(path="mem.json"))
```
与 `InMemoryMemoryService` 相比有两个额外的优点:`search_memory` 通过超级会话过滤的 `recall()`(不会返回已纠正的关键事实),而 `forget_subject_for(app_name, user_id, request_id=…)` 提供了按用户划分的删除权,带有签名的删除墓碑。在 real `google-adk` 2.4.0 上进行端到端验证(`mnemo/probes/mnemo_adk_adapter_probe.py`,4/4,包括按用户隔离、当前真相和核算删除)。可选的额外功能;`import mnemo` 保持零依赖。
### 面向 Pydantic AI 的工具内存:`mnemo_toolset` (0.7.8+)
Pydantic AI 默认不提供持久内存;模式(Hindsight 的 `hindsight-pydantic-ai` 等)是将内存作为代理工具公开。`mnemo.integrations.pydantic_ai.mnemo_toolset` 返回一个 [`FunctionToolset`](https://ai.pydantic.dev/toolsets/),代理可以调用 — `remember`、`recall`、`check_conflict`、`forget`:
```python
from pydantic_ai import Agent
from mnemo.integrations.pydantic_ai import mnemo_toolset
agent = Agent("openai:gpt-4o-mini", toolsets=[mnemo_toolset(path="mem.json")])
```
与内置的“给模型一个草稿板”模式的区别在于:`recall` 是超级会话过滤的(修正后的值停止表面,因此代理读取当前真相),而 `check_conflict` 允许代理在提交之前测试一个事实是否存在矛盾。可选的额外功能;导入此模块会导入 Pydantic AI(`import mnemo` 保持零依赖)。
### 使治理层能够在自由文本上自行索引:`extractor` 钩子 (0.7.5+)
mnemo 的超级会话、`echo_guard`、`check_conflict` 和 `forget_subject` 都在事实的 `(key, object)` 上索引。这在编写结构化事实时效果很好,但对话 `Session` 或聊天回合是自由文本,没有索引,因此超级会话永远不会触发。只需插入一个 `extractor`,每个 `remember()` 都会为您推导出索引,因此整个治理层在无需逐次索引的情况下与自由文本组合:
```python
import re
m.extractor = lambda t: (m := re.match(r"(.+?) is (\w+)", t)) and (f"fact::{m[1].strip()}", m[2])
m.remember("server timezone is UTC")
m.remember("server timezone is PST") # 相同的派生索引 -> 取代 UTC,无需手动索引=
m.recall("server timezone") # -> 只有 PST
```
您的提取器可以是一个正则表达式或您调用并缓存的 LLM;它返回 `(key, object)` 或 `None`。显式 `key=`/`object=` 始终优先,而损坏的提取器会打开(写入仍然作为纯追加落地)。诚实的限制:超级会话和冲突检测的质量取决于您的提取器,因此派生索引错误会导致错误取代(与错误的手动索引=相同风险)- 保持确定性和可审查性。这是保存前钩子(DB 触发器/ORM 保存前;教材),打包后使得完整性基元无需处处键入线程。
免费文本框架适配器(OpenAI Agents `Session`,AutoGen `Memory`,LlamaIndex `BaseMemoryBlock`,Google ADK `MemoryService`,Pydantic AI `mnemo_toolset`)接受 `extractor=` 并将其连接到其存储,因此只需插入一次即可在对话回合上自动触发当前真相回忆:
```python
mem = MnemoMemory(path="mem.json", extractor=my_extractor) # AutoGen;其他类似
```
在 real `autogen-core` 上验证(`mnemo/probes/extractor_adapter_wireup_probe.py`):如果没有提取器,修正后的事实仍然会泄漏;如果有提取器,则只会回忆当前值。
### 数据最小化:`apply_retention(max_age_days)` (0.7.7+)
这是与 `capacity=`(大小限制)和 `forget_subject`(主题删除)相配套的、基于年龄的 GDPR 存储限制原则:不要保留比所需更长的数据。`apply_retention(days)` 硬删除旧记忆,但绝不会删除键的当前值或分级 `semantic`/`procedural` 事实,这些是实时状态,而不是过时积累。默认情况下,它会删除旧的 **superseded** 值(最小化保留的 PII,这会牺牲那些区间的 `as_of()` 历史,具体取决于 `drop_superseded`),以及旧的无键 **episodic** 回合。你可以直接运行它,也可以空闲时通过 `sleep(retention_days=90)` 运行:
```python
m.apply_retention(max_age_days=90) # 或:m.sleep(retention_days=90)
```
教材(DB TTL / 日志保留),打包为原生零依赖保留基元。验收:`mnemo/probes/retention_probe.py`(7/7,包括“当前键值和语义事实绝不会过期”)。
### 具有撤销解析的单次写入路由器:`route()` (0.7.9+)
“恢复到我们之前的状态”这个名字并没有指定值,因此值键控存储没有要匹配的内容,余弦也没有要抓取的内容 - 它是一个未解析的指针,而不是相似性失败。`route(text)` 提供了正好这两个功能:一个确定性的、账本感知的意图标记器(断言 / 修正 / 撤销 / 回声)在前面,以及一个模糊版本解析器在后面(“返回 / 恢复到以前的状态”→通过 `revert()` 获取前任;“原始 / 我们开始使用的”→第一个版本;命名旧值→该版本)- 因此撤销操作通过认可的重新确认通道在版本图上执行,而相似性从不会在撤销上运行:
```python
m.route("缓存区域是大阪", key="缓存区域", object="大阪")
m.route("修正:缓存区域现在是马尔莫", key="缓存区域", object="马尔莫")
m.route("恢复到我们为缓存区域所拥有的状态") # 没有命名的数值 -> 从账本中恢复大阪
```
衡量(`mnemo/probes/route_probe.py`,148 行):每个标记类 - 修正、值混淆撤销、命名撤销、原始恢复、 innocuous 时间聊天 - 在每个策略下以 1.00 的端到端路由,通过零 LLM(在相同行上测量的 LLM 标记器增加的内容:标记类也为 1.00)。诚实的限制是衡量而不是隐藏的:未标记的重新陈述具有模糊性,因此通过构造 `policy=` 选择失败模式 - `safe`(默认)绝不会在未标记的重新陈述上恢复(回声阻止 1.00 / 合法确认 - 优先 0.00),`context` 通过前面的回合分离诚实的双胞胎(1.00/1.00)但可伪造(伪造的变更感知上下文可以走过它),`trusting` 始终恢复(0.00/1.00)。不可伪造的分隔符是出处 - 显式 `revert()` 通道或撤销标记 - 而不是更智能的分类。也是一个 MCP 工具(`route`)。
### 授权撤销通道:阻止内容撤销修正 (0.7.10+)
值混淆的“恢复到我们之前的状态”和陈旧的回声是字节相同的,因此 - 正如一个尖锐的 r/RAG 线程所说的那样 - 决定因素是一个 **authentication** 问题,而不是 NLP 问题:它不能来自文本,只能来自一个攻击者无法撰写文本的权威。选择加入并且 `route()`/`revert()` 需要一个外部 **capability** 才会恢复一个被取代的值:
```python
from mnemo import Mnemo, new_receipt_keypair, sign_revert
# 对称(零额外依赖):运行环境持有密钥;内容路径无法铸造能力
m = Mnemo(path="mem.json", revert_authority="一个运行环境侧的密钥")
m.route("恢复到我们为该区域所拥有的状态", policy="trusting") # -> action="authorization_required"
m.revert("区域", capability=m.revert_capability("区域")) # 主要路径执行
# 不对称(关闭残留值:即使一个受损的本地运行环境无法铸造):
sk, pk = new_receipt_keypair() # 私钥保留在盒外,存储仅持有公钥
m = Mnemo(path="mem.json", revert_pubkey=pk)
cap = sign_revert(sk, m.revert_challenge("区域")) # 只有盒外的私钥才能生成此值
m.revert("区域", capability=cap)
```
设置了权限后,基于文本的撤销永远不会执行 - `route()` 返回 `authorization_required` 并且主要确认在带外;`remember(reaffirm=True)` 也被同样的方式控制,因此原始基元无法绕过它。能力绑定到键和当前记录(`revert_challenge`),因此捕获的能力无法在值移动或重新定位到另一个键后重放。教材能力安全(Dennis & Van Horn 1966)/混淆委托修复(Hardy 1988),打包到内存存储的撤销路径上。诚实的边界:这关闭了内容→恢复路径(和,在非对称模式下,本地运行环境→恢复路径);它不会阻止被盗的私钥或验证人类。敌对验收:`mnemo/probes/authorized_revert_probe.py`(11/11:阻止内容,运行环境无法铸造,拒绝重放/重定位/伪造,主要路径有效)。
## 用作 MCP 服务器(任何 Claude / Cursor / 代理客户端)
`mnemo` 提供了一个 [MCP](https://modelcontextprotocol.io) 标准输入/输出服务器,因此任何兼容 MCP 的代理都可以将其用作长期内存 - `remember`(带有每种类型的衰减优先级),值排名 `recall`,`consolidate`,`consolidate_clusters`,`contradictions`,`value_by_cohort`,`forget`(验证删除)。MCP `remember` 公开 `key`(确定性取代)加上 `object` / `reaffirm`,并且服务器默认启用 **`echo_guard`**(0.6.11),因此修正后的事实即使稍后重新声明旧值也保持修正。设置 `MNEMO_ECHO_GUARD=0` 以禁用。从 PyPI 直接安装和运行服务器(`[mcp]` 额外功能会拉取 MCP SDK;核心库保持无依赖):
```bash
pip install "agora-mnemo[mcp]" # 库 + MCP 服务器 SDK
mnemo-mcp # 通过标准输入/输出进行 MCP 通信
```
使用任何 MCP 客户端注册它 - Claude Code(`.mcp.json`),Claude Desktop(`claude_desktop_config.json`),Cursor,Windsurf,Codex,Gemini。 使用 `uvx` 零设置(在首次运行时安装):
```json
{
"mcpServers": {
"mnemo": {
"command": "uvx",
"args": ["--from", "agora-mnemo[mcp]", "mnemo-mcp"],
"env": { "MNEMO_PATH": "./mnemo_memory.json" }
}
}
}
```
或者,在安装 `pip install "agora-mnemo[mcp]"` 后,直接使用控制台脚本:
```json
{
"mcpServers": {
"mnemo": {
"command": "mnemo-mcp",
"env": { "MNEMO_PATH": "./mnemo_memory.json" }
}
}
}
```
对于 **semantic** 回忆,通过 `MNEMO_EMBED_URL` / `MNEMO_EMBED_MODEL` / `MNEMO_EMBED_KEY` 指向任何 OpenAI 兼容的嵌入端点;如果没有设置,则使用词汇回退。然后,代理在推理前调用 `recall(query)`,在学习时调用 `remember(fact)` - 其内存是值排名和追加的,而不是最近的缓冲区。
## 四个操作
| 操作 | 执行操作 |
|---|---|
| `remember(text, tags, value, mtype, key)` | **追加** 原始捕获,绝对 UTC 时间,绝不编辑;`mtype` ∈ {episodic, semantic, procedural} 设置 **衰减优先级**(事件快速消退,持久事实缓慢消退,规则几乎不消退)。可选 `key` = 一个 **确定性(主题,关系)取代键**:新值取代具有相同键的每个活动记录 - *没有相似性阈值,没有 LLM* - 因此回忆绝不会提供过时的值(双时间:后填充的早期值无法覆盖当前值) |
| `recall(query, k, where=…)` | **值排名** 检索:相关性 × 值,**按内存的每种类型半衰期衰减**(访问重置时钟),因此重要的持久记忆优于相似和过时的记忆。可选 `where` = 一个 **元数据预过滤器**(廉价的 *过滤后再排名* 杠杆):字段 → 标量/列表/运算符(`$gte $lte $gt $lt $in $nin $ne $contains`),匹配顶级然后 `meta`,所有字段 AND-ed - 例如 硬时间范围 `where={"valid_from":{"$gte":t0,"$lte":t1}}` 或封闭集实体 `where={"speaker":{"$in":[…]}}`。衡量以击败 LoCoMo 上的检索器选择(`probes/locomo_metadata_prefilter.py`);它是一个 **硬** 过滤器,因此在有损/预测提取时保持宽松(错误的过滤器会硬删除答案)。强化是 **相关性加权**(一个命中优于另一个,因此弱但频繁的假阳性无法长存);重复召回的情景记忆 **升级为语义**,只有在得到证实时 - 通过赚取的结果,或 **≥2 个不同的*规范*源**(在计数前解析实体,因此 sybil 相同来源的变体 - `Wikipedia` / `wikipedia.org` / 一个完整的 URL - 折叠为一个,不能铸造持久性);并且一个其来源后来被矛盾的记忆是 **证明降级** + 标记 `stale_derived` |
| `consolidate(keep)` | **梦之旅**:标记通用匹配器 *中心*,链接近似重复,应用 **状态切换守卫**(极性冲突取代,不合并),取代低价值过剩 - 仅 *添加* 一个派生层 |
| `consolidate_clusters(threshold)` | **集群触发** 整合:仅当语义集群增长超过 `threshold` 时整合 - 稀疏主题保留其原始情节,密集主题不无限增长 |
| `contradictions()` | 标记相互不兼容的 **相关** 记忆(相似性门控)供人工审查 |
| `forget(ids, where)` | 一个操作,**真正删除**(其他都是追加):硬删除匹配的记录 *和* 从每个幸存者的链接 + 切换指针 + 矢量/标记缓存中清除它们的 ID,因此被遗忘的记忆不能通过回忆、整合链接或梦之旅重新出现。用于删除 / 被遗忘权 / 中毒删除或硬修正 - 在经过验证的遗忘严酷测试中衡量 15/15 |
## 五条规则它不会破坏(每一条都花费我们去学习)
1. **原始捕获是不可变的。** 整合添加链接和标记;它绝不会覆盖源。这停止了 LLM 重写内存的缓慢精度漂移。
2. **写入时的绝对时间戳。** 相对/派生时间一经整合就会腐烂。
3. **值排名,类型感知衰减。** 保留是 `值 × 每种类型的半衰期`,而不是基于最近或访问频率。 *统一* 访问重置时钟保留了流行的记忆,而基于负载的但冷的事实 - 每月查询一次,防止破坏性操作 - 饥饿;我们准确测量了这一点。修复是半衰期由 **种类** 设置,而不是读取次数:情景事件在几天内消退,语义事实在几个月内消退,程序规则几乎不会。冷但关键的事实通过 **类型** 语义/程序(长半衰期 × 其高价值)存活,而不是通过频繁读取;访问仅在类型窗口内重置时钟。
4. **在群体层面上报告价值**(标签/时间块),而不是每条记忆。
5. **矛盾被标记,从不自动解决。** 沉默的重写会破坏对整个记忆的信任。
## 来源 — 为什么有这些规则,有凭证
`mnemo` 的设计不是凭感觉;它是 Agora 实验室 *测量* 的结果:
- **语义召回优于关键词召回,且随着规模扩大,差距加大** — 随着存储库增长到约 6,000 条笔记,全语料库的词汇 `recall@5` 从 **0.94** (小存储库)下降到 **0.25**,而语义 **保持在约 0.65** — ≈**2.6×** 在完全规模下(Agora 实验室 `b4c260`);在改述查询中,语义 `recall@5` 为 **0.86 vs 0.20** 词汇(`3501f1`)。嵌入器在规模上是真正的杠杆;词汇重叠匹配是零依赖 *底线*,即使在小存储库上也能运行。 (诚实的脚注:剪枝通用匹配器 *中心* 笔记仅在存储库被链接垃圾邮件时将词汇召回率提升约 20%,并且 **不** 改变语义召回率 — 它是一种词汇/混合优化,而不是一个标题。)
- **价值排名的整合** — 在保持预算下,按价值排名 *保留什么* 比 FIFO/随机更好,并且优势 **随着预算缩小而超线性扩展**(≈1.8× 在半预算 → ≈4× 在八分之一预算),在承受重估噪声方面存活下来。
- **保留必须将价值与最近性结合,而不是仅基于访问的衰减** — 我们模拟了一个半衰期重置访问策略(一种 *流行度* 信号)与在缩小预算下的价值感知混合策略,价值被故意设置为与访问频率反相关,以承载但冷的负载。对于 30% 的保留预算,访问衰减策略仅保留了 **2.8%** 的高价值/低频记忆和 **20%** 的总价值,而混合策略为 **100%** 和 **64%** — 约 **3× 保留的价值更多**(即使在 7% 的预算下,差距仍然存在,≈2.2× 保留的价值)。纯粹的访问频率衰减使很少被查询但关键的记忆饿死;遗忘必须消耗一个明确的价值通道 *独立于* 访问最近性。(Agora 实验室 `19d802`。)
- **超会话需要确定性的键,而不是嵌入相似性** — 在我们自己的本地 `nomic` 堆栈上复制外部结果(MemStrata / Yadav,arXiv 2606.26511):一个余弦相似性分类器,将 *矛盾* 的事实与 *改述的重复* 分开,分数 **AUROC ~0.61** (接近随机)— 矛盾通常 *更* 嵌入类似于原始的,而不是真正的改述。因此,基于相似性的存储器在 **约 42%** 的时间内提供 *过时的值**;确定性 `(subject, relation, object)` 超会话键(`remember(..., key=...)`)将其驱动到 **0%** (Agora 实验室 `exp_supersession_replication`,严重测试 8/8)。这就是为什么超会话是一个键,而不是一个阈值。
- **没有单一的召回机制能够在所有操作点存活下来 — 只有分层存储器才能** — 在合成的 *演化 + 污染* 流(稳定/过时/中毒事实,本地 `nomic`)上进行正面比较:一个朴素的 **余弦 top-1** 存储器得分 **42%** (在稳定方面表现良好,但对超会话 *盲目* — **0/8** 在更新的事实 — 并且被重复的谎言所欺骗);一个 **最近性** 存储器 **67%** (修复了超会话,但提供了 *最新的谎言* — **0/8** 在中毒);`mnemo` — 确定性超会话键 **+** 证实门 **+** 价值排名 — 是 **100%**,在所有三个方面都具有鲁棒性。每个单一机制在一个区域中获胜,在另一个区域中失败(*记忆操作点陷阱*),这就是为什么持久层需要将它们三个结合起来(探针 `mnemo/probes/operating_point_memory.py`)。
- **群体级价值** — 每个记忆的结果归因在统计上是 *不充分的*,在 n=1 时;群体是信号所在的地方。因此规则 4。
- **矛盾检测** 在生产环境中运行在 6,000 条笔记的保险库上;它必须 *标记,而不是自动编辑*(规则 5)的教训是为什么禁止沉默重写。
(方法和数字存在于 Agora 的跟踪记录中:<https://dancenitra.github.io/agora/>。)
## 威胁模型和分层防御(对抗性记忆完整性)
不可信任的摄取内存存储无法确定写入的声明是否 *真实*。`mnemo` 不尝试这样做;它让攻击者 **付出代价**,并且诚实的映射,即每一层购买的是什么 — 在公开从业者线程中经过对抗性审查后 — 如下所示。 这里的每个声明都有一个可运行的凭证,位于 [`mnemo/probes/`](probes/) 中;这是教科书式的机制,有凭证,**不是** 新理论。
**攻击者也可以编写的防御是建议,而不是防御。** 内容声明的来源是 *剧场*:`Source: X` 和 `corroborated by N` 是作者可以控制的字符串,因此默认(不同的源 *字符串*)证实会沦为 Sybil 攻击,它会伪造两个标签(在 10 个模型中约 0.9 攻击成功率 — [`memory_defense_layer_probe`](probes/memory_defense_layer_probe.py))。只有作者 **不** 控制的通道才能持有 — 不同的 *已验证的键*(`strict_corroboration`,Ed25519 `attest`)**其发布本身是昂贵/限速的**(免费铸造的键只是另一个字符串,Sybil 可以花费),一个 *赚取的* Beta(好,坏)结果信用,会议不能自我授予,以及 *系统写入历史*。
那是 Biba 完整性(1977)/ Cheng-Friedman(2005):没有对称声誉是 sybilproof 的;逃脱口是一个外源的、不可写的锚点。
**层,以及每个层留下的确切残留量:**
- **来源 — 调用是否发生?** 将立足点绑定到 *运行时* 签名,覆盖真正的 `(tool, result)`,而不是会话的日志([`execution_receipt_gate`](probes/execution_receipt_gate_probe.py))。在执行器是与会话 *分开的信任域* 时,关闭伪造的日志;进程内工具 → 键是会话可读的 → 剧场。
- **真实性 — 真实调用的输出是否真实?** 没有写入时预言机(新事实没有先验;证明 = 作者身份,而不是真理)。立足点在 *使用时* 从观察到的结果中 *赚取*([`veracity_outcome_grounding`](probes/veracity_outcome_grounding_probe.py))— 只在损害可观察的地方有所帮助。
- **不可逆的尾部。** 基于来源的 `spend_irreversible` 预算,使低来源的记忆绑定诚实行为的不可逆支出([`soft_influence_taint`](probes/soft_influence_taint_probe.py));可逆性检查点限定了一次性的叛逃者。限定,而不是关闭(Sybil 将其重定位到每个身份)。
- **亚阈值协调的运动。** 时间变化检测器可以免费在偏差 `h→0` 时规避,但 `B≈N·h` 迫使增长的相关低来源人口 **横截面** 检测器捕获([`two_detector_trap`](probes/two_detector_trap_probe.py));并且随机检索将 *目标* 攻击者的覆盖范围从 O(1)提高到 ~Θ(fN),无需锚点([`moving_target_retrieval`](probes/moving_target_retrieval_probe.py))。
**基石。** 从约 8 个方向检查(检测,声誉,诱导 - 无地面实况,如贝叶斯真理血清,鲁棒聚合,移动目标),我们检查的每一层都归结为两个外源锚点之一 — 有限的腐败 *计数*(拜占庭;Sybil 破坏它)或共享的攻击者独立 *先验*(对等预测;协调其报告的联盟破坏它)。 *(这些情况的综合,而不是证明。)* 您不能将大型协调联盟与内部信号的真实共识分开(Cheng-Friedman + Lamport-Shostak-Pease 1982;并且没有内部真理预言机,以 Tarski 不可定义性为类比)。剩下的不是“放弃”,而是一个形状: **在罕见的高后果不可逆步骤中本地化一个外源检查**(人类,单独证明的源 - 通道,毒药无法到达),并且 **不要让无证据的共识驱动不可逆的行为**(将其权重视为 ~0;在可观察的目标上,需要独立的证据来源,这是伪造的超线性,而不是 N 声誉)。残留量是那个最小锚点的完整性 — 一个标准,有限的问题,而不是不可解的全部记忆验证。
## `second_brain` 思考层
`mnemo_mcp` 为代理提供 **记忆**。`second_brain_mcp` 为它提供 **第二个思考大脑** — 将其指向任何 Markdown 笔记的文件夹(Obsidian 保险库,Zettelkasten,`docs/` 树),MCP 客户端(Claude Desktop,Claude Code,Cursor,您的代理)获得基质,以便 *推理* 这些笔记:提取相关的东西,找到网络的盲点,浮现非显而易见的桥梁,隔离值得检查的声明,并通过命名方法生成想法。
**保持诚实的分割。** 服务器返回 **检索 + 结构**;调用 LLM 执行 **推理**。工具是记忆和地图;代理是思维。在此服务器内部没有 LLM 调用 — 它对笔记进行评分,链接和切片,然后将材料交回。因此,以下声明是关于代理如何使用工具,而不是工具“独立思考”。没有自主预言机。
**今天运行,零配置。** 它在启动时将您的笔记索引到进程内的 `mnemo` 存储中;如果没有嵌入器,它使用词汇重叠回退。在 ~6,000 笔记的保险库中,词汇 recall@5 从 0.94(小存储库)下降到 **0.25**,而语义 **保持在 ~0.65** — ≈2.6×(Agora 实验室 `b4c260`);在改述查询中,语义 recall@5 为 **0.86 vs 0.20** 词汇(`3501f1`)。
```bash
NOTES_DIR=/path/to/your/vault python second_brain_mcp.py # 在下载两个文件后运行
```
### 看看它运行(无需设置)

`python examples/demo.py` 在一个小型的捆绑样本保险库中运行所有工具 — 无需 MCP 客户端,无需密钥,无需嵌入器。(使用 `python examples/_make_gif.py`(Pillow)或 [`examples/demo.tape`](../examples/demo.tape) + [`vhs`](https://github.com/charmbracelet/vhs) 重新生成 GIF。)
```text
▸ relevant_notes("如何通过反馈加速学习", k=3)
→ Deliberate Practice (Learning) 相关性 0.60
→ Expected Value (Decisions) 相关性 0.20
▸ find_gaps() → isolated: ["Sourdough Starter"] (一个没有 [[链接]] 的笔记)
▸ bridge_candidates("Deliberate Practice")
→ Habit Loops (Habits, DISTANT 域) — 两个都开启了“反馈延迟”,而它们之间没有任何联系
▸ extract_claims("Deliberate Practice")
→ "反馈延迟是一个隐藏的变量:动作和反馈之间的间隔越长,学习速度越慢。" (第 3 行 — 去验证或挑战它)
▸ idea_methods() → 10 个食谱(Hidden-Connection Bridge, Missing-Reciprocity, …)
```
这个 `bridge_candidates` 命中的要点是:一个跨两个文件夹的连接,您 *没有* 链接 — 代理现在写入映射(或拒绝它)。工具找到了材料;代理进行了思考。
用 MCP 客户端注册它(将 `args` 指向文件的绝对路径,以便找到 `mnemo.py`,它位于旁边):
```json
{
"mcpServers": {
"second_brain": {
"command": "python",
"args": ["/abs/path/to/second_brain_mcp.py"],
"env": {
"NOTES_DIR": "/abs/path/to/your/vault",
"SECOND_BRAIN_INDEX": "/abs/path/to/second_brain_index.json"
}
}
}
}
```
| 工具 | 返回 |
|---|---|
| `index_status` | 已索引的笔记,文件夹分布,已解析的 `NOTES_DIR`(首先调用;`0` ⇒ 修复 `NOTES_DIR`) |
| `relevant_notes` | 按相关性 × 累积价值(价值随着使用而累积;冷索引有效地按相关性排名)排名前 `k` 个最相关的笔记,附带摘录 |
| `coverage_gap` | 问题的 *负面空间*:顶级笔记 + 衡量完整性得分 + 明确指出 *没有* 支持笔记的术语 — WYSIATI 保护装置,防止代理在不完整但完整的上下文中提供虚假自信的答案 |
| `find_gaps` | 隔离/链接不足的笔记 + 薄文件夹 — 网络的盲点(在小保险库上噪声较大;在大规模上发挥作用) |
| `bridge_candidates` | 语义上接近的远程笔记(不同文件夹,无链接)= 候选连接;代理写入或拒绝映射 |
| `extract_claims` | 笔记中的类似声明的句子,以便代理可以验证或挑战它们 |
| `idea_methods` | 命名想法生成食谱的工具包,以便生成有原则,而不是凭感觉 |
在作者自己的 ~6,000 笔记保险库中,使用这些工具的代理捕获了一个数字,作者的预测笔记中膨胀了 ~7 倍(“60-78%” vs 真实的 ~6-11%),浮现了两个无声矛盾的笔记,并通过 `idea_methods` 提出了想法 — 其中两个在 Agora 的独立研究实验室中进行了严格测试(不是在这个服务器内部),并被保留。LLM 进行了推理;纠正仍然需要在公共引用之前进行源检查。
## 信任与安全
- **只读您的笔记。** 服务器递归读取 `NOTES_DIR`;它不执行任何 `eval`,不执行 shell,不执行子进程,并且只写入自己的索引文件。指向 *外部* `NOTES_DIR` 的符号链接/连接将被刻意 **不** 跟随(因此,共享/克隆保险库中的种植链接无法从磁盘上的其他地方泄露文件)。
- **嵌入器是一个信任边界。** 如果您设置 `MNEMO_EMBED_URL`,**每条笔记的全文** 都将被发布到那里。它在启动时被验证 — 任何地方的 `https`,纯 `http` 仅限环回(本地 Ollama 等),云元数据/链接本地目标被拒绝。仅将此指向您信任的端点。
- **跳过 ~2 MB 以上的笔记**(可通过 `SECOND_BRAIN_MAX_BYTES` 配置),以便单个巨大的文件不会耗尽内存。
## 状态
`v0.2` — 核心,诚实且可运行,**现在有两个 MCP 服务器**(`mnemo_mcp` 用于内存,`second_brain_mcp` 用于笔记上的思考层)**和一个确定性的超会话键**(`remember(..., key=...)`),它关闭了嵌入式 **超会话盲点**。路线图:可插拔的矢量存储,托管层。开放核心;核心保持免费。
MIT 许可 · [Agora](https://github.com/DanceNitra/agora) 的一部分。
## 自我维护(maintain.py)
第二个大脑的最大挫折是 **维护**,而不是捕捉。`maintain.py` 运行人们停止做的杂务 — 在 Markdown 笔记的文件夹上,它找到 **死的 [[wikilinks]],孤立的笔记,旧笔记,近似重复的集群**,以及一个 **保险库健康评分**(`self_legibility` = 知识图中的巨型组件中的笔记百分比 — 知识债务是一种 *渗流* 崩溃,因此在 **悬崖** 之前发出警告)。关键是,它将发现转化为 **行动**:对于每个孤立的笔记,它 **建议将其链接到哪个现有的笔记**(将它重新连接到图中),并标记 **存档候选者**(旧且孤立的)。它通过文件名 *或* 前置别名解析链接,并通过前置元数据(不是 git-reset mtime)对笔记进行日期标注 — 两个都从在现实的 ~7,700 笔记保险库(它挽救了 ~300 个错误标记的孤立者)中吸取的教训。建议 + 安全:它返回计划和行动列表;它永远不会编辑,移动或删除笔记。并且它可以 **应用** 修复:`apply_suggestions` 将标记的 `## Related (auto-suggested)` 块的 [[链接]] 追加到每个孤立者 — 仅附加,幂等(重新运行替换它自己的块),**默认干燥运行**。`python maintain.py` 在合成保险库上运行验证往返(诊断 → 建议 → 应用);`maintenance_report` 和 `apply_links` 在 `second_brain_mcp.py` 中向任何 MCP 代理公开它。
<!-- MCP 注册所有权证明 -->
mcp-name: io.github.DanceNitra/mnemo
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器