门户首页
专题 05 · Agent 轨迹建模(reflection 工具链)

把任意 Agent 轨迹变成统一模型

投喂一份任意 Code Agent 的落盘轨迹文件——先认出它是哪个 Agent,再用该 Agent 自己的规则把它回译成一个统一的元模型实例,最后诚实判档。这一页把这条链拆开讲透,并附一个可以直接点的三态演示。

生成时间:2026-10-04 · 生成 Agent:ZCode · 载体:agentsoft-research-platform teaching-web-platform · 版本 v0.2

概览

19
认得出的 Agent 源
13
统一元模型 EClass
4
诚实态(ok/partial/misrouted/rejected)
25
随仓演示语料(真实件)
项目说明
这是什么一条「任意轨迹文件 → 认源 → 回译成 M0 实例 → 合模检查 → 四态诚实判档 → 网页呈现」的完整工具链;本平台以 vendor 方式随仓收录(ref-work-modules/agent-trajectory-modeling/)
解决什么问题19 种 Agent 各有各的落盘格式(jsonl / json / sqlite / markdown / .traj),做轨迹研究先要「认清它 + 统一它」——认错了源的模型是结构完整但语义全错的产物,比不产出更有害
怎么跑bash scripts/start_tmr.sh start(本机一键起服务,自动探空口 8767–8778);CLI 同源:python -m trajectory_model_reflection inspect <轨迹文件>
与讲义的关系讲义层的 trajectory 系列(如何读轨迹)回答「轨迹怎么读、怎么评」;本专题回答「轨迹怎么被形式化地统一成模型」——一个管分析,一个管建模
阅读路线:先读第一、二篇建立「这条链在干什么」的全景 → 第三、四篇是两条工程主线(认源怎么不出错、判档怎么不说谎)→ 第五篇动手点演示 → 最后两篇看设计原则。全程不要求读者访问任何外部仓库。

第一篇 · 这条链在干什么

第一篇 · 工具链全景(五步流水)
一份轨迹文件进来,一张五步流水把它变成「可判档、可下钻、可复现」的统一模型。
flowchart LR A["轨迹文件
jsonl · json · sqlite · md · traj"] --> B["① 认源 matcher
19 源注册表 · 多信号打分"] B -->|"阈值 0.55 且 裕度 0.20"| C["命中源 + 面"] B -->|"不达标"| X["拒答
拒派不冒充"] C --> D["② 回译 mapper / importer
该源既有规则 → M0 实例"] D --> E["③ 闸 D 合模
check_conformance"] E --> F["④ 四态诚实判档
ok · partial · misrouted · rejected"] F --> G["⑤ 网页呈现
M1 × M0 × 判档着色"] style X stroke-dasharray: 4 3
图 1 · 工具链五步流水:认源不达标就拒答(虚线出口),绝不猜一个源硬转
工程原则 · 只调度,不生产规则

本工具自己不写任何一条转换规则

全部规则真源在各源的「源元模型 + 对照件 + 适配器 + 规则包」里,本工具链只做调度:认出源之后,转调该源的既有规则把它回译成 M0。每个匹配信号都要标注派生来源(来自哪份元模型、哪个适配器词表),并有机器可核的登记——漏标即测试红、漂移即测试红。

三条不可协商的原则
  • 只调度不生产——不复制映射、不新建第二套词表,防止与规则真源漂移
  • 匹配失败明说——不中就显式报「未识别」并列出候选得分,绝不硬猜
  • 输出可复现——同文件 + 同规则版本 = 同结果;匹配依据、规则版本、源指纹随产物同档
关联:讲义·软件过程分析四讲
架构细节 · 依赖环消解

两个互相需要的模块,如何不成环

这里有一个教科书级的工程决策:认源引擎要被「规则真源包」反向调用(自动认源收口),而认源引擎又要调用规则真源——直接互相 import 就是运行即崩的循环依赖。解法是把认源引擎切成零依赖叶子:只用标准库、不 import 项目内任何包,包入口保持空壳。于是规则真源可以安全地单向引用它,环就断了。

判断标准很简单:「谁被谁 import」必须能画成一张无环图。做不到时,先切出一个谁都不依赖的叶子,再让两边分别引用它——而不是用动态 import 把环藏起来。

第二篇 · 元建模视角:19 种落盘,1 个模型

第二篇 · M0 × M1 的活例子
这是元建模三层抽象(实例—模型—元模型)最好的实战教材:M0 是每份轨迹回译出的实例,M1 是那套 13 类的统一元模型。
核心知识点

从「各家各自落盘」到「跨 Agent 共性层」

19 种 Agent 的落盘形态千差万别:有的是 SQLite 库(单数三表 / 复数两表 / 独有看板表各不相同),有的是逐行事件流,有的是缩进式单文档 JSON,甚至还有 Markdown 对话录。元模型做的事,是在这之上抽出一层所有 Agent 共有的结构——会话(Session)、轨迹(Trajectory)、轮次(Turn)、步骤(Step)、消息(Message)、片段(Part),以及压缩记录、上下文纪元、子代理链、分析报告。任何一份轨迹回译之后,都长在同一棵容纳树上。

  • M0(实例模型):某一份具体轨迹回译出的对象树——「这条轨迹里有 1 个 Trajectory、9 条 Message、9 个 Part」
  • M1(元模型):定义 M0 必须长什么样的那套类图——13 个 EClass、8 条容纳边、20 条不变量
  • 合模(闸 D):每个 M0 产出后都要过一遍 M1 校验——不合规即显形为 ERROR,不静默
对接:MDE 高级讲义·元建模和建模 对接:专题 03 · UML 架构建模
classDiagram class Session { +id : string +lifecycle } class Agent { +id +origin +model } class Tool { +name +inputSchema } class Trajectory { +id +format +turnEndReason } class Turn { +id +steeringSignal } class Step { +id +kind +source } class Message { +id +role +seq } class Part { +partType +content +callId } class CompactionRecord { +semantics +medium +trigger } class ContextEpoch { +baseline +baselineSeq } class SubagentLink { +kind +granularity } class AnalysisReport { +id +kind } class AnalysisRecord { +kind +label +evidenceGrade } Trajectory *-- Turn : turns Trajectory *-- Step : steps Trajectory *-- Message : messages Trajectory *-- CompactionRecord : compactionRecords Trajectory *-- ContextEpoch : contextEpochs Trajectory *-- SubagentLink : subagentLinks Message *-- Part : parts AnalysisReport *-- AnalysisRecord : records Session o-- Trajectory : trajectories Agent o-- Trajectory : owned Tool ..> Part : inputSchema
图 2 · 统一元模型 M1(13 EClass):实心菱形 = 容纳(Trajectory 是枢纽,挂 6 类),空心菱形 = 共享引用;与真实元模型文件逐边对应(容纳边 8 条全量)
注意「合法缺席」也是信息:不是每个源都有全部 13 类。比如 mini-SWE-agent 的轨迹没有独立的 Session/Agent/Tool 对象——判档会如实标「缺席」,而不是硬造一个空对象充数。这一点第四篇展开。

第三篇 · 认源工程:怎么认,怎么不出错

第三篇 · 多信号打分 + 阈值 + 裕度 + 拒答
认源是整条链的第一道闸,也是最容易「自信地错」的地方——这里的每条机制都是从真实误配事故里长出来的。
核心知识点

注册表驱动的多信号打分

每个源登记 2–5 个信号探针,权重分强(3)/中(2)/弱(1):得分 = 命中信号权重和 ÷ 总权重。判定要同时过两关:top1 ≥ 0.55(阈值),且 top1 − top2 ≥ 0.20(裕度)——分够高但拉不开差距,同样拒答。几个源的真实信号长这样:

源(面)强信号(权重 3)中 / 弱信号消歧要点
zcode(sqlite 面)SQLite magic 字节单数三表 message/part/session(2);路径语境(1)另一源 OpenCode 独有表 session_context_epoch 出现即一票否决(排他指纹)
dsh(事件流)首行信封 type=session 且带 createdAt.jsonl 后缀(1);快照路径语境(1)与 pi 源信封同形——真区分锚是时间戳字段名(dsh 恒 createdAt,pi 恒 timestamp),双向消歧
qwen(chats 流)provenance 三值作者轴键qwen 路径语境(1)通用消息树坐标(uuid/parentUuid)与 Claude Code 同形,不能当锚——键级深检后放弃该弱信号
  • 排他指纹(veto):「唯此源有」的特征命中即否决该源——防跨源误配的一票否决权
  • 参考分不入账:规则未显式化的面只记参考分供诊断,不参与命中判定
  • 人工兜底仍走全链:确知来源可 --source 覆写,但转换与判档照跑,页面显著标 FORCED
事故驱动的设计

为什么宁可拒答,不可错认

把 OpenCode 的库认成 zcode、把 Claude Code 的会话派给 qwen——这些不是假想的:它们都真实发生过,且每一次错配都产出过「结构完全合法、语义整体错误」的 M0。错误在最上游(认源),影响却贯通到最下游(模型、判档、统计),事后极难发现。所以这套机制的默认姿势是往回退:分数不够、拉不开、特征未显式化——一律拒答,给出候选得分和下一步建议,把判断权交还给人。

教学要点:这是「fail-safe vs fail-silent」的活案例——编排层的本能是把所有分支都兜住往下传(看起来更健壮),但错误的产出比没有产出更危险,因为下游无法分辨它错了。

第四篇 · 诚实四态与判档四档

第四篇 · 两个正交的「说真话」维度
四态回答「这次转换整体成没成」,四档回答「模型里每个概念是实证的还是缺席的」——一层管作业,一层管内容。
核心知识点 · 作业层

四态:一次投喂的诚实结论

状态判据(要点)一句人话
● ok 可解认到源、退出码真 0、闸 D 无 ERROR、有内容对象全链干净,结果可以直接用
▲ partial 半解出件了但有任一不干净:退出码非 0 / 闸 D ERROR / 有损档能用但要带着伤情读——伤在哪,理由栏直说
✗ misrouted 误派认到了源,但事件 ≥25、映射率 <5% 且内容对象为 0「认对了名字、翻错了内容」——不据此下任何结论
○ rejected 拒解未识别 / 非可回译档 / 该面无 runner / 出件无内容这道题我不会——拒派不冒充

四态判据的两条铁律都来自真实事故:不采信编排层吞掉的退出码(旧调度层只看合模结果、把回译子进程的非零退出吞成了「成功」);不用分数边际当判据(0.55 的高分照样错派过——误派要看内容对象落地没有,不看自信程度)。

核心知识点 · 内容层

四档:模型里每个概念的成色

回译出的 M0 里,每个概念相对源数据都有一档成色判定,四色着色、颜色永远与符号和文字同出(色觉可达性——只靠颜色传达信息是被禁止的):

档含义典型来源
● 实证 ok源数据里有直接证据消息正文、工具名等直接映射
◐ 推断 derivable源数据可无歧义推出由序号推轮次、由结构推父子
▲ 有损 degraded有对应物但丢了信息截断的正文、丢失的元数据
○ 缺席 unavailable源里就没有这个概念mini-SWE-agent 无独立 Session 对象——合法缺席也是缺口,如实标注

逐类还聚合出「最劣档」着色(一个类只要有一条有损,整类标有损)——但按类独立聚合,不是全局最劣发给每个类:那是反向误导。

对接:讲义·过程分析与 Linting

第五篇 · 动手点:三态演示

第五篇 · 静态交互演示(数据来自真实作业)
下面三个样例都是工具真实跑出的作业结果(仅截短了本机路径)。点样例切换,看诚实条、M1 树与 M0 树联动——本页零后端零网络请求,所有数据内联。
交互 · 三态各一点

投递区:选一份轨迹

左树 = 元模型 M1(点类名联动右树高亮,角标为该类判档)· 右树 = 实例 M0(脱敏,正文已锚化)

元模型 M1(容纳链)
实例 M0(沿容纳链下钻)

想投自己的轨迹文件?本机起服务:bash scripts/start_tmr.sh start,浏览器打开后把文件拖进投递区即可;CLI 同源:python -m trajectory_model_reflection inspect <文件>。随仓还带了 18 个源的真实代表性样例(认源 25/25 复验通过)。

第六篇 · 隐私即架构 + 本机复现

第六篇 · 五件套与复现入口
隐私在这条链里不是事后补丁,而是 PRD 级红线——五个机制都是结构性的,不是配置项。
核心知识点 · 隐私五件套

结构性隐私,不靠自觉

  • 回环绑定:服务只听 127.0.0.1,公网物理不可达
  • 路径白名单:只能读白名单根内的文件,家目录整体不在白名单——越界一律 403
  • 键面扫描:拒解时展示的「源结构树」只读键名 / 类型 / 计数,正文一律不碰
  • 产物 TTL:含轨迹正文的作业目录 48 小时自动清理 + 数量上限,不过夜积累
  • 出口脱敏:实例出口有白名单脱敏机——正文键无条件锚化成 ⟨长度·指纹⟩(本页 M0 树就是脱敏产物)

本页嵌入的三份作业数据同样过了一遍:result 结构性不含正文(有测试断言钉住),实例树走脱敏出口,绝对路径已截短。

课堂实训

本机复现这条链

  • 起服务:bash scripts/start_tmr.sh start(自动探空口 8767–8778 并开浏览器)
  • 健康检查(防连到本机同端口的别的服务):curl …/api/health 应答 app = trajectory_model_reflection
  • 三态各投一次:mini-SWE-agent 样例 → 可解;dsh 压缩恢复样例 → 半解;随手造一个陌生 JSON → 拒解
  • 只认源不转换:python -m trajectory_model_reflection match <文件>,看候选得分与拒答诊断
思考与讨论
  • 如果把阈值从 0.55 降到 0.40,拒答率和误配率会怎么变?有没有「两全」的取值?
  • 「合法缺席也是缺口」——你觉得缺席档应该算模型的缺陷还是源的特性?为什么它必须显式存在?
  • 编排层吞掉子进程的非零退出码,为什么比「转换失败」更危险?你在哪些系统里见过同型问题?
工具真源:ref-work-modules/agent-trajectory-modeling