docs: 全面按当前源码更新文档 + 删除已过时文档

## 删除(内容已落地/已被替换,保留只会误导)
- demo.md ................... failback 与 recoverydiag 均已实现,0 引用
- docs/defect-qq-output-send-loop.md .. 已修复(本身也标了「已修复」),0 引用
- docs/embedding-comparison.md ....... 一次性选型报告,仅被 agent 产物引用
- docs/zh/plan.md ............ 描述的旧 nav 布局已重写、死配置已清,全部完成
- docs/zh/plugin-migration-plan.md ... 迁移已上生产,纯过程稿(Part 0~6 全完成)

## 更新(按当前源码核对)
- assets/docs/{zh,en}/ARCHITECTURE.md(README 指向的用户文档,最重要):
  把只讲 cancel/intercept 的旧「中断机制」章节重写为「输入调度器与中断机制」——
  补上两类别 + 四级中断(L1~L4,默认 L1、外部插件 L4 夹到 L3)+ 抢占/挂起/中断栈
  + 饥饿防护(PreemptCount 提升,封顶 L4)+ 抢占冷却(2s)+ 停止语义(cancelBudget)
  + 驻留子/分诊助手/残余任务;新增「上下文预算」章节(窗口 ≠ 工作面,600K 封顶,
  预算是上限非填充目标)。中英章节数现已对齐(各 13 节)。
- assets/docs/{zh,en}/PLUGIN_DEV.md:插件示例表补 6 个缺失项
  (acp/deepsearch/plugindev/recoverydiag/vanblog/vikunja);qq 工具数 17 → 20(实测)。
- README.md / README_EN.md:补 v1.3.x 线(此前只到 v1.2.0,而 1.3.x 已发布 12 个 patch)——
  驻留式子 agent、输出通道寻址、输入调度器、轻量内核 profile、积压及时反馈。
- plan.md:开头两个「⚠️ 紧急/正在持续污染」是过期告警(实测  残留 = 0),
  改为「已解决」并加文档定位说明;§13 仍是活跃路线图故保留。
- docs/zh/plugin-interface-matrix.md + 两处源码注释:清理指向已删文档的断链。

全仓 md 断链检查:仅剩 1 处,位于 third_party 的 oh_modules(第三方依赖,非本项目)。
This commit is contained in:
JianFeeeee
2026-09-19 19:14:55 +08:00
parent ee3e0da7de
commit 4aed6c445a
15 changed files with 238 additions and 1679 deletions

View File

@ -455,7 +455,74 @@ Extended fields:
- Relation extension: Confidence
## Interrupt Mechanism
## Input Scheduler & Interrupt Mechanism
Inputs do not go straight to the LLM — they first enter the **input scheduler**
(`internal/agent/core/scheduler.go`). Full design:
[`docs/zh/input-scheduler-design.md`](../../../docs/zh/input-scheduler-design.md).
### Two task classes
| Class | Level | Meaning |
|-------|-------|---------|
| `TaskQueued` | none (always 0) | Pending work. Any interrupt (≥ L1) preempts it |
| `TaskInterrupt` | L1–L4 | "How urgent is this", declared by the source via `InjectOptions.Priority` |
### Four interrupt levels
| Level | Meaning | Typical source |
|-------|---------|----------------|
| L1 Background | Fully deferrable | QQ/WeChat messages, bulk notifications |
| L2 Message | General notice | Plugin hints that should be seen soon but aren't urgent |
| L3 Interactive | Needs timely handling | Timer expiry, terminal output, resident-agent reports |
| L4 Critical | **Kernel-exclusive** | panic, kernel events, kernel-level plugin stop button |
When no level is declared it defaults to **L1** — "explicit is a privilege", so a new
plugin never gets preemption rights by accident. L4 declared by an external plugin is
**clamped to L3** (`clampPluginLevel`).
### Preemption and suspension
- **Same level never preempts same level** (`canPreempt` requires strictly greater) —
this is why messages normally wait for the running task to finish.
- A preempted task is pushed onto the **interrupt stack** (LIFO) with its frame saved,
and resumed later; the stack is never re-sorted by priority.
- **Starvation guard**: preemption count raises the effective level
(`effectiveLevel = Level + min(PreemptCount, 2)`, capped at L4).
- **Preemption cooldown**: a just-preempted task cannot be preempted again for
`preemptCooldown` (2s), so a high-priority stream cannot interrupt the same task forever.
- The interrupt stack depth is structurally bounded (chain = queued ← L1 ← L2 ← L3 ← L4).
### Stop (user presses stop / `/stop`)
Stop is not an empty interrupt. It does two things: ① cancel the current LLM inference;
② short-circuit the x messages **already queued at the moment of stop** during their
pre-action phase (`cancelBudget` snapshot), instead of running them as new input.
Inputs arriving **after** the stop are unaffected.
`PendingInputs()` must include the segment still sitting in `io.inputCh` (not yet moved
into the queue by `pumpInbox`) — during a stop the scheduler is usually busy running a
task, and counting only `sched.queue` yields 0.
### Resident sub-agents and timely feedback
Design: [`docs/zh/resident-subagent-design.md`](../../../docs/zh/resident-subagent-design.md).
- A **resident** is an independent lightweight-kernel agent: its own scheduler, its own
temp graph memory, sharing the channel registry.
- Parent→child control plane: `resident_agents`
(list / create / send / inspect / compress / reclaim / destroy).
- **Backlog feedback**: when the main agent is busy for a long time (default > 5m,
configurable), the kernel hands queued inputs to a temporary **triage assistant**
(`offload_*` config): simple ones are handled directly, ones needing the main agent
get an immediate "busy, please wait". Users no longer wait 10+ minutes in silence.
- The triage assistant gets **no inputch** (it receives no plugin user input) and
**all output channels** (results must reach the original channel).
- On reclaim/destroy, its **residual tasks are decided explicitly by the parent**:
`residual=keep` (returned to the parent queue, default) or `drop` (explicitly
discarded with a per-item log entry).
### Legacy three-path view (still present, now a layer beneath the scheduler)
```
interceptLoop (goroutine)
@ -465,15 +532,28 @@ interceptLoop (goroutine)
└── (c) InjectInput() → Trigger new processing when idle
```
Three delivery paths:
Code: `internal/agent/core/scheduler.go` (scheduler), `eventloop.go` (intercept loop).
| Path | Effect | Timing |
|------|--------|--------|
| cancelLLM | Cancel current HTTP request | On context.Canceled |
| interceptCh | Insert `[interrupt message]` in process() | Before each LLM call |
| InjectInput | Trigger new processing when eventLoop is idle | No ongoing request |
## Context Budget
Code: `internal/agent/core/eventloop.go` — `interceptLoop` / `drainInterrupts`
`internal/agent/core/tokenbudget.go` — `ComputeTokenBudget`:
```
maxCtx = provider.MaxContextTokens() // declared window (per-source context_window wins)
targetUsage = min(maxCtx × 0.8, 600000) // working band, capped at 600K
├── memory recall budget = (targetUsage - fixed) / 3
└── context events budget = remaining 2/3
```
★ **Window ≠ working band**: a source's real window may reach 1M, but near-full windows
lose attention and cost/latency rise linearly, so `maxTargetTokens=600000` caps the
working band separately. If the model name (e.g. `AUTO`) yields no window,
`ModelContextWindow` **logs a warning** and falls back conservatively; operators should
declare `core.llm.sources.<name>.context_window` explicitly.
**Budgets are ceilings, not fill targets**: memory is recall-ranked (it stops when nothing
is relevant) and the timeline is taken newest-first within budget. Measured: with a 400K
budget, actual injection was still a few hundred characters.
## Configuration System

View File

@ -804,7 +804,7 @@ Internal: records are stored in SQLite `disabled_plugins` table (`name`, `disabl
|---------|------|----------|
| [weather](https://gitcode.com/JianFeeeee/homeagent-sdk/tree/main/example/weather) | Go | Weather queries (wttr.in); demonstrates NoMemory/Cleaner/stage hooks/channels/text memory |
| [luademo](https://gitcode.com/JianFeeeee/homeagent-sdk/tree/main/example/luademo) | Lua | Full-featured Lua example covering the whole v0.8.0 Lua SDK surface |
| [qq](https://gitcode.com/JianFeeeee/homeagent-sdk/tree/main/example/qq) | Go | NapCat OneBot integration, 17 tools, full input/output channel wiring |
| [qq](https://gitcode.com/JianFeeeee/homeagent-sdk/tree/main/example/qq) | Go | NapCat OneBot integration, 20 tools, full input/output channel wiring |
| [memo](https://gitcode.com/JianFeeeee/homeagent-sdk/tree/main/example/memo) | Go | Memo management, PreAction injection + timed interrupt dual reminder |
| [files](https://gitcode.com/JianFeeeee/homeagent-sdk/tree/main/example/files) | Go | File system operations, 4 write modes, sandbox isolation |
| [browser](https://gitcode.com/JianFeeeee/homeagent-sdk/tree/main/example/browser) | Go | Web search + HTTP fetch (SSRF) + Chromium render (merged from web/webfetch) |
@ -817,6 +817,12 @@ Internal: records are stored in SQLite `disabled_plugins` table (`name`, `disabl
| [rss](https://gitcode.com/JianFeeeee/homeagent-sdk/tree/main/example/rss) | Go | RSS subscriptions |
| [ai_image](https://gitcode.com/JianFeeeee/homeagent-sdk/tree/main/example/ai_image) | Go | AI image generation |
| [music](https://gitcode.com/JianFeeeee/homeagent-sdk/tree/main/example/music) | Go | Music playback |
| [vikunja](https://gitcode.com/JianFeeeee/homeagent-sdk/tree/main/example/vikunja) | Go | Vikunja task management (projects/tasks/labels CRUD) |
| [vanblog](https://gitcode.com/JianFeeeee/homeagent-sdk/tree/main/example/vanblog) | Go | VanBlog publishing and management |
| [deepsearch](https://gitcode.com/JianFeeeee/homeagent-sdk/tree/main/example/deepsearch) | Go | Multi-round deep search (progressive focus + cited summary) |
| [acp](https://gitcode.com/JianFeeeee/homeagent-sdk/tree/main/example/acp) | Go | Agent Client Protocol (external editors/IDEs drive this agent) |
| [recoverydiag](https://gitcode.com/JianFeeeee/homeagent-sdk/tree/main/example/recoverydiag) | Go | Five-part fault diagnosis (triage / sqlite check / log signatures / diff / ranked conclusions); core plugin of failback mode |
| [plugindev](https://gitcode.com/JianFeeeee/homeagent-sdk/tree/main/example/plugindev) | Go | Plugin scaffolding: generate, build, install — lets the agent develop plugins itself |
### Built-in Plugins