@yottameta/yotta-memory 0.5.4 → 0.6.1

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 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,6 +40,7 @@
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
46
  | **局域网共享与自启** | 每智能体独立 token(Bearer + X-Agent-Id)鉴权、可吊销;`lan enable` 注册开机自启(Windows:优先计划任务;非管理员自动降级用户级 Startup 静默自启);MCP 工具集与 CLI 一致(8 个工具),管理动作不远程暴露 |
@@ -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**:一键生成开工上下文包——多智能体接入铁律 + 身份 + 用户画像摘要 + 近期记忆(按 importance 排序)+ 边界提醒 + 承诺 / 锚点;支持 `--budget` 字符预算(token 恒定,不随记忆膨胀)。
93
+ - **记忆守则**:SKILL.md 内置规则层(类型红线 / 主动捕获触发信号 / 了解用户三阶段四手法 / 心理学底座与对齐 / 底线与边界 / 宿主隔离 / 反模式),让 AI「越用越懂」有章法。
94
+
86
95
  ### 检索:索引 + 中文分词打分
87
96
 
88
97
  - `remember` 时自动构建 `index.json` 索引;recall 用 TF 打分排序,中文按 bigram 分词,不用额外模型。
@@ -100,7 +109,7 @@
100
109
 
101
110
  ### 智能体接入
102
111
 
103
- 把技能装进智能体后,SKILL.md 会自动教会智能体一套工作流:**开工 `recall` 恢复上下文 → 重要信息 `remember` 落盘 → 收工归档**。也可在对话中直接说「记住 XXX」「上次说到哪了」「记一笔」触发。
112
+ 把技能装进智能体后,SKILL.md 会自动教会智能体一套工作流:**开工 `context` 主注入(身份 + 画像 + 近期记忆 + 边界 + 承诺)→ 重要信息 `remember` 落盘 → 收工归档**,并遵循「记忆守则」(类型红线 / 触发信号 / 底线 / 宿主隔离)。也可在对话中直接说「记住 XXX」「上次说到哪了」「记一笔」触发。
104
113
 
105
114
  ### 便携记忆盘(记忆库即引擎)
106
115
 
@@ -174,15 +183,17 @@ 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>] [--source <来源>] [--weight <0..>] [--verify] [--no-hint]` | 写入记忆(同 subject+statement 自动更新;--owner 标注归属;--source 记录来源;--weight 重要性权重、去重取 max;--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>] [--budget N]` | 生成开工上下文包(身份 + 多智能体铁律 + 画像 + 近期记忆 + 边界 + 承诺;--budget 近期记忆字符预算,token 恒定)|
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
199
  | `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理(Windows:计划任务,默认 ONLOGON 登录自启、--onstart 开机即启需管理员;非管理员自动降级用户级 Startup 静默自启)|
@@ -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
 
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.5.4
3
+ description: "元忆 —— 有权限边界的文件式智能体记忆。文件式、零依赖、可 diff/回滚:让任何 AI 智能体活过会话,开工 recall 恢复上下文、重要信息 remember 落盘、收工归档。类型体系 FACT(公共共享)/ PREF / BOUND / COMMIT(私密隔离)。触发:记住、别忘了、记一笔、记忆、remember、recall、跨会话、上次说到、续测、交接、归档、记忆盘、共享记忆、局域网记忆、画像、开工上下文、记忆守则、profile、context、越用越懂"
4
+ version: 0.6.1
5
5
  license: MIT
6
6
  ---
7
7
 
8
8
  # yotta-memory(元忆)— 有权限边界的文件式智能体记忆
9
9
 
10
10
  > 一句话:元忆 —— 有权限边界的文件式智能体记忆(不注入、可 diff、能回滚;FACT 共享、PREF / BOUND / COMMIT 私密隔离)。
11
- > 版本:0.5.4 | 最后更新:2026-08-25
11
+ > 版本:0.6.1 | 最后更新: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,82 @@ 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
+
79
+ ### 5. 写入时序(一次会话的节奏)
80
+
81
+ 1. 开工:whoami → iam(身份)→ `context`(主注入)→ `recall`(关键词补细节)。
82
+ 2. 进行中:增量写,触发信号即记;`remember --verify` 写后回读确认落盘。
83
+ 3. 收工:留交接锚点(COMMIT / 笔记),定期 `archive`。
84
+
85
+ ### 6. 写后验证
86
+
87
+ - `remember --verify`:写后自动回读校验,输出「已写回读 OK」。
88
+ - 定期 `recall` 抽查:确认能读回、无错库。
89
+ - `profile` 刷新:熟识 / 深交阶段主动重新生成画像。
90
+
91
+ ### 7. 底线与边界(不可覆盖)
92
+
93
+ - 四条刚性底线:陪伴不操控 / 理解不越界(不贴标签不诊断)/ 诚实不伪装 / 不降格。
94
+ - 数据安全:用户可查改删(被遗忘权 = `forget`);记忆明文可读不黑箱;敏感信息归用户本地。
95
+ - 操作安全:操作用户电脑须授权;个人目录不递归删;破坏性操作须明确确认。
96
+
97
+ ### 8. 宿主隔离(平行层,不是覆盖层)
98
+
99
+ - 只写本记忆库(YOTTA_MEMORY_HOME / config 指向的目录),不读写宿主 AI 自身 memory / 配置 / 系统文件。
100
+ - 数据主权:本记忆库归用户,纯文本可迁移;换宿主数据跟着走。
101
+
102
+ ### 9. 反模式(禁止)
103
+
104
+ - 查户口(连续追问私人信息)/ 贴标签(当面「你这是焦虑」)/ 表演记忆(硬塞「我记得你说过」)/ 过度推断(低置信当事实)/ 遗忘关键承诺与边界 / 把一次性闲聊当记忆。
105
+
35
106
  ### 开工第一步:确认记忆位置 + 智能体身份(每次会话先做,必做)
36
107
 
37
108
  **A. 确认记忆库位置**(AI 不会自动知道记忆库在哪,先检测,避免「recall 读空库 / 错库」):
@@ -51,13 +122,19 @@ license: MIT
51
122
  - 🔒 征得用户同意后执行 `yotta-memory iam <id>`:引擎**强制唯一性**(ID 已被其它主机/来源占用 → 拒绝并提示换 ID;确认是同一智能体才 `--force`),并**自动写自我档案**到本智能体私密区。
52
123
  - 回读:`yotta-memory whoami` 显示「已登记 + 自我档案」。
53
124
  3. **自我档案校验**:`yotta-memory recall "自我接入档案"`(本智能体)能读回字段才算就绪:
54
- `agent_id / host / memory_home / mcp_mode(stdio|http)/ engine_url(仅远端)/ token(仅远端;本机不存 token)`。
125
+ `agent_id / host / memory_home / mcp_mode(stdio|http)/ engine_url(仅远端)/ token(仅远端;本机不存 token)`,可扩展 `agent_name / user_name / relationship`(`iam --name/--user/--relationship` 写入)。
55
126
  4. **本机多智能体**:本机多个 AI 智能体共用引擎时,**每个都必须**在它自己的 MCP 配置里声明唯一 `YOTTA_AGENT_ID`(如 `env: { YOTTA_AGENT_ID: "<该智能体唯一ID>" }`),各自 `whoami` 各回各的、互不撞;本机走 stdio 免 token。
56
127
 
57
128
  **C. 身份红线(强制)**:
58
129
 
59
130
  - 智能体 ID **必须全局唯一**;**禁止**「从记忆里读到别人的 ID 就当自己的」(如看到「Kali 智能体 ID 为 dashu」就把自己当 dashu)。
60
131
  - 不确定自己的 ID → 先 `whoami` / `agent_info`,再向用户确认;**禁止猜**。
132
+
133
+ **D. 开工主注入(context)**:
134
+
135
+ - 身份就绪后运行 `yotta-memory context [--limit 10] [--budget 1800]`(远端经 MCP 用 `recall` 补细节):一键拿到「身份 + 多智能体铁律 + 用户画像摘要 + 近期记忆 + 边界提醒 + 承诺 / 锚点」;`--budget` 控制近期记忆字符预算(token 恒定,不随记忆膨胀)。
136
+ - 无画像时 context 自动生成一次或降级输出其余段,不报错。
137
+ - 需要深挖旧事再 `recall <关键词>`。
61
138
  - 私密记忆(PREF / BOUND / COMMIT)**必须有 owner**:未声明身份写私密会被引擎拒绝(公共 FACT 不受影响)。
62
139
  - **禁止直接读写记忆库文件(硬红线)**:一切读写走 `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
140
 
@@ -68,15 +145,17 @@ license: MIT
68
145
  | 命令 | 作用 |
69
146
  |---|---|
70
147
  | `yotta-memory init [--project] [--dir <目录>]` | 初始化记忆库(默认用户级;--dir 显式指定位置)|
71
- | `yotta-memory remember <type> <subject> <statement> [--owner <id>]` | 写入(同 subject+statement 自动更新;--owner 标注归属,默认取 agent id)|
148
+ | `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 关闭类型启发式提示)|
72
149
  | `yotta-memory recall [关键词] [--type T] [--limit N] [--agent <id>] [--owner <id>] [--all] [--unsafe]` | 检索(索引+TF 打分,读取分区过滤;越界读其它智能体私密默认拒绝,需 grant / identity=user / `--unsafe`;`--agent <其它>` 只作身份声明/展示,不授予跨读——读他人私密同样要授权;项目级优先)|
150
+ | `yotta-memory profile [--owner <id>]` | 生成用户画像(聚合 `private/<owner>/` 原文,零推断,写 `profile.md`;跨 owner 默认拒绝)|
151
+ | `yotta-memory context [--limit N] [--owner <id>] [--budget N]` | 生成开工上下文包(身份 + 多智能体铁律 + 画像 + 近期记忆 + 边界 + 承诺;--budget 近期记忆字符预算,0=不限)|
73
152
  | `yotta-memory forget <文件>` | 删除(按类型目录路径或文件名)|
74
153
  | `yotta-memory archive [--days 180] [--threshold 0.4]` | 归档旧记忆(盖棺分+年龄,immutable 除外)|
75
154
  | `yotta-memory reindex` | 重建索引(手动改 .md 后校正)|
76
155
  | `yotta-memory export [--out f.json]` / `import <f.json>` | 导出 / 导入 |
77
156
  | `yotta-memory config set memory_home <目录>` / `config get` | 持久记住 / 查看记忆库位置(`~/.yottamemory/config.json`)|
78
157
  | `yotta-memory whoami` | 查看当前智能体身份与登记状态(读 `YOTTA_AGENT_ID` / `X-Agent-Id`,不猜不默认)|
79
- | `yotta-memory iam <id> [--force]` | 登记本智能体唯一身份并自动落自我档案(`agents.json`,ID 必须唯一)|
158
+ | `yotta-memory iam <id> [--name <显示名>] [--user <用户名>] [--relationship <关系>] [--force]` | 登记本智能体唯一身份并自动落自我档案(`agents.json`,ID 必须唯一;可选扩展显示名 / 用户 / 关系)|
80
159
  | `yotta-memory token new --agent <id> [--force]` / `token list` / `token revoke --agent <id>` | 每智能体访问 token:生成 / 列出 / 吊销(登记 `<记忆库>/.server/tokens.json`;同 ID 已被其它来源占用需 `--force` 覆盖,防不同智能体合流)|
81
160
  | `yotta-memory serve [--host 0.0.0.0] [--port 8787] [--no-auth] [--stdio]` | 启动 MCP 记忆引擎(streamable HTTP 局域网 / --stdio 本地零进程模式;Bearer token + X-Agent-Id 鉴权)|
82
161
  | `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理(Windows:计划任务,默认 ONLOGON 登录自启、--onstart 开机即启需管理员;非管理员自动降级用户级 Startup 静默自启)|
@@ -89,14 +168,15 @@ license: MIT
89
168
  <root>/
90
169
  ├── facts/ # FACT 事实(公共可共享)
91
170
  ├── private/<owner>/<type>/ # PREF / BOUND / COMMIT,按智能体隔离
171
+ ├── private/<owner>/profile.md # 用户画像(profile 命令生成,可再生成,零推断)
92
172
  ├── .archive/ # 归档区
93
173
  ├── index.json # 检索索引(tokens = 分词词频,非访问令牌)
94
174
  └── agents.json # 智能体身份登记表(唯一性)
95
175
  ```
96
176
 
97
- 记忆文件 `<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>/`。
177
+ 记忆文件 `<YYYY-MM-DD>-<NNNN>.md`,frontmatter 含 `type / subject / statement / confidence / created / updated / tags / immutable / scope / owner / source / weight / access_count / last_accessed`(`source` 记录来源、`weight` 重要性权重默认 1.0);正文为记忆内容。旧版根下平铺的 `prefs/` `bounds/` `commits/` 会在 `reindex`(或首次 recall 建索引)时按 frontmatter `owner` 自动迁移到 `private/<owner>/<type>/`。
98
178
 
99
- 自我档案(本智能体身份,强制落盘):PREF,`subject=自我接入档案`,`owner=<本智能体ID>`,statement 为 `; ` 分隔的 key:value——`agent_id / host / memory_home / mcp_mode(stdio|http)/ engine_url(仅远端)/ token(仅远端;本机不存 token)`。
179
+ 自我档案(本智能体身份,强制落盘):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
180
 
101
181
  ## 便携记忆盘模式(局域网多机共享)
102
182
 
@@ -173,7 +253,7 @@ yotta-memory --version # 或 command -v yotta-memory / where yotta-memory
173
253
  4. 按当前智能体机制重载 MCP(必要时请用户重启会话);
174
254
  5. 用 MCP tools 读写记忆。
175
255
 
176
- > MCP 工具集与 CLI 一致:remember / recall / search / forget / archive / reindex / export / import;管理动作(init / config / token / lan / serve)不进 MCP,token 管理不远程暴露。
256
+ > MCP 工具集与 CLI 一致:remember / recall / search / forget / archive / reindex / export / import / profile;管理动作(init / config / token / lan / serve)不进 MCP,token 管理不远程暴露。
177
257
 
178
258
  ### 4.7 MCP 配置位置表
179
259
  | 智能体 | 常见 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 --budget 1800 # 生成开工上下文包(身份+铁律+画像+近期记忆+边界+承诺,预算控 token)
76
+ yotta-memory iam <id> --name 元忆 --user 老张 --relationship 伙伴 # 自我档案扩展显示名/用户/关系
77
+ ```
78
+
79
+ - `profile` 引擎零推断:只按类型 / 主题 / 标签归组呈现原文,画像结论由 AI 内部形成,不当面贴标签。
80
+ - `context` 是每次会话开工的主注入,替代裸 `recall`;无画像时自动生成一次或降级,不报错;`--budget` 控制近期记忆字符预算(token 恒定)。
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`。
@@ -231,15 +244,17 @@ 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>] [--source <来源>] [--weight <0..>] [--verify] [--no-hint]` | 写入记忆(--source 来源;--weight 重要性权重;--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>] [--budget N]` | 开工上下文包(身份+铁律+画像+近期记忆+边界+承诺;--budget 近期记忆字符预算)|
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
260
  | `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理(Windows:计划任务;非管理员自动降级用户级 Startup 静默自启)|
@@ -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
 
@@ -5,6 +5,8 @@
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
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「记忆守则」
9
+ // v0.6.1 新增:灵魂盘机制精髓补齐——context --budget(token 预算,近记忆按剩余预算放行)/ context 内嵌「多智能体接入铁律」段 / remember --source/--weight(来源 + 重要性权重,去重 weight 取 max)/ 近期记忆排序融合 importance(confidence×recency+updated+weight+immutable)
8
10
  'use strict';
9
11
 
10
12
  const fs = require('fs');
@@ -14,7 +16,7 @@ const crypto = require('crypto');
14
16
  const http = require('http');
15
17
  const child_process = require('child_process');
16
18
 
17
- const VERSION = '0.5.4';
19
+ const VERSION = '0.6.1';
18
20
  const TYPES = ['FACT', 'PREF', 'BOUND', 'COMMIT'];
19
21
  const TYPE_DIRS = { FACT: 'facts', PREF: 'prefs', BOUND: 'bounds', COMMIT: 'commits' };
20
22
  const PUBLIC_DIR = 'facts';
@@ -23,11 +25,14 @@ const PRIVATE_LEAF = ['prefs', 'bounds', 'commits'];
23
25
  const ARCHIVE_DIR = '.archive';
24
26
  const INDEX_FILE = 'index.json';
25
27
  const PRIVATE_TYPES = ['PREF', 'BOUND', 'COMMIT'];
26
- const FIELD_ORDER = ['type', 'subject', 'statement', 'confidence', 'created', 'updated', 'tags', 'immutable', 'scope', 'owner', 'access_count', 'last_accessed'];
28
+ const FIELD_ORDER = ['type', 'subject', 'statement', 'confidence', 'created', 'updated', 'tags', 'immutable', 'scope', 'owner', 'source', 'weight', 'access_count', 'last_accessed'];
27
29
  const CONFIG_FILE = 'config.json';
28
30
  const SERVER_SUBDIR = '.server';
29
31
  const TOKENS_FILE = 'tokens.json';
30
32
  const AGENTS_FILE = 'agents.json';
33
+ const PROFILE_FILE = 'profile.md';
34
+ // remember 类型启发式提示关键词(statement 含主观/关系词且 type=FACT 时提示改 PREF,仅提示不拦截)
35
+ const HINT_KEYWORDS = ['用户', '偏好', '喜欢', '关系', '称呼', '本人', '希望', '讨厌', '欣赏', '习惯', '忌讳', '介意', '不要', '别用'];
31
36
 
32
37
  // ---- 全局配置(记忆库位置持久化,固定 ~/.yottamemory/config.json)----
33
38
  function configPath() {
@@ -270,6 +275,8 @@ function readEntry(fp, root) {
270
275
  access_count: parseInt(meta.access_count || '0', 10) || 0,
271
276
  last_accessed: meta.last_accessed || '',
272
277
  immutable: meta.immutable === 'true',
278
+ source: meta.source || '',
279
+ weight: (parseFloat(meta.weight) > 0 ? parseFloat(meta.weight) : 1.0),
273
280
  tokens: buildTokens(subject, statement, tags),
274
281
  meta: meta,
275
282
  };
@@ -318,6 +325,21 @@ function vitality(meta) {
318
325
  if (last) { const d = daysBetween(last, today()); recency = Math.max(0, 1 - Math.floor(d / 30) * 0.2); }
319
326
  return 0.4 * conf + 0.3 * accScore + 0.3 * recency;
320
327
  }
328
+ // 灵魂盘 importance 融合:confidence × (0.5 + recency) + updated 加分 + weight 乘子 + immutable 加分
329
+ function importanceScore(meta) {
330
+ const conf = parseFloat(meta.confidence || 1.0);
331
+ let ageDays = 999;
332
+ if (meta.created) {
333
+ const d = daysBetween(meta.created, today());
334
+ if (!isNaN(d)) ageDays = d;
335
+ }
336
+ const recency = 1.0 / (1.0 + Math.max(ageDays, 0) / 180.0);
337
+ let score = conf * (0.5 + recency);
338
+ if (meta.updated && meta.created && meta.updated !== meta.created) score += 0.5;
339
+ const w = parseFloat(meta.weight || 1.0); if (w > 0) score *= w;
340
+ if (meta.immutable === 'true') score += 2.0;
341
+ return score;
342
+ }
321
343
  function loadGrants(root) {
322
344
  const fp = path.join(root, 'grants.json');
323
345
  try { return JSON.parse(fs.readFileSync(fp, 'utf8')) || {}; } catch (e) { return {}; }
@@ -382,9 +404,22 @@ function rememberCore(type, subject, statement, opts) {
382
404
  const patch = { updated: today() };
383
405
  if (owner && !meta.owner) patch.owner = owner;
384
406
  if (scope && !meta.scope) patch.scope = scope;
407
+ if (opts.source && !meta.source) patch.source = opts.source;
408
+ const w = parseFloat(opts.weight); if (w > 0) patch.weight = Math.max(parseFloat(meta.weight || '1.0') || 1.0, w);
385
409
  rewriteFrontmatter(fp, patch);
386
410
  upsertIndexEntry(root, readEntry(fp, root));
387
- return { error: false, text: '已更新: ' + fp };
411
+ let text = '已更新: ' + fp;
412
+ if (opts.hint !== false && t === 'FACT') {
413
+ const hit = HINT_KEYWORDS.filter(function (w) { return stmt.indexOf(w) !== -1; });
414
+ if (hit.length) text += '\n[提示] statement 含主观/关系词(' + hit.join('、') + '),疑似用户偏好 / 关系内容——若属此类建议改用 PREF(私密,仅本人可读)。仅提示不拦截;--no-hint 可关闭。';
415
+ }
416
+ if (opts.verify) {
417
+ const rel = path.relative(root, fp).replace(/\\/g, '/');
418
+ const rr = recallCore(subj, { limit: 10, agent: selfAgent || opts.agent });
419
+ const ok = rr.text.replace(/\\/g, '/').indexOf(rel) !== -1;
420
+ text += '\n[verify] ' + (ok ? '已写回读 OK: ' + rel : '回读未命中,请检查: ' + rel);
421
+ }
422
+ return { error: false, text: text };
388
423
  }
389
424
  }
390
425
  }
@@ -394,11 +429,25 @@ function rememberCore(type, subject, statement, opts) {
394
429
  type: t, subject: subj, statement: stmt,
395
430
  confidence: 1.0, created: today(), updated: today(),
396
431
  tags: [], immutable: false,
397
- scope: scope, owner: owner, access_count: 0, last_accessed: '',
432
+ scope: scope, owner: owner,
433
+ source: opts.source || '',
434
+ weight: (parseFloat(opts.weight) > 0 ? parseFloat(opts.weight) : 1.0),
435
+ access_count: 0, last_accessed: '',
398
436
  };
399
437
  fs.writeFileSync(file, frontmatterToText(rec, stmt), 'utf8');
400
438
  upsertIndexEntry(root, readEntry(file, root));
401
- return { error: false, text: '已记录: ' + file };
439
+ let text = '已记录: ' + file;
440
+ if (opts.hint !== false && t === 'FACT') {
441
+ const hit = HINT_KEYWORDS.filter(function (w) { return stmt.indexOf(w) !== -1; });
442
+ if (hit.length) text += '\n[提示] statement 含主观/关系词(' + hit.join('、') + '),疑似用户偏好 / 关系内容——若属此类建议改用 PREF(私密,仅本人可读)。仅提示不拦截;--no-hint 可关闭。';
443
+ }
444
+ if (opts.verify) {
445
+ const rel = path.relative(root, file).replace(/\\/g, '/');
446
+ const rr = recallCore(subj, { limit: 10, agent: selfAgent || opts.agent });
447
+ const ok = rr.text.replace(/\\/g, '/').indexOf(rel) !== -1;
448
+ text += '\n[verify] ' + (ok ? '已写回读 OK: ' + rel : '回读未命中,请检查: ' + rel);
449
+ }
450
+ return { error: false, text: text };
402
451
  }
403
452
  function recallCore(query, opts) {
404
453
  const roots = [projectRoot(), userRoot()].filter(function (r) { return fs.existsSync(r); });
@@ -536,7 +585,9 @@ function cmdInit(opts) {
536
585
  console.log(r.text);
537
586
  }
538
587
  function cmdRemember(type, subject, statement, opts) {
539
- const r = rememberCore(type, subject, statement, opts);
588
+ const o = Object.assign({}, opts);
589
+ if (o.noHint) o.hint = false;
590
+ const r = rememberCore(type, subject, statement, o);
540
591
  console.log(r.text);
541
592
  if (r.error) process.exit(2);
542
593
  }
@@ -608,6 +659,8 @@ function importCore(root, src) {
608
659
  updated: meta.updated || today(),
609
660
  tags: parseTags(meta.tags),
610
661
  immutable: meta.immutable === true || meta.immutable === 'true',
662
+ source: meta.source || '',
663
+ weight: (parseFloat(meta.weight) > 0 ? parseFloat(meta.weight) : 1.0),
611
664
  scope: meta.scope || defaultScope(t),
612
665
  owner: meta.owner || '',
613
666
  access_count: parseInt(meta.access_count || '0', 10) || 0,
@@ -698,6 +751,14 @@ function findSelfProfile(root, agentId) {
698
751
  }
699
752
  return '';
700
753
  }
754
+ function parseKvBody(body) {
755
+ const out = {};
756
+ for (const seg of String(body || '').split(';')) {
757
+ const m = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s*:\s*([\s\S]*)$/.exec(seg);
758
+ if (m && m[2] !== undefined) out[m[1]] = m[2].trim();
759
+ }
760
+ return out;
761
+ }
701
762
  function selfProfileBody(agentId, root, extra) {
702
763
  const lines = [];
703
764
  lines.push('agent_id: ' + agentId);
@@ -706,14 +767,41 @@ function selfProfileBody(agentId, root, extra) {
706
767
  lines.push('mcp_mode: ' + (extra.mcpMode || 'stdio'));
707
768
  if (extra.engineUrl) lines.push('engine_url: ' + extra.engineUrl);
708
769
  if (extra.token) lines.push('token: ' + extra.token);
770
+ if (extra.name) lines.push('agent_name: ' + extra.name);
771
+ if (extra.userName) lines.push('user_name: ' + extra.userName);
772
+ if (extra.relationship) lines.push('relationship: ' + extra.relationship);
709
773
  return lines.join('; ');
710
774
  }
711
775
  function writeSelfProfile(root, agentId, extra) {
776
+ extra = extra || {};
712
777
  const dir = selfPrefsDir(root, agentId);
713
778
  fs.mkdirSync(dir, { recursive: true });
779
+ const existing = findSelfProfile(root, agentId);
780
+ if (existing) {
781
+ // 已有自我档案:合并 kv 字段更新(不重复建文件)
782
+ const fp = path.join(dir, existing);
783
+ const parsed = parseFrontmatter(fs.readFileSync(fp, 'utf8'));
784
+ const kv = parseKvBody(parsed.body);
785
+ kv.agent_id = agentId;
786
+ kv.host = os.hostname();
787
+ kv.memory_home = root;
788
+ kv.mcp_mode = extra.mcpMode || kv.mcp_mode || 'stdio';
789
+ if (extra.engineUrl) kv.engine_url = extra.engineUrl;
790
+ if (extra.token) kv.token = extra.token;
791
+ if (extra.name) kv.agent_name = extra.name;
792
+ if (extra.userName) kv.user_name = extra.userName;
793
+ if (extra.relationship) kv.relationship = extra.relationship;
794
+ const body = Object.keys(kv).map(function (k) { return k + ': ' + kv[k]; }).join('; ');
795
+ const meta = Object.assign({}, parsed.meta);
796
+ meta.updated = today();
797
+ meta.statement = body;
798
+ fs.writeFileSync(fp, frontmatterToText(meta, body), 'utf8');
799
+ upsertIndexEntry(root, readEntry(fp, root));
800
+ return fp;
801
+ }
714
802
  const seq = nextSeq(dir);
715
803
  const file = path.join(dir, today() + '-' + seq + '.md');
716
- const body = selfProfileBody(agentId, root, extra || {});
804
+ const body = selfProfileBody(agentId, root, extra);
717
805
  const rec = {
718
806
  type: 'PREF', subject: SELF_PROFILE_SUBJECT, statement: body,
719
807
  confidence: 1.0, created: today(), updated: today(),
@@ -736,7 +824,7 @@ function identityConflict(root, agentId) {
736
824
  function cmdIam(agentId, opts) {
737
825
  const root = userRoot();
738
826
  ensureInit(root);
739
- if (!agentId) { console.error('请指定 <id>:yotta-memory iam <id> [--force]'); process.exit(2); }
827
+ if (!agentId) { console.error('请指定 <id>:yotta-memory iam <id> [--name <显示名>] [--user <用户名>] [--relationship <关系>] [--force]'); process.exit(2); }
740
828
  const conflict = identityConflict(root, agentId);
741
829
  if (conflict && !opts.force) {
742
830
  console.error("错误: ID '" + agentId + "' " + conflict + '。每个 AI 智能体的 ID 必须唯一。请换一个唯一 ID,或确认是同一智能体后加 --force 覆盖。');
@@ -749,8 +837,11 @@ function cmdIam(agentId, opts) {
749
837
  const existed = data.agents[agentId];
750
838
  data.agents[agentId] = { host: host, created: (existed && existed.created) || today() };
751
839
  saveAgents(root, data);
752
- const file = writeSelfProfile(root, agentId, { mcpMode: 'stdio' });
840
+ const file = writeSelfProfile(root, agentId, { mcpMode: 'stdio', name: opts.name, userName: opts.user, relationship: opts.relationship });
753
841
  console.log('已登记智能体身份: ' + agentId + '(host=' + host + ',' + (conflict ? '--force 覆盖' : '新建') + ')');
842
+ if (opts.name || opts.user || opts.relationship) {
843
+ console.log('自我档案扩展: ' + [opts.name && '显示名=' + opts.name, opts.user && '用户=' + opts.user, opts.relationship && '关系=' + opts.relationship].filter(Boolean).join(' / '));
844
+ }
754
845
  console.log('已写入自我档案: ' + file);
755
846
  console.log('本机免 token:以后用 whoami 确认身份;远端接入需 token new --agent ' + agentId);
756
847
  }
@@ -772,9 +863,194 @@ function cmdWhoami() {
772
863
  console.log('当前智能体身份: ' + id);
773
864
  console.log('登记状态: ' + reg);
774
865
  console.log('自我档案: ' + (profile ? profile : '未写入(请执行 yotta-memory iam ' + id + ')'));
866
+ if (profile) {
867
+ const kv = parseKvBody(parseFrontmatter(fs.readFileSync(path.join(selfPrefsDir(root, id), profile), 'utf8')).body);
868
+ if (kv.agent_name) console.log('显示名: ' + kv.agent_name);
869
+ if (kv.user_name) console.log('用户: ' + kv.user_name);
870
+ if (kv.relationship) console.log('关系: ' + kv.relationship);
871
+ }
775
872
  if (!agents[id] && !tokens[id]) console.log('提示: 请先 yotta-memory iam ' + id + ' 登记唯一身份并落自我档案,再写私密记忆。');
776
873
  }
777
874
 
875
+ // ---- 用户画像聚合(v0.6.0,零推断:只归组呈现原文,结论由承载 AI 依据「记忆守则」内部形成)----
876
+ function profileGroups(root, owner) {
877
+ const groups = [];
878
+ const map = {};
879
+ for (const t of PRIVATE_LEAF) {
880
+ const dir = path.join(root, PRIVATE_DIR, owner, t);
881
+ if (!fs.existsSync(dir)) continue;
882
+ for (const f of fs.readdirSync(dir)) {
883
+ const fp = path.join(dir, f);
884
+ if (!fs.statSync(fp).isFile()) continue;
885
+ const e = readEntry(fp, root);
886
+ const key = e.type + '\u0000' + (e.subject || '') + '\u0000' + e.tags.join(',');
887
+ if (!map[key]) {
888
+ map[key] = { type: e.type, subject: e.subject || '', tags: e.tags || [], items: [] };
889
+ groups.push(map[key]);
890
+ }
891
+ map[key].items.push({
892
+ file: e.file, statement: e.statement, confidence: e.confidence,
893
+ last_accessed: e.last_accessed, updated: e.updated,
894
+ });
895
+ }
896
+ }
897
+ return groups;
898
+ }
899
+ function profileCore(opts) {
900
+ opts = opts || {};
901
+ const root = userRoot();
902
+ const selfAgent = opts.selfAgent !== undefined ? opts.selfAgent : currentAgent();
903
+ const owner = opts.owner || selfAgent;
904
+ if (!owner) return { error: true, exitCode: 2, text: '请先声明身份(YOTTA_AGENT_ID / AGENT_ID)或传 --owner <id>,再生成画像。' };
905
+ if (selfAgent && owner !== selfAgent && owner !== 'user' && !opts.unsafe && !hasGrant(selfAgent, owner)) {
906
+ return { error: true, exitCode: 3, text: '拒绝: 不能生成其它智能体 ' + owner + ' 的画像(private/' + owner + '/ 为私密区)。如需读取请 --owner user 或 --unsafe(用户显式授权)。' };
907
+ }
908
+ const ownerDir = path.join(root, PRIVATE_DIR, owner);
909
+ if (!fs.existsSync(ownerDir)) return { error: false, text: '该智能体(' + owner + ')暂无画像:private/' + owner + '/ 不存在或为空。' };
910
+ const groups = profileGroups(root, owner);
911
+ if (!groups.length) return { error: false, text: '该智能体(' + owner + ')暂无画像:private/' + owner + '/ 下无 PREF / BOUND / COMMIT 条目。' };
912
+ const lines = [];
913
+ lines.push('# 用户画像(' + owner + ')');
914
+ lines.push('');
915
+ lines.push('> 生成: yotta-memory profile | 引擎零推断:以下为私密记忆原文的结构化归组,画像结论由承载 AI 依据「记忆守则」在内部形成,不当面贴标签。');
916
+ lines.push('> 刷新: ' + today());
917
+ lines.push('');
918
+ for (const g of groups) {
919
+ lines.push('## ' + g.type + ' · ' + (g.subject || '(无主题)') + (g.tags.length ? ' [tags: ' + g.tags.join(', ') + ']' : ''));
920
+ lines.push('');
921
+ for (const it of g.items) {
922
+ const acc = it.last_accessed ? '最近访问 ' + it.last_accessed : '未访问';
923
+ lines.push('- ' + it.statement + '(confidence ' + it.confidence + ' · ' + acc + ')');
924
+ lines.push(' - ' + it.file);
925
+ }
926
+ lines.push('');
927
+ }
928
+ const text = lines.join('\n');
929
+ const outFile = path.join(ownerDir, PROFILE_FILE);
930
+ fs.writeFileSync(outFile, text + '\n', 'utf8');
931
+ return { error: false, text: text + '\n\n[已写入] ' + outFile };
932
+ }
933
+ function cmdProfile(opts) {
934
+ const r = profileCore(opts);
935
+ console.log(r.text);
936
+ if (r.exitCode) process.exit(r.exitCode);
937
+ if (r.error) process.exit(2);
938
+ }
939
+
940
+ // ---- 开工上下文包(v0.6.0:身份 + 画像 + 近期记忆 + 边界 + 承诺,stdout 不落盘)----
941
+ function selfProfileKv(root, id) {
942
+ const profile = findSelfProfile(root, id);
943
+ if (!profile) return {};
944
+ return parseKvBody(parseFrontmatter(fs.readFileSync(path.join(selfPrefsDir(root, id), profile), 'utf8')).body);
945
+ }
946
+ function contextCore(opts) {
947
+ opts = opts || {};
948
+ const roots = [projectRoot(), userRoot()].filter(function (r) { return fs.existsSync(r); });
949
+ if (!roots.length) return { error: false, exitCode: 0, text: '记忆库不存在,请先运行: yotta-memory init' };
950
+ const root = userRoot();
951
+ const limit = opts.limit || 10;
952
+ const selfAgent = opts.selfAgent !== undefined ? opts.selfAgent : currentAgent();
953
+ const owner = opts.owner || selfAgent;
954
+ const unsafe = !!opts.unsafe;
955
+ const budget = opts.budget ? parseInt(opts.budget, 10) : 0;
956
+ const lines = [];
957
+ function usedChars() { return lines.reduce(function (s, x) { return s + String(x).length + 1; }, 0); }
958
+ lines.push('# 开工上下文包(yotta-memory context)');
959
+ lines.push('');
960
+ lines.push('## 1. 身份');
961
+ lines.push('');
962
+ if (!owner) {
963
+ lines.push('- 未声明智能体身份(无 YOTTA_AGENT_ID / --owner)。私密记忆与画像不可用;请先 whoami / iam。');
964
+ } else {
965
+ lines.push('- agent_id: ' + owner);
966
+ const kv = selfProfileKv(root, owner);
967
+ if (kv.agent_name) lines.push('- agent_name: ' + kv.agent_name);
968
+ if (kv.user_name) lines.push('- user_name: ' + kv.user_name);
969
+ if (kv.relationship) lines.push('- relationship: ' + kv.relationship);
970
+ lines.push('- host: ' + os.hostname());
971
+ lines.push('- memory_home: ' + root);
972
+ }
973
+ lines.push('');
974
+ lines.push('## 1.5 多智能体接入铁律');
975
+ lines.push('');
976
+ lines.push('- 可读:A. 公共 FACT(facts/)B. 本智能体私密(private/<owner>/)C. 其它智能体私密 = 禁区(grant / identity=user / --unsafe 显式授权除外)。');
977
+ lines.push('- 可写:FACT→公共;PREF / BOUND / COMMIT→仅本智能体私密区;禁止写其它智能体私密区。');
978
+ lines.push('- 违规红线:禁止搜索 / 读取 / 总结其它智能体私密;禁止把用户私密关系 / 心理健康 / 生活隐私写入公共区。');
979
+ lines.push('');
980
+ lines.push('## 2. 用户画像摘要');
981
+ lines.push('');
982
+ if (owner) {
983
+ const pf = path.join(root, PRIVATE_DIR, owner, PROFILE_FILE);
984
+ if (fs.existsSync(pf)) {
985
+ lines.push('```markdown');
986
+ lines.push(fs.readFileSync(pf, 'utf8').trim());
987
+ lines.push('```');
988
+ } else {
989
+ const pr = profileCore({ owner: owner, unsafe: unsafe, selfAgent: selfAgent });
990
+ if (!pr.error && pr.text.indexOf('[已写入]') !== -1 && fs.existsSync(pf)) {
991
+ lines.push('(画像不存在,已自动生成一次,见下)');
992
+ lines.push('```markdown');
993
+ lines.push(fs.readFileSync(pf, 'utf8').trim());
994
+ lines.push('```');
995
+ } else {
996
+ lines.push('(该身份暂无画像;可运行 yotta-memory profile 生成)');
997
+ }
998
+ }
999
+ } else {
1000
+ lines.push('(未声明身份,跳过画像;先 iam 登记后可生成)');
1001
+ }
1002
+ lines.push('');
1003
+ lines.push('## 3. 近期记忆(按活跃度前 ' + limit + ' 条)');
1004
+ lines.push('');
1005
+ const recent = [];
1006
+ for (const r of roots) {
1007
+ for (const e of ensureIndex(r)) {
1008
+ if (classifyRead(e, owner, '', unsafe, selfAgent) === 'denied') continue;
1009
+ recent.push({ e: e, s: importanceScore(e) });
1010
+ }
1011
+ }
1012
+ recent.sort(function (a, b) { return b.s - a.s; });
1013
+ const recentShown = recent.slice(0, limit);
1014
+ if (!recentShown.length) lines.push('(暂无记忆)');
1015
+ for (const h of recentShown) {
1016
+ const line = '- [' + h.e.type + '] ' + h.e.subject + ': ' + h.e.statement;
1017
+ if (budget > 0 && usedChars() + line.length > budget) break;
1018
+ lines.push(line);
1019
+ }
1020
+ lines.push('');
1021
+ lines.push('## 4. 边界提醒(BOUND)');
1022
+ lines.push('');
1023
+ const bounds = [];
1024
+ for (const r of roots) {
1025
+ for (const e of ensureIndex(r)) {
1026
+ if (e.type !== 'BOUND') continue;
1027
+ if (classifyRead(e, owner, '', unsafe, selfAgent) === 'denied') continue;
1028
+ bounds.push(e);
1029
+ }
1030
+ }
1031
+ if (!bounds.length) lines.push('(无)');
1032
+ for (const e of bounds) lines.push('- ' + (e.subject || '边界') + ': ' + e.statement);
1033
+ lines.push('');
1034
+ lines.push('## 5. 承诺 / 锚点(COMMIT)');
1035
+ lines.push('');
1036
+ const commits = [];
1037
+ for (const r of roots) {
1038
+ for (const e of ensureIndex(r)) {
1039
+ if (e.type !== 'COMMIT') continue;
1040
+ if (classifyRead(e, owner, '', unsafe, selfAgent) === 'denied') continue;
1041
+ commits.push(e);
1042
+ }
1043
+ }
1044
+ if (!commits.length) lines.push('(无)');
1045
+ for (const e of commits) lines.push('- ' + (e.subject || '承诺') + ': ' + e.statement);
1046
+ return { error: false, exitCode: 0, text: lines.join('\n') };
1047
+ }
1048
+ function cmdContext(opts) {
1049
+ const r = contextCore(opts);
1050
+ console.log(r.text);
1051
+ if (r.exitCode) process.exit(r.exitCode);
1052
+ }
1053
+
778
1054
  // ---- config 命令 ----
779
1055
  function cmdConfigSet(key, value) {
780
1056
  if (key !== 'memory_home') { console.error('未知配置项: ' + key + '(可用: memory_home)'); process.exit(2); }
@@ -803,6 +1079,7 @@ function mcpTools() {
803
1079
  { name: 'export', description: '导出全部记忆到引擎主机上的 JSON 文件。out 可选(默认 <记忆库>/yottamemory-export-<日期>.json)', inputSchema: { type: 'object', properties: { out: { type: 'string' } } } },
804
1080
  { name: 'import', description: '从引擎主机上的 JSON 文件导入记忆。src 为文件路径(可绝对路径,或相对记忆库目录)', inputSchema: { type: 'object', properties: { src: { type: 'string' } }, required: ['src'] } },
805
1081
  { name: 'agent_info', description: '查看当前智能体身份与登记状态(远端读经 token 校验的 X-Agent-Id;本机读 YOTTA_AGENT_ID)。开工先确认「我是谁」,禁止从记忆里抄别人的 ID', inputSchema: { type: 'object', properties: {} } },
1082
+ { name: 'profile', description: '生成当前智能体的用户画像(只读聚合 private/<owner>/ 下 PREF/BOUND/COMMIT 原文,零推断,写入 private/<owner>/profile.md)。owner 默认当前智能体', inputSchema: { type: 'object', properties: { owner: { type: 'string' } } } },
806
1083
  ];
807
1084
  }
808
1085
  function callTool(name, args, ctx) {
@@ -850,7 +1127,19 @@ function callTool(name, args, ctx) {
850
1127
  if (agents[id]) reg = 'agents.json 已登记(host=' + agents[id].host + ')';
851
1128
  else if (tokens[id]) reg = 'tokens.json 已登记(远端)';
852
1129
  const profile = findSelfProfile(root, id);
853
- return { text: '当前智能体身份: ' + id + '\n登记状态: ' + reg + '\n自我档案: ' + (profile || '未写入(本地引擎主机执行 yotta-memory iam ' + id + ')'), error: false };
1130
+ let rel = '';
1131
+ if (profile) {
1132
+ const kv = selfProfileKv(root, id);
1133
+ if (kv.agent_name) rel += '\n显示名: ' + kv.agent_name;
1134
+ if (kv.user_name) rel += '\n用户: ' + kv.user_name;
1135
+ if (kv.relationship) rel += '\n关系: ' + kv.relationship;
1136
+ }
1137
+ return { text: '当前智能体身份: ' + id + '\n登记状态: ' + reg + '\n自我档案: ' + (profile || '未写入(本地引擎主机执行 yotta-memory iam ' + id + ')') + rel, error: false };
1138
+ }
1139
+ if (name === 'profile') {
1140
+ const ownerArg = args.owner ? String(args.owner) : '';
1141
+ const r = profileCore({ owner: ownerArg || agent, selfAgent: agent });
1142
+ return { text: r.text, error: r.error };
854
1143
  }
855
1144
  return { text: '未知工具: ' + name, error: true };
856
1145
  } catch (e) {
@@ -1134,7 +1423,9 @@ function usage() {
1134
1423
  ' yotta-memory init [--project] [--dir <目录>] 初始化记忆库(默认用户级;--dir 显式指定位置)',
1135
1424
  ' yotta-memory config set memory_home <目录> 持久记住记忆库位置',
1136
1425
  ' yotta-memory config get 查看当前配置与生效位置',
1137
- ' yotta-memory remember <type> <subject> <statement> [--owner <id>] 写入记忆',
1426
+ ' yotta-memory remember <type> <subject> <statement> [--owner <id>] [--source <来源>] [--weight <0..>] [--verify] [--no-hint] 写入记忆(--source 记录来源;--weight 重要性权重默认 1.0 去重取 max;--verify 写后回读校验;--no-hint 关闭类型启发式提示)',
1427
+ ' yotta-memory profile [--owner <id>] 生成用户画像(聚合 private/<owner>/ 原文,零推断,写 profile.md)',
1428
+ ' yotta-memory context [--limit N] [--owner <id>] [--budget N] 生成开工上下文包(身份+铁律+画像+近期记忆+边界+承诺;--budget 近期记忆字符预算,0=不限)',
1138
1429
  ' yotta-memory recall [关键词] [--type T] [--limit N] [--agent <id>] [--owner <id>] [--all] [--unsafe] 检索(索引+TF,读取分区;--agent 仅表示“以该身份检索/模拟”,跨智能体私密读取需 --unsafe / 授权)',
1139
1430
  ' yotta-memory forget <文件或文件名> 删除一条记忆',
1140
1431
  ' yotta-memory archive [--days 180] [--threshold 0.4] 归档(盖棺分+年龄)',
@@ -1142,7 +1433,7 @@ function usage() {
1142
1433
  ' yotta-memory export [--out 文件.json] 导出全部记忆',
1143
1434
  ' yotta-memory import <文件.json> 导入记忆',
1144
1435
  ' yotta-memory whoami 查看当前智能体身份与登记状态(读 YOTTA_AGENT_ID / X-Agent-Id,不猜不默认)',
1145
- ' yotta-memory iam <id> [--force] 登记本智能体唯一身份并自动落自我档案(agents.json;ID 必须唯一)',
1436
+ ' yotta-memory iam <id> [--name <显示名>] [--user <用户名>] [--relationship <关系>] [--force] 登记本智能体唯一身份并自动落自我档案(agents.json;ID 必须唯一)',
1146
1437
  ' yotta-memory token new --agent <id> [--force] 生成/重置某智能体访问 token(同 ID 已被其它来源占用需 --force 覆盖)',
1147
1438
  ' yotta-memory token list 列出已登记智能体',
1148
1439
  ' yotta-memory token revoke --agent <id> 吊销某智能体 token',
@@ -1162,7 +1453,7 @@ function main() {
1162
1453
  if (!args.length) { usage(); return; }
1163
1454
  const opts = {};
1164
1455
  const positional = [];
1165
- const valueOpts = new Set(['--type', '--limit', '--days', '--out', '--owner', '--agent', '--threshold', '--scope', '--host', '--port', '--dir']);
1456
+ const valueOpts = new Set(['--type', '--limit', '--days', '--out', '--owner', '--agent', '--threshold', '--scope', '--host', '--port', '--dir', '--name', '--user', '--relationship', '--source', '--weight', '--budget']);
1166
1457
  for (let i = 0; i < args.length; i++) {
1167
1458
  const a = args[i];
1168
1459
  if (a === '--version' || a === '-v') { console.log(VERSION); return; }
@@ -1174,6 +1465,8 @@ function main() {
1174
1465
  else if (a === '--stdio') opts.stdio = true;
1175
1466
  else if (a === '--onstart') opts.onstart = true;
1176
1467
  else if (a === '--force') opts.force = true;
1468
+ else if (a === '--verify') opts.verify = true;
1469
+ else if (a === '--no-hint') opts.noHint = true;
1177
1470
  else if (valueOpts.has(a)) {
1178
1471
  const v = args[++i];
1179
1472
  if (a === '--type') opts.type = v;
@@ -1187,6 +1480,12 @@ function main() {
1187
1480
  else if (a === '--host') opts.host = v;
1188
1481
  else if (a === '--port') opts.port = parseInt(v, 10) || 8787;
1189
1482
  else if (a === '--dir') opts.dir = v;
1483
+ else if (a === '--name') opts.name = v;
1484
+ else if (a === '--user') opts.user = v;
1485
+ else if (a === '--relationship') opts.relationship = v;
1486
+ else if (a === '--source') opts.source = v;
1487
+ else if (a === '--weight') opts.weight = parseFloat(v);
1488
+ else if (a === '--budget') opts.budget = parseInt(v, 10) || 0;
1190
1489
  } else if (a.startsWith('--')) {
1191
1490
  console.error('未知选项: ' + a);
1192
1491
  process.exit(2);
@@ -1212,6 +1511,8 @@ function main() {
1212
1511
  case 'forget': cmdForget(rest[0]); break;
1213
1512
  case 'archive': cmdArchive(opts); break;
1214
1513
  case 'reindex': cmdReindex(); break;
1514
+ case 'profile': cmdProfile(opts); break;
1515
+ case 'context': cmdContext(opts); break;
1215
1516
  case 'export': cmdExport(opts.out); break;
1216
1517
  case 'import': cmdImport(rest[0]); break;
1217
1518
  case 'token': {
@@ -1246,6 +1547,9 @@ module.exports = {
1246
1547
  projectRoot: projectRoot,
1247
1548
  rememberCore: rememberCore,
1248
1549
  recallCore: recallCore,
1550
+ profileCore: profileCore,
1551
+ contextCore: contextCore,
1552
+ importanceScore: importanceScore,
1249
1553
  forgetCore: forgetCore,
1250
1554
  archiveCore: archiveCore,
1251
1555
  mcpTools: mcpTools,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yottameta/yotta-memory",
3
- "version": "0.5.4",
3
+ "version": "0.6.1",
4
4
  "description": "元忆 —— 有权限边界的文件式智能体记忆。文件式、零依赖、可 diff/回滚;FACT/PREF/BOUND/COMMIT 四分类(公共共享/私密隔离),用户级+项目级双级存储。",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -1,4 +1,4 @@
1
- # yotta-memory 协议规范 v0.5.4
1
+ # yotta-memory 协议规范 v0.6.1
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
- │ └── commits/ # COMMIT 承诺(该 owner 私密)
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. 文件格式
@@ -73,6 +74,8 @@ immutable: false
73
74
  | `immutable` | 否 | `true` 时 archive 不移动(用户级硬事实)|
74
75
  | `scope` | 否 | `public` / `private`,默认按类型(FACT=public,其余 private)|
75
76
  | `owner` | 否 | 归属 agent id,默认空;private+owner 非空默认仅供该 owner agent 读取,其它 agent 越界读需 grant 授权、identity=user 或 `--unsafe` |
77
+ | `source` | 否 | 记录来源(如 `对话` / `推断`),`remember --source` 写入 |
78
+ | `weight` | 否 | 重要性权重(默认 1.0,>1 提权 / <1 降权),去重时取 max |
76
79
  | `access_count` | 否 | 命中次数,recall 命中展示集时 +1 |
77
80
  | `last_accessed` | 否 | 最近访问日期 `YYYY-MM-DD` |
78
81
 
@@ -111,14 +114,41 @@ immutable: false
111
114
  | 命令 | 行为 |
112
115
  |---|---|
113
116
  | `init [--project]` | 创建目录结构;默认用户级,`--project` 建项目级 |
114
- | `remember <type> <subject> <statement> [--owner <id>]` | 写入;同 subject+statement 已存在则只更新 `updated`;`--owner` 标注归属 |
117
+ | `remember <type> <subject> <statement> [--owner <id>] [--source <来源>] [--weight <0..>] [--verify] [--no-hint]` | 写入;同 subject+statement 已存在则更新 `updated` 且 `weight` 取 max;`--source` 记录来源;`--weight` 重要性权重;`--verify` 写后回读校验;`--no-hint` 关闭类型启发式提示 |
115
118
  | `recall [关键词] [--type T] [--limit N] [--agent <id>] [--owner <id>] [--all] [--unsafe]` | 索引+TF 打分匹配;读取分区过滤;越界(读其它智能体私密)默认拒绝,需 grant / identity=user / `--unsafe` 授权;项目级优先;默认 50 条 |
116
119
  | `forget <文件>` | 删除(按路径或文件名)|
117
120
  | `archive [--days 180] [--threshold 0.4]` | 按盖棺分+年龄移入 `.archive/`(`vitality < threshold` 且超过 N 天)|
118
121
  | `reindex` | 全量扫描重建 `index.json`(手动改 .md 后校正)|
119
122
  | `export [--out f.json]` | 导出全部记忆为 JSON |
120
- | `import <f.json>` | 从 JSON 导入(幂等)|
123
+ | `import <f.json>` | 从 JSON 导入(幂等)|
124
+ | `profile [--owner <id>]` | 用户画像聚合(零推断,写 `private/<owner>/profile.md`;跨 owner 默认拒绝)|
125
+ | `context [--limit N] [--owner <id>] [--budget N]` | 开工上下文包(stdout:身份 + 多智能体铁律 + 画像 + 近期记忆 + 边界 + 承诺;`--budget` 近期记忆字符预算)|
126
+ | `iam <id> [--name] [--user] [--relationship] [--force]` | 登记身份 + 自我档案(可选扩展显示名 / 用户 / 关系)|
121
127
 
128
+
129
+ ### profile(用户画像聚合,v0.6.0)
130
+
131
+ - `profile [--owner <id>]`:扫描 `private/<owner>/{prefs,bounds,commits}`,按 type + subject + tags 归组,输出各条 statement 原文 + confidence + 最近访问,写 `private/<owner>/profile.md`。
132
+ - 零推断:引擎只做结构化归组与原文呈现,不做语义推断;画像结论由承载 AI 依据 SKILL「记忆守则」内部形成。
133
+ - 权限:profile.md 属私密区(按 owner);跨 owner 生成默认拒绝(exit 3),需 `--owner user` / `--unsafe` / grant 授权。
134
+ - profile.md 为生成物,可随时重新生成;不进入 index.json。
135
+
136
+ ### context(开工上下文包,v0.6.0)
137
+
138
+ - `context [--limit N] [--owner <id>] [--budget N]`:stdout 输出开工上下文包(不落盘),含多智能体接入铁律段(可读 A/B/C、可写范围、违规红线)与五段:
139
+ 1. 身份:agent_id / agent_name / user_name / relationship / host / memory_home(读自我档案)
140
+ 2. 用户画像摘要:profile.md(不存在则自动生成一次或降级跳过)
141
+ 3. 近期记忆:按 importance(confidence × recency + updated + weight + immutable)排序前 N 条(默认 10),读取分区过滤;`--budget` 时按剩余字符预算逐条放行(身份/铁律/画像/边界/承诺 必保)
142
+ 4. 边界提醒:BOUND 全列(可读范围内)
143
+ 5. 承诺 / 锚点:COMMIT 全列(可读范围内)
144
+
145
+ ### remember / iam 扩展(v0.6.0)
146
+
147
+ - `remember ... --verify`:写后自动回读校验(recall 命中刚写入条目),输出「已写回读 OK」。
148
+ - `remember ... --no-hint`:关闭类型启发式提示。默认开启:statement 含主观/关系词(用户 / 偏好 / 喜欢 / 关系 / 称呼 等)且 type=FACT 时 stdout 提示「疑似用户偏好 / 关系,建议 PREF」,仅提示不拦截。
149
+ - `remember ... --source <来源>`:记录来源(如 `对话` / `推断` / `交接`)。
150
+ - `remember ... --weight <0..>`:重要性权重(默认 1.0,>1 提权 / <1 降权);去重更新时 `weight` 取 max(多次印证自动提权,与灵魂盘 record.py 一致)。
151
+ - `iam <id> [--name <显示名>] [--user <用户名>] [--relationship <关系>] [--force]`:自我档案 PREF 扩展 `agent_name / user_name / relationship`;不带新参数行为与 0.4.2 一致。
122
152
  ### 去重与更新
123
153
 
124
154
  - remember 时若同 `type + subject + statement` 已存在,仅刷新 `updated`,不产生重复文件。