mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-21 09:28:14 +00:00
v4 architecture: pipeline stages, SDK, event bus, LLM-driven memory consolidation
- SDK PluginAPI (internal/plugin/sdk/): RegisterTool/RegisterStage/Subscribe/Publish - EventBus (internal/events/): system-level pub/sub with wildcard support - StageHost (internal/agent/core/stages.go): 7-stage message pipeline - Agent core: on_input/pre_action/post_action/before_toolcall/after_toolcall/before_output/after_output - Plugin Registry: SDK plugin registration and tool routing - GraphDB.MergeEntities: entity consolidation with relation redirection - memory_merge tool: allows LLM to merge similar entities - Consolidation task: heartbeat detects conflicts, enqueues via IO for LLM decision - _consolidation_ internal channel for system-level memory maintenance - Comprehensive documentation: ARCHITECTURE.md, PLAN.md, DESIGN.md, README.md - 54 tests across all packages, all passing
This commit is contained in:
@ -1,284 +1,540 @@
|
||||
# HomeAgent 架构参考
|
||||
# HomeAgent 架构设计 v4
|
||||
|
||||
> 基于 NextAgent 认知解耦架构 + TrulyMEM 自主图记忆 + OneBot 协议。
|
||||
## 一、核心理念
|
||||
|
||||
## 一、分层架构
|
||||
24 小时陪伴用户的智能管家。**单会话·单 Agent**,身份不漂移。
|
||||
|
||||
### 设计原则
|
||||
- **核心零 IO** — Core 没有任何硬编码 IO 能力,所有 IO 来自插件
|
||||
- **所有输出是工具调用** — Agent 必须显式调用 `output_send` 才能通信,推理不自动路由
|
||||
- **所有 LLM 调用走 Provider 接口** — 不直连 API
|
||||
- **DeepSeek v4 flash** 为默认 LLM,thinking 模式关闭
|
||||
- **人格固定**(personal.md),记忆分层管理防止性格突变
|
||||
- **知识独立于记忆**,agent 主动学习
|
||||
- **插件 = 三通道**:工具、阶段钩子、事件订阅
|
||||
|
||||
---
|
||||
|
||||
## 二、核心域 vs 插件域
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ IO 抽象层(唯一输入路径) │
|
||||
│ Device(Mic/Speaker/Camera/GPIO/OneBot-QQ/PluginDevice) │
|
||||
│ 所有外部输入 → InputEvent → inputCh │
|
||||
│ 输出通道: 能力声明(text/file/image/audio/structured) │
|
||||
│ 路由表: 输入源 → 默认输出通道 │
|
||||
└──────────────────────────┬───────────────────────────────┘
|
||||
│
|
||||
┌──────────────────────────▼───────────────────────────────┐
|
||||
│ Agent 操作层(核心编排器) │
|
||||
│ eventLoop() consume inputCh │
|
||||
│ ├─ RelevanceContext(TF-IDF 相关性管理) │
|
||||
│ ├─ 工具循环(Provider.Chat → tool_calls → Execute → ...) │
|
||||
│ ├─ 记忆索引注入(图索引 + 文档摘要) │
|
||||
│ └─ 心跳蒸馏(30min:向量同步 + 冷归档 + 同义合并) │
|
||||
└──────────────────────────┬───────────────────────────────┘
|
||||
│
|
||||
┌──────────────────────────▼───────────────────────────────┐
|
||||
│ API 抽象层(唯一输出路径) │
|
||||
│ Provider.Chat() → DeepSeek API / OpenAI / Ollama │
|
||||
│ LuaAdapter 做请求/响应格式转换 │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ 核心域 (Core Domain) │
|
||||
│ │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌───────────────────┐ │
|
||||
│ │ Provider │ │ Memory │ │Knowledge │ │ Pipeline Stages │ │
|
||||
│ │ (LLM) │ │ (T/D/G) │ │ (TF-IDF) │ │ 编排器 │ │
|
||||
│ └──────────┘ └──────────┘ └──────────┘ └───────────────────┘ │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────────────────────────────┐ │
|
||||
│ │ Relevance│ │ Context │ │ Plugin Host │ │
|
||||
│ │ Tf-Idf │ │ Persist │ │ (调用钩子 + 路由工具) │ │
|
||||
│ └──────────┘ └──────────┘ └──────────────────────────────────┘ │
|
||||
│ 核心无任何 IO 能力 │
|
||||
├──────────────────────────────────────────────────────────────────┤
|
||||
│ 边界 (Plugin API) │
|
||||
│ ┌──────────────────────────────────────────────────────────────┐ │
|
||||
│ │ RegisterTool(name, handler) ← 插件注册工具给 LLM │ │
|
||||
│ │ RegisterStage(stage, handler) ← 插件挂入消息处理阶段 │ │
|
||||
│ │ Subscribe(eventType, handler) ← 插件订阅系统事件 │ │
|
||||
│ │ Publish(event) → 插件发布事件 │ │
|
||||
│ └──────────────────────────────────────────────────────────────┘ │
|
||||
├──────────────────────────────────────────────────────────────────┤
|
||||
│ 插件域 (Plugin Domain) │
|
||||
│ │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
|
||||
│ │ WebUI │ │ QQ │ │OutputBus │ │ 未来插件: …… │ │
|
||||
│ │ HTTP/WS │ │ OneBot │ │通道管理 │ │ │ │
|
||||
│ └──────────┘ └──────────┘ └──────────┘ └──────────────────┘ │
|
||||
│ 所有 IO 都在这里 │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**三条核心规则:**
|
||||
1. **所有外部输入** → 必须通过 `IOManager.InjectInput()` / `InjectText()` 注入
|
||||
2. **所有 LLM 调用** → 必须通过 `Provider.Chat()` 发出
|
||||
3. **Agent 不直接操作记忆系统**,只发射 `memory_candidate` 事件,由 Memory Pipeline 异步消费
|
||||
### 核心职责
|
||||
- LLM 调用编排(Provider → Agent 工具循环)
|
||||
- 三层记忆管理(Context → Document → Graph)
|
||||
- 知识库维护(Knowledge Store)
|
||||
- 阶段管道编排(Stage Pipeline)
|
||||
- Context 相关性管理(TF-IDF 余弦相似度)
|
||||
- 心跳蒸馏 + 图重整
|
||||
|
||||
## 二、子系统详解
|
||||
### 插件职责
|
||||
- 接收外部输入(WebSocket、HTTP、硬件等)
|
||||
- 提供输出能力(文本发送、文件传输等)
|
||||
- 干预消息处理流(阶段钩子)
|
||||
- 观察系统状态(事件订阅)
|
||||
|
||||
### 2.1 IO 抽象层(唯一输入路径)
|
||||
---
|
||||
|
||||
所有外部输入必须通过此层进入系统。
|
||||
## 三、消息处理阶段管道
|
||||
|
||||
```
|
||||
Device(Microphone) ─┐
|
||||
Device(OneBot-QQ) ─┤
|
||||
Device(Plugin) ─┤──→ IOManager → InputEvent → inputCh → Agent
|
||||
Device(GPIO) ─┤
|
||||
HTTP API ─┘
|
||||
┌──────────────────────────────────────────┐
|
||||
│ on_input │
|
||||
│ 消息到达,Agent 未做任何处理 │
|
||||
│ └→ 插件可鉴权/拉黑/改写/短路回复 │
|
||||
└──────────┬───────────────────────────────┘
|
||||
│ 通过
|
||||
┌──────────▼───────────────────────────────┐
|
||||
│ 内部:Context Append + Memory Recall │
|
||||
│ + Context 组装 │
|
||||
└──────────┬───────────────────────────────┘
|
||||
│ 就绪
|
||||
┌──────────▼───────────────────────────────┐
|
||||
│ pre_action │
|
||||
│ 上下文已就绪,即将调用 LLM │
|
||||
│ └→ 插件可注入 system 消息 / 修改 context │
|
||||
└──────────┬───────────────────────────────┘
|
||||
│ LLM 调用
|
||||
┌──────────▼───────────────────────────────┐
|
||||
│ post_action │
|
||||
│ LLM 返回文本 + 工具调用列表 │
|
||||
│ └→ 插件可审查/修改文本、增删工具调用 │
|
||||
└──────────┬───────────────────────────────┘
|
||||
│ 判断有无工具调用
|
||||
╱─────────────┴─────────────╲
|
||||
有工具调用 无工具调用
|
||||
│ │
|
||||
┌──────────▼──────────────┐ │
|
||||
│ before_toolcall │ │
|
||||
│ 即将执行某个工具调用 │ │
|
||||
│ └→ 插件可拒绝/放行/ │ │
|
||||
│ 修改参数/审计 │ │
|
||||
└──────────┬──────────────┘ │
|
||||
│ 执行工具 │
|
||||
┌──────────▼──────────────┐ │
|
||||
│ after_toolcall │ │
|
||||
│ 工具执行完毕,准备喂回 │ │
|
||||
│ └→ 插件可脱敏/改写结果 │ │
|
||||
└──────────┬──────────────┘ │
|
||||
│ 回到 post_action 继续循环 │
|
||||
└──────────────────────────────┘
|
||||
│
|
||||
┌──────────────────────────┘
|
||||
▼
|
||||
┌──────────────────────────────────────────┐
|
||||
│ before_output │
|
||||
│ 最终文本就绪,即将调用 output_send │
|
||||
│ └→ 插件可改写回复/添加格式/适配渠道 │
|
||||
└──────────────────┬───────────────────────┘
|
||||
│ output_send 调用
|
||||
┌──────────────────▼───────────────────────┐
|
||||
│ after_output │
|
||||
│ 输出完成 │
|
||||
│ └→ 记录/统计/清理资源 │
|
||||
└──────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**核心类型:**
|
||||
### 7 个阶段总表
|
||||
|
||||
| 类型 | 说明 |
|
||||
|------|------|
|
||||
| `InputEvent` | Source + Type + Payload — 所有外部输入的标准化格式 |
|
||||
| `OutputEvent` | Target + Type + Payload + OutputChannel — 输出路由 |
|
||||
| `Device` | 接口:Name/Type/Description/Tools/Execute/Start/Stop/OutputCapabilities |
|
||||
| `DeviceType` | Input / Output / IO |
|
||||
| `ToolDef` | Name + Description + Parameters + Handler — 与 LLM 函数调用同构 |
|
||||
| `OutputCapability` | 位掩码:text, file, image, audio, structured |
|
||||
| 阶段 | 触发时机 | 插件读写权限 | 典型用途 |
|
||||
|---|---|---|---|
|
||||
| `on_input` | 消息到 Agent,零处理 | 可读写 `raw_message`,可设置 `response` 短路 | 黑名单、限流、自定义指令前缀 |
|
||||
| `pre_action` | Memory+Context 就绪,LLM 调用前 | 可读写 `context_messages`(追加/修改) | 注入 RAG 结果、插入时政 context |
|
||||
| `post_action` | LLM 返回文本 + 工具调用列表 | 可读写 `llm_text`、`tool_calls`、`context_messages` | 敏感词过滤、强制 redirect 工具 |
|
||||
| `before_toolcall` | 单个工具调用执行前 | 可读写 `tool_call.name`、`tool_call.args`,设置 `deny=true` 拒绝 | 审计高危操作、OS 命令白名单 |
|
||||
| `after_toolcall` | 单个工具执行完毕 | 可读写 `tool_result` | 脱敏数据库结果、排序搜索结果 |
|
||||
| `before_output` | 最终文本就绪,output_send 前 | 可读写 `final_text`,可设置 `skip_output=false` | 添加表情/at 前缀、多平台格式适配 |
|
||||
| `after_output` | output_send 已调用 | 只读 `final_text` | 统计日志、触发后续流程 |
|
||||
|
||||
**内置设备:**
|
||||
### 循环规则
|
||||
|
||||
| 设备 | 方向 | 工具 |
|
||||
|------|------|------|
|
||||
| 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 |
|
||||
`post_action → before_toolcall → after_toolcall → 回到 post_action` 构成**内循环**。Agent 在以下条件退出循环进入 `before_output`:
|
||||
- LLM 返回纯文本(无工具调用)
|
||||
- `before_toolcall` 拒绝所有剩余工具且 LLM 无可执行工具
|
||||
- 循环超过 `max_tool_rounds` 上限
|
||||
|
||||
**输出通道能力校验:**
|
||||
- 每个 Device 声明 `OutputCapabilities()` → 位掩码
|
||||
- `output_send` 工具发送前校验通道是否支持文本
|
||||
- `output_list_channels` 只列出有输出能力的通道
|
||||
|
||||
### 2.2 API 抽象层(唯一输出路径)
|
||||
### 短路规则
|
||||
|
||||
每个阶段插件都可设置 `ctx.Response`,一旦设置管道立即短路到 `after_output`:
|
||||
```
|
||||
Agent Core → Provider.Chat()
|
||||
│
|
||||
┌───────┴───────┐
|
||||
▼ ▼
|
||||
OpenAIProvider LuaAdapter
|
||||
(DeepSeek API) (格式转换)
|
||||
on_input → ctx.Response = "hello" → 跳过后面的所有阶段 → after_output
|
||||
```
|
||||
|
||||
| 实现 | 说明 |
|
||||
|------|------|
|
||||
| `OpenAIProvider` | 标准 OpenAI API 格式,DeepSeek v4 flash 默认 |
|
||||
| `LuaAdaptedProvider` | 通过 Lua 脚本转换请求/响应的适配 wrapper |
|
||||
---
|
||||
|
||||
Lua 适配器位于 `{dataDir}/adapters/*.lua`,每个适配器返回 `name` + `transform_request` + `transform_response`。
|
||||
|
||||
### 2.3 Agent 操作层(核心编排器)
|
||||
## 四、记忆体系(三层递进)
|
||||
|
||||
```
|
||||
eventLoop() → select on inputCh
|
||||
输入消息
|
||||
│
|
||||
▼
|
||||
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
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Layer 1: Context (RelevanceContext) │
|
||||
│ 内存中维护最近 topK 条事件,TF-IDF 评分,JSON 持久化 │
|
||||
│ 每次 Append/Prune → save() 防崩溃丢数据 │
|
||||
│ keep=30 条活跃,多余 → 归档到 Document │
|
||||
└────────────────────┬────────────────────────────────┘
|
||||
│ Prune 时
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Layer 2: Document (document.Store) │
|
||||
│ 文件系统 JSON + TF-IDF 向量索引 │
|
||||
│ 冷文档(72h 未访问 + access ≤ 2)→ 蒸馏到 Graph │
|
||||
│ 也可以由用户主动 commit(doc_commit 工具) │
|
||||
└────────────────────┬────────────────────────────────┘
|
||||
│ reorg 心跳
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Layer 3: Graph (GraphDB + Indexer) │
|
||||
│ SQLite: entities + relations │
|
||||
│ Entity: name, type, mention_count │
|
||||
│ Relation: source → target, relation_type, confidence│
|
||||
│ 搜索: 关键词 → 向量搜索实体 → BFS 遍历邻居 │
|
||||
│ 蒸馏: 原始记录 → Distiller → 三元组提交 │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**人格注入:** personal.md 加载一次,固定在 system prompt 最前,永不漂移。
|
||||
|
||||
**工具分发:**
|
||||
```
|
||||
executeToolCall(tc)
|
||||
├─ memory_* → executeMemoryTool
|
||||
├─ knowledge_* → executeKnowledgeTool
|
||||
├─ doc_* → executeDocTool
|
||||
├─ output_* → executeOutputChannel/Send/ListChannels
|
||||
└─ 其他 → io.ExecuteTool → Device.Execute
|
||||
```
|
||||
|
||||
### 2.4 记忆系统
|
||||
|
||||
三层分级,自顶向下逐渐持久化、抽象化:
|
||||
### 数据流关系
|
||||
|
||||
```
|
||||
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 同义合并
|
||||
Context 修剪 → Document 归档 → reorg 心跳 → Graph 消化
|
||||
↑
|
||||
Distiller (原始记录 → 三元组)
|
||||
```
|
||||
|
||||
### 2.5 知识系统
|
||||
### 工具入口(Agent 暴露给 LLM)
|
||||
|
||||
- `memory_recall(query)` → 从 Graph 召回
|
||||
- `memory_commit(triples)` → 写入 Graph
|
||||
- `memory_introspect()` → 查看统计
|
||||
- `doc_query(query)` → 从 Document 搜索
|
||||
- `doc_commit(title, content)` → 写入 Document
|
||||
|
||||
---
|
||||
|
||||
## 五、知识体系
|
||||
|
||||
独立于记忆,agent 主动学习。
|
||||
|
||||
```
|
||||
knowledge/{category}/content.md
|
||||
knowledge/<name>/
|
||||
content.md
|
||||
|
||||
knowledge.Store
|
||||
└─ TF-IDF 向量索引 (character bigram)
|
||||
└─ 独立于 memory 的 vector.Store 实例
|
||||
└─ Start() 时扫描目录训练索引
|
||||
└─ Add(name, content) 时增量更新
|
||||
```
|
||||
|
||||
### 工具入口
|
||||
|
||||
- `knowledge_search(query)` → 向量搜索
|
||||
- `knowledge_create(name, content)` → 新增
|
||||
- `knowledge_list()` → 列出所有
|
||||
|
||||
### 为什么独立于 memory?
|
||||
|
||||
- Memory 是 LLM 的"对话记忆"——谁说过什么、上下文
|
||||
- Knowledge 是 LLM 的"知识库"——外部注入的固定知识
|
||||
- 两者 TF-IDF 索引实例隔离,不互相污染
|
||||
|
||||
---
|
||||
|
||||
## 六、三通道插件交互
|
||||
|
||||
```
|
||||
插件 ──→ 核心 核心 ──→ 插件
|
||||
──────────────────────────────────────────────────
|
||||
RegisterTool(name, fn) ──→ buildToolDefs()
|
||||
executeToolCall() → fn
|
||||
(Tracker 自动包裹 Pre/PostAction)
|
||||
|
||||
RegisterStage(stage, fn) ──→ runStage() 在对应阶段调用 fn(ctx)
|
||||
返回后检查 ctx.Response 决定是否短路
|
||||
|
||||
Subscribe(eventType, fn) ──→ Publish(event)
|
||||
所有订阅者收到(观察型)
|
||||
```
|
||||
|
||||
### 通道对比
|
||||
|
||||
| 通道 | 方向 | 用途 | 可否拦截 |
|
||||
|---|---|---|---|
|
||||
| **工具** (RegisterTool) | 插件→核心→LLM | LLM 主动调用插件功能 | 否 |
|
||||
| **阶段** (RegisterStage) | 核心→插件 | 核心触发插件干预消息流 | 是(response 短路) |
|
||||
| **事件** (Subscribe/Publish) | 双方向 | 审计/日志/状态通知 | 否 |
|
||||
|
||||
---
|
||||
|
||||
## 七、Agent 内部完整流程
|
||||
|
||||
```
|
||||
processTextInput(input)
|
||||
│
|
||||
├── on_input stage ────────────── 插件可拦截/改写
|
||||
│
|
||||
├── context.Append(input)
|
||||
├── context.Prune(input) → 归档到 Document
|
||||
├── buildMemoryContext() → Indexer.BuildContext → Graph Recall
|
||||
│
|
||||
├── pre_action stage ──────────── 插件可注入 context
|
||||
│
|
||||
├── [循环] process(input)
|
||||
│ ├── buildSystemPrompt (人格+记忆+技能+上下文)
|
||||
│ ├── buildToolDefs (内置工具 + 插件工具)
|
||||
│ ├── provider.Chat() → LLM
|
||||
│ │
|
||||
│ ├── post_action stage ─────── 插件可见 LLM 输出 + 工具列表
|
||||
│ │
|
||||
│ ├── 有工具调用?
|
||||
│ │ ├── 每个工具:
|
||||
│ │ │ ├── before_toolcall stage ── 插件可拒绝/改参
|
||||
│ │ │ ├── Tracker.PreAction
|
||||
│ │ │ ├── executeToolCall() ──── 路由到插件或内置
|
||||
│ │ │ ├── Tracker.PostAction
|
||||
│ │ │ └── after_toolcall stage ── 插件可改结果
|
||||
│ │ └── → 回到 post_action (继续循环)
|
||||
│ │
|
||||
│ └── 无工具调用 → 退出循环
|
||||
│
|
||||
├── context.Append(response)
|
||||
├── before_output stage ───────── 插件可改写最终文本
|
||||
├── Publish(agent_output event)
|
||||
├── output_send (调用插件注册的 output 工具)
|
||||
│
|
||||
└── after_output stage ────────── 插件只读,做统计/日志
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 八、记忆整理(心跳 LLM 驱动消歧)
|
||||
|
||||
图数据库在长期运行中会积累**同义实体**(如「张三」与「张先生」指同一人)和**矛盾关系**。心跳流程如下:
|
||||
|
||||
### 流程
|
||||
|
||||
```
|
||||
心跳 tick (30min)
|
||||
│
|
||||
├── distillContext() — 蒸馏上下文
|
||||
├── syncGraphToDocs() — 图→文档
|
||||
│
|
||||
└── reorgGraph()
|
||||
├── Indexer.Sync() — 图→向量(自动)
|
||||
├── DocStore.Reindex() — 文档重建索引(自动)
|
||||
├── 冷文档→图归化 — 将冷文档归档为图三元组(自动)
|
||||
│
|
||||
└── 实体冲突检测 → 发现相似实体对
|
||||
│ 如:「张三」(person, 5次) vs 「张先生」(person, 3次) 相似度 0.75
|
||||
│
|
||||
▼
|
||||
enqueueConsolidationTask()
|
||||
│ 通过 IO 层注入 Agent 输入队列
|
||||
│ channel = "_consolidation_"(内部通道,不对外输出)
|
||||
▼
|
||||
Agent 处理 (processConsolidation)
|
||||
│ 如同普通用户消息,走完整 LLM 工具循环
|
||||
│ 但输出仅写记忆,不发外部通道
|
||||
▼
|
||||
LLM 决策:
|
||||
├─ 判断为同一实体 → 调用 memory_merge 合并
|
||||
│ → "已将「张先生」合并到「张三」,3 条关系已重定向"
|
||||
├─ 判断为不同实体 → 回复"跳过"
|
||||
└─ 不确定 → 回复"待定,需更多上下文"
|
||||
```
|
||||
|
||||
### 关键设计
|
||||
|
||||
| 特性 | 说明 |
|
||||
|---|---|
|
||||
| **启发式检测,LLM 决策** | bigram Jaccard 仅做候选筛选(低门槛 0.5),LLM 做最终判断 |
|
||||
| **走 IO 输入队列** | 不阻塞心跳,不抢占用户输入,享受完整 Agent 上下文 |
|
||||
| **`_consolidation_` 通道** | 内部专用通道,输出只写记忆层,不被外部插件路由 |
|
||||
| **`memory_merge` 工具** | LLM 通过此工具执行合并,自动重定向关系 + 累积 mention_count |
|
||||
| **异步非阻塞** | 整理任务排队在 inputCh 尾部,Agent 按序处理,不影响用户体验 |
|
||||
|
||||
### 类比
|
||||
|
||||
类似人类睡眠时大脑的海马体回放——白天经历的记忆在休息时被自发整理、关联、去重。HomeAgent 的心跳就是它的"睡眠周期",而 LLM 的参与相当于前额叶皮层执行语义判断。类比:
|
||||
|
||||
```diff
|
||||
- 人类: 白天经历 → 海马体暂存 → 睡眠 → 前额叶整理 → 长期记忆
|
||||
+ Agent: 用户交互 → Context缓存 → 心跳 → LLM 消歧 → GraphDB 存储
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 九、Child Agent
|
||||
|
||||
不走阶段管道,独立轻量 Agent:
|
||||
|
||||
```
|
||||
spawn_child(task) → 新建轻量 Agent
|
||||
├── 独立 system prompt(仅有任务描述)
|
||||
├── 仅 output_send 工具
|
||||
├── 无 persistent memory
|
||||
├── 无 Graph/Document 访问
|
||||
├── 上限 5 轮工具循环
|
||||
└── 销毁时返回结果文本
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十、SDK API 定义
|
||||
|
||||
### PluginAPI (`internal/plugin/sdk/api.go`)
|
||||
|
||||
```go
|
||||
type PluginAPI struct {
|
||||
Name string
|
||||
Version string
|
||||
}
|
||||
|
||||
func NewPluginAPI(name, version string, bus EventBus, mem MemoryAPI, know KnowledgeAPI) *PluginAPI
|
||||
|
||||
// 三通道
|
||||
func (p *PluginAPI) RegisterTool(name string, handler ToolHandler) error
|
||||
func (p *PluginAPI) RegisterStage(stage Stage, handler StageHandler)
|
||||
func (p *PluginAPI) Subscribe(eventType EventType, handler EventHandler)
|
||||
func (p *PluginAPI) Publish(evt *Event)
|
||||
|
||||
// 访问子系统的快捷方式
|
||||
func (p *PluginAPI) Memory() MemoryAPI
|
||||
func (p *PluginAPI) Knowledge() KnowledgeAPI
|
||||
```
|
||||
|
||||
### 阶段上下文 (`StageContext`)
|
||||
|
||||
```go
|
||||
type StageContext struct {
|
||||
RawMessage string // 当前输入(可改写 on_input)
|
||||
UserID string
|
||||
GroupID string
|
||||
ContextMsgs []map[string]interface{} // 可注入的消息
|
||||
LLMText string // LLM 返回文本(可改写 post_action)
|
||||
ToolCalls []ToolCall // 工具调用列表(可增删 post_action/before_toolcall)
|
||||
ToolResults []ToolResult // 工具执行结果(可改写 after_toolcall)
|
||||
FinalText string // 最终输出文本(可改写 before_output)
|
||||
Response *string // 设置后短路管道
|
||||
Phase Stage // 当前阶段
|
||||
Memory []MemItem // 召回的记忆
|
||||
Extra map[string]interface{} // 扩展字段
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十一、事件系统
|
||||
|
||||
### 事件类型
|
||||
|
||||
| 类型 | 发布时机 | 用途 |
|
||||
|---|---|---|
|
||||
| `raw_input` | 消息到达 Agent | 记录输入日志 |
|
||||
| `agent_output` | 最终输出发送后 | 记录输出日志 |
|
||||
| `tool_call` | 每个工具调用完成 | 审计工具调用 |
|
||||
| `reasoning` | LLM 推理文本 | 展示推理过程 |
|
||||
| `system` | 系统状态变更 | 健康检查、插件变更 |
|
||||
|
||||
### Event Bus (`internal/events/bus.go`)
|
||||
|
||||
```go
|
||||
type Bus struct{}
|
||||
func NewBus() *Bus
|
||||
func (b *Bus) Publish(event *Event)
|
||||
func (b *Bus) Subscribe(eventType EventType, handler Handler) func()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十二、数据流全景
|
||||
|
||||
```
|
||||
外部 (QQ/HTTP/硬件)
|
||||
│ 通过插件
|
||||
▼
|
||||
IOManager.InjectInput() → inputCh
|
||||
│
|
||||
▼
|
||||
TF-IDF 向量索引(字符 bigram)
|
||||
Agent.eventLoop() → handleInput → processTextInput
|
||||
│
|
||||
▼
|
||||
工具: knowledge_search / knowledge_create / knowledge_list
|
||||
├── 1. on_input stage(插件可拦截)
|
||||
├── 2. Context.Append
|
||||
├── 3. Memory Recall (Indexer → Graph)
|
||||
├── 4. pre_action stage(插件可注入)
|
||||
├── 5. 工具循环 (最多 10 轮)
|
||||
│ LLM → post_action → [before_toolcall → 执行 → after_toolcall] → LLM ...
|
||||
├── 6. Context.Append(response)
|
||||
├── 7. Prune(不相关 → Document)
|
||||
├── 8. before_output stage(插件可改写)
|
||||
├── 9. EmitOutput (通过 output_send 到对应通道)
|
||||
├── 10. after_output stage(插件只读)
|
||||
└── 11. memory_candidate → TextMemory + Distiller → GraphDB
|
||||
|
||||
心跳(30min):
|
||||
├── distillContext()
|
||||
├── syncGraphToDocs()
|
||||
└── reorgGraph()
|
||||
├── Indexer.Sync() — 图→向量(自动)
|
||||
├── DocStore.Reindex() — 文档向量重建(自动)
|
||||
├── 冷文档→图归化(自动)
|
||||
└── 实体冲突检测 → 走 LLM 消歧(详见第八章)
|
||||
```
|
||||
|
||||
### 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 Client(internal/onebot/)
|
||||
├─ 事件: message/notice/request → IO InputEvent
|
||||
├─ 动作: send_private_msg / send_group_msg / get_group_info / ...
|
||||
├─ 自动重连(指数退避 1s→30s)
|
||||
└─ 事件 handler 注入 IO 层
|
||||
│
|
||||
▼
|
||||
Device(internal/onebot/device.go)
|
||||
├─ Tools: qq_send_private_msg, qq_send_group_msg, etc.
|
||||
├─ Execute → Client.SendAction
|
||||
└─ Start → Connect + 注册事件 handler
|
||||
```
|
||||
|
||||
### 2.8 变更追踪器
|
||||
|
||||
Overlayfs 文件变更追踪:
|
||||
|
||||
```
|
||||
PreAction(tool) → 记录文件 hash
|
||||
PostAction(tool) → diff → ChangeSet{ID, Tool, Files[]SHA256}
|
||||
Rollback() → 用 overlayfs 下层恢复
|
||||
```
|
||||
|
||||
健康检查失败 → `tracker.Rollback()`。
|
||||
|
||||
### 2.9 Supervisor 守护进程
|
||||
|
||||
```
|
||||
Daemon:
|
||||
├─ SetTracker() → 绑定变更追踪器
|
||||
├─ RegisterAgent("main") → 注册主 agent
|
||||
└─ 健康检查周期 → LLM API 可达性
|
||||
└─ 连续失败 → tracker.Rollback()
|
||||
```
|
||||
|
||||
## 三、配置
|
||||
|
||||
```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/v1"
|
||||
api_key: "${DEEPSEEK_API_KEY}"
|
||||
temperature: 0.7
|
||||
max_tokens: 4096
|
||||
```
|
||||
|
||||
## 四、数据目录
|
||||
|
||||
```
|
||||
{dataDir}/
|
||||
cmd/homed/main.go — 入口:组装所有子系统
|
||||
internal/
|
||||
├── agent/
|
||||
│ ├── core/
|
||||
│ │ ├── agent.go — Agent 核心:事件循环、工具循环、心跳
|
||||
│ │ ├── context.go — RelevanceContext:TF-IDF 上下文管理
|
||||
│ │ └── stages.go — StageHost:阶段管道编排
|
||||
│ ├── api/
|
||||
│ │ └── provider.go — Provider 接口 + DeepSeek/Ollama 实现
|
||||
│ ├── io/
|
||||
│ │ └── channel.go — IOManager + Device 接口(过渡期保留)
|
||||
│ └── personal.go — 人格加载
|
||||
├── api/
|
||||
│ ├── handler.go — HTTP API 端点
|
||||
│ └── plugin.go — WebUI Device 包装
|
||||
├── events/
|
||||
│ └── bus.go — 系统事件总线 (Publish/Subscribe)
|
||||
├── 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/ # 快照
|
||||
│ ├── graph.go — SQLite 图数据库
|
||||
│ ├── indexer.go — 图索引器
|
||||
│ ├── vector/store.go — TF-IDF 向量存储
|
||||
│ ├── document/doc.go — 文档记忆
|
||||
│ ├── text/text.go — 文本记忆(JSONL)
|
||||
│ └── pipeline/ — 蒸馏器
|
||||
├── knowledge/
|
||||
│ └── knowledge.go — 知识系统
|
||||
├── plugin/
|
||||
│ ├── plugin.go — 插件注册表 + 旧 Device 兼容层
|
||||
│ └── sdk/
|
||||
│ ├── api.go — PluginAPI 定义
|
||||
│ └── bus.go — 插件内部 EventBus 接口
|
||||
├── onebot/ — OneBot V11 QQ 协议实现
|
||||
├── tracker/ — 变更追踪 (overlayfs)
|
||||
├── supervisor/ — 守护进程
|
||||
├── skill/ — 技能管理器
|
||||
├── lua/ — Lua 适配器
|
||||
├── network/ — 网络监控
|
||||
├── container/ — 容器管理
|
||||
├── snapshot/ — 快照
|
||||
├── embed/ — 嵌入
|
||||
└── tokenizer/ — 分词器
|
||||
config/
|
||||
├── config.go — 配置加载
|
||||
└── config.yaml
|
||||
pkg/types/ — 类型定义
|
||||
docs/
|
||||
└── ARCHITECTURE.md — 本架构文档
|
||||
```
|
||||
|
||||
## 五、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/config` | 配置查看 |
|
||||
## 十四、与旧设计 (v3) 的关键区别
|
||||
|
||||
## 六、构建与部署
|
||||
|
||||
```bash
|
||||
make build # 编译主二进制
|
||||
make run # 编译 + 启动(数据 /tmp/homeagent)
|
||||
make install # 安装到系统
|
||||
make test # 运行测试
|
||||
```
|
||||
|
||||
依赖:Go 1.19+ (CGo enabled for go-sqlite3)。单二进制部署。
|
||||
| 维度 | v3 | v4 |
|
||||
|------|-----|-----|
|
||||
| 插件交互 | Device 接口 + IOManager 路由 | 三通道:Tool/Stage/Event |
|
||||
| 消息流编辑 | 无(纯事件推送) | 阶段管道 7 个 hook 点 |
|
||||
| Event Bus | 无 | `internal/events/bus.go` |
|
||||
| SDK | 无 | `internal/plugin/sdk/` |
|
||||
| 核心 IO | IOManager `EmitOutput` 直出 | 全部走 `output_send` 工具 |
|
||||
| 插件工具路由 | IOManager `ExecuteTool` 链 | StageHost + Registry 双层路由 |
|
||||
|
||||
Reference in New Issue
Block a user