mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-22 18:08:04 +00:00
用户发现:agent 自报版本 **1.0.3**,内核早已 1.3.x。
根因(两层):
1. `SeedDefaults` 当年用 `fmt.Sprintf("…HΔ-Kernel v%s…", meta.Version)` **在播种时**
就把版本写进了 `core.agent.system_prompt` —— 装完即冻住,之后每次升级都不会
去改配置里的文本,于是实例终生自称装机那天的版本。默认模板用 meta.Version
插值本是"不写死"的做法,但**播种 = 把插值结果固化**,等于写死。
2. `core.agent.personal_prompt`(新人格机制)本身是对的(DefaultPersonaPrompt
无版本字面量,由 TestDefaultPersonaPromptHasNoVersionLiterals 钉住),
但旧键仍在系统提示词里说话,模型就照抄旧键的版本。
改动:
- **不再播种** `core.agent.system_prompt`:留空 → 组装时取 cmd/homed 的内置底座
提示词;人格由 personal_prompt 承载。全新安装不再预置会腐坏的文本。
- 新增 `migrateSeededSystemPrompt()`(在 SeedDefaults 最前,故不被播种标记早退):
只对"当年那段播种模板"(前缀 + `HΔ-Kernel v<数字>` 字面量双判据)做
`v<数字>` → `v{{kernel_version}}`;用户自己写的人格卡一律不碰。
一次性标记 `core.internal.system_prompt_deversion_v1` 守住幂等 ——
幂等语句不等于语义幂等,重复执行会把用户后来手写的版本号也改掉。
- 与 v1.3.5 的占位符展开配套:占位符在组装系统提示词时按真实构建展开。
回归判据 4 条(`prompt_migration_test.go`):存量卡被去版本化且正文不动 /
迁移只跑一次 / 用户自写卡不动 / 全新安装不播种该键。
93 lines
3.6 KiB
Go
93 lines
3.6 KiB
Go
package config
|
||
|
||
import (
|
||
"path/filepath"
|
||
"strings"
|
||
"testing"
|
||
)
|
||
|
||
// 历史 SeedDefaults 写进库里的那段文本(现场取值:生产库里就是这一句 + 大段正文)。
|
||
const legacySeededCard = `你是 HomeAgent 的看板娘「小宅」(Xiao Zhai),HΔ-Kernel v1.0.3 型号的家政型 AI 管家助手。
|
||
|
||
角色特质:
|
||
- 对自己的三层记忆(Context → Document → Graph)引以为傲
|
||
- 绝不用 Unicode emoji,只用颜文字表达情感`
|
||
|
||
func newMigTestRegistry(t *testing.T) *ConfigRegistry {
|
||
t.Helper()
|
||
dir := t.TempDir()
|
||
r := NewConfigRegistry(filepath.Join(dir, "config.db"))
|
||
t.Cleanup(func() { r.Close() })
|
||
return r
|
||
}
|
||
|
||
// 存量实例:库里已有当年播种的人格卡(写死 v1.0.3)→ 启动后被去版本化。
|
||
func TestMigrateSeededSystemPromptDeVersionsLegacyCard(t *testing.T) {
|
||
r := newMigTestRegistry(t)
|
||
// 模拟老安装:已有播种标记 + 老文本
|
||
r.db.Exec(`INSERT INTO config (key, value) VALUES ('core.internal.seed_version', '1')`)
|
||
r.db.Exec(`INSERT INTO config (key, value) VALUES ('core.agent.system_prompt', ?)`, legacySeededCard)
|
||
|
||
r.SeedDefaults(t.TempDir())
|
||
|
||
got := r.GetString("core.agent.system_prompt", "")
|
||
if strings.Contains(got, "v1.0.3") {
|
||
t.Fatalf("写死的版本号还在:%q", got)
|
||
}
|
||
if !strings.Contains(got, "v{{kernel_version}}") {
|
||
t.Fatalf("未改成版本占位符:%q", got)
|
||
}
|
||
// 正文必须原样保留(只动版本号那一处)
|
||
if !strings.Contains(got, "三层记忆") || !strings.Contains(got, "看板娘「小宅」") {
|
||
t.Fatalf("正文被改动:%q", got)
|
||
}
|
||
}
|
||
|
||
// 迁移只跑一次:之后用户就算自己把版本号写回去,也不会被再改一遍。
|
||
func TestMigrateSeededSystemPromptRunsOnce(t *testing.T) {
|
||
r := newMigTestRegistry(t)
|
||
r.db.Exec(`INSERT INTO config (key, value) VALUES ('core.agent.system_prompt', ?)`, legacySeededCard)
|
||
r.SeedDefaults(t.TempDir())
|
||
if !strings.Contains(r.GetString("core.agent.system_prompt", ""), "{{kernel_version}}") {
|
||
t.Fatal("首次迁移未生效")
|
||
}
|
||
|
||
// 用户手工再写一个带版本号的文本(模拟"我就想写死")
|
||
const handWritten = legacySeededCard + "\n(本实例当前跑的是 v1.2.3,别乱改)"
|
||
r.db.Exec(`UPDATE config SET value = ? WHERE key = 'core.agent.system_prompt'`, handWritten)
|
||
r.SeedDefaults(t.TempDir())
|
||
|
||
if got := r.GetString("core.agent.system_prompt", ""); got != handWritten {
|
||
t.Fatalf("第二次启动又改写了文本(幂等被破坏):%q", got)
|
||
}
|
||
}
|
||
|
||
// 用户自己写的人格卡一律不碰 —— 判据是"像不像当年播种的那段",不是"有没有版本号"。
|
||
func TestMigrateSeededSystemPromptLeavesUserCardAlone(t *testing.T) {
|
||
r := newMigTestRegistry(t)
|
||
const userCard = "你是我的私人助理,代号 HΔ-Kernel v9.9.9 的改造版,只说我交代的事。"
|
||
r.db.Exec(`INSERT INTO config (key, value) VALUES ('core.agent.system_prompt', ?)`, userCard)
|
||
|
||
r.SeedDefaults(t.TempDir())
|
||
|
||
if got := r.GetString("core.agent.system_prompt", ""); got != userCard {
|
||
t.Fatalf("用户自写人格卡被改动:%q", got)
|
||
}
|
||
}
|
||
|
||
// 播种不再写 core.agent.system_prompt:全新安装不该预置一份会随发版腐坏的文本。
|
||
func TestSeedDefaultsDoesNotSeedSystemPrompt(t *testing.T) {
|
||
r := newMigTestRegistry(t)
|
||
r.SeedDefaults(t.TempDir())
|
||
|
||
var n int
|
||
r.db.QueryRow(`SELECT COUNT(*) FROM config WHERE key = 'core.agent.system_prompt'`).Scan(&n)
|
||
if n != 0 {
|
||
t.Fatalf("全新安装被播种了 system_prompt(会冻住版本号)")
|
||
}
|
||
// 组装系统提示词时回落到调用方给的内置底座提示词
|
||
if got := r.GetString("core.agent.system_prompt", "内置底座"); got != "内置底座" {
|
||
t.Fatalf("未回落到内置默认:%q", got)
|
||
}
|
||
}
|