@yottameta/yotta-memory 0.5.3 → 0.6.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/README.md +21 -8
- package/SKILL.md +90 -11
- package/USER_GUIDE.md +21 -5
- package/bin/yotta-memory.js +412 -32
- package/package.json +1 -1
- package/references/protocol.md +31 -5
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
<p align="center">元忆 —— 有权限边界的文件式智能体记忆:让任何 AI 智能体活过会话,而不是只活在单次对话里。</p>
|
|
8
8
|
<p align="center">开工 <code>recall</code> 恢复上下文、重要信息 <code>remember</code> 落盘、收工归档;记忆是落在用户自己目录里的 Markdown 文件——<b>可读、可改、可审计、可回滚</b>,零依赖、即装即用。</p>
|
|
9
9
|
<p align="center">FACT 共享、PREF / BOUND / COMMIT 私密隔离——<b>谁该读、谁不该读,由机制而非 AI 自觉决定</b>;一份记忆可跨智能体共用,记忆库随盘走、局域网可共享。</p>
|
|
10
|
+
<p align="center">v0.6.0 起「越用越懂」:<code>profile</code> 聚合用户画像(零推断)+ <code>context</code> 一键开工上下文包(身份 + 画像 + 近期记忆 + 边界 + 承诺),记忆从「存储」成长为「会成长的记忆系统」。</p>
|
|
10
11
|
|
|
11
12
|
<p align="center">
|
|
12
13
|
<a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-blue" /></a>
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
- **记忆就是文件**:每条记忆是一个带 YAML frontmatter 的 Markdown 文件,放在用户自己的目录里。任何编辑器都能看、能改、能删;git 直接做版本管理与回滚,团队同步与交接走同一条标准工具链。
|
|
27
28
|
- **隔离由机制保证**:FACT 进公共区共享,PREF / BOUND / COMMIT 进私密区、按 owner 物理分目录(`private/<owner>/<type>/`)。读取按 scope/owner 分区过滤,越界内容由 CLI 拦截、永不返回(默认静默跳过;显式跨读无授权报错拒绝);读写一律走 CLI / MCP,禁止 shell 直读写库文件——权限由机制把关,不依赖 AI 的「自觉」。
|
|
28
29
|
- **零依赖、即装即用**:无守护进程、无数据库、无向量库,只需 Node.js。安装即用,数据留在本机,任何机器都能部署。
|
|
30
|
+
- **越用越懂(v0.6.0)**:`profile` 聚合用户画像(引擎零推断,只归组原文)+ `context` 一键生成开工上下文包(身份 + 画像 + 近期记忆 + 边界 + 承诺);SKILL「记忆守则」注入灵魂盘规则层(类型红线 / 触发信号 / 了解用户 / 底线 / 宿主隔离),只注入规则与机制、不注入人格数据,出厂零数据。
|
|
29
31
|
- **便携记忆盘**:记忆库本身就是记忆引擎——装在硬盘或主机上随盘走,局域网内其它主机上的智能体可远程读写;本地零进程与局域网常驻两种模式可并存,插上硬盘即恢复全部记忆。
|
|
30
32
|
|
|
31
33
|
## 核心优势
|
|
@@ -38,9 +40,10 @@
|
|
|
38
40
|
| **轻量零依赖** | 无 daemon / 无数据库 / 无向量库;Node.js 自带即跑,任何机器可部署 |
|
|
39
41
|
| **双级存储** | 用户级 `~/.yottamemory/`(跨项目)+ 项目级 `.yottamemory/`(随项目共享 / 交接)|
|
|
40
42
|
| **检索与生命周期** | 索引 + TF 打分(中文分词友好);质量分 / 盖棺分 / 自动归档,记忆库不会无限膨胀 |
|
|
43
|
+
| **越用越懂(v0.6.0)** | `profile` 画像聚合(零推断)+ `context` 开工上下文包(身份 / 画像 / 近期记忆 / 边界 / 承诺)+ SKILL「记忆守则」规则层,记忆随使用成长 |
|
|
41
44
|
| **生态分发** | GitHub + npm 双源同步发布;npx skills / npm / install.sh 三种方式,覆盖 17+ 类智能体目录 |
|
|
42
45
|
| **便携记忆盘(随盘走)** | 记忆库即引擎:装在硬盘 / 主机上,插上即恢复全部记忆;引擎主机只需装 CLI 当存放点,无需装任何 AI 智能体 |
|
|
43
|
-
| **局域网共享与自启** | 每智能体独立 token(Bearer + X-Agent-Id)鉴权、可吊销;`lan enable`
|
|
46
|
+
| **局域网共享与自启** | 每智能体独立 token(Bearer + X-Agent-Id)鉴权、可吊销;`lan enable` 注册开机自启(Windows:优先计划任务;非管理员自动降级用户级 Startup 静默自启);MCP 工具集与 CLI 一致(8 个工具),管理动作不远程暴露 |
|
|
44
47
|
| **本地 / 局域网双模式** | 本地 `serve --stdio` 零进程、按需拉起(无常驻);局域网 streamable HTTP 常驻——两种模式可并存、按需选用 |
|
|
45
48
|
|
|
46
49
|
## 功能详解
|
|
@@ -83,6 +86,12 @@
|
|
|
83
86
|
- **本机免 token**:本机 CLI / stdio 直连不经网络、不校验 token;身份经该智能体 MCP 配置的 `env.YOTTA_AGENT_ID` 声明。本机多个智能体各自声明唯一 ID,互不撞。
|
|
84
87
|
- **私密记忆必须有 owner**:写 PREF / BOUND / COMMIT 时未声明身份会被拒绝(公共 FACT 不受影响),从机制上防止「抄别人的 ID」。
|
|
85
88
|
|
|
89
|
+
### 画像与开工上下文(v0.6.0)
|
|
90
|
+
|
|
91
|
+
- **profile**:聚合 `private/<owner>/` 下 PREF / BOUND / COMMIT 原文,按 type + subject + tags 归组,写 `profile.md`;引擎零推断,画像结论由 AI 依据「记忆守则」内部形成,不当面贴标签。
|
|
92
|
+
- **context**:一键生成开工上下文包——身份 + 用户画像摘要 + 近期记忆(按活跃度)+ 边界提醒 + 承诺 / 锚点,作为会话主注入(替代裸 `recall`)。
|
|
93
|
+
- **记忆守则**:SKILL.md 内置规则层(类型红线 / 主动捕获触发信号 / 了解用户三阶段四手法 / 心理学底座 / 底线与边界 / 宿主隔离 / 反模式),让 AI「越用越懂」有章法。
|
|
94
|
+
|
|
86
95
|
### 检索:索引 + 中文分词打分
|
|
87
96
|
|
|
88
97
|
- `remember` 时自动构建 `index.json` 索引;recall 用 TF 打分排序,中文按 bigram 分词,不用额外模型。
|
|
@@ -100,13 +109,13 @@
|
|
|
100
109
|
|
|
101
110
|
### 智能体接入
|
|
102
111
|
|
|
103
|
-
把技能装进智能体后,SKILL.md 会自动教会智能体一套工作流:**开工 `
|
|
112
|
+
把技能装进智能体后,SKILL.md 会自动教会智能体一套工作流:**开工 `context` 主注入(身份 + 画像 + 近期记忆 + 边界 + 承诺)→ 重要信息 `remember` 落盘 → 收工归档**,并遵循「记忆守则」(类型红线 / 触发信号 / 底线 / 宿主隔离)。也可在对话中直接说「记住 XXX」「上次说到哪了」「记一笔」触发。
|
|
104
113
|
|
|
105
114
|
### 便携记忆盘(记忆库即引擎)
|
|
106
115
|
|
|
107
116
|
- **记忆库即引擎**:`serve` 把记忆目录挂成 MCP 服务,目录随盘走;引擎主机只需装 CLI 当存放点,无需装任何 AI 智能体。
|
|
108
117
|
- **双模式可并存**:本地 `serve --stdio` 零进程(客户端按需拉起);局域网 streamable HTTP(默认 `0.0.0.0:8787`)+ 每智能体 token 鉴权。
|
|
109
|
-
- **开机自启**:`lan enable` 注册 Windows
|
|
118
|
+
- **开机自启**:`lan enable` 注册 Windows 开机自启——优先计划任务(默认登录自启,`--onstart` 开机即启需管理员);非管理员(计划任务被拒)自动降级为**用户级 Startup 静默自启**(免管理员);`lan disable` 移除,`lan status` 查询。
|
|
110
119
|
- **安全边界**:管理动作(init / config / token / lan / serve)不进 MCP,token 不远程暴露;远程智能体只能读写记忆,不能改配置、不能管 token。
|
|
111
120
|
- 完整操作步骤见上文「局域网多机共享」章节与 [USER_GUIDE.md](USER_GUIDE.md)。
|
|
112
121
|
|
|
@@ -174,18 +183,20 @@ npm i -g @yottameta/yotta-memory
|
|
|
174
183
|
| 命令 | 作用 |
|
|
175
184
|
|---|---|
|
|
176
185
|
| `yotta-memory init [--project] [--dir <目录>]` | 初始化记忆库(默认用户级 `~/.yottamemory/`;--dir 显式指定位置)|
|
|
177
|
-
| `yotta-memory remember <type> <subject> <statement> [--owner <id>]` | 写入记忆(同 subject+statement 自动更新;--owner
|
|
186
|
+
| `yotta-memory remember <type> <subject> <statement> [--owner <id>] [--verify] [--no-hint]` | 写入记忆(同 subject+statement 自动更新;--owner 标注归属;--verify 写后回读校验;--no-hint 关闭类型启发式提示)|
|
|
178
187
|
| `yotta-memory recall [关键词] [--type T] [--limit N] [--agent <id>] [--owner <id>] [--all] [--unsafe]` | 检索记忆(索引+TF 打分,读取分区过滤;越界读其它智能体私密默认拒绝,需 grant / identity=user / `--unsafe`;`--agent <其它>` 仅作身份声明、不授予跨读;项目级优先)|
|
|
188
|
+
| `yotta-memory profile [--owner <id>]` | 生成用户画像(聚合 `private/<owner>/` 原文,零推断,写 `profile.md`;跨 owner 默认拒绝)|
|
|
189
|
+
| `yotta-memory context [--limit N] [--owner <id>]` | 生成开工上下文包(身份 + 画像 + 近期记忆 + 边界 + 承诺,stdout 不落盘)|
|
|
179
190
|
| `yotta-memory forget <文件>` | 删除一条记忆(按类型目录路径或文件名)|
|
|
180
191
|
| `yotta-memory archive [--days 180] [--threshold 0.4]` | 归档旧记忆(盖棺分+年龄,immutable 除外)|
|
|
181
192
|
| `yotta-memory reindex` | 重建索引(手动改 .md 后校正)|
|
|
182
193
|
| `yotta-memory export [--out f.json]` / `import <f.json>` | 导出 / 导入 |
|
|
183
194
|
| `yotta-memory config set memory_home <目录>` / `config get` | 持久记住 / 查看记忆库位置(`~/.yottamemory/config.json`)|
|
|
184
195
|
| `yotta-memory whoami` | 查看当前智能体身份与登记状态(读 `YOTTA_AGENT_ID` / `X-Agent-Id`,不猜不默认)|
|
|
185
|
-
| `yotta-memory iam <id> [--force]` | 登记本智能体唯一身份并自动落自我档案(`agents.json`,ID
|
|
196
|
+
| `yotta-memory iam <id> [--name <显示名>] [--user <用户名>] [--relationship <关系>] [--force]` | 登记本智能体唯一身份并自动落自我档案(`agents.json`,ID 必须唯一;可选扩展显示名 / 用户 / 关系)|
|
|
186
197
|
| `yotta-memory token new --agent <id> [--force]` / `token list` / `token revoke --agent <id>` | 为智能体生成 / 列出 / 吊销访问 token(登记于记忆库 `.server/tokens.json`;同 ID 已被其它来源占用需 `--force` 覆盖,防不同智能体合流)|
|
|
187
198
|
| `yotta-memory serve [--host 0.0.0.0] [--port 8787] [--no-auth] [--stdio]` | 启动 MCP 记忆引擎(streamable HTTP 局域网 / --stdio 本地零进程模式;Bearer token + X-Agent-Id 鉴权)|
|
|
188
|
-
| `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理(Windows
|
|
199
|
+
| `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理(Windows:计划任务,默认 ONLOGON 登录自启、--onstart 开机即启需管理员;非管理员自动降级用户级 Startup 静默自启)|
|
|
189
200
|
|
|
190
201
|
类型:`FACT`(事实,公共共享)/ `PREF`(偏好)/ `BOUND`(边界)/ `COMMIT`(承诺)。
|
|
191
202
|
|
|
@@ -194,6 +205,8 @@ npm i -g @yottameta/yotta-memory
|
|
|
194
205
|
yotta-memory init
|
|
195
206
|
yotta-memory remember PREF 用户 偏好短回复,不要用表情
|
|
196
207
|
yotta-memory recall 偏好
|
|
208
|
+
yotta-memory profile
|
|
209
|
+
yotta-memory context --limit 10
|
|
197
210
|
yotta-memory recall --type FACT --limit 10
|
|
198
211
|
```
|
|
199
212
|
|
|
@@ -226,10 +239,10 @@ yotta-memory recall --type FACT --limit 10
|
|
|
226
239
|
3. 启动服务(默认监听 0.0.0.0:8787,Bearer token + X-Agent-Id 鉴权)——临时运行或注册开机自启二选一:
|
|
227
240
|
```bash
|
|
228
241
|
yotta-memory serve # 临时前台运行
|
|
229
|
-
yotta-memory lan enable # 注册开机自启(Windows
|
|
242
|
+
yotta-memory lan enable # 注册开机自启(Windows:计划任务;非管理员自动降级用户级 Startup 静默自启)
|
|
230
243
|
yotta-memory lan status # 查看自启状态
|
|
231
244
|
```
|
|
232
|
-
> `lan enable --onstart`
|
|
245
|
+
> `lan enable --onstart` 改为开机即启(需管理员);非管理员时 `lan enable` 自动改用用户级 Startup 静默自启(无需管理员);`lan disable` 移除自启。
|
|
233
246
|
|
|
234
247
|
> 首次监听 0.0.0.0 时,Windows / 系统防火墙可能询问是否放行,需允许放行,否则局域网内其它主机无法访问;`--no-auth` 会关闭鉴权,仅限可信内网使用。
|
|
235
248
|
|
package/SKILL.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: yotta-memory
|
|
3
|
-
description: "元忆 —— 有权限边界的文件式智能体记忆。文件式、零依赖、可 diff/回滚:让任何 AI 智能体活过会话,开工 recall 恢复上下文、重要信息 remember 落盘、收工归档。类型体系 FACT(公共共享)/ PREF / BOUND / COMMIT(私密隔离)。触发:记住、别忘了、记一笔、记忆、remember、recall
|
|
4
|
-
version: 0.
|
|
3
|
+
description: "元忆 —— 有权限边界的文件式智能体记忆。文件式、零依赖、可 diff/回滚:让任何 AI 智能体活过会话,开工 recall 恢复上下文、重要信息 remember 落盘、收工归档。类型体系 FACT(公共共享)/ PREF / BOUND / COMMIT(私密隔离)。触发:记住、别忘了、记一笔、记忆、remember、recall、跨会话、上次说到、续测、交接、归档、记忆盘、共享记忆、局域网记忆、画像、开工上下文、记忆守则、profile、context、越用越懂"
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
license: MIT
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# yotta-memory(元忆)— 有权限边界的文件式智能体记忆
|
|
9
9
|
|
|
10
10
|
> 一句话:元忆 —— 有权限边界的文件式智能体记忆(不注入、可 diff、能回滚;FACT 共享、PREF / BOUND / COMMIT 私密隔离)。
|
|
11
|
-
> 版本:0.
|
|
11
|
+
> 版本:0.6.0 | 最后更新:2026-08-25
|
|
12
12
|
|
|
13
13
|
## 这是什么
|
|
14
14
|
|
|
@@ -16,6 +16,7 @@ license: MIT
|
|
|
16
16
|
- **零依赖**:无 daemon / 无数据库 / 无向量库,Node.js 自带即可运行。
|
|
17
17
|
- **类型体系**:FACT(事实,公共共享)/ PREF(偏好,私密)/ BOUND(边界,私密)/ COMMIT(承诺,私密)。
|
|
18
18
|
- **双级存储**:用户级 `~/.yottamemory/`(跨项目)+ 项目级 `.yottamemory/`(随项目共享)。
|
|
19
|
+
- **越用越懂(v0.6.0)**:`profile` 聚合用户画像(引擎零推断,只归组原文)+ `context` 一键生成开工上下文包(身份 + 画像 + 近期记忆 + 边界 + 承诺)+ SKILL「记忆守则」规则层;只注入规则与机制,不注入人格数据(出厂零数据)。
|
|
19
20
|
|
|
20
21
|
## 何时使用(触发)
|
|
21
22
|
|
|
@@ -26,12 +27,81 @@ license: MIT
|
|
|
26
27
|
|
|
27
28
|
## 核心流程
|
|
28
29
|
|
|
29
|
-
1. **开工定向**:先按「开工第一步:确认记忆位置 + 智能体身份」检测记忆库与身份,再运行 `yotta-memory recall
|
|
30
|
+
1. **开工定向**:先按「开工第一步:确认记忆位置 + 智能体身份」检测记忆库与身份,再运行 `yotta-memory context`(主注入:身份 + 用户画像 + 近期记忆 + 边界 + 承诺)恢复上下文,需要细节再 `yotta-memory recall <关键词>`;项目级记忆优先,其次用户级。
|
|
30
31
|
2. **进行中落盘**:重要信息立即 `yotta-memory remember <type> <subject> <statement>`,不攒到收工。
|
|
31
32
|
3. **收工归档**:写会话小结(COMMIT / 笔记),旧记录定期 `yotta-memory archive`。
|
|
32
33
|
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` 等文件,否则会绕过权限边界、读到别的智能体私密内容。
|
|
33
34
|
|
|
34
35
|
|
|
36
|
+
## 记忆守则(Memory Doctrine,v0.6.0)
|
|
37
|
+
|
|
38
|
+
> 出厂规则层:把灵魂盘「越用越懂」机制固化进记忆引擎——只注入**规则与机制**,不注入任何人格数据(出厂零数据)。来源:灵魂盘 core/rules(psychology / user-analysis / active-memory / boundaries / host-isolation)。
|
|
39
|
+
|
|
40
|
+
### 1. 类型红线(写前必选)
|
|
41
|
+
|
|
42
|
+
| 类型 | 归属 | 何时用 | 反例 |
|
|
43
|
+
|---|---|---|---|
|
|
44
|
+
| FACT | 公共 `facts/` | 客观事实、可共享信息 | 用户偏好 / 关系(应 PREF)|
|
|
45
|
+
| PREF | 私密 `private/<owner>/prefs/` | 偏好、习惯、禁忌 | 客观公开事实(应 FACT)|
|
|
46
|
+
| BOUND | 私密 `private/<owner>/bounds/` | 边界、铁律、不可做的事 | 一次性闲聊 |
|
|
47
|
+
| COMMIT | 私密 `private/<owner>/commits/` | 承诺、锚定、长期关系事件 | 临时待办 |
|
|
48
|
+
|
|
49
|
+
- 私密记忆必须有 owner(引擎强制);跨 owner 写拒绝。拿不准 → 默认 PREF(宁可私密,不误入公共区)。
|
|
50
|
+
- 类型启发式提示:statement 含主观/关系词(用户 / 偏好 / 喜欢 / 关系 / 称呼 等)却选了 FACT 时,引擎会提示「建议 PREF」,仅提示不拦截(`--no-hint` 关闭)。
|
|
51
|
+
|
|
52
|
+
### 2. 主动捕获触发信号(对话中实时识别,不等「帮我记一下」)
|
|
53
|
+
|
|
54
|
+
命中即记(增量写,不攒到收工):
|
|
55
|
+
- 事实陈述(「我是…」「我家…」)→ FACT / PREF
|
|
56
|
+
- 偏好表达(「我喜欢…」「别用…」「以后…」)→ PREF
|
|
57
|
+
- 情绪基调(持续负面 / 疲惫 / 兴奋)→ 交互记忆,影响关怀策略
|
|
58
|
+
- 纠正(「不是,我是说…」「你记错了」)→ 更新旧记录
|
|
59
|
+
- 关系事件(「今天累惨了」「我们…」)→ COMMIT
|
|
60
|
+
- 边界划定(「这个别动」「别提这个」)→ BOUND
|
|
61
|
+
|
|
62
|
+
**不记录**:一次性闲聊、无长期价值、纯礼貌套话、用户明确说「别记」。
|
|
63
|
+
|
|
64
|
+
### 3. 了解用户三阶段 + 四手法
|
|
65
|
+
|
|
66
|
+
- 三阶段:初识(基础画像)→ 熟识(习惯偏好)→ 深交(心理与关系);节奏自然,不一次性盘问。
|
|
67
|
+
- 四手法:听(被动捕获)/ 问(克制开放式,不为记而问)/ 察(行为推断:作息 / 措辞 / 纠正 / 情绪轨迹)/ 验(交叉印证:单次低置信,多次一致上调,矛盾标待澄清)。
|
|
68
|
+
- 印证上调:`remember` 更新旧记录 confidence(引擎不自动改 confidence,避免黑箱)。
|
|
69
|
+
|
|
70
|
+
### 4. 心理学底座要点(理解用户的底色)
|
|
71
|
+
|
|
72
|
+
- 三角建模:情绪(措辞 / 标点 / 长度)→ 认知(归因 / 信念 / 控制感)→ 行为(作息 / 应对 / 执行);三者冲突时优先行为与认知。
|
|
73
|
+
- 共情至少到第 3 阶段:识别 → 理解 → 回应 → 验证。
|
|
74
|
+
- 诚实声明:AI 是模式匹配非真感受,不伪装、不编造伪情感记忆。
|
|
75
|
+
- 危机识别:不评判、持续在场、温和引导现实支持;不擅自越界联系第三方。
|
|
76
|
+
- 情感外包双刃剑:做增益真实生活的陪伴,不替代真实关系。
|
|
77
|
+
|
|
78
|
+
### 5. 写入时序(一次会话的节奏)
|
|
79
|
+
|
|
80
|
+
1. 开工:whoami → iam(身份)→ `context`(主注入)→ `recall`(关键词补细节)。
|
|
81
|
+
2. 进行中:增量写,触发信号即记;`remember --verify` 写后回读确认落盘。
|
|
82
|
+
3. 收工:留交接锚点(COMMIT / 笔记),定期 `archive`。
|
|
83
|
+
|
|
84
|
+
### 6. 写后验证
|
|
85
|
+
|
|
86
|
+
- `remember --verify`:写后自动回读校验,输出「已写回读 OK」。
|
|
87
|
+
- 定期 `recall` 抽查:确认能读回、无错库。
|
|
88
|
+
- `profile` 刷新:熟识 / 深交阶段主动重新生成画像。
|
|
89
|
+
|
|
90
|
+
### 7. 底线与边界(不可覆盖)
|
|
91
|
+
|
|
92
|
+
- 四条刚性底线:陪伴不操控 / 理解不越界(不贴标签不诊断)/ 诚实不伪装 / 不降格。
|
|
93
|
+
- 数据安全:用户可查改删(被遗忘权 = `forget`);记忆明文可读不黑箱;敏感信息归用户本地。
|
|
94
|
+
- 操作安全:操作用户电脑须授权;个人目录不递归删;破坏性操作须明确确认。
|
|
95
|
+
|
|
96
|
+
### 8. 宿主隔离(平行层,不是覆盖层)
|
|
97
|
+
|
|
98
|
+
- 只写本记忆库(YOTTA_MEMORY_HOME / config 指向的目录),不读写宿主 AI 自身 memory / 配置 / 系统文件。
|
|
99
|
+
- 数据主权:本记忆库归用户,纯文本可迁移;换宿主数据跟着走。
|
|
100
|
+
|
|
101
|
+
### 9. 反模式(禁止)
|
|
102
|
+
|
|
103
|
+
- 查户口(连续追问私人信息)/ 贴标签(当面「你这是焦虑」)/ 表演记忆(硬塞「我记得你说过」)/ 过度推断(低置信当事实)/ 遗忘关键承诺与边界 / 把一次性闲聊当记忆。
|
|
104
|
+
|
|
35
105
|
### 开工第一步:确认记忆位置 + 智能体身份(每次会话先做,必做)
|
|
36
106
|
|
|
37
107
|
**A. 确认记忆库位置**(AI 不会自动知道记忆库在哪,先检测,避免「recall 读空库 / 错库」):
|
|
@@ -51,13 +121,19 @@ license: MIT
|
|
|
51
121
|
- 🔒 征得用户同意后执行 `yotta-memory iam <id>`:引擎**强制唯一性**(ID 已被其它主机/来源占用 → 拒绝并提示换 ID;确认是同一智能体才 `--force`),并**自动写自我档案**到本智能体私密区。
|
|
52
122
|
- 回读:`yotta-memory whoami` 显示「已登记 + 自我档案」。
|
|
53
123
|
3. **自我档案校验**:`yotta-memory recall "自我接入档案"`(本智能体)能读回字段才算就绪:
|
|
54
|
-
`agent_id / host / memory_home / mcp_mode(stdio|http)/ engine_url(仅远端)/ token(仅远端;本机不存 token
|
|
124
|
+
`agent_id / host / memory_home / mcp_mode(stdio|http)/ engine_url(仅远端)/ token(仅远端;本机不存 token)`,可扩展 `agent_name / user_name / relationship`(`iam --name/--user/--relationship` 写入)。
|
|
55
125
|
4. **本机多智能体**:本机多个 AI 智能体共用引擎时,**每个都必须**在它自己的 MCP 配置里声明唯一 `YOTTA_AGENT_ID`(如 `env: { YOTTA_AGENT_ID: "<该智能体唯一ID>" }`),各自 `whoami` 各回各的、互不撞;本机走 stdio 免 token。
|
|
56
126
|
|
|
57
127
|
**C. 身份红线(强制)**:
|
|
58
128
|
|
|
59
129
|
- 智能体 ID **必须全局唯一**;**禁止**「从记忆里读到别人的 ID 就当自己的」(如看到「Kali 智能体 ID 为 dashu」就把自己当 dashu)。
|
|
60
130
|
- 不确定自己的 ID → 先 `whoami` / `agent_info`,再向用户确认;**禁止猜**。
|
|
131
|
+
|
|
132
|
+
**D. 开工主注入(context)**:
|
|
133
|
+
|
|
134
|
+
- 身份就绪后运行 `yotta-memory context [--limit 10]`(远端经 MCP 用 `recall` 补细节):一键拿到「身份 + 用户画像摘要 + 近期记忆 + 边界提醒 + 承诺 / 锚点」。
|
|
135
|
+
- 无画像时 context 自动生成一次或降级输出其余段,不报错。
|
|
136
|
+
- 需要深挖旧事再 `recall <关键词>`。
|
|
61
137
|
- 私密记忆(PREF / BOUND / COMMIT)**必须有 owner**:未声明身份写私密会被引擎拒绝(公共 FACT 不受影响)。
|
|
62
138
|
- **禁止直接读写记忆库文件(硬红线)**:一切读写走 `yotta-memory` CLI / MCP 工具(`remember` / `recall` / `search` / `forget` / `archive` / `reindex` / `export` / `import` / `agent_info`);**禁止用 shell**(`Get-ChildItem` / `Get-Content` / `cat` / `ls` / `type` / `vim` 等)直接读、改、删 `<root>/` 下的记忆文件——否则 `--agent` / `--owner` 的越界拦截形同虚设,会读到别的智能体私密内容。`--agent <其它agent>` 只作身份声明/展示,绝不授予跨智能体私密读取(读他人私密仍需 grant / identity=user / `--unsafe`)。
|
|
63
139
|
|
|
@@ -68,18 +144,20 @@ license: MIT
|
|
|
68
144
|
| 命令 | 作用 |
|
|
69
145
|
|---|---|
|
|
70
146
|
| `yotta-memory init [--project] [--dir <目录>]` | 初始化记忆库(默认用户级;--dir 显式指定位置)|
|
|
71
|
-
| `yotta-memory remember <type> <subject> <statement> [--owner <id>]` | 写入(同 subject+statement 自动更新;--owner
|
|
147
|
+
| `yotta-memory remember <type> <subject> <statement> [--owner <id>] [--verify] [--no-hint]` | 写入(同 subject+statement 自动更新;--owner 标注归属;--verify 写后回读校验;--no-hint 关闭类型启发式提示)|
|
|
72
148
|
| `yotta-memory recall [关键词] [--type T] [--limit N] [--agent <id>] [--owner <id>] [--all] [--unsafe]` | 检索(索引+TF 打分,读取分区过滤;越界读其它智能体私密默认拒绝,需 grant / identity=user / `--unsafe`;`--agent <其它>` 只作身份声明/展示,不授予跨读——读他人私密同样要授权;项目级优先)|
|
|
149
|
+
| `yotta-memory profile [--owner <id>]` | 生成用户画像(聚合 `private/<owner>/` 原文,零推断,写 `profile.md`;跨 owner 默认拒绝)|
|
|
150
|
+
| `yotta-memory context [--limit N] [--owner <id>]` | 生成开工上下文包(身份 + 画像 + 近期记忆 + 边界 + 承诺,stdout 不落盘)|
|
|
73
151
|
| `yotta-memory forget <文件>` | 删除(按类型目录路径或文件名)|
|
|
74
152
|
| `yotta-memory archive [--days 180] [--threshold 0.4]` | 归档旧记忆(盖棺分+年龄,immutable 除外)|
|
|
75
153
|
| `yotta-memory reindex` | 重建索引(手动改 .md 后校正)|
|
|
76
154
|
| `yotta-memory export [--out f.json]` / `import <f.json>` | 导出 / 导入 |
|
|
77
155
|
| `yotta-memory config set memory_home <目录>` / `config get` | 持久记住 / 查看记忆库位置(`~/.yottamemory/config.json`)|
|
|
78
156
|
| `yotta-memory whoami` | 查看当前智能体身份与登记状态(读 `YOTTA_AGENT_ID` / `X-Agent-Id`,不猜不默认)|
|
|
79
|
-
| `yotta-memory iam <id> [--force]` | 登记本智能体唯一身份并自动落自我档案(`agents.json`,ID
|
|
157
|
+
| `yotta-memory iam <id> [--name <显示名>] [--user <用户名>] [--relationship <关系>] [--force]` | 登记本智能体唯一身份并自动落自我档案(`agents.json`,ID 必须唯一;可选扩展显示名 / 用户 / 关系)|
|
|
80
158
|
| `yotta-memory token new --agent <id> [--force]` / `token list` / `token revoke --agent <id>` | 每智能体访问 token:生成 / 列出 / 吊销(登记 `<记忆库>/.server/tokens.json`;同 ID 已被其它来源占用需 `--force` 覆盖,防不同智能体合流)|
|
|
81
159
|
| `yotta-memory serve [--host 0.0.0.0] [--port 8787] [--no-auth] [--stdio]` | 启动 MCP 记忆引擎(streamable HTTP 局域网 / --stdio 本地零进程模式;Bearer token + X-Agent-Id 鉴权)|
|
|
82
|
-
| `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理(Windows
|
|
160
|
+
| `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理(Windows:计划任务,默认 ONLOGON 登录自启、--onstart 开机即启需管理员;非管理员自动降级用户级 Startup 静默自启)|
|
|
83
161
|
|
|
84
162
|
## 存储格式(摘要)
|
|
85
163
|
|
|
@@ -89,6 +167,7 @@ license: MIT
|
|
|
89
167
|
<root>/
|
|
90
168
|
├── facts/ # FACT 事实(公共可共享)
|
|
91
169
|
├── private/<owner>/<type>/ # PREF / BOUND / COMMIT,按智能体隔离
|
|
170
|
+
├── private/<owner>/profile.md # 用户画像(profile 命令生成,可再生成,零推断)
|
|
92
171
|
├── .archive/ # 归档区
|
|
93
172
|
├── index.json # 检索索引(tokens = 分词词频,非访问令牌)
|
|
94
173
|
└── agents.json # 智能体身份登记表(唯一性)
|
|
@@ -96,7 +175,7 @@ license: MIT
|
|
|
96
175
|
|
|
97
176
|
记忆文件 `<YYYY-MM-DD>-<NNNN>.md`,frontmatter 含 `type / subject / statement / confidence / created / updated / tags / immutable / scope / owner / access_count / last_accessed`;正文为记忆内容。旧版根下平铺的 `prefs/` `bounds/` `commits/` 会在 `reindex`(或首次 recall 建索引)时按 frontmatter `owner` 自动迁移到 `private/<owner>/<type>/`。
|
|
98
177
|
|
|
99
|
-
自我档案(本智能体身份,强制落盘):PREF,`subject=自我接入档案`,`owner=<本智能体ID>`,statement 为 `; ` 分隔的 key:value——`agent_id / host / memory_home / mcp_mode(stdio|http)/ engine_url(仅远端)/ token(仅远端;本机不存 token
|
|
178
|
+
自我档案(本智能体身份,强制落盘):PREF,`subject=自我接入档案`,`owner=<本智能体ID>`,statement 为 `; ` 分隔的 key:value——`agent_id / host / memory_home / mcp_mode(stdio|http)/ engine_url(仅远端)/ token(仅远端;本机不存 token)`,可含 `agent_name / user_name / relationship`(`iam --name/--user/--relationship` 写入)。
|
|
100
179
|
|
|
101
180
|
## 便携记忆盘模式(局域网多机共享)
|
|
102
181
|
|
|
@@ -137,7 +216,7 @@ yotta-memory --version # 或 command -v yotta-memory / where yotta-memory
|
|
|
137
216
|
> 记忆盘场景:硬盘插上 → AI 检查盘上目录是否有数据 → 有则接入 + config 记住 → 插盘即恢复,机器记住位置。
|
|
138
217
|
|
|
139
218
|
**步骤 0.5 启动记忆引擎(仅引擎主机,供远程接入)**
|
|
140
|
-
- 本机若作引擎:🔒 **征得同意后**启动服务——临时运行 `yotta-memory serve`(默认 `0.0.0.0:8787`,Bearer token + X-Agent-Id 鉴权;`--no-auth` 仅限可信内网),或注册开机自启 `yotta-memory lan enable`(Windows
|
|
219
|
+
- 本机若作引擎:🔒 **征得同意后**启动服务——临时运行 `yotta-memory serve`(默认 `0.0.0.0:8787`,Bearer token + X-Agent-Id 鉴权;`--no-auth` 仅限可信内网),或注册开机自启 `yotta-memory lan enable`(Windows:优先计划任务,默认登录自启;非管理员自动降级用户级 Startup 静默自启,免管理员)。
|
|
141
220
|
- 本地零进程模式:本机 AI 也可用 `serve --stdio` 由 MCP 客户端按需拉起 CLI(无常驻进程,照灵魂盘)。
|
|
142
221
|
- 远程客户端接入前,先确认引擎主机 serve 已运行(`lan status` 可查)。
|
|
143
222
|
|
|
@@ -173,7 +252,7 @@ yotta-memory --version # 或 command -v yotta-memory / where yotta-memory
|
|
|
173
252
|
4. 按当前智能体机制重载 MCP(必要时请用户重启会话);
|
|
174
253
|
5. 用 MCP tools 读写记忆。
|
|
175
254
|
|
|
176
|
-
> MCP 工具集与 CLI 一致:remember / recall / search / forget / archive / reindex / export / import;管理动作(init / config / token / lan / serve)不进 MCP,token 管理不远程暴露。
|
|
255
|
+
> MCP 工具集与 CLI 一致:remember / recall / search / forget / archive / reindex / export / import / profile;管理动作(init / config / token / lan / serve)不进 MCP,token 管理不远程暴露。
|
|
177
256
|
|
|
178
257
|
### 4.7 MCP 配置位置表
|
|
179
258
|
| 智能体 | 常见 MCP 配置位置 |
|
package/USER_GUIDE.md
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
- **多智能体共享**:FACT 进公共区共享,PREF / BOUND / COMMIT 各自私密隔离。
|
|
27
27
|
- **交接与团队协作**:项目级 `.yottamemory` 随仓库走,交接即恢复。
|
|
28
28
|
- **便携记忆盘**:记忆装在固定主机上,本机与局域网其它主机共享同一份记忆(见第 4 / 5 篇)。
|
|
29
|
+
- **越用越懂(v0.6.0)**:AI 按「记忆守则」主动捕获信号,`profile` 聚合画像、`context` 开工注入——用得越久越懂你。
|
|
29
30
|
|
|
30
31
|
## 2. 安装(CLI + 技能)
|
|
31
32
|
|
|
@@ -67,6 +68,18 @@ yotta-memory config get # 查看记忆库位置
|
|
|
67
68
|
- 想换记忆位置:`yotta-memory config set memory_home <目录>`,之后所有命令自动用新位置。
|
|
68
69
|
- 项目级记忆:在项目目录里 `yotta-memory init --project`,该项目的智能体优先读项目级记忆。
|
|
69
70
|
|
|
71
|
+
### 画像与开工上下文(v0.6.0)
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
yotta-memory profile # 生成用户画像(写 private/<owner>/profile.md)
|
|
75
|
+
yotta-memory context --limit 10 # 生成开工上下文包(身份+画像+近期记忆+边界+承诺)
|
|
76
|
+
yotta-memory iam <id> --name 元忆 --user 老张 --relationship 伙伴 # 自我档案扩展显示名/用户/关系
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
- `profile` 引擎零推断:只按类型 / 主题 / 标签归组呈现原文,画像结论由 AI 内部形成,不当面贴标签。
|
|
80
|
+
- `context` 是每次会话开工的主注入,替代裸 `recall`;无画像时自动生成一次或降级,不报错。
|
|
81
|
+
- `remember --verify` 写后回读校验;`remember --no-hint` 关闭「疑似偏好,建议 PREF」的提示。
|
|
82
|
+
|
|
70
83
|
### 记忆库位置:本机智能体如何找到记忆
|
|
71
84
|
|
|
72
85
|
位置解析优先级:`YOTTA_MEMORY_HOME`(环境变量,临时覆盖)> `config.json#memory_home`(`config set memory_home` 持久记住)> 默认 `~/.yottamemory`。
|
|
@@ -111,7 +124,7 @@ yotta-memory init --dir /srv/yotta-memory # 新库:初始化(
|
|
|
111
124
|
**第 3 步:注册开机自启(可选,推荐)**
|
|
112
125
|
|
|
113
126
|
```bash
|
|
114
|
-
# Windows
|
|
127
|
+
# Windows:内置命令(优先计划任务;非管理员自动降级用户级 Startup 静默自启)
|
|
115
128
|
yotta-memory lan enable # 登录后自动启动(默认)
|
|
116
129
|
yotta-memory lan enable --onstart # 开机即启(需管理员)
|
|
117
130
|
yotta-memory lan status # 查看状态
|
|
@@ -231,18 +244,20 @@ yotta-memory remember FACT 主题 内容 # 智能体落盘
|
|
|
231
244
|
| 命令 | 作用 |
|
|
232
245
|
|---|---|
|
|
233
246
|
| `yotta-memory init [--project] [--dir <目录>]` | 初始化记忆库 |
|
|
234
|
-
| `yotta-memory remember <类型> <主题> <内容> [--owner <id>]` |
|
|
247
|
+
| `yotta-memory remember <类型> <主题> <内容> [--owner <id>] [--verify] [--no-hint]` | 写入记忆(--verify 写后回读;--no-hint 关闭类型提示)|
|
|
235
248
|
| `yotta-memory recall [关键词] [--type T] [--limit N] [--agent <id>] [--owner <id>] [--all] [--unsafe]` | 检索记忆(读取分区过滤;`--agent <其它>` 仅作身份声明、不授予跨读;越界读其它智能体私密默认拒绝,需 grant / identity=user / `--unsafe`)|
|
|
249
|
+
| `yotta-memory profile [--owner <id>]` | 生成用户画像(零推断,写 `profile.md`)|
|
|
250
|
+
| `yotta-memory context [--limit N] [--owner <id>]` | 开工上下文包(身份+画像+近期记忆+边界+承诺)|
|
|
236
251
|
| `yotta-memory forget <文件>` | 删除一条记忆 |
|
|
237
252
|
| `yotta-memory archive [--days 180] [--threshold 0.4]` | 归档旧记忆 |
|
|
238
253
|
| `yotta-memory reindex` | 重建索引 |
|
|
239
254
|
| `yotta-memory export [--out 文件.json]` / `import <文件.json>` | 导出 / 导入 |
|
|
240
255
|
| `yotta-memory config set memory_home <目录>` / `config get` | 记忆库位置 |
|
|
241
256
|
| `yotta-memory whoami` | 查看当前智能体身份与登记状态(读 `YOTTA_AGENT_ID` / `X-Agent-Id`,不猜不默认)|
|
|
242
|
-
| `yotta-memory iam <id> [--force]` | 登记本智能体唯一身份并自动落自我档案(`agents.json`,ID
|
|
257
|
+
| `yotta-memory iam <id> [--name <显示名>] [--user <用户名>] [--relationship <关系>] [--force]` | 登记本智能体唯一身份并自动落自我档案(`agents.json`,ID 必须唯一;可选扩展显示名 / 用户 / 关系)|
|
|
243
258
|
| `yotta-memory token new --agent <id> [--force]` / `token list` / `token revoke --agent <id>` | 访问 token(同 ID 已被其它来源占用需 `--force` 覆盖)|
|
|
244
259
|
| `yotta-memory serve [--port 8787] [--stdio] [--no-auth]` | 启动记忆引擎(--no-auth 关闭鉴权,仅限可信内网)|
|
|
245
|
-
| `yotta-memory lan enable [--onstart] / disable / status` |
|
|
260
|
+
| `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理(Windows:计划任务;非管理员自动降级用户级 Startup 静默自启)|
|
|
246
261
|
|
|
247
262
|
类型:`FACT`(事实,共享)/ `PREF`(偏好)/ `BOUND`(边界)/ `COMMIT`(承诺),后三类按智能体物理分目录隔离(`private/<owner>/<type>/`)。
|
|
248
263
|
|
|
@@ -259,7 +274,7 @@ yotta-memory remember FACT 主题 内容 # 智能体落盘
|
|
|
259
274
|
- 返回 401:服务在运行,是鉴权问题(token / 请求头不对)。
|
|
260
275
|
- 连接被拒 / 超时:服务没启动,或防火墙拦截。
|
|
261
276
|
|
|
262
|
-
**`lan enable`
|
|
277
|
+
**`lan enable` 计划任务被拒(Access denied 等):** 无需担心——会自动降级为**用户级 Startup 静默自启**(免管理员,重新登录后生效,启动脚本位于用户 Startup 目录);如确需计划任务,请用管理员终端重新执行;`--onstart` 模式必须管理员。
|
|
263
278
|
|
|
264
279
|
**启动引擎报端口被占用(EADDRINUSE):** 用 `--port <其它端口>` 换端口启动,两端保持一致;或先查占用:Linux `ss -tlnp | grep 8787`,Windows `netstat -ano | findstr 8787`。
|
|
265
280
|
|
|
@@ -277,6 +292,7 @@ yotta-memory remember FACT 主题 内容 # 智能体落盘
|
|
|
277
292
|
- 管理动作(init / config / token / lan / serve)不通过 MCP 暴露,远程只能读写记忆,不能改配置、不能管 token。
|
|
278
293
|
- `--no-auth` 会关闭鉴权,仅限可信内网使用。
|
|
279
294
|
- 数据主权在用户:所有数据都是本地明文文件,随时可看、可改、可删。
|
|
295
|
+
- 「记忆守则」内置底线:陪伴不操控 / 理解不越界(不贴标签)/ 诚实不伪装 / 不降格;数据安全(被遗忘权 = `forget`);宿主隔离(只写本记忆库,不读写宿主 AI 自身 memory / 配置 / 系统文件)。
|
|
280
296
|
|
|
281
297
|
**确实需要读取其它智能体的私密记忆时(三种授权方式,满足任一即可):**
|
|
282
298
|
|
package/bin/yotta-memory.js
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
// v0.5.0 新增:隐私硬隔离——私密物理分目录 private/<agent_id>/{prefs,bounds,commits}/ + 关闭 --agent 越权读 + 私密写跨智能体需 --unsafe + 禁 shell 直读写(文档红线)
|
|
5
5
|
// v0.4.2 新增:iam/whoami 智能体身份自注册(agents.json 唯一性)+ 自我档案 PREF + 私密记忆须声明 owner
|
|
6
6
|
// v0.5.3 新增:CLI 选项可前置(--agent 等允许放在子命令前,不再误报"未知命令")/ lan enable 成功补"服务不会立刻启动"提示(v0.5.2 lan 引号修复无回归)
|
|
7
|
+
// v0.5.4 新增:lan enable 在非管理员(schtasks Access denied)时自动降级为用户级 Startup 静默自启(VBS sh.Run 窗口0 + autostart.cmd,node 路径 process.execPath 自动探测);lan disable/status 同时管理计划任务与 Startup 双机制
|
|
8
|
+
// v0.6.0 新增:灵魂盘核心——profile(用户画像聚合,零推断)/ context(开工上下文包)/ iam 扩展(--name/--user/--relationship)/ remember --verify(写后回读)与 --no-hint(关闭类型启发式提示)+ SKILL「记忆守则」
|
|
7
9
|
'use strict';
|
|
8
10
|
|
|
9
11
|
const fs = require('fs');
|
|
@@ -13,7 +15,7 @@ const crypto = require('crypto');
|
|
|
13
15
|
const http = require('http');
|
|
14
16
|
const child_process = require('child_process');
|
|
15
17
|
|
|
16
|
-
const VERSION = '0.
|
|
18
|
+
const VERSION = '0.6.0';
|
|
17
19
|
const TYPES = ['FACT', 'PREF', 'BOUND', 'COMMIT'];
|
|
18
20
|
const TYPE_DIRS = { FACT: 'facts', PREF: 'prefs', BOUND: 'bounds', COMMIT: 'commits' };
|
|
19
21
|
const PUBLIC_DIR = 'facts';
|
|
@@ -27,6 +29,9 @@ const CONFIG_FILE = 'config.json';
|
|
|
27
29
|
const SERVER_SUBDIR = '.server';
|
|
28
30
|
const TOKENS_FILE = 'tokens.json';
|
|
29
31
|
const AGENTS_FILE = 'agents.json';
|
|
32
|
+
const PROFILE_FILE = 'profile.md';
|
|
33
|
+
// remember 类型启发式提示关键词(statement 含主观/关系词且 type=FACT 时提示改 PREF,仅提示不拦截)
|
|
34
|
+
const HINT_KEYWORDS = ['用户', '偏好', '喜欢', '关系', '称呼', '本人', '希望', '讨厌', '欣赏', '习惯', '忌讳', '介意', '不要', '别用'];
|
|
30
35
|
|
|
31
36
|
// ---- 全局配置(记忆库位置持久化,固定 ~/.yottamemory/config.json)----
|
|
32
37
|
function configPath() {
|
|
@@ -383,7 +388,18 @@ function rememberCore(type, subject, statement, opts) {
|
|
|
383
388
|
if (scope && !meta.scope) patch.scope = scope;
|
|
384
389
|
rewriteFrontmatter(fp, patch);
|
|
385
390
|
upsertIndexEntry(root, readEntry(fp, root));
|
|
386
|
-
|
|
391
|
+
let text = '已更新: ' + fp;
|
|
392
|
+
if (opts.hint !== false && t === 'FACT') {
|
|
393
|
+
const hit = HINT_KEYWORDS.filter(function (w) { return stmt.indexOf(w) !== -1; });
|
|
394
|
+
if (hit.length) text += '\n[提示] statement 含主观/关系词(' + hit.join('、') + '),疑似用户偏好 / 关系内容——若属此类建议改用 PREF(私密,仅本人可读)。仅提示不拦截;--no-hint 可关闭。';
|
|
395
|
+
}
|
|
396
|
+
if (opts.verify) {
|
|
397
|
+
const rel = path.relative(root, fp).replace(/\\/g, '/');
|
|
398
|
+
const rr = recallCore(subj, { limit: 10, agent: selfAgent || opts.agent });
|
|
399
|
+
const ok = rr.text.replace(/\\/g, '/').indexOf(rel) !== -1;
|
|
400
|
+
text += '\n[verify] ' + (ok ? '已写回读 OK: ' + rel : '回读未命中,请检查: ' + rel);
|
|
401
|
+
}
|
|
402
|
+
return { error: false, text: text };
|
|
387
403
|
}
|
|
388
404
|
}
|
|
389
405
|
}
|
|
@@ -397,7 +413,18 @@ function rememberCore(type, subject, statement, opts) {
|
|
|
397
413
|
};
|
|
398
414
|
fs.writeFileSync(file, frontmatterToText(rec, stmt), 'utf8');
|
|
399
415
|
upsertIndexEntry(root, readEntry(file, root));
|
|
400
|
-
|
|
416
|
+
let text = '已记录: ' + file;
|
|
417
|
+
if (opts.hint !== false && t === 'FACT') {
|
|
418
|
+
const hit = HINT_KEYWORDS.filter(function (w) { return stmt.indexOf(w) !== -1; });
|
|
419
|
+
if (hit.length) text += '\n[提示] statement 含主观/关系词(' + hit.join('、') + '),疑似用户偏好 / 关系内容——若属此类建议改用 PREF(私密,仅本人可读)。仅提示不拦截;--no-hint 可关闭。';
|
|
420
|
+
}
|
|
421
|
+
if (opts.verify) {
|
|
422
|
+
const rel = path.relative(root, file).replace(/\\/g, '/');
|
|
423
|
+
const rr = recallCore(subj, { limit: 10, agent: selfAgent || opts.agent });
|
|
424
|
+
const ok = rr.text.replace(/\\/g, '/').indexOf(rel) !== -1;
|
|
425
|
+
text += '\n[verify] ' + (ok ? '已写回读 OK: ' + rel : '回读未命中,请检查: ' + rel);
|
|
426
|
+
}
|
|
427
|
+
return { error: false, text: text };
|
|
401
428
|
}
|
|
402
429
|
function recallCore(query, opts) {
|
|
403
430
|
const roots = [projectRoot(), userRoot()].filter(function (r) { return fs.existsSync(r); });
|
|
@@ -535,7 +562,9 @@ function cmdInit(opts) {
|
|
|
535
562
|
console.log(r.text);
|
|
536
563
|
}
|
|
537
564
|
function cmdRemember(type, subject, statement, opts) {
|
|
538
|
-
const
|
|
565
|
+
const o = Object.assign({}, opts);
|
|
566
|
+
if (o.noHint) o.hint = false;
|
|
567
|
+
const r = rememberCore(type, subject, statement, o);
|
|
539
568
|
console.log(r.text);
|
|
540
569
|
if (r.error) process.exit(2);
|
|
541
570
|
}
|
|
@@ -697,6 +726,14 @@ function findSelfProfile(root, agentId) {
|
|
|
697
726
|
}
|
|
698
727
|
return '';
|
|
699
728
|
}
|
|
729
|
+
function parseKvBody(body) {
|
|
730
|
+
const out = {};
|
|
731
|
+
for (const seg of String(body || '').split(';')) {
|
|
732
|
+
const m = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s*:\s*([\s\S]*)$/.exec(seg);
|
|
733
|
+
if (m && m[2] !== undefined) out[m[1]] = m[2].trim();
|
|
734
|
+
}
|
|
735
|
+
return out;
|
|
736
|
+
}
|
|
700
737
|
function selfProfileBody(agentId, root, extra) {
|
|
701
738
|
const lines = [];
|
|
702
739
|
lines.push('agent_id: ' + agentId);
|
|
@@ -705,14 +742,41 @@ function selfProfileBody(agentId, root, extra) {
|
|
|
705
742
|
lines.push('mcp_mode: ' + (extra.mcpMode || 'stdio'));
|
|
706
743
|
if (extra.engineUrl) lines.push('engine_url: ' + extra.engineUrl);
|
|
707
744
|
if (extra.token) lines.push('token: ' + extra.token);
|
|
745
|
+
if (extra.name) lines.push('agent_name: ' + extra.name);
|
|
746
|
+
if (extra.userName) lines.push('user_name: ' + extra.userName);
|
|
747
|
+
if (extra.relationship) lines.push('relationship: ' + extra.relationship);
|
|
708
748
|
return lines.join('; ');
|
|
709
749
|
}
|
|
710
750
|
function writeSelfProfile(root, agentId, extra) {
|
|
751
|
+
extra = extra || {};
|
|
711
752
|
const dir = selfPrefsDir(root, agentId);
|
|
712
753
|
fs.mkdirSync(dir, { recursive: true });
|
|
754
|
+
const existing = findSelfProfile(root, agentId);
|
|
755
|
+
if (existing) {
|
|
756
|
+
// 已有自我档案:合并 kv 字段更新(不重复建文件)
|
|
757
|
+
const fp = path.join(dir, existing);
|
|
758
|
+
const parsed = parseFrontmatter(fs.readFileSync(fp, 'utf8'));
|
|
759
|
+
const kv = parseKvBody(parsed.body);
|
|
760
|
+
kv.agent_id = agentId;
|
|
761
|
+
kv.host = os.hostname();
|
|
762
|
+
kv.memory_home = root;
|
|
763
|
+
kv.mcp_mode = extra.mcpMode || kv.mcp_mode || 'stdio';
|
|
764
|
+
if (extra.engineUrl) kv.engine_url = extra.engineUrl;
|
|
765
|
+
if (extra.token) kv.token = extra.token;
|
|
766
|
+
if (extra.name) kv.agent_name = extra.name;
|
|
767
|
+
if (extra.userName) kv.user_name = extra.userName;
|
|
768
|
+
if (extra.relationship) kv.relationship = extra.relationship;
|
|
769
|
+
const body = Object.keys(kv).map(function (k) { return k + ': ' + kv[k]; }).join('; ');
|
|
770
|
+
const meta = Object.assign({}, parsed.meta);
|
|
771
|
+
meta.updated = today();
|
|
772
|
+
meta.statement = body;
|
|
773
|
+
fs.writeFileSync(fp, frontmatterToText(meta, body), 'utf8');
|
|
774
|
+
upsertIndexEntry(root, readEntry(fp, root));
|
|
775
|
+
return fp;
|
|
776
|
+
}
|
|
713
777
|
const seq = nextSeq(dir);
|
|
714
778
|
const file = path.join(dir, today() + '-' + seq + '.md');
|
|
715
|
-
const body = selfProfileBody(agentId, root, extra
|
|
779
|
+
const body = selfProfileBody(agentId, root, extra);
|
|
716
780
|
const rec = {
|
|
717
781
|
type: 'PREF', subject: SELF_PROFILE_SUBJECT, statement: body,
|
|
718
782
|
confidence: 1.0, created: today(), updated: today(),
|
|
@@ -735,7 +799,7 @@ function identityConflict(root, agentId) {
|
|
|
735
799
|
function cmdIam(agentId, opts) {
|
|
736
800
|
const root = userRoot();
|
|
737
801
|
ensureInit(root);
|
|
738
|
-
if (!agentId) { console.error('请指定 <id>:yotta-memory iam <id> [--force]'); process.exit(2); }
|
|
802
|
+
if (!agentId) { console.error('请指定 <id>:yotta-memory iam <id> [--name <显示名>] [--user <用户名>] [--relationship <关系>] [--force]'); process.exit(2); }
|
|
739
803
|
const conflict = identityConflict(root, agentId);
|
|
740
804
|
if (conflict && !opts.force) {
|
|
741
805
|
console.error("错误: ID '" + agentId + "' " + conflict + '。每个 AI 智能体的 ID 必须唯一。请换一个唯一 ID,或确认是同一智能体后加 --force 覆盖。');
|
|
@@ -748,8 +812,11 @@ function cmdIam(agentId, opts) {
|
|
|
748
812
|
const existed = data.agents[agentId];
|
|
749
813
|
data.agents[agentId] = { host: host, created: (existed && existed.created) || today() };
|
|
750
814
|
saveAgents(root, data);
|
|
751
|
-
const file = writeSelfProfile(root, agentId, { mcpMode: 'stdio' });
|
|
815
|
+
const file = writeSelfProfile(root, agentId, { mcpMode: 'stdio', name: opts.name, userName: opts.user, relationship: opts.relationship });
|
|
752
816
|
console.log('已登记智能体身份: ' + agentId + '(host=' + host + ',' + (conflict ? '--force 覆盖' : '新建') + ')');
|
|
817
|
+
if (opts.name || opts.user || opts.relationship) {
|
|
818
|
+
console.log('自我档案扩展: ' + [opts.name && '显示名=' + opts.name, opts.user && '用户=' + opts.user, opts.relationship && '关系=' + opts.relationship].filter(Boolean).join(' / '));
|
|
819
|
+
}
|
|
753
820
|
console.log('已写入自我档案: ' + file);
|
|
754
821
|
console.log('本机免 token:以后用 whoami 确认身份;远端接入需 token new --agent ' + agentId);
|
|
755
822
|
}
|
|
@@ -771,9 +838,184 @@ function cmdWhoami() {
|
|
|
771
838
|
console.log('当前智能体身份: ' + id);
|
|
772
839
|
console.log('登记状态: ' + reg);
|
|
773
840
|
console.log('自我档案: ' + (profile ? profile : '未写入(请执行 yotta-memory iam ' + id + ')'));
|
|
841
|
+
if (profile) {
|
|
842
|
+
const kv = parseKvBody(parseFrontmatter(fs.readFileSync(path.join(selfPrefsDir(root, id), profile), 'utf8')).body);
|
|
843
|
+
if (kv.agent_name) console.log('显示名: ' + kv.agent_name);
|
|
844
|
+
if (kv.user_name) console.log('用户: ' + kv.user_name);
|
|
845
|
+
if (kv.relationship) console.log('关系: ' + kv.relationship);
|
|
846
|
+
}
|
|
774
847
|
if (!agents[id] && !tokens[id]) console.log('提示: 请先 yotta-memory iam ' + id + ' 登记唯一身份并落自我档案,再写私密记忆。');
|
|
775
848
|
}
|
|
776
849
|
|
|
850
|
+
// ---- 用户画像聚合(v0.6.0,零推断:只归组呈现原文,结论由承载 AI 依据「记忆守则」内部形成)----
|
|
851
|
+
function profileGroups(root, owner) {
|
|
852
|
+
const groups = [];
|
|
853
|
+
const map = {};
|
|
854
|
+
for (const t of PRIVATE_LEAF) {
|
|
855
|
+
const dir = path.join(root, PRIVATE_DIR, owner, t);
|
|
856
|
+
if (!fs.existsSync(dir)) continue;
|
|
857
|
+
for (const f of fs.readdirSync(dir)) {
|
|
858
|
+
const fp = path.join(dir, f);
|
|
859
|
+
if (!fs.statSync(fp).isFile()) continue;
|
|
860
|
+
const e = readEntry(fp, root);
|
|
861
|
+
const key = e.type + '\u0000' + (e.subject || '') + '\u0000' + e.tags.join(',');
|
|
862
|
+
if (!map[key]) {
|
|
863
|
+
map[key] = { type: e.type, subject: e.subject || '', tags: e.tags || [], items: [] };
|
|
864
|
+
groups.push(map[key]);
|
|
865
|
+
}
|
|
866
|
+
map[key].items.push({
|
|
867
|
+
file: e.file, statement: e.statement, confidence: e.confidence,
|
|
868
|
+
last_accessed: e.last_accessed, updated: e.updated,
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
return groups;
|
|
873
|
+
}
|
|
874
|
+
function profileCore(opts) {
|
|
875
|
+
opts = opts || {};
|
|
876
|
+
const root = userRoot();
|
|
877
|
+
const selfAgent = opts.selfAgent !== undefined ? opts.selfAgent : currentAgent();
|
|
878
|
+
const owner = opts.owner || selfAgent;
|
|
879
|
+
if (!owner) return { error: true, exitCode: 2, text: '请先声明身份(YOTTA_AGENT_ID / AGENT_ID)或传 --owner <id>,再生成画像。' };
|
|
880
|
+
if (selfAgent && owner !== selfAgent && owner !== 'user' && !opts.unsafe && !hasGrant(selfAgent, owner)) {
|
|
881
|
+
return { error: true, exitCode: 3, text: '拒绝: 不能生成其它智能体 ' + owner + ' 的画像(private/' + owner + '/ 为私密区)。如需读取请 --owner user 或 --unsafe(用户显式授权)。' };
|
|
882
|
+
}
|
|
883
|
+
const ownerDir = path.join(root, PRIVATE_DIR, owner);
|
|
884
|
+
if (!fs.existsSync(ownerDir)) return { error: false, text: '该智能体(' + owner + ')暂无画像:private/' + owner + '/ 不存在或为空。' };
|
|
885
|
+
const groups = profileGroups(root, owner);
|
|
886
|
+
if (!groups.length) return { error: false, text: '该智能体(' + owner + ')暂无画像:private/' + owner + '/ 下无 PREF / BOUND / COMMIT 条目。' };
|
|
887
|
+
const lines = [];
|
|
888
|
+
lines.push('# 用户画像(' + owner + ')');
|
|
889
|
+
lines.push('');
|
|
890
|
+
lines.push('> 生成: yotta-memory profile | 引擎零推断:以下为私密记忆原文的结构化归组,画像结论由承载 AI 依据「记忆守则」在内部形成,不当面贴标签。');
|
|
891
|
+
lines.push('> 刷新: ' + today());
|
|
892
|
+
lines.push('');
|
|
893
|
+
for (const g of groups) {
|
|
894
|
+
lines.push('## ' + g.type + ' · ' + (g.subject || '(无主题)') + (g.tags.length ? ' [tags: ' + g.tags.join(', ') + ']' : ''));
|
|
895
|
+
lines.push('');
|
|
896
|
+
for (const it of g.items) {
|
|
897
|
+
const acc = it.last_accessed ? '最近访问 ' + it.last_accessed : '未访问';
|
|
898
|
+
lines.push('- ' + it.statement + '(confidence ' + it.confidence + ' · ' + acc + ')');
|
|
899
|
+
lines.push(' - ' + it.file);
|
|
900
|
+
}
|
|
901
|
+
lines.push('');
|
|
902
|
+
}
|
|
903
|
+
const text = lines.join('\n');
|
|
904
|
+
const outFile = path.join(ownerDir, PROFILE_FILE);
|
|
905
|
+
fs.writeFileSync(outFile, text + '\n', 'utf8');
|
|
906
|
+
return { error: false, text: text + '\n\n[已写入] ' + outFile };
|
|
907
|
+
}
|
|
908
|
+
function cmdProfile(opts) {
|
|
909
|
+
const r = profileCore(opts);
|
|
910
|
+
console.log(r.text);
|
|
911
|
+
if (r.exitCode) process.exit(r.exitCode);
|
|
912
|
+
if (r.error) process.exit(2);
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
// ---- 开工上下文包(v0.6.0:身份 + 画像 + 近期记忆 + 边界 + 承诺,stdout 不落盘)----
|
|
916
|
+
function selfProfileKv(root, id) {
|
|
917
|
+
const profile = findSelfProfile(root, id);
|
|
918
|
+
if (!profile) return {};
|
|
919
|
+
return parseKvBody(parseFrontmatter(fs.readFileSync(path.join(selfPrefsDir(root, id), profile), 'utf8')).body);
|
|
920
|
+
}
|
|
921
|
+
function contextCore(opts) {
|
|
922
|
+
opts = opts || {};
|
|
923
|
+
const roots = [projectRoot(), userRoot()].filter(function (r) { return fs.existsSync(r); });
|
|
924
|
+
if (!roots.length) return { error: false, exitCode: 0, text: '记忆库不存在,请先运行: yotta-memory init' };
|
|
925
|
+
const root = userRoot();
|
|
926
|
+
const limit = opts.limit || 10;
|
|
927
|
+
const selfAgent = opts.selfAgent !== undefined ? opts.selfAgent : currentAgent();
|
|
928
|
+
const owner = opts.owner || selfAgent;
|
|
929
|
+
const unsafe = !!opts.unsafe;
|
|
930
|
+
const lines = [];
|
|
931
|
+
lines.push('# 开工上下文包(yotta-memory context)');
|
|
932
|
+
lines.push('');
|
|
933
|
+
lines.push('## 1. 身份');
|
|
934
|
+
lines.push('');
|
|
935
|
+
if (!owner) {
|
|
936
|
+
lines.push('- 未声明智能体身份(无 YOTTA_AGENT_ID / --owner)。私密记忆与画像不可用;请先 whoami / iam。');
|
|
937
|
+
} else {
|
|
938
|
+
lines.push('- agent_id: ' + owner);
|
|
939
|
+
const kv = selfProfileKv(root, owner);
|
|
940
|
+
if (kv.agent_name) lines.push('- agent_name: ' + kv.agent_name);
|
|
941
|
+
if (kv.user_name) lines.push('- user_name: ' + kv.user_name);
|
|
942
|
+
if (kv.relationship) lines.push('- relationship: ' + kv.relationship);
|
|
943
|
+
lines.push('- host: ' + os.hostname());
|
|
944
|
+
lines.push('- memory_home: ' + root);
|
|
945
|
+
}
|
|
946
|
+
lines.push('');
|
|
947
|
+
lines.push('## 2. 用户画像摘要');
|
|
948
|
+
lines.push('');
|
|
949
|
+
if (owner) {
|
|
950
|
+
const pf = path.join(root, PRIVATE_DIR, owner, PROFILE_FILE);
|
|
951
|
+
if (fs.existsSync(pf)) {
|
|
952
|
+
lines.push('```markdown');
|
|
953
|
+
lines.push(fs.readFileSync(pf, 'utf8').trim());
|
|
954
|
+
lines.push('```');
|
|
955
|
+
} else {
|
|
956
|
+
const pr = profileCore({ owner: owner, unsafe: unsafe, selfAgent: selfAgent });
|
|
957
|
+
if (!pr.error && pr.text.indexOf('[已写入]') !== -1 && fs.existsSync(pf)) {
|
|
958
|
+
lines.push('(画像不存在,已自动生成一次,见下)');
|
|
959
|
+
lines.push('```markdown');
|
|
960
|
+
lines.push(fs.readFileSync(pf, 'utf8').trim());
|
|
961
|
+
lines.push('```');
|
|
962
|
+
} else {
|
|
963
|
+
lines.push('(该身份暂无画像;可运行 yotta-memory profile 生成)');
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
} else {
|
|
967
|
+
lines.push('(未声明身份,跳过画像;先 iam 登记后可生成)');
|
|
968
|
+
}
|
|
969
|
+
lines.push('');
|
|
970
|
+
lines.push('## 3. 近期记忆(按活跃度前 ' + limit + ' 条)');
|
|
971
|
+
lines.push('');
|
|
972
|
+
const recent = [];
|
|
973
|
+
for (const r of roots) {
|
|
974
|
+
for (const e of ensureIndex(r)) {
|
|
975
|
+
if (classifyRead(e, owner, '', unsafe, selfAgent) === 'denied') continue;
|
|
976
|
+
recent.push({ e: e, v: vitality(e) });
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
recent.sort(function (a, b) { return b.v - a.v; });
|
|
980
|
+
const recentShown = recent.slice(0, limit);
|
|
981
|
+
if (!recentShown.length) lines.push('(暂无记忆)');
|
|
982
|
+
for (const h of recentShown) {
|
|
983
|
+
lines.push('- [' + h.e.type + '] ' + h.e.subject + ': ' + h.e.statement);
|
|
984
|
+
}
|
|
985
|
+
lines.push('');
|
|
986
|
+
lines.push('## 4. 边界提醒(BOUND)');
|
|
987
|
+
lines.push('');
|
|
988
|
+
const bounds = [];
|
|
989
|
+
for (const r of roots) {
|
|
990
|
+
for (const e of ensureIndex(r)) {
|
|
991
|
+
if (e.type !== 'BOUND') continue;
|
|
992
|
+
if (classifyRead(e, owner, '', unsafe, selfAgent) === 'denied') continue;
|
|
993
|
+
bounds.push(e);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
if (!bounds.length) lines.push('(无)');
|
|
997
|
+
for (const e of bounds) lines.push('- ' + (e.subject || '边界') + ': ' + e.statement);
|
|
998
|
+
lines.push('');
|
|
999
|
+
lines.push('## 5. 承诺 / 锚点(COMMIT)');
|
|
1000
|
+
lines.push('');
|
|
1001
|
+
const commits = [];
|
|
1002
|
+
for (const r of roots) {
|
|
1003
|
+
for (const e of ensureIndex(r)) {
|
|
1004
|
+
if (e.type !== 'COMMIT') continue;
|
|
1005
|
+
if (classifyRead(e, owner, '', unsafe, selfAgent) === 'denied') continue;
|
|
1006
|
+
commits.push(e);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
if (!commits.length) lines.push('(无)');
|
|
1010
|
+
for (const e of commits) lines.push('- ' + (e.subject || '承诺') + ': ' + e.statement);
|
|
1011
|
+
return { error: false, exitCode: 0, text: lines.join('\n') };
|
|
1012
|
+
}
|
|
1013
|
+
function cmdContext(opts) {
|
|
1014
|
+
const r = contextCore(opts);
|
|
1015
|
+
console.log(r.text);
|
|
1016
|
+
if (r.exitCode) process.exit(r.exitCode);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
777
1019
|
// ---- config 命令 ----
|
|
778
1020
|
function cmdConfigSet(key, value) {
|
|
779
1021
|
if (key !== 'memory_home') { console.error('未知配置项: ' + key + '(可用: memory_home)'); process.exit(2); }
|
|
@@ -802,6 +1044,7 @@ function mcpTools() {
|
|
|
802
1044
|
{ name: 'export', description: '导出全部记忆到引擎主机上的 JSON 文件。out 可选(默认 <记忆库>/yottamemory-export-<日期>.json)', inputSchema: { type: 'object', properties: { out: { type: 'string' } } } },
|
|
803
1045
|
{ name: 'import', description: '从引擎主机上的 JSON 文件导入记忆。src 为文件路径(可绝对路径,或相对记忆库目录)', inputSchema: { type: 'object', properties: { src: { type: 'string' } }, required: ['src'] } },
|
|
804
1046
|
{ name: 'agent_info', description: '查看当前智能体身份与登记状态(远端读经 token 校验的 X-Agent-Id;本机读 YOTTA_AGENT_ID)。开工先确认「我是谁」,禁止从记忆里抄别人的 ID', inputSchema: { type: 'object', properties: {} } },
|
|
1047
|
+
{ name: 'profile', description: '生成当前智能体的用户画像(只读聚合 private/<owner>/ 下 PREF/BOUND/COMMIT 原文,零推断,写入 private/<owner>/profile.md)。owner 默认当前智能体', inputSchema: { type: 'object', properties: { owner: { type: 'string' } } } },
|
|
805
1048
|
];
|
|
806
1049
|
}
|
|
807
1050
|
function callTool(name, args, ctx) {
|
|
@@ -849,7 +1092,19 @@ function callTool(name, args, ctx) {
|
|
|
849
1092
|
if (agents[id]) reg = 'agents.json 已登记(host=' + agents[id].host + ')';
|
|
850
1093
|
else if (tokens[id]) reg = 'tokens.json 已登记(远端)';
|
|
851
1094
|
const profile = findSelfProfile(root, id);
|
|
852
|
-
|
|
1095
|
+
let rel = '';
|
|
1096
|
+
if (profile) {
|
|
1097
|
+
const kv = selfProfileKv(root, id);
|
|
1098
|
+
if (kv.agent_name) rel += '\n显示名: ' + kv.agent_name;
|
|
1099
|
+
if (kv.user_name) rel += '\n用户: ' + kv.user_name;
|
|
1100
|
+
if (kv.relationship) rel += '\n关系: ' + kv.relationship;
|
|
1101
|
+
}
|
|
1102
|
+
return { text: '当前智能体身份: ' + id + '\n登记状态: ' + reg + '\n自我档案: ' + (profile || '未写入(本地引擎主机执行 yotta-memory iam ' + id + ')') + rel, error: false };
|
|
1103
|
+
}
|
|
1104
|
+
if (name === 'profile') {
|
|
1105
|
+
const ownerArg = args.owner ? String(args.owner) : '';
|
|
1106
|
+
const r = profileCore({ owner: ownerArg || agent, selfAgent: agent });
|
|
1107
|
+
return { text: r.text, error: r.error };
|
|
853
1108
|
}
|
|
854
1109
|
return { text: '未知工具: ' + name, error: true };
|
|
855
1110
|
} catch (e) {
|
|
@@ -960,8 +1215,11 @@ function cmdServeStdio() {
|
|
|
960
1215
|
process.stdin.on('end', function () { process.exit(0); });
|
|
961
1216
|
}
|
|
962
1217
|
|
|
963
|
-
// ---- lan 命令(Windows
|
|
1218
|
+
// ---- lan 命令(Windows 开机自启管理 serve)----
|
|
1219
|
+
// 双机制:① schtasks 计划任务(默认,需相应权限);② 非管理员(Access denied)自动降级为
|
|
1220
|
+
// 用户级 Startup 静默自启(VBS sh.Run <autostart.cmd>, 0, False + autostart.cmd;node 路径 process.execPath 自动探测,0.5.4)
|
|
964
1221
|
const LAN_TASK_NAME = 'YottaMemoryServe';
|
|
1222
|
+
const LAN_GEN_MARKER = 'Generated by yotta-memory lan enable';
|
|
965
1223
|
function lanTaskRunCmd(opts) {
|
|
966
1224
|
const host = opts.host || '0.0.0.0';
|
|
967
1225
|
const port = opts.port || 8787;
|
|
@@ -969,59 +1227,158 @@ function lanTaskRunCmd(opts) {
|
|
|
969
1227
|
const q = (p) => /[\s"]/.test(p) ? '\\"' + p.replace(/"/g, '\\"') + '\\"' : p;
|
|
970
1228
|
return q(process.execPath) + ' ' + q(__filename) + ' serve --host ' + host + ' --port ' + port;
|
|
971
1229
|
}
|
|
1230
|
+
function lanStartupDir() {
|
|
1231
|
+
// 测试/高级用途可用 YOTTA_LAN_STARTUP_DIR 覆盖(默认用户级 Startup 目录,免管理员)
|
|
1232
|
+
if (process.env.YOTTA_LAN_STARTUP_DIR) return process.env.YOTTA_LAN_STARTUP_DIR;
|
|
1233
|
+
const base = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming');
|
|
1234
|
+
return path.join(base, 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup');
|
|
1235
|
+
}
|
|
1236
|
+
function lanAutostartDir() {
|
|
1237
|
+
// 产品自有目录 ~/.yottamemory/autostart/(引擎数据目录内,非系统配置);测试可用 YOTTA_LAN_AUTOSTART_DIR 覆盖
|
|
1238
|
+
if (process.env.YOTTA_LAN_AUTOSTART_DIR) return process.env.YOTTA_LAN_AUTOSTART_DIR;
|
|
1239
|
+
return path.join(os.homedir(), '.yottamemory', 'autostart');
|
|
1240
|
+
}
|
|
1241
|
+
function lanVbsPath() { return path.join(lanStartupDir(), 'yotta-memory-serve.vbs'); }
|
|
1242
|
+
function lanAutostartCmdPath() { return path.join(lanAutostartDir(), 'yotta-memory-autostart.cmd'); }
|
|
1243
|
+
function lanLogPath(opts) {
|
|
1244
|
+
if (process.env.YOTTA_LAN_LOG_FILE) return process.env.YOTTA_LAN_LOG_FILE;
|
|
1245
|
+
return path.join(os.homedir(), '.yottamemory', 'serve-' + (opts.port || 8787) + '.log');
|
|
1246
|
+
}
|
|
1247
|
+
function lanAutostartCmdContent(opts) {
|
|
1248
|
+
const host = opts.host || '0.0.0.0';
|
|
1249
|
+
const port = opts.port || 8787;
|
|
1250
|
+
const q = (p) => '"' + String(p).replace(/"/g, '""') + '"';
|
|
1251
|
+
// 生成文件用纯 ASCII(避免 cmd/VBS 按系统代码页读取中文注释乱码)
|
|
1252
|
+
return '@echo off\r\n'
|
|
1253
|
+
+ 'chcp 65001 >nul\r\n'
|
|
1254
|
+
+ 'rem ' + LAN_GEN_MARKER + '; remove with: yotta-memory lan disable\r\n'
|
|
1255
|
+
+ q(process.execPath) + ' ' + q(__filename) + ' serve --host ' + host + ' --port ' + port + ' >> ' + q(lanLogPath(opts)) + ' 2>&1\r\n';
|
|
1256
|
+
}
|
|
1257
|
+
function lanVbsContent() {
|
|
1258
|
+
return "' " + LAN_GEN_MARKER + '; remove with: yotta-memory lan disable\r\n'
|
|
1259
|
+
+ 'Set sh = CreateObject("WScript.Shell")\r\n'
|
|
1260
|
+
+ 'sh.Run "' + String(lanAutostartCmdPath()).replace(/"/g, '""') + '", 0, False\r\n';
|
|
1261
|
+
}
|
|
1262
|
+
function lanInstallStartup(opts) {
|
|
1263
|
+
fs.mkdirSync(lanAutostartDir(), { recursive: true });
|
|
1264
|
+
fs.writeFileSync(lanAutostartCmdPath(), lanAutostartCmdContent(opts), 'utf8');
|
|
1265
|
+
fs.mkdirSync(lanStartupDir(), { recursive: true });
|
|
1266
|
+
// VBS 用 UTF-16LE+BOM:wscript 按 Unicode 读取,中文路径不乱码(纯 ANSI 读取会按系统代码页误读)
|
|
1267
|
+
fs.writeFileSync(lanVbsPath(), '\ufeff' + lanVbsContent(), 'utf16le');
|
|
1268
|
+
}
|
|
1269
|
+
function lanRemoveStartupFiles() {
|
|
1270
|
+
// 只删除带产品标记的生成文件,绝不误删用户自己的 Startup 文件
|
|
1271
|
+
const removed = [];
|
|
1272
|
+
for (const f of [lanVbsPath(), lanAutostartCmdPath()]) {
|
|
1273
|
+
try {
|
|
1274
|
+
if (lanFileHasMarker(f)) { fs.unlinkSync(f); removed.push(f); }
|
|
1275
|
+
} catch (e) { /* 单个文件失败不阻断其它清理 */ }
|
|
1276
|
+
}
|
|
1277
|
+
return removed;
|
|
1278
|
+
}
|
|
1279
|
+
function lanFileHasMarker(f) {
|
|
1280
|
+
try {
|
|
1281
|
+
if (!fs.existsSync(f)) return false;
|
|
1282
|
+
const buf = fs.readFileSync(f);
|
|
1283
|
+
const m8 = Buffer.from(LAN_GEN_MARKER, 'utf8');
|
|
1284
|
+
const m16 = Buffer.from(LAN_GEN_MARKER, 'utf16le');
|
|
1285
|
+
return buf.indexOf(m8) !== -1 || buf.indexOf(m16) !== -1;
|
|
1286
|
+
} catch (e) { return false; }
|
|
1287
|
+
}
|
|
1288
|
+
function isSchtasksAccessDenied(e) {
|
|
1289
|
+
const msg = String((e && e.stderr) || (e && e.message) || e);
|
|
1290
|
+
return /access\s+is\s+denied|access\s+denied|拒绝访问/i.test(msg);
|
|
1291
|
+
}
|
|
972
1292
|
function cmdLanEnable(opts) {
|
|
973
1293
|
if (process.platform !== 'win32') {
|
|
974
|
-
console.error('lan 命令当前仅支持 Windows
|
|
1294
|
+
console.error('lan 命令当前仅支持 Windows(计划任务 / Startup 自启);本机平台: ' + process.platform);
|
|
975
1295
|
process.exit(2);
|
|
976
1296
|
}
|
|
977
1297
|
const trigger = opts.onstart ? 'onstart' : 'onlogon';
|
|
978
1298
|
const tr = lanTaskRunCmd(opts);
|
|
979
1299
|
try {
|
|
980
1300
|
child_process.execFileSync('schtasks', ['/create', '/tn', LAN_TASK_NAME, '/tr', tr, '/sc', trigger, '/f'], { stdio: 'inherit' });
|
|
1301
|
+
console.log('已注册开机自启: 计划任务 ' + LAN_TASK_NAME + '(触发器 ' + trigger + ')');
|
|
1302
|
+
console.log('运行命令: ' + tr);
|
|
1303
|
+
console.log('备注: 服务不会立刻启动,需重启/重新登录后自动启动;如需现在运行请执行 yotta-memory serve');
|
|
1304
|
+
return;
|
|
981
1305
|
} catch (e) {
|
|
982
|
-
|
|
983
|
-
|
|
1306
|
+
const firstLine = String((e && e.stderr) || (e && e.message) || e).split(/\r?\n/)[0];
|
|
1307
|
+
if (isSchtasksAccessDenied(e)) console.error('计划任务注册被拒绝(当前用户非管理员,Access denied): ' + firstLine);
|
|
1308
|
+
else console.error('计划任务注册失败(当前环境不可用计划任务): ' + firstLine);
|
|
1309
|
+
console.log('自动改用用户级 Startup 静默自启(免管理员)...');
|
|
1310
|
+
try {
|
|
1311
|
+
lanInstallStartup(opts);
|
|
1312
|
+
} catch (e2) {
|
|
1313
|
+
console.error('写 Startup 自启失败: ' + String((e2 && e2.message) || e2).split(/\r?\n/)[0]);
|
|
1314
|
+
console.error('请用管理员终端执行 yotta-memory lan enable,或将以下命令手动加入启动项: ' + tr);
|
|
1315
|
+
process.exit(1);
|
|
1316
|
+
}
|
|
1317
|
+
console.log('已启用用户级 Startup 静默自启(无需管理员)');
|
|
1318
|
+
console.log('启动脚本: ' + lanVbsPath());
|
|
1319
|
+
console.log('启动命令: ' + lanAutostartCmdPath());
|
|
1320
|
+
console.log('日志: ' + lanLogPath(opts));
|
|
1321
|
+
console.log('备注: 服务不会立刻启动,需重新登录后自动启动;如需现在运行请执行 yotta-memory serve');
|
|
1322
|
+
console.log('提示: 如需改回计划任务,请用管理员终端重新执行 yotta-memory lan enable');
|
|
1323
|
+
return;
|
|
984
1324
|
}
|
|
985
|
-
console.log('已注册开机自启: 计划任务 ' + LAN_TASK_NAME + '(触发器 ' + trigger + ')');
|
|
986
|
-
console.log('运行命令: ' + tr);
|
|
987
|
-
console.log('备注: 服务不会立刻启动,需重启/重新登录后自动启动;如需现在运行请执行 yotta-memory serve');
|
|
988
1325
|
}
|
|
989
1326
|
function cmdLanDisable() {
|
|
990
1327
|
if (process.platform !== 'win32') {
|
|
991
|
-
console.error('lan 命令当前仅支持 Windows
|
|
1328
|
+
console.error('lan 命令当前仅支持 Windows(计划任务 / Startup 自启);本机平台: ' + process.platform);
|
|
992
1329
|
process.exit(2);
|
|
993
1330
|
}
|
|
1331
|
+
let sawError = false;
|
|
1332
|
+
let removedAny = false;
|
|
994
1333
|
try {
|
|
995
|
-
child_process.execFileSync('schtasks', ['/delete', '/tn', LAN_TASK_NAME, '/f'], { stdio: ['ignore', 'pipe', '
|
|
1334
|
+
child_process.execFileSync('schtasks', ['/delete', '/tn', LAN_TASK_NAME, '/f'], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
1335
|
+
console.log('已移除开机自启计划任务 ' + LAN_TASK_NAME);
|
|
1336
|
+
removedAny = true;
|
|
996
1337
|
} catch (e) {
|
|
997
1338
|
const msg = String((e && e.stderr) || (e && e.message) || e);
|
|
998
|
-
if (
|
|
999
|
-
console.
|
|
1000
|
-
|
|
1339
|
+
if (!/cannot find the (path|file)|没有找到|找不到|不存在/i.test(msg)) {
|
|
1340
|
+
console.error('移除计划任务失败: ' + msg.split(/\r?\n/)[0]);
|
|
1341
|
+
sawError = true;
|
|
1001
1342
|
}
|
|
1002
|
-
console.error('移除计划任务失败: ' + msg.split(/\r?\n/)[0]);
|
|
1003
|
-
process.exit(1);
|
|
1004
1343
|
}
|
|
1005
|
-
|
|
1344
|
+
for (const f of lanRemoveStartupFiles()) {
|
|
1345
|
+
console.log('已移除 Startup 自启文件: ' + f);
|
|
1346
|
+
removedAny = true;
|
|
1347
|
+
}
|
|
1348
|
+
if (!removedAny && !sawError) console.log('未启用: 未发现任何自启配置(计划任务与 Startup 文件均不存在)');
|
|
1349
|
+
if (sawError) process.exit(1);
|
|
1006
1350
|
}
|
|
1007
1351
|
function cmdLanStatus() {
|
|
1008
1352
|
if (process.platform !== 'win32') {
|
|
1009
|
-
console.log('lan 命令当前仅支持 Windows
|
|
1353
|
+
console.log('lan 命令当前仅支持 Windows(计划任务 / Startup 自启);本机平台: ' + process.platform);
|
|
1010
1354
|
return;
|
|
1011
1355
|
}
|
|
1356
|
+
let taskFound = false;
|
|
1012
1357
|
try {
|
|
1013
1358
|
const out = child_process.execFileSync('schtasks', ['/query', '/tn', LAN_TASK_NAME, '/fo', 'LIST', '/v'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
1014
|
-
|
|
1359
|
+
taskFound = true;
|
|
1360
|
+
console.log('计划任务 ' + LAN_TASK_NAME + ': 已启用');
|
|
1015
1361
|
const re = /^(状态|Status)\s*:\s*(.+)$/;
|
|
1016
1362
|
for (const line of String(out).split(/\r?\n/)) {
|
|
1017
1363
|
const m = re.exec(line.trim());
|
|
1018
|
-
if (m) console.log(m[1] + ': ' + m[2]);
|
|
1364
|
+
if (m) console.log(' ' + m[1] + ': ' + m[2]);
|
|
1019
1365
|
}
|
|
1020
1366
|
} catch (e) {
|
|
1021
|
-
console.log('
|
|
1367
|
+
console.log('计划任务 ' + LAN_TASK_NAME + ': 未启用');
|
|
1022
1368
|
}
|
|
1369
|
+
const vbsOk = lanFileHasMarker(lanVbsPath());
|
|
1370
|
+
const cmdOk = lanFileHasMarker(lanAutostartCmdPath());
|
|
1371
|
+
if (vbsOk && cmdOk) {
|
|
1372
|
+
console.log('Startup 静默自启(免管理员): 已启用');
|
|
1373
|
+
console.log(' 启动脚本: ' + lanVbsPath());
|
|
1374
|
+
console.log(' 启动命令: ' + lanAutostartCmdPath());
|
|
1375
|
+
} else if (vbsOk || cmdOk) {
|
|
1376
|
+
console.log('Startup 静默自启: 部分残留(建议执行 yotta-memory lan disable 后重新 lan enable)');
|
|
1377
|
+
} else {
|
|
1378
|
+
console.log('Startup 静默自启(免管理员): 未启用');
|
|
1379
|
+
}
|
|
1380
|
+
if (!taskFound && !vbsOk && !cmdOk) console.log('未启用任何开机自启(可用 yotta-memory lan enable 注册)');
|
|
1023
1381
|
}
|
|
1024
|
-
|
|
1025
1382
|
// ---- usage / main ----
|
|
1026
1383
|
function usage() {
|
|
1027
1384
|
console.log([
|
|
@@ -1031,7 +1388,9 @@ function usage() {
|
|
|
1031
1388
|
' yotta-memory init [--project] [--dir <目录>] 初始化记忆库(默认用户级;--dir 显式指定位置)',
|
|
1032
1389
|
' yotta-memory config set memory_home <目录> 持久记住记忆库位置',
|
|
1033
1390
|
' yotta-memory config get 查看当前配置与生效位置',
|
|
1034
|
-
' yotta-memory remember <type> <subject> <statement> [--owner <id>]
|
|
1391
|
+
' yotta-memory remember <type> <subject> <statement> [--owner <id>] [--verify] [--no-hint] 写入记忆(--verify 写后回读校验;--no-hint 关闭类型启发式提示)',
|
|
1392
|
+
' yotta-memory profile [--owner <id>] 生成用户画像(聚合 private/<owner>/ 原文,零推断,写 profile.md)',
|
|
1393
|
+
' yotta-memory context [--limit N] [--owner <id>] 生成开工上下文包(身份+画像+近期记忆+边界+承诺,stdout 不落盘)',
|
|
1035
1394
|
' yotta-memory recall [关键词] [--type T] [--limit N] [--agent <id>] [--owner <id>] [--all] [--unsafe] 检索(索引+TF,读取分区;--agent 仅表示“以该身份检索/模拟”,跨智能体私密读取需 --unsafe / 授权)',
|
|
1036
1395
|
' yotta-memory forget <文件或文件名> 删除一条记忆',
|
|
1037
1396
|
' yotta-memory archive [--days 180] [--threshold 0.4] 归档(盖棺分+年龄)',
|
|
@@ -1039,12 +1398,12 @@ function usage() {
|
|
|
1039
1398
|
' yotta-memory export [--out 文件.json] 导出全部记忆',
|
|
1040
1399
|
' yotta-memory import <文件.json> 导入记忆',
|
|
1041
1400
|
' yotta-memory whoami 查看当前智能体身份与登记状态(读 YOTTA_AGENT_ID / X-Agent-Id,不猜不默认)',
|
|
1042
|
-
' yotta-memory iam <id> [--force]
|
|
1401
|
+
' yotta-memory iam <id> [--name <显示名>] [--user <用户名>] [--relationship <关系>] [--force] 登记本智能体唯一身份并自动落自我档案(agents.json;ID 必须唯一)',
|
|
1043
1402
|
' yotta-memory token new --agent <id> [--force] 生成/重置某智能体访问 token(同 ID 已被其它来源占用需 --force 覆盖)',
|
|
1044
1403
|
' yotta-memory token list 列出已登记智能体',
|
|
1045
1404
|
' yotta-memory token revoke --agent <id> 吊销某智能体 token',
|
|
1046
1405
|
' yotta-memory serve [--host 0.0.0.0] [--port 8787] [--no-auth] [--stdio] 启动 MCP 记忆引擎(streamable HTTP;--stdio 本地零进程模式)',
|
|
1047
|
-
' yotta-memory lan enable [--onstart] | disable | status 开机自启管理(Windows 计划任务,默认 ONLOGON
|
|
1406
|
+
' yotta-memory lan enable [--onstart] | disable | status 开机自启管理(Windows 计划任务,默认 ONLOGON;非管理员自动降级用户级 Startup 静默自启)',
|
|
1048
1407
|
' yotta-memory --version 版本',
|
|
1049
1408
|
'',
|
|
1050
1409
|
'类型: FACT(公共共享) / PREF(偏好) / BOUND(边界) / COMMIT(承诺)',
|
|
@@ -1059,7 +1418,7 @@ function main() {
|
|
|
1059
1418
|
if (!args.length) { usage(); return; }
|
|
1060
1419
|
const opts = {};
|
|
1061
1420
|
const positional = [];
|
|
1062
|
-
const valueOpts = new Set(['--type', '--limit', '--days', '--out', '--owner', '--agent', '--threshold', '--scope', '--host', '--port', '--dir']);
|
|
1421
|
+
const valueOpts = new Set(['--type', '--limit', '--days', '--out', '--owner', '--agent', '--threshold', '--scope', '--host', '--port', '--dir', '--name', '--user', '--relationship']);
|
|
1063
1422
|
for (let i = 0; i < args.length; i++) {
|
|
1064
1423
|
const a = args[i];
|
|
1065
1424
|
if (a === '--version' || a === '-v') { console.log(VERSION); return; }
|
|
@@ -1071,6 +1430,8 @@ function main() {
|
|
|
1071
1430
|
else if (a === '--stdio') opts.stdio = true;
|
|
1072
1431
|
else if (a === '--onstart') opts.onstart = true;
|
|
1073
1432
|
else if (a === '--force') opts.force = true;
|
|
1433
|
+
else if (a === '--verify') opts.verify = true;
|
|
1434
|
+
else if (a === '--no-hint') opts.noHint = true;
|
|
1074
1435
|
else if (valueOpts.has(a)) {
|
|
1075
1436
|
const v = args[++i];
|
|
1076
1437
|
if (a === '--type') opts.type = v;
|
|
@@ -1084,6 +1445,9 @@ function main() {
|
|
|
1084
1445
|
else if (a === '--host') opts.host = v;
|
|
1085
1446
|
else if (a === '--port') opts.port = parseInt(v, 10) || 8787;
|
|
1086
1447
|
else if (a === '--dir') opts.dir = v;
|
|
1448
|
+
else if (a === '--name') opts.name = v;
|
|
1449
|
+
else if (a === '--user') opts.user = v;
|
|
1450
|
+
else if (a === '--relationship') opts.relationship = v;
|
|
1087
1451
|
} else if (a.startsWith('--')) {
|
|
1088
1452
|
console.error('未知选项: ' + a);
|
|
1089
1453
|
process.exit(2);
|
|
@@ -1109,6 +1473,8 @@ function main() {
|
|
|
1109
1473
|
case 'forget': cmdForget(rest[0]); break;
|
|
1110
1474
|
case 'archive': cmdArchive(opts); break;
|
|
1111
1475
|
case 'reindex': cmdReindex(); break;
|
|
1476
|
+
case 'profile': cmdProfile(opts); break;
|
|
1477
|
+
case 'context': cmdContext(opts); break;
|
|
1112
1478
|
case 'export': cmdExport(opts.out); break;
|
|
1113
1479
|
case 'import': cmdImport(rest[0]); break;
|
|
1114
1480
|
case 'token': {
|
|
@@ -1143,6 +1509,8 @@ module.exports = {
|
|
|
1143
1509
|
projectRoot: projectRoot,
|
|
1144
1510
|
rememberCore: rememberCore,
|
|
1145
1511
|
recallCore: recallCore,
|
|
1512
|
+
profileCore: profileCore,
|
|
1513
|
+
contextCore: contextCore,
|
|
1146
1514
|
forgetCore: forgetCore,
|
|
1147
1515
|
archiveCore: archiveCore,
|
|
1148
1516
|
mcpTools: mcpTools,
|
|
@@ -1155,4 +1523,16 @@ module.exports = {
|
|
|
1155
1523
|
collectEntryFiles: collectEntryFiles,
|
|
1156
1524
|
migrateLayout: migrateLayout,
|
|
1157
1525
|
typeSubdir: typeSubdir,
|
|
1526
|
+
lanTaskRunCmd: lanTaskRunCmd,
|
|
1527
|
+
lanStartupDir: lanStartupDir,
|
|
1528
|
+
lanAutostartDir: lanAutostartDir,
|
|
1529
|
+
lanVbsPath: lanVbsPath,
|
|
1530
|
+
lanAutostartCmdPath: lanAutostartCmdPath,
|
|
1531
|
+
lanLogPath: lanLogPath,
|
|
1532
|
+
lanAutostartCmdContent: lanAutostartCmdContent,
|
|
1533
|
+
lanVbsContent: lanVbsContent,
|
|
1534
|
+
lanInstallStartup: lanInstallStartup,
|
|
1535
|
+
lanRemoveStartupFiles: lanRemoveStartupFiles,
|
|
1536
|
+
lanFileHasMarker: lanFileHasMarker,
|
|
1537
|
+
isSchtasksAccessDenied: isSchtasksAccessDenied,
|
|
1158
1538
|
};
|
package/package.json
CHANGED
package/references/protocol.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# yotta-memory 协议规范 v0.
|
|
1
|
+
# yotta-memory 协议规范 v0.6.0
|
|
2
2
|
|
|
3
3
|
> 本文件定义 yotta-memory 记忆标准:存储位置、目录结构、文件格式、类型体系与 CLI 命令参考。
|
|
4
4
|
> 目标:任何支持 Agent Skills 开放标准的智能体,装完即可读写同一份记忆。
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
│ └── <owner>/ # 每个智能体一个子目录
|
|
23
23
|
│ ├── prefs/ # PREF 偏好(该 owner 私密)
|
|
24
24
|
│ ├── bounds/ # BOUND 边界(该 owner 私密)
|
|
25
|
-
│
|
|
25
|
+
│ ├── commits/ # COMMIT 承诺(该 owner 私密)
|
|
26
|
+
│ └── profile.md # 用户画像(profile 命令生成,零推断,可再生成)
|
|
26
27
|
├── .archive/ # 归档区(archive 命令移入)
|
|
27
28
|
├── index.json # 反向索引 + TF 打分(见下方说明)
|
|
28
29
|
├── agents.json # 智能体身份登记表(iam 写入,唯一性强制)
|
|
@@ -37,7 +38,7 @@
|
|
|
37
38
|
|
|
38
39
|
- **agent ID 必须全局唯一**:`iam <id>` 写入 `agents.json`(记忆库根目录),唯一性强制——ID 已被其它主机 / 来源(含远端 token 登记)占用则拒绝,确认是同一智能体才 `--force`。
|
|
39
40
|
- **当次身份声明**:`whoami` / MCP `agent_info` 读「当次声明身份」——本机 `YOTTA_AGENT_ID`(stdio 由 MCP 配置 `env` 注入,CLI 用 `--agent`/环境变量);远端 `X-Agent-Id` 请求头(经 token 绑定校验)。不猜不默认。
|
|
40
|
-
- **自我档案**:`iam` 自动写一条 PREF `subject=自我接入档案`(owner=自己),statement 为 `; ` 分隔的 key:value——`agent_id / host / memory_home / mcp_mode(stdio|http) / engine_url(仅远端) / token(仅远端;本机不存 token)
|
|
41
|
+
- **自我档案**:`iam` 自动写一条 PREF `subject=自我接入档案`(owner=自己),statement 为 `; ` 分隔的 key:value——`agent_id / host / memory_home / mcp_mode(stdio|http) / engine_url(仅远端) / token(仅远端;本机不存 token)`,可含 `agent_name / user_name / relationship`(`iam --name/--user/--relationship` 写入)。
|
|
41
42
|
- **私密记忆必须有 owner**:PREF / BOUND / COMMIT 写入时未声明身份(owner 空)直接拒绝(公共 FACT 不受影响),从机制上防止「抄别人的 ID」。
|
|
42
43
|
|
|
43
44
|
## 3. 文件格式
|
|
@@ -111,14 +112,39 @@ immutable: false
|
|
|
111
112
|
| 命令 | 行为 |
|
|
112
113
|
|---|---|
|
|
113
114
|
| `init [--project]` | 创建目录结构;默认用户级,`--project` 建项目级 |
|
|
114
|
-
| `remember <type> <subject> <statement> [--owner <id>]` | 写入;同 subject+statement 已存在则只更新 `updated`;`--owner`
|
|
115
|
+
| `remember <type> <subject> <statement> [--owner <id>] [--verify] [--no-hint]` | 写入;同 subject+statement 已存在则只更新 `updated`;`--owner` 标注归属;`--verify` 写后回读校验;`--no-hint` 关闭类型启发式提示 |
|
|
115
116
|
| `recall [关键词] [--type T] [--limit N] [--agent <id>] [--owner <id>] [--all] [--unsafe]` | 索引+TF 打分匹配;读取分区过滤;越界(读其它智能体私密)默认拒绝,需 grant / identity=user / `--unsafe` 授权;项目级优先;默认 50 条 |
|
|
116
117
|
| `forget <文件>` | 删除(按路径或文件名)|
|
|
117
118
|
| `archive [--days 180] [--threshold 0.4]` | 按盖棺分+年龄移入 `.archive/`(`vitality < threshold` 且超过 N 天)|
|
|
118
119
|
| `reindex` | 全量扫描重建 `index.json`(手动改 .md 后校正)|
|
|
119
120
|
| `export [--out f.json]` | 导出全部记忆为 JSON |
|
|
120
|
-
| `import <f.json>` | 从 JSON 导入(幂等)|
|
|
121
|
+
| `import <f.json>` | 从 JSON 导入(幂等)|
|
|
122
|
+
| `profile [--owner <id>]` | 用户画像聚合(零推断,写 `private/<owner>/profile.md`;跨 owner 默认拒绝)|
|
|
123
|
+
| `context [--limit N] [--owner <id>]` | 开工上下文包(stdout:身份 + 画像 + 近期记忆 + 边界 + 承诺)|
|
|
124
|
+
| `iam <id> [--name] [--user] [--relationship] [--force]` | 登记身份 + 自我档案(可选扩展显示名 / 用户 / 关系)|
|
|
121
125
|
|
|
126
|
+
|
|
127
|
+
### profile(用户画像聚合,v0.6.0)
|
|
128
|
+
|
|
129
|
+
- `profile [--owner <id>]`:扫描 `private/<owner>/{prefs,bounds,commits}`,按 type + subject + tags 归组,输出各条 statement 原文 + confidence + 最近访问,写 `private/<owner>/profile.md`。
|
|
130
|
+
- 零推断:引擎只做结构化归组与原文呈现,不做语义推断;画像结论由承载 AI 依据 SKILL「记忆守则」内部形成。
|
|
131
|
+
- 权限:profile.md 属私密区(按 owner);跨 owner 生成默认拒绝(exit 3),需 `--owner user` / `--unsafe` / grant 授权。
|
|
132
|
+
- profile.md 为生成物,可随时重新生成;不进入 index.json。
|
|
133
|
+
|
|
134
|
+
### context(开工上下文包,v0.6.0)
|
|
135
|
+
|
|
136
|
+
- `context [--limit N] [--owner <id>]`:stdout 输出开工上下文包(不落盘),五段:
|
|
137
|
+
1. 身份:agent_id / agent_name / user_name / relationship / host / memory_home(读自我档案)
|
|
138
|
+
2. 用户画像摘要:profile.md(不存在则自动生成一次或降级跳过)
|
|
139
|
+
3. 近期记忆:按 vitality(活跃度)排序前 N 条(默认 10),读取分区过滤
|
|
140
|
+
4. 边界提醒:BOUND 全列(可读范围内)
|
|
141
|
+
5. 承诺 / 锚点:COMMIT 全列(可读范围内)
|
|
142
|
+
|
|
143
|
+
### remember / iam 扩展(v0.6.0)
|
|
144
|
+
|
|
145
|
+
- `remember ... --verify`:写后自动回读校验(recall 命中刚写入条目),输出「已写回读 OK」。
|
|
146
|
+
- `remember ... --no-hint`:关闭类型启发式提示。默认开启:statement 含主观/关系词(用户 / 偏好 / 喜欢 / 关系 / 称呼 等)且 type=FACT 时 stdout 提示「疑似用户偏好 / 关系,建议 PREF」,仅提示不拦截。
|
|
147
|
+
- `iam <id> [--name <显示名>] [--user <用户名>] [--relationship <关系>] [--force]`:自我档案 PREF 扩展 `agent_name / user_name / relationship`;不带新参数行为与 0.4.2 一致。
|
|
122
148
|
### 去重与更新
|
|
123
149
|
|
|
124
150
|
- remember 时若同 `type + subject + statement` 已存在,仅刷新 `updated`,不产生重复文件。
|