Files
HomeAgent/internal/agent/core/child/kinds.go
JianFeeeee 637a3a3d3f refactor(agent): R4 工作式子语义落地 + 清除 R1–R3 遗留的死代码
## 1. 工作式子的工具禁令收成单一事实来源

禁令此前写了两遍:定义侧(把工具从 childTools 滤掉)与执行侧
(模型硬调时拒绝)。两处名单必须逐字一致,任何一边落后就是真实缺口:

  定义侧漏滤 ⇒ 模型看得见但不该能用(白耗 token,还会反复试)
  执行侧漏拒 ⇒ 模型绕过定义直接调用**就能生效**

现在只有 workerForbidden 一张表,每条禁令把"匹配方式 + 类别 + 解释"
绑在一个结构体里;定义侧 workerToolAllowed、执行侧 workerToolReject
都从它派生。

上一版我用 reason 字符串 + 下标猜分类 + hint 查表做了三处间接,
属于新造的技术债,已重写为单表直查。

判据(均经变异验证):
  TestWorkerToolDenyListIsSingleSource  长出第二份名单 ⇒ 红(实测确认)
  TestWorkerToolAllowedMatchesReject    两侧判据漂移 ⇒ 红
  TestWorkerToolRejectExplainsWhy       拒绝文案不可执行 ⇒ 红

## 2. 删除零使用的抽象(不留技术债)

R1–R3 引入了 ChildKind / Host / Deliverer / DeviceResolver / ToolSpec /
ToolResult / DeclaredKinds / KindWorker / State / SetState / BumpRound /
ChannelDisposition,实现过程中发现它们是假抽象:

  · 各类型要用载体 Agent 的大半能力,要满足接口就得在实现里类型断言回 *Agent
  · 接口比被抽象的东西还大 ⇒ 只是把复杂度换个地方放
  · 实测:26 个导出符号里 20 个零使用、零实现

已删除 host.go 与 kind.go,并把"四种类型清单"和"转投驻留子为何不是类型"
这两个有价值的结论移进 kinds.go 的文档注释(知识保留,死代码删除)。

child 包现在只剩实际被用的 9 个符号:Kind / Child / Entry / Registry /
StopDeps / ErrChildExists / ErrChildNotFound(+ 2 个 Kind 常量)。

## 3. 修掉误导性签名

Entry.Dispose(policy ResidualPolicy) 里 policy **两个实现都不读** ——
残余处置实际走 ApplyResidual / FinishResidentWith,在 Dispose 之前就
已经完成。看签名会以为这里处理残余,是误导。已去掉该参数,
并删掉 child 包里重复定义的 ResidualPolicy(core 包已有一份,且多一个 stash)。

## 验证

  go build ./...                                  PASS
  go vet ./...                                    PASS
  gofmt -l                                        干净
  go test ./internal/agent/core/ -count=1         ok 15.300s
  go test ./internal/agent/core/ -count=1 -race   ok 18.040s
  干净检出(git worktree)单独 build + vet + test  PASS
2026-11-04 08:35:23 +08:00

45 lines
2.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package child
// kinds.go 记录**当前全部子 agent 类型**及其归属。
//
// # 为什么这份清单必须以文档形式留在代码里
//
// 用户曾问"其他的哪几个子 agent 类型也要迁移"。这个问题的存在本身说明
// 没有一份唯一清单 —— 只能靠 grep `Spawn*` 反推,而反推会漏:
//
// 工作式子 入口叫 runChildTask,前面没有 Spawn 前缀 ⇒ grep 会漏
// 转投驻留子 入口叫 ensureOffloadResident,同样没有前缀 ⇒ grep 会漏
//
// # 四种类型的实际形态(2026-11-04 实测)
//
// 类型 入口 载体 独立IO 通道 登记表
// 主 agent New() *Agent(完整) 有 有 -
// 驻留子 SpawnResident 完整 *Agent + Start 有 有 children(KindResident)
// rtchild SpawnRTChild 完整 *Agent 不 Start 有 有 children(KindRT)
// 工作式子 runChildTask goroutine + 局部msgs 无 无 ✗(见下)
// 转投驻留子 ensureOffloadResident = 驻留子 有 有 children(KindResident)
//
// # 为什么这里没有 KindWorker 常量
//
// 工作式子**不进** children 登记表,因此不需要类型标签:
//
// · 它没有 io、没有通道、没有子策略 ⇒ 不需要"按通道归属路由"
// · 它的 `childTasks` 是**已完成任务的幂等结果缓存**(child_result 要能
// 重复查询 + 20 条有界淘汰),语义与"存活登记"完全不同
// · 塞进 registry 会让 StopAll 去 Dispose 一个已经跑完的实例,
// 并把结果缓存一起清掉 ⇒ child_result 立刻变成"不存在",
// 模型会无限重试(实测单轮 35 次工具调用、持续 514 秒)
//
// 曾定义过 `KindWorker` 常量,实测零使用,已删除(不留技术债)。
//
// # 为什么转投驻留子不是独立类型
//
// `ensureOffloadResident` 走的就是 `SpawnResident`,差异只有两处,
// 且都已在 ResidentOptions 上表达:
//
// ① OffloadOwned 布尔标记 —— 只影响"能否被自动复用 / 是否计入上限"
// ② TaskPrompt 是分诊职责说明 —— 纯粹是提示词内容,不是机制
//
// 若为它新增类型,那份实现会与驻留子逐字重复(除了一个 bool),
// 而我们在消灭的正是"同一份逻辑写两遍、然后漂移"这个形态。