跳到正文
博客园· 罗西的思考·· 2 小时前AI 评分42

MemTrace 如何实现 LLM 记忆系统的自动溯源与自动优化

【Agentic Memory】MemTrace 如何做到自动溯源和自动优化 - 罗西的思考

AI 导读

MemTrace 框架将 LLM 记忆流水线转化为可执行的记忆演化图,通过迭代追踪操作子图定位失败案例的根因,并利用归因信号指导提示词优化,在 Mem0 + LoCoMo 上三轮优化后下游任务性能提升最高 7.62%。作者同时构建了 MemTraceBench 基准,从 Long-Context、RAG、Mem0 和 EverMemOS 等记忆系统中收集数据,系统性研究记忆失效模式。

正文

【Agentic Memory】MemTrace 如何做到自动溯源和自动优化


0x00 概要

记忆对于让大语言模型支持长时程推理至关重要,然而现有记忆系统仍然不可靠且难以调试。追踪记忆的动态演化,是理解信息如何随时间被综合、传播或污染的关键。MemTrace 作者研究了大语言模型记忆系统中错误追踪与归因这一新问题,然后提出了一种新颖框架,将记忆流水线转化为可执行的记忆演化图,从而实现对操作级信息流的细粒度追踪。随后该作者也构建了 MemTraceBench——一个从 Long-Context、RAG、Mem0 和 EverMemOS 等代表性记忆系统中收集的基准,用以系统性地研究记忆失效模式。该作者还引入了一种自动归因方法,通过迭代追踪操作子图,精确定位任意失败案例的根因。分析表明,记忆失效是系统性的,其根源在于信息丢失、检索错位等操作级问题。关键的是,该作者还利用这些细粒度归因信号来指导下游提示词优化,建立起一个能自动修正故障的闭环系统,并将下游任务性能提升最高达 7.62%。

0-framework

论文:MemTrace: Tracing and Attributing Errors in Large Language Model Memory Systems https://arxiv.org/abs/2605.28732

代码:https://github.com/zjunlp/MemTrace

0x01 MemTrace 在做什么——三层递进解读

下面用三个层次解释同一个核心问题:当 LLM 记忆系统答错问题时,MemTrace 如何自动找出根因并修复它?


1.1 第一层:给孩子的解释

想象一个机器人管家,专记全家人的喜好。你告诉它"妈妈爱喝热可可",它记下了。后来你问它"妈妈喝什么",它答"可乐"。妈妈从来不喝可乐——哪里出错了?

自动溯源就是让 MemTrace 当侦探。它不像普通人只看最后一步("答错了"),而是从头翻小本子:找到"热可可"这条记录,追踪它在小本子里的经历——有没有被涂改?有没有被撕掉?有没有被别的字盖住?如果发现中途有人把"热可可"改成了"可乐",那这就是罪犯。它告诉我们:问题出在记忆更新环节,类型是更新错误。

自动优化更进一步。既然找到了罪犯——原来是那条"更新指令"写得不好,让机器人在更新时把正确信息弄丢了。那我们改改这条指令,比如加上一句"发现新旧矛盾时,保留旧信息并标记提醒"。下次就不犯了。

整个过程:侦探破案 → 找到罪犯 → 修改法律条文 → 不再犯。


1.2 第二层:给半专业人士的解释

LLM 记忆系统的流水线有四步:

消息 → [事实提取] → [存储/更新/删除] → [检索] → [LLM 回答]

答案错了,原因藏在前面任一步。手动排查像大海捞针。MemTrace 的解法是用执行图串联整个过程。

第一步,smartcomment 录制执行图:在记忆系统的关键位置插入追踪代码,记录每一步产出了什么变量(事实列表、记忆单元、检索结果、提示词)、执行了什么操作(LLM 调用、检索、过滤)、以及变量和操作之间的依赖关系。最终得到一张有向无环二分图——不是扁平的日志,而是体现信息传递路径的网络。

第二步,MemTrace 在图上自动归因:把执行图和失败问题交给一个配备了图工具的大模型 Agent。Agent 从最早的消息开始,沿图逐操作检查:这个操作在其自身职责范围内正确吗?如果正确,追踪它的产出继续向下游;如果不正确且修正它能挽救最终答案——这就是"最早决定性故障"。输出为 {error_type, op_id, reason}。

第三步,利用归因信号做自动优化:每次失败案例的归因结果,相当于给了系统一个"改进方向"(论文称之为"文本梯度")。归因找到的故障操作,必然涉及某个可配置的变量(比如 prompt 模板)。把失败案例和归因结果作为"改进信号"馈给优化器,自动改写这些变量的值。多轮闭环后,系统性能提升。

1. 执行记忆系统 → 2. 运行 MemTrace 归因 → 3. 分析哪些提示词导致了错误 → 4. 用 LLM 改进这些提示词 → 5. 回到 1

论文在 Mem0 记忆系统 + LoCoMo 基准上验证,三轮优化后任务性能提升 7.62%。


1.3 第三层:给专业人士的解释

问题形式化

设非参数记忆系统 ℳ 处理轨迹 τ、回答问题 q 得到预测 â(golden answer 为 a)。其执行为三步:记忆更新 𝒰_ℳ、记忆读取 ℛ_ℳ、答案生成 𝒬。

通过 smartcomment 插桩录制得到一个有向无环二分图 𝒢 = (𝒱, 𝒪, ℰ):

  • 变量节点 𝒱:消息、记忆单元、中间摘要、LLM 提示词等执行中的具体产物
  • 操作节点 𝒪:LLM 推理、工具调用、检索、过滤等计算步骤
  • 边 ℰ:变量与操作间的信息流,每个操作 o 有输入 In(o) ⊂ 𝒱 和输出 Out(o) ⊂ 𝒱
  • 结果指示器 Z(𝒢) ∈ {0,1},由 LLM Judge 比较 â 与 a 得到

决定性错误集(Decisive Error Set) 𝒪* 被定义为满足以下条件的最小因果割集:

  1. 故障性:∀ o ∈ 𝒪*,o 在执行中是错误的
  2. 上游正确性:所有严格上游祖先 Anc_𝒢(𝒪*) 功能正确
  3. 因果充分性:将 𝒪* 的输出替换为正确版本且下游假设完美,则 Z(𝒢^(𝒪*,*)) = 0(即能挽救失败)
  4. 最小性:去掉 𝒪* 中任何操作都破坏因果充分性

论文专注于单元素场景 |𝒪*| = 1。

自动溯源(MemTrace)

MemTrace 将归因建模为Agentic Graph Exploration。Agent(ReAct 框架,backbone 为 GPT-4.1 mini 或 GPT-5.4)配备三个模块:

模块 1:起始点选择

  • 构造检索查询:q + a 拼接
  • 对历史消息集同时执行稠密检索和稀疏检索(BM25),各取 top-N
  • RRF 融合,取 ⌊N/2⌋ 条,和 q 一起构成初始前沿队列 ℒ₀
  • 预留前沿容量供 Agent 动态添加

模块 2:图探索

  • 前沿队列按 created_at 排序(越早的变量优先级越高)
  • 每轮取最早变量 v_t,获取 𝒪(v_t) =
  • 每个操作子图通过 RuntimeGraph.to_xml() 渲染给 Agent
  • Agent 判断:局部正确 → 加入下游变量到 ℒ_t;故障 → 检查决定性 → 输出
  • 更新 ℒ_t = (ℒ_{t-1} \ {v_t}) ∪ 𝒜_t

模块 3:上下文管理

  • 预览模式(省略变量值)、view_node_value() 支持分页+正则搜索
  • 超出阈值 T=272k token 时自动摘要压缩

消融变体 MemTrace-OBS:将操作子图压缩为扁平文本块(移除边和变量 ID),按时间戳串联,提供全局正则搜索工具。适用于图结构弱(如退化为长链)的场景。

自动优化

将信用分配(credit assignment)与提示词改写解耦:

smartcomment 录制执行图
    → MemTrace 归因(定位故障操作)
    → 优化问题简化为局部问题(仅涉及该操作的部分提示词)
    → 三阶段文本梯度优化:
       (1) 生成反馈(每个失败 case → 结构化改进建议)
       (2) 聚合反馈(合并冗余、去重)
       (3) 更新目标(根据聚合反馈改进提示词,无关部分不变)

三个可优化目标:fact-extraction-system-prompt、memory-update-decision-prompt、question-answering-prompt。梯度历史使用 deque(maxlen=N) 存储 (previous_value, aggregated_feedback) 对作为优化动量。

0x02 组件详解 —— 一条主线贯穿

以下所有组件围绕一条核心脉络展开,请始终记住这个逻辑链:

smartcomment → 录制 → 执行图DAG
              ↓
         graphtrace → 沿图归因(定位故障操作 o*)
         obstrace   → 消融对照(去掉图结构的影响)
              ↓
         toolkits   → 消费归因结果(优化、报告、评测)
              ↓
         扩展架构   → 从 prompt 优化扩展到任意变量类型

每个组件都有其设计原理(为什么这么做)、具体实现(代码怎么做的)、跨领域类比(编译器等其他领域中的类似思路)。

1-workflow

3.1 设计原理:为什么需要一个新的追踪工具?

回忆一下我们想要什么样的执行图:它不仅要知道"做了哪些操作",还要知道每个操作的输入变量来自哪里、输出变量去了哪里、同一变量在多次操作中的版本演化。这需要追踪工具满足三个要求:

  1. 可插桩任意 Python 代码——记忆系统有异构的 schema,不可能要求它们统一改写为某个框架
  2. 变量是一等公民——要能追踪变量的依赖关系,而不只是事件的时序
  3. 变量是可版本化的——同一逻辑变量经历多次更新后,要能恢复其演化轨迹

下图对比了现有工具:

类型 代表 优点 缺点
插桩式 MLflow, Langfuse 非侵入 事件中心,无变量依赖
抽象原生式 PyTorch, DSPy 自动计算图 必须按框架重写代码

smartcomment 填补了空白:插桩式 + 图追踪。它通过显式插桩追踪任意 Python 变量,但以图结构而非事件流的方式记录变量依赖。这一设计选择背后的信念是:在 AI 辅助开发时代,编写少量插桩代码的成本远低于将系统重写到一个新框架中。

3.2 层次化数据模型

ExecNetwork(顶层执行图)
  ├── graph_id, user_id, project_id(元数据)
  └── GraphDriver(存储后端抽象)
       ├── Session(会话:memory_construction / retrieval / response)
       │    ├── Operation(操作:LLMInference、EmbeddingSearch、Filter…)
       │    │    └── Edges(边:source_variable → operation → target_variable)
       │    └── Variable(变量:消息、事实列表、记忆单元、提示词…版本化)
       └── ...更多 Session...

关键数据结构:

ExecNetwork(smartcomment-main/smartcomment/runtime/network.py:80)——顶层容器:

  • filter_by_operation(op_id) → RuntimeGraph:按操作 ID 提取子图(节点+边+操作三元组),这是归因 Agent 最频繁调用的方法
  • export_graph() / import_graph():JSON 持久化,使执行图可在各阶段间传递
  • _reachable_closure(full_node_id, direction):BFS 可达闭包,支持方向参数和最大深度

RuntimeGraph(smartcomment-main/smartcomment/runtime/graph.py:13)——只读子图视图:

  • .nodes / .edges / .ops:构成子图的三元组
  • .to_xml(include_variable_value=True) → XML 字符串(供 LLM 消费的格式)
  • .to_markdown() → Markdown 字符串

版本化变量(runtime/variable.py:17):

  • full_node_id = name@version(如 memory_unit@3 表示第 3 版)
  • comment_mutation 创建新版本而非覆盖,保留完整演化历史

3.3 核心 API

smartcomment 的公有 API 分为两层:

上下文管理器(控制追踪范围):

API 作用
comment_graph(user_id, project_id) 创建/进入一个执行图
comment_session(session_id, category, metadata) 进入一个会话(如 memory_construction)
comment_op_scope(op_name, category, comment) 进入一个操作作用域

注解 API(录制变量和依赖):

API 作用
comment_variable(value, id_strategy, comment, category) 将任意值注册为追踪变量
comment_link(source, target, comment) 创建变量间的依赖边,自动关联当前操作
comment_mutation(variable, new_version, ...) 标记变量原地更新
comment_fn(...) 装饰器:将函数调用注册为操作

全局控制:

API 作用
enable_tracing() / disable_tracing() 全局开关
current_graph() / current_session() / current_op() 获取当前上下文

上下文通过 ContextVar 实现线程安全(runtime/context.py:24-26),这一设计使得同一进程内可并行录制多个独立执行图。

Identity 机制:记忆系统中同一变量可能在多处被序列化/反序列化。smartcomment 通过 IdentityRegistry + 用户自定义 identity 函数来判定"两个运行时对象是否应视为同一追踪变量"。这是保证版本化变量正确性的关键。

3.4 在 Membase 中的实际使用

membase/runners/construction.py 展示了完整的录制流程:

if cfg.tracing:
    enable_tracing()

with comment_graph(user_id=user_id, project_id=layer_type, ...) as graph:
    with MonkeyPatcher(specs):  # 注入记忆系统的 smartcomment 插桩
        for session in trajectory:
            with comment_session(session_id=session.id, category="memory_construction"):
                for message in session:
                    layer.add_message(message)  # 内部调用 comment_variable + comment_link
        with comment_session(category="memory_construction"):
            layer.flush()

if graph is not None:
    graph_data = graph.export_graph()
    json.dump(graph_data, open(traced_data_path, "w"))

三个 Runner 分别录制不同阶段:ConstructionRunner → 记忆构造阶段、SearchRunner → 检索阶段、EvaluationRunner → 回答与评测阶段。在优化流程中,这三个阶段的图被加载为完整 ExecNetwork。

0x04 graphtrace:图归因 Agent 的核心引擎

这是 MemTrace 的核心——一个专门为 DAG 执行图设计的 ReAct Agent,通过在图上的边跟随来定位最早决定性故障。

4.1 归因原理:三个保证 + 一个归纳

为什么图结构能定位错误?

核心问题

核心问题:给定一个失败执行图 𝒢 = (𝒱, 𝒪, ℰ) 和失败查询 (â ≠ a),目标是找出"最早决定性故障" o* ∈ 𝒪。

这个问题本质上是动态程序切片(Dynamic Program Slicing) 在记忆系统上的应用。我们来拆解其原理。

原理

原理一:信息流必然是沿图传递的

执行图 𝒢 是一个二分有向无环图。每个操作 o ∈ 𝒪 产出 Out(o),而这些产出又会成为下游操作的输入 In(o')。关键前提是:

如果所有上游操作 Anc_𝒢(o) 都正确,且操作 o 自身正确,那么 Out(o) 必然是正确的。

反之,如果最终输出 Z(𝒢) = 1(失败),则必然存在至少一个操作 o,使得 o 自身错误,且其所有祖先都正确。这就是"最早决定性故障"的因果充分性条件——因为 DAG 保证了偏序关系,最早错误的操作一定存在于从输入到输出的因果路径上。

这等价于编译器中的数据流分析。考虑一个经典问题:程序输出了错误值,如何定位?

领域 结构 分析方法 目标
编译器 程序依赖图(PDG) 定义-使用链追溯 找出导致错误值的语句
MemTrace 操作-变量二分图 沿边回溯操作子图 找出最早决定性故障操作

程序依赖图(Program Dependence Graph, PDG) 是编译器中间表示的一种形式,其中:

  • 节点 = 语句/表达式
  • 数据依赖边 = 变量定义→使用
  • 控制依赖边 = 分支条件→受控语句

MemTrace 的执行图与之高度相似:变量节点 = 定义值,操作节点 = 计算/转换,边 = 数据依赖。区别在于 PDG 中控制依赖在 MemTrace 中被显式建模为操作的一部分(因为记忆系统本质上是顺序流水线,控制流是确定的)。

原理二:二分图边跟随等价于定义-使用链追踪

_render_operation_subgraph(graphtrace/_utils/_helpers.py:9)的核心函数做了这样一件事:

给定焦点变量 v_t,获取涉及它的操作集:
    O(v_t) = { o ∈ O | v_t ∈ In(o) ∪ Out(o) }
    
对于每个操作 o ∈ O(v_t),渲染子图 G_o(包含 o 的输入/输出/边/变量值)
Agent 判断 o 是否正确:
    ├── 正确 → 将 Out(o) 中的下游变量加入前沿
    └── 错误 → 检查决定性 → 输出 {error_type, op_id, reason}

这完全等同于编译器调试中的向后切片(backward slice):从某个关注点出发,沿数据依赖边反向追踪所有可能影响该点的语句。

数学基础

归因可行的数学基础——DAG 的三个保证:

  1. 偏序保证:DAG 拓扑序 = 执行顺序。如果 o_i 在 o_j 之前发生,图中不存在从 o_j 到 o_i 的路径。
  2. 因果封闭性:操作 o 的输出 Out(o) 完全由 o 自身及其严格祖先 Anc_𝒢(o) 决定。
  3. 可干预性:Z(𝒢) = 1(失败)→ 必然存在至少一个操作 o,使得修正 o 的输出且保持 Anc_𝒢(o*) 不变即可产生 Z(𝒢') = 0。

有了这三个保证,归因就简化为:从最早的变量开始,沿拓扑序逐个检查操作,直到找到"自身错误且所有上游正确"的那个操作。 这等价于 DAG 上的"第一个断点"搜索——信息流路径上距离入口最近的断裂点。

阶段级归纳

五种错误类型——阶段级归纳:

ErrorAttributionPrediction(toolkits/memtrace_utils.py:266)定义了五类错误,它们构成一个互斥完备划分:

class ErrorAttributionPrediction(BaseModel):
    error_type: Literal[
        "ExtractionError",   # ① 信息从未进入记忆
        "UpdateError",       # ② 信息进入后被降级/覆盖
        "DeletionError",     # ③ 信息被删除
        "RetrievalError",    # ④ 信息在存储中但检索未命中
        "ResponseError",     # ⑤ 检索完备但 LLM 答错
    ]
    op_id: str
    reason: str

为什么恰好是这五类?因为任何记忆系统都包含五个串行阶段:

输入 → [提取] → 记忆 → [更新/删除] → 记忆 → [检索] → 上下文 → [回答] → 输出

信息从输入到输出的路径必然依次经过这五个阶段,不会跳过任何一个。因此任何信息丢失/错误必然发生在且仅发生在其中一个阶段——这就是"互斥完备"的来源。

4.2 编译器领域的类比:程序切片 + Delta Debugging

类比一:程序切片(Program Slicing)

给定程序点 p 和变量 v,程序切片是"所有可能影响 v 在 p 处值的语句"(Weiser, 1984)。计算切片需要对程序依赖图(PDG)做可达性分析。

MemTrace 的图探索本质上是对执行图的动态向后切片:

传统程序切片 MemTrace
结构 程序依赖图(PDG):节点=语句,边=数据依赖+控制依赖 执行图:节点=变量/操作,边=信息流
分析方式 静态分析所有可能路径 动态分析一次具体执行
目标 找出影响某值的所有语句 找出导致错误的最早故障操作
方法 可达闭包(从关注点沿边反向遍历) 边跟随(从最早变量沿边正向检查)

类比二:Delta Debugging

Zeller(1999)用 Delta Debugging 定位导致程序失败的最小输入差异——核心是寻找"最小因果集"。这与 MemTrace 的 𝒪* 最小性约束完全一致:

  • Delta Debugging:二分搜索输入空间,找最小失败诱导子集
  • MemTrace:拓扑序扫描操作集,找最小决定性故障集

两者共享同一个数学模式:在因果偏序集上搜索最小充分失败原因。

类比三:关键路径分析

编译器指令调度中,关键路径是从入口到出口的最长延迟路径——优化关键路径上的指令能最大程度加速。

MemTrace 的"最早决定性故障"是信息流关键路径上的第一个断点:从输入证据到失败回答有一条信息传递链,链上任一环断裂(信息丢失)则下游必然失败。找到距离入口最近的断点,就是最早决定性故障。

类比四:CEGAR(Counterexample-Guided Abstraction Refinement)

CEGAR 的循环是:模型检查发现反例 → 检查反例是否为假 → 若是,沿反例回溯到抽象边界 → 细化该处抽象 → 重新检查。

MemTrace 的循环是:Agent 发现故障操作 o* → 检查 o* 涉及的可优化变量(prompt)→ "细化"该变量的值 → 重新执行验证。CEGAR 在抽象层面"细化",MemTrace 在 prompt 层面"优化"。

4.3 代码实现

归因由两个核心类协同完成:GraphTraceNotebook 管理图探索状态,GraphTraceAgent 驱动 LLM 推理。

GraphTraceNotebook(graphtrace/graph_trace_notebook.py:125)——管理探索状态:

class GraphTraceState:          # graphtrace/models.py:167
    to_explore_nodes: SortedDict[(created_at, full_node_id) → GraphTraceNodeRecord]
    exploration_history: OrderedDict[full_node_id → GraphTraceNodeRecord]

关键方法对应论文的四个模块:

方法 论文模块
initialize_execution_graph(initial_full_node_ids) §4.1 起始点选择
explore_graph_trace_node(offset, limit) §4.2 图探索
view_node_value(full_node_id, pattern, offset, limit) §4.3 上下文管理
update_to_explore_nodes(action, full_node_id) §4.2 前沿管理
finish_node_exploration(outcome) §4.2 节点结论
finish_graph_exploration(outcome) §4.2 全局终结

GraphTraceAgent(graphtrace/graph_trace_agent.py:24)——驱动 LLM:

继承 agentscope.agent.ReActAgent,关键定制:

  • _reasoning()(:164):每个推理步骤前注入 <system-hint>(来自 DefaultGraphTraceToHint),内容取决于当前探索状态——未初始化则提示调 initialize_execution_graph,有前沿则提示 explore_graph_trace_node,正在探索则提示继续/扩展/完成
  • _compress_memory_if_needed()(:189):超过 max_context_limit(默认 600k)时用结构化输出压缩历史

操作子图渲染(graphtrace/_utils/_helpers.py:9):

def _render_operation_subgraph(graph, op, focus_full_node_id, ...):
    # 1. 确定焦点的角色:input / output / intermediate / disconnected
    # 2. 列出直接后继变量
    # 3. 列出子图中叶变量
    # 4. 渲染规范子图(RuntimeGraph.to_xml())

渲染结果形如:

# Operation `o_123`
- Name: `LLMInference`
- Category: `fact_extraction`
- Focus Variable: `message@2`
- Focus Role: The focus variable is an input to this operation.
- Direct Successor Variables: `extracted_facts@3`
- Leaf Variables: `extracted_facts@3`
--- Subgraph ---
<graph>
  <nodes>...</nodes>
  <edges>...</edges>
  <operations>...</operations>
</graph>

LLM 基于此做出判断:这个提取操作是否"在其职责范围内正确"?

4.4 探索策略对比

MemTraceConfig.exploration_strategy 支持两种策略:

策略 代码枚举 前沿初始化 适用场景
基于图探索 "graph_search" 通过混合检索获取起始证据节点 默认,图结构清晰
搜索式探索 "operation_block_search" 起始节点作为搜索索引而非边跟随起点 图结构弱/退化成长链

0x05 obstrace:搜索式消融变体

论文 §4.4 的 MemTrace-OBS:当执行图结构弱或退化为长链时,图的边跟随策略效率低。OBS 将每个操作子图压缩为扁平文本块,按时间戳串联,提供全局正则搜索工具。

5.1 设计原理:为什么去掉图结构?

当执行图结构弱(如长上下文记忆反复追加内容,图退化为一条链)时,边跟随效率低——Agent 每一步只能查看一个操作,而链上的操作几乎等价,逐个检查是浪费。

OBS 的核心假设:

如果操作名、变量值和注释已经能揭示大致的信息流和功能角色,Agent 可以通过全局搜索而不是边跟随来定位故障。

具体做法(obstrace/trace_notebook.py:82):

def flatten_execution_graph(network: ExecNetwork) -> str:
    # 将每个操作子图渲染为文本块
    # 移除:边和唯一变量标识符
    # 保留:操作名、注释、类别、输入/输出/中间变量值
    # 按时间戳排序,用 "### Operation …" 分隔符串联

这创建了一个无 def-use 链的扁平中间表示——类似编译器中的线性 IR(栈式字节码或三元式序列),数据依赖隐含在操作顺序中,而非显式的图边。

OBS 的有效性取决于一个条件:当操作本身的语义足够丰富时(操作名如 LLMInference:fact_extraction、注释如 "Extract key facts from user message"、变量值如 ["User likes hot cocoa"]),Agent 可以通过语义理解来推断信息流,而不需要依赖图结构。

5.2 为什么这是一个有效消融?

两者使用完全相同的 Agent 框架(ReAct)、同一个 LLM backbone、同一组归因指令,唯一差异是信息组织方式。因此性能差异直接量化了图结构对归因的贡献:

维度 graphtrace(MemTrace) obstrace(MemTrace-OBS)
信息结构 有向无环二分图 扁平的线性文本
数据依赖 显式边(source→target) 隐含在操作顺序中
探索方式 边跟随(逐操作正向检查) 全局正则搜索(直接跳到匹配处)
变量标识 name@version 无唯一标识,纯文本内容

论文表 1 的关键发现:

2-table

  • GPT-4.1 mini:MemTrace ETA 36.46% vs OBS 20.00%(+16.46pp)→ 小模型依赖图结构缩小搜索空间
  • GPT-5.4:MemTrace ETA 54.38% vs OBS 53.75%(接近)→ 大模型语义弥补了结构缺失
  • 但 OIA 差距仍存在(46.25% vs 38.13%)→ 精确定位操作仍需图结构

OBS 的操作搜索工具(trace_index.py:107 的 grep)支撑了这种消融设计:

def grep(self, pattern: str, fields: Iterable[str] | None = None,
         regex: bool = True, max_results: int = 10) -> list[GrepMatch]:
    """跨所有操作块的文本搜索,返回匹配的操作 ID 和上下文"""

如果 Agent 怀疑"hot cocoa"关键词在提取阶段丢失,它可以直接搜索,而不是从输入一步步追踪到提取操作。这提供了更灵活的探索路径,代价是失去了结构约束带来的准确性保证。

5.3 编译器领域的类比

SSA vs 线性 IR:

编译器 记忆系统
SSA(图结构,def-use 链显式) MemTrace(执行图,边显式)
线性 IR(三元式,依赖隐含) OBS(扁平日志,依赖隐含)
循环不变式外提沿 def-use 链分析 边跟随追踪信息流
窥孔优化在指令序列上模式匹配 正则搜索在日志上定位操作

断言式调试 vs 顺序调试:

  • 顺序单步调试(图探索):逐语句执行,观察状态变化
  • 断言式调试(OBS 搜索):设置条件断点(正则),命中时检查上下文

OBS 的 grep 工具(obstrace/trace_index.py:107)就是 Agent 的"条件断点"——Agent 基于对错误类型的假设("可能是提取阶段把热可可弄丢了"),直接跳转到对应阶段检查。

控制流图中的可达性分析

编译器在不可达代码消除(dead code elimination)中,从入口节点沿 CFG 的边做可达性分析,标记不可达的基本块。

MemTrace 的边跟随是"逆可达性分析"——从失败输出反向上溯,标记哪些变量/操作参与了失败路径。OBS 的搜索则像基于断言的检查——设置断点条件(正则表达式),命中时检查该位置的上下文。

5.4 核心组件

# TraceIndex(obstrace/trace_index.py:35)
# 解析扁平化日志文本,支持:
index = TraceIndex(ops_text)
index.get_op("o_42")           # 按 ID 取单个操作块
index.grep("hot cocoa",        # 跨块正则搜索
           fields=["input", "output"],
           max_results=10)

# CCTraceNotebook(obstrace/trace_notebook.py:191)
# 包装 TraceIndex,注册四个 Agent 工具:
notebook = CCTraceNotebook(graph)
notebook.inspect_trace(range)     # 查看一段连续的操作日志
notebook.grep_log(regex)          # 全局搜索
notebook.view_log_window(offset)  # 日志分页
notebook.view_operation(op_id)    # 查看单个操作详情

# ObsTraceAttributionAgent(obstrace/agent.py:47)
# 返回 AttributionPrediction = {error_type, op_id, reason, warning, trace_stats}


0x06 自动优化:从归因信号到系统改进

归因的目的是改进。这是 MemTrace 最独特的价值——不仅告诉你哪里错了,还能自动帮你修。

目前的实现只优化 LLM 提示词。

6.1 原理:文本梯度

文本梯度的本质是用 LLM 模拟反向传播,具体做两件事:(1) 分析失败原因并生成改进建议;(2) 根据建议改写目标文本。

神经网络训练 MemTrace 优化
前向传播:模型计算输出 前向:执行记忆系统 → 得到 QA 结果
损失函数:比较输出与标签 LLM Judge:判定回答是否正确
反向传播:计算 ∂L/∂w MemTrace 归因:定位故障操作 o*
梯度:∂L/∂w(数值、连续) 文本梯度:"故障分析 → 改进建议"(语义、离散)
优化器:SGD 更新 w 优化器:LLM 改写 prompt
动量:v ← αv - η∇L / 历史梯度加权平均 梯度历史:deque(旧值, 聚合反馈)

关键差异:数值梯度在连续空间中有明确定义的"方向"和"步长";文本梯度在自然语言空间中,LLM 充当了"语义微分器"——输入失败信号和操作上下文,语义上推断 prompt 应该向哪个方向改进。

这本质上是一种符号优化——类似遗传规划中语义导向的变异操作,而不是数值优化。

6.2 三阶段代码实现

阶段一:生成反馈(optimization_utils.py:833)

对每个归因失败案例,构造如下 prompt:

FEEDBACK_INSTRUCTION_TEMPLATE:
  你的任务是为改进目标变量值生成反馈。

  输入:
  - 目标变量信息(值、注释、类别等)
  - 失败案例(归因结果 + 操作子图 XML)
  - 历史梯度(前几轮的优化历史)

  优化边界:
  - 操作流程不可优化
  - 只有目标变量的值可优化
  - 每一条反馈必须是对目标变量值的具体改进建议

输出 _FeedbackSuggestion({rationale, feedback}):

class _FeedbackSuggestion(BaseModel):
    rationale: str     # 思考过程
    feedback: str      # 具体改进建议

其中 _format_failed_case(:488)将归因结果渲染为 LLM 输入:

def _format_failed_case(error, graph):
    op_graph = graph.filter_by_operation(error.op_id)
    return (
        f"Error type: {error.error_type}\n"
        f"op_id: {error.op_id}\n"
        f"Reason: {error.reason}\n\n"
        "Attributed operation graph:\n"
        f"{op_graph.to_xml(include_metadata=False)}"
    )

阶段二:聚合反馈(:845)

多条失败案例归因到同一目标变量时,产生多条反馈。聚合策略是分层分批摘要:

if len(feedbacks) == 1:
    return feedbacks[0], 0.0        # 单条直接返回

summary = await summarize_batch(feedbacks[:batch_size])
for batch in remaining_batches:
    summary = await summarize_batch([summary, *batch])  # 滚动聚合

这类似 MapReduce 或梯度累积——多条"梯度"不被平均,而是被 LLM 语义融合。输出 _AggregatedFeedbackSuggestion。

阶段三:更新目标(:910)

UPDATE_INSTRUCTION_TEMPLATE:
  根据聚合反馈改进目标变量值。
  与反馈无关的部分应尽量保留。
输出:_TargetVariableUpdate = {rationale, improved_value}

更新约束(模板 :123-125)保证局部性——只改动与梯度相关的部分,对应神经网络中的参数选择性更新。

6.3 梯度历史机制

# optimization_utils.py:575
self._gradient_histories = defaultdict(
    lambda: deque(maxlen=num_gradient_histories)
)

每次更新后存储 (previous_target_value, aggregated_feedback)。在后续轮次注入到反馈 prompt 的 {history_input_section} 中:

def _format_history(history):
    for i, (prev, fb) in enumerate(history, 1):
        yield f"Previous value (iter -{i}):\n{prev}"
        yield f"Feedback from that iteration:\n{fb}"

这对应优化算法中的动量项——在深度学习中,SGD with Momentum 维护速度向量 v ← αv - η∇L;在文本优化中,历史反馈帮助 LLM 避免反复犯同样的优化错误。

6.4 跨领域类比

类比一:AutoTVM / 编译器自动调优

AutoTVM 是 TVM 深度编译器中的自动调优框架,核心流程:

程序 → 搜索空间定义 → 采样配置 → 在硬件上运行 → 收集性能数据 → 更新模型 → 采样更好配置

这与 MemTrace 优化高度相似:

AutoTVM MemTrace 优化
搜索空间定义 PROMPT_TARGETS 注册
采样配置并运行硬件评测 执行记忆系统 + LLM Judge
收集性能数据 收集归因结果
成本模型预测更好配置 LLM 生成改进反馈
更新模型参数 聚合反馈 + 更新 prompt

类比二:Profile-Guided Optimization (PGO)

编译器 PGO 三步走:插桩编译 → 代表性输入运行 → 收集 profile 数据 → 反馈指导优化。

MemTrace 优化:启用 smartcomment 追踪 → 在 LoCoMo 上运行 → 收集失败执行图 → 归因指导 prompt 更新。

类比三:RL 信用分配

RL 的核心问题:当智能体在 t=10 收到奖励时,t=1~t=9 中哪些动作有贡献?

  • 蒙特卡洛:整条轨迹累积奖励,每个动作等分信用
  • TD(λ):资格迹按时间折扣分配信用
  • MemTrace:执行图精确追踪,故障操作获得全部信用(credit=1),其他操作 credit=0

_format_history 等价于 RL 中的资格迹——旧轮次的反馈以衰减后的形式持续影响当前决策。

类比四:与 TextGrad 的对比

TextGrad 是直接对文本做"反向传播"的框架——用 LLM 计算文本节点对损失函数的"梯度"(自然语言描述),然后沿计算图反向传播更新。

TextGrad 要求整个系统是"纯文本可微"的——所有中间结果必须是文本,所有操作必须是 LLM 调用。MemTrace 的优势在于不需要整个系统可微:smartcomment 录制异构执行图(包含检索、过滤、字符串操作等非 LLM 操作),归因定位故障后只在局部做文本梯度更新——只要归因能找到故障操作,将该操作的输出变量(prompt)视为可优化文本即可。

6.5 当前的边界:为什么只优化了 prompt?

run_optimization.py:53 的 PROMPT_TARGETS 硬编码了三个变量:

PROMPT_TARGETS = {
    "custom_fact_extraction_prompt": "fact-extraction-system-prompt",
    "custom_update_memory_prompt": "memory-update-decision-prompt",
    "question_answering_prompt": "question-answering-prompt",
}

group_attributions(:320)检查每个归因结果的操作子图是否涉及这三个变量——若不涉及,则归入 unoptimizable 列表,静默丢弃。

这是合理但并不充分的"低垂果实"策略:

理由 说明
优化工具现成 LLM 改写文本是最自然的操作
prompt 确实敏感 调好它们就带来 7.62% 的提升
边界是自限而非系统限制 模板的"Only the value is optimizable"是给 LLM 的约束,不是架构约束

0x07 toolkits:连接归因与优化的桥梁

以上三部分解决了"如何录制执行图"和"如何在图上归因"的问题。接下来,归因结果如何被消费?这由 toolkits/ 提供。

7.1 体系概览

_base.py
  ├── _ApiPool(轮询凭证池,多 API key 负载均衡)
  └── AgentBaseConfig / AgentBaseRunner(基础配置)

bench_utils.py
  ├── FailedQueryCase(失败查询的数据模型)
  ├── collect_failed_queries(exec_network) → list[FailedQueryCase]
  └── load_memtracebench() → (graphs, failed_cases)

memtrace_utils.py              ← 归因核心
  ├── ATTRIBUTION_INSTRUCTIONS(Agent 推理指令)
  ├── ErrorAttributionPrediction(归因输出 schema)
  ├── DocumentRetriever(BM25 + 稠密 + RRF)
  └── MemTraceRunner(异步归因运行器)

optimization_utils.py          ← 优化核心
  ├── OptimizerRunner(三阶段文本梯度)
  └── FEEDBACK / AGGREGATION / UPDATE 模板

error_analysis_utils.py        ← 报告核心
  └── ErrorAnalysisReportRunner(迭代式报告)

7.2 归因指令的完整推理范式

ATTRIBUTION_INSTRUCTIONS(memtrace_utils.py:98)定义了 Agent 必须遵循的推理步骤:

第一步:理解操作子图结构

  • 子图中入度 0 的变量 = 操作的输入
  • 出度 0 的变量 = 操作的最终输出
  • 通过操作名、类别、注释、变量值判断操作的功能角色

第二步:对照"理想行为"

  • 提取阶段:关键信息应进入记忆
  • 更新/删除阶段:关键信息应被保留
  • 检索阶段:包含关键信息的记忆应被命中
  • 回答阶段:LLM 应基于检索结果正确回答

第三步:应用"最早决定性故障"准则

  • 局部正确性:操作仅当其自身职责范围内的输出错误时才标记为故障
  • 决定性:修正该操作 + 假设下游完美 → 能挽救失败
  • 最早:按执行顺序第一个满足条件的操作
  • 优先性:RAG 先查检索 → Mem0 先确认提取 → EverMemOS 先验分段质量 → Long-Context 先查上下文裁剪

第四步:映射到五类错误

7.3 混合检索机制

DocumentRetriever(memtrace_utils.py:480)用于当标注的源证据不可用时,从执行图的 category="message" 节点中检索起始证据:

# 构建检索查询 = question + "\n" + golden_answer
# 三种检索模式:
sparse_retriever = BM25Retriever()       # rank_bm25
dense_retriever = EmbeddingRetriever()    # OpenAI-compatible API + QdrantStore
hybrid_retriever = RRFFuser(              # Reciprocal Rank Fusion
    retrievers=[sparse_retriever, dense_retriever],
    k=N//2
)

0x08 扩展讨论:归因与优化的解耦架构

前面我们看到当前的 prompt-only 优化只是"低垂果实"。现在我们来讨论归因与优化在架构层面是天然解耦的——归因定位任意故障操作,优化的本质是"修改该操作涉及的某个可配置变量的值"。这两种能力之间没有硬性耦合。

8.1 为什么"可以优化"?

归因输出是一个具体操作 ID(如 o_42)和错误类型。通过 graph.filter_by_operation("o_42") 得到操作子图,子图中包含了该操作涉及的所有变量——包括可写的配置变量。因此优化就是:

  1. 可定位:filter_by_operation(o*) → 子图 → 包含可优化变量
  2. 可修改:文本→LLM 改写,数值→参数更新,枚举→A/B 切换
  3. 可验证:修改后重跑记忆系统 → 重做归因 → 比较前后错误率

8.2 哪些错误是当前版本"未处理"的?

根据 group_attributions 的逻辑,以下归因结果会被丢进 unoptimizable:

归因类型 实际原因 可能的优化手段
RetrievalError 但检索非 prompt 驱动 Top-k 太小、embedding 模型差、reranker 阈值不对 超参搜索(贝叶斯、网格)
UpdateError 源于 buffer 管理逻辑 Buffer size 过大/过小,合并策略不对 代码策略调整或参数优化
DeletionError 源于删除策略 TTL 太短、删除条件太宽松 配置参数调整
ExtractionError 源于 chunk 策略 Chunk size 不合理、重叠策略不对 超参优化
ResponseError 但检索上下文完备 LLM backbone 能力不足 模型替换或 prompt 优化(后者已覆盖)

8.3 可以扩展到哪些类型的变量?

记忆系统出错的原因多种多样,提示词只是冰山一角。MemTrace 的归因已经能定位到任意环节的问题,优化的扩展只是工程实现问题,而非设计限制。

变量类型 示例 优化策略 前提
文本型 prompt 模板 LLM 文本梯度(已实现) LLM 调用作为优化算子
数值整型 top_k@1, chunk_size@1 贝叶斯优化 / 离散搜索 定义搜索空间 + 评估函数
连续值 temperature@1, similarity_threshold@1 贝叶斯优化 / 网格搜索 同上
枚举型 retrieval_strategy@1 = Bandit / A/B 候选集 + 切换机制
布尔型 enable_reranker@1 消融比较 开关机制
可组合型 多个变量共同导致一个错误 多目标优化 联合搜索空间

8.4 架构上如何扩展?——归因与优化本应是解耦的

MemTrace 的核心贡献在归因(credit assignment),优化是下游消费端。架构上,优化器只需替换为不同策略即可。

当前的 OptimizerRunner 接受 errors: dict[str, list[ErrorAttributionPrediction]],key 是"目标变量在图中对应的 full_node_id"。这个设计是通用的——任何被 smartcomment 录制的变量,只要在执行图中能定位到,都可以作为优化目标。

扩展的思路:

归因输出:op_id="o_42", error_type="RetrievalError"
    ↓  graph.filter_by_operation("o_42") → 操作子图
    ↓  子图中包含哪些变量?
    │
    ├─ fact-extraction-system-prompt (文本) → 文本梯度优化(已有)
    │
    ├─ top_k@3 (整数数值) → 
    │   优化策略:贝叶斯优化 / 网格搜索 / △top-k ± 5 闭式试探
    │   前提:需要定义搜索空间 + 评估指标
    │
    ├─ similarity_threshold@1 (浮点数) →
    │   优化策略:二分搜索 / 贝叶斯优化
    │
    ├─ reranker_model@1 (枚举:cross-encoder vs cohere vs none) →
    │   优化策略:A/B 测试 / 消融
    │
    └─ buffer_max_tokens@2 (整数) →
        优化策略:梯度试探 + 效果回测

要实现这点,只需:

  1. 在 PROMPT_TARGETS 之外定义 PARAM_TARGETS 或其他优化目标注册表
  2. 为不同数据类型实现不同的优化算子(文本→LLM 改写,数值→搜索,枚举→A/B)
  3. 在 group_attributions 中检查所有注册表,而不是只查 PROMPT_TARGETS

核心思路:为每种变量类型注册一个优化算子,group_attributions 检查归因结果中涉及的变量类型,分派给对应算子:

OPTIMIZABLE_VARIABLES = {
    "prompts": {
        "fact-extraction-system-prompt@3": TextGradientOptimizer(),
    },
    "params": {
        "top_k@1": BayesianOptimizer(bounds=(1, 50), discrete=True),
    },
    "configs": {
        "retrieval_strategy@1": BanditOptimizer(
            choices=["sparse", "dense", "hybrid"]
        ),
    },
}

这样,每增加一种优化策略,unoptimizable 列表就缩窄一部分。

8.3 自动化的边界在哪里?

当前已经是自动闭环——不需要人工干预即可完成"采样 → 执行 → 归因 → 优化 → 下一轮"。

1. 采样 trajectory ──────────→ 2. 执行记忆系统 ──→ 3. 收集失败案例
                                                      ↓
6. 更新配置并进入下一轮 ←─── 5. 三阶段优化 ←── 4. MemTrace 归因

但真实的"自动程度"受限于:

  1. 归因准确率:OIA 最高 46.25%——约一半的归因定位错了操作。但只要信号的有用信号占比足够高,就能推动改进(论文 7.62% 提升证实了这点)
  2. 优化空间自推导:当前 PROMPT_TARGETS 是硬编码的。完全自动需要系统扫描执行图发现所有可写变量、自动推断类型、匹配合适优化器——类似 AutoTVM 的搜索空间自动推导
  3. 验证闭环开销:每轮需完整执行构造+检索+评测流水线,计算成本较高。可引入 early stopping(本轮无新失败 → 提前终止)

0x09 一条主线,三个解耦

最后用一个总结收束全文。MemTrace 整个项目围绕一条主线,实现了三个层次的解耦:

一条主线:录制 → 归因 → 优化,形成一个完整的闭环。

三个解耦:

层次 解耦了什么 为什么重要
追踪与分析 smartcomment 录制执行图,graphtrace/obstrace 在图上分析 更换记忆系统只需重新插桩,归因逻辑无需修改
归因与优化 归因只回答"哪里错了",优化决定"怎么修" 归因可以独立改进(更高的 OIA),优化可以扩展(更多的变量类型)
优化策略与变量类型 文本梯度、贝叶斯、Bandit 等都是"优化算子",按变量类型注册即可 每增加一种优化策略,就有一部分 unoptimizable 变为可优化

这种解耦架构使得 MemTrace 虽然当前只优化了三个 prompt 变量,但框架层面已经为扩展到任意变量类型做好了准备——归因已经能定位到任意操作,剩下的只是在优化端注册对应的优化算子。

附录

附录A 入口脚本:各组件如何协作

四个入口脚本展示了上述组件如何被组合调用:

A.1 run_error_attribution.py

load_memtracebench(split)
    → [(ExecNetwork, FailedQueryCase), ...]
    ↓
MemTraceRunner.arun()           # 对每个 case 异步归因
  ├─ DocumentRetriever.retrieve()  # 或使用标注源证据
  ├─ GraphTraceNotebook.initialize_execution_graph()
  ├─ GraphTraceAgent._reasoning()  # ReAct 循环 (≤200 轮)
  │   └─ notebook.explore_graph_trace_node()
  │   └─ notebook.update_to_explore_nodes()
  │   └─ notebook.finish_node_exploration()
  │   └─ notebook.finish_graph_exploration()
  └─ → ErrorAttributionPrediction{error_type, op_id, reason}
    ↓
evaluate() → {ETA, OIA, ...}
output: JSON(per-case + aggregate metrics)

缓存策略(memtrace_utils.py:796):cache_dir / sha256(graph_id + case_id + config).json。避免重复归因。

A.2 run_optimization.py

for iteration in 1..sample_size:
    ├─ load_dataset().sample(1)         # 采一个 LoCoMo trajectory
    ├─ ConstructionRunner.run()          # + smartcomment 追踪
    ├─ SearchRunner.run()                # + smartcomment 追踪
    ├─ EvaluationRunner.run()            # + smartcomment 追踪
    ├─ collect_failed_queries()          # 从执行图中提取失败 QA
    ├─ MemTraceRunner.arun()             # 归因每个失败 case
    ├─ group_attributions()              # 归因 → prompt target 分组
    └─ OptimizerRunner.arun()            # 三阶段优化
         ├─ generate_feedback()          # 每 case → _FeedbackSuggestion
         ├─ aggregate_feedbacks()        # 分组内聚合
         └─ update_target()              # _TargetVariableUpdate
    ↓
    save(): iteration_N/{memory_system_config, feedbacks, summary, ...}

A.3 generate_report.py

load_cases(path)               # JSON: [{query, pred, golden, attribution, graph_path}]
    ↓
ErrorAnalysisReportRunner.arun()
    ├─ 每个 case: graph.filter_by_operation(op_id).to_xml()
    ├─ 分批送入 LLM
    └─ 迭代式报告生成
    ↓
output: error_analysis_report.json

A.4 eval_retrieval_performance.py

评估伪证据检索的 recall@k:

query = question + "\n" + golden_answer
corpus = graph.nodes where category == "message"
retrieval = sparse | dense | hybrid
metric = recall@k (ground truth source evidence in top-k?)

附录B annotation_interface:可视化标注

Streamlit 前端,供人工检查和标注执行图上的错误。核心视图:

  • Timeline:Plotly 蛇形布局的消息流时间线
  • Variable Relation:点击变量节点 → 1-hop BFS 关系子图
  • Error Attribution:自动归因流水线 + 手动标注表单

标注支持七种错误类型(annotation_interface/components/error_attribution_view.py:38):AnnotationError、LLMAsAJudgeError、ExtractionError、UpdateError、DeletionError、RetrievalError、ResponseError。结果追加到 --output-path 指定的 JSON 数组。


附录C 项目文件全景

MemTrace-main/
│
├── smartcomment-main/            ← vendored smartcomment 0.1.2(追踪层)
│   └── smartcomment/
│       ├── api/public.py         — comment_variable/link/mutation/op/fn
│       ├── runtime/              — ExecNetwork / RuntimeGraph / 上下文 / 变量 / 操作
│       ├── schema/               — Pydantic 数据模型
│       ├── drivers/              — GraphDriver 抽象 + InMemoryDriver
│       └── identity/             — IdentityRegistry
│
├── graphtrace/                   ← MemTrace 图归因 Agent
│   ├── graph_trace_agent.py      — GraphTraceAgent(ReActAgent 子类)
│   ├── graph_trace_notebook.py   — GraphTraceNotebook + DefaultGraphTraceToHint
│   ├── models.py                 — GraphTraceNodeRecord + GraphTraceState
│   └── _utils/_helpers.py        — _render_operation_subgraph + _paginate_text
│
├── obstrace/                     ← MemTrace-OBS 消融变体
│   ├── agent.py                  — ObsTraceAttributionAgent
│   ├── trace_notebook.py         — CCTraceNotebook(扁平日志工具)
│   ├── trace_index.py            — TraceIndex(日志解析 + grep 搜索)
│   └── models.py                 — OperationBlock + GrepMatch
│
├── toolkits/                     ← 桥梁层
│   ├── memtrace_utils.py         — ATTRIBUTION_INSTRUCTIONS、DocumentRetriever、MemTraceRunner
│   ├── optimization_utils.py     — 三阶段优化器、PROMPT_TARGETS、梯度历史
│   ├── error_analysis_utils.py   — 迭代式报告生成器
│   ├── bench_utils.py            — MemTraceBench 加载、collect_failed_queries
│   └── _base.py                  — _ApiPool、AgentBaseConfig/BaseRunner
│
├── membase/                      ← 记忆系统框架(被追踪的目标)
│   ├── layers/                   — 8 种记忆系统适配器
│   ├── runners/                  — 构造/检索/评测流水线(含 smartcomment 插桩)
│   ├── baselines/                — vendored 记忆系统源码
│   ├── datasets/                 — 数据集加载器
│   └── evaluation/               — 评测指标(TokenF1, BLEU, LLMJudge...)
│
├── annotation_interface/         ← Streamlit 可视化标注前端
│
├── run_error_attribution.py      — 归因入口
├── run_optimization.py           — 优化入口
├── generate_report.py            — 报告生成入口
├── eval_retrieval_performance.py — 检索评估入口
│
└── my-docs/                      — 本文档

TransFormer-封面

来源:博客园 · cnblogs.com