mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-06 17:27:31 +00:00
fix(memory,llm): L2 预填充并入召回主路 + 回退换 provider 时换 model(视觉失效真因)
## 1. L2(冷上下文)预填充此前只走「注入文本」,等于没有
设计:prune 与 recall 是一对 —— 工具声明 ContextPolicy=prune + RecallPolicy=auto
时,工具输出既触发裁剪、也触发预填充(memoryPass → recallTextFor)。
但 L2 那一路只写在 buildSystemPrompt 里,query 用的是**注入文本**;
QQ 注入的是「来自「X」的私聊消息(message_id=…),先用 qq_get_message 取正文」,
与用户真正说的话毫无相似度 ⇒ 冷上下文预填充恒为空。
(现场:头天晚上讲的冷笑话,第二天早上完全唤不回;14 小时里
memory recall (input…) 只有 5 次、且 query 是元信息。)
改法:把 L2 检索并入 buildMemoryContext —— 输入路用输入文本、工具路用工具正文,
两条路共用同一次召回与预算;buildSystemPrompt 里那份重复注入删掉。
中断消息仍然 NoMemory + RecallPolicy=none(不裁剪、不召回、不进记忆),
这是设计要求,未改动。
## 2. 回退换 provider 时必须换 model(视觉失效的真因)
现场日志:
deepseek 402 Insufficient Balance
→ 回退 llmsproxy,但 req 仍带 model=「deepseek-flash」
→ llmsproxy 403 model_not_allowed
→ llmsproxy 被整源标记 unavailable(冷却)
→ 视觉回退 resolveModalFallback 跳过它 ⇒「配置了视觉却说没有视觉」
两处修正:
· 每个 provider 用自己的 model 发请求(model 是 provider 的属性)
· 403 区分「模型不被该 key 允许」与「凭证失效」:前者只否一个 model,
provider 仍可用于其他 model 与视觉回退
判据:internal/agent/core/provider_model_fallback_test.go
This commit is contained in:
66
third_party/homeagent-sdk/sdk/events.go
vendored
Normal file
66
third_party/homeagent-sdk/sdk/events.go
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
package sdk
|
||||
|
||||
// 受限事件总线(EventsAPI)。
|
||||
//
|
||||
// ## 为什么现在才敢暴露
|
||||
//
|
||||
// 事件总线是内核内部机制:它连接内核模块、状态面、WebUI 与插件。
|
||||
// 早先不给插件的理由是**没有边界**——一给就是"任何插件能发任何事件、能订阅任何事件",
|
||||
// 等于把内核内部的同步面变成公共 API,且没有任何审查。
|
||||
//
|
||||
// 2026-10 之后内核侧有了 security 模块(凭据闸门 + 内容审查 + 审计),
|
||||
// 于是可以只开**受限的一条缝**:
|
||||
//
|
||||
// · 发布:只能用白名单事件类型,且只能在自己的命名空间下扩展
|
||||
// · 载荷:出向经 security 审查(明文凭据会被换成 @{别名}),并限制大小
|
||||
// · 订阅:只能订阅白名单里的只读事件
|
||||
//
|
||||
// ## 插件能做什么
|
||||
//
|
||||
// Publish("skill_detected", {...}) // 交接类事件(兼容层 → skillmgr)
|
||||
// Publish("plugin.<自己名字>.<任意>", {...}) // 自己的命名空间,供插件间协作
|
||||
// Subscribe("tools_ready", handler) // 等内核就绪再做事的入口
|
||||
//
|
||||
// 越界会**报错**而不是静默丢弃:静默丢弃会让插件以为发成功了。
|
||||
|
||||
// EventsAPI 是插件可见的事件总线。
|
||||
//
|
||||
// 它**不是** *events.Bus 的投影:这是一个有边界、有审查的接口。
|
||||
type EventsAPI interface {
|
||||
// Publish 发布一个事件。
|
||||
//
|
||||
// eventType 必须在白名单内(见 AllowedPublish),或形如
|
||||
// "plugin.<本插件名>.<后缀>"。载荷大小与键数有上限。
|
||||
Publish(eventType string, payload map[string]interface{}) error
|
||||
|
||||
// Subscribe 订阅一个白名单内的事件(只读面)。
|
||||
//
|
||||
// 返回的 cancel 在插件 Stop 时调用可解除订阅;宿主卸载插件时也会自动解除。
|
||||
// handler 的 panic 会被总线隔离,不影响其他订阅者。
|
||||
Subscribe(eventType string, handler func(payload map[string]interface{})) (cancel func(), err error)
|
||||
|
||||
// AllowedPublish 返回当前可发布的事件类型(白名单 + 自己的命名空间前缀)。
|
||||
AllowedPublish() []string
|
||||
|
||||
// AllowedSubscribe 返回当前可订阅的事件类型。
|
||||
AllowedSubscribe() []string
|
||||
}
|
||||
|
||||
// 常用事件名(**只是名字,不是授权表**)。
|
||||
//
|
||||
// 授权表由内核 security 模块持有(`security.EventPolicy`)并可随配置调整;
|
||||
// SDK 侧只声明"这些名字存在",避免插件作者拼错字符串。
|
||||
//
|
||||
// 是否允许你用某个名字,以运行时 `Events().Publish/Subscribe` 的返回为准:
|
||||
// 越界会**报错**,不会静默丢弃。
|
||||
const (
|
||||
// EventNameSkillDetected:兼容层把发现的纯 SKILL 条目交给 skillmgr 接管。
|
||||
// (职责链交接,不是执行指令)
|
||||
EventNameSkillDetected = "skill_detected"
|
||||
// EventNameToolsReady:全部插件加载完、工具注册齐(只读订阅)。
|
||||
EventNameToolsReady = "tools_ready"
|
||||
// EventNameSecurityReady:安全闸门装配完成(只读订阅)。
|
||||
EventNameSecurityReady = "security_ready"
|
||||
// EventNameDeviceReady:工具/设备数据面就绪(只读订阅)。
|
||||
EventNameDeviceReady = "device_ready"
|
||||
)
|
||||
Reference in New Issue
Block a user