refactor(prompt): 提示词去工具化 + WebUI 插件状态按运行期真相展示

## 1. 系统提示词/人格瘦身(工具用法搬去 tooldef 与 skill)

用户指出:默认系统提示词与 personal 里有大量本该属于 skill / 工具描述的内容。
核对属实,且已造成**真实故障**:提示词教模型用 cmd_run,而 cmd 外置按需后
工具根本不在清单里 ⇒ 模型第一轮调用不存在的工具,拿到「工具 cmd_run 不存在」。

改动:
- kernelmod/agent.defaultSystemPrompt:删掉 output_* 用法、工具分类清单、
  cmd_run/terminal/files 策略、并行策略、媒体工具清单、emoji 风格要求。
  只留:身份 + 「工具描述与技能索引会注入,先读再用」+「只依据工具返回值作答」。
- config.DefaultPersonaPrompt:只留身份/称呼/语气 + 版本以运行时快照为准
  (后者由 TestDefaultPersonaPromptHasNoVersionLiterals 钉住)。
- 命令与文件策略下沉到工具描述:cmd_run 补「完整 bash,支持管道/heredoc/重定向」,
  files_write 补「原子替换 + 覆盖受保护路径前留档」。
- 对话时序块自带语义说明(原先靠提示词解释,提示词一精简就丢语义)。
- 文档新增「提示词分层」章节(中英),写明归属判据与这次的反面教材。

## 2. WebUI:插件「启动状态」按运行期真相展示

旧实现只看内核 status 的 loaded 一个布尔,三种形态被混成同一个「未加载」:
auto_start=false 的按需待命(正常)、该起没起(异常)、plugin_stop 后的运行态停止。
用户看到一屏「未加载」以为插件全坏了。

- sdk.PluginRuntimeInfo 新增 AutoStart / RuntimeStopped;registry 从清单填充。
- pluginmgr /plugins 列表暴露 auto_start / runtime_stopped / builtin / tool_count。
- webui 新增 POST /plugins/<name>/start|stop(运行态,不动持久禁用清单)。
- dashboard 状态列改为五态:已禁用 / 进程丢失 / 已运行 / 按需待命 / 未运行,
  并额外标注「自启 / 按需」;按钮改为 启动/停止(运行态)与 禁用/启用(持久态)分列。
- 新增两条回归判据(引用 auto_start/runtime_stopped/alive 字段;startPlugin 走 /start)。

## 验收(本机实例实测)

- files 保持 auto_start=false(按需);问 agent 写文件 → 它先 plugin_start cmd+files,
  再 files_write/files_read,内容校验一致;未用 shell 代替文件操作。
- WebUI 插件页截图确认:a2a/acp/agentcli 显示「已运行 自启」,
  ai_image 显示「按需待命 按需」+「启动」按钮。
- go test(config/agent core/plugin/pluginmgr/webui/sdk)全绿。
This commit is contained in:
root
2026-10-20 08:57:36 +08:00
parent 2aab90c508
commit b5f024b054
13 changed files with 332 additions and 71 deletions

View File

@ -627,6 +627,37 @@ is relevant) and the timeline is taken newest-first within budget. Measured: wit
budget, actual injection was still a few hundred characters.
## Prompt layering (established 2026-10-20)
The system prompt is **not** the place for tool usage instructions. Decide where a
piece of text belongs by its lifetime and visibility:
| Content | Belongs in | Why |
|---|---|---|
| Identity, how to address the user, tone, appearance | Persona (`personal/personal.md` or `core.agent.personal_prompt`) | Lives and dies with the persona; stable across plugins and channels |
| One tool: when to use, parameter meaning, side effects, pitfalls | The tool `description` | **Travels with the tool**: if the plugin is absent or not started, the description is not in the list, so the model cannot misuse it |
| Multi-step procedures, strategies, domain knowledge | Skills (`skill/<name>/SKILL.md`) | Progressive disclosure: the index is always present, the body is fetched on demand via `skill_info` |
| Output-delivery rules, same-round parallelism, timeline semantics | Kernel runtime blocks (`buildSystemPrompt` / timeline header) | Generated from **actual** execution semantics; static copies drift from the implementation |
**Anti-pattern (measured 2026-10-20)**: the default system prompt carried
"cmd_run runs through a full bash…", "prefer files_write for writing files…",
"spawn_child in parallel…". After those plugins were externalized and made
on-demand, `cmd_run` was no longer in the tool list while the prompt still told
the model to use it — the first turn called a nonexistent tool and only then
learned "tool cmd_run not registered".
**Rule of thumb**: a concrete tool name plus usage instructions in the prompt is
almost always misplaced. The only exceptions are (1) tool names **injected at
runtime** (e.g. `output_send__{channel}`, whose channel set changes per turn) and
(2) statements about **kernel behaviour** rather than tool behaviour (e.g.
"multiple calls in one round run in parallel by default").
Likewise, a plugin's skill must ship **in the same package**: an
`auto_start=false` plugin without a skill has its tools absent from the initial
list, so the model never knows it exists and never starts it — equivalent to not
being installed. hmapdev enforces this at packaging time
(`autostart_skill_guard_test.go`).
## Configuration System
`internal/config/registry.go` — ConfigRegistry

View File

@ -596,6 +596,31 @@ targetUsage = min(maxCtx × 0.8, 600000) // 工作面:封顶 600K
实测:预算 400K 时实际注入仍只有几百字符。
## 提示词分层(2026-10-20 定型)
系统提示词**不是**放工具用法的位置。判断一段话该放哪里,看它的**生命周期与可见性**:
| 内容 | 归属 | 为什么 |
|---|---|---|
| 身份、对用户的称呼、语气风格、形象 | 人格(`personal/personal.md` 或 `core.agent.personal_prompt`) | 与人设同生共死,跨插件、跨通道稳定 |
| 单工具:何时用、参数含义、副作用、坑 | 工具 `description` | **跟着工具走**:插件没装/没启动,描述就不在清单里,模型不会误用 |
| 多步流程、策略、领域知识(如「怎么查 QQ 历史」「怎么改文件」) | skill(`skill/<name>/SKILL.md`) | 渐进披露:索引常驻、全文按需 `skill_info` 取,不占常驻预算 |
| 输出投递规则、同轮并行语义、对话时序语义 | 内核运行时块(`buildSystemPrompt` / 时间线头) | 由内核按**真实**执行语义生成;写成静态文本会与实现漂移 |
**反面教材(实测 2026-10-20)**:默认系统提示词里写了一大段
「cmd_run 经完整 bash 执行…」「写文件优先 files_write…」「并行优先 spawn_child…」。
这些插件外置并按需加载后,`cmd_run` 根本不在工具清单里,而提示词还在教模型用它 ——
模型第一轮就调用不存在的工具,拿到
「工具 cmd_run 不存在或未注册」后才发现。
**判据**:提示词里凡是出现**具体工具名 + 用法**,几乎都是放错了位置。
例外只有两类:① 工具名是**运行时注入**的(如 `output_send__{通道名}`,通道集合每轮变);
② 那句话讲的是**内核行为**而不是工具行为(如「同轮调用默认并行」)。
同理,插件的 skill 必须与插件**同包分发**:`auto_start=false` 的插件若没有 skill,
它的工具不在初始清单、模型不知道它存在、也永远不会拉起它 —— 等价于没装。
hmapdev 在打包期强制校验这条(`autostart_skill_guard_test.go`)。
## 配置系统
`internal/config/registry.go` — ConfigRegistry