Files
HomeAgent/README.md
JianFeeeee 152dc680ad docs: 架构节写清上下文管理与记忆的实际做法
## 补上真正的差异点

之前架构节只有一张图和七阶段表,把最核心的上下文管理省略了。
用 codegraph 查了 `RelevanceContext` / `PruneWithProtected` /
`coldcontext.Store` / `block_recall`,确认实际做法,逐条对照常见 agent:

### 上下文:按相关度取舍,不是截断或摘要

常见做法是超窗裁掉旧对话,或让模型自己摘要 —— 共同问题是**丢什么由位置决定**。
这里是给每条历史事件按当前输入打分,低分下沉到文档层。

打分主路径是稠密余弦(与媒体、文档同一向量空间),模型未就绪退到 TF-IDF。

关键在 `effectiveProtectedCount`:保护条数受预算约束。

```go
budgetCap = ContextTokens / avgEventTokens
pCount = min(配置值, budgetCap)
```

源码里记着这条约束的来历:50k 窗口下单条事件约 5800 token(工具回灌型),
预算 40000,「钉住 10 条」本身就装不下 —— 每轮裁剪都撞保护下限,
两轮后预算耗尽,任务直接终止,召回率从 44% 掉到 22%。

### 三层是迁移,不是复制

`ColdDoc` 的注释写得很清楚:「块的真实归属在 Events[i].Blocks 里」,
块在任一层只有一个持有者,不靠引用计数保活。

回灌用 `Peek` + 确认 + `Remove` 两阶段,源码明确标注「**禁止先删后灌**
—— 删除必须排在确认之后,回灌失败即永久丢数据」。

### 场面:两条路并行,且互不吞并

声明场景**不吸收整轮指纹**(`EnsureScene` 的注释):否则它会以接近 1.0 的相似度
吃掉后续所有同类轮次,被动聚类再也长不出更细的场面。

这条边界是踩出来的,不是设计时想到的。

## 开头相应收缩

原来开头四段「**粗体断言** + 三行解释」与架构节重复。改成一句介绍 + 安装 +
三段话点出差异,细节留给架构节。

两版 163 / 191 行,9 个标题对齐,代码块 10/10 配对,无死链。
2026-10-31 11:30:18 +08:00

164 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

> ⚠️ **AI 辅助编程声明**:本项目代码、文档及提交历史中,部分内容由 AI 辅助生成或修改。人工已审阅关键改动,但使用时请自行评估与验证。
# HologramAgent
> **English**: [README_EN.md](./README_EN.md)
自托管 Agent 内核。跑在自己的机器上,插件以独立进程接入。
```bash
make build build-cli
./build/homed -data /tmp/ha
```
API 密钥在 WebUI(`http://localhost:8080`)里填。需要 Linux。
跟常见 agent 框架的主要差别在上下文管理:不是超窗就截断或让模型摘要,
而是按相关度取舍,低分的下沉成文档、再蒸馏进图数据库。另外两处不太一样的是
启动顺序在编译期定序,以及插件按设施就绪提前启动(详见下方架构)。
## 架构
```
homed(内核)
LLM 编排 · 三层记忆 · 检索 · 调度
│
┌──────────────┼──────────────┐
│ │ │
stdio RPC 共享内存 事件环
控制 大块数据 通知
│ │ │
└──────────────┼──────────────┘
│
插件(各自独立进程)
WebUI · QQ · 命令行 · 文件 · 浏览器
```
内核不发 HTTP、不读文件。所有接触外部世界的能力都在插件里,插件崩溃不影响内核,
重启退避 1s / 2s / 3s,5 分钟内第 4 次崩溃停止自动重启等人工介入。
### 上下文不是「截断 + 摘要」
常见做法是把超窗的对话裁掉或让模型自己摘要,两者的共同问题是**丢什么由位置决定**。
这里换成了按相关度取舍:用当前输入给每条历史事件打分,低分的下沉到文档层。
打分用稠密向量余弦(与媒体、文档共享同一向量空间),模型未就绪时退化到 TF-IDF。
裁剪时无条件保住最近 N 条,但 N 本身受预算约束:
```go
// 保护条数不超过预算能装下的条数
budgetCap = ContextTokens / avgEventTokens
pCount = min(配置值, budgetCap)
```
这条约束来自一次实测:50k 窗口下单条事件约 5800 token(工具回灌型),预算约 40000,
「钉住 10 条」本身就装不下,于是每轮裁剪都撞保护下限,两轮后预算耗尽,任务直接终止。
召回率从 44% 掉到 22%。现在窗口越小越自动收紧,1M 窗口下则能用满配置值。
被裁掉的事件不是丢弃,是迁移:事件连同它持有的媒体块一起进文档层,块的 ID 不变、
只换持有层。回灌时用 `coldcontext_query` 取回,两阶段(先 Peek 确认、再 Remove),
不用「先删后灌」——那会在回灌失败时永久丢数据。
### 三层记忆是迁移,不是复制
```
L0 上下文 相关性裁剪,最近 N 条恒受保护
↓ 低分事件下沉(带媒体块)
L1 文档 稠密检索,访问计数 ≤2 且久未访问则继续下沉
↓ 蒸馏成三元组
L2 图 实体关系 + 场面索引,支持按场面召回
```
块在任一层只有一份持有者,不靠引用计数保活。媒体不是附件:图片、音频是图里的一等节点,
有自己的向量,和文本在同一空间里检索。
### 记忆按场面分,不按会话
普通 agent 通常一条会话一条历史,换个话题就失忆,或者全塞进一个上下文里互相干扰。
这里按「可观察的场所指纹」分:通道、对话对象、工具、话题、时段,权重 1.0/1.0/0.8/0.4/0.2。
同一指纹出现到第 2 次才建场景(`minSceneEvidence = 2`),只发生一次的不算。
归属阈值 0.5、唤起阈值 0.35,唤起比归属宽松——想不起来是损失,多想一条只是多几行上下文。
场景每次重现强度 +1,久不重现按半衰期衰减。
两个来源并行:**声明**(插件在注入点说明"这是哪个场面")与**涌现**(指纹自己聚类)。
声明场景不吸收整轮指纹,否则它会以接近 1.0 的相似度吃掉后续所有同类轮次,
被动路再也长不出更细的场面。
### 七个阶段
`internal/agent/core/stages.go`
| 阶段 | 时机 | 插件可做 |
|---|---|---|
| `on_input` | 消息到达 | 黑名单、限流、短路回复 |
| `pre_action` | 上下文就绪,调 LLM 前 | 注入外部数据 |
| `post_action` | LLM 返回文本与工具调用 | 敏感词过滤、强制改向 |
| `before_toolcall` | 单个工具执行前 | 审计、拒绝、改参 |
| `after_toolcall` | 单个工具执行完 | 脱敏、排序 |
| `before_output` | 最终文本就绪 | 格式适配 |
| `after_output` | 已发送 | 统计 |
`post_action → [before_toolcall → 执行 → after_toolcall] → post_action` 是内循环,
退出条件:模型不再调工具、全部被拒、或达到上限。
## 其余
- **通道**:WebUI、命令行、QQ 群与私聊、A2A、IDE 里的 ACP
- **工具**:文件、命令、代码、浏览器(真实 Chrome)、日程、备忘、RSS、音乐
- **模态**:图片、视频、语音、截图、网页
28 个内置插件、89 个模型可调工具。外部插件一插件一仓,见 [github.com/HologramAgent](https://github.com/HologramAgent)。
## 下载
[Releases](https://github.com/JianFeeeee/HologramAgent/releases) 提供三种变体:
| 变体 | 内容 | 适用 |
| --- | --- | --- |
| **full** | homed + waiter + 桌面 GUI + systemd unit | 单机全功能 |
| **server** | homed + waiter + systemd unit | 服务器(无桌面环境) |
| **client** | waiter + 桌面 GUI | 连接远程 HologramAgent |
- Linux:`.deb`(amd64/arm64)、`.rpm`(x86_64)、`.tar.gz`
- Windows:`HologramAgent_v1.2.0_{Full,Server,Client}_win64.exe`(NSIS 安装向导,含 AGPL 许可页)。自 v1.2.0 起因 `homed` 不再支持 Windows 原生(依赖 fd 继承与共享内存段内偏移解引用),安装器改为引导到 **WSL2**,并把 Linux 包送进发行版里按 Linux 方式安装。
- 免安装:`homeagent-bin-<os>_<arch>.tar.gz`(含 homed/waiter/initconfig)
- 校验:`SHA256SUMS`
macOS 的 `homed` 需在原生 macOS 构建(CGO + sqlite3),发布包仅含 `waiter`/`initconfig`。
## 构建
依赖 Go 1.25+、CGo(go-sqlite3)、Linux。
```bash
make build build-cli # 守护进程 + CLI
make test # go test ./...
make install # 安装到系统
```
`homed` 只能在 Linux 上跑(依赖 fd 继承与共享内存段的段内偏移解引用)。
Windows 上构建 `waiter.exe`,`homed` 跑在 WSL2 里;macOS 要原生构建 `homed`。
## 许可
内核以 **AGPL-3.0-only** 发布([LICENSE](LICENSE))。改过的版本如果对外提供网络服务,
必须向使用者提供源码(§13)。
自己写的插件不受此约束。插件静态链接的 [hologramagentsdk](https://github.com/JianFeeeee/hologramagentsdk)
是 **MIT**,宽松许可不继承 AGPL,所以外部插件可以闭源、可以商用,也不必开源自改。
随包第三方组件保持各自许可,全文在 `/usr/share/doc/hologramagent/licenses/`。
### 随包分发的第三方组件
| 组件 | 许可 | 位置 |
| --- | --- | --- |
| Chinese-CLIP ViT-B/16(ONNX 产物) | Apache-2.0 | `/usr/lib/hologram/models/chinese-clip-vit-b16-onnx/` |
| ONNX Runtime(`libonnxruntime.so`) | MIT | `/usr/lib/hologram/onnxruntime/` |
| jieba 词库(内嵌进二进制) | MIT | 源码 `internal/memory/jiebadict/` |
| Go 依赖(go-sqlite3、gojieba、bubbletea 等) | MIT / BSD-3 / Apache-2.0 | 均为宽松许可,与 AGPL-3.0 兼容 |
这些组件**保持各自原有许可**,不在本项目的 AGPL 授权范围内;发行包把它们的许可全文放在
`/usr/share/doc/hologramagent/licenses/`,并在 dep/rpm 元数据里声明本包许可为 `AGPL-3.0-only`。