--- name: kernel-rtchild description: Realtime child for live calls; inputs interrupt, output is text only. version: 1.0.0 --- # 实时子 agent(rtchild) rtchild 是**第三种**子 agent,只为一件事存在:**通话**。 它不是"更快的驻留子",也不是"带语音的子任务"——如果场景不是 "用户正在说话、你必须立刻接",就不要用它。 ## 1. 三个子 agent 的分界 | 类型 | 工具 | 生命周期 | 适用 | | --- | --- | --- | --- | | 一次性子任务 | `spawn_child` | 一轮内跑完就没 | 单一明确的小活 | | 驻留子 | `resident_agents` | 长期存在,有自己的调度器/记忆 | 长期派驻、持续盯一条线 | | **rtchild** | `rt_children` | 一次通话 | **实时对话**,输入随时打断 | 选错的代价不是"慢一点":把通话场景做成驻留子,用户每句话都要排队等前一轮 跑完,主观感受是"这个助手反应特别慢";把长期任务做成 rtchild,它会被 每一句新输入反复打断,永远做不完。 ## 2. 通道必须成对,且两侧都要声明 rt ```text inputch(rt) ← 采集层喂进来的话 outputch(rt) → 你说的文本(内核只出文本,TTS 由插件做) ``` - **必须成对**:只给一侧会被拒(两侧都会校验 `Realtime`)。 - **两侧都要 `Realtime: true`**:普通通道也能划给 rtchild,但**承载不了 rt 流**。 这条以前被跨进程白名单丢过——插件声明了、内核收到 false、报错却指向 "创建 rtchild 失败",与真因隔两层。 - 划进去必须**验一次**:投一条输入后看 `list` 里轮次是否 +1。没动就是没划对。 ## 3. 打断是默认语义,不是例外 往 rt 输入通道投递**就是打断**:当前轮的 LLM 流被取消,新输入立刻开新一轮。 不排队、不重排、不重放残留。 **唯一不被打断的窗口是工具执行期。** 工具跑到一半被取消会留下半完成的 副作用(文件写一半、命令跑到一半),所以那里挂起取消、等它跑完。 ⇒ 由此得出一条必须遵守的规矩: > **调工具之前,先用 outputch 说一句"稍等"。** > > 模型一旦进入工具执行期就听不见用户的新输入了。不说这句,用户会觉得 > "它没在听我说话"——而实际上它只是卡在一个 3 秒的命令里。 ## 4. 输出只有文本 `outputch(rt)` 收到的永远是文本。**内核不做音频合成**: - TTS / 字幕 / 播放 → 插件自己的事 - 不要指望内核返回音频,也不要为了"发语音"去挂 `audio` 能力位 ## 5. 上下文:窗口裁剪,但原文不丢 rtchild 用的是**普通上下文 + 窗口**(没有 rt 专用的分页/锚点那一套): - 旧内容**不再进模型**(否则窗口撑爆,且早年的寒暄会污染当前对话) - 旧内容**原文仍在**(不删,只是不进视图) - 裁剪**从不切开 tool call 与它的结果**(切开会留下无主的 tool result) ## 6. 回收:原文交给你,取舍由你定 `rt_children action="stop"` 会把这场通话**作为一条 L4 中断**交给你, 并告诉你共多少条消息。 ★ **不要**直接把整段塞进上下文——那正是窗口爆掉的原因。 用 `rt_transcript(id, offset, limit)` **分段**取,只留你真正需要的。 如果 `stop` 回的是"本次通话没有上下文需要交接",那是**真的没有** (该子还没产生任何消息),此时不该再调 `rt_transcript`。 ## 7. 闲置零 token 没有输入时不跑任何 LLM 调用。所以通话结束**必须 stop**—— 挂着的 rtchild 虽然不烧 token,但它占着 rt 通道、占着登记表, 而且你迟早会忘了它。 ## 8. 回收判据(出现任一条,当轮就 stop) 1. **通话结束**:用户挂了/说了再见/话题收口 → stop,并按第 6 条取原文。 2. **通道没了**:插件卸载、对应通道注销 → stop,否则是个收不到活的僵尸。 3. **建错了**(输入通道填成输出通道名等)→ 立刻 stop 重建, 别留着调试——它会把本该给别人的输入吃掉。 ## 9. 常见错误 | 现象 | 真因 | | --- | --- | | create 报"通道不能承载 rt 流" | 该通道没声明 `Realtime`,或用了只注册了一侧的通道 | | create 成功但 list 里轮次永远 0 | inputch 名填错了(常见:把 outputch 名当 inputch 用) | | 用户说"它不听我说话" | 工具执行期不可打断,且模型没先说"稍等" | | 期望收到音频却只有文本 | 内核只出文本,TTS 是插件的事 | | stop 后拿不到原文 | 顺着提示用 `rt_transcript` 分段取,不要期待整段自动进上下文 |