fix(寻址): flatten 的候选**逐条**标注 —— 第一版把 §C 噪声放进了新端点
## 缺口(部署后实测才发现,是我自己引入的)
第一版 flatten 只在响应的 `paths[]` 数组里标注。实测:
222 条候选,其中 37 条(16%)落在桥内部目录(/root/.pi/mail-sessions/<uuid>)
而标注在**另一个数组** —— 模型必须自己把 candidates 与 paths 对照才认得出
那正是「§C 噪声淹没信号」换个位置复活。我在动手前的判断是「先修 C 再修 A,
否则新端点会把噪声一起放大」—— 做了 A,却让 C 的噪声原样跟进了 A。
只在真机跑过 `flatten=1` 才看见:单测全绿(它们只断言了 paths[] 有标注),
是生产数据的 16% 把它翻出来的。
## 修法
`AddressedCandidate` 逐候选带 `path_kind` / `path_note` / `is_absolute_path`,
MCP 渲染逐条打 `⚠`。
marker 收敛到 repo 层一份,handler 的 `classifyPath` 改为委托调用:
同一目录在 path 列表里标成「工作区」、在候选列表里却没标 ——
而那两个数组是**同一次调用**返回的。两处各写一份 marker 时,
改一处忘另一处就会出现这种自相矛盾,且没有任何报错。
## 判据(2 格)
TestFlattenAnnotatesEachCandidate 桥内部目录/相对路径能分类 + 带说明;
真工作区不得被误标(否则全是噪声)
TestClassifyPathAgreesWithRepo handler 与 repo 口径必须逐条一致
## 顺带
第一版 flatten 本身已验证有效(生产实测):
flatten=1 → 222 条候选、66 个工作区
/home/program/agentmail 125 条 · /root 16 条 · root 2 条
⇒ root 与 /root **同时可见**且各自带 path,不再需要「先猜 path 再枚举」
path 标注:66 条候选里 35 条桥内部目录 + 1 条相对路径被标出
This commit is contained in:
@ -250,8 +250,17 @@ func summarizeSuggest(payload map[string]any) string {
|
||||
if numberOf(m["unread"]) > 0 {
|
||||
flag = fmt.Sprintf(" ★%d 未读", int(numberOf(m["unread"])))
|
||||
}
|
||||
fmt.Fprintf(&b, "· %s\n 工作区:%s\n 标题:%s%s\n",
|
||||
str(m, "address"), str(m, "path"),
|
||||
// ★ 逐条把「这个工作区不该选」标出来(实测 222 条候选里 37 条
|
||||
// 落在桥内部目录)。标注只在 paths[] 里的话,模型得自己把两个
|
||||
// 数组对照才认得出 —— 那就是 §C 噪声换个位置复活。
|
||||
warn := ""
|
||||
if k := str(m, "path_kind"); k == "bridge-internal" {
|
||||
warn = " ⚠ 桥内部目录,不是项目工作区"
|
||||
} else if note := str(m, "path_note"); note != "" {
|
||||
warn = " ⚠ " + note
|
||||
}
|
||||
fmt.Fprintf(&b, "· %s\n 工作区:%s%s\n 标题:%s%s\n",
|
||||
str(m, "address"), str(m, "path"), warn,
|
||||
firstNonEmpty(str(m, "title"), "(无标题)"), flag)
|
||||
}
|
||||
if paths, ok := payload["paths"].([]any); ok && len(paths) > 0 {
|
||||
|
||||
Reference in New Issue
Block a user