diff --git a/README.md b/README.md index 15b5179..d198468 100644 --- a/README.md +++ b/README.md @@ -23,197 +23,106 @@ homed(内核零 IO) ← PluginSDK → 插件(所有 IO 能力) ```mermaid sequenceDiagram - participant User as 用户/插件 + participant U as 用户/插件 participant IO as IOManager - participant Event as eventLoop - participant Ctx as RelevanceContext - participant LLM as LLM + 工具循环 - participant Stage as StageHost(7个钩子) - participant Mem as 三层记忆 + participant EV as eventLoop + participant CTX as RelevanceContext + participant LLM as LLM+工具循环 + participant ST as StageHost + participant MEM as 三层记忆 - User->>IO: InjectInput(type, payload) - IO->>Event: inputCh (buf 256) - - rect rgb(240, 240, 255) - Note over Event: processTextInput - Event->>Stage: StageOnInput — 插件可改写/短路 - Event->>Ctx: Prune(input, topK) — TF-IDF评分裁剪 - Ctx->>Mem: 低分事件 → Document层归档 - Event->>Ctx: Append(input) — 5s debounce写盘 + U->>IO: InjectInput(type, payload) + IO->>EV: inputCh + rect lavender + Note over EV: processTextInput + EV->>ST: StageOnInput 插件可改写/短路 + EV->>CTX: Prune(input,topK) TF-IDF裁剪 + CTX->>MEM: 低分事件归档 Document + EV->>CTX: Append(input) 5s写盘 end - - rect rgb(240, 255, 240) - Note over Event,LLM: process() ← a.mu.Lock() - Event->>Mem: buildMemoryContext() — Indexer自动召回Graph实体 - Event->>Mem: buildSystemPrompt() — 人格+记忆+技能注入 - Event->>Stage: StagePreAction — 插件可预拦截 - - loop 工具循环(无上限) - LLM->>LLM: drainInterrupts() — 检查打断 - LLM->>LLM: LLM Chat — provider自动降级 - LLM->>Stage: StagePostAction — 插件可修改/短路 + rect lightgreen + Note over EV,LLM: process() + EV->>MEM: buildMemoryContext Indexer召回Graph + EV->>MEM: buildSystemPrompt 人格+记忆+技能注入 + EV->>ST: StagePreAction 插件可预拦截 + loop 工具循环 + LLM->>LLM: drainInterrupts + LLM->>LLM: LLM Chat + LLM->>ST: StagePostAction 插件可修改/短路 alt 无tool call - LLM-->>Event: 返回response - else 有tool call + LLM-->>EV: 返回response + else loop 每个tool - Stage->>Stage: StageBeforeToolcall — 插件可拒绝 - LLM->>LLM: executeToolCall() — 按前缀路由 - Stage->>Stage: StageAfterToolcall — 插件可改结果 - LLM->>LLM: recordToolCall() + 追加消息 + ST->>ST: StageBeforeToolcall 插件可拒绝 + LLM->>LLM: executeToolCall + ST->>ST: StageAfterToolcall end end end end - - rect rgb(255, 240, 240) - Note over Event: emitResponse - Ctx->>Ctx: Append(response) — 全量交换写入 - Stage->>Stage: StageBeforeOutput — 插件可改写文本 - Event-->>User: ResponseCh (同步, CLI用) - Event-->>Event: 事件总线 (WebUI SSE) - Stage->>Stage: StageAfterOutput — 只读观测 - Event->>Mem: emitMemoryCandidate() → 蒸馏管道 + rect lightpink + Note over EV: emitResponse + CTX->>CTX: Append(response) + ST->>ST: StageBeforeOutput 插件可改写 + EV-->>U: ResponseCh CLI同步 + EV-->>EV: 事件总线 WebUI SSE + ST->>ST: StageAfterOutput 只读 + EV->>MEM: emitMemoryCandidate end - - Note over User,Mem: ★ LLM不调用output_send时,内核绝不自动转发到输出通道 ``` -### 二、Stage 管道模型 +### 二、Stage 管道 ```mermaid flowchart LR - subgraph "7个阶段钩子" - direction LR - S1[① StageOnInput
输入后·可短路] --> - S2[② StagePreAction
LLM前·可短路] --> - S3[③ StagePostAction
LLM后·可短路] - S3 --> S4{有tool call?} - S4 -->|是| S5[④ StageBeforeToolcall
tool前·可拒绝] - S5 --> T[executeToolCall] - T --> S6[⑤ StageAfterToolcall
tool后·只读] - S6 --> S3 - S4 -->|否| S7[⑥ StageBeforeOutput
输出前·改写文本] - S7 --> S8[⑦ StageAfterOutput
输出后·只读] - end - Input[用户输入] --> S1 - S8 --> Output[输出] - + S1[① on_input] --> S2[② pre_action] + S2 --> S3[③ post_action] + S3 --> Q{有tool?} + Q -->|是| S4[④ before_toolcall] + S4 --> T[executeToolCall] + T --> S5[⑤ after_toolcall] + S5 --> S3 + Q -->|否| S6[⑥ before_output] + S6 --> S7[⑦ after_output] style S1 fill:#e1f5fe - style S2 fill:#e1f5fe style S3 fill:#fff3e0 - style S5 fill:#fce4ec - style S6 fill:#f3e5f5 - style S7 fill:#e8f5e9 - style S8 fill:#f5f5f5 + style S6 fill:#e8f5e9 ``` +### 三、三层记忆 + ```mermaid flowchart TB - subgraph "并行调用模型" - direction LR - SH[StageHost] --> G1[goroutine 1] - SH --> G2[goroutine 2] - SH --> G3[goroutine 3] - G1 & G2 & G3 --> SC[StageContext
RWMutex共享] + subgraph C[① Context 工作窗口] + RC[RelevanceContext] + A[Append] -->|Vectorize char 1-2gram| RC + P[Prune TF-IDF Cosine] -->|低分| D + P -->|保留| TL[timeline→system prompt] end + subgraph D[② Document 文件记忆] + DS[DocStore JSON+TF-IDF] + Q1[Query 自动注入] -->|【相关记忆文档】| SP + Q2[doc_query LLM主动] -->|Consume+删除| DS + Q2 -->|原始时间戳写入| RC + CD[FindColdDocs 72h] -->|docToTriples| G + end + subgraph G[③ Graph 图数据库] + DB[(SQLite)] + IDX[Indexer BFS depth=2] -->|【记忆索引】| SP + MEM[memory_recall/commit] + SOC[person_query/set_trait] + end + subgraph H[④ 心跳蒸馏] + REORG -->|Step3 冷文档| CD + REORG -->|Step4 Bigram Jaccard| CONS[consolidation] + PIPE[Pipeline 正则] -->|姓名/住址/喜好/年龄/职业| DB + end + SP[System Prompt] -->|顺序组装| LLM + LLM[LLM] -->|doc_query| Q2 + LLM -->|memory_recall| MEM ``` -| 阶段 | 位置 | 调用时机 | 可短路? | 可写字段 | -|------|------|----------|:-------:|----------| -| StageOnInput | ① | 构建stageCtx后 | ✅ | RawMessage / Response | -| StagePreAction | ② | 构建消息后,LLM前 | ✅ | ContextMsgs / Response | -| StagePostAction | ③ | LLM返回后,检查tool前 | ✅ | LLMText / ToolCalls / Response | -| StageBeforeToolcall | ④ | 每个tool执行前 | ✅(拒绝) | ToolCalls / Response | -| StageAfterToolcall | ⑤ | 每个tool执行后 | ❌ | ToolResults | -| StageBeforeOutput | ⑥ | 发送输出前 | ❌ | FinalText | -| StageAfterOutput | ⑦ | 发送输出后 | ❌ | (只读) | - -### 三、三层记忆架构 - -#### TF-IDF 字符 n-gram 向量化(char 1-2 gram) - -TF-IDF 是贯穿三层记忆的**核心算法**,在 4 个独立位置以不同方式使用: - -| 位置 | 文件 | n-gram | 用途 | 算法 | -|------|------|:------:|------|------| -| Context Prune | `context.go:162` | 2 | 裁剪低相关性上下文事件 | CosineSimilarity(queryVec, evt.Vector) | -| DocStore Query | `document.go:205` | 2 | 从文档记忆召回相关内容 | InvertedIndex + CosineSimilarity | -| Indexer 实体搜索 | `indexer.go:149` | 2 | 从Graph图数据库召回相关实体 | InvertedIndex + CosineSimilarity | -| 实体相似度 | `agent.go:2297` | 2 | 检测Graph中相似实体 | Bigram Jaccard (>0.75→consolidation) | - -#### 记忆流转图 - -```mermaid -flowchart TB - subgraph "① Context 层 — 工作窗口" - RC[RelevanceContext
内存events[] + JSON文件] - A[Append
每次输入时调用] -->|Vectorize
char 1-2gram TF-IDF| RC - P[Prune
TF-IDF CosineSimilarity
保留 topK + 最近10条] -->|低分事件归档| CDoc - P -->|保留| TL[timeline → system prompt
按时间排序输出给LLM] - S[save
5s debounce写盘] --> RC - end - - RC -->|读取全部事件| TL - - subgraph "② Document 层 — 文件记忆" - D[DocStore
JSON文件 + TF-IDF向量索引] - CDoc[ContextToDoc
Prune归档入口] -->|结构化摘要+标签+实体| D - LLMDC[LLM工具
doc_commit] -->|手动提交| D - GSD[syncGraphToDocs
每30min] -->|Graph快照| D - Q[Query
system prompt注入] -->|Vectorize输入
InvertedIndex+Cosine
召回top 3| D - Q -->|格式: 【相关记忆文档】| SP - AC[AccessCount++
LastAccess更新] -->|每次Query命中| D - end - - subgraph "③ Graph 层 — 图数据库" - G[(SQLite)] - G --> ENT[entities
{Name,Type,Summary,Vector}] - G --> REL[relations
{PersonA,Relation,PersonB}] - IDX[Indexer 自动召回
每30min重建向量索引] -->|Vectorize实体名
TF-IDF + BFS depth=2| G - IDX -->|格式: 【记忆索引】| SP - MEM[LLM工具
memory_recall/commit/merge] --> G - SOC[Social层
person_query/set_trait/relate] --> G - end - - subgraph "④ 蒸馏管道 — 每30min心跳" - DC[distillContext
窗口>2×max→强制Prune] -->|触发| P - SYNC[syncGraphToDocs] -->|Graph→Document| GSD - REORG[reorgGraph] -->|Step1| IDXSYNC[indexer.Sync
重建实体向量索引] - REORG -->|Step2| DREIDX[docStore.Reindex
重建文档向量索引] - REORG -->|Step3 冷文档→Graph| CD[FindColdDocs
72h / ≤2次访问] -->|docToTriples| G - REORG -->|Step4 实体相似度| BIGRAM[Bigram Jaccard
>0.75] -->|consolidation| LLMCONS[LLM判断
是否merge] - REORG -->|Step5 图质量| QEVAL[evaluateGraphQuality
低置信度关系] --> LLMCONS2[LLM判断
保留/删除] - end - - SP[System Prompt
组装顺序] -->|base→人格→| GI[【记忆索引】
Indexer召回实体] - GI -->|清洗指令→| DM[【相关记忆文档】
DocStore.Query top3] - DM -->|技能注入→| TDS[工具定义] --> LLM - - subgraph "⑤ LLM 工具循环" - LLM[LLM 推理] -->|memory_recall| MEM - LLM -->|doc_query| Q2[doc_query 工具] - Q2 -->|Consume
读取并删除| D - Q2 -->|③ 写入context
原始时间戳+源cold_storage| RC - Q2 -->|返回: 已加载N篇| LLM - LLM -->|doc_commit| LLMDC - LLM -->|output_send| OUT[输出通道] - end - - subgraph "⑥ Pipeline 规则蒸馏器" - PIPE[pipeline/distillOnce
每心跳] -->|正则匹配| R1[我叫X→(用户,姓名,X)] - PIPE -->|正则匹配| R2[我住在X→(用户,居住地,X)] - PIPE -->|正则匹配| R3[我喜欢X→(用户,喜好,X)] - PIPE -->|正则匹配| R4[我X岁→(用户,年龄,X)] - PIPE -->|正则匹配| R5[我的工作是X→(用户,职业,X)] - R1 & R2 & R3 & R4 & R5 -->|GraphDB.Commit| G - end -``` - -| 层级 | 存储介质 | 索引 | 写入路径 | 读取路径 | 触发方式 | -|------|---------|------|----------|----------|---------| -| Context | 内存+JSON文件 | 无独立索引,TF-IDF向量缓存在event上 | Append(每次输入) | timeline格式化→system prompt | 自动 | -| Document | JSON文件 | TF-IDF InvertedIndex(char 1-2gram) | Prune归档 / doc_commit / Graph快照 | DocStore.Query→【相关记忆文档】→system prompt | 自动+LLM调用 | -| Graph | SQLite | 实体名TF-IDF向量索引(Indexer) + BFS遍历 | memory_commit / 冷文档蒸馏 / 规则蒸馏 | Indexer.BuildContext→【记忆索引】→system prompt | 自动+LLM调用 | +详细说明见 [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md)。 ## 快速体验 diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 89f6b38..d843c78 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -64,31 +64,68 @@ eventLoop() → processTextInput() ## 三层记忆 +### 记忆流转 + ``` - 输入 - │ - ▼ -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 (工作窗口) + RelevanceContext — 内存 events[] + JSON持久化 + Append: 每次输入, Vectorize(char 1-2gram TF-IDF) + Prune: TF-IDF CosineSimilarity, 保留 topK + 最近10条 + ├── 保留 → timeline → system prompt (按时间排序) + └── 低分 → Document 层归档 (ContextToDoc) + Save: 5s debounce 写盘 + + ↓ Prune 归档 ↑ LLM 主动召回 + +② Document (文件记忆) + DocStore — JSON文件 + TF-IDF InvertedIndex + 写入: Prune归档 / doc_commit / Graph快照(syncGraphToDocs) + 读取: + ├── 自动注入: Query(input, top3) → 【相关记忆文档】→ system prompt (只读, 更新 AccessCount) + └── LLM主动: doc_query → Consume(读取并删除) + → 逐条 context.Append{Timestamp: d.CreatedAt, Source: "cold_storage"} + → 文档以原始时间戳写入 context 时间线, 从 docStore 删除 + 冷化: FindColdDocs(72h, ≤2次访问) → docToTriples → Graph + + ↓ 冷文档蒸馏 ↑ 自动召回 + +③ Graph (图数据库) + SQLite — entities + relations 表 + 写入: memory_commit / 冷文档蒸馏 / Pipeline 规则蒸馏 + 读取: + ├── 自动召回: Indexer.BuildContext(input) + │ → TF-IDF 实体名搜索 → BFS depth=2 + │ → 【记忆索引】→ system prompt + └── LLM主动: memory_recall / doc_query + Social: person_query / set_trait / relate (包装 GraphDB) + +④ 蒸馏管道 (每30min心跳) + distillContext → 窗口>2×maxSize → 强制Prune + syncGraphToDocs → Graph 快照写入 Document(跨层可搜索) + reorgGraph: + Step1: indexer.Sync — 重建实体TF-IDF向量索引 + Step2: docStore.Reindex — 重建文档TF-IDF向量索引 + Step3: 冷文档 → docToTriples → GraphDB.Commit + Step4: 实体相似度(Bigram Jaccard>0.75) → consolidation → LLM判断合并 + Step5: evaluateGraphQuality → LLM判断保留/删除 + +⑤ Pipeline 规则蒸馏器 (每心跳) + distillOnce → 正则匹配个人信息: + 我叫X / 我住在X / 我喜欢X / 我X岁 / 我的工作是X + → 三元组 → GraphDB.Commit ``` +### TF-IDF 向量化(char 1-2 gram) + +TF-IDF 是贯穿三层记忆的核心算法,在 4 个独立位置以不同方式使用: + +| 位置 | 文件 | 用途 | 算法 | +|------|------|------|------| +| Context Prune | `context.go:162` | 裁剪低相关性上下文事件 | CosineSimilarity(queryVec, evt.Vector) | +| DocStore Query | `document.go:205` | 从文档记忆召回相关内容 | InvertedIndex + CosineSimilarity | +| Indexer 实体搜索 | `indexer.go:149` | 从Graph召回相关实体 | InvertedIndex + CosineSimilarity | +| 实体相似度检测 | `agent.go:2297` | 检测Graph中相似实体 | Bigram Jaccard (>0.75 → consolidation) | + ### Context 层 `internal/agent/core/context.go` — `RelevanceContext`