mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-21 01:18:08 +00:00
docs: revise architecture docs for academic rigor and source-code accuracy
- Rewrite 体系结构原则/Architectural Principles section with precise implementation details from source code (3-way eventLoop select, StageHost parallelism, provider ordered fallback, recency bias) - Document shared StaticEmbedder vector space between Context and Document - Remove marketing tone, OS analogy table, and mascot emoji separators - Remove contrastive sentence patterns (而非/not...but/rather than) - Align terminology with actual implementation across all 6 doc files
This commit is contained in:
@ -2,9 +2,23 @@
|
||||
|
||||
# HomeAgent Architecture
|
||||
|
||||
The kernel performs zero IO; all external interaction comes from plugins.
|
||||
## Architectural Principles
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
HomeAgent's cognitive architecture consists of three subsystems: the event loop (eventLoop), the context window (RelevanceContext), and the stage pipeline (StageHost). Together they form the orchestration framework. Within this framework, the LLM serves as a scheduled reasoning unit; cognitive continuity is maintained by the event loop, context window, and stage pipeline.
|
||||
|
||||
**The event loop (eventLoop)** is a three-way select: `a.io.InputChan()` receives external user input and dispatches to `processTextInput` / `processMediaInput`; `a.selfInputCh` receives internal system tasks (memory merges, distillation callbacks) routed through `processConsolidation` under the `_consolidation_` output channel; `a.ctx.Done()` accepts shutdown signals. A concurrently running `interceptLoop` goroutine independently reads `a.io.InputInterruptChan()` — on receiving a high-priority interrupt, it cancels the in-flight LLM HTTP request (`a.cancelLLM()`), then writes the event to `a.interceptCh`. This channel is drained non-blockingly by `drainInterrupts()` before each LLM call in `process()`, injecting interrupts as `[打断消息]` formatted entries into message history. The three interrupt delivery paths carry distinct semantics: `cancelLLM` terminates the current HTTP request, `interceptCh` injects text before the next LLM turn, and `InjectInput` triggers a new processing cycle when the event loop is idle.
|
||||
|
||||
**The stage pipeline (StageHost)** manages two registration categories: tool definitions (ToolDef) and stage handlers (StageHandler). `RegisterTool` rejects duplicate names, infers the owning plugin name from the tool name prefix, and maintains a `toolPlugins` mapping. `RegisterStage` appends handlers to the corresponding stage list. On stage execution (`RunStage`), **all registered handlers execute in parallel via goroutines**, sharing a single `*StageContext` protected by `sync.RWMutex`. Individual handler panics are recovered independently without affecting other handlers. Short-circuit semantics are implemented by checking `ctx.Response != nil` — any stage handler can set this value to terminate the pipeline early. `ExecuteTool` includes built-in panic recovery with stack-trace recording. `UnregisterPluginTools` removes a plugin's tool set during hot-reload.
|
||||
|
||||
**The context window (RelevanceContext)** maintains a chronologically ordered event list. `Append` applies `CleanTemplateText` to strip QQ templates and timestamp noise before computing the embedding vector using a three-branch strategy (agent events use Response, user events use Input, cold_storage uses Input+Response). `Prune` triggers when the event count exceeds `topK`: it **unconditionally protects the last 10 events from eviction** (recency bias), scores remaining candidates against the current input via CosineSimilarity, keeps `topK - 10` highest-scoring entries (floor at 0), then re-sorts chronologically. Pruned events from sources other than `agentcli` and `terminal` are archived to the Document layer via `docStore.ContextToDoc`, retaining original timestamps. Persistence uses 5-second debounced writes to a JSON file.
|
||||
|
||||
**Tool definitions are aggregated from five sources**: IOManager-registered plugin tools; StageHost-registered SDK tools; Indexer-provided memory index tools; conditionally added built-in tools (depending on non-nil state of memory/knowledge/docStore/social/pluginReg/providerManager modules — including memory operations, knowledge retrieval, document queries, social networking, plugin reloading, child-agent spawning, per-output-channel send tools, and LLM source switching); and media processing tools added based on `pendingMedia` state. `buildToolDefs()` re-aggregates all sources on each process cycle.
|
||||
|
||||
**Provider invocation follows an ordered fallback strategy**: `ProviderManager.OrderedProviders()` returns the provider list in registration order. The `process()` inner loop iterates this list attempting `Chat()` on each. HTTP 401/403 responses mark the provider as permanently unavailable; other error types also mark unavailability but with higher tolerance. If all providers fail, an error is returned to the caller. If a call is interrupted by context cancellation while the agent is still running, it is retried (only on non-consolidation paths).
|
||||
|
||||
**The memory system adopts a three-tier storage hierarchy (Context → Document → Graph), tiering data by access locality and persistence requirements**: the Context layer is a fast-volatile working window using StaticEmbedder (pretrained word embeddings with TF-IDF fallback) for semantic relevance scoring; the Document layer **shares the same StaticEmbedder vector space with Context** (the embedder is injected into the Document Store at agent startup via `docStore.SetVectorizer(embedder)`), ensuring that relevance scores during Context pruning and semantic retrieval during Document queries operate within the same vector space — TF-IDF serves only as a fallback when the embedder is unavailable; the Graph layer uses SQLite as its persistence substrate with an entities table (nodes) and a relations table (directed edges), supporting BFS traversal recall. Data migration policies govern movement across tiers: low-scoring events sink from Context to Document (vectorized using the same embedder at archival time); cold documents, after a 72-hour no-access threshold, are distilled into triples via `docToTriples` and committed to Graph. The Indexer uses dual retrieval (entity vector similarity search + jieba keyword extraction) to construct Graph query seeds, and the `MarkRecalled` mechanism prevents entities already fetched via tool calls from being re-injected into the system prompt.
|
||||
|
||||
**The separation of core domain and application domain** constrains the kernel's responsibilities to LLM orchestration, memory management, and knowledge retrieval — no direct IO operations; all external interaction is mediated through the plugin domain. This separation limits the kernel's complexity to a verifiable scope while granting the plugin domain independent evolution: plugins can be independently developed, independently released, hot-loaded, and do not directly affect the stability of the core domain.
|
||||
|
||||
## Message Processing Flow
|
||||
|
||||
@ -66,7 +80,6 @@ Exit conditions: LLM has no tool calls / all rejected / exceeded limit.
|
||||
|
||||
Setting `ctx.Response` at any stage jumps to `after_output`.
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## Three-Layer Memory
|
||||
|
||||
@ -86,10 +99,10 @@ Setting `ctx.Response` at any stage jumps to `after_output`.
|
||||
↓ Prune archive ↑ LLM active recall
|
||||
|
||||
② Document (File Memory)
|
||||
DocStore — JSON files + TF-IDF InvertedIndex
|
||||
DocStore — JSON files + shared StaticEmbedder vector space with Context (fallback: TF-IDF InvertedIndex)
|
||||
Write: Prune archive / doc_commit / Graph snapshot (syncGraphToDocs)
|
||||
Read:
|
||||
├── Auto-inject: Query(input, top3) → similarity summary → [Related Memory Docs] → system prompt (read-only)
|
||||
├── Auto-inject: Query(input, top3) → similarity summary under same vector space → [Related Memory Docs] → system prompt (read-only)
|
||||
└── LLM active: doc_query → Consume(read and delete)
|
||||
→ context.Append{Timestamp: d.CreatedAt, Source: "cold_storage"} per doc
|
||||
→ Docs written to context timeline with original timestamps, deleted from docStore
|
||||
@ -139,7 +152,7 @@ All vectorization unified under `StaticEmbedder` (`internal/memory/static_embedd
|
||||
| Location | File | Purpose | Algorithm |
|
||||
|----------|------|---------|-----------|
|
||||
| Context Prune | `context.go:155` | Trim low-relevance context events | VectorizeClean → CosineSimilarity(queryVec, evt.Vector) |
|
||||
| DocStore Query | `document.go:206` | Recall from document memory | TF-IDF Vectorize → vec.Search |
|
||||
| DocStore Query | `document.go:206` | Recall from document memory | StaticEmbedder.Vectorize (primary) / TF-IDF (fallback) → vec.Search |
|
||||
| Indexer Entity Search | `indexer.go:96+111` | Recall from Graph | vector entity search + jieba keywords → SQLite LIKE + BFS |
|
||||
| Entity Similarity Detection | `distill.go` | Detect similar entities in Graph | Bigram Jaccard (>0.75 → consolidation) |
|
||||
|
||||
@ -157,7 +170,7 @@ All vectorization unified under `StaticEmbedder` (`internal/memory/static_embedd
|
||||
|
||||
`internal/memory/document/document.go` — `Store`
|
||||
- Consume-on-read mode: deleted after `doc_query` retrieval
|
||||
- Dual recall: char-bigram TF-IDF vector search + jieba keyword extraction
|
||||
- Dual recall: shared StaticEmbedder semantic vector search + jieba keyword extraction (falls back to char-bigram TF-IDF when model is not loaded)
|
||||
|
||||
### Graph Layer
|
||||
|
||||
@ -210,7 +223,6 @@ Heartbeat 30min:
|
||||
|
||||
Entity conflict detection heuristic (bigram Jaccard > 0.75), routed through `selfInputCh` internal channel, LLM makes the final merge decision.
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## Knowledge Base
|
||||
|
||||
@ -219,7 +231,6 @@ Entity conflict detection heuristic (bigram Jaccard > 0.75), routed through `sel
|
||||
- Independent TF-IDF index, separate from memory system
|
||||
- `knowledge_search` / `knowledge_create` / `knowledge_list`
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## Provider & Lua Adapter Layer
|
||||
|
||||
@ -245,7 +256,6 @@ ProviderManager manages multiple sources, fallback in registration order. Lua ad
|
||||
|
||||
VM built-ins: `json.encode` / `json.decode` / `log` / `http_get` / `http_post`.
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## Plugin System
|
||||
|
||||
@ -297,7 +307,6 @@ type Plugin interface {
|
||||
}
|
||||
```
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## Output Channel System
|
||||
|
||||
@ -321,7 +330,6 @@ Capability flags:
|
||||
System prompt injection: output gate rules, multi-call support, long message splitting.
|
||||
Child agent permission: `output_send__` prefix tools are allowed.
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## EventAgentLLMChain Event
|
||||
|
||||
@ -330,7 +338,6 @@ Child agent permission: `output_send__` prefix tools are allowed.
|
||||
- WebUI subscribes to this event via SSE for real-time display
|
||||
- Plugins can subscribe via EventSubscriber (read-only for external plugins)
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## Restricted External Plugin API
|
||||
|
||||
@ -345,7 +352,6 @@ Extended fields:
|
||||
- Triple extensions: Confidence, SubjectType, ObjectType
|
||||
- Relation extension: Confidence
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## Interrupt Mechanism
|
||||
|
||||
@ -367,7 +373,6 @@ Three delivery paths:
|
||||
|
||||
Code: `internal/agent/core/eventloop.go` — `interceptLoop` / `drainInterrupts`
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## Configuration System
|
||||
|
||||
@ -378,7 +383,6 @@ Code: `internal/agent/core/eventloop.go` — `interceptLoop` / `drainInterrupts`
|
||||
- `RegisterDefault` inserts ~80 default keys (seeds for 8 LLM sources)
|
||||
- WebUI settings page `/api/v1/settings` for read/write
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## Code Structure
|
||||
|
||||
|
||||
@ -2,32 +2,28 @@
|
||||
|
||||
# HomeAgent — Project Overview
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## What Is This
|
||||
|
||||
HomeAgent is a continuously-running personal intelligent Agent framework.
|
||||
|
||||
Core architecture: a long-running kernel process (`homed`) that connects to various IO channels (QQ, Web, CLI, etc.) through a plugin system. The kernel handles LLM orchestration, memory management, and knowledge retrieval; plugins handle all external IO — sending/receiving messages, file operations, web search, etc.
|
||||
|
||||
### Key Innovations
|
||||
### Design Highlights
|
||||
|
||||
**Separation of Core Domain and Application Domain** — This is the first Agent framework to explicitly make this distinction. The kernel (core domain) performs zero IO; all IO capabilities belong to plugins (application domain). The boundary is clearly defined through PluginSDK:
|
||||
**Separation of Core Domain and Application Domain** — The kernel (core domain) performs no IO operations; all IO capabilities belong to plugins (application domain). The boundary is defined through PluginSDK:
|
||||
- Plugins register tools (Tool) with the kernel for LLM invocation
|
||||
- Plugins hook into the processing pipeline (Stage) to intercept/rewrite message flow at various phases
|
||||
- Plugins subscribe/publish events (Event) for loosely-coupled communication
|
||||
- Plugins queue or interrupt input delivery through IO API
|
||||
|
||||
The significance: the kernel stays pure (zero IO, only orchestration and memory), plugins stay flexible (each does its job, hot-loadable), with no cross-contamination.
|
||||
The significance lies in clear responsibility boundaries: the kernel focuses on orchestration and memory management, while plugins handle IO implementation — the two are not coupled.
|
||||
|
||||
**Three-Layer Memory Architecture** — Solves the memory decay problem for long-running agents:
|
||||
**Three-Layer Memory Architecture** — Manages information retention across long agent runtimes through a tiered storage strategy:
|
||||
- **Context Layer**: In-memory local word embedding scored event window (jieba + TF-IDF + PMI → CosineSimilarity), maintains recent context in real-time, low-relevance events automatically sink to the next layer
|
||||
- **Document Layer**: JSON files + TF-IDF vector-indexed temporary memory, supports explicit submission and implicit archival, cold data distills to Graph
|
||||
- **Graph Layer**: SQLite graph database, persists entities and relations, BFS traversal recall, distillation pipeline extracts triples from conversations
|
||||
|
||||
Three progressive layers: context → cold archive → long-term graph memory, ensuring the agent doesn't degrade over time.
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
Three progressive layers — context, cold archive, long-term graph memory — form an information decay and consolidation pipeline from short-term to persistent storage.
|
||||
|
||||
## What It Actually Does
|
||||
|
||||
@ -77,7 +73,6 @@ Code is in the project root, implemented in Go.
|
||||
- 9 provider types mapped to LLM-accessible tools (image generation, web search, speech, etc.)
|
||||
- OC channels auto-registered as IO devices with text/file/image/audio capability flags
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## Project Status
|
||||
|
||||
|
||||
@ -2,9 +2,23 @@
|
||||
|
||||
# HomeAgent 架构
|
||||
|
||||
内核零 IO,一切外界交互来自插件。
|
||||
## 体系结构原则
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
HomeAgent 的认知架构由三个核心子系统构成:事件循环(eventLoop)、上下文窗口(RelevanceContext)与阶段管道(StageHost)。三者共同组成编排框架。LLM 在其中作为可调度的推理执行单元运行;认知连续性由事件循环、上下文窗口与阶段管道维持。
|
||||
|
||||
**事件循环(eventLoop)** 是一个三路 select 循环:`a.io.InputChan()` 接收外部用户输入并分发至 `processTextInput` / `processMediaInput`;`a.selfInputCh` 接收内部系统任务(如记忆合并、蒸馏回调),以 `_consolidation_` 输出通道标识区分,走 `processConsolidation` 路径;`a.ctx.Done()` 接受关闭信号。与之并行运行的 `interceptLoop` 协程独立监听 `a.io.InputInterruptChan()`,收到高优先级中断时先取消当前 LLM HTTP 请求(`a.cancelLLM()`),再将事件写入 `a.interceptCh`——该通道在 `process()` 每次 LLM 调用前由 `drainInterrupts()` 非阻塞排空,以 `[打断消息]` 格式注入消息历史。三条中断投递路径各具语义:`cancelLLM` 终结当前 HTTP 请求,`interceptCh` 在下一轮 LLM 调用前注入文本,`InjectInput` 在 eventLoop 空闲时触发新一轮处理。
|
||||
|
||||
**阶段管道(StageHost)** 管理两类注册:工具定义(ToolDef)与阶段处理器(StageHandler)。`RegisterTool` 拒绝同名注册,推断工具所属插件名,并维护工具到插件的映射表 `toolPlugins`。`RegisterStage` 将处理器追加至对应阶段的处理器列表。触发阶段执行时(`RunStage`),**所有已注册处理器通过 goroutine 并行执行**,共享同一 `*StageContext` 实例(通过 `sync.RWMutex` 保护并发访问)。单个处理器的 panic 被独立恢复,不影响其他处理器。短路语义通过检查 `ctx.Response != nil` 实现——任一阶段处理器可设置此值提前终止当前链路。工具执行 `ExecuteTool` 内置 panic 恢复与栈追踪记录。`UnregisterPluginTools` 在插件热重载时移除对应工具集。
|
||||
|
||||
**上下文窗口(RelevanceContext)** 维护一个按时间排序的事件列表。`Append` 在录入前经 `CleanTemplateText` 剥离 QQ 模板与时间戳噪声,再通过三分支向量策略(agent 事件用 Response,用户事件用 Input,cold_storage 用 Input+Response)计算嵌入向量。`Prune` 在事件数超过 `topK` 时触发,**无条件保护最近 10 条事件不被裁剪**(recency bias),对剩余候选事件计算与当前输入的 CosineSimilarity,按评分降序保留 `topK - 10` 条(下限为 0),之后按时间戳重排序。裁剪出的事件中,过滤掉 `agentcli` 和 `terminal` 来源后,其余通过 `docStore.ContextToDoc` 归档至 Document 层,保留原始时间戳。持久化采用 5 秒防抖写入磁盘 JSON 文件。
|
||||
|
||||
**工具定义聚合自五个来源**:IOManager 注册的插件工具;StageHost 注册的 SDK 插件工具;Indexer 提供的记忆索引工具;内置条件工具(依据 memory / knowledge / docStore / social / pluginReg / providerManager 等模块的非空状态选择性添加,包括记忆操作、知识检索、文档查询、社交网络、插件重载、子代理生成、输出通道工具、LLM 源切换等);以及按 `pendingMedia` 状态添加的媒体处理工具。`buildToolDefs()` 在每次 process 周期中重新聚合所有这些来源。
|
||||
|
||||
**Provider 调用采用有序降级策略**:`ProviderManager.OrderedProviders()` 返回按注册顺序排列的 provider 列表。`process()` 内循环遍历该列表依次尝试 `Chat()` 调用。401/403 状态码将对应 provider 标记为永久不可用;其他错误类型同样标记不可用但容忍度更高。全部 provider 失败时返回错误返回调用方。若调用因 context 取消而中断且 Agent 仍在运行,则重试(仅当处理非 consolidation 路径时)。
|
||||
|
||||
**记忆体系采用三级存储层级结构(Context → Document → Graph),按访问局部性与持久化需求进行数据分置**:Context 层为高速易失工作窗口,使用 StaticEmbedder(预训练词嵌入)进行语义相关性评分,以 TF-IDF 为回退策略;Document 层与 Context **共享同一 StaticEmbedder 向量空间**(agent 启动时将 embedder 注入 Document Store),使 Context 裁剪时的事件相关性评分与 Document 查询时的语义检索处于同一向量空间中,确保冷热数据之间的相似度可比——TF-IDF 仅在 embedder 未加载时作为兜底方案;Graph 层以 SQLite 为持久化载体,entities 表与 relations 表分别存储节点与有向边,支持 BFS 遍历召回。三级之间定义数据迁移策略:低分事件从 Context 下沉至 Document(以归档时相同的 embedder 向量化写入),冷文档经 72 小时未访问阈值判定后通过 `docToTriples` 蒸馏为三元组写入 Graph。Indexer 通过双路召回(实体向量相似度搜索 + jieba 关键词提取)构建 Graph 查询种子,结合 `MarkRecalled` 机制避免已被工具调用取回的实体重复注入系统提示。
|
||||
|
||||
**核心域与应用域的职责域分离**是一项系统级架构决策:内核的责任边界限定在 LLM 编排、记忆管理与知识检索三个维度内,不直接承载任何 IO 操作;所有外部交互通过插件域接入。该分离将核心域的复杂度控制在可验证范围内,同时赋予插件域独立的演化自由度——后者可独立开发、独立发布、热加载,且不对核心域的稳定性构成直接影响。
|
||||
|
||||
## 消息处理流程
|
||||
|
||||
@ -66,7 +80,6 @@ eventLoop() → processTextInput()
|
||||
|
||||
任意阶段设 `ctx.Response` 即跳到 `after_output`。
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## 三层记忆
|
||||
|
||||
@ -86,10 +99,10 @@ eventLoop() → processTextInput()
|
||||
↓ Prune 归档 ↑ LLM 主动召回
|
||||
|
||||
② Document (文件记忆)
|
||||
DocStore — JSON文件 + TF-IDF InvertedIndex
|
||||
DocStore — JSON文件 + 与 Context 共享的 StaticEmbedder 向量空间(兜底: TF-IDF InvertedIndex)
|
||||
写入: Prune归档 / doc_commit / Graph快照(syncGraphToDocs)
|
||||
读取:
|
||||
├── 自动注入: Query(input, top3) → 相似度摘要 → 【相关记忆文档】→ system prompt (只读)
|
||||
├── 自动注入: Query(input, top3) → 同一向量空间下相似度摘要 → 【相关记忆文档】→ system prompt (只读)
|
||||
└── LLM主动: doc_query → Consume(读取并删除)
|
||||
→ 逐条 context.Append{Timestamp: d.CreatedAt, Source: "cold_storage"}
|
||||
→ 文档以原始时间戳写入 context 时间线, 从 docStore 删除
|
||||
@ -139,7 +152,7 @@ eventLoop() → processTextInput()
|
||||
| 位置 | 文件 | 用途 | 算法 |
|
||||
|------|------|------|------|
|
||||
| Context Prune | `context.go:155` | 裁剪低相关性上下文事件 | VectorizeClean → CosineSimilarity(queryVec, evt.Vector) |
|
||||
| DocStore Query | `document.go:206` | 文档记忆召回 | TF-IDF Vectorize → vec.Search |
|
||||
| DocStore Query | `document.go:206` | 文档记忆召回 | StaticEmbedder.Vectorize(首选)/ TF-IDF(兜底)→ vec.Search |
|
||||
| Indexer 实体搜索 | `indexer.go:96+111` | Graph实体召回 | 向量实体搜索 + jieba关键词 → SQLite LIKE + BFS |
|
||||
| 实体相似度检测 | `distill.go` | Graph中相似实体 | Bigram Jaccard (>0.75 → consolidation) |
|
||||
|
||||
@ -157,7 +170,7 @@ eventLoop() → processTextInput()
|
||||
|
||||
`internal/memory/document/document.go` — `Store`
|
||||
- 消费即删模式:`doc_query` 检索到后删除
|
||||
- 双路召回:char-bigram TF-IDF 向量搜索 + jieba 关键词提取
|
||||
- 双路召回:与 Context 共享的 StaticEmbedder 语义向量搜索 + jieba 关键词提取(模型未加载时回退 char-bigram TF-IDF)
|
||||
|
||||
### Graph 层
|
||||
|
||||
@ -210,7 +223,6 @@ eventLoop() → processTextInput()
|
||||
|
||||
实体冲突检测启发式(bigram Jaccard > 0.75),走 `selfInputCh` 内部通道,LLM 最终判断是否合并。
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## 知识库
|
||||
|
||||
@ -219,7 +231,6 @@ eventLoop() → processTextInput()
|
||||
- 独立 TF-IDF 索引,与记忆系统不冲突
|
||||
- `knowledge_search` / `knowledge_create` / `knowledge_list`
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## Provider 与 Lua 适配层
|
||||
|
||||
@ -245,7 +256,6 @@ ProviderManager 管理多个源,按注册顺序 fallback。Lua 适配器位于
|
||||
|
||||
VM 内置 `json.encode` / `json.decode` / `log` / `http_get` / `http_post`。
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## 插件系统
|
||||
|
||||
@ -297,7 +307,6 @@ type Plugin interface {
|
||||
}
|
||||
```
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## 输出通道系统
|
||||
|
||||
@ -321,7 +330,6 @@ type Plugin interface {
|
||||
系统提示注入:输出门控规则、多调用支持、长消息拆分。
|
||||
子代理权限:`output_send__` 前缀工具允许使用。
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## LLM 链事件
|
||||
|
||||
@ -330,7 +338,6 @@ type Plugin interface {
|
||||
- WebUI 通过 SSE 订阅此事件实现实时显示
|
||||
- 插件可通过 EventSubscriber 订阅(外部插件只读)
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## 受限外部插件 API
|
||||
|
||||
@ -345,7 +352,6 @@ type Plugin interface {
|
||||
- Triple 扩展:Confidence、SubjectType、ObjectType
|
||||
- Relation 扩展:Confidence
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## 中断机制
|
||||
|
||||
@ -367,7 +373,6 @@ interceptLoop (goroutine)
|
||||
|
||||
代码:`internal/agent/core/eventloop.go` — `interceptLoop` / `drainInterrupts`
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## 配置系统
|
||||
|
||||
@ -378,7 +383,6 @@ interceptLoop (goroutine)
|
||||
- `RegisterDefault` 插入 ~80 个默认键(8 个 LLM 源的 seeds)
|
||||
- WebUI 设置页 `/api/v1/settings` 读写
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## 代码结构
|
||||
|
||||
|
||||
@ -2,32 +2,28 @@
|
||||
|
||||
# HomeAgent — 项目概览
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## 这是什么
|
||||
|
||||
HomeAgent 是一个持续运行的个人智能 Agent 框架。
|
||||
|
||||
核心架构:一个长时间运行的内核进程(`homed`),通过插件系统接入各种 IO 通道(QQ、Web、命令行等)。内核负责 LLM 调用编排、记忆管理、知识检索;插件负责所有外部 IO——收发消息、执行文件操作、搜索网络等。
|
||||
|
||||
### 核心创新
|
||||
### 设计要点
|
||||
|
||||
**核心域与应用域分离** — 这是首个明确提出这一划分的 Agent 框架。内核(核心域)不做任何 IO,所有 IO 能力归属插件(应用域)。边界通过 PluginSDK 明确定义:
|
||||
**核心域与应用域分离** — 内核(核心域)不执行任何 IO 操作,所有 IO 能力归属插件(应用域)。边界通过 PluginSDK 明确定义:
|
||||
- 插件向内核注册工具(Tool),供 LLM 调用
|
||||
- 插件挂入处理管道(Stage),在各阶段拦截/改写消息流
|
||||
- 插件订阅/发布事件(Event),松耦合通信
|
||||
- 插件通过 IO API 排队或打断投递输入
|
||||
|
||||
这一划分的意义:内核保持纯粹(零 IO,只做编排和记忆),插件保持灵活(各司其职,热加载),互不污染。
|
||||
这一划分的意义在于职责边界清晰:内核专注于编排与记忆管理,插件负责具体 IO 实现,二者互不耦合。
|
||||
|
||||
**三层记忆架构** — 解决 Agent 长期运行的记忆衰减:
|
||||
**三层记忆架构** — 通过分级存储策略管理 Agent 长周期运行中的信息留存:
|
||||
- **Context 层**:内存中局部词嵌入评分的事件窗口(jieba + TF-IDF + PMI → CosineSimilarity),实时维护最近上下文,低相关性事件自动下沉到下一层
|
||||
- **Document 层**:JSON 文件 + TF-IDF 向量索引的临时记忆,支持显式提交和隐式归档,冷数据蒸馏到 Graph
|
||||
- **Graph 层**:SQLite 图数据库,持久化实体(entities)和关系(relations),BFS 遍历召回,蒸馏管道从对话中提取三元组
|
||||
|
||||
三层递进:上下文 → 冷归档 → 长期图记忆,确保 Agent 长时间运行不退化。
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
三层递进:上下文 → 冷归档 → 长期图记忆,构成从短期到持久的信息衰减与整合管道。
|
||||
|
||||
## 它实际做了什么
|
||||
|
||||
@ -77,7 +73,6 @@ HomeAgent 是一个持续运行的个人智能 Agent 框架。
|
||||
- 9 种 Provider 类型映射为 LLM 可用工具(图片生成、搜索、语音等)
|
||||
- OC 通道自动注册为 IO 设备,支持文本/文件/图片/音频能力标志
|
||||
|
||||
<img src="../../branding/mascot-xiaozhai.webp" width="20" style="border-radius:50%;vertical-align:middle"> :
|
||||
|
||||
## 项目状态
|
||||
|
||||
|
||||
Reference in New Issue
Block a user