docs: rewrite DESIGN.md and ARCHITECTURE.md matching current architecture

- Plugin = container with embedded IO channel
- OneBot QQ protocol support
- Output channel capability validation
- Atomic hot-reload (plgreload)
- Three-layer memory (TF-IDF relevance pruning)
- Remove all Docker/snapshot/multi-agent outdated content
This commit is contained in:
root
2026-07-02 12:18:46 +08:00
parent 9e5e15b2d3
commit e450edf865
2 changed files with 370 additions and 598 deletions

365
DESIGN.md
View File

@ -1,241 +1,188 @@
# HomeAgent 架构设计 v2
# HomeAgent 架构设计 v3
## 一、核心理念
24 小时陪伴用户、随时待命的智能管家。**单会话·单 Agent**(可分身/调用其他 Agent,身份不漂移。
24 小时陪伴用户、随时待命的智能管家。**单会话·单 Agent**,身份不漂移。
### 设计原则
- 所有输入走 IO 抽象层(中断模式),不直调 agent 方法
- DeepSeek v4 flash 为默认 LLMthinking 模式关闭
- 人格固定personal.md记忆分层管理防止性格突变
- 知识独立于记忆agent 主动学习
- **所有输入走 IO 抽象层(中断模式)**,不直调 agent 方法
- **所有 LLM 调用走 Provider 接口**,不直连 API
- **DeepSeek v4 flash** 为默认 LLMthinking 模式关闭
- **人格固定**personal.md记忆分层管理防止性格突变
- **知识独立于记忆**agent 主动学习
- **插件 = 容器**IO 通道是插件的内嵌组件
---
## 二、人格内核Personality Core
## 二、核心抽象
### IO 抽象层(唯一输入路径)
```
personal.md ──→ 固定注入 system prompt
外部设备 (Mic/Camera/OneBot/GPIO/HTTP)
IOManager
├─ Device 接口(每个设备实现)
│ ├─ Name() / Type() / Description()
│ ├─ Tools() → 给 LLM 的 Function Calling 工具
│ ├─ Execute() → 工具调用分发
│ ├─ Start() / Stop()
│ └─ OutputCapabilities() → 输出能力声明
├─ InputEvent{Source, Type, Payload} → inputCh
├─ OutputEvent{Target, Type, Payload} → outputCh
└─ 路由表: 输入源 → 默认输出通道
```
- 纯文本 Markdown 文件,定义 agent 的人格、行为准则
- 加载一次永不改变(不随对话漂移)
- 放在 prompt 最前面,优先级最高
**输出通道能力声明**`OutputCapability` 位掩码):
| 能力 | 说明 |
|------|------|
| CapText | 文本输出 |
| CapFile | 文件传输 |
| CapImage | 图片输出 |
| CapAudio | 音频输出 |
| CapStructured | 结构化数据JSON/卡片) |
**Agent 可调用通道工具**
- `output_list_channels` — 查看所有可用通道及其能力
- `output_set_channel` — 切换当前回复的输出通道
- `output_send` — 通过指定通道异步发送消息(校验能力)
### API 抽象层(唯一输出路径)
```
Agent Core → Provider.Chat() → LLM API
┌───────┴───────┐
▼ ▼
OpenAIProvider LuaAdapter
(DeepSeek API) (格式转换)
```
### 插件系统OpenClaw 兼容)
```
Plugin容器
├─ 元数据: name, version, author, description
├─ IOConfig可选: 声明 IO 端口
├─ Device可选: 原生 Go IO 设备实现
└─ Tools: 给 LLM 的工具定义
```
插件来源:
- `plugins/` 目录热加载agent 通过 `plgreload` 工具显式控制重载
- 有原生工厂注册的(如 QQ/OneBot→ 构造原生设备
- 纯 SKILL.md → 用 PluginDevice 包装为 IO 设备
- 原子化替换:新设备 Start → 原子换路由 → 旧设备 Stop
---
## 三、记忆体系(Memory System
三层分级架构,自顶向下逐渐持久化、抽象化:
## 三、记忆体系(三层
```
用户输入
┌─────────────────────┐
│ Layer 1: 上下 文 │ RelevanceContext
│ 基于相关性保留 │ 30 条活跃TF-IDF 排序
│ 最不相关的→文档记忆 │
└────────┬────────────┘
│ 相关性裁剪(每次响应后)
用户输入 → Context内存, 30条, TF-IDF排序
每次响应后裁剪最不相关的
┌─────────────────────┐
│ Layer 2: 文档记忆 │ document.Store
│ 全文 + TF-IDF 向量 │ JSON 持久化
│ 冷文档→图数据库 │ 72h 未访问→Graph
└────────┬────────────┘
DocumentJSON + TF-IDF向量, 冷72h→图
│ 心跳蒸馏30min
┌─────────────────────┐
│ Layer 3: 图数据库 │ memory.GraphDB (SQLite)
│ 实体-关系三元组 │ 向量索引(实体名)
│ 定期重整+同义合并 │ bigram Jaccard
└─────────────────────┘
GraphSQLite三元组, 定期重整+同义合并)
```
### Layer 1: 上下文RelevanceContext
| 层 | 存储 | 容量 | 裁剪策略 |
|---|---|---|---|
| Layer 1: 上下文 | `RelevanceContext`(内存环形缓冲) | 30 条 | TF-IDF 余弦相似度排序,低分→文档 |
| Layer 2: 文档 | `document.Store`JSON 文件 + 向量索引) | 无上限 | 72h 未访问 + ≤2 次命中→图 |
| Layer 3: 图 | `memory.GraphDB`SQLite 三元组) | 无上限 | 定期重整 + bigram Jaccard 同义合并 |
**不再使用固定条数裁剪。改为:**
1. 每次用户输入后,计算**模型输出**与每条上下文的 TF-IDF 余弦相似度
2. 按相关性从高到低排序,保留 topK默认 30
3. 最不相关的上下文 → 归档到文档记忆Layer 2保留全文+向量
4. 活跃上下文中只保留最近且相关性高的内容
**相关文件:** `internal/agent/core/context.go`
### Layer 2: 文档记忆Document Store
**作用:**
- 存储被上下文裁剪下来的事件摘要、agent 主动提交的文档、图记忆同步的索引
- 每个文档包含:摘要、全文、标签、实体、来源、访问计数、最后访问时间
- TF-IDF 向量索引(字符 bigram支持向量相似度搜索
**冷文档归化:**
- 心跳检测(每 30min找出 72h 未访问且访问次数 ≤ 2 的文档
- 转为图数据库三元组(文档→包含内容、提及实体、标签、来源)
- 清理已归化的冷文档
**相关文件:** `internal/memory/document/document.go`
### Layer 3: 图数据库Graph Memory
**存储:** SQLite 三元组(实体-关系-实体),每个关系带置信度、会话 ID、日期桶
**注入方式(区分于文档注入):**
- 用户消息到达时,先对实体名做**向量相似度搜索**TF-IDF
- 找到相关实体名 → 查询图数据库
- 只注入**节点索引**(实体名+类型+提及次数+关系类型)到 prompt不注入全文
- 需要更多细节时agent 调用 `memory_recall` 工具查询
**定期重整(心跳触发,每 30min**
1. 同步实体名到向量索引(`Indexer.Sync()`
2. 文档记忆向量索引重建
3. 冷文档→图归化
4. 实体同义合并bigram Jaccard > 0.5
**相关文件:** `internal/memory/graph.go`, `internal/memory/indexer.go`
### Agent 可用记忆工具
| 工具 | 作用 | 操作对象 |
|------|------|----------|
| memory_recall | 检索图记忆 | GraphDB |
| memory_commit | 写入三元组 | GraphDB |
| memory_introspect | 查看记忆统计 | GraphDB |
| doc_query | 向量查询文档记忆 | Document Store |
| doc_commit | 提交文档 | Document Store |
**注入策略**
- 只注入**图索引**(实体名+类型+提及数)到 prompt不注入全文
- agent 通过 `memory_recall` 主动查询详情
- 文档摘要按相关性注入前 3 条
---
## 四、知识体系Knowledge System
## 四、知识体系
独立于记忆系统,用于 agent 学习知识:
独立于记忆agent 主动学习。
```
knowledge/
smart_home/
content.md ← 原始知识文件
cooking/
content.md
...
smart_home/content.md
cooking/content.md
TF-IDF 向量索引 ← 知识目录扫描时自动构建
TF-IDF 向量索引(字符 bigram
knowledge_search(query) → 返回相关内容
knowledge_search / knowledge_create / knowledge_list
```
**知识来源:**
1. **agent 主动学习**:调用 `knowledge_create` 工具,生成知识→写入目录+向量化
2. **用户上传**HTTP 文件上传端点 → 写入目录+向量化
3. **预置知识**`knowledge/` 目录下的 content.md
**相关文件:** `internal/knowledge/knowledge.go`
### Agent 可用知识工具
| 工具 | 作用 |
|------|------|
| knowledge_search | 向量搜索知识库 |
| knowledge_list | 列出知识分类 |
| knowledge_create | agent 主动创建知识 |
---
## 五、数据流总览
## 五、人格内核
```personal.md``` → 加载一次 → 固定在 system prompt 最前 → 永不漂移。
---
## 六、OneBot QQ 通道
```
用户消息
IO 输入中断 (channel.go)
eventLoop → processTextInput
├─ 1. 追加上下文 (RelevanceContext.Append)
│ └─ 计算向量,缓存
├─ 2. 构建 prompt
│ ├─ 人格设定 (personal.md)
│ ├─ 图记忆索引 (Indexer.BuildContext → 向量搜索实体名 → Recall → 摘要注入)
│ ├─ 文档记忆摘要 (DocStore.Query → 注入前3条摘要)
│ ├─ 工作记忆上下文 (RelevanceContext.Format)
│ ├─ 技能注入 (skills)
│ └─ 工具说明 (indexer + doc + knowledge tools)
├─ 3. 工具循环 (process)
│ ├─ 调用 LLM (DeepSeek v4 flash)
│ ├─ 解析 tool_calls
│ ├─ 执行工具 (memory_*/knowledge_*/doc_*/设备工具)
│ └─ 返回结果,循环直到无 tool_calls
go-cqhttp / LagrangeOneBot 前端)
│ Reverse WebSocket
OneBot Clientinternal/onebot/
├─ 事件循环 → Event → IO InputEvent
├─ Action 调用 → send_private_msg / send_group_msg / ...
└─ 自动重连 + 心跳检测
Device 注册 → IOManager → Agent
```
---
## 七、数据流
```
用户消息 → IO InputEvent → eventLoop
├─ 1. 追加到 RelevanceContext
├─ 2. 构建 prompt:
│ personal.md + 图索引 + 文档摘要 + 上下文 + 工具
├─ 3. 工具循环(最多 10 轮)
│ LLM → tool_calls → Execute → 结果 → LLM → ...
├─ 4. 追加响应到上下文
├─ 5. 相关性裁剪 (RelevanceContext.Prune)
│ └─ 最不相关的 → 文档记忆归档
├─ 6. IO 输出响应
└─ 7. 记忆候选事件
└─ → TextMemory (JSONL 持久化)
└─ → Distiller → GraphMemory (三元组萃取)
├─ 5. 相关性裁剪Prune → 不相关的归档到文档)
├─ 6. OutputEvent → 输出通道
└─ 7. 记忆候选 → TextMemory(JSONL) + Distiller → GraphDB
心跳线程 (每 30min):
├─ distillContext: 安全裁剪兜底
├─ syncGraphToDocs: 图→文档索引同步
├─ reorgGraph:
Indexer.Sync(): 实体名→向量索引
│ ├─ DocStore.Reindex(): 文档向量重建
│ ├─ 冷文档→图归化
│ └─ 同义实体合并
(后续) GraphDB 向量索引生成/重整
心跳(30min:
├─ Indexer.Sync() — 图→向量
├─ DocStore.Reindex() — 文档向量重建
├─ 冷文档→图归化
图同义合并
插件重载plgreload:
├─ 扫描 plugins/ 目录
加载新插件Start 新设备
├─ 原子替换 IOManager 设备表 + 路由表
└─ Stop 旧设备
```
---
## 六、关键文件
```
cmd/homed/main.go — 入口:组装所有子系统
internal/agent/core/agent.go — Agent 核心:事件循环、工具循环、心跳
internal/agent/core/context.go — RelevanceContext基于 TF-IDF 的上下文管理
internal/agent/personal.go — Personalitypersonal.md 加载
internal/agent/api/provider.go — LLM ProviderDeepSeek API 封装
internal/agent/io/channel.go — IO 抽象层:中断输入、设备注册
internal/api/handler.go — HTTP APIREST + OpenAI 兼容端点
internal/knowledge/knowledge.go — 知识系统:目录扫描、向量索引、搜索
internal/memory/graph.go — 图数据库SQLite 三元组 CRUD
internal/memory/indexer.go — 图索引器:向量搜索实体名、摘要注入
internal/memory/vector/store.go — 向量存储TF-IDF + 倒排索引 + 余弦相似度
internal/memory/document/doc.go — 文档记忆:存储、查询、冷文档检测
internal/memory/text/text.go — 文本记忆JSONL 原始日志持久化
internal/memory/pipeline/ — 蒸馏器:原始日志→图记忆
internal/tracker/tracker.go — Change Trackeroverlayfs 文件变更追踪
internal/plugin/plugin.go — 插件平台SKILL.md 加载
internal/supervisor/daemon.go — 守护进程:健康检查、自动 rollback
internal/lua/vm.go — Lua 适配器 VM
config/config.go — 配置加载
pkg/types/ — 类型定义
```
---
## 七、配置示例
## 八、配置
```yaml
daemon:
listen_addr: ":8080"
data_dir: "/var/lib/homeagent"
heartbeat_interval: 15s
check_interval: 30s
llm:
provider: "openai"
model: "deepseek-v4-flash"
base_url: "https://api.deepseek.com"
base_url: "https://api.deepseek.com/v1"
api_key: "${DEEPSEEK_API_KEY}"
temperature: 0.7
max_tokens: 4096
@ -243,15 +190,51 @@ llm:
---
## 八、对比原设计
## 九、关键文件
| 维度 | 旧设计 | 新设计 |
|------|--------|--------|
| 上下文裁剪 | 固定 20 条 FIFO | TF-IDF 相关性排序,保留最相关 |
| 上下文→文档 | 蒸馏器每周期 flush | 每次响应后按相关性裁剪归档 |
```
cmd/homed/main.go — 入口:组装所有子系统
internal/agent/core/agent.go — Agent 核心:事件循环、工具循环、心跳
internal/agent/core/context.go — RelevanceContextTF-IDF 上下文管理
internal/agent/personal.go — 人格加载
internal/agent/api/provider.go — Provider 接口 + DeepSeek 实现
internal/agent/io/channel.go — IO 抽象层Device/InputEvent/OutputEvent/路由
internal/api/handler.go — HTTP API 端点
internal/knowledge/knowledge.go — 知识系统
internal/memory/graph.go — SQLite 图数据库
internal/memory/indexer.go — 图索引器
internal/memory/vector/store.go — TF-IDF 向量存储
internal/memory/document/doc.go — 文档记忆
internal/memory/text/text.go — 文本记忆JSONL
internal/memory/pipeline/ — 蒸馏器
internal/onebot/ — OneBot V11 协议实现
internal/plugin/plugin.go — 插件系统
internal/tracker/tracker.go — 变更追踪
internal/supervisor/daemon.go — 守护进程
internal/lua/vm.go — Lua 适配器 VM
plugins/ — 插件目录
qq/SKILL.md — QQ 插件 SKILL.md
qq/skill.json — QQ 插件 JSON 元数据
config/config.go — 配置加载
pkg/types/ — 类型定义
DESIGN.md — 本架构文档
```
---
## 十、与旧设计的核心区别
| 维度 | 旧设计 (v1) | 当前设计 (v3) |
|------|-------------|---------------|
| 上下文裁剪 | 固定 FIFO 20 条 | TF-IDF 相关性排序 top-K |
| 图记忆注入 | 关键词 LIKE 查询 | 向量搜索实体名,只注索引 |
| 文档→图 | 无 | 72h 冷文档自动归化 |
| 图重整 | 无 | 心跳:向量同步+同义合并 |
| 知识系统 | 无 | `knowledge/` 目录+向量索引+工具 |
| 图重整 | 无 | 向量同步 + bigram Jaccard 同义合并 |
| 知识系统 | 无 | `knowledge/` 目录 + TF-IDF 向量 |
| 人格 | 无 | `personal.md` 固定注入 |
| 向量引擎 | 无 | 自研 TF-IDF + 倒排索引 |
| 向量引擎 | 无 | 自研 TF-IDF + 倒排索引(字符 bigram |
| 部署 | Docker 容器 + 快照 | 单二进制 + overlayfs 追踪 |
| Agent 模型 | 多 Agent 编排 | 单 Agent + 工具循环 |
| 输出通道 | 无 | 能力声明 + 路由 + 校验 |
| 插件系统 | 无 | OpenClaw SKILL.md + 原生工厂 |
| QQ 通道 | 无 | OneBot V11 Reverse WS |

View File

@ -1,79 +1,50 @@
# HomeAgent 架构设计文档
# HomeAgent 架构参考
> 基于 NextAgent 认知解耦架构,结合图记忆与工具调用系统的单二进制 AI 家庭助手
> 参考设计NextAgent — 严格的边界划分 + 工具调用TrulyMEM — 自主图记忆系统
> 基于 NextAgent 认知解耦架构 + TrulyMEM 自主图记忆 + OneBot 协议
---
## 一、核心设计原则
### 1. 认知解耦架构(源自 NextAgent
系统分为三个严格边界层:
## 一、分层架构
```
┌──────────────────────────────────────────────────────────────────
外层:IO 抽象层(唯一输入路径)
IOManager ── Microphone / Camera / GPIO / HTTP / Sensors
│ 所有外部输入 → InputEvent → inputCh
└──────────────────────────┬───────────────────────────────────────┘
┌──────────────────────────────────────────────────────────┐
│ IO 抽象层(唯一输入路径) │
Device(Mic/Speaker/Camera/GPIO/OneBot-QQ/PluginDevice)
│ 所有外部输入 → InputEvent → inputCh │
│ 输出通道: 能力声明(text/file/image/audio/structured) │
│ 路由表: 输入源 → 默认输出通道 │
└──────────────────────────┬───────────────────────────────┘
┌──────────────────────────▼───────────────────────────────────────
中层:Agent 操作层(核心编排器)
Agent Core ── 事件循环 consume inputCh │
│ ├─ 构建上下文(记忆索引 + 技能注入 + ToolDef
│ ├─ Provider.Chat()CompletionResponse
通过 IO 层输出 text + memory_candidate 事件
└──────────────────────────┬───────────────────────────────────────┘
┌──────────────────────────▼───────────────────────────────┐
Agent 操作层(核心编排器) │
eventLoop() consume inputCh
│ ├─ RelevanceContextTF-IDF 相关性管理)
│ ├─ 工具循环(Provider.Chat → tool_calls → Execute → ...)
记忆索引注入(图索引 + 文档摘要)
│ └─ 心跳蒸馏30min向量同步 + 冷归档 + 同义合并) │
└──────────────────────────┬───────────────────────────────┘
┌──────────────────────────▼───────────────────────────────────────
内层:API 抽象层(唯一输出路径) │
│ Provider ── OpenAI / Ollama / LuaAdaptedProvider
所有 LLM 调用通过 Provider.Chat() / ChatStream()
└──────────────────────────────────────────────────────────────────
┌──────────────────────────▼───────────────────────────────┐
│ API 抽象层(唯一输出路径) │
│ Provider.Chat() → DeepSeek API / OpenAI / Ollama
LuaAdapter 做请求/响应格式转换
└──────────────────────────────────────────────────────────┘
```
**三条核心规则:**
1. 所有外部输入 → 必须通过 `IOManager.InjectInput()` / `InjectText()` 注入
2. 所有 LLM 调用 → 必须通过 `Provider.Chat()` / `ChatStream()` 发出
3. Agent Core 不直接操作记忆系统,只发射 `memory_candidate` 事件,由 Memory Pipeline 异步消费
1. **所有外部输入** → 必须通过 `IOManager.InjectInput()` / `InjectText()` 注入
2. **所有 LLM 调用** → 必须通过 `Provider.Chat()` 发出
3. **Agent 不直接操作记忆系统**,只发射 `memory_candidate` 事件,由 Memory Pipeline 异步消费
### 2. 自主记忆系统(源自 TrulyMEM
## 二、子系统详解
```
User Input ──→ Agent Core ──→ Provider ──→ LLM Response
IO 层发射 memory_candidate 事件
Memory Pipeline Distiller 消费
周期蒸馏 → Graph Memory (SQLite)
删除原始会话文件
```
### 2.1 IO 抽象层(唯一输入路径)
**关键约束:**
- Agent 上下文中只注入**记忆索引 + 摘要****永远不**注入原始文本
- Agent 必须通过 `memory_recall` tool call **主动查询** 获取完整记忆细节
- 记忆蒸馏完全异步、自主运行,不阻塞主流程
---
## 二、系统层次详解
### 2.1 IO 抽象层(`internal/agent/io/channel.go`
**唯一输入路径。** 所有外部输入必须通过此层进入系统。
所有外部输入必须通过此层进入系统。
```
Device(Microphone) ─┐
Device(Camera) ─┤
Device(GPIO) ─┤──→ IOManager.InjectInput() → InputEvent → inputCh → Agent Core
Device(RobotArm) ─┤
Device(OneBot-QQ) ─┤
Device(Plugin) ─┤──→ IOManager → InputEvent → inputCh → Agent
Device(GPIO) ─┤
HTTP API ─┘
```
@ -82,297 +53,175 @@ HTTP API ─┘
| 类型 | 说明 |
|------|------|
| `InputEvent` | Source + Type + Payload — 所有外部输入的标准化格式 |
| `OutputEvent` | Target + Type + Payload — 所有输出的标准化格式 |
| `Device` | 接口Name() / Type() / Tools() / Execute() |
| `OutputEvent` | Target + Type + Payload + OutputChannel — 输出路由 |
| `Device` | 接口Name/Type/Description/Tools/Execute/Start/Stop/OutputCapabilities |
| `DeviceType` | Input / Output / IO |
| `ToolDef` | Name + Description + Parameters — 与 LLM Function Calling 同构 |
| `ToolDef` | Name + Description + Parameters + Handler — 与 LLM 函数调用同构 |
| `OutputCapability` | 位掩码text, file, image, audio, structured |
**内置设备(目前为桩实现,待真正硬件接入)**
**内置设备:**
| 设备 | 方向 | ToolDef 暴露 |
|------|------|-------------|
| Microphone | Input | `{name}_capture` — 录音 |
| Speaker | Output | `{name}_speak` — 语音播放 |
| Camera | Input | `{name}_capture` 拍照 + `{name}_stream` 视频流 |
| RobotArm | IO | `{name}_move` 移动 + `{name}_grip` 夹爪 |
| GPIO | IO | `{name}_gpio_write` + `{name}_gpio_read` |
| 设备 | 方向 | 工具 |
|------|------|------|
| Microphone | Input | capture — 录音 |
| Speaker | Output | speak — 语音播放 |
| Camera | Input | capture 拍照 |
| OneBot QQ | IO | send_private/send_group/get_group_member_info/get_group_list |
| PluginDevice | IO | 插件声明工具 |
| GPIO | IO | gpio_write/gpio_read |
**Device 与 Tool 同构原则**
- 所有 Device `Tools()` 返回 `[]ToolDef`,格式与 LLM Function Calling 完全一致
- Agent Core 自动收集所有 Device 的 ToolDef 合并到请求的 `tools` 字段
- Agent 通过 Function Calling 调用设备 capability
**输出通道能力校验**
- 每个 Device 声明 `OutputCapabilities()` → 位掩码
- `output_send` 工具发送前校验通道是否支持文本
- `output_list_channels` 只列出有输出能力的通道
### 2.2 API 抽象层(`internal/agent/api/provider.go`
**唯一输出路径。** 所有 LLM 请求通过此层发出。
### 2.2 API 抽象层(唯一输出路径
```
Agent Core ──→ ProviderManager ──→ Provider.Chat()
┌─────────┼─────────┐
OpenAI Ollama LuaAdaptedProvider
┌───────┴───────┐
▼ ▼
Lua Adapter Base Provider
(transform) (OpenAI/Ollama)
```
**Provider 接口:**
```go
type Provider interface {
Name() string
Chat(ctx, req) (*CompletionResponse, error)
ChatStream(ctx, req) (<-chan StreamChunk, error)
}
Agent Core → Provider.Chat()
┌──────────────┐
▼ ▼
OpenAIProvider LuaAdapter
(DeepSeek API) (格式转换)
```
| 实现 | 说明 |
|------|------|
| `OpenAIProvider` | 标准 OpenAI API 格式,支持 /chat/completions |
| `OllamaProvider` | Ollama /api/chat 格式,本地部署 |
| `OpenAIProvider` | 标准 OpenAI API 格式,DeepSeek v4 flash 默认 |
| `LuaAdaptedProvider` | 通过 Lua 脚本转换请求/响应的适配 wrapper |
**Lua 适配器机制:**
- 适配器文件位于 `{dataDir}/adapters/*.lua`
- 每个适配器返回 Lua table 包含 `name` + `transform_request` + `transform_response`
- 首次运行时自动从 embed.FS 复制捆绑适配器openai / deepseek / ollama / custom
- 支持热重载(`POST /api/v1/adapters`
- 用于兼容不同 API 供应商的请求/响应格式差异
Lua 适配器位于 `{dataDir}/adapters/*.lua`,每个适配器返回 `name` + `transform_request` + `transform_response`
### 2.3 Agent 操作层(`internal/agent/core/agent.go`
**纯编排器,不涉及张量运算。**
### 2.3 Agent 操作层(核心编排器
```
eventLoop()
▼ select on inputCh
handleInput(evt)
eventLoop() → select on inputCh
process(input)
├─ indexer.BuildContext(input) → 仅摘要+索引
├─ buildSystemPrompt() → 拼接 system prompt
├─ buildToolDefs() → 收集 IO 工具 + 记忆工具
├─ provider.Chat(req) → LLM 调用
├─ 记忆候选事件 → IO 层 EmitOutput("memory", "memory_candidate", ...)
输出 → IO 层 EmitOutput(source, "text", ...)
handleInput(evt) → processTextInput(input)
├─ 1. RelevanceContext.Append(input)
├─ 2. 构建 prompt: personal.md + 图索引 + 文档摘要 + 上下文 + 工具
├─ 3. 工具循环(最多 10 轮)
│ LLM → tool_calls → Execute → 结果注入 → 下一轮
4. 追加响应到上下文
├─ 5. RelevanceContext.Prune() → 低分事件→文档记忆归档
├─ 6. OutputEvent → 输出通道
└─ 7. memory_candidate → TextMemory + Distiller → GraphDB
```
**关键设计决策:**
- Agent 不直接持有 `memory.GraphDB` 引用 — 只通过 `Indexer` 构建上下文
- Agent 不直接调用 `memory.Commit()` — 只发射事件让 Pipeline 异步处理
- `Indexer.BuildContext()` 只返回内存索引摘要,不返回原始数据
- `buildSystemPrompt()` 中注入 `memory_recall` / `memory_commit` / `memory_introspect` 工具说明
- **所有输出走 IO 层** — 文本输出和记忆事件都通过 EmitOutput
**人格注入:** personal.md 加载一次,固定在 system prompt 最前,永不漂移。
### 2.4 图记忆系统(`internal/memory/graph.go`
SQLite 三元组存储,支持实体-关系-实体的图遍历。
**数据库 Schema**
```sql
entities(id PK, name UNIQUE, type, mention_count, created_at, updated_at)
relations(id PK, source_id FKentities, target_id FKentities,
relation_type, confidence, status, session_id, turn_id,
created_at, updated_at, date_bucket)
**工具分发:**
```
executeToolCall(tc)
├─ memory_* → executeMemoryTool
├─ knowledge_* → executeKnowledgeTool
├─ doc_* → executeDocTool
├─ output_* → executeOutputChannel/Send/ListChannels
└─ 其他 → io.ExecuteTool → Device.Execute
```
**核心操作:**
### 2.4 记忆系统
| 操作 | 说明 |
|------|------|
| `Commit(triples, sessionID, turnID)` | 写入三元组 → 自动 upsert 实体 + 插入关系 |
| `Recall(keywords, seedEntities, depth, session)` | 关键词搜索 → 图遍历 → 返回实体+关系 |
| `Purge(criteria, mode)` | 软/硬删除匹配的关系 |
| `Introspect()` | 统计信息:实体数、关系数、热点实体 |
| `Archive(days)` | 归档超过指定天数的关系 |
**Context Injection 机制(`internal/memory/indexer.go`**
- `BuildContext(userInput)` → 关键词提取 → `GraphDB.Recall()` → 构建摘要
- `FormatContext(context)` → 输出格式如:
`【记忆索引】关联 N 个记忆实体高频A、B、C 索引: A, B, C | 需更多细节请用 memory_recall 查询`
- `BuildToolPrompt()` → 生成 `memory_recall/commit/introspect/purge` 工具的 prompt 说明
- `GetToolDefinitions()` → 返回 LLM Function Calling 格式的工具定义
### 2.5 记忆管道(`internal/memory/pipeline/pipeline.go`
自主异步蒸馏管线。
三层分级,自顶向下逐渐持久化、抽象化:
```
Agent Core → EmitOutput("memory", "memory_candidate", {input, response})
Layer 1: 上下文RelevanceContext
内存环形缓冲TF-IDF 余弦相似度排序
每次响应后保留 top 30低分→文档记忆
Layer 2: 文档记忆document.Store
JSON 文件 + TF-IDF 向量索引(字符 bigram
冷文档72h 未访问 + ≤2 次)→ 图数据库
Layer 3: 图数据库memory.GraphDB
SQLite 三元组(实体-关系-实体)
只注入索引(实体名+类型+提及次数)到 prompt
定期重整:向量同步 + bigram Jaccard 同义合并
```
### 2.5 知识系统
独立于记忆agent 主动学习。
```
knowledge/{category}/content.md
onMemory(input, response) 回调
TF-IDF 向量索引(字符 bigram
Append() → records[] 内存缓冲区
▼ 每 10 分钟触发
distillLoop()
工具: knowledge_search / knowledge_create / knowledge_list
```
### 2.6 插件系统
OpenClaw SKILL.md 兼容。
```
Plugin容器
├─ 元数据: name, version, author
├─ IOConfig可选: 声明 IO 端口
├─ Device可选: 原生 Go 设备
└─ Tools: LLM 工具定义
Registry:
├─ NativeFactory: "qq" → onebot.NewDevice
├─ SetIOManager: 绑定 IO 管理器
└─ Reload(): 原子化重载
```
**热插拔流程:**
1. 扫描 `plugins/` 目录
2. 原生工厂优先,无工厂则 LoadSKILL.md
3. 新设备 Start预先启动
4. `IOManager.AtomicSwapDevices()` 原子替换设备表 + 路由表
5. 旧设备 Stop后台 goroutine
### 2.7 OneBot QQ 通道
```
OneBot 前端go-cqhttp/Lagrange
│ Reverse WebSocket
OneBot Clientinternal/onebot/
├─ 事件: message/notice/request → IO InputEvent
├─ 动作: send_private_msg / send_group_msg / get_group_info / ...
├─ 自动重连(指数退避 1s→30s
└─ 事件 handler 注入 IO 层
extractKeyTriples() → GraphDB.Commit()
cleanupRawFiles() 删除超期原始文件
Deviceinternal/onebot/device.go
├─ Tools: qq_send_private_msg, qq_send_group_msg, etc.
├─ Execute → Client.SendAction
└─ Start → Connect + 注册事件 handler
```
**配置:**
- `Interval: 10m` — 每 10 分钟蒸馏一次
- `RetentionDays: 7` — 原始记录保留 7 天
- `BatchSize: 50` — 每批处理 50 条
### 2.8 变更追踪器
### 2.6 技能系统(`internal/skill/manager.go`
兼容 OpenClaw 格式的技能管理。
**技能格式:**
- `{skillsDir}/{name}/SKILL.md` — Markdown 描述文件
- `{skillsDir}/{name}/skill.json` — 可选的元数据文件
**注入机制:**
- `GetInjectedPrompt()` → 收集所有已启用的技能内容注入到 system prompt
- 支持安装/卸载/启用/禁用
### 2.7 Supervisor 守护进程(`internal/supervisor/daemon.go`
```go
type Daemon struct {
cfg *types.Config
cm *container.Manager // Docker 容器管理
nm *network.Monitor // 网络监控
sm *snapshot.Manager // 快照管理
agents map[AgentID]*agentInstance
}
```
**职责:**
- Agent 生命周期管理launch / restart / shutdown
- 健康检查循环(心跳间隔 15s
- 自动快照循环(快照间隔 10m
- 故障恢复:失败 MaxRetries(3) 次后自动回滚到最近快照
- 网络监控:检查 LLM API 可达性,影响 Agent 健康状态
### 2.8 快照与回滚(`internal/snapshot/manager.go`
基于 Docker commit/save/load 的版本管理。
**快照流程:**
`Docker Commit(container → image) → SaveImage(image → .tar) → 记录快照元数据`
**回滚流程:**
`Stop(container) → Remove(container) → LoadImage(.tar) → 创建新容器 → Start`
**策略:**
- 定时快照(每 10m
- 操作前快照PreAction — 可选)
- 限制最大保留(默认 20 个)
- 溢出时自动删除最旧的
### 2.9 网络监控(`internal/network/monitor.go`
异步定时检查 LLM API 端点可达性。
**监控方式:**
- HTTP HEAD 请求到配置的 endpoints
- 并发检查goroutine per endpoint
- DNS 解析检查fallback: google.com → baidu.com
- 结果聚合:`LLMAPIReachable` + `DNSResolving` + 平均延迟
**影响:**
- 网络不可达 → Agent 状态变为 `HealthDegraded`
- 持续不可达 → 触发回滚策略
### 2.10 Lua 虚拟机(`internal/lua/vm.go`
纯 Go 的 gopher-lua 5.1 VM用于 API 格式适配器。
**功能:**
- 加载 `{adapterDir}/*.lua` 适配器脚本
- `CallTransform(name, input)` — 调用 adapter 的 transform_request
- `CallResponseTransform(name, raw)` — 调用 adapter 的 transform_response
- `ReloadAll()` — 热重载所有适配器
- 内置 mock 函数:`log()`, `json_encode()`, `http_get()`, `http_post()`
### 2.11 Embedder`internal/embed/embedder.go`
向量嵌入接口,支持文本相似度计算。
| 实现 | 说明 |
|------|------|
| `OllamaEmbedder` | 通过 Ollama API 获取嵌入向量(默认: nomic-embed-text, 768d |
| `HashEmbedder` | 基于字符哈希的本地嵌入(无需外部依赖),用于备选方案 |
**可用性:** Embedder 已定义但尚未集成到记忆系统中。
### 2.12 Tokenizer`internal/tokenizer/jieba.go`
中文分词工具,基于 gojieba。全局单例线程安全。
**功能:**
- `ExtractKeywords(text, topK)` — 提取关键词TF-IDF 加权)
- `Cut(text)` — 分词
- `Tag(text)` — 词性标注
---
## 三、数据流全景
### 3.1 正常交互流程
Overlayfs 文件变更追踪:
```
外部输入HTTP POST / voice / GPIO 事件)
IOManager.InjectInput()
→ InputEvent{Source, Type, Payload}
→ 推入 inputCh
Agent Core eventLoop()
1. consume InputEvent
2. Indexer.BuildContext(input) → 记忆摘要(仅索引+摘要)
3. buildSystemPrompt() → 拼接 system + 记忆 + 技能 + 工具
4. buildToolDefs() → IO 工具 + 记忆工具
5. Provider.Chat(req) → LLM 响应
6. EmitOutput(target, "text", response)
7. EmitOutput("memory", "memory_candidate", {input, response})
┌─────┴─────┐
▼ ▼
IO 输出 Memory Pipeline异步
│ │
▼ ▼
HTTP Append() → 周期蒸馏
Response → GraphDB.Commit()
Speaker → 清理原始文件
PreAction(tool) → 记录文件 hash
PostAction(tool) → diff → ChangeSet{ID, Tool, Files[]SHA256}
Rollback() → 用 overlayfs 下层恢复
```
### 3.2 记忆查询流程
健康检查失败 → `tracker.Rollback()`
### 2.9 Supervisor 守护进程
```
Agent 推理中决定调用 memory_recall
LLM 返回 tool_call: {name: "memory_recall", args: {query_intent: "..."}}
Agent Core 解析 tool_call → 调用 GraphDB.Recall(keywords)
返回实体+关系数据 → 注入后续 LLM 请求上下文
Daemon:
├─ SetTracker() → 绑定变更追踪器
├─ RegisterAgent("main") → 注册主 agent
└─ 健康检查周期 → LLM API 可达性
└─ 连续失败 → tracker.Rollback()
```
---
## 四、配置系统
配置文件: `/etc/homeagent/config.yaml`YAML。若文件不存在则使用默认配置。
## 三、配置
```yaml
daemon:
@ -380,116 +229,56 @@ daemon:
data_dir: "/var/lib/homeagent"
heartbeat_interval: 15s
check_interval: 30s
log_level: "info"
defaults:
image: "homeagent/agent-base:latest"
llm_endpoints:
- "https://api.openai.com/v1"
snapshot_policy:
interval: 10m
max_snapshots: 20
pre_action: true
post_action: false
rollback_policy:
max_retries: 3
health_threshold: 3
cooldown_period: 30s
auto_rollback: true
openclaw_enabled: true
agents: []
llm:
provider: "openai"
model: "deepseek-v4-flash"
base_url: "https://api.deepseek.com/v1"
api_key: "${DEEPSEEK_API_KEY}"
temperature: 0.7
max_tokens: 4096
```
---
## 四、数据目录
```
{dataDir}/
├── memory/
│ ├── graph.db # SQLite 图数据库
│ ├── text/ # JSONL 文本记忆
│ ├── documents/ # JSON 文档记忆
│ └── raw/ # 原始记录(蒸馏后删除)
├── knowledge/ # 知识库
│ └── {name}/content.md
├── plugins/ # 插件
│ └── {name}/
│ ├── SKILL.md
│ └── skill.json
├── skills/ # 技能
├── adapters/ # Lua 适配器
├── personal/ # 人格
│ └── personal.md
├── changesets/ # 变更追踪记录
└── snapshots/ # 快照
```
## 五、HTTP API
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/v1/chat/completions` | OpenAI 兼容对话 |
| GET | `/api/v1/knowledge` | 知识查询/列表 |
| POST | `/api/v1/knowledge` | 创建知识 |
| DELETE | `/api/v1/knowledge?name=` | 删除知识 |
| GET | `/api/v1/status` | 系统状态 |
| GET | `/api/v1/agents` | Agent 列表 |
| POST | `/api/v1/agents` | 创建 Agent |
| GET | `/api/v1/agents/{id}` | Agent 详情 |
| POST | `/api/v1/agents/{id}/start/stop/restart` | 操作 |
| GET | `/api/v1/agents/{id}/snapshots` | 快照列表 |
| POST | `/api/v1/agents/{id}/rollback/{snap}` | 回滚 |
| GET | `/api/v1/memory?q=关键词` | 记忆检索 |
| POST | `/api/v1/memory` | 写入三元组 |
| DELETE | `/api/v1/memory` | 删除记忆 |
| GET | `/api/v1/memory/context?q=...` | 获取上下文注入 |
| GET | `/api/v1/memory/tools` | 记忆工具定义 |
| GET | `/api/v1/skills` | 技能列表 |
| POST | `/api/v1/skills` | 安装技能 |
| DELETE | `/api/v1/skills?name=...` | 卸载技能 |
| GET | `/api/v1/adapters` | 适配器列表 |
| POST | `/api/v1/adapters` | 安装适配器 |
| DELETE | `/api/v1/adapters/{name}` | 删除适配器 |
| GET | `/api/v1/network` | 网络状态 |
| GET | `/api/v1/config` | 配置查看 |
| PUT | `/api/v1/config` | 配置更新 |
| GET | `/` | WebUI 仪表盘 |
---
## 六、数据目录结构
```
{dataDir}/
├── config.yaml # 系统配置
├── memory/
│ ├── graph.db # SQLite 图记忆数据库
│ └── raw/ # 原始会话记录文件
│ └── raw_*.jsonl
├── skills/ # 安装的技能
│ └── {name}/
│ ├── SKILL.md
│ └── skill.json
├── adapters/ # Lua API 格式适配器
│ ├── openai.lua
│ ├── deepseek.lua
│ ├── ollama.lua
│ └── custom.lua
└── snapshots/ # Docker 快照
└── {agent_id}/
└── snap_*.tar
```
---
## 七、构建与部署
**构建:**
## 六、构建与部署
```bash
make build # 编译主二进制~15MB
make install # 编译 + 安装到 /usr/local/bin
make build # 编译主二进制
make run # 编译 + 启动(数据 /tmp/homeagent
make install # 安装到系统
make test # 运行测试
make fmt # gofmt
make lint # golangci-lint
```
**部署:**
- 单二进制:`homed -config /etc/homeagent/config.yaml -data /var/lib/homeagent`
- systemd`deploy/homeagent.service`
- 依赖Go 1.19+CGo enabled用于 go-sqlite3Docker 可选(快照/回滚)
---
## 八、设计限制与后续计划
### 已知限制
1. Agent Core 目前是单轮 tool_call 处理,尚未实现完整的多轮 tool 执行循环
2. Embedder 已定义但未接入图记忆 — 缺失向量相似度排序和 ANN 索引
3. 记忆蒸馏器使用简单启发式三元组提取,生产环境应调用 LLM 进行结构化抽取
4. Linux namespace 隔离overlayfs尚未实现作为 Docker 替代方案
5. Speaker / Microphone / Camera 均为桩实现,无实际 ALSA/PulseAudio/Video4Linux 驱动
6. 无真正的唤醒词检测
7. LuaAdaptedProvider 失败时无重试/降级逻辑
8. 无持久化消息历史管理(目前仅内存中保留最近 50 条)
### 路线图
- **近期**:完成 tool_call 执行循环 → 设备真正驱动 → 向量记忆增强
- **中期**:单二进制 namespace 隔离 → 唤醒词检测 → CI/CD 流水线
- **远期**:多 Agent 协作 → 分布式部署 → 联邦记忆
依赖Go 1.19+ (CGo enabled for go-sqlite3)。单二进制部署。