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:客户端与内核版本一致
138 lines
5.2 KiB
Go
138 lines
5.2 KiB
Go
package core
|
||
|
||
import (
|
||
"strings"
|
||
|
||
agentAPI "github.com/JianFeeeee/HologramAgent/internal/agent/api"
|
||
sdk "github.com/JianFeeeee/HologramAgent/internal/sdk"
|
||
)
|
||
|
||
// 本文件实现内核侧对"内置工具注册面"(方案 B)的注入。
|
||
//
|
||
// 背景与方案 B 的完整理由见 internal/sdk/tool.go 末尾的注释。
|
||
// 一句话:`memory_*` / `knowledge_*` / `doc_*` / `person_*` 这 20+ 个是
|
||
// **内核内置**的,在 executeToolCallInner 里按前缀分派,从不进 ToolAPI,
|
||
// 于是插件(seq)既查不到也调不了 —— 真机实跑实证:seq_run 报
|
||
// 「工具 knowledge_list 不存在或未注册」,而同一轮模型直接调它是成功的。
|
||
//
|
||
// 注入必须**晚绑定**且**per-agent**:内置工具的可见性由运行期状态门控
|
||
// (`if a.memory != nil` / `if a.knowledge != nil` …),而驻留子是轻量内核、
|
||
// memory 为 nil。而 ToolAPI 是全局单例,拿不到 agent,只能由 agent 自己
|
||
// 提供一份 provider。
|
||
|
||
// builtinProvider 是 *Agent 上的适配器:把 agent 的内置工具面
|
||
// 转成 sdk.BuiltinProvider。
|
||
type builtinProvider struct{ a *Agent }
|
||
|
||
// Defs 返回当前 agent 可见的内置工具声明。
|
||
//
|
||
// ⚠️ **必须复用 buildToolDefs 的同一批生成逻辑**,否则会出现"两套语义":
|
||
// 一套决定模型看得到什么(buildToolDefs),另一套决定插件看得到什么。
|
||
// 这里直接从 buildToolDefs 里筛出**不在** StageHost/IOManager 中的那些,
|
||
// 从而保证门控条件(memory/knowledge 是否就绪)完全一致。
|
||
func (p builtinProvider) Defs() []sdk.BuiltinToolDef {
|
||
if p.a == nil {
|
||
return nil
|
||
}
|
||
// 先算出"插件/设备侧已有的名字",剩下的才是内核内置的。
|
||
external := map[string]bool{}
|
||
if p.a.io != nil {
|
||
for _, d := range p.a.io.GetAllTools() {
|
||
external[d.Name] = true
|
||
}
|
||
}
|
||
if p.a.stageHost != nil {
|
||
for _, d := range p.a.stageHost.GetToolDefs() {
|
||
external[d.Name] = true
|
||
}
|
||
}
|
||
|
||
var out []sdk.BuiltinToolDef
|
||
for _, raw := range p.a.buildToolDefs() {
|
||
m, ok := raw.(map[string]interface{})
|
||
if !ok {
|
||
continue
|
||
}
|
||
fn, ok := m["function"].(map[string]interface{})
|
||
if !ok {
|
||
continue
|
||
}
|
||
name, _ := fn["name"].(string)
|
||
if name == "" || external[name] {
|
||
continue
|
||
}
|
||
desc, _ := fn["description"].(string)
|
||
params, _ := fn["parameters"].(map[string]interface{})
|
||
out = append(out, sdk.BuiltinToolDef{
|
||
Name: name, Description: desc, Parameters: params,
|
||
})
|
||
}
|
||
return out
|
||
}
|
||
|
||
// Exec 执行一个内置工具。
|
||
//
|
||
// 直接复用 executeToolCall 的完整路径(内置分支 + 授权闸 + 异常处理),
|
||
// **不复用** executeToolCallInner:后者要求经前缀 switch,而 ToolAPI 的
|
||
// 存在性判定已在 ToolDefByName/ExecuteTool 做过一次。
|
||
//
|
||
// ⚠️ 传入的 toolCall 不带 RawArguments(插件侧没有原始 JSON),
|
||
// 因此 __arg_error 的信息面在插件路径上天然缺失 —— 插件调用的是
|
||
// **已解析**的参数,不存在被截断的中间态。
|
||
func (p builtinProvider) Exec(name string, args map[string]interface{}) (string, error) {
|
||
if p.a == nil {
|
||
return "", errBuiltinNoAgent
|
||
}
|
||
tc := agentAPI.ToolCall{
|
||
ID: "builtin_" + name,
|
||
Name: name,
|
||
Arguments: args,
|
||
}
|
||
return p.a.executeToolCall(tc, p.a.defaultChannelForBuiltin()), nil
|
||
}
|
||
|
||
// defaultChannelForBuiltin 给出内置工具执行时的输出通道。
|
||
//
|
||
// 内置工具本身不产出"用户可见输出"(结果回给调用方),但 executeToolCall
|
||
// 的签名需要 channel(如记忆写入会记场景)。用 agent 的调度当前通道不可靠
|
||
// (它逐任务变化),故用一个稳定的内部标记。
|
||
func (a *Agent) defaultChannelForBuiltin() string { return "builtin" }
|
||
|
||
// errBuiltinNoAgent 表示 provider 未绑定 agent(装配顺序错误)。
|
||
var errBuiltinNoAgent = &builtinErr{"内置工具执行器未绑定 agent"}
|
||
|
||
type builtinErr struct{ msg string }
|
||
|
||
func (e *builtinErr) Error() string { return e.msg }
|
||
|
||
// InstallBuiltinToolProvider 把本 agent 的内置工具面注入 ToolAPI。
|
||
//
|
||
// 供内核在**创建 agent 之后**调用(bootstrap / resident 创建处)。
|
||
// 幂等:重复调用只是覆盖为同一个 agent。
|
||
func (a *Agent) InstallBuiltinToolProvider() { a.installBuiltinProvider() }
|
||
|
||
// installBuiltinProvider 把本 agent 的内置工具面注入 ToolAPI。
|
||
//
|
||
// 由内核在**创建 agent 之后**调用(bootstrat / resident 创建处)。
|
||
// 注入是**全局**的:最后一次注入生效。⚠️ 因此多 agent 场景下,
|
||
// ToolAPI 看到的是"最近一个注入者"的内置工具面 —— 这是当前架构的
|
||
// 已知局限(ToolAPI 是单例却需要 per-agent 数据)。
|
||
// 记入设计文档 §10 待定项,不在本次解决。
|
||
func (a *Agent) installBuiltinProvider() {
|
||
sdk.SetBuiltinProvider(builtinProvider{a: a})
|
||
}
|
||
|
||
// isBuiltinToolName 粗判某名字是否可能是内置工具(供提示词/文档用)。
|
||
// 真正的判定以 ToolAPI 查询为准(带门控)。
|
||
func isBuiltinToolName(name string) bool {
|
||
for _, p := range []string{
|
||
"memory_", "knowledge_", "doc_", "person_",
|
||
"output_", "input_", "resident_", "notify_parent", "persona_set",
|
||
} {
|
||
if strings.HasPrefix(name, p) {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|