aizo-node 0.4.0 → 1.0.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 CHANGED
@@ -1,5 +1,7 @@
1
1
  # aizo 爱憎
2
2
 
3
+ ![aizo — preference memory for AI agents](assets/aizo-hero.png)
4
+
3
5
  [中文文档](README.zh.md)
4
6
 
5
7
  **aizo** (爱憎, *ài zēng*, "love and hate") is a lightweight, high-performance preference memory system for AI agents, built entirely in Rust.
@@ -10,62 +12,58 @@ It mimics human cognitive memory: rather than storing full conversation transcri
10
12
 
11
13
  ## How it fits together
12
14
 
13
- aizo is designed for two complementary usage loops:
15
+ aizo is designed around two complementary patterns:
14
16
 
15
17
  ```
16
18
  ╔══════════════════════════════════════════════════════════════════════╗
17
19
  ║ 1. In-session (reactive — detects specific emotions in real time) ║
18
20
  ╚══════════════════════════════════════════════════════════════════════╝
19
21
 
20
- user ──► Claude Code ─────── aizo add ──────────────────┐
21
-
22
- CLAUDE.md ◄── contributes ── local SQLite
23
- (user preference)
22
+ user ──► agent ─────── aizo add ──────────────────┐
23
+
24
+ CLAUDE.md ◄── contributes ── local SQLite
25
+ (user preference)
24
26
 
25
27
 
26
28
  ╔══════════════════════════════════════════════════════════════════════╗
27
- ║ 2. Background (cron task batch-analyzes accumulated sessions)
29
+ ║ 2. Just-in-time recall (on-demandpulls relevant prefs per task)
28
30
  ╚══════════════════════════════════════════════════════════════════════╝
29
31
 
30
- user ──► openclaw ──► sessions ─── aizo analyze ─────────┐
31
-
32
- USER.md, SOUL.md, IDENTITY.md … ◄── contributes ── local SQLite
33
- (user preference)
32
+ agent gets task ──► aizo recall --scenario coding ──► session context
33
+
34
+
35
+ generate with preferences
34
36
  ```
35
37
 
36
38
  **Loop 1 — In-session:** the agent detects a strong preference signal mid-conversation
37
- (praise, complaint, explicit rule) and calls `aizo add` immediately. The updated SQLite
38
- profile is then injected into `CLAUDE.md` (or equivalent context file) so the next
39
- session starts with the latest understanding of the user.
39
+ (praise, complaint, explicit rule) and calls `aizo add` immediately. Key preferences
40
+ that should persist across all sessions are written into `CLAUDE.md` or `MEMORY.md`.
40
41
 
41
- **Loop 2 — Background:** other agents (openclaw, etc.) accumulate session transcripts
42
- over time. A scheduled cron job runs `aizo analyze` to extract implicit preferences the
43
- reactive loop may have missed. The enriched profile is then written into richer identity
44
- files `USER.md`, `SOUL.md`, `IDENTITY.md` that build a persistent, evolving
45
- picture of the user across all agents and tools.
42
+ **Loop 2 — Just-in-time recall:** not all preferences belong in persistent context files.
43
+ The agent classifies each incoming task into a scenario, calls `aizo recall --scenario <X>`,
44
+ and injects only the relevant preferences into the current session context. This keeps
45
+ the agent's base context lean while giving it access to the full preference profile —
46
+ just like human working memory.
46
47
 
47
- The two loops reinforce each other: reactive writes give immediate recall accuracy;
48
- batch analysis fills in the gaps and stabilises scores over time.
48
+ For batch analysis of historical sessions to discover new preferences, see SOP 6 in
49
+ `skills/aizo-sop.md` this is a skill-level concern, not a built-in aizo command.
49
50
 
50
51
  ---
51
52
 
52
53
  ## Core design
53
54
 
54
55
  ```
55
- session transcript
56
+ agent observation (praise, complaint, rule, habit)
56
57
 
57
58
 
58
- flash LLM (claude-haiku-4-5)
59
- semantic extraction
60
-
61
- structured entries { category, item, base_score 0–10 }
62
- │ smooth merge
59
+ aizo add { item, base_score 0–10, keywords, scenarios }
60
+ smooth merge (old×0.4 + new×0.6)
63
61
 
64
62
  SQLite (~/.aizo/preferences.db)
65
63
 
66
64
 
67
65
  effective_weight = s · d(t)^α (score-modulated decay)
68
- │ keyword or top-N recall
66
+ │ keyword / score-band / scenario recall
69
67
 
70
68
  agent reads profile → personalizes response
71
69
  ```
@@ -74,19 +72,19 @@ session transcript
74
72
 
75
73
  All scoring logic lives in `src/scoring/mod.rs`. Every preference entry carries three computed fields, derived at read time from its `base_score` and `last_seen` timestamp.
76
74
 
77
- **Step 1 — Decay coefficient** $d(t)$
75
+ **Step 1 — Decay coefficient d(t)**
78
76
 
79
77
  $$d(t) = \phi + (1 - \phi) \cdot e^{-\lambda t}, \quad \lambda = \frac{\ln 2}{t_{1/2}}$$
80
78
 
81
- where $t$ is days since `last_seen`, $t_{1/2}$ is the configured half-life, and $\phi$ is the floor.
79
+ where t is days since `last_seen`, (half-life) is the configured half-life, and φ (floor) is the minimum decay.
82
80
 
83
- **Step 2 — Score-dependent exponent** $\alpha$
81
+ **Step 2 — Score-dependent exponent α**
84
82
 
85
83
  $$\alpha = \frac{10 - s}{10}$$
86
84
 
87
- Higher score → smaller $\alpha$ → decay has less effect. A score-10 preference ($\alpha = 0$) is fully decay-resistant; a score-0 entry ($\alpha = 1$) decays at full speed.
85
+ Higher score → smaller α → decay has less effect. A score-10 preference (α = 0) is fully decay-resistant; a score-0 entry (α = 1) decays at full speed.
88
86
 
89
- **Step 3 — Effective weight** $w$
87
+ **Step 3 — Effective weight w**
90
88
 
91
89
  $$w = s \cdot d(t)^{\alpha}$$
92
90
 
@@ -96,25 +94,15 @@ $$\boxed{w = s \cdot \left[\phi + (1-\phi) \cdot e^{-\lambda t}\right]^{\frac{10
96
94
 
97
95
  **Boundary behaviour**
98
96
 
99
- | Score $s$ | $\alpha$ | Decay effect | Interpretation |
97
+ | Score s | α | Decay effect | Interpretation |
100
98
  |---|---|---|---|
101
- | 10 | 0.0 | None — $d^0 = 1$ | Core value, never fades |
99
+ | 10 | 0.0 | None (d = 1) | Core value, never fades |
102
100
  | 7 | 0.3 | Slight | Strong preference, slow fade |
103
101
  | 5 | 0.5 | Moderate | Neutral habit, fades at half speed |
104
102
  | 1 | 0.9 | Near-full | Weak aversion, fades quickly |
105
- | 0 | 1.0 | Full | $w = 0$ always absolute zero |
106
-
107
- Entries are **never hard-deleted by decay** — they sink toward the floor and persist as weak long-term memory. Use `--type taboo` to surface them explicitly regardless of effective weight.
103
+ | 0 | 1.0 | Full (w = 0) | Absolute zero |
108
104
 
109
- ### Scoring scale (0–10)
110
-
111
- | Score | Meaning |
112
- |---|---|
113
- | 0 | Absolute taboo / hard rejection |
114
- | 1–3 | Clear dislike / aversion |
115
- | 4–6 | Neutral tendency / weak pattern |
116
- | 7–9 | Clear preference |
117
- | 10 | Strong, consistent, high-priority love |
105
+ Entries are **never hard-deleted by decay** — they sink toward the floor and persist as weak long-term memory. Use `aizo recall --type taboo` to surface hard limits regardless of effective weight.
118
106
 
119
107
  ### Score smoothing
120
108
 
@@ -128,19 +116,33 @@ new_base_score = old_base_score × 0.4 + incoming_score × 0.6
128
116
 
129
117
  ## Installation
130
118
 
131
- ### From source (Rust ≥ 1.70)
132
-
133
119
  ```bash
120
+ # Cargo (recommended)
121
+ cargo install aizo
122
+
123
+ # npm / npx
124
+ npm install -g aizo
125
+ npx aizo top 10
126
+
127
+ # From source (Rust ≥ 1.70)
134
128
  git clone https://github.com/mmmarcinho/aizo
135
- cd aizo
136
- cargo build --release
129
+ cd aizo && cargo build --release
137
130
  cp target/release/aizo /usr/local/bin/aizo
138
131
  ```
139
132
 
133
+ ### Configuration
134
+
135
+ Set env vars in `~/.aizo/.env` (user-wide) or `./.env` (per-project). Shell env always wins.
136
+
140
137
  ```bash
141
- export ANTHROPIC_API_KEY=sk-ant-... # required for 'analyze'
138
+ # Only AIZO_DB_PATH is needed for basic use (add/recall/top/show)
139
+ export AIZO_DB_PATH=~/.aizo/preferences.db
142
140
  ```
143
141
 
142
+ | Variable | Default | Description |
143
+ |---|---|---|
144
+ | `AIZO_DB_PATH` | `~/.aizo/preferences.db` | SQLite database path |
145
+
144
146
  ---
145
147
 
146
148
  ## CLI reference
@@ -151,20 +153,35 @@ aizo [--db <path>] <COMMAND>
151
153
 
152
154
  | Command | Description |
153
155
  |---|---|
154
- | `analyze [file]` | Analyze session file or JSON export with flash LLM |
155
- | `recall [query] [--type …] [--limit N] [--scenario …]` | Keyword + score-range recall **primary agent call** |
156
- | `top [N] [--type …]` | Top-N entries by effective weight (default 10) |
157
- | `show` | Full profile sorted by effective weight |
158
- | `add <item> <reason> [--score N]` | Manually add or update a preference |
159
- | `tag <item> <keywords…>` | Add or replace keywords on an existing entry |
160
- | `touch <item…>` | Reset decay clock without changing score |
156
+ | `recall [query]` | Keyword + score-range recall **primary agent call** |
157
+ | `top [N]` | Top-N entries by effective weight (read-only, default 10) |
158
+ | `show` | Full profile sorted by effective weight (read-only) |
159
+ | `add <item> <reason>` | Manually add or update a preference |
160
+ | `update <item>` | Update fields on an existing entry (item, reason, score, keywords, scenarios) |
161
+ | `apply <id…>` | Mark recalled entries as actually used; refreshes decay with a 12-hour cooldown |
162
+ | `touch <item…>` | Reset decay clock by item label, subject to the same 12-hour cooldown |
161
163
  | `remove <item…>` | Hard-remove an entry |
162
164
  | `keywords` | List all stored keywords with entry counts |
163
- | `clear` | Wipe entire profile and session history |
164
- | `info` | DB path, score distribution, decay settings |
165
- | `config show` | Print decay configuration |
166
- | `config set-half-life <days>` | Set decay half-life |
167
- | `config set-floor <0.0–1.0>` | Set minimum decay floor |
165
+ | `scenarios` | List all scenarios with entry counts and configured keywords |
166
+ | `clear` | Wipe entire preference profile |
167
+ | `info` | DB path, score distribution, env config, decay settings |
168
+ | `config show/set-half-life/set-floor` | Get or set decay parameters |
169
+
170
+ **`recall` flags:**
171
+
172
+ | Flag | Description |
173
+ |---|---|
174
+ | `--type/-t <types>` | Score-range filter, comma-separated: `preference`, `style`, `habit`, `aversion`, `taboo` |
175
+ | `--limit/-l <N>` | Cap results after sorting by effective weight |
176
+ | `--scenario <name>` | Scenario-tagged recall + keyword expansion from `~/.aizo/scenarios.yaml` |
177
+ | `--min-score <N>` | Minimum `base_score` threshold (0.0–10.0); clamps band lower bounds |
178
+ | `--touch` | Refresh matched entries, subject to the 12-hour cooldown; recall is read-only by default |
179
+ | `--no-touch` | Deprecated no-op kept for older scripts |
180
+ | `--json` | Output raw JSON instead of human-readable text |
181
+
182
+ **`top` flags:** `--type/-t`, `--scenario`, `--json`. Read-only — never touches `last_seen`.
183
+
184
+ **`show` flags:** `--json` only. Read-only — never touches `last_seen`.
168
185
 
169
186
  ### Score guide
170
187
 
@@ -186,15 +203,11 @@ aizo recall --type taboo # all hard limits, no keyword needed
186
203
  aizo top 5 --type preference
187
204
  ```
188
205
 
189
- Use keywords (`--keywords` on add, or `aizo tag`) to add any taxonomy you want.
206
+ Use keywords (`--keywords` on add, or `aizo update --keywords`) to add any taxonomy you want.
190
207
 
191
208
  ### Examples
192
209
 
193
210
  ```bash
194
- # Analyze a session log
195
- aizo analyze ./chat.txt
196
- cat conversation.md | aizo analyze
197
-
198
211
  # Agent recalls preferences before generating
199
212
  aizo top 5
200
213
  aizo recall "code style"
@@ -202,24 +215,30 @@ aizo recall "code style"
202
215
  # Scenario-aware recall for coding tasks (expands to ~10 coding keywords)
203
216
  aizo recall --scenario coding --type preference,style,habit,taboo --limit 20
204
217
 
218
+ # Mark only the preferences the agent actually used
219
+ aizo apply 3 8 12
220
+
205
221
  # Type-only recall (no keyword — returns all entries in that score range)
206
222
  aizo recall --type taboo # all hard limits
207
223
  aizo recall code --type preference --limit 10 # top coding preferences
208
224
  aizo recall code --type preference,habit --limit 20 # multiple types
209
225
 
210
- # Inspect full profile
226
+ # Custom minimum score threshold
227
+ aizo recall --scenario coding --min-score 5.0 --limit 20
228
+
229
+ # Inspect full profile or top-N
211
230
  aizo show
231
+ aizo top 20 --scenario coding --json
212
232
 
213
233
  # Manual entries — score encodes sentiment
214
234
  aizo add "concise code" "Always asks for shorter implementations" --score 9.0
215
235
  aizo add "verbose comments" "Complained about over-documented code" --score 1.5
216
236
  aizo add "emojis in output" "Explicitly said never use emojis" --score 0.5
217
- aizo add "uses dark mode" "Mentioned dark theme in every UI session" --score 5.0
218
- aizo add "terse naming" "Consistently chose short variable names" --score 8.0
237
+ aizo add "uses dark mode" "Mentioned dark theme in every UI session" --score 5.0 --scenarios coding
219
238
 
220
- # Add or manage keywords for richer recall
221
- aizo tag "concise code" brevity minimal short lean
222
- aizo tag "verbose comments" verbosity docs comments over-engineering
239
+ # Update an existing entry
240
+ aizo update "concise code" --score 8.5 --keywords brevity,minimal,short
241
+ aizo update "verbose comments" --scenarios coding,writing
223
242
 
224
243
  # Tune decay (default: half-life 30d, floor 0.1)
225
244
  aizo config set-half-life 14
@@ -262,7 +281,8 @@ CREATE TABLE preferences (
262
281
  base_score REAL NOT NULL DEFAULT 5.0, -- 0-10
263
282
  source TEXT NOT NULL DEFAULT 'manual',
264
283
  added_at TEXT NOT NULL,
265
- last_seen TEXT NOT NULL -- resets decay clock on each reinforcement
284
+ last_seen TEXT NOT NULL, -- resets decay clock on each reinforcement
285
+ touch_count INTEGER NOT NULL DEFAULT 0
266
286
  );
267
287
  -- UNIQUE on LOWER(item)
268
288
 
@@ -271,12 +291,6 @@ CREATE TABLE decay_config (
271
291
  half_life_days REAL NOT NULL DEFAULT 30.0,
272
292
  floor REAL NOT NULL DEFAULT 0.1
273
293
  );
274
-
275
- CREATE TABLE sessions (
276
- id INTEGER PRIMARY KEY AUTOINCREMENT,
277
- analyzed_at TEXT NOT NULL,
278
- extracted INTEGER NOT NULL DEFAULT 0
279
- );
280
294
  ```
281
295
 
282
296
  ---
@@ -288,15 +302,31 @@ Any agent can call aizo as a subprocess — no embedding, no vector index, no ru
288
302
  ```python
289
303
  import subprocess, json
290
304
 
305
+ def recall_scenario(scenario: str, min_score: float = 3.0) -> list[dict]:
306
+ """Just-in-time recall for a specific task scenario."""
307
+ return json.loads(subprocess.check_output(
308
+ ["aizo", "recall", "--scenario", scenario,
309
+ "--type", "preference,style,habit,taboo",
310
+ "--min-score", str(min_score), "--limit", "20", "--json"]
311
+ ))
312
+
291
313
  def top_preferences(n: int = 10) -> list[dict]:
292
314
  return json.loads(subprocess.check_output(["aizo", "top", str(n), "--json"]))
293
315
 
294
- def recall(query: str) -> list[dict]:
295
- return json.loads(subprocess.check_output(["aizo", "recall", query, "--json"]))
316
+ def apply_preferences(ids: list[int]) -> None:
317
+ """Call after the agent actually used specific recalled preferences."""
318
+ if ids:
319
+ subprocess.check_call(["aizo", "apply", *map(str, ids)])
320
+
321
+ # Just-in-time: before coding, recall coding-specific preferences
322
+ coding_prefs = recall_scenario("coding")
323
+ # Inject into session context — don't write to disk
324
+ context = f"[Coding preferences]\n{json.dumps(coding_prefs, indent=2)}"
325
+ # After generation, apply only the ids that shaped the response.
326
+ apply_preferences([p["id"] for p in coding_prefs[:3]])
296
327
 
297
- # Inject into system prompt before generating
298
- prefs = top_preferences(10)
299
- system = f"User preferences:\n{json.dumps(prefs, indent=2)}\n\n{base_system}"
328
+ # Before writing a document, recall writing preferences
329
+ writing_prefs = recall_scenario("writing")
300
330
  ```
301
331
 
302
332
  Or configure `AIZO_DB_PATH` per-project to maintain separate profiles:
@@ -306,6 +336,61 @@ export AIZO_DB_PATH=./project-prefs.db
306
336
  aizo show
307
337
  ```
308
338
 
339
+ ### Just-in-time scenario recall
340
+
341
+ Not all preferences belong in persistent context files like `CLAUDE.md` or `MEMORY.md` —
342
+ those files would grow unboundedly. Instead, use **scenario-based recall** to pull relevant
343
+ preferences on demand, right when the agent receives a task:
344
+
345
+ ```
346
+ agent receives task ──► classify into scenario ──► aizo recall --scenario <X>
347
+
348
+
349
+ inject results into session context
350
+
351
+
352
+ generate response with preferences applied
353
+
354
+
355
+ aizo apply <used ids>
356
+ ```
357
+
358
+ This keeps the agent's base context lean while giving it access to the full preference
359
+ profile. The pattern works like human memory: you don't pre-load every preference into
360
+ working memory — you recall the relevant ones when the situation calls for it.
361
+
362
+ **Example flow (coding task):**
363
+
364
+ ```bash
365
+ # Agent classifies the user's request as a coding task, then recalls candidates:
366
+ aizo recall --scenario coding --type preference,style,habit,taboo --min-score 3.0 --limit 20 --json
367
+
368
+ # After generating, mark only the entries that were actually used:
369
+ aizo apply 3 8 12
370
+ ```
371
+
372
+ **Example flow (writing task):**
373
+
374
+ ```bash
375
+ # Agent classifies the user's request as a writing task, then:
376
+ aizo recall --scenario writing --type preference,style,taboo --limit 15 --json
377
+ ```
378
+
379
+ **Creating scenario-specific entries.** When the user expresses a preference that only
380
+ applies to a certain context, tag it with that scenario so it surfaces only for relevant
381
+ tasks:
382
+
383
+ ```bash
384
+ aizo add "no emojis in code" "Rejected emoji in a PR comment" --score 1.5
385
+ aizo update "no emojis in code" --scenarios coding,review
386
+
387
+ aizo add "use active voice" "Praised direct, active-voice writing" --score 8.5
388
+ aizo update "use active voice" --scenarios writing
389
+ ```
390
+
391
+ The agent then only sees "no emojis in code" when coding — not when writing casual messages.
392
+ This scoped recall prevents preference leakage across unrelated task domains.
393
+
309
394
  ---
310
395
 
311
396
  ## Standard Operating Procedure (SOP)
@@ -315,7 +400,7 @@ The SOP for how an agent should use aizo is defined as a skill file at
315
400
  (e.g. `.claude/skills/` for Claude Code) and any agent in that project will
316
401
  automatically follow the protocol.
317
402
 
318
- The skill defines six triggers:
403
+ The skill defines seven triggers:
319
404
 
320
405
  | # | Trigger | aizo call | Timing |
321
406
  |---|---|---|---|
@@ -323,15 +408,15 @@ The skill defines six triggers:
323
408
  | 2 | User shows negative feedback | `aizo add … --score 1.5` then `aizo recall <topic>` | Sync, before corrected reply |
324
409
  | 3 | User praises something | `aizo add … --score 9.0` | Async, after reply sent |
325
410
  | 4 | User states an explicit rule | `aizo add … --score 0.5` or `--score 10` | Sync, immediate |
326
- | 5 | About to generate on topic X | `aizo recall <X>` | Sync, before generation |
327
- | 6 | Session ends | `aizo analyze <transcript>` | Async, background |
328
- | 7 | Daily cron job | Agent LLM scans logs → `aizo touch` confirmed items | Scheduled, background |
411
+ | 5 | About to generate on topic X | Classify task → `aizo recall --scenario <X> --min-score 3.0` → inject → `aizo apply <used ids>` | Sync recall before generation, apply after |
412
+ | 6 | Historical batch analysis | Agent LLM scans past sessions → `aizo add` new + `aizo apply`/`touch` confirmed | Scheduled, background |
413
+ | 7 | Daily cron job | Agent LLM scans logs → `aizo apply`/`touch` confirmed items | Scheduled, background |
329
414
 
330
415
  **Key rules encoded in the skill:**
331
416
  - Taboos always win over preferences in conflicts
332
- - `analyze` is for full sessions, not single messages — it calls an LLM
333
417
  - Silence (`recall` returning nothing) means no data, not neutral preference
334
418
  - Never mention aizo to the user — it runs silently
419
+ - Use scenario recall for just-in-time context; don't dump everything into CLAUDE.md
335
420
 
336
421
  ---
337
422
 
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aizo-node",
3
- "version": "0.4.0",
3
+ "version": "1.0.0",
4
4
  "description": "爱憎 — preference memory for AI agents: extract, decay, and recall user likes and dislikes",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -21,6 +21,7 @@
21
21
  "files": [
22
22
  "install.js",
23
23
  "run.js",
24
- "README.md"
24
+ "README.md",
25
+ "assets"
25
26
  ]
26
27
  }