@yottameta/yotta-memory 0.8.7 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +10 -7
- package/README.zh-CN.md +11 -8
- package/SKILL.md +6 -5
- package/USER_GUIDE.md +12 -5
- package/bin/yotta-memory.js +224 -15
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
## v0.9.0 (2026-09-01)
|
|
2
|
+
|
|
3
|
+
召回质量与上下文选择升级。
|
|
4
|
+
|
|
5
|
+
- **两阶段召回**:词法候选 + 可选本地 embedding 插件候选,统一按语义分与效用分融合排序。
|
|
6
|
+
- **可选 embedding 插件**:`--embedding <command>` 或 `config set embedding_cmd <command>`;本地子进程、`stdin/stdout` JSON 协议、超时默认 3000ms、失败自动降级为词法召回。
|
|
7
|
+
- **任务感知上下文**:`context --focus <关键词>` 新增任务相关记忆段,按身份 / 边界 / 承诺 / 画像 / 任务记忆 / 近期记忆优先级组装。
|
|
8
|
+
- **选择解释**:`context --explain` 输出 included / dropped 与原因;`recall --explain` 继续显示命中理由并补充 embedding 分。
|
|
9
|
+
- **MCP 同步**:`recall` / `search` 新增 `embedding` / `embeddingTimeout` / `explain` 参数;新增 `context` 工具。
|
|
10
|
+
- **版本四件对齐**:package.json / SKILL.md / CHANGELOG.md / 引擎 VERSION = 0.9.0。
|
|
11
|
+
|
|
1
12
|
## v0.8.7 (2026-09-01)
|
|
2
13
|
|
|
3
14
|
评测反馈优化(文档 + 错误提示,功能不变)。
|
package/README.md
CHANGED
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
|
|
24
24
|
> 📖 The user-facing operations manual lives in [USER_GUIDE.md](USER_GUIDE.md).
|
|
25
25
|
|
|
26
|
+
> 🆕 **v0.9.0**: recall quality + context selection — optional local embedding plugin, `context --focus`, and `--explain` selection trace.
|
|
27
|
+
|
|
26
28
|
> 🆕 **v0.8.5**: security hardening — MCP `distill` no longer accepts `--model`; MCP `export` / `import` paths are restricted to the memory root; CLI `distill --model` no longer shells out (allowlist-based).
|
|
27
29
|
|
|
28
30
|
## Core value
|
|
@@ -71,18 +73,19 @@ Each agent has a globally unique agent ID: it is the ownership key for private m
|
|
|
71
73
|
- **No token locally**: local CLI / stdio direct connection bypasses the network and does not validate tokens; identity is declared via the agent's MCP `env.YOTTA_AGENT_ID`.
|
|
72
74
|
- **Private memory requires an owner**: writing PREF / BOUND / COMMIT without declaring identity is rejected (public FACT is unaffected), mechanically preventing ID spoofing.
|
|
73
75
|
|
|
74
|
-
### Profile & start-of-work context (v0.6.0)
|
|
76
|
+
### Profile & start-of-work context (v0.6.0 + v0.9.0)
|
|
75
77
|
|
|
76
78
|
- **profile**: aggregates `private/<owner>/` PREF / BOUND / COMMIT verbatim, grouped by type + subject + tags, written to `profile.md`; the engine infers nothing — profile conclusions are formed internally by the AI per the "memory discipline", never pasted as labels.
|
|
77
|
-
- **context**: one-shot start-of-work package — multi-agent integration rules + identity + user profile digest + recent memory (importance-sorted) + boundary reminders + commitments/anchors; supports `--budget` character budget
|
|
79
|
+
- **context**: one-shot start-of-work package — multi-agent integration rules + identity + user profile digest + optional task-focused memory (`--focus`) + recent memory (importance-sorted) + boundary reminders + commitments/anchors; supports `--budget` character budget and `--explain` selection trace.
|
|
78
80
|
- **Memory discipline**: SKILL.md embeds a rule layer (type red lines / proactive trigger capture / know-the-user three stages / psychological grounding & alignment / bottom lines / host isolation / anti-patterns).
|
|
79
81
|
|
|
80
|
-
### Retrieval: semantic search (v0.8.0
|
|
82
|
+
### Retrieval: semantic search (v0.8.0 + v0.9.0 embedding)
|
|
81
83
|
|
|
82
84
|
- `remember` auto-builds the `index.json` index (version 4 since v0.8.1 with field weighting and pinyin tokens; public indexes over 5000 entries shard by year `index-<year>.json`; old indexes rebuild on first recall); recall defaults to semantic search — exact (field weighting: subject×3 / tags×2 / statement×1) + synonyms (built-in wordlist, extensible) + pinyin (full / initials, built-in 3755 common characters) + fuzzy (edit distance ≤ 2) + substring fallback, blended with utility score (0.65 × semantic + 0.35 × utility), zero-dependency.
|
|
83
85
|
- `recall --explain`: shows each hit's reason (exact / synonym / pinyin / fuzzy + field) and utility components.
|
|
84
86
|
- **Candidate pre-filtering (v0.8.1)**: before semantic scoring, index tokens coarsely filter the candidate set (exact / synonym / pinyin / substring / fuzzy length gate) — hit set identical to v0.8.0; the `view` platform paginates by offset, fetching only the current page.
|
|
85
|
-
- Optional embedding plugin
|
|
87
|
+
- **Optional embedding plugin (v0.9.0)**: `recall --embedding <command>` or `config set embedding_cmd <command>` runs a local subprocess that accepts JSON on stdin and returns vectors on stdout; results are blended into the same ranking. Failures, timeouts, or malformed output automatically fall back to zero-dependency lexical recall.
|
|
88
|
+
- **Embedding cache**: vectors are cached under each memory root at `.embed/cache.json`, keyed by `sha256(command + text)`; only vectors are stored, not plaintext.
|
|
86
89
|
- The `tokens` field of `index.json` is a Chinese-tokenization term-frequency table (for TF scoring), **not** an access credential; auth tokens live at `.server/tokens.json`.
|
|
87
90
|
- Supports keywords, `--type` filter, `--limit` truncation, project-level priority.
|
|
88
91
|
- **Root de-duplication (v0.6.5)**: when project and user roots point at the same directory, recall / context uniquify roots so a file shows once.
|
|
@@ -221,9 +224,9 @@ npm i -g @yottameta/yotta-memory
|
|
|
221
224
|
|---|---|
|
|
222
225
|
| `yotta-memory init [--project] [--dir <dir>]` | Initialize the store (default user-level `~/.yottamemory/`; --dir sets an explicit location) |
|
|
223
226
|
| `yotta-memory remember <type> <subject> <statement> [--owner <id>] [--source <src>] [--weight <0..>] [--verify] [--no-hint]` | Write a memory (same subject+statement auto-updates; --owner marks ownership; --source records origin; --weight importance, dedup takes max; --verify read-back; --no-hint disables type hints) |
|
|
224
|
-
| `yotta-memory recall [keywords] [--type T] [--limit N] [--agent <id>] [--owner <id>] [--all] [--unsafe]` | Search memory (
|
|
227
|
+
| `yotta-memory recall [keywords] [--type T] [--limit N] [--agent <id>] [--owner <id>] [--all] [--unsafe] [--explain] [--semantic] [--embedding <cmd>] [--embedding-timeout N]` | Search memory (semantic + utility ranking; optional local embedding plugin; partitioned reads; cross-reading other agents' private is denied by default, needs grant / identity=user / `--unsafe`; project-level priority) |
|
|
225
228
|
| `yotta-memory profile [--owner <id>]` | Generate a user profile (aggregates `private/<owner>` verbatim, zero inference, writes `profile.md`; cross-owner denied by default) |
|
|
226
|
-
| `yotta-memory context [--limit N] [--owner <id>] [--budget N]` | Generate the start-of-work package (identity + multi-agent rules + profile + recent memory + boundaries + commitments; --budget
|
|
229
|
+
| `yotta-memory context [--limit N] [--owner <id>] [--budget N] [--focus <text>] [--explain] [--embedding <cmd>]` | Generate the start-of-work package (identity + multi-agent rules + profile + task-focused memory + recent memory + boundaries + commitments; --budget caps chars, --focus adds task relevance, --explain shows included/dropped) |
|
|
227
230
|
| `yotta-memory forget <file>` | Delete a memory (by type-dir path or file name) |
|
|
228
231
|
| `yotta-memory archive [--days 180] [--threshold 0.4]` | Archive old memory (final score + age; immutable excluded) |
|
|
229
232
|
| `yotta-memory reindex` | Rebuild the index (after manually editing .md) |
|
|
@@ -301,7 +304,7 @@ Register the connection in the agent's MCP config (`url` + two headers):
|
|
|
301
304
|
}
|
|
302
305
|
```
|
|
303
306
|
|
|
304
|
-
Once connected, MCP tools (remember / recall / search / forget / archive / reindex / export / import / agent_info) read/write memory and confirm identity; management actions (init / config / token / lan / serve) are not exposed via MCP, and token management is never exposed remotely. MCP `export` / `import` paths are restricted inside the memory root,
|
|
307
|
+
Once connected, MCP tools (remember / recall / search / context / forget / archive / reindex / export / import / agent_info) read/write memory and confirm identity; management actions (init / config / token / lan / serve) are not exposed via MCP, and token management is never exposed remotely. MCP `export` / `import` paths are restricted inside the memory root, MCP `distill` does not support `--model`, and MCP never accepts a raw embedding command from remote callers — the local embedding plugin must be configured on the engine host with `config set embedding_cmd`. `X-Agent-Id` must match the token's registered agent; read-partition rules are the same as the CLI (FACT public-readable, PREF / BOUND / COMMIT private).
|
|
305
308
|
|
|
306
309
|
### Location persistence
|
|
307
310
|
|
package/README.zh-CN.md
CHANGED
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
|
|
24
24
|
> 📖 面向用户的操作手册见 [USER_GUIDE.md](USER_GUIDE.md)。
|
|
25
25
|
|
|
26
|
+
> 🆕 **v0.9.0**:召回质量与上下文选择——可选本地 embedding 插件、`context --focus`、`--explain` 选择解释。
|
|
27
|
+
|
|
26
28
|
> 🆕 **v0.8.5**:安全加固——MCP `distill` 不再接受 `--model`;MCP `export` / `import` 路径限记忆库内;CLI `distill --model` 不再走 shell(改为允许清单)。
|
|
27
29
|
|
|
28
30
|
> 🆕 **v0.8.2**:发布元数据修复——三次源重发带 `--name 元忆 yotta-memory`,修复 ClawHub 展示名缺失中文(原为裸 `yotta-memory`);无功能变更。
|
|
@@ -98,18 +100,19 @@
|
|
|
98
100
|
- **本机免 token**:本机 CLI / stdio 直连不经网络、不校验 token;身份经该智能体 MCP 配置的 `env.YOTTA_AGENT_ID` 声明。本机多个智能体各自声明唯一 ID,互不撞。
|
|
99
101
|
- **私密记忆必须有 owner**:写 PREF / BOUND / COMMIT 时未声明身份会被拒绝(公共 FACT 不受影响),从机制上防止「抄别人的 ID」。
|
|
100
102
|
|
|
101
|
-
### 画像与开工上下文(v0.6.0)
|
|
103
|
+
### 画像与开工上下文(v0.6.0 + v0.9.0)
|
|
102
104
|
|
|
103
105
|
- **profile**:聚合 `private/<owner>/` 下 PREF / BOUND / COMMIT 原文,按 type + subject + tags 归组,写 `profile.md`;引擎零推断,画像结论由 AI 依据「记忆守则」内部形成,不当面贴标签。
|
|
104
|
-
- **context**:一键生成开工上下文包——多智能体接入铁律 + 身份 + 用户画像摘要 + 近期记忆(按 importance 排序)+ 边界提醒 + 承诺 / 锚点;支持 `--budget`
|
|
106
|
+
- **context**:一键生成开工上下文包——多智能体接入铁律 + 身份 + 用户画像摘要 + 任务相关记忆(`--focus`,v0.9.0)+ 近期记忆(按 importance 排序)+ 边界提醒 + 承诺 / 锚点;支持 `--budget` 字符预算与 `--explain` 选择解释。
|
|
105
107
|
- **记忆守则**:SKILL.md 内置规则层(类型红线 / 主动捕获触发信号 / 了解用户三阶段四手法 / 心理学底座与对齐 / 底线与边界 / 宿主隔离 / 反模式),让 AI「越用越懂」有章法。
|
|
106
108
|
|
|
107
|
-
### 检索:语义检索(v0.8.0
|
|
109
|
+
### 检索:语义检索(v0.8.0 + v0.9.0 embedding)
|
|
108
110
|
|
|
109
111
|
- `remember` 时自动构建 `index.json` 索引(v0.8.1 起 version=4,含字段加权与拼音 token;公共索引超过 5000 条按年份分片 `index-<year>.json`;旧索引首次 recall 自动重建);recall 默认语义检索——精确(字段加权:subject×3 / tags×2 / statement×1)+ 同义词(内置词表,可扩展)+ 拼音(全拼 / 首字母,内置 3755 常用字表)+ 模糊(编辑距离 ≤ 2)+ 子串兜底,并与效用分融合排序(0.65 × 语义 + 0.35 × 效用),零依赖。
|
|
110
112
|
- `recall --explain`:展示每条命中理由(精确 / 同义 / 拼音 / 模糊 + 字段)与效用分项。
|
|
111
113
|
- **recall 候选预过滤(v0.8.1)**:语义打分前先用索引 token 粗筛候选集(精确 / 同义 / 拼音 / 子串 / 模糊长度门槛),命中集与 v0.8.0 完全一致;`view` 查看平台按偏移分页返回,一次只取当前页。
|
|
112
|
-
-
|
|
114
|
+
- **可选 embedding 插件(v0.9.0)**:`recall --embedding <命令>` 或 `config set embedding_cmd <命令>`,本地子进程通过 stdin/stdout JSON 协议返回向量;结果并入同一套排序。插件失败、超时或输出非法时自动降级为零依赖词法检索。
|
|
115
|
+
- **向量缓存**:向量缓存在各记忆根下 `.embed/cache.json`,键为 `sha256(命令 + 文本)`;只存向量,不存明文。
|
|
113
116
|
- `index.json` 的 `tokens` 字段是中文分词的词频表(TF 打分用),**不是**访问凭证;鉴权令牌在记忆目录下 `.server/tokens.json`。
|
|
114
117
|
- 支持关键词、`--type` 过滤、`--limit` 截断、项目级优先。
|
|
115
118
|
- **根位置去重(v0.6.5)**:当项目级与用户级记忆库指向同一目录(如 cwd = home 或其父)时,`recall` / `context` 自动唯一化根,同一文件只展示一次。
|
|
@@ -261,9 +264,9 @@ npm i -g @yottameta/yotta-memory
|
|
|
261
264
|
|---|---|
|
|
262
265
|
| `yotta-memory init [--project] [--dir <目录>]` | 初始化记忆库(默认用户级 `~/.yottamemory/`;--dir 显式指定位置)|
|
|
263
266
|
| `yotta-memory remember <type> <subject> <statement> [--owner <id>] [--source <来源>] [--weight <0..>] [--verify] [--no-hint]` | 写入记忆(同 subject+statement 自动更新;--owner 标注归属;--source 记录来源;--weight 重要性权重、去重取 max;--verify 写后回读;--no-hint 关闭类型提示)|
|
|
264
|
-
| `yotta-memory recall [关键词] [--type T] [--limit N] [--agent <id>] [--owner <id>] [--all] [--unsafe]` |
|
|
267
|
+
| `yotta-memory recall [关键词] [--type T] [--limit N] [--agent <id>] [--owner <id>] [--all] [--unsafe] [--explain] [--semantic] [--embedding <命令>] [--embedding-timeout N]` | 检索记忆(语义+效用分排序;可选本地 embedding 插件;读取分区过滤;越界读其它智能体私密默认拒绝,需 grant / identity=user / `--unsafe`;`--agent <其它>` 仅作身份声明、不授予跨读;项目级优先)|
|
|
265
268
|
| `yotta-memory profile [--owner <id>]` | 生成用户画像(聚合 `private/<owner>/` 原文,零推断,写 `profile.md`;跨 owner 默认拒绝)|
|
|
266
|
-
| `yotta-memory context [--limit N] [--owner <id>] [--budget N]` | 生成开工上下文包(身份 + 多智能体铁律 + 画像 + 近期记忆 + 边界 + 承诺;--budget
|
|
269
|
+
| `yotta-memory context [--limit N] [--owner <id>] [--budget N] [--focus <关键词>] [--explain] [--embedding <命令>]` | 生成开工上下文包(身份 + 多智能体铁律 + 画像 + 任务相关记忆 + 近期记忆 + 边界 + 承诺;--budget 字符预算;--focus 任务聚焦;--explain 输出 included/dropped 选择解释)|
|
|
267
270
|
| `yotta-memory forget <文件>` | 删除一条记忆(按类型目录路径或文件名)|
|
|
268
271
|
| `yotta-memory archive [--days 180] [--threshold 0.4]` | 归档旧记忆(盖棺分+年龄,immutable 除外)|
|
|
269
272
|
| `yotta-memory reindex` | 重建索引(手动改 .md 后校正)|
|
|
@@ -341,7 +344,7 @@ yotta-memory recall --type FACT --limit 10
|
|
|
341
344
|
}
|
|
342
345
|
```
|
|
343
346
|
|
|
344
|
-
连接后可通过 MCP tools(remember / recall / search / forget / archive / reindex / export / import / agent_info)读写记忆与确认身份;管理动作(init / config / token / lan / serve)不进 MCP,token 管理不远程暴露;MCP export/import 路径限记忆库内、distill 不支持 `--model
|
|
347
|
+
连接后可通过 MCP tools(remember / recall / search / context / forget / archive / reindex / export / import / agent_info)读写记忆与确认身份;管理动作(init / config / token / lan / serve)不进 MCP,token 管理不远程暴露;MCP export/import 路径限记忆库内、distill 不支持 `--model`,MCP 也不接受远端传入 embedding 命令——embedding 插件只能由引擎主机本地 `config set embedding_cmd` 配置。`X-Agent-Id` 必须与 token 登记的智能体一致;读取分区规则与 CLI 相同(FACT 公共可读,PREF / BOUND / COMMIT 私密隔离)。
|
|
345
348
|
|
|
346
349
|
### 位置持久化
|
|
347
350
|
|
|
@@ -360,4 +363,4 @@ yotta-memory config get
|
|
|
360
363
|
|
|
361
364
|
## 许可证
|
|
362
365
|
|
|
363
|
-
MIT © YottaMeta —— 详见 [LICENSE](./LICENSE)。
|
|
366
|
+
MIT © YottaMeta —— 详见 [LICENSE](./LICENSE)。
|
package/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: yotta-memory
|
|
3
|
-
description: "元忆 —— 有权限边界的文件式智能体记忆。文件式、零依赖、可 diff
|
|
4
|
-
version: 0.
|
|
3
|
+
description: "元忆 —— 有权限边界的文件式智能体记忆。文件式、零依赖、可 diff/可回滚:让任何 AI 智能体活过会话,开工 recall 恢复上下文、重要信息 remember 落盘、收工归档。类型体系 FACT(公共共享)/ PREF / BOUND / COMMIT(私密隔离)。触发:记住、别忘了、记一笔、记忆、remember、recall、跨会话、上次说到、续测、交接、归档、记忆盘、共享记忆、局域网记忆、画像、开工上下文、记忆守则、profile、context、越用越懂、语义检索、反馈、维护、蒸馏、feedback、maintain、distill、explain、自我学习、自我进化、自我提升、查看平台分页、recall 候选预过滤、任务相关记忆、--focus、--embedding"
|
|
4
|
+
version: 0.9.0
|
|
5
5
|
license: MIT
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -18,6 +18,7 @@ license: MIT
|
|
|
18
18
|
- **双级存储**:用户级 `~/.yottamemory/`(跨项目)+ 项目级 `.yottamemory/`(随项目共享)。
|
|
19
19
|
- **越用越懂**:`profile` 聚合用户画像(引擎零推断,只归组原文)+ `context` 一键生成开工上下文包(身份 + 画像 + 近期记忆 + 边界 + 承诺)+ SKILL「记忆守则」规则层;只注入规则与机制,不注入人格数据(出厂零数据)。
|
|
20
20
|
- **自我学习 / 自我进化 / 自我提升(v0.8.0)**:`recall` 语义检索(同义词 / 拼音 / 字段加权 / 模糊匹配,零依赖);`feedback` 显式使用反馈闭环(useful / useless → weight / confidence / feedback_net 演化,越用越懂);`maintain` 规则层自组织(统一效用分 + 年龄自动归档 / 遗忘候选 / 去重,默认 dry-run,immutable / BOUND 豁免);`distill` 心理日志蒸馏(统计摘要 / 主题画像 / 知识地图,可选 `--model` 外部模型增强);`explain` 查看单条记忆效用分项。
|
|
21
|
+
- **召回质量与上下文选择(v0.9.0)**:`recall` 支持可选本地 embedding 插件(`--embedding <command>` / `config set embedding_cmd <command>`);`context --focus <关键词>` 生成任务感知上下文;`--explain` 输出选择 trace,无插件时自动降级为词法检索。
|
|
21
22
|
|
|
22
23
|
## 何时使用(触发)
|
|
23
24
|
|
|
@@ -28,7 +29,7 @@ license: MIT
|
|
|
28
29
|
|
|
29
30
|
## 核心流程
|
|
30
31
|
|
|
31
|
-
1. **开工定向**:先按「开工第一步:确认记忆位置 + 智能体身份」检测记忆库与身份,再运行 `yotta-memory context`(主注入:身份 + 用户画像 + 近期记忆 + 边界 + 承诺)恢复上下文,需要细节再 `yotta-memory recall
|
|
32
|
+
1. **开工定向**:先按「开工第一步:确认记忆位置 + 智能体身份」检测记忆库与身份,再运行 `yotta-memory context`(主注入:身份 + 用户画像 + 近期记忆 + 边界 + 承诺)恢复上下文,需要细节再 `yotta-memory recall <关键词>`;若有明确任务关键词,用 `context --focus <关键词>` 获得任务相关记忆;项目级记忆优先,其次用户级。
|
|
32
33
|
2. **进行中落盘**:重要信息立即 `yotta-memory remember <type> <subject> <statement>`,不攒到收工。
|
|
33
34
|
3. **收工归档**:写会话小结(COMMIT / 笔记),旧记录定期 `yotta-memory archive`。
|
|
34
35
|
4. **多智能体纪律**:FACT 写入公共区,PREF / BOUND / COMMIT 只写本智能体私密区;不读取其他智能体私密区。**一切读写一律走 `yotta-memory` CLI / MCP 工具**——禁止用 shell(`Get-ChildItem` / `Get-Content` / `cat` / `ls` / `type` 等)直接读或改记忆库目录下的 `.md` / `index.json` / `tokens.json` / `agents.json` / `grants.json` 等文件,否则会绕过权限边界、读到别的智能体私密内容。
|
|
@@ -161,9 +162,9 @@ license: MIT
|
|
|
161
162
|
| `yotta-memory reset-password [--password <当前> | --recovery-key <钥匙>] [--new-password <新>]` | 重设主口令(忘口令用恢复钥匙)|
|
|
162
163
|
| `yotta-memory key list / authorize <id> / revoke <id>` | 管理 AI 私密读取授权缓存(authorize 需主口令;revoke 立即吊销该 AI 解密能力)|
|
|
163
164
|
| `yotta-memory remember <type> <subject> <statement> [--owner <id>] [--source <来源>] [--weight <0..>] [--verify] [--no-hint]` | 写入(同 subject+statement 自动更新;--owner 标注归属;--source 记录来源;--weight 重要性权重默认 1.0、去重取 max;--verify 写后回读校验;--no-hint 关闭类型启发式提示)|
|
|
164
|
-
| `yotta-memory recall [关键词] [--type T] [--limit N] [--agent <id>] [--owner <id>] [--all] [--unsafe] [--explain] [--semantic]` | 检索(v0.8.0 默认语义检索:同义词 / 拼音全拼+首字母 / 字段加权 / 模糊匹配 +
|
|
165
|
+
| `yotta-memory recall [关键词] [--type T] [--limit N] [--agent <id>] [--owner <id>] [--all] [--unsafe] [--explain] [--semantic] [--embedding <command>] [--embedding-timeout N]` | 检索(v0.8.0 默认语义检索:同义词 / 拼音全拼+首字母 / 字段加权 / 模糊匹配 + 效用分融合排序;v0.9.0 支持可选本地 embedding 插件,失败自动降级;`--explain` 显示命中理由与效用分项;`--semantic` 显式开启;读取分区过滤;越界读其它智能体私密默认拒绝,需 grant / identity=user / `--unsafe`;`--agent <其它>` 只作身份声明/展示,不授予跨读——读他人私密同样要授权;项目级优先)|
|
|
165
166
|
| `yotta-memory profile [--owner <id>]` | 生成用户画像(聚合 `private/<owner>/` 原文,零推断,写 `profile.md`;跨 owner 默认拒绝)|
|
|
166
|
-
| `yotta-memory context [--limit N] [--owner <id>] [--budget N]` | 生成开工上下文包(身份 + 多智能体铁律 + 画像 + 近期记忆 + 边界 + 承诺;--budget
|
|
167
|
+
| `yotta-memory context [--limit N] [--owner <id>] [--budget N] [--focus <关键词>] [--explain] [--embedding <command>]` | 生成开工上下文包(身份 + 多智能体铁律 + 画像 + 任务相关记忆 + 近期记忆 + 边界 + 承诺;--budget 字符预算,0=不限;--explain 输出 included / dropped 选择 trace)|
|
|
167
168
|
| `yotta-memory forget <文件>` | 删除(按类型目录路径或文件名)|
|
|
168
169
|
| `yotta-memory archive [--days 180] [--threshold 0.35]` | 归档旧记忆(v0.8.0 统一效用分+年龄,immutable 除外;阈值默认读 config `maintain_archived_utility`)|
|
|
169
170
|
| `yotta-memory reindex` | 重建索引(手动改 .md 后校正)|
|
package/USER_GUIDE.md
CHANGED
|
@@ -25,12 +25,12 @@
|
|
|
25
25
|
|
|
26
26
|
**典型使用场景:**
|
|
27
27
|
|
|
28
|
-
- **跨会话续接**:AI 开工 `recall`
|
|
28
|
+
- **跨会话续接**:AI 开工 `recall` 恢复上次上下文,不丢记忆;v0.9.0 起可用 `context --focus <关键词>` 生成任务感知上下文。
|
|
29
29
|
- **多智能体共享**:FACT 进公共区共享,PREF / BOUND / COMMIT 各自私密隔离。
|
|
30
30
|
- **交接与团队协作**:项目级 `.yottamemory` 随仓库走,交接即恢复。
|
|
31
31
|
- **便携记忆盘**:记忆装在固定主机上,本机与局域网其它主机共享同一份记忆(见第 4 / 5 篇)。
|
|
32
32
|
- **越用越懂(v0.6.0)**:AI 按「记忆守则」主动捕获信号,`profile` 聚合画像、`context` 开工注入——用得越久越懂你。
|
|
33
|
-
- **自我学习 / 自我进化 / 自我提升(v0.8.0)**:`recall` 语义检索(同义词 / 拼音 / 字段加权 /
|
|
33
|
+
- **自我学习 / 自我进化 / 自我提升(v0.8.0 + v0.9.0)**:`recall` 语义检索(同义词 / 拼音 / 字段加权 / 模糊,v0.9.0 可选本地 embedding 插件)+ `feedback` 使用反馈闭环 + `maintain` 规则层自组织 + `distill` 心理日志蒸馏——记忆系统会自己整理、提炼、演化。
|
|
34
34
|
|
|
35
35
|
## 2. 安装(CLI + 技能)
|
|
36
36
|
|
|
@@ -142,9 +142,16 @@ statement: 本周完成发布
|
|
|
142
142
|
|
|
143
143
|
- `recall <关键词>` 默认语义检索:同义词(内置词表)、拼音(全拼 / 首字母,内置 3755 常用字表)、字段加权(subject 优先)、模糊匹配(编辑距离 ≤ 2)、子串兜底。
|
|
144
144
|
- 例:记过「越用越懂」,用 `recall yyyd`(首字母)或 `recall yueyong yuedong`(拼音)都能找回。
|
|
145
|
-
- `recall --explain
|
|
145
|
+
- `recall --explain`:显示每条命中理由与效用分项;配置 embedding 插件后还会显示向量相似度。
|
|
146
146
|
- 旧索引首次 recall 自动重建(version 4),无需手动 reindex。
|
|
147
147
|
|
|
148
|
+
**可选本地 embedding 插件(v0.9.0)**
|
|
149
|
+
|
|
150
|
+
- `recall <关键词> --embedding <命令>`:调用本地子进程,stdin 传 `{texts:[]}`,stdout 返回 `{vectors:[]}`;向量与词法得分共同参与排序。
|
|
151
|
+
- `config set embedding_cmd <命令>`:持久启用插件;`--embedding` 优先级更高。
|
|
152
|
+
- `--embedding-timeout N`:默认 3000ms;插件失败、超时或输出非法时自动降级为词法检索,不会中断命令。
|
|
153
|
+
- 向量缓存写入各记忆根下 `.embed/cache.json`,只存向量不存明文;同一命令与文本命中缓存时不再重复调用插件。
|
|
154
|
+
|
|
148
155
|
**使用反馈闭环(feedback)**
|
|
149
156
|
|
|
150
157
|
- `feedback <文件> --useful`:这条记忆有用 → weight ×1.2(上限 3.0)、confidence +0.05、feedback_net +1。
|
|
@@ -308,9 +315,9 @@ yotta-memory remember FACT 主题 内容 # 智能体落盘
|
|
|
308
315
|
|---|---|
|
|
309
316
|
| `yotta-memory init [--project] [--dir <目录>]` | 初始化记忆库 |
|
|
310
317
|
| `yotta-memory remember <类型> <主题> <内容> [--owner <id>] [--source <来源>] [--weight <0..>] [--verify] [--no-hint]` | 写入记忆(--source 来源;--weight 重要性权重;--verify 写后回读;--no-hint 关闭类型提示)|
|
|
311
|
-
| `yotta-memory recall [关键词] [--type T] [--limit N] [--agent <id>] [--owner <id>] [--all] [--unsafe] [--explain]
|
|
318
|
+
| `yotta-memory recall [关键词] [--type T] [--limit N] [--agent <id>] [--owner <id>] [--all] [--unsafe] [--explain] [--semantic] [--embedding <命令>] [--embedding-timeout N]` | 检索记忆(语义 + 效用分排序;可选本地 embedding 插件;读取分区过滤;越界读其它智能体私密默认拒绝,需 grant / identity=user / `--unsafe`)|
|
|
312
319
|
| `yotta-memory profile [--owner <id>]` | 生成用户画像(零推断,写 `profile.md`)|
|
|
313
|
-
| `yotta-memory context [--limit N] [--owner <id>] [--budget N]` |
|
|
320
|
+
| `yotta-memory context [--limit N] [--owner <id>] [--budget N] [--focus <关键词>] [--explain] [--embedding <命令>]` | 开工上下文包(身份+铁律+画像+任务相关记忆+近期记忆+边界+承诺;--focus 任务聚焦;--explain 输出 included/dropped 选择解释)|
|
|
314
321
|
| `yotta-memory forget <文件>` | 删除一条记忆 |
|
|
315
322
|
| `yotta-memory archive [--days 180] [--threshold 0.4]` | 归档旧记忆 |
|
|
316
323
|
| `yotta-memory reindex` | 重建索引 |
|
package/bin/yotta-memory.js
CHANGED
|
@@ -22,7 +22,7 @@ const crypto = require('crypto');
|
|
|
22
22
|
const http = require('http');
|
|
23
23
|
const child_process = require('child_process');
|
|
24
24
|
|
|
25
|
-
const VERSION = '0.
|
|
25
|
+
const VERSION = '0.9.0';
|
|
26
26
|
const TYPES = ['FACT', 'PREF', 'BOUND', 'COMMIT'];
|
|
27
27
|
const TYPE_DIRS = { FACT: 'facts', PREF: 'prefs', BOUND: 'bounds', COMMIT: 'commits' };
|
|
28
28
|
const PUBLIC_DIR = 'facts';
|
|
@@ -150,6 +150,103 @@ function runDistillModel(modelStr, payload) {
|
|
|
150
150
|
const r = child_process.spawnSync(argv[0], argv.slice(1), { input: payload, encoding: 'utf8', maxBuffer: 1024 * 1024, shell: false, windowsHide: true });
|
|
151
151
|
return r;
|
|
152
152
|
}
|
|
153
|
+
function cosineSimilarity(a, b) {
|
|
154
|
+
if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return 0;
|
|
155
|
+
let dot = 0, na = 0, nb = 0;
|
|
156
|
+
for (let i = 0; i < a.length; i++) {
|
|
157
|
+
const x = Number(a[i]) || 0;
|
|
158
|
+
const y = Number(b[i]) || 0;
|
|
159
|
+
dot += x * y;
|
|
160
|
+
na += x * x;
|
|
161
|
+
nb += y * y;
|
|
162
|
+
}
|
|
163
|
+
if (!na || !nb) return 0;
|
|
164
|
+
return dot / (Math.sqrt(na) * Math.sqrt(nb));
|
|
165
|
+
}
|
|
166
|
+
function runEmbeddingPlugin(command, texts, timeoutMs) {
|
|
167
|
+
const argv = splitCommandArgv(String(command || ''));
|
|
168
|
+
if (!argv.length) throw new Error('embedding 命令为空');
|
|
169
|
+
const payload = JSON.stringify({ texts: (texts || []).map(String) });
|
|
170
|
+
const timeout = Math.max(1, parseInt(timeoutMs, 10) || 3000);
|
|
171
|
+
const r = child_process.spawnSync(argv[0], argv.slice(1), {
|
|
172
|
+
input: payload,
|
|
173
|
+
encoding: 'utf8',
|
|
174
|
+
maxBuffer: 1024 * 1024,
|
|
175
|
+
shell: false,
|
|
176
|
+
windowsHide: true,
|
|
177
|
+
timeout: timeout
|
|
178
|
+
});
|
|
179
|
+
if (r.error) throw r.error;
|
|
180
|
+
if (r.status !== 0) throw new Error('embedding 插件退出码 ' + r.status);
|
|
181
|
+
const parsed = JSON.parse(String(r.stdout || ''));
|
|
182
|
+
if (!parsed || !Array.isArray(parsed.vectors)) throw new Error('embedding 插件输出缺少 vectors 数组');
|
|
183
|
+
if (parsed.vectors.length !== texts.length) throw new Error('embedding 向量数量不匹配');
|
|
184
|
+
return parsed.vectors;
|
|
185
|
+
}
|
|
186
|
+
function embeddingCandidates(entries, query, opts) {
|
|
187
|
+
opts = opts || {};
|
|
188
|
+
const command = opts.embedding || '';
|
|
189
|
+
if (!command) return [];
|
|
190
|
+
const texts = [String(query || '')].concat((entries || []).map(function (e) {
|
|
191
|
+
return [e.subject || '', e.statement || '', (e.tags || []).join(' ')].join(' ');
|
|
192
|
+
}));
|
|
193
|
+
const root = opts.root ? String(opts.root) : '';
|
|
194
|
+
let cache = {};
|
|
195
|
+
let cachePath = '';
|
|
196
|
+
if (root) {
|
|
197
|
+
cachePath = path.join(root, '.embed', 'cache.json');
|
|
198
|
+
try {
|
|
199
|
+
fs.mkdirSync(path.dirname(cachePath), { recursive: true });
|
|
200
|
+
cache = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
|
|
201
|
+
} catch (err) {
|
|
202
|
+
cache = {};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const keys = texts.map(function (text) {
|
|
206
|
+
return crypto.createHash('sha256').update(command + '\0' + text).digest('hex');
|
|
207
|
+
});
|
|
208
|
+
const missing = [];
|
|
209
|
+
const missingIndexes = [];
|
|
210
|
+
const vectors = new Array(texts.length);
|
|
211
|
+
for (let i = 0; i < texts.length; i++) {
|
|
212
|
+
if (cache[keys[i]]) {
|
|
213
|
+
vectors[i] = cache[keys[i]];
|
|
214
|
+
} else {
|
|
215
|
+
missing.push(texts[i]);
|
|
216
|
+
missingIndexes.push(i);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (missing.length) {
|
|
220
|
+
const missingVectors = runEmbeddingPlugin(command, missing, opts.embeddingTimeout);
|
|
221
|
+
for (let i = 0; i < missingVectors.length; i++) {
|
|
222
|
+
const idx = missingIndexes[i];
|
|
223
|
+
vectors[idx] = missingVectors[i];
|
|
224
|
+
cache[keys[idx]] = missingVectors[i];
|
|
225
|
+
}
|
|
226
|
+
if (cachePath) {
|
|
227
|
+
try { fs.writeFileSync(cachePath, JSON.stringify(cache, null, 2), 'utf8'); } catch (err) {}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
const queryVec = vectors[0];
|
|
231
|
+
const out = [];
|
|
232
|
+
for (let i = 1; i < vectors.length; i++) {
|
|
233
|
+
const sim = cosineSimilarity(queryVec, vectors[i]);
|
|
234
|
+
if (sim > 0) out.push({ entry: entries[i - 1], score: sim, detail: ['embedding:' + round3(sim)] });
|
|
235
|
+
}
|
|
236
|
+
return out;
|
|
237
|
+
}
|
|
238
|
+
function effectiveEmbeddingCommand(opts) {
|
|
239
|
+
opts = opts || {};
|
|
240
|
+
if (opts.embedding) return String(opts.embedding);
|
|
241
|
+
const cfg = loadConfig();
|
|
242
|
+
return cfg && cfg.embedding_cmd ? String(cfg.embedding_cmd) : '';
|
|
243
|
+
}
|
|
244
|
+
function effectiveEmbeddingTimeout(opts) {
|
|
245
|
+
opts = opts || {};
|
|
246
|
+
if (opts.embeddingTimeout) return parseInt(opts.embeddingTimeout, 10) || 3000;
|
|
247
|
+
const cfg = loadConfig();
|
|
248
|
+
return cfg && cfg.embedding_timeout ? parseInt(cfg.embedding_timeout, 10) || 3000 : 3000;
|
|
249
|
+
}
|
|
153
250
|
function parseFrontmatter(text) {
|
|
154
251
|
const m = text.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
155
252
|
if (!m) return { meta: {}, body: text };
|
|
@@ -1154,6 +1251,28 @@ function recallCore(query, opts) {
|
|
|
1154
1251
|
const wantExplain = !!opts.explain;
|
|
1155
1252
|
const useSemantic = opts.semantic !== false;
|
|
1156
1253
|
const prefilter = (q && useSemantic) ? recallPrefilter(q) : null;
|
|
1254
|
+
const embeddingMap = new Map();
|
|
1255
|
+
const embeddingCommand = effectiveEmbeddingCommand(opts);
|
|
1256
|
+
const embeddingTimeout = effectiveEmbeddingTimeout(opts);
|
|
1257
|
+
if (q && embeddingCommand) {
|
|
1258
|
+
for (const root of roots) {
|
|
1259
|
+
const rootEntries = ensureIndex(root).filter(function (e) {
|
|
1260
|
+
return classifyRead(e, agent, ownerFilter, allSafe, selfAgent) !== 'denied';
|
|
1261
|
+
});
|
|
1262
|
+
try {
|
|
1263
|
+
const hits = embeddingCandidates(rootEntries, q, {
|
|
1264
|
+
embedding: embeddingCommand,
|
|
1265
|
+
embeddingTimeout: embeddingTimeout,
|
|
1266
|
+
root: root
|
|
1267
|
+
});
|
|
1268
|
+
for (const h of hits) {
|
|
1269
|
+
embeddingMap.set(root + '\0' + h.entry.file, h);
|
|
1270
|
+
}
|
|
1271
|
+
} catch (err) {
|
|
1272
|
+
// Plugin failure is not fatal; keep lexical-only recall.
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1157
1276
|
const hits = [];
|
|
1158
1277
|
let deniedCount = 0;
|
|
1159
1278
|
for (const root of roots) {
|
|
@@ -1164,12 +1283,19 @@ function recallCore(query, opts) {
|
|
|
1164
1283
|
if (r === 'denied') { deniedCount++; continue; }
|
|
1165
1284
|
let score = 0;
|
|
1166
1285
|
let detail = null;
|
|
1286
|
+
const embeddingHit = embeddingMap.get(root + '\0' + e.file) || null;
|
|
1167
1287
|
if (q) {
|
|
1168
1288
|
if (useSemantic) {
|
|
1169
|
-
if (prefilter && !prefilter(e)) continue; // v0.8.1
|
|
1289
|
+
if (prefilter && !prefilter(e) && !embeddingHit) continue; // v0.8.1 候选预过滤:粗筛后再语义打分;embedding 命中可越过词法预过滤
|
|
1170
1290
|
const m = semanticMatch(e, q, wantExplain);
|
|
1171
1291
|
score = m.score;
|
|
1172
1292
|
if (wantExplain && m.detail && m.detail.length) detail = m.detail;
|
|
1293
|
+
if (embeddingHit) {
|
|
1294
|
+
score = Math.max(score, embeddingHit.score);
|
|
1295
|
+
if (wantExplain && embeddingHit.detail) {
|
|
1296
|
+
detail = (detail || []).concat(embeddingHit.detail);
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1173
1299
|
} else {
|
|
1174
1300
|
const qtoks = tokenize(q);
|
|
1175
1301
|
for (const tt of qtoks) { if (e.tokens && e.tokens[tt]) score += e.tokens[tt]; }
|
|
@@ -1245,7 +1371,21 @@ function recallCore(query, opts) {
|
|
|
1245
1371
|
if (deniedCount > 0 && explicitCross) {
|
|
1246
1372
|
lines.push('\n[警告] 本次检索共拒绝 ' + deniedCount + ' 条越界访问(其它智能体私密记忆,未授权不展示)。如需读取请加 --unsafe 或 --owner user。');
|
|
1247
1373
|
}
|
|
1248
|
-
return {
|
|
1374
|
+
return {
|
|
1375
|
+
error: false,
|
|
1376
|
+
exitCode: 0,
|
|
1377
|
+
text: lines.join('\n'),
|
|
1378
|
+
entries: shown.map(function (h) {
|
|
1379
|
+
return {
|
|
1380
|
+
root: h.root,
|
|
1381
|
+
file: h.entry.file,
|
|
1382
|
+
type: h.entry.type,
|
|
1383
|
+
subject: h.entry.subject,
|
|
1384
|
+
statement: h.entry.statement,
|
|
1385
|
+
score: h.finalScore === undefined ? h.score : h.finalScore
|
|
1386
|
+
};
|
|
1387
|
+
})
|
|
1388
|
+
};
|
|
1249
1389
|
}
|
|
1250
1390
|
function resolveMemoryFile(root, ref) {
|
|
1251
1391
|
const map = {};
|
|
@@ -2509,6 +2649,11 @@ function contextCore(opts) {
|
|
|
2509
2649
|
const owner = opts.owner || selfAgent;
|
|
2510
2650
|
const unsafe = !!opts.unsafe;
|
|
2511
2651
|
const budget = opts.budget ? parseInt(opts.budget, 10) : 0;
|
|
2652
|
+
const focus = opts.focus ? String(opts.focus) : '';
|
|
2653
|
+
const explain = !!opts.explain;
|
|
2654
|
+
const embeddingCommand = effectiveEmbeddingCommand(opts);
|
|
2655
|
+
const embeddingTimeout = effectiveEmbeddingTimeout(opts);
|
|
2656
|
+
const trace = [];
|
|
2512
2657
|
const lines = [];
|
|
2513
2658
|
const FENCE = String.fromCharCode(96, 96, 96);
|
|
2514
2659
|
function usedChars() { return lines.reduce(function (s, x) { return s + String(x).length + 1; }, 0); }
|
|
@@ -2561,6 +2706,32 @@ function contextCore(opts) {
|
|
|
2561
2706
|
lines.push('(未声明身份,跳过画像;先 iam 登记后可生成)');
|
|
2562
2707
|
}
|
|
2563
2708
|
lines.push('');
|
|
2709
|
+
|
|
2710
|
+
if (focus) {
|
|
2711
|
+
lines.push('## 2.5 任务相关记忆(--focus)');
|
|
2712
|
+
lines.push('');
|
|
2713
|
+
const focused = recallCore(focus, {
|
|
2714
|
+
limit: limit,
|
|
2715
|
+
owner: owner,
|
|
2716
|
+
unsafe: unsafe,
|
|
2717
|
+
selfAgent: selfAgent,
|
|
2718
|
+
embedding: embeddingCommand,
|
|
2719
|
+
embeddingTimeout: embeddingTimeout
|
|
2720
|
+
});
|
|
2721
|
+
const focusedEntries = focused.entries || [];
|
|
2722
|
+
if (!focusedEntries.length) lines.push('(无匹配记忆)');
|
|
2723
|
+
for (const e of focusedEntries) {
|
|
2724
|
+
const line = '- [' + e.type + '] ' + e.subject + ': ' + e.statement;
|
|
2725
|
+
if (budget > 0 && usedChars() + line.length > budget) {
|
|
2726
|
+
trace.push('[dropped] ' + e.file + ' reason: budget_exceeded');
|
|
2727
|
+
continue;
|
|
2728
|
+
}
|
|
2729
|
+
lines.push(line);
|
|
2730
|
+
trace.push('[included] ' + e.file + ' reason: focus_match score: ' + round3(e.score));
|
|
2731
|
+
}
|
|
2732
|
+
lines.push('');
|
|
2733
|
+
}
|
|
2734
|
+
|
|
2564
2735
|
lines.push('## 3. 近期记忆(按活跃度前 ' + limit + ' 条)');
|
|
2565
2736
|
lines.push('');
|
|
2566
2737
|
const recent = [];
|
|
@@ -2576,8 +2747,12 @@ function contextCore(opts) {
|
|
|
2576
2747
|
if (!recentShown.length) lines.push('(暂无记忆)');
|
|
2577
2748
|
for (const h of recentShown) {
|
|
2578
2749
|
const line = '- [' + h.e.type + '] ' + h.e.subject + ': ' + h.e.statement;
|
|
2579
|
-
if (budget > 0 && usedChars() + line.length > budget)
|
|
2750
|
+
if (budget > 0 && usedChars() + line.length > budget) {
|
|
2751
|
+
trace.push('[dropped] ' + h.e.file + ' reason: budget_exceeded');
|
|
2752
|
+
break;
|
|
2753
|
+
}
|
|
2580
2754
|
lines.push(line);
|
|
2755
|
+
trace.push('[included] ' + h.e.file + ' reason: recent_match');
|
|
2581
2756
|
}
|
|
2582
2757
|
lines.push('');
|
|
2583
2758
|
lines.push('## 4. 边界提醒(BOUND)');
|
|
@@ -2620,6 +2795,13 @@ function contextCore(opts) {
|
|
|
2620
2795
|
}
|
|
2621
2796
|
const oldCount = allEntries.filter(function (e) { return e.created && daysBetween(e.created, today()) > 180 && classifyRead(e, owner, '', unsafe, selfAgent) !== 'denied'; }).length;
|
|
2622
2797
|
if (oldCount > 0) lines.push('- 归档提醒: ' + oldCount + ' 条超 180 天,建议 archive');
|
|
2798
|
+
if (explain) {
|
|
2799
|
+
lines.push('');
|
|
2800
|
+
lines.push('## 7. 选择解释(--explain)');
|
|
2801
|
+
lines.push('');
|
|
2802
|
+
if (!trace.length) lines.push('(无选择记录)');
|
|
2803
|
+
for (const t of trace) lines.push(t);
|
|
2804
|
+
}
|
|
2623
2805
|
return { error: false, exitCode: 0, text: lines.join('\n') };
|
|
2624
2806
|
}
|
|
2625
2807
|
function cmdContext(opts) {
|
|
@@ -2630,16 +2812,20 @@ function cmdContext(opts) {
|
|
|
2630
2812
|
|
|
2631
2813
|
// ---- config 命令 ----
|
|
2632
2814
|
function cmdConfigSet(key, value) {
|
|
2633
|
-
if (key !== 'memory_home') { console.error('未知配置项: ' + key + '(可用: memory_home)'); process.exit(2); }
|
|
2634
|
-
if (!value) { console.error('缺少值: config set
|
|
2815
|
+
if (key !== 'memory_home' && key !== 'embedding_cmd' && key !== 'embedding_timeout') { console.error('未知配置项: ' + key + '(可用: memory_home / embedding_cmd / embedding_timeout)'); process.exit(2); }
|
|
2816
|
+
if (!value) { console.error('缺少值: config set ' + key + ' <值>'); process.exit(2); }
|
|
2635
2817
|
const cfg = loadConfig();
|
|
2636
|
-
cfg.memory_home = value;
|
|
2818
|
+
if (key === 'memory_home') cfg.memory_home = value;
|
|
2819
|
+
else if (key === 'embedding_cmd') cfg.embedding_cmd = value;
|
|
2820
|
+
else if (key === 'embedding_timeout') cfg.embedding_timeout = parseInt(value, 10) || 3000;
|
|
2637
2821
|
saveConfig(cfg);
|
|
2638
|
-
console.log('
|
|
2822
|
+
console.log('已写入配置: ' + key + ' = ' + (key === 'embedding_timeout' ? (parseInt(value, 10) || 3000) : value));
|
|
2639
2823
|
}
|
|
2640
2824
|
function cmdConfigGet() {
|
|
2641
2825
|
const cfg = loadConfig();
|
|
2642
2826
|
console.log('memory_home: ' + (cfg.memory_home || '(未设置,默认 ~/.yottamemory)'));
|
|
2827
|
+
console.log('embedding_cmd: ' + (cfg.embedding_cmd || '(未设置)'));
|
|
2828
|
+
console.log('embedding_timeout: ' + (cfg.embedding_timeout || 3000));
|
|
2643
2829
|
console.log('当前生效用户级位置: ' + userRoot());
|
|
2644
2830
|
if (cfg.serve && Object.keys(cfg.serve).length) console.log('serve: ' + JSON.stringify(cfg.serve));
|
|
2645
2831
|
}
|
|
@@ -2648,8 +2834,9 @@ function cmdConfigGet() {
|
|
|
2648
2834
|
function mcpTools() {
|
|
2649
2835
|
return [
|
|
2650
2836
|
{ name: 'remember', description: '写入一条记忆。参数 type(FACT/PREF/BOUND/COMMIT)、subject、statement 必填;owner 可选,默认当前智能体', inputSchema: { type: 'object', properties: { type: { type: 'string', description: 'FACT/PREF/BOUND/COMMIT' }, subject: { type: 'string' }, statement: { type: 'string' }, owner: { type: 'string' } }, required: ['type', 'subject', 'statement'] } },
|
|
2651
|
-
{ name: 'recall', description: '检索记忆。query 可选;type 可选;limit 可选(默认 20
|
|
2652
|
-
{ name: 'search', description: '检索记忆(同 recall)。query 可选;type 可选;limit 可选(默认 20
|
|
2837
|
+
{ name: 'recall', description: '检索记忆。query 可选;type 可选;limit 可选(默认 20);explain 可选。只返回当前智能体可读记忆;embedding 插件只能由本机 config 配置,远端不可传命令', inputSchema: { type: 'object', properties: { query: { type: 'string' }, type: { type: 'string' }, limit: { type: 'number' }, embeddingTimeout: { type: 'number' }, explain: { type: 'boolean' } } } },
|
|
2838
|
+
{ name: 'search', description: '检索记忆(同 recall)。query 可选;type 可选;limit 可选(默认 20);explain 可选;embedding 插件只能由本机 config 配置,远端不可传命令', inputSchema: { type: 'object', properties: { query: { type: 'string' }, type: { type: 'string' }, limit: { type: 'number' }, embeddingTimeout: { type: 'number' }, explain: { type: 'boolean' } } } },
|
|
2839
|
+
{ name: 'context', description: '生成开工上下文包。focus 可选;limit 可选;budget 可选;explain 可选;embedding 插件只能由本机 config 配置,远端不可传命令', inputSchema: { type: 'object', properties: { focus: { type: 'string' }, limit: { type: 'number' }, budget: { type: 'number' }, explain: { type: 'boolean' }, embeddingTimeout: { type: 'number' } } } },
|
|
2653
2840
|
{ name: 'forget', description: '删除一条记忆。file 为记忆文件路径(如 facts/2026-08-24-0001.md 或文件名)', inputSchema: { type: 'object', properties: { file: { type: 'string' } }, required: ['file'] } },
|
|
2654
2841
|
{ name: 'archive', description: '归档旧记忆。days 默认 180;threshold 默认 0.4', inputSchema: { type: 'object', properties: { days: { type: 'number' }, threshold: { type: 'number' } } } },
|
|
2655
2842
|
{ name: 'reindex', description: '重建索引(手动改 .md 后校正;扫描 facts/prefs/bounds/commits 四目录)', inputSchema: { type: 'object', properties: {} } },
|
|
@@ -2673,7 +2860,24 @@ function callTool(name, args, ctx) {
|
|
|
2673
2860
|
return { text: r.text, error: r.error };
|
|
2674
2861
|
}
|
|
2675
2862
|
if (name === 'recall' || name === 'search') {
|
|
2676
|
-
const r = recallCore(args.query ? String(args.query) : null, {
|
|
2863
|
+
const r = recallCore(args.query ? String(args.query) : null, {
|
|
2864
|
+
limit: args.limit || 20,
|
|
2865
|
+
type: args.type ? String(args.type) : null,
|
|
2866
|
+
agent: agent,
|
|
2867
|
+
embeddingTimeout: args.embeddingTimeout || 3000,
|
|
2868
|
+
explain: !!args.explain
|
|
2869
|
+
});
|
|
2870
|
+
return { text: r.text, error: r.error };
|
|
2871
|
+
}
|
|
2872
|
+
if (name === 'context') {
|
|
2873
|
+
const r = contextCore({
|
|
2874
|
+
focus: args.focus ? String(args.focus) : '',
|
|
2875
|
+
limit: args.limit || 10,
|
|
2876
|
+
budget: args.budget || 0,
|
|
2877
|
+
explain: !!args.explain,
|
|
2878
|
+
embeddingTimeout: args.embeddingTimeout || 3000,
|
|
2879
|
+
selfAgent: agent
|
|
2880
|
+
});
|
|
2677
2881
|
return { text: r.text, error: r.error };
|
|
2678
2882
|
}
|
|
2679
2883
|
if (name === 'forget') {
|
|
@@ -3315,7 +3519,7 @@ function usage() {
|
|
|
3315
3519
|
['view', '启动用户查看平台(--port/--host;口令解锁浏览/授权/吊销 AI)'],
|
|
3316
3520
|
['reset-password', '重设主口令(忘口令用恢复钥匙)'],
|
|
3317
3521
|
['key', '管理 AI 私密读取授权缓存(list / authorize <id> / revoke <id>)'],
|
|
3318
|
-
['config', '
|
|
3522
|
+
['config', '查看/设置配置(get / set memory_home <目录> / set embedding_cmd <命令> / set embedding_timeout <毫秒>)']
|
|
3319
3523
|
]],
|
|
3320
3524
|
['平台与服务', [
|
|
3321
3525
|
['serve', '启动 MCP 记忆引擎(streamable HTTP;--stdio 本地零进程)'],
|
|
@@ -3343,7 +3547,7 @@ async function main() {
|
|
|
3343
3547
|
if (!args.length) { usage(); return; }
|
|
3344
3548
|
const opts = {};
|
|
3345
3549
|
const positional = [];
|
|
3346
|
-
const valueOpts = new Set(['--type', '--limit', '--days', '--out', '--owner', '--agent', '--threshold', '--scope', '--host', '--port', '--dir', '--name', '--user', '--relationship', '--source', '--weight', '--budget', '--password', '--new-password', '--recovery-key', '--reason', '--merge', '--model', '--subject']);
|
|
3550
|
+
const valueOpts = new Set(['--type', '--limit', '--days', '--out', '--owner', '--agent', '--threshold', '--scope', '--host', '--port', '--dir', '--name', '--user', '--relationship', '--source', '--weight', '--budget', '--password', '--new-password', '--recovery-key', '--reason', '--merge', '--model', '--subject', '--embedding', '--focus', '--embedding-timeout']);
|
|
3347
3551
|
for (let i = 0; i < args.length; i++) {
|
|
3348
3552
|
const a = args[i];
|
|
3349
3553
|
if (a === '--version' || a === '-v') { console.log(VERSION); return; }
|
|
@@ -3394,6 +3598,9 @@ async function main() {
|
|
|
3394
3598
|
else if (a === '--merge') opts.merge = v;
|
|
3395
3599
|
else if (a === '--model') opts.model = v;
|
|
3396
3600
|
else if (a === '--subject') opts.subject = v;
|
|
3601
|
+
else if (a === '--embedding') opts.embedding = v;
|
|
3602
|
+
else if (a === '--focus') opts.focus = v;
|
|
3603
|
+
else if (a === '--embedding-timeout') opts.embeddingTimeout = parseInt(v, 10) || 3000;
|
|
3397
3604
|
} else if (a.startsWith('--')) {
|
|
3398
3605
|
console.error('未知选项: ' + a);
|
|
3399
3606
|
process.exit(2);
|
|
@@ -3411,7 +3618,7 @@ async function main() {
|
|
|
3411
3618
|
const sub = rest[0];
|
|
3412
3619
|
if (sub === 'set') cmdConfigSet(rest[1], rest[2]);
|
|
3413
3620
|
else if (sub === 'get') cmdConfigGet();
|
|
3414
|
-
else { console.error('config 子命令: set memory_home <目录> / get'); process.exit(2); }
|
|
3621
|
+
else { console.error('config 子命令: set memory_home <目录> / set embedding_cmd <命令> / set embedding_timeout <毫秒> / get'); process.exit(2); }
|
|
3415
3622
|
break;
|
|
3416
3623
|
}
|
|
3417
3624
|
case 'remember': cmdRemember(rest[0], rest[1], rest[2], opts); break;
|
|
@@ -3536,6 +3743,9 @@ module.exports = {
|
|
|
3536
3743
|
utilityScore: utilityScore,
|
|
3537
3744
|
utilityBreakdown: utilityBreakdown,
|
|
3538
3745
|
semanticMatch: semanticMatch,
|
|
3746
|
+
runEmbeddingPlugin: runEmbeddingPlugin,
|
|
3747
|
+
cosineSimilarity: cosineSimilarity,
|
|
3748
|
+
embeddingCandidates: embeddingCandidates,
|
|
3539
3749
|
pinyinTokens: pinyinTokens,
|
|
3540
3750
|
isEncrypted: isEncrypted,
|
|
3541
3751
|
collectOwners: collectOwners,
|
|
@@ -3585,4 +3795,3 @@ module.exports = {
|
|
|
3585
3795
|
viewServerCore: viewServerCore,
|
|
3586
3796
|
|
|
3587
3797
|
};
|
|
3588
|
-
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yottameta/yotta-memory",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Yuanyi (元忆) — boundary-aware, file-based memory for AI agents. File-based, zero-dependency, diff/rollback-able; FACT/PREF/BOUND/COMMIT four types (public shared / private isolated), user-level + project-level storage; v0.8 semantic search
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "Yuanyi (元忆) — boundary-aware, file-based memory for AI agents. File-based, zero-dependency, diff/rollback-able; FACT/PREF/BOUND/COMMIT four types (public shared / private isolated), user-level + project-level storage; v0.9 recall quality, context focus, optional local embedding plugin, plus v0.8 semantic search, feedback loop, self-organization, and distillation.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"agent-skills",
|