refactor: 全面改名为 HologramAgent + 提示词技术内容下沉到 skill/工具描述 + 修 21 处乱码

## ① 全面改名 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:客户端与内核版本一致
This commit is contained in:
JianFeeeee
2026-10-22 20:49:44 +08:00
parent 6bea130639
commit f256098c9f
204 changed files with 310 additions and 252 deletions

View File

@ -0,0 +1,174 @@
/**
* 插件数据获取(无 UI 依赖)。
*
* 从 pages/PluginsPage.ets 抽出:三处接口的取数、合并口径与字段解析
* 都放在这里,页面只负责把结果落到 @State。
*
* 数据源对齐 WebGUI renderPlugins:
* - GET /kernel → plugins[{name,loaded}](含全部内置插件)+ tools(按 plugin 归属)
* - GET /plugins → 已安装外部插件元数据(version/description 等)
* - GET /plugins/disabled → {disabled:[{name,...}]}
* 三方按名称合并去重排序。
*/
import { apiClient } from './ApiClient';
import { PluginRow, PluginDetail, emptyPluginDetail } from '../model/Model';
/** 拉取并合并插件列表;失败时抛错,由调用方转成人话提示。 */
export async function fetchPluginRows(): Promise<PluginRow[]> {
// ---- kernel: loaded plugins + tool ownership ----
const loadedMap: Map<string, boolean> = new Map<string, boolean>();
const toolsByPlugin: Map<string, string[]> = new Map<string, string[]>();
const kResp = await apiClient.getWithTimeout('/kernel', 12000);
const kernelObj: Record<string, Object> = JSON.parse(kResp.body) as Record<string, Object>;
const kpRaw: Object | undefined = kernelObj['plugins'];
if (kpRaw !== undefined && kpRaw !== null) {
const kpArr: Object[] = kpRaw as Object[];
for (let i = 0; i < kpArr.length; i++) {
const item: Record<string, Object> = kpArr[i] as Record<string, Object>;
const n: string = item['name'] as string ?? '';
if (n.length === 0) {
continue;
}
loadedMap.set(n, item['loaded'] as boolean ?? true);
}
}
const tRaw: Object | undefined = kernelObj['tools'];
if (tRaw !== undefined && tRaw !== null) {
const tArr: Object[] = tRaw as Object[];
for (let i = 0; i < tArr.length; i++) {
const item: Record<string, Object> = tArr[i] as Record<string, Object>;
const tn: string = item['name'] as string ?? '';
const owner: string = item['plugin'] as string ?? '';
if (tn.length === 0 || owner.length === 0) {
continue;
}
let list: string[] | undefined = toolsByPlugin.get(owner);
if (list === undefined) {
list = [];
toolsByPlugin.set(owner, list);
}
// 每插件最多展示 8 个工具名,避免卡片过长
if (list.length < 8) {
list.push(tn);
}
}
}
// ---- installed external plugins metadata ----
const externalMeta: Map<string, Record<string, Object>> = new Map<string, Record<string, Object>>();
try {
const pResp = await apiClient.getWithTimeout('/plugins', 10000);
const bodyTrim = pResp.body.trim();
let arr: Object[] = [];
if (bodyTrim.length > 0 && bodyTrim.charAt(0) === '[') {
arr = JSON.parse(pResp.body) as Object[];
} else {
const obj: Record<string, Object> = JSON.parse(pResp.body) as Record<string, Object>;
const rawList: Object = obj['plugins'] ?? obj['data'];
if (rawList !== undefined && rawList !== null) {
arr = rawList as Object[];
}
}
for (let i = 0; i < arr.length; i++) {
const item: Record<string, Object> = arr[i] as Record<string, Object>;
const n: string = item['name'] as string ?? '';
if (n.length > 0) {
externalMeta.set(n, item);
}
}
} catch (e) {
// 外部列表失败不阻塞内置展示
}
// ---- disabled list ----
const disabledNames: Set<string> = new Set<string>();
try {
const dResp = await apiClient.getWithTimeout('/plugins/disabled', 8000);
const dObj: Record<string, Object> = JSON.parse(dResp.body) as Record<string, Object>;
const dArr: Object | undefined = dObj['disabled'];
if (dArr !== undefined && dArr !== null) {
const items: Object[] = dArr as Object[];
for (let di = 0; di < items.length; di++) {
const dItem: Record<string, Object> = items[di] as Record<string, Object>;
const dn: string = dItem['name'] as string ?? '';
if (dn.length > 0) {
disabledNames.add(dn);
}
}
}
} catch (e) {
// disabled endpoint may not exist; ignore
}
// ---- merge: allNames sorted(与 GUI 一致)----
const allNames: Set<string> = new Set<string>();
loadedMap.forEach((v: boolean, k: string) => {
allNames.add(k);
});
externalMeta.forEach((v: Record<string, Object>, k: string) => {
allNames.add(k);
});
disabledNames.forEach((n: string) => {
allNames.add(n);
});
const names: string[] = Array.from(allNames);
names.sort();
const rows: PluginRow[] = [];
for (let i = 0; i < names.length; i++) {
const name: string = names[i];
const meta: Record<string, Object> | undefined = externalMeta.get(name);
const tools: string[] | undefined = toolsByPlugin.get(name);
const row: PluginRow = {
name: name,
loaded: loadedMap.get(name) ?? false,
disabled: disabledNames.has(name),
external: externalMeta.has(name),
version: meta !== undefined ? meta['version'] as string ?? '' : '',
description: meta !== undefined ? meta['description'] as string ?? '' : '',
tools: tools,
};
rows.push(row);
}
return rows;
}
function strArray(raw: Object | undefined): string[] {
const out: string[] = [];
if (raw === undefined || raw === null) {
return out;
}
const arr: Object[] = raw as Object[];
for (let i = 0; i < arr.length; i++) {
const s: string = arr[i] as string ?? '';
if (s.length > 0) {
out.push(s);
}
}
return out;
}
/**
* GET /plugins/{name} —— 后端返回插件清单字段。
* WebGUI 只是把它 JSON.stringify 进 <pre>,这里逐字段结构化展示。
* 内置插件不在 /plugins 里,取不到详情时由调用方退回列表已有信息。
*/
export async function fetchPluginDetail(name: string): Promise<PluginDetail> {
const resp = await apiClient.getWithTimeout('/plugins/' + name, 10000);
const o: Record<string, Object> = JSON.parse(resp.body) as Record<string, Object>;
const d: PluginDetail = emptyPluginDetail();
d.name = o['name'] as string ?? name;
d.version = o['version'] as string ?? '';
d.description = o['description'] as string ?? '';
d.author = o['author'] as string ?? '';
d.license = o['license'] as string ?? '';
d.homepage = o['homepage'] as string ?? '';
d.repository = o['repository'] as string ?? '';
d.entry = o['entry'] as string ?? '';
d.minVersion = o['min_version'] as string ?? '';
d.deprecated = o['deprecated'] as boolean ?? false;
d.tags = strArray(o['tags']);
d.files = strArray(o['files']);
return d;
}