Files
HomeAgent/docs/ARCHITECTURE.md
root df0abcd298 feat: files built-in plugin, doc rewrite, architecture cleanup
- Add files plugin as built-in (internal/plugins/files/) with read/write/edit/ls tools,
  supporting overwrite/append/insert/create modes and offset/limit segmented reading
- Rewrite README.md with core domain separation and three-layer memory highlights
- Rewrite docs/OVERVIEW.md with per-subsystem file path references
- Rewrite docs/ARCHITECTURE.md (783→~300 lines), merge redundant sections
- Clean docs/PLUGIN_DEV.md: remove emoji, simplify SDK examples
- Fix provider Model pollution in LuaAdaptedProvider.Chat()
- Fix executeToolCall to return actual error vs quiet not-found
- Fix plugin.Open path caching with SHA256 temp-path workaround
- Add knowledge/homeagent_architecture demo entry
- Add config/personal/personal.md identity configuration
2026-07-06 14:33:09 +08:00

11 KiB
Raw Blame History

HomeAgent 架构

内核零 IO一切外界交互来自插件。

消息处理流程

完整链路

外部输入(通过插件 InjectInput
        │
        ▼
eventLoop() → processTextInput()
        │
        ├── on_input stage          插件可拦截/改写/短路
        ├── Context.Append          记录到上下文窗口
        ├── Context.Prune           低相关性事件归档到 Document
        ├── buildMemoryContext()    Indexer 召回 → GraphDB BFS 遍历
        │
        ├── pre_action stage        插件可注入 system 消息
        │
        ├── [工具循环] process()
        │   ├── buildSystemPrompt   人格 + 记忆 + 知识 + 上下文
        │   ├── buildToolDefs       内置工具 + 插件工具
        │   ├── provider.Chat()     LLM 调用
        │   ├── post_action stage   插件可见 LLM 输出 + 工具列表
        │   ├── 有工具?
        │   │   ├── before_toolcall 插件可拒绝/改参
        │   │   ├── executeToolCall 路由到插件/内置
        │   │   ├── after_toolcall  插件可改结果
        │   │   └── → 回到 post_action
        │   └── 无工具 → 退出循环
        │
        ├── Context.Append(response)
        ├── before_output stage      插件可改最终文本
        ├── emitResponse()          通过 output_send 发送
        └── after_output stage       插件只读,收尾

代码:internal/agent/core/agent.goprocess() 是工具循环主体

7 个阶段钩子

阶段 触发时机 插件可做
on_input 消息到 Agent零处理 黑名单/限流/短路回复
pre_action 上下文就绪LLM 调用前 注入外部数据到 context
post_action LLM 返回文本+工具列表 敏感词过滤/强制 redirect
before_toolcall 单个工具执行前 审计/拒绝/改参
after_toolcall 单个工具执行完 脱敏/排序结果
before_output 最终文本就绪,发送前 格式适配
after_output 已发送 统计日志

代码:internal/agent/core/stages.goStageHost 编排

循环规则

post_action → [before_toolcall → 执行 → after_toolcall] → post_action 构成内循环。 退出条件LLM 无工具调用 / 全部被拒绝 / 超上限。

短路规则

任意阶段设 ctx.Response 即跳到 after_output

三层记忆

                            输入
                              │
                              ▼
Context (RelevanceContext) ←────────────────┐
  ├─ 内存中 topK 条事件, TF-IDF 评分         │
  ├─ keep=30, 超出 → Document               │
  └─ JSON 持久化防崩溃                      │
                              │             │
                              ▼             │
Document (document.Store) ────┤             │
  ├─ JSON 文件 + TF-IDF 向量索引            │
  ├─ 3 层冷化: 72h+access≤2 → Graph        │
  └─ 用户也可主动 commit                    │
                              │             │
                              ▼             │
Graph (GraphDB + Indexer) ────┘             │
  ├─ SQLite: entities / relations 表        │
  ├─ 搜索: BFS 遍历邻居                      │
  └─ 蒸馏: Distiller 原始记录→三元组         │
                              │             │
                              ▼             │
Context.Append(response) ───────────────────┘

Context 层

internal/agent/core/context.goRelevanceContext

  • 维护最近事件列表,每次 Append/Prune 写入 JSON 防丢
  • 用户输入时做 TF-IDF 相关性评分,保留 topK

Document 层

internal/memory/document/doc.goStore

  • 消费即删模式:doc_query 检索到后删除
  • TF-IDF 索引 character bigram + 倒排

Graph 层

internal/memory/graph.goGraphDB

  • SQLite WAL 模式,两张表
  • Commit(triples) — UPSERT entities + INSERT relations
  • Recall(keywords, depth) — 关键词 LIKE 搜索 + BFS 遍历

记忆工具LLM 可直接调用)

工具 作用
memory_recall 从 Graph 召回
memory_commit 写入 Graph 三元组
memory_introspect 查看记忆统计
doc_query 从 Document 搜索
doc_commit 写入 Document

其他记忆层

  • Social (internal/memory/social/social.go) — 人格特质和关系网,包装 GraphDB 实体类型
  • Text Memory (internal/memory/text/text.go) — 原始对话 JSONL 日志,轮转策略
  • Memory Indexer (internal/memory/indexer.go) — 实体向量化,自动注入 system prompt

蒸馏管道

internal/memory/pipeline/pipeline.go

  • 10 分钟 tick7 天保留
  • 规则提取三元组name / location / likes / age / job 模式)
  • 写入 GraphDB

上下文剪枝

心跳 30min:
  ├── distillContext()        — 蒸馏当前上下文
  ├── syncGraphToDocs()       — Graph→Document 同步
  └── reorgGraph()
      ├── Indexer.Sync()
      ├── DocStore.Reindex()
      ├── 冷文档→Graph
      └── 实体冲突 → enqueueConsolidationTask()
                          │
                    selfInputCh → LLM 判断合并/跳过

实体冲突检测启发式bigram Jaccard > 0.5),走 selfInputCh 内部通道LLM 最终判断是否合并。

知识库

internal/knowledge/knowledge.go

  • 文件目录 knowledge/<name>/content.md
  • 独立 TF-IDF 索引,与记忆系统不冲突
  • knowledge_search / knowledge_create / knowledge_list

Provider 与 Lua 适配层

Agent
  │
  ▼
Provider 接口 (Name / Chat / ChatStream)
  │
  ├── OpenAIProvider   — 标准 OpenAI API
  ├── OllamaProvider   — 本地 Ollama
  └── LuaAdaptedProvider (主要)
      ├── 序列化 CompletionRequest → JSON
      ├── adapter.transform_request() → API 格式
      ├── HTTP 请求 + adapter.headers
      ├── adapter.transform_response() → 统一格式
      └── 反序列化

代码:internal/agent/api/provider.go

ProviderManager 管理多个源,按注册顺序 fallback。Lua 适配器位于 internal/lua/adapters/,每个 .lua 脚本定义 transform_request / transform_response / transform_stream_chunk

VM 内置 json.encode / json.decode / log / http_get / http_post

插件系统

三种加载方式

方式 注册机制 编译 用途
内置插件 init()RegisterFactory internal/plugins/ 编译进内核 webui/cli/timer/mcp 等
外部 .so plugin.Open 动态加载 -buildmode=plugin qq/files/web/memo 等
SKILL 插件 解析 SKILL.md Markdown 定义 OpenClaw 兼容

内置插件注册:internal/plugins/all.go 空白导入 → 各插件 init()Registry.Load() 扫描目录匹配工厂。 外部插件加载:internal/plugin/dynamic.go → 复制到 SHA256 临时路径(绕过 plugin.Open 路径缓存)→ Open + Lookup("NewPlugin")

PluginSDK 三通道

插件 ──→ 核心

RegisterTool(name, fn)   ──→  buildToolDefs() / executeToolCall()
RegisterStage(stage, fn)  ──→  runStage() 在对应阶段调用
Subscribe(event, fn)      ──→  Publish() 通知所有订阅者

internal/plugin/sdk/ 定义完整 SDK

sdk.RegisterTool(name, def, handler)
sdk.RegisterStage(stage, handler)
sdk.Subscribe(eventType, handler)
sdk.Publish(event)
sdk.InjectInput(source, channel, payload)
sdk.InjectInterrupt(source, channel, payload)
sdk.Memory().Recall/Commit
sdk.Knowledge().Search/Create
sdk.Settings().Get/Set/List

Plugin 接口

type Plugin interface {
    Name() string
    Start(sdk *PluginSDK) error
    Stop() error
}

中断机制

interceptLoop (goroutine)
  ├── InputInterruptChan() ← 定时器/消息通知
  ├── (a) cancelLLM() → 取消 Provider HTTP 请求
  ├── (b) interceptCh → process() 轮前读 [打断消息]
  └── (c) InjectInput() → 空闲时触发新处理

三种投递路径:

路径 效果 时机
cancelLLM 取消当前 HTTP 请求 收到 context.Canceled
interceptCh process() 中插入 [打断消息] 每个 LLM call 前
InjectInput eventLoop 空闲时触发新处理 无进行中请求

代码:internal/agent/core/agent.gointerceptLoop / drainInterrupt

配置系统

internal/config/registry.go — ConfigRegistry

  • SQLite 存储,config 表 + config_<plugin> 独立表
  • 命名空间:core.* / plugin.<name>.*
  • RegisterDefault 插入 ~80 个默认键8 个 LLM 源的 seeds
  • WebUI 设置页 /api/v1/settings 读写

代码结构

cmd/homed/main.go          — 入口:组装所有子系统
cmd/waiter/main.go         — CLI 客户端 (Unix socket)
internal/
├── agent/
│   ├── core/              — Agent 核心 (eventLoop/process/stages/context)
│   ├── api/               — Provider 接口 + LuaAdaptedProvider
│   ├── io/                — IOManager (排队/中断/输出)
│   └── personal.go        — 人格加载
├── plugin/
│   ├── registry.go        — 注册表 + 生命周期
│   ├── dynamic.go         — .so 动态加载器
│   └── manifest.go        — plugin.json 元数据
├── plugins/               — 内置插件实现
│   ├── all.go             — 空白导入
│   ├── webui/             — HTTP 服务器 + 嵌入式 SPA
│   ├── cli/               — Unix socket CLI
│   ├── timer/             — 定时器
│   ├── cmd/               — 命令执行
│   ├── mcp/               — MCP 协议
│   ├── openclaw/          — OpenClaw 兼容
│   ├── agentcli/          — PTY 终端
│   ├── healthcheck/       — 健康检查
│   └── pluginmgr/         — 插件管理器
├── sdk/                   — PluginSDK 定义
│   ├── plugin.go          — Plugin 接口 + PluginSDK
│   ├── memory.go          — MemoryAPI
│   ├── knowledge.go       — KnowledgeAPI
│   ├── settings.go        — SettingsAPI
│   └── llm.go             — LLMAPI
├── memory/
│   ├── graph.go           — SQLite 图数据库
│   ├── indexer.go         — 图→向量索引
│   ├── vector/store.go    — TF-IDF 向量引擎
│   ├── document/doc.go    — 文档记忆
│   ├── text/text.go       — 文本日志
│   └── pipeline/          — 蒸馏器
├── knowledge/knowledge.go — 知识库
├── lua/
│   ├── vm.go              — Lua VM (json/log/http)
│   └── adapters/          — 8 个 LLM 适配器脚本
├── config/registry.go     — SQLite 配置中心
├── events/bus.go          — 事件总线
├── tracker/               — OverlayFS 变更追踪
├── supervisor/            — 守护进程管理
├── snapshot/              — 快照
└── tokenizer/             — 中文分词 (jieba 包装)