mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-06 01:07:35 +00:00
docs: 内核性能实测基线 + payload 透传/保留键设计
## docs/zh/kernel-performance.md(新增) 全部是实测数据,不是估算,每条标出测量方法与复现命令。 冷启动 15.4s(5 层 17 模块 + 22 插件),耗时分布: 第 0→1 层 3.1s 20% 内存/ONNX/文档存储初始化 第 1→4 层 2.4s 16% LLM/知识库/agent 装配 第 4→完成 9.9s 64% 22 个插件子进程建链 ★ 与早期记录(1538–1709ms)的差异要说明:那是**纯内核**装配时间, 不含插件。两者口径不同,混用会得出"启动变慢 10 倍"的错误结论。 其余指标:RSS 26MB / 插件进程 12 个(13 个按需加载)/ 三层工具超时 (30s ctx → max(声明,参数) → 10m 硬上限)/ 上下文预算实测值 / `offload_busy_after` 1m。 含三个实测踩过的性能陷阱:长工具执行期间 stop 无法生效、 同阶段重复注册静默覆盖、before_output 双触发路径。 ## kernel-modularization-design.md 新增 2.7 节 注入 payload 的透传机制与保留键硬约束。 保留键必须覆盖两类:注入路径自己写入的(no_memory/context_policy/…) 与内核读取的语义键(interrupt/stop/command/…)。插件写保留键会被 跳过并记日志 —— 允许覆盖等于允许伪造"这条不记记忆"或"这条立即打断", 那是权限提升。 同时记录 output_send__* 的 stage 语义:before_output/after_output 有两条触发路径,output.go 那条才是"消息真发出去"的时刻,而它历史上 连 Extra 都没初始化(实测 extra_keys=0)。 ## 设计过程中的一次返工 曾加 InjectOptions.Meta,等于把同一件事做两遍且只服务注入路径。 用户指出后改为复用 InputEvent.Payload 的扩展口,所有注入路径共用。
This commit is contained in:
@ -921,6 +921,78 @@ func (d *Distiller) distillLoop() {
|
||||
- `TestDeclarationsTraceToRealCallSites`:声明与**签名**不漂移
|
||||
- §2.8.2(A) 的 onnx→textmem 顺序:声明与**运行期读取**不漂移
|
||||
|
||||
## 2.7 注入 payload 的透传与保留键(2026-10-18)
|
||||
|
||||
### 背景
|
||||
|
||||
通道插件在**注入**时要能让"这一轮的身份"跟着走到后续阶段。但:
|
||||
|
||||
```
|
||||
on_input 的 stageCtx 是局部变量(stageCtxFromInput),runStage 完就丢
|
||||
before_output 的 stageCtx 是内核新建的(emitResponse / output.go)
|
||||
⇒ 两者不共享 Extra
|
||||
```
|
||||
|
||||
插件于是只能读全局状态猜"这轮是谁引起的"——并发多个会话时会**串号**
|
||||
(A 的指示打到 B 身上)。
|
||||
|
||||
### 机制
|
||||
|
||||
`InjectOptions.Payload` 是插件自带的不透明字段,内核不解释:
|
||||
|
||||
```text
|
||||
注入点 opts.Payload
|
||||
→ 与真 payload 合并(保留键跳过)
|
||||
→ emitResponse / output_send 把**非保留键**原样写进 stageCtx.Extra
|
||||
```
|
||||
|
||||
`InputEvent.Payload` 本就是通道送入内核的请求体,业务字段本该在那里;
|
||||
缺的只是一个**扩展口**(`IOInjector` 的方法签名是
|
||||
`(source, channel, text, opts)`,插件无法直接给 payload 加字段)。
|
||||
|
||||
> 设计过程中曾误加 `InjectOptions.Meta` —— 那等于把同一件事做两遍,
|
||||
> 且只服务注入路径。改名为 `Payload` 后 HTTP/CLI/ACP 共用同一套机制。
|
||||
|
||||
### 保留键(硬约束)
|
||||
|
||||
`reservedPayloadKeys` 必须覆盖两类键:
|
||||
|
||||
1. **注入路径自己写入的**:`no_memory` / `context_policy` /
|
||||
`recall_policy` / `scene_policy` / `cleaner_name` / `priority` /
|
||||
`type` / `content`
|
||||
2. **内核读取的语义键**:`interrupt` / `interrupt_source` /
|
||||
`interrupt_channel` / `stop` / `command` / `scene` / `source` /
|
||||
`channel` / `media_blocks` / `media_type`
|
||||
|
||||
插件写了保留键会被**跳过并记日志**,不是拒绝整次注入。
|
||||
|
||||
**为什么必须拦**:允许覆盖等于允许插件伪造「这条不记记忆」或
|
||||
「这条要立即打断」——那是权限提升,不是便利。
|
||||
|
||||
**为什么是跳过而不是报错**:插件可能只想带一个业务字段,因顺手多写一个
|
||||
保留键就让整条注入失败,代价太大。
|
||||
|
||||
⚠️ **新增内核语义键时必须同步这个集合**,否则新键就是新的提权面
|
||||
(判据 `TestReservedKeysCoverInjectionOwnKeys` 覆盖这一点)。
|
||||
|
||||
### `output_send__*` 的 stage 语义
|
||||
|
||||
`before_output` / `after_output` 都有**两条触发路径**:
|
||||
|
||||
| 来源 | Extra | 含义 |
|
||||
|---|---|---|
|
||||
| `emitResponse` | `input_source` + payload 透传字段 | 一轮的回复(turn 边界) |
|
||||
| `output.go` | `output_channel` / `output_meta` / `output_type` | 单次发送 |
|
||||
|
||||
`output.go` 的路径**才是"消息真的发出去"的时刻**,但历史上它构造 stageCtx 时
|
||||
连 `Extra` 都没初始化(nil),插件在那个时点读不到任何上下文
|
||||
(实测 `extra_keys=0`)。
|
||||
|
||||
现在补齐三个键,并补上缺失的 `after_output`(`Execute` 成功后触发;
|
||||
失败时不触发 —— 消息没送达,"发出后收尾"的语义不成立)。
|
||||
|
||||
插件用 `_, has := ctx.Extra["output_meta"]` 区分两条路径。
|
||||
|
||||
## 3. 兼容性约束(★ 硬约束)
|
||||
|
||||
1. **存量插件的 `plugin.json` 不变必须照常工作**
|
||||
|
||||
Reference in New Issue
Block a user