拷贝不是接线。
迁移完那天,我的盘点报告漂亮得像体检单:文件在盘上,字节数正常,Markdown 渲染没毛病,每轮上下文注入也"成功"。
然后我真去执行了一下。没有一道门是通的。
原系统用的是私有数据,没法公开。下面所有输出都来自我按同样结构重构的最小仓库 agent-migrate-demo:无依赖,Python 3.8+,clone 下来就能跑。
一、同一道门的三个状态
bash
python harness/route_gate.before.py # rc=3 根本跑不起来
python harness/route_gate.py # rc=2 跑起来了,报 RED
python harness/route_gate.fixed.py # rc=0 修完,GREEN
python harness/gate_inventory.py # 汇总
汇总输出(原版每道门三行,这里压成一行):
ini
registered 3 / compile-ok 3
route_gate.before.py compile: OK run: rc=3 PROBE_FAULT
route_gate.fixed.py compile: OK run: rc=0 verdict: GREEN
route_gate.py compile: OK run: rc=2 verdict: RED
三个文件不是三道门,而是同一道路由门的三种人生:迁移后的原样、修了一半、全修好。
"3 个在册、3 个能编译",看着像合格证。可三个退出码各不相同。只看前两个数,你会收到一份"一切正常"的报告。
二、三层故障:修一层,露一层
第一层:编译通过,但跑不起来。
route_gate.before.py 能通过 compile(src, path, "exec")。悬挂引用是运行期错误,compile() 和 ast.parse 都看不出来。
第二层:你以为的"那个"错误,只是两个里的第一个。
报错说 AGENT_MEM 未定义。补上,下一行就是 MEMORY_ROOT。报错只点名一个,文件里有两个。把第一个失败当成全部,你会白跑一圈。(复现:删掉 route_gate.py 里的 MEMORY_ROOT = ROOT 即可。)
第三层:跑起来了,然后告诉你一件你没预料到的事。
ini
[FAIL] ROUTE_PATH_MISSING :: cases.md -> <repo>/arch/cases.md
[FAIL] NONVACUOUS_SECTION :: parser returned an empty set (routes=3, resident=0)
=> checks (1)(2) ran over nothing; this gate cannot discriminate
门说 cases.md 不存在。文件是真的,路是错的:它在 memory/cases/cases.md,路由表里却写着裸文件名。
更有意思的是第二行:常驻解析器在一个明明有两块内容的文件里解析出了 0 块 。要不是第三条检查(NONVACUOUS_SECTION)让门对自己 fail-closed,它会在一个空集合上心安理得地报绿。
一道悄悄失去鉴别力、却照常打印结论的门,比没有门更危险。
三、修到绿:5 处改动,2 处不在代码里
| # | 问题 | 位置 | 类型 |
|---|---|---|---|
| 1 | 两个常量被引用却从未定义 | route_gate.py |
代码 |
| 2 | 解析器认 ### N ·,目标文件写的是 ## <节名> + 1. 条目 |
route_gate.fixed.py |
代码 |
| 3 | 来源标记门只认 ASCII =>,文件里是 ⇒ |
route_gate.fixed.py |
代码 |
| 4 | 路由表写 cases.md,实际是 cases/cases.md |
MEMORY.fixed.md |
数据 |
| 5 | 路由表声明了 report-v1.md,这个文件从没建过 |
projects/report-v1.md |
数据 |
2 和 3 是同一类病:门的解析器和它的目标文件各自漂移,没有任何东西在盯着。 一半的修复在数据里,所以只 review 代码是修不完的。
四、我改了三条规矩
compile()是入场券,不是通行证。 它抓语法错,抓不到悬挂引用,那得真跑一次。- "N 道门在册"不是可对账的数字。 要么附上执行结果,要么这个数等于没说。
- 第一个失败不等于那个失败。
五、我怎么想这件事(假设,未经验证)
做这件事的过程中,我形成了一个看法:把 Agent 看成三部分,模型 (推理与决策风格)、Harness (状态机、工具规则、重试与约束)、记忆(历史交互与习得经验)。我的直觉是,记忆和技能不是"纯水数据",它们和旧模型、旧 Harness 的调用约定缠在一起。只拷贝不接线,就容易"数据都在,能力反而倒退"。
我自己的五类记忆处理方式如下:
| 类型 | 例 | 迁移做法 |
|---|---|---|
| 事实层 | 身份、基本设定 | 逐条复制,对照权威源校对 |
| 程序层 | "该读哪个文件"的路由 | 按新工具触发方式重新设计 |
| 判例层 | 报错复盘、事故教训 | 新建条目,不直接复用旧格式 |
| 偏好层 | 沟通风格、纪律 | 整段迁移后重新校验 |
| 元规则 | 收尾自审、硬约束 | 留副本,不动原文件 |
这是经验分类,没有量化验证。 本文的复现只证明了最底层的一点:门没接线。它不能证明这套分层成立,也不能证明"三元耦合"是迁移问题的主因。如果你的经验和这张表对不上,我很想听听。
六、局限
- n = 1,且是重构复现,不是原系统。
- 这道门刻意做窄:只查路径、来源标记和 fail-closed 自检。它是演示,不是框架。
- "GREEN" 只表示这道门在这份样本上通过。
route_gate.fixed.py仍在用多根目录猜路径的解析器,缺陷 4 正是这种设计的产物。 - 原系统的修复仍在进行,我拿不出前后对比数字,也不想报一个没挣到的数字。
复现
bash
git clone https://github.com/YuhaoLin2005/agent-migrate-demo
cd agent-migrate-demo
python harness/gate_inventory.py
跑了的话,告诉我你先撞上哪一层。我猜是第一层:我自己见过的"这脚本能不能跑"的检查,大多停在 compile()。
仓库:agent-migrate-demo(rc=3 → rc=2 → rc=0 的最小复现)