mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-03 15:57:35 +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:客户端与内核版本一致
152 lines
5.3 KiB
Go
152 lines
5.3 KiB
Go
package main
|
||
|
||
import (
|
||
"encoding/json"
|
||
"net/http"
|
||
"net/http/httptest"
|
||
"testing"
|
||
"time"
|
||
)
|
||
|
||
func TestNormalizeGateway(t *testing.T) {
|
||
cases := map[string]string{
|
||
// 已是完整端点:原样
|
||
"ws://devices.localhost:8080/api/v1/device/ws": "ws://devices.localhost:8080/api/v1/device/ws",
|
||
"wss://devices.example.com/api/v1/device/ws": "wss://devices.example.com/api/v1/device/ws",
|
||
// 只有 ws 根:补路径
|
||
"ws://127.0.0.1:9890": "ws://127.0.0.1:9890/api/v1/device/ws",
|
||
"ws://devices.example.com/": "ws://devices.example.com/api/v1/device/ws",
|
||
// 裸 host:port:补 scheme + 路径(旧行为)
|
||
"127.0.0.1:9890": "ws://127.0.0.1:9890/api/v1/device/ws",
|
||
"devices.example.com:8080": "ws://devices.example.com:8080/api/v1/device/ws",
|
||
// http(s) → ws(s)
|
||
"http://127.0.0.1:9890": "ws://127.0.0.1:9890/api/v1/device/ws",
|
||
"https://devices.example.com": "wss://devices.example.com/api/v1/device/ws",
|
||
// ★ 子域地址不得被改写(这正是改造后的正确形态)
|
||
"devices.example.com": "ws://devices.example.com/api/v1/device/ws",
|
||
// 空
|
||
"": "",
|
||
" ": "",
|
||
}
|
||
for in, want := range cases {
|
||
if got := normalizeGateway(in); got != want {
|
||
t.Errorf("normalizeGateway(%q) = %q,期望 %q", in, got, want)
|
||
}
|
||
}
|
||
}
|
||
|
||
// 发现端点返回权威地址时,必须采用它(而不是自己拼门户同源地址)。
|
||
func TestDiscoverGatewayUsesServerAnswer(t *testing.T) {
|
||
var gotPath, gotKey string
|
||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
gotPath = r.URL.Path
|
||
gotKey = r.Header.Get("X-API-Key")
|
||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||
"available": true,
|
||
"url": "ws://devices.localhost:8080/api/v1/device/ws",
|
||
"url_portal": "ws://127.0.0.1:8080/api/v1/device/ws",
|
||
"auth": "none",
|
||
})
|
||
}))
|
||
defer srv.Close()
|
||
|
||
url, err := discoverGateway(srv.URL, "PORTAL-KEY", 3*time.Second)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
// ★ 必须优先门户同源形态:waiter 走系统解析器,*.localhost 解析不到
|
||
if url != "ws://127.0.0.1:8080/api/v1/device/ws" {
|
||
t.Errorf("未优先采用门户同源形态: %q", url)
|
||
}
|
||
if gotPath != "/api/v1/device/gateway" {
|
||
t.Errorf("发现路径不对: %q", gotPath)
|
||
}
|
||
if gotKey != "PORTAL-KEY" {
|
||
t.Errorf("未带门户凭证: %q", gotKey)
|
||
}
|
||
}
|
||
|
||
// 用户填的是完整网关地址时,也要能正确截到门户根再问。
|
||
func TestDiscoverGatewayFromFullEndpointInput(t *testing.T) {
|
||
var gotPath string
|
||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
gotPath = r.URL.Path
|
||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||
"available": true, "url": "ws://devices.localhost/api/v1/device/ws",
|
||
})
|
||
}))
|
||
defer srv.Close()
|
||
|
||
// 输入形态:完整旧端点(含 /api/v1/device/ws)与 ws:// 前缀
|
||
for _, in := range []string{
|
||
srv.URL + "/api/v1/device/ws",
|
||
"ws://" + srv.Listener.Addr().String() + "/api/v1/device/ws",
|
||
} {
|
||
gotPath = ""
|
||
if _, err := discoverGateway(in, "k", 3*time.Second); err != nil {
|
||
t.Errorf("输入 %q 应成功: %v", in, err)
|
||
continue
|
||
}
|
||
if gotPath != "/api/v1/device/gateway" {
|
||
t.Errorf("输入 %q 未截到门户根,实际路径 %q", in, gotPath)
|
||
}
|
||
}
|
||
}
|
||
|
||
// 服务端明确报告不可用 → 必须返回错误(调用方据此回退),而不是给个连不上的 URL。
|
||
func TestDiscoverGatewayUnavailableReportsError(t *testing.T) {
|
||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||
"available": false,
|
||
"reason": "本实例没有声明设备网关反代",
|
||
})
|
||
}))
|
||
defer srv.Close()
|
||
|
||
if _, err := discoverGateway(srv.URL, "k", 3*time.Second); err == nil {
|
||
t.Error("服务端报告不可用时应返回错误")
|
||
}
|
||
}
|
||
|
||
// 老版本 HologramAgent 没有该端点(404)→ 返回错误而不是 panic/空成功。
|
||
func TestDiscoverGatewayOldServerFallsBack(t *testing.T) {
|
||
srv := httptest.NewServer(http.NotFoundHandler())
|
||
defer srv.Close()
|
||
if _, err := discoverGateway(srv.URL, "k", 3*time.Second); err == nil {
|
||
t.Error("404 应返回错误,让调用方回退到自配地址")
|
||
}
|
||
// 空地址快速失败
|
||
if _, err := discoverGateway("", "k", 3*time.Second); err == nil {
|
||
t.Error("空门户地址应返回错误")
|
||
}
|
||
}
|
||
|
||
// 老版本只给 url(无 url_portal)时,仍必须能用 —— 退回子域形态。
|
||
func TestDiscoverGatewayFallsBackToSubdomainForm(t *testing.T) {
|
||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||
"available": true,
|
||
"url": "ws://devices.example.com/api/v1/device/ws",
|
||
})
|
||
}))
|
||
defer srv.Close()
|
||
got, err := discoverGateway(srv.URL, "k", 3*time.Second)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if got != "ws://devices.example.com/api/v1/device/ws" {
|
||
t.Errorf("无 url_portal 时应退回 url,实际 %q", got)
|
||
}
|
||
}
|
||
|
||
// 两者都没有 → 明确报错,而不是返回空串让调用方拿着空地址去连。
|
||
func TestDiscoverGatewayNoURLReportsError(t *testing.T) {
|
||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
json.NewEncoder(w).Encode(map[string]interface{}{"available": true})
|
||
}))
|
||
defer srv.Close()
|
||
if _, err := discoverGateway(srv.URL, "k", 3*time.Second); err == nil {
|
||
t.Error("两个形态都缺时应报错")
|
||
}
|
||
}
|