mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-01 14:57:38 +00:00
## ① 全面改名 HomeAgent → HologramAgent
仓库已改名,但代码与文档里仍有大量旧名。本次按"用户能看到的优先"全量替换:
- 用户可见:登录页标题/页脚、默认人格第一句、CLI 状态输出、启动日志、
plugin_install 的工具描述、AI 图片插件的 User-Agent
- 文档与脚本:README、assets/docs、docs/zh、deploy/*.sh、.github/workflows
- 鸿蒙工程:cmd/ohos/HomeAgent → cmd/ohos/HologramAgent(git mv)
目录内的 module/app 描述同步;**证书路径原样保留**
(default_HomeAgent_xxx.cer/p7b/p12 是签名材料的实际文件名,改了会找不到证书)
- 插件元数据:plugin.json 的 author
★ 必须保持原样的两处(改了会坏):
· legacySeededPromptSignature = "你是 HomeAgent 的看板娘「小宅」…"
—— 那是识别历史播种人格卡的**原文锚点**,改了就识别不出旧数据
· 公开 SDK 的 ProxyAuthHomeAgent = "homeagent"
—— 那是 SDK 契约里的值,属独立仓库,本仓不改
## ② 修 21 处乱码(U+FFFD)
这些是历史编辑留下的编码破坏,全在注释里,例如:
「赌注重下���遍」→「赌注重下一遍」
「「不��滤」」 →「「不过滤」」
「**待��退场**」→「**待退场**」
★ 保留 providers/chineseclip/tokenizer.go 里的 2 处:那是**故意**用
U+FFFD 演示"非法 UTF-8 字节被归一成 U+FFFD",不是乱码。
## ③ 提示词技术内容下沉(用户:技术性内容应该收敛到 skill 与 -help 中)
实测系统提示词 2309 字,其中两段几乎全是**模型无法据以行动的实现细节**:
【工具执行顺序】1272 字(ParallelSafe 声明、批内降级、batchRunnable 判据…)
【三层记忆】 1069 字(L1/L2/L3 编号、消费式删除、向量空间、指纹…)
收敛后 **2309 → 1723 字(-25%)**,保留的全是"模型据此做什么不同的事":
工具顺序:默认并行 / 不要依赖先后 / 同通道保序 / 写类工具可能串行
三层记忆:时序里能看最近 / doc_query 把历史事件按原始时间戳插回时序 /
关系用 memory_recall / 成文知识用 knowledge_search
判断口径写进判据:新加一段提示词前先问"模型会据此做什么不同的事",
答不上来就该进 skill 或工具描述。
## 判据
prompt_memory_test.go 新增 2 条:
- TestSystemPromptHasNoImplementationJargon
黑名单:ParallelSafe / batchRunnable / 消费式 / DenseCosine / fingerprint /
向量空间 / 批内降级 / 自激
- TestSystemPromptStaysCompact:上限 2000 字(当前 1723)
变异:把 "ParallelSafe 声明、批内降级" 加回工具顺序段 ⇒ 第一条立刻红。
## 验证
- go build ./... / go vet ./... 干净
- go test ./... -count=1:58 包 0 失败
- make check-client-versions:客户端与内核版本一致
318 lines
13 KiB
Go
318 lines
13 KiB
Go
package packaging_test
|
||
|
||
// 发行打包脚本的**结构性判据**(2026-10-17)。
|
||
//
|
||
// ## 为什么必须有这组判据
|
||
//
|
||
// 这轮打包实测撞到 **4 个「包打出来了、装上去不能用」**的缺陷,
|
||
// 而且它们**全部**满足「构建日志全绿 + 脚本退出码 0」:
|
||
//
|
||
// 1. `package-linux.sh` **完全不拷 `depgraph.bin`**
|
||
// ⇒ 内核启动校验失败:`生产构建(内嵌 hash 非空)但读不到依赖图`
|
||
// ⇒ **退出码 1,内核根本起不来**(实测确认)。
|
||
// 2. `deploy/packaging/build.sh` 的 `build_homed` **不生成图、不注入 digest**
|
||
// ⇒ 与 (1) 同因,另一条独立构建路径。
|
||
// 3. `build.sh` 的 `LDFLAGS` 用**历史模块路径 `gitcode.com/`**
|
||
// ⇒ `-X` 静默失效(Go 对未知包路径不报错)⇒ 发行包 `--version`
|
||
// 全是 `unknown`(实测:`waiter 1.4.0 (commit unknown, built unknown)`)。
|
||
// 4. (Makefile 侧)`-ldflags` 用**单引号**包 `$$GRAPH_HASH`
|
||
// ⇒ 注入字面量 `$GRAPH_HASH` ⇒ 同 (1)。已在
|
||
// `internal/kernelboot/makefile_inject_test.go` 守。
|
||
//
|
||
// ## 判据形态:源码扫描(此处**恰当**)
|
||
//
|
||
// 断言的是"这些脚本里**存在**某个必需的处置"(拷贝 depgraph / 用当前模块
|
||
// 路径 / 拒绝缺资产),属于"模式存在性",不是接线或顺序断言
|
||
// —— 后者用源码扫描会骗人(见 docs/zh/kernel-modularization-design.md §8.6)。
|
||
//
|
||
// 端到端验证(真的装 deb 再跑内核)由发版流程做;这里守的是"结构性缺失"
|
||
// 这一类,它比端到端更早、更便宜。
|
||
|
||
import (
|
||
"os"
|
||
"strings"
|
||
"testing"
|
||
)
|
||
|
||
const (
|
||
pkgLinux = "../../deploy/packaging/package-linux.sh"
|
||
buildSh = "../../deploy/packaging/build.sh"
|
||
makefile = "../../Makefile"
|
||
)
|
||
|
||
func readOrSkip(t *testing.T, p string) string {
|
||
t.Helper()
|
||
b, err := os.ReadFile(p)
|
||
if err != nil {
|
||
t.Skipf("读不到 %s: %v", p, err)
|
||
}
|
||
return string(b)
|
||
}
|
||
|
||
// ★★★ 判据:打包时必须把 `depgraph.bin` 放进与 homed **同目录**。
|
||
//
|
||
// 违反后果(实测):内核启动即失败退出 1 ——
|
||
//
|
||
// kernelboot: 生产构建(内嵌 hash 非空)但读不到依赖图 <dir>/depgraph.bin
|
||
func TestPackageLinuxStagesDepgraph(t *testing.T) {
|
||
src := readOrSkip(t, pkgLinux)
|
||
if !strings.Contains(src, "stage_depgraph") {
|
||
t.Fatal("★ package-linux.sh 没有 stage_depgraph —— " +
|
||
"打出的包缺依赖图 ⇒ 内核拒绝启动(退出码 1)")
|
||
}
|
||
// 必须真的**调用**(定义而不用等于没接)
|
||
if strings.Count(src, "stage_depgraph \"$staging\"") == 0 {
|
||
t.Fatal("★ stage_depgraph 定义但从未被调用 —— 图不会进包")
|
||
}
|
||
// 必须放到 usr/bin(与 homed 同目录),不能是别处
|
||
if !strings.Contains(src, "usr/bin/depgraph.bin") {
|
||
t.Fatal("★ depgraph.bin 必须与 homed 同在 usr/bin —— " +
|
||
"内核按「可执行文件同目录」找图,放别处等于没有")
|
||
}
|
||
}
|
||
|
||
// ★★★ 判据:`build.sh` 构建 homed 时必须**生成图并注入 digest**。
|
||
//
|
||
// 这与 Makefile 是**两条独立构建路径**:只修 Makefile 不够
|
||
// (实测:用 build.sh 打出的 homed 缺图,起不来)。
|
||
func TestPackagingBuildShGeneratesGraphForHomed(t *testing.T) {
|
||
src := readOrSkip(t, buildSh)
|
||
if !strings.Contains(src, "homed-depgraph") {
|
||
t.Fatal("★ deploy/packaging/build.sh 不构建 homed-depgraph —— " +
|
||
"它无法产出依赖图 ⇒ 该路径打出的 homed 起不来")
|
||
}
|
||
if !strings.Contains(src, "EmbeddedGraphHash=") {
|
||
t.Fatal("★ build.sh 未注入 EmbeddedGraphHash —— " +
|
||
"即使有图,digest 与二进制不匹配同样拒绝启动")
|
||
}
|
||
// 必须用 -digest 拿**真实**的 digest(不能写死/留空)
|
||
if !strings.Contains(src, "-digest") {
|
||
t.Fatal("★ build.sh 必须调用 homed-depgraph -digest 取真实 digest")
|
||
}
|
||
}
|
||
|
||
// ★★★ 判据:`-X` 的包路径必须是**当前**模块路径(不能是历史 gitcode.com)。
|
||
//
|
||
// Go 对未知 `-X` 路径**静默忽略**(不报错)⇒ 发行包版本/commit 全 unknown。
|
||
func TestPackagingLdflagsUseCurrentModulePath(t *testing.T) {
|
||
src := readOrSkip(t, buildSh)
|
||
if strings.Contains(src, "gitcode.com/JianFeeeee/HologramAgent") {
|
||
t.Fatal("★ build.sh 的 LDFLAGS 用的是**历史**模块路径 " +
|
||
"`gitcode.com/JianFeeeee/HologramAgent`;当前模块是 " +
|
||
"`github.com/JianFeeeee/HologramAgent`。\n" +
|
||
" `-X` 对未知路径**静默无效** ⇒ 发行包的 Version/Commit/BuildTime " +
|
||
"全是默认值(实测 --version 输出 unknown)")
|
||
}
|
||
if !strings.Contains(src, "github.com/JianFeeeee/HologramAgent/internal/meta.Version") {
|
||
t.Fatal("★ build.sh 必须用 github.com/... 注入 meta.Version")
|
||
}
|
||
}
|
||
|
||
// ★★★ 判据:Makefile 注入依赖图 digest 时引号必须**允许展开**。
|
||
//
|
||
// (与 internal/kernelboot/makefile_inject_test.go 同源,这里从打包视角再守一遍:
|
||
//
|
||
// 打包依赖 Makefile 产出的二进制,所以这条对打包也关键。)
|
||
func TestMakefileInjectsExpandableGraphHash(t *testing.T) {
|
||
src := readOrSkip(t, makefile)
|
||
idx := strings.Index(src, "EmbeddedGraphHash=$$GRAPH_HASH")
|
||
if idx < 0 {
|
||
t.Skip("Makefile 的注入方式已变,请同步更新判据")
|
||
}
|
||
lineStart := strings.LastIndex(src[:idx], "\n") + 1
|
||
line := src[lineStart:idx]
|
||
ld := strings.Index(line, "-ldflags")
|
||
if ld < 0 {
|
||
return
|
||
}
|
||
if strings.Count(line[ld:], "'")%2 == 1 {
|
||
t.Fatal("★ Makefile 的 -ldflags 用单引号包住 $$GRAPH_HASH ⇒ shell 不展开 " +
|
||
"⇒ 注入字面量 $GRAPH_HASH ⇒ 内核拒绝启动")
|
||
}
|
||
}
|
||
|
||
// ★★★ 判据:onnx 资产缺失时打包必须**拒绝**(不能出「默认启用但用不了」的假包)。
|
||
//
|
||
// 用户明确要求 onnx 的 so 与模型进包;官方脚本的态度是"缺资产就失败"。
|
||
// 这条守的是那个**拒绝**仍然存在。
|
||
func TestPackagingRefusesMissingONNXAssets(t *testing.T) {
|
||
src := readOrSkip(t, pkgLinux)
|
||
for _, kw := range []string{"ONNXRUNTIME_LIB", "CHINESECLIP_BUNDLE_DIR"} {
|
||
if !strings.Contains(src, kw) {
|
||
t.Errorf("★ package-linux.sh 不再检查 %s —— "+
|
||
"onnx 资产缺失时会打出「默认启用但装完不能用」的假包", kw)
|
||
}
|
||
}
|
||
// 必须**真的**拒绝:出现 ERROR 且 return 1
|
||
if !strings.Contains(src, "拒绝打包") {
|
||
t.Error("★ onnx 资产缺失时打包脚本必须明确拒绝(ERROR + 非零退出)")
|
||
}
|
||
// 许可证必须随二进制分发
|
||
if !strings.Contains(src, "ThirdPartyNotices") {
|
||
t.Error("★ onnxruntime 的 ThirdPartyNotices 必须随包分发(MIT 要求)")
|
||
}
|
||
}
|
||
|
||
// ★★★ 判据:`toolchain` 变体的 control 文件必须存在且描述准确。
|
||
func TestToolchainVariantHasControlFile(t *testing.T) {
|
||
src := readOrSkip(t, pkgLinux)
|
||
if !strings.Contains(src, "toolchain") {
|
||
t.Skip("toolchain 变体已移除,请同步更新判据")
|
||
}
|
||
// 变体循环里必须含 toolchain
|
||
if !strings.Contains(src, "for variant in") || !strings.Contains(src, "toolchain; do") {
|
||
t.Error("★ 变体循环必须包含 toolchain")
|
||
}
|
||
b, err := os.ReadFile("../../deploy/packaging/linux/deb/control-toolchain")
|
||
if err != nil {
|
||
t.Fatal("★ 缺 control-toolchain —— toolchain 变体打不出来")
|
||
}
|
||
c := string(b)
|
||
for _, kw := range []string{"hologramagent-toolchain", "hmapdev", "SDK", "depgraph"} {
|
||
if !strings.Contains(c, kw) {
|
||
t.Errorf("control-toolchain 的描述里应提到 %s(让 apt show 能说清装了什么)", kw)
|
||
}
|
||
}
|
||
}
|
||
|
||
// ★★★ 判据:打包产物里的**安装路径与单元名**必须已随品牌改名(批 2)。
|
||
//
|
||
// 这条守的是"改一半就静默失效"那一类:包能打出来、退出码 0,但内容里
|
||
// 还写着旧路径 —— 装上去之后 systemd 找不到单元、数据目录对不上、
|
||
// ONNX 运行库指向空目录,而且没有任何一步报错。
|
||
//
|
||
// 只扫**产物布局相关**的旧串,不扫 module 路径(那是批 1 的地盘,
|
||
// 见 TestPackagingLdflagsUseCurrentModulePath)。
|
||
func TestPackagingUsesRenamedInstallPaths(t *testing.T) {
|
||
src := readOrSkip(t, pkgLinux)
|
||
|
||
// 安装路径 / 单元文件:一个都不许留旧值。
|
||
for _, stale := range []string{
|
||
"/var/lib/homeagent",
|
||
"/usr/lib/homeagent",
|
||
"/usr/share/doc/homeagent",
|
||
"deploy/homeagent.service",
|
||
"/etc/systemd/system/homeagent.service",
|
||
"/usr/bin/homeagent-gui",
|
||
"/usr/lib/homeagent-gui",
|
||
`pkg_name="homeagent-`,
|
||
} {
|
||
if strings.Contains(src, stale) {
|
||
t.Errorf("★ package-linux.sh 仍含旧安装路径/包名 %q —— "+
|
||
"打出的包会装到旧位置(日志全绿,装完路径对不上)", stale)
|
||
}
|
||
}
|
||
|
||
// 新值必须真的出现(否则上面的"无旧值"可能只是整段被删了)。
|
||
for _, want := range []string{
|
||
"/var/lib/hologram",
|
||
"/usr/lib/hologram",
|
||
"/usr/share/doc/hologramagent",
|
||
"deploy/hologramagent.service",
|
||
"/etc/systemd/system/hologramagent.service",
|
||
"/usr/bin/hologram-gui",
|
||
"/usr/lib/hologram-gui",
|
||
`pkg_name="hologramagent-`,
|
||
} {
|
||
if !strings.Contains(src, want) {
|
||
t.Errorf("★ package-linux.sh 缺少 %q —— 改名没落到位", want)
|
||
}
|
||
}
|
||
}
|
||
|
||
// ★★★ 判据:deb 的 postinst / prerm 必须操作**新**单元名。
|
||
//
|
||
// postinst 的 SERVICE_NAME 若还是旧名,装完 systemctl enable 找不到单元:
|
||
// 它带 `|| true`,所以一声不响地什么也没启用 —— 装完不会开机自启。
|
||
func TestDebMaintainerScriptsUseRenamedService(t *testing.T) {
|
||
for _, p := range []string{
|
||
"../../deploy/packaging/linux/deb/postinst",
|
||
"../../deploy/packaging/linux/deb/prerm",
|
||
} {
|
||
b, err := os.ReadFile(p)
|
||
if err != nil {
|
||
t.Skipf("读不到 %s: %v", p, err)
|
||
}
|
||
s := string(b)
|
||
if !strings.Contains(s, `SERVICE_NAME="hologramagent"`) {
|
||
t.Errorf("★ %s 的 SERVICE_NAME 不是 hologramagent —— "+
|
||
"systemctl enable/stop 会作用在不存在的单元上(|| true 吞掉失败)", p)
|
||
}
|
||
if strings.Contains(s, `SERVICE_NAME="homeagent"`) {
|
||
t.Errorf("★ %s 仍用旧 SERVICE_NAME=homeagent", p)
|
||
}
|
||
if strings.Contains(s, "/var/lib/homeagent") || strings.Contains(s, "/usr/lib/homeagent") {
|
||
t.Errorf("★ %s 仍含旧安装路径", p)
|
||
}
|
||
}
|
||
}
|
||
|
||
// ★★★ 判据:四个 deb control 的包名与 SDK 安装路径必须已改。
|
||
//
|
||
// `dpkg-deb -f <pkg> Package` 的输出就是这里的 Package 字段;
|
||
// 旧名会让 apt 认为是另一个包(升级路径直接断成"装了第二个包")。
|
||
func TestDebControlFilesUseRenamedPackage(t *testing.T) {
|
||
for _, variant := range []string{"client", "server", "full", "toolchain"} {
|
||
p := "../../deploy/packaging/linux/deb/control-" + variant
|
||
b, err := os.ReadFile(p)
|
||
if err != nil {
|
||
t.Fatalf("读不到 %s: %v", p, err)
|
||
}
|
||
s := string(b)
|
||
want := "Package: hologramagent-" + variant + "\n"
|
||
if !strings.Contains(s, want) {
|
||
t.Errorf("★ %s 缺少 %q", p, strings.TrimSpace(want))
|
||
}
|
||
if strings.Contains(s, "Package: homeagent-") {
|
||
t.Errorf("★ %s 的 Package 仍是旧的 homeagent-*", p)
|
||
}
|
||
if strings.Contains(s, "/usr/lib/homeagent") {
|
||
t.Errorf("★ %s 的 SDK 安装路径仍是 /usr/lib/homeagent", p)
|
||
}
|
||
}
|
||
if b, err := os.ReadFile("../../deploy/packaging/linux/deb/control-toolchain"); err == nil {
|
||
if !strings.Contains(string(b), "/usr/lib/hologram/sdk") {
|
||
t.Error("★ control-toolchain 没把 SDK 装到 /usr/lib/hologram/sdk")
|
||
}
|
||
}
|
||
}
|
||
|
||
// ★★★ 判据:systemd 单元文件本身(deploy/hologramagent.service)。
|
||
//
|
||
// 文件名改了、内容没改是最典型的一半:ExecStart 仍写旧数据目录,
|
||
// 于是 StateDirectory / ReadWritePaths 与实际 --data 不一致,
|
||
// 内核起得来但写到只读路径上(或反之)—— 取决于 sandbox 设置,难查。
|
||
func TestSystemdUnitUsesRenamedPaths(t *testing.T) {
|
||
b, err := os.ReadFile("../../deploy/hologramagent.service")
|
||
if err != nil {
|
||
t.Fatalf("★ 缺 deploy/hologramagent.service: %v", err)
|
||
}
|
||
s := string(b)
|
||
for _, want := range []string{
|
||
"--data /var/lib/hologram",
|
||
"WorkingDirectory=/var/lib/hologram",
|
||
"StateDirectory=hologram",
|
||
"ReadWritePaths=/var/lib/hologram",
|
||
"ReadOnlyPaths=/etc/hologram",
|
||
"ONNXRUNTIME_DIR=/usr/lib/hologram/onnxruntime",
|
||
"LoadCredential=homed_security_key:/etc/hologram/security.key",
|
||
} {
|
||
if !strings.Contains(s, want) {
|
||
t.Errorf("★ hologramagent.service 缺 %q", want)
|
||
}
|
||
}
|
||
// 注释里可以留旧路径(升级说明),但**指令行**不能有。
|
||
for _, line := range strings.Split(s, "\n") {
|
||
trimmed := strings.TrimSpace(line)
|
||
if trimmed == "" || strings.HasPrefix(trimmed, "#") {
|
||
continue
|
||
}
|
||
if strings.Contains(trimmed, "/var/lib/homeagent") || strings.Contains(trimmed, "/usr/lib/homeagent") {
|
||
t.Errorf("★ hologramagent.service 指令行仍含旧路径: %s", trimmed)
|
||
}
|
||
}
|
||
if _, err := os.Stat("../../deploy/homeagent.service"); err == nil {
|
||
t.Error("★ deploy/homeagent.service 仍然存在 —— 旧单元文件没删,两个单元会打架")
|
||
}
|
||
}
|