把任意 Agent 轨迹变成统一模型
投喂一份任意 Code Agent 的落盘轨迹文件——先认出它是哪个 Agent,再用该 Agent 自己的规则把它回译成一个统一的元模型实例,最后诚实判档。这一页把这条链拆开讲透,并附一个可以直接点的三态演示。
概览
| 项目 | 说明 |
|---|---|
| 这是什么 | 一条「任意轨迹文件 → 认源 → 回译成 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 系列(如何读轨迹)回答「轨迹怎么读、怎么评」;本专题回答「轨迹怎么被形式化地统一成模型」——一个管分析,一个管建模 |
第一篇 · 这条链在干什么
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
本工具自己不写任何一条转换规则
全部规则真源在各源的「源元模型 + 对照件 + 适配器 + 规则包」里,本工具链只做调度:认出源之后,转调该源的既有规则把它回译成 M0。每个匹配信号都要标注派生来源(来自哪份元模型、哪个适配器词表),并有机器可核的登记——漏标即测试红、漂移即测试红。
- 只调度不生产——不复制映射、不新建第二套词表,防止与规则真源漂移
- 匹配失败明说——不中就显式报「未识别」并列出候选得分,绝不硬猜
- 输出可复现——同文件 + 同规则版本 = 同结果;匹配依据、规则版本、源指纹随产物同档
两个互相需要的模块,如何不成环
这里有一个教科书级的工程决策:认源引擎要被「规则真源包」反向调用(自动认源收口),而认源引擎又要调用规则真源——直接互相 import 就是运行即崩的循环依赖。解法是把认源引擎切成零依赖叶子:只用标准库、不 import 项目内任何包,包入口保持空壳。于是规则真源可以安全地单向引用它,环就断了。
第二篇 · 元建模视角:19 种落盘,1 个模型
从「各家各自落盘」到「跨 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,不静默
第三篇 · 认源工程:怎么认,怎么不出错
注册表驱动的多信号打分
每个源登记 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。错误在最上游(认源),影响却贯通到最下游(模型、判档、统计),事后极难发现。所以这套机制的默认姿势是往回退:分数不够、拉不开、特征未显式化——一律拒答,给出候选得分和下一步建议,把判断权交还给人。
第四篇 · 诚实四态与判档四档
四态:一次投喂的诚实结论
| 状态 | 判据(要点) | 一句人话 |
|---|---|---|
| ● 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 对象——合法缺席也是缺口,如实标注 |
逐类还聚合出「最劣档」着色(一个类只要有一条有损,整类标有损)——但按类独立聚合,不是全局最劣发给每个类:那是反向误导。
第五篇 · 动手点:三态演示
投递区:选一份轨迹
左树 = 元模型 M1(点类名联动右树高亮,角标为该类判档)· 右树 = 实例 M0(脱敏,正文已锚化)
第六篇 · 隐私即架构 + 本机复现
结构性隐私,不靠自觉
- 回环绑定:服务只听 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,拒答率和误配率会怎么变?有没有「两全」的取值?
- 「合法缺席也是缺口」——你觉得缺席档应该算模型的缺陷还是源的特性?为什么它必须显式存在?
- 编排层吞掉子进程的非零退出码,为什么比「转换失败」更危险?你在哪些系统里见过同型问题?