mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-06 17:27:31 +00:00
fix(kernel): 补上统一挂载点的生产接线 —— 上一版功能实际未生效
## 这个 commit 修的是我自己上一版的破绽
上一轮我报告"63 包全绿"。今天用户问「确定都可以正常工作」,
一查发现:**生产路径上根本没人调 kstate.Mount()**。
$ grep -rn 'kstate.Mount(' --include='*.go' internal/ cmd/ | grep -v _test
(空)
后果链:
kstate.Get() 永远返回 nil
⇒ agent/core 的 renderSystemPrompt 走 `if ks != nil` 跳过分支
⇒ 插件声明的系统提示词段**一条都不渲染**
⇒ 模型看不到任何 skill 索引,只能凭工具名猜用法
⇒ 正是本次重构要修的那个生产故障(拿 terminal_create 当 cmd_run)
而且**日志里一个字都没有** —— 静默跳过,不是报错。
## 为什么测试全绿
因为测试自己调了 `MountForTest`:
restore := kstate.MountForTest(&kstate.State{PromptSections: reg})
t.Cleanup(restore)
判据把缺失的那根线补上了,所以永远绿。这正是本仓反复吃过的亏
(engineering-lessons §1.1)。
## 一共两个断点
① kernelboot.Run 没调 kstate.Mount(ks)
② sdk.SetSystemPromptAPI 生产上没人调
⇒ 插件的 s.SystemPrompt() 返回 nil
⇒ skillmgr.publishIndex() 对 nil 是静默 return(它不能 panic)
## 修复
① kernelboot.Run 装入挂载点(唯一入口)
② 补齐三段接线:
plugin.Registry 加 promptSections 字段 + SetPromptSections
kernelmod/pluginhost 从 ks.PromptSections 注入进去
internal/sdk 收到后调 SetSystemPromptAPI(promptSectionAdapter)
## 新增判据:**不自己 Mount**,只读 Run 之后的状态
TestKstateIsMountedByRun 源码判据(那行必须在)
TestPromptSectionsWiredToPlugins 三段接线逐个核对
TestSharedDataInjectionsHaveProducers 通用:注入点必须有生产者
TestRunActuallyMountsState ★ 端到端
最后一条是关键:它先 `MountForTest(nil)` **清空**挂载点,再跑真实
`kernelboot.Run` + 真实模块登记表,然后从底层包视角(kstate.Get())
检查能不能看到东西。它不能靠"测试自己挂"蒙混过关。
变异验证(实测):
· 删掉 kstate.Mount(ks) ⇒ 源码判据红 + 端到端判据红
「kernelboot.Run 跑完了,但 kstate.Get() 仍是 nil」
## 验证
go build ./... PASS
go vet ./... PASS
gofmt -l 干净
go test ./... -count=1 63 包全过,0 FAIL
干净检出(git worktree) build + 端到端判据全绿
六环人工核对 ①Mount ②Run ③产出 ④注入 ⑤sdk ⑥skillmgr 各 1 处
## 教训
"全绿"只在**判据不自己补线**时才是证据。凡是"被测方需要某个前置
装配、而判据自己提供了它"的判据,都必须再配一条"从零开始"的
端到端版本。
This commit is contained in:
@ -1,6 +1,8 @@
|
||||
package sdk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/JianFeeeee/HologramAgent/internal/promptsec"
|
||||
"github.com/JianFeeeee/HologramAgent/internal/security"
|
||||
"log"
|
||||
"sync"
|
||||
@ -228,6 +230,37 @@ func (s *PluginSDK) Knowledge() KnowledgeAPI { return s.know }
|
||||
// LLM 返回大模型源。媒体字节请走 Memory().MediaBytes(ref)(CAS 已淘汰)。
|
||||
func (s *PluginSDK) LLM() LLMAPI { return s.llm }
|
||||
|
||||
// promptSectionAdapter 把内核的 promptsec.Registry 适配成公共 SDK 的
|
||||
// SystemPromptAPI。
|
||||
//
|
||||
// ★ 为什么需要适配层:SDK 是对外契约(外部插件照它编译),不能 import
|
||||
// 内核内部包;而 Registry 要能被内核其它部分直接读。两边共享的只有
|
||||
// "段"这个数据形状。
|
||||
type promptSectionAdapter struct{ reg *promptsec.Registry }
|
||||
|
||||
func (a promptSectionAdapter) DeclarePromptSection(s pubsdk.PromptSection) error {
|
||||
if a.reg == nil {
|
||||
// 未装配时**报错而不是静默丢弃**:静默丢的后果是"插件以为声明
|
||||
// 成功了,模型却看不到",与不声明无法区分。
|
||||
return fmt.Errorf("system prompt registry 未装配")
|
||||
}
|
||||
return a.reg.Declare(promptsec.Section{
|
||||
Section: s.Section,
|
||||
Owner: s.Owner,
|
||||
Order: s.Order,
|
||||
Content: s.Content,
|
||||
Volatile: s.Volatile,
|
||||
})
|
||||
}
|
||||
|
||||
func (a promptSectionAdapter) RemovePromptSection(section string) error {
|
||||
if a.reg == nil {
|
||||
return fmt.Errorf("system prompt registry 未装配")
|
||||
}
|
||||
a.reg.Remove(section)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ioAdapter 桥接 IOManager 到公共 SDK 的 IOInjector 接口,
|
||||
// 确保外部插件通过 s.InjectText() 等方法的调用能被路由到内核 IO 层。
|
||||
type ioAdapter struct{ iom *agentIO.IOManager }
|
||||
@ -348,13 +381,18 @@ type SDKConfig struct {
|
||||
// "单例装的时机"与"插件加载的时机"隐式耦合 —— 装晚了插件就用默认
|
||||
// 最小策略,而日志里一个字都没有。
|
||||
SecurityRuntime *security.Runtime
|
||||
Memory MemoryAPI
|
||||
TextMemory TextMemoryAPI
|
||||
DocMemory DocMemoryAPI
|
||||
Knowledge KnowledgeAPI
|
||||
LLM LLMAPI
|
||||
Settings SettingsAPI
|
||||
Secrets pubsdk.SecretsAPI
|
||||
// PromptSections 是插件声明式系统提示词段的注册表(内核级共享)。
|
||||
//
|
||||
// sdk 不能 import kstate(kstate 依赖 plugin → 依赖 sdk,成环),
|
||||
// 所以由 plugin.Registry 从 ks.PromptSections 取出来传进来。
|
||||
PromptSections *promptsec.Registry
|
||||
Memory MemoryAPI
|
||||
TextMemory TextMemoryAPI
|
||||
DocMemory DocMemoryAPI
|
||||
Knowledge KnowledgeAPI
|
||||
LLM LLMAPI
|
||||
Settings SettingsAPI
|
||||
Secrets pubsdk.SecretsAPI
|
||||
// Security 是**策略声明面**(插件声明安全档位;判定由内核执行)。
|
||||
//
|
||||
// ★ 只声明、不判定:本接口没有 CheckTool 之类的方法 —— 那属于内核。
|
||||
@ -387,6 +425,12 @@ func New(name string, cfg SDKConfig) *PluginSDK {
|
||||
if cfg.Security != nil {
|
||||
base.SetSecurityAPI(cfg.Security)
|
||||
}
|
||||
// ★ 系统提示词声明面(2026-11-04)。不接这一行,插件的
|
||||
// DeclarePromptSection 会拿到 nil API ⇒ skillmgr 的索引**静默不入**
|
||||
// 系统提示词(测试全绿因为测试自己 Mount 了挂载点,生产是坏的)。
|
||||
if cfg.PromptSections != nil {
|
||||
base.SetSystemPromptAPI(promptSectionAdapter{reg: cfg.PromptSections})
|
||||
}
|
||||
if cfg.RawFiles != nil {
|
||||
base.SetRawFilesAPI(cfg.RawFiles)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user