mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-01 06:47:29 +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:客户端与内核版本一致
317 lines
10 KiB
Plaintext
317 lines
10 KiB
Plaintext
/**
|
||
* 设备页的四个二级页面内容:本机设备 / 设备能力 / 设备通道 / 接入的设备。
|
||
*
|
||
* 从 pages/DevicePage.ets 抽出(原来是 LocalDeviceContent / CapsContent /
|
||
* GatewayContent / OnlineDevicesContent 四个 @Builder + KvRow)。
|
||
* 每个面板自带 SubPageLayer 外壳(标题、所属 Tab、返回、刷新),
|
||
* 页面只保留路由分发 —— 与 SettingsPage 拆出的三个 Pane 同一套做法。
|
||
*/
|
||
|
||
import { DeviceInfo } from '../model/Model';
|
||
import { ThemePalette, DARK_PALETTE, LIGHT_PALETTE, ANIM_FAST, ANIM_NORMAL,
|
||
ANIM_ENTER } from '../common/Constants';
|
||
import { LOCAL_DEVICE_CAPS } from '../common/DeviceBridgeSession';
|
||
import { MotionBase } from './MotionBase';
|
||
import { PlainCard, SubPageLayer } from './SubPage';
|
||
|
||
/** 通用 KV 行(面板之间共用) */
|
||
@Component
|
||
export struct DeviceKvRow {
|
||
@StorageProp('themeIsDark') private isDark: boolean = true;
|
||
@Prop label: string = '';
|
||
@Prop value: string = '';
|
||
|
||
private palette(): ThemePalette {
|
||
return this.isDark ? DARK_PALETTE : LIGHT_PALETTE;
|
||
}
|
||
|
||
build() {
|
||
Row() {
|
||
Text(this.label)
|
||
.fontSize(13)
|
||
.fontColor(this.palette().textSecondary)
|
||
Blank()
|
||
Text(this.value)
|
||
.fontSize(13)
|
||
.fontColor(this.palette().textPrimary)
|
||
.maxLines(1)
|
||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||
}
|
||
.width('100%')
|
||
.padding({ top: 8, bottom: 8 })
|
||
.border({ width: { bottom: 1 }, color: this.palette().kvBorder })
|
||
}
|
||
}
|
||
|
||
/** 二级:本机设备(基本信息 + 远程控制授权) */
|
||
@Component
|
||
export struct DeviceLocalPane {
|
||
@StorageProp('themeIsDark') private isDark: boolean = true;
|
||
@Prop deviceId: string = '';
|
||
@Prop authorized: boolean = false;
|
||
onBack?: () => void;
|
||
onToggleAuth?: (on: boolean) => void;
|
||
|
||
private palette(): ThemePalette {
|
||
return this.isDark ? DARK_PALETTE : LIGHT_PALETTE;
|
||
}
|
||
|
||
build() {
|
||
SubPageLayer({
|
||
title: '本机设备',
|
||
tab: 2,
|
||
onBack: () => {
|
||
const cb: (() => void) | undefined = this.onBack;
|
||
if (cb !== undefined) {
|
||
cb();
|
||
}
|
||
},
|
||
}) {
|
||
PlainCard({ caption: '基本信息' }) {
|
||
DeviceKvRow({ label: '设备 ID', value: this.deviceId.length > 0 ? this.deviceId : '未注册' })
|
||
DeviceKvRow({ label: '名称', value: 'HologramAgent OHOS' })
|
||
DeviceKvRow({ label: '类型', value: 'phone' })
|
||
}
|
||
|
||
PlainCard({ caption: '权限控制' }) {
|
||
Row() {
|
||
Column({ space: 2 }) {
|
||
Text('允许 agent 控制本机')
|
||
.fontSize(14)
|
||
.fontColor(this.palette().textPrimary)
|
||
Text('授权后 agent 可调用下方能力;截屏仅捕获本应用画面,剪贴板读取需系统弹窗确认。')
|
||
.fontSize(11)
|
||
.fontColor(this.palette().textMuted)
|
||
.margin({ top: 4 })
|
||
}
|
||
.layoutWeight(1)
|
||
.alignItems(HorizontalAlign.Start)
|
||
|
||
Toggle({ type: ToggleType.Switch, isOn: this.authorized })
|
||
.selectedColor(this.palette().accent)
|
||
.onChange((on: boolean) => {
|
||
const cb: ((on: boolean) => void) | undefined = this.onToggleAuth;
|
||
if (cb !== undefined) {
|
||
cb(on);
|
||
}
|
||
})
|
||
}
|
||
.width('100%')
|
||
.alignItems(VerticalAlign.Center)
|
||
|
||
Row() {
|
||
Circle({ width: 8, height: 8 })
|
||
.fill(this.authorized ? '#17A964' : '#E84026')
|
||
.animation({ duration: ANIM_NORMAL, curve: Curve.EaseOut })
|
||
.margin({ right: 8 })
|
||
Text(this.authorized ? '已授权 — agent 可远程调用能力' : '未授权 — agent 将拒绝远程命令')
|
||
.fontSize(12)
|
||
.fontColor(this.authorized ? '#17A964' : this.palette().textMuted)
|
||
.animation({ duration: ANIM_NORMAL, curve: Curve.EaseOut })
|
||
}
|
||
.width('100%')
|
||
.margin({ top: 12 })
|
||
.padding({ left: 4 })
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/** 二级:设备能力(本机能被 agent 调用的能力清单) */
|
||
@Component
|
||
export struct DeviceCapsPane {
|
||
@StorageProp('themeIsDark') private isDark: boolean = true;
|
||
onBack?: () => void;
|
||
|
||
private palette(): ThemePalette {
|
||
return this.isDark ? DARK_PALETTE : LIGHT_PALETTE;
|
||
}
|
||
|
||
build() {
|
||
SubPageLayer({
|
||
title: '设备能力',
|
||
tab: 2,
|
||
onBack: () => {
|
||
const cb: (() => void) | undefined = this.onBack;
|
||
if (cb !== undefined) {
|
||
cb();
|
||
}
|
||
},
|
||
}) {
|
||
PlainCard({ caption: '能力清单' }) {
|
||
Text('agent 通过设备桥可调用的本机能力:')
|
||
.fontSize(12)
|
||
.fontColor(this.palette().textMuted)
|
||
.margin({ bottom: 10 })
|
||
|
||
Flex({ wrap: FlexWrap.Wrap }) {
|
||
ForEach(LOCAL_DEVICE_CAPS, (cap: string) => {
|
||
Text(cap)
|
||
.fontSize(11)
|
||
.fontColor(this.palette().accent)
|
||
.padding({ left: 8, right: 8, top: 4, bottom: 4 })
|
||
.borderRadius(999)
|
||
.backgroundColor(this.palette().bgHover)
|
||
.border({ width: 1, color: this.palette().glassBorder })
|
||
.margin({ right: 6, bottom: 6 })
|
||
}, (cap: string) => cap)
|
||
}
|
||
.width('100%')
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/** 二级:设备通道(网关地址 / Token / 连接状态) */
|
||
@Component
|
||
export struct DeviceGatewayPane {
|
||
@StorageProp('themeIsDark') private isDark: boolean = true;
|
||
@Prop bridgeUrl: string = '';
|
||
@Prop bridgeToken: string = '';
|
||
@Prop bridgeConnected: boolean = false;
|
||
onBack?: () => void;
|
||
onRefresh?: () => void;
|
||
|
||
private palette(): ThemePalette {
|
||
return this.isDark ? DARK_PALETTE : LIGHT_PALETTE;
|
||
}
|
||
|
||
build() {
|
||
SubPageLayer({
|
||
title: '设备通道',
|
||
tab: 2,
|
||
onBack: () => {
|
||
const cb: (() => void) | undefined = this.onBack;
|
||
if (cb !== undefined) {
|
||
cb();
|
||
}
|
||
},
|
||
}) {
|
||
PlainCard({ caption: '连接信息' }) {
|
||
DeviceKvRow({ label: '网关地址', value: this.bridgeUrl.length > 0 ? this.bridgeUrl : '-' })
|
||
DeviceKvRow({ label: 'Token', value: this.bridgeToken.length > 0 ? '已从连接继承' : '未配置' })
|
||
DeviceKvRow({ label: '状态', value: this.bridgeConnected ? '已连接' : '未连接' })
|
||
}
|
||
|
||
PlainCard({ caption: '操作' }) {
|
||
Row() {
|
||
Blank()
|
||
MotionBase({ pressEnabled: true, fillWidth: false }) {
|
||
Button('刷新设备')
|
||
.height(34)
|
||
.fontSize(12)
|
||
.backgroundColor(Color.Transparent)
|
||
.border({ width: 1, color: this.palette().btnGhostBorder })
|
||
.fontColor(this.palette().textSecondary)
|
||
.onClick(() => {
|
||
const cb: (() => void) | undefined = this.onRefresh;
|
||
if (cb !== undefined) {
|
||
cb();
|
||
}
|
||
})
|
||
}
|
||
}
|
||
.width('100%')
|
||
|
||
Text('设备通道由应用前台生命周期统一管理;切换连接配置后会自动使用新地址和 Token。')
|
||
.fontSize(11)
|
||
.fontColor(this.palette().textMuted)
|
||
.margin({ top: 10 })
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/** 二级:接入的设备(网关侧在线设备列表) */
|
||
@Component
|
||
export struct DeviceListPane {
|
||
@StorageProp('themeIsDark') private isDark: boolean = true;
|
||
@Prop devices: DeviceInfo[] = [];
|
||
onBack?: () => void;
|
||
onRefresh?: () => void;
|
||
|
||
private palette(): ThemePalette {
|
||
return this.isDark ? DARK_PALETTE : LIGHT_PALETTE;
|
||
}
|
||
|
||
build() {
|
||
SubPageLayer({
|
||
title: '接入的设备',
|
||
tab: 2,
|
||
showRefresh: true,
|
||
onBack: () => {
|
||
const cb: (() => void) | undefined = this.onBack;
|
||
if (cb !== undefined) {
|
||
cb();
|
||
}
|
||
},
|
||
onRefresh: () => {
|
||
const cb: (() => void) | undefined = this.onRefresh;
|
||
if (cb !== undefined) {
|
||
cb();
|
||
}
|
||
},
|
||
}) {
|
||
DeviceOnlineList({ devices: this.devices })
|
||
}
|
||
}
|
||
}
|
||
|
||
/** 在线设备列表本体(抽出来只是为了让 DeviceListPane 的 build 更短) */
|
||
@Component
|
||
struct DeviceOnlineList {
|
||
@StorageProp('themeIsDark') private isDark: boolean = true;
|
||
@Prop devices: DeviceInfo[] = [];
|
||
|
||
private palette(): ThemePalette {
|
||
return this.isDark ? DARK_PALETTE : LIGHT_PALETTE;
|
||
}
|
||
|
||
build() {
|
||
Column() {
|
||
if (this.devices.length === 0) {
|
||
Text('暂无其他设备。电脑 GUI 或 CLI 连接同一网关后会出现在这里。')
|
||
.fontSize(12)
|
||
.fontColor(this.palette().textMuted)
|
||
.padding({ left: 4 })
|
||
.transition(TransitionEffect.OPACITY.animation({ duration: ANIM_FAST, curve: Curve.EaseOut }))
|
||
}
|
||
|
||
ForEach(this.devices, (dev: DeviceInfo) => {
|
||
// PlainCard 是自定义组件,transition 不能直接挂在它上面(会生成 __Common__ 包装),
|
||
// 所以用一个无 padding、满宽的 Column 承载入场动画,布局不受影响。
|
||
Column() {
|
||
PlainCard({ caption: '' }) {
|
||
Row() {
|
||
Circle({ width: 8, height: 8 })
|
||
.fill(dev.online ? '#17A964' : '#77809A')
|
||
.margin({ right: 10 })
|
||
Column() {
|
||
Text(dev.name.length > 0 ? dev.name : dev.deviceId)
|
||
.fontSize(14)
|
||
.fontColor(this.palette().textPrimary)
|
||
Text(dev.kind + (dev.authorized ? ' · 已授权' : ' · 未授权'))
|
||
.fontSize(11)
|
||
.fontColor(dev.authorized ? '#17A964' : this.palette().textMuted)
|
||
}
|
||
.layoutWeight(1)
|
||
.alignItems(HorizontalAlign.Start)
|
||
|
||
Text(dev.caps.length.toString() + ' 能力')
|
||
.fontSize(10)
|
||
.fontColor(this.palette().textMuted)
|
||
}
|
||
.width('100%')
|
||
}
|
||
}
|
||
.width('100%')
|
||
.transition(TransitionEffect.OPACITY
|
||
.combine(TransitionEffect.translate({ y: 12 }))
|
||
.animation({ duration: ANIM_ENTER, curve: Curve.EaseOut }))
|
||
}, (dev: DeviceInfo) => dev.deviceId + dev.online.toString())
|
||
}
|
||
.width('100%')
|
||
.alignItems(HorizontalAlign.Start)
|
||
}
|
||
}
|