feat(channel): N1a —— inputch 一等化(归属插件/归属 agent/容量/共享登记表)+ 单工具多视图总览

设计:docs/zh/resident-subagent-design.md §4.6(里程碑 N1a)。

## 用户要求

「父 agent 可以看到所有已注册的 inputch 以及 inputch 的划分情况,用**单工具多视图**方式构筑」

## 登记层(internal/agent/io/inputch.go)

inputch 是**最基本的输入路由单位**(由插件注册,一个插件可注册多个),
所以登记表以 inputch 为键,每条记录:

    名字 / 归属插件 / 归属 agent(被划给谁) / 容量 / 默认回程 outputch / 记忆策略(ChannelDef)

- 新增 `ChannelRegistry`,设计成**可共享对象**(`*ChannelRegistry`):
  根 agent 与驻留子共用同一份,"划入/授权"才有意义;`SetChannelRegistry` 注入。
- **插件重载不得抹掉划分**:重复登记只更新「归属插件 + 策略」,
  保留已有 Owner/Capacity/Output(否则一次 reload 就把父做的划分清空)。
- `Assign` 语义按设计 R6 默认:**读写授权,不转移所有权**(Plugin 与 Owner 分别记录)。
- 原 `inputChannels map[string]ChannelDef` 被登记表取代;`GetInputChannelDef` 保持兼容。
- `plugin.Registry` 注册时带上**归属插件名**(此前完全无归属信息)。

## 总览工具(internal/agent/core/inputch.go)

单工具 **`input_channels`** + `view` 参数(不是一堆小工具):

    all(默认)= 全部已注册(带归属插件)
    mine       = 划给本 agent 的
    unassigned = 尚未划出的
    by_agent   = 划分情况总览(按归属分组)
    detail     = 单个 inputch 全字段(需 name)

未知 view **报错并列出可用值**(拼错不得被静默当成默认视图);登记表为空时明确说明。

## 验收

- `internal/agent/io/inputch_test.go`:归属记录、重载保划分、Assign/视图数据面、
  **跨 manager 共享登记表**、策略查询向后兼容 —— 5 项
- `internal/agent/core/inputch_test.go`:单工具多视图逐视图断言(含未知 view 与空表)—— 2 项
- 全仓 `go test ./...` 37 包 ok / 0 FAIL;`-race ./internal/agent/... ./internal/plugin/...` 干净
This commit is contained in:
JianFeeeee
2026-09-13 09:06:50 +08:00
parent f2ec46480e
commit 3c961b7bd7
10 changed files with 787 additions and 27 deletions

View File

@ -125,6 +125,28 @@
- **输出通道 → 目标 agent 的 inputch** 的解析;
- **授权过滤**`output_list_channels` 只列该 agent 被授权的通道。
### 4.6 inputch 总览:**单工具多视图** [已定]
父 agent 必须能看清两件事:**有哪些 inputch 已注册(谁注册的)**、**它们是怎么划分的**。
按用户要求构筑为**单工具多视图**(一个工具 + 一个 `view` 参数),而不是一堆小工具 ——
视图切换比工具增殖更好用,也更省提示词预算。
工具:**`input_channels`**
| view | 内容 |
|---|---|
| `all`(默认) | 全部已注册 inputch名字 / **归属插件** / 归属 agent / 容量 / 记忆策略标记 |
| `mine` | 划给**本 agent** 的 |
| `unassigned` | **尚未划出**的(可按需分配) |
| `by_agent` | **划分情况总览**:按归属 agent 分组列出各自拥有哪些 inputch |
| `detail`(需 `name` | 单个 inputch 的全字段(注册插件 / 归属 / 容量 / 默认回程 / 记忆策略) |
- 未知 `view` **必须报错并列出可用值**(拼错不得被静默当成默认视图)。
- 登记表是**可共享对象**`*ChannelRegistry`):根 agent 与它的驻留子共用同一份,
这样"划入/授权"才有意义(默认每个 agent 自带一份,向后兼容)。
- **插件重载不得抹掉划分**:重复登记只更新「归属插件 + 策略」,
保留已有的 Owner / Capacity / Output。
---
## 5. 记忆模型:两级空间 [已定]
@ -375,6 +397,8 @@
| S16 | 父退出清理 | 父 `Stop()`(多个子、有子在工作中) | 全部子被销毁;登记表空;**无孤儿 goroutine / 无悬空通道 / 无残留 temp** |
| S17 | L4 独占 | 子内部(子自己的输入/工具/定时器)试图产生 L4 | 被夹到 **≤L3**;只有父的消息是 L4 |
| S21 | 一个插件多个 inputch 可分别路由 | 同一插件注册 2 个 inputch分别划给父与子后各投一条输入 | 各自只到被划给的 agent互不串台 |
| S22 | inputch 总览(单工具多视图) | 一个插件注册 2 个 inputch、另一插件 1 个;把其中若干划给本 agent | `view=all` 列出全部(带归属插件);`mine`/`unassigned` 各自正确;`by_agent` 给出划分总览;`detail` 给出单条全字段;未知 view 报错并列出可用值 |
| S23 | 插件重载不抹划分 | 先划分 inputch再重复登记模拟插件重载 | Owner/Capacity 保留,仅策略被更新 |
| S19 | 父消息必能打断子 | 子在长任务中LLM 流式段)时父发送消息 | 子按 L4 被打断;若子在不可抢占临界区,则在安全点生效 |
| S20 | 子的内核级事件上报 | 子 panic / 子 contextfull | 在**父的阶梯上以 L4带子标识**出现;子侧不自己产生 L4 |
| S18 | 内核不持有"当前通道" | 抢占/中断后被打断任务恢复并发响应 | 提示词与事件标签都**只来自输入事件**(不再有被覆盖的字段) |
@ -388,6 +412,7 @@
| **R1** | 驻留子的内核形态 | **[已定]独立轻量内核**(自己的调度器/中断栈/上下文/记忆作用域) |
| **R2** | 插件与工具 | **[已定]父授权,默认完整授权**(可收窄) |
| **R3** | 记忆模型 | **[已定]两级空间**:读 tempmain写 temp |
| **R13** | inputch 总览的形态 | **[已定]单工具多视图**`input_channels` + `view` |
| **R11** | 父消息的级别 | **[已定]对子 = L4**(子的阶梯上唯一 L4 来源;子内部一律 ≤L3 |
| **R12** | 子的内核级事件panic / contextfull上报级别 | **[默认/推论]在父的阶梯上以 L4带子标识上报** |
| **R4** | 父→子消息落地 | **[默认]挂起/恢复**(现场不丢);一处开关可改"直接丢弃" |
@ -405,7 +430,8 @@
| 里程碑 | 内容 | 验收 |
|---|---|---|
| **N0** | **无状态化**:删 `Agent.currentOutputChannel`、删提示词里的通道预设 | S18既有全部测试通过这是纯收敛不含新能力 |
| **N1** | **通道层**inputch 一等化(归属 / 容量 / 授权 / 目标解析)+ 输出通道授权过滤 | S1S3 |
| **N1a** | **通道登记层**inputch 一等化(归属插件 / 归属 agent / 容量 / 共享登记表)+ **单工具多视图总览** | S21S23 |
| **N1b** | 输出通道授权过滤 + 目标解析outputch → 目标 agent 的 inputch | S1S3 |
| **N2** | **轻量内核 + 记忆作用域**:记忆子系统加 space 维度;`AgentConfig` 加作用域参数(写 temp、读 tempmain | S13S15 |
| **N3** | **驻留子生命周期**:创建 / 销毁 / 登记表 / 父退出清理 | S12、S16 |
| **N4** | **跨 agent 投递**:子→父 L3、父→子 L4+ `isKernelLevelSource` 分层) | S4、S6、S17 |