htm 0.0.1 → 0.0.2
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.
- checksums.yaml +4 -4
- data/.envrc +1 -0
- data/.tbls.yml +30 -0
- data/CHANGELOG.md +30 -0
- data/SETUP.md +132 -101
- data/db/migrate/20250125000001_add_content_hash_to_nodes.rb +14 -0
- data/db/migrate/20250125000002_create_robot_nodes.rb +35 -0
- data/db/migrate/20250125000003_remove_source_and_robot_id_from_nodes.rb +28 -0
- data/db/migrate/20250126000001_create_working_memories.rb +19 -0
- data/db/migrate/20250126000002_remove_unused_columns.rb +12 -0
- data/db/schema.sql +226 -43
- data/docs/api/database.md +20 -232
- data/docs/api/embedding-service.md +1 -7
- data/docs/api/htm.md +195 -449
- data/docs/api/index.md +1 -7
- data/docs/api/long-term-memory.md +342 -590
- data/docs/architecture/adrs/001-postgresql-timescaledb.md +1 -1
- data/docs/architecture/adrs/003-ollama-embeddings.md +1 -1
- data/docs/architecture/adrs/010-redis-working-memory-rejected.md +2 -27
- data/docs/architecture/adrs/index.md +2 -13
- data/docs/architecture/hive-mind.md +165 -166
- data/docs/architecture/index.md +2 -2
- data/docs/architecture/overview.md +5 -171
- data/docs/architecture/two-tier-memory.md +1 -35
- data/docs/assets/images/adr-010-current-architecture.svg +37 -0
- data/docs/assets/images/adr-010-proposed-architecture.svg +48 -0
- data/docs/assets/images/adr-dependency-tree.svg +93 -0
- data/docs/assets/images/class-hierarchy.svg +55 -0
- data/docs/assets/images/exception-hierarchy.svg +45 -0
- data/docs/assets/images/htm-architecture-overview.svg +83 -0
- data/docs/assets/images/htm-complete-memory-flow.svg +160 -0
- data/docs/assets/images/htm-context-assembly-flow.svg +148 -0
- data/docs/assets/images/htm-eviction-process.svg +141 -0
- data/docs/assets/images/htm-memory-addition-flow.svg +138 -0
- data/docs/assets/images/htm-memory-recall-flow.svg +152 -0
- data/docs/assets/images/htm-node-states.svg +123 -0
- data/docs/assets/images/project-structure.svg +78 -0
- data/docs/assets/images/test-directory-structure.svg +38 -0
- data/{dbdoc → docs/database}/README.md +5 -3
- data/{dbdoc → docs/database}/public.node_tags.md +4 -5
- data/docs/database/public.node_tags.svg +106 -0
- data/{dbdoc → docs/database}/public.nodes.md +3 -8
- data/docs/database/public.nodes.svg +152 -0
- data/docs/database/public.robot_nodes.md +44 -0
- data/docs/database/public.robot_nodes.svg +121 -0
- data/{dbdoc → docs/database}/public.robots.md +1 -2
- data/docs/database/public.robots.svg +106 -0
- data/docs/database/public.working_memories.md +40 -0
- data/docs/database/public.working_memories.svg +112 -0
- data/{dbdoc → docs/database}/schema.json +342 -110
- data/docs/database/schema.svg +223 -0
- data/docs/development/index.md +1 -29
- data/docs/development/schema.md +84 -324
- data/docs/development/testing.md +1 -9
- data/docs/getting-started/index.md +47 -0
- data/docs/{installation.md → getting-started/installation.md} +2 -2
- data/docs/{quick-start.md → getting-started/quick-start.md} +5 -5
- data/docs/guides/adding-memories.md +221 -655
- data/docs/guides/search-strategies.md +85 -51
- data/docs/images/htm-er-diagram.svg +156 -0
- data/docs/index.md +16 -31
- data/docs/multi_framework_support.md +4 -4
- data/examples/basic_usage.rb +18 -16
- data/examples/cli_app/htm_cli.rb +86 -8
- data/examples/custom_llm_configuration.rb +1 -2
- data/examples/example_app/app.rb +11 -14
- data/examples/sinatra_app/Gemfile +1 -0
- data/examples/sinatra_app/Gemfile.lock +166 -0
- data/examples/sinatra_app/app.rb +219 -24
- data/lib/htm/active_record_config.rb +10 -3
- data/lib/htm/configuration.rb +265 -78
- data/lib/htm/{sinatra.rb → integrations/sinatra.rb} +87 -12
- data/lib/htm/job_adapter.rb +10 -3
- data/lib/htm/long_term_memory.rb +220 -57
- data/lib/htm/models/node.rb +36 -7
- data/lib/htm/models/robot.rb +30 -4
- data/lib/htm/models/robot_node.rb +50 -0
- data/lib/htm/models/tag.rb +52 -0
- data/lib/htm/models/working_memory_entry.rb +88 -0
- data/lib/htm/tasks.rb +4 -0
- data/lib/htm/version.rb +1 -1
- data/lib/htm.rb +34 -13
- data/lib/tasks/htm.rake +32 -1
- data/lib/tasks/jobs.rake +7 -3
- data/lib/tasks/tags.rake +34 -0
- data/mkdocs.yml +56 -9
- metadata +61 -31
- data/dbdoc/public.node_tags.svg +0 -112
- data/dbdoc/public.nodes.svg +0 -118
- data/dbdoc/public.robots.svg +0 -90
- data/dbdoc/schema.svg +0 -154
- /data/{dbdoc → docs/database}/public.node_stats.md +0 -0
- /data/{dbdoc → docs/database}/public.node_stats.svg +0 -0
- /data/{dbdoc → docs/database}/public.nodes_tags.md +0 -0
- /data/{dbdoc → docs/database}/public.nodes_tags.svg +0 -0
- /data/{dbdoc → docs/database}/public.ontology_structure.md +0 -0
- /data/{dbdoc → docs/database}/public.ontology_structure.svg +0 -0
- /data/{dbdoc → docs/database}/public.operations_log.md +0 -0
- /data/{dbdoc → docs/database}/public.operations_log.svg +0 -0
- /data/{dbdoc → docs/database}/public.relationships.md +0 -0
- /data/{dbdoc → docs/database}/public.relationships.svg +0 -0
- /data/{dbdoc → docs/database}/public.robot_activity.md +0 -0
- /data/{dbdoc → docs/database}/public.robot_activity.svg +0 -0
- /data/{dbdoc → docs/database}/public.schema_migrations.md +0 -0
- /data/{dbdoc → docs/database}/public.schema_migrations.svg +0 -0
- /data/{dbdoc → docs/database}/public.tags.md +0 -0
- /data/{dbdoc → docs/database}/public.tags.svg +0 -0
- /data/{dbdoc → docs/database}/public.topic_relationships.md +0 -0
- /data/{dbdoc → docs/database}/public.topic_relationships.svg +0 -0
|
@@ -4,744 +4,344 @@ This guide covers everything you need to know about storing information in HTM e
|
|
|
4
4
|
|
|
5
5
|
## Basic Usage
|
|
6
6
|
|
|
7
|
-
The primary method for adding memories is `
|
|
7
|
+
The primary method for adding memories is `remember`:
|
|
8
8
|
|
|
9
9
|
```ruby
|
|
10
|
-
node_id = htm.
|
|
11
|
-
key, # Unique identifier
|
|
12
|
-
value, # Content (string)
|
|
13
|
-
type: :fact, # Memory type
|
|
14
|
-
category: nil, # Optional category
|
|
15
|
-
importance: 1.0, # Importance score (0.0-10.0)
|
|
16
|
-
related_to: [], # Array of related node keys
|
|
17
|
-
tags: [] # Array of tags
|
|
18
|
-
)
|
|
10
|
+
node_id = htm.remember(content, tags: [])
|
|
19
11
|
```
|
|
20
12
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
## Memory Types Deep Dive
|
|
24
|
-
|
|
25
|
-
HTM supports six memory types, each optimized for specific use cases.
|
|
13
|
+
**Parameters:**
|
|
26
14
|
|
|
27
|
-
|
|
15
|
+
| Parameter | Type | Default | Description |
|
|
16
|
+
|-----------|------|---------|-------------|
|
|
17
|
+
| `content` | String | *required* | The information to remember |
|
|
18
|
+
| `tags` | Array\<String\> | `[]` | Manual tags to assign (in addition to auto-extracted tags) |
|
|
28
19
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
```ruby
|
|
32
|
-
# User information
|
|
33
|
-
htm.add_node(
|
|
34
|
-
"user_name",
|
|
35
|
-
"The user's name is Alice Thompson",
|
|
36
|
-
type: :fact,
|
|
37
|
-
importance: 9.0,
|
|
38
|
-
tags: ["user", "identity"]
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
# System configuration
|
|
42
|
-
htm.add_node(
|
|
43
|
-
"system_timezone",
|
|
44
|
-
"System timezone is UTC",
|
|
45
|
-
type: :fact,
|
|
46
|
-
importance: 6.0,
|
|
47
|
-
tags: ["system", "config"]
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
# Domain knowledge
|
|
51
|
-
htm.add_node(
|
|
52
|
-
"fact_photosynthesis",
|
|
53
|
-
"Photosynthesis converts light energy into chemical energy in plants",
|
|
54
|
-
type: :fact,
|
|
55
|
-
importance: 7.0,
|
|
56
|
-
tags: ["biology", "science"]
|
|
57
|
-
)
|
|
58
|
-
```
|
|
20
|
+
The method returns the database ID of the created node.
|
|
59
21
|
|
|
60
|
-
|
|
61
|
-
- User profile information (name, email, preferences)
|
|
62
|
-
- System configuration that rarely changes
|
|
63
|
-
- Scientific facts or domain knowledge
|
|
64
|
-
- Historical events
|
|
65
|
-
- API endpoints and credentials
|
|
22
|
+
## How Remember Works
|
|
66
23
|
|
|
67
|
-
|
|
24
|
+
When you call `remember()`:
|
|
68
25
|
|
|
69
|
-
|
|
26
|
+
1. **Content hashing**: A SHA-256 hash of the content is computed
|
|
27
|
+
2. **Deduplication check**: If a node with the same hash exists, reuse it
|
|
28
|
+
3. **Node creation/linking**: Create new node OR link robot to existing node
|
|
29
|
+
4. **Working memory**: Add node to working memory (evict if needed)
|
|
30
|
+
5. **Background jobs**: Enqueue embedding and tag generation (async)
|
|
70
31
|
|
|
71
32
|
```ruby
|
|
72
|
-
#
|
|
73
|
-
htm.
|
|
74
|
-
|
|
75
|
-
"User is asking about database performance optimization",
|
|
76
|
-
type: :context,
|
|
77
|
-
importance: 6.0,
|
|
78
|
-
tags: ["conversation", "current"]
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
# Conversation mood
|
|
82
|
-
htm.add_node(
|
|
83
|
-
"context_mood",
|
|
84
|
-
"User seems frustrated with slow query times",
|
|
85
|
-
type: :context,
|
|
86
|
-
importance: 7.0,
|
|
87
|
-
tags: ["conversation", "sentiment"]
|
|
88
|
-
)
|
|
33
|
+
# First robot remembers something
|
|
34
|
+
node_id = htm.remember("PostgreSQL supports vector similarity search")
|
|
35
|
+
# => 123 (new node created)
|
|
89
36
|
|
|
90
|
-
#
|
|
91
|
-
htm.
|
|
92
|
-
|
|
93
|
-
"Helping user optimize their PostgreSQL queries",
|
|
94
|
-
type: :context,
|
|
95
|
-
importance: 8.0,
|
|
96
|
-
tags: ["task", "active"]
|
|
97
|
-
)
|
|
37
|
+
# Same content remembered again (by same or different robot)
|
|
38
|
+
node_id = htm.remember("PostgreSQL supports vector similarity search")
|
|
39
|
+
# => 123 (same node_id returned, just updates remember_count)
|
|
98
40
|
```
|
|
99
41
|
|
|
100
|
-
|
|
101
|
-
- Current conversation topics
|
|
102
|
-
- Session state
|
|
103
|
-
- Temporary workflow status
|
|
104
|
-
- User's current goals or questions
|
|
105
|
-
- Conversation sentiment or mood
|
|
106
|
-
|
|
107
|
-
!!! note
|
|
108
|
-
Context memories are typically lower importance (4-6) since they become outdated quickly. They'll naturally get evicted from working memory as new context arrives.
|
|
42
|
+
## Content Types
|
|
109
43
|
|
|
110
|
-
|
|
44
|
+
HTM doesn't enforce content types - just store meaningful text that stands alone:
|
|
111
45
|
|
|
112
|
-
|
|
46
|
+
### Facts
|
|
113
47
|
|
|
114
48
|
```ruby
|
|
115
|
-
#
|
|
116
|
-
htm.
|
|
117
|
-
"code_date_parser",
|
|
118
|
-
<<~CODE,
|
|
119
|
-
def parse_date(date_string)
|
|
120
|
-
Date.parse(date_string)
|
|
121
|
-
rescue ArgumentError
|
|
122
|
-
nil
|
|
123
|
-
end
|
|
124
|
-
CODE
|
|
125
|
-
type: :code,
|
|
126
|
-
importance: 6.0,
|
|
127
|
-
tags: ["ruby", "date", "parsing"]
|
|
128
|
-
)
|
|
49
|
+
# User information
|
|
50
|
+
htm.remember("The user's name is Alice Thompson")
|
|
129
51
|
|
|
130
|
-
#
|
|
131
|
-
htm.
|
|
132
|
-
"code_user_query",
|
|
133
|
-
<<~SQL,
|
|
134
|
-
SELECT u.id, u.name, COUNT(o.id) as order_count
|
|
135
|
-
FROM users u
|
|
136
|
-
LEFT JOIN orders o ON u.id = o.user_id
|
|
137
|
-
GROUP BY u.id, u.name
|
|
138
|
-
HAVING COUNT(o.id) > 10
|
|
139
|
-
SQL
|
|
140
|
-
type: :code,
|
|
141
|
-
category: "sql",
|
|
142
|
-
importance: 7.0,
|
|
143
|
-
tags: ["sql", "aggregation", "joins"]
|
|
144
|
-
)
|
|
52
|
+
# System configuration
|
|
53
|
+
htm.remember("System timezone is UTC")
|
|
145
54
|
|
|
146
|
-
#
|
|
147
|
-
htm.
|
|
148
|
-
"code_redis_config",
|
|
149
|
-
<<~YAML,
|
|
150
|
-
redis:
|
|
151
|
-
host: localhost
|
|
152
|
-
port: 6379
|
|
153
|
-
pool_size: 5
|
|
154
|
-
timeout: 2
|
|
155
|
-
YAML
|
|
156
|
-
type: :code,
|
|
157
|
-
category: "config",
|
|
158
|
-
importance: 5.0,
|
|
159
|
-
tags: ["redis", "configuration", "yaml"]
|
|
160
|
-
)
|
|
55
|
+
# Domain knowledge
|
|
56
|
+
htm.remember("Photosynthesis converts light energy into chemical energy in plants")
|
|
161
57
|
```
|
|
162
58
|
|
|
163
|
-
|
|
164
|
-
- Reusable code snippets
|
|
165
|
-
- Configuration examples
|
|
166
|
-
- SQL queries and patterns
|
|
167
|
-
- API request/response examples
|
|
168
|
-
- Algorithm implementations
|
|
169
|
-
- Regular expressions
|
|
170
|
-
|
|
171
|
-
### :preference - User Preferences
|
|
172
|
-
|
|
173
|
-
Store user preferences and settings.
|
|
59
|
+
### Preferences
|
|
174
60
|
|
|
175
61
|
```ruby
|
|
176
62
|
# Communication style
|
|
177
|
-
htm.
|
|
178
|
-
"pref_communication",
|
|
179
|
-
"User prefers concise answers with bullet points",
|
|
180
|
-
type: :preference,
|
|
181
|
-
importance: 8.0,
|
|
182
|
-
tags: ["user", "communication", "style"]
|
|
183
|
-
)
|
|
63
|
+
htm.remember("User prefers concise answers with bullet points")
|
|
184
64
|
|
|
185
65
|
# Technical preferences
|
|
186
|
-
htm.
|
|
187
|
-
"pref_language",
|
|
188
|
-
"User prefers Ruby over Python for scripting tasks",
|
|
189
|
-
type: :preference,
|
|
190
|
-
importance: 7.0,
|
|
191
|
-
tags: ["user", "programming", "language"]
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
# UI preferences
|
|
195
|
-
htm.add_node(
|
|
196
|
-
"pref_theme",
|
|
197
|
-
"User uses dark theme in their IDE",
|
|
198
|
-
type: :preference,
|
|
199
|
-
importance: 4.0,
|
|
200
|
-
tags: ["user", "ui", "theme"]
|
|
201
|
-
)
|
|
202
|
-
|
|
203
|
-
# Work preferences
|
|
204
|
-
htm.add_node(
|
|
205
|
-
"pref_working_hours",
|
|
206
|
-
"User typically codes in the morning, prefers design work in afternoon",
|
|
207
|
-
type: :preference,
|
|
208
|
-
importance: 5.0,
|
|
209
|
-
tags: ["user", "schedule", "productivity"]
|
|
210
|
-
)
|
|
66
|
+
htm.remember("User prefers Ruby over Python for scripting tasks")
|
|
211
67
|
```
|
|
212
68
|
|
|
213
|
-
|
|
214
|
-
- Communication style preferences
|
|
215
|
-
- Technical tool preferences
|
|
216
|
-
- UI/UX preferences
|
|
217
|
-
- Work habits and patterns
|
|
218
|
-
- Learning style preferences
|
|
219
|
-
|
|
220
|
-
### :decision - Architectural Decisions
|
|
221
|
-
|
|
222
|
-
Track important decisions with rationale.
|
|
69
|
+
### Decisions
|
|
223
70
|
|
|
224
71
|
```ruby
|
|
225
72
|
# Technology choice
|
|
226
|
-
htm.
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
- MongoDB (rejected: eventual consistency issues)
|
|
239
|
-
- Redis (rejected: limited persistence)
|
|
240
|
-
DECISION
|
|
241
|
-
type: :decision,
|
|
242
|
-
category: "architecture",
|
|
243
|
-
importance: 9.5,
|
|
244
|
-
tags: ["architecture", "database", "timescaledb"]
|
|
245
|
-
)
|
|
246
|
-
|
|
247
|
-
# Design pattern choice
|
|
248
|
-
htm.add_node(
|
|
249
|
-
"decision_memory_architecture",
|
|
250
|
-
<<~DECISION,
|
|
251
|
-
Decision: Implement two-tier memory (working + long-term)
|
|
252
|
-
|
|
253
|
-
Rationale:
|
|
254
|
-
- Working memory provides fast access
|
|
255
|
-
- Long-term memory ensures durability
|
|
256
|
-
- Mirrors human memory architecture
|
|
257
|
-
- Allows token-limited LLM context
|
|
258
|
-
|
|
259
|
-
Trade-offs:
|
|
260
|
-
- Added complexity in synchronization
|
|
261
|
-
- Eviction strategy needs tuning
|
|
262
|
-
DECISION
|
|
263
|
-
type: :decision,
|
|
264
|
-
category: "architecture",
|
|
265
|
-
importance: 10.0,
|
|
266
|
-
tags: ["architecture", "memory", "design-pattern"]
|
|
267
|
-
)
|
|
268
|
-
|
|
269
|
-
# Process decision
|
|
270
|
-
htm.add_node(
|
|
271
|
-
"decision_testing",
|
|
272
|
-
"Decided to use Minitest over RSpec for simplicity and speed",
|
|
273
|
-
type: :decision,
|
|
274
|
-
category: "process",
|
|
275
|
-
importance: 6.0,
|
|
276
|
-
tags: ["testing", "tools"]
|
|
277
|
-
)
|
|
73
|
+
htm.remember(<<~DECISION)
|
|
74
|
+
Decision: Use PostgreSQL with pgvector for HTM storage
|
|
75
|
+
|
|
76
|
+
Rationale:
|
|
77
|
+
- Excellent vector search via pgvector
|
|
78
|
+
- Strong consistency guarantees
|
|
79
|
+
- Mature ecosystem
|
|
80
|
+
|
|
81
|
+
Alternatives considered:
|
|
82
|
+
- MongoDB (rejected: eventual consistency issues)
|
|
83
|
+
- Redis (rejected: limited persistence)
|
|
84
|
+
DECISION
|
|
278
85
|
```
|
|
279
86
|
|
|
280
|
-
|
|
281
|
-
- Technology selections
|
|
282
|
-
- Architecture patterns
|
|
283
|
-
- API design choices
|
|
284
|
-
- Process decisions
|
|
285
|
-
- Trade-off analysis results
|
|
286
|
-
|
|
287
|
-
!!! note "Decision Template"
|
|
288
|
-
Include: what was decided, why, alternatives considered, and trade-offs. This context helps future decision-making.
|
|
289
|
-
|
|
290
|
-
### :question - Unresolved Questions
|
|
291
|
-
|
|
292
|
-
Track questions that need answering.
|
|
87
|
+
### Code Snippets
|
|
293
88
|
|
|
294
89
|
```ruby
|
|
295
|
-
#
|
|
296
|
-
htm.
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
# Design question
|
|
305
|
-
htm.add_node(
|
|
306
|
-
"question_auth",
|
|
307
|
-
"How should we handle authentication for multi-robot scenarios?",
|
|
308
|
-
type: :question,
|
|
309
|
-
importance: 8.0,
|
|
310
|
-
tags: ["security", "architecture", "open"]
|
|
311
|
-
)
|
|
312
|
-
|
|
313
|
-
# Research question
|
|
314
|
-
htm.add_node(
|
|
315
|
-
"question_embeddings",
|
|
316
|
-
"Would fine-tuning embeddings on our domain improve recall accuracy?",
|
|
317
|
-
type: :question,
|
|
318
|
-
importance: 6.0,
|
|
319
|
-
tags: ["embeddings", "research", "open"]
|
|
320
|
-
)
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
!!! tip "When to Use :question"
|
|
324
|
-
- Open technical questions
|
|
325
|
-
- Design uncertainties
|
|
326
|
-
- Research topics to investigate
|
|
327
|
-
- Feature requests to evaluate
|
|
328
|
-
- Performance questions
|
|
329
|
-
|
|
330
|
-
!!! tip "Closing Questions"
|
|
331
|
-
When a question is answered, add a related decision node and mark the question as resolved by updating its tags.
|
|
332
|
-
|
|
333
|
-
## Importance Scoring Guidelines
|
|
334
|
-
|
|
335
|
-
The importance score (0.0-10.0) determines memory retention and eviction priority.
|
|
336
|
-
|
|
337
|
-

|
|
338
|
-
|
|
339
|
-
### Scoring Framework
|
|
90
|
+
# Function example
|
|
91
|
+
htm.remember(<<~CODE)
|
|
92
|
+
def parse_date(date_string)
|
|
93
|
+
Date.parse(date_string)
|
|
94
|
+
rescue ArgumentError
|
|
95
|
+
nil
|
|
96
|
+
end
|
|
97
|
+
CODE
|
|
340
98
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
# Medium (4.0-6.9): Moderately important
|
|
351
|
-
htm.add_node("code_snippet", "Useful utility function", importance: 6.0)
|
|
352
|
-
htm.add_node("context_current", "Current conversation topic", importance: 5.0)
|
|
353
|
-
htm.add_node("preference_minor", "Prefers tabs over spaces", importance: 4.0)
|
|
354
|
-
|
|
355
|
-
# Low (1.0-3.9): Nice to have, can evict
|
|
356
|
-
htm.add_node("temp_note", "Check logs later", importance: 3.0)
|
|
357
|
-
htm.add_node("minor_context", "Mentioned weather briefly", importance: 2.0)
|
|
358
|
-
htm.add_node("throwaway", "Temporary calculation result", importance: 1.0)
|
|
99
|
+
# SQL query pattern
|
|
100
|
+
htm.remember(<<~SQL)
|
|
101
|
+
SELECT u.id, u.name, COUNT(o.id) as order_count
|
|
102
|
+
FROM users u
|
|
103
|
+
LEFT JOIN orders o ON u.id = o.user_id
|
|
104
|
+
GROUP BY u.id, u.name
|
|
105
|
+
HAVING COUNT(o.id) > 10
|
|
106
|
+
SQL
|
|
359
107
|
```
|
|
360
108
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
Typical importance ranges for each type:
|
|
364
|
-
|
|
365
|
-
| Type | Typical Range | Example |
|
|
366
|
-
|------|---------------|---------|
|
|
367
|
-
| `:fact` | 7.0-10.0 | User identity, system facts |
|
|
368
|
-
| `:decision` | 7.0-10.0 | Architecture, major choices |
|
|
369
|
-
| `:preference` | 4.0-8.0 | User preferences |
|
|
370
|
-
| `:code` | 4.0-7.0 | Code snippets, examples |
|
|
371
|
-
| `:context` | 3.0-6.0 | Conversation state |
|
|
372
|
-
| `:question` | 5.0-8.0 | Open questions |
|
|
109
|
+
## Using Tags
|
|
373
110
|
|
|
374
|
-
|
|
375
|
-
When working memory is full, HTM evicts memories with lower importance first. Set importance thoughtfully based on long-term value.
|
|
111
|
+
Tags provide hierarchical organization for your memories. HTM automatically extracts tags from content, but you can also specify manual tags.
|
|
376
112
|
|
|
377
|
-
|
|
113
|
+
### Hierarchical Tag Convention
|
|
378
114
|
|
|
379
|
-
|
|
115
|
+
Use colons to create hierarchical namespaces:
|
|
380
116
|
|
|
381
117
|
```ruby
|
|
382
|
-
#
|
|
383
|
-
htm.
|
|
384
|
-
"
|
|
385
|
-
"
|
|
386
|
-
type: :decision,
|
|
387
|
-
importance: 9.0
|
|
118
|
+
# Manual tags with hierarchy
|
|
119
|
+
htm.remember(
|
|
120
|
+
"PostgreSQL 17 adds MERGE statement improvements",
|
|
121
|
+
tags: ["database:postgresql", "database:sql", "version:17"]
|
|
388
122
|
)
|
|
389
123
|
|
|
390
|
-
#
|
|
391
|
-
|
|
392
|
-
"code_db_connection",
|
|
393
|
-
"PG.connect(ENV['DATABASE_URL'])",
|
|
394
|
-
type: :code,
|
|
395
|
-
importance: 6.0,
|
|
396
|
-
related_to: ["decision_database"]
|
|
397
|
-
)
|
|
398
|
-
|
|
399
|
-
# Add related configuration
|
|
400
|
-
htm.add_node(
|
|
401
|
-
"fact_db_config",
|
|
402
|
-
"Database uses connection pool of size 5",
|
|
403
|
-
type: :fact,
|
|
404
|
-
importance: 7.0,
|
|
405
|
-
related_to: ["decision_database", "code_db_connection"]
|
|
406
|
-
)
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
!!! tip "Relationship Patterns"
|
|
410
|
-
- Link implementation code to decisions
|
|
411
|
-
- Connect questions to related facts
|
|
412
|
-
- Link preferences to user facts
|
|
413
|
-
- Connect related decisions (e.g., database choice → ORM choice)
|
|
414
|
-
|
|
415
|
-
## Categorization with Tags
|
|
416
|
-
|
|
417
|
-
Tags enable flexible organization and retrieval:
|
|
418
|
-
|
|
419
|
-
```ruby
|
|
420
|
-
# Use multiple tags for rich categorization
|
|
421
|
-
htm.add_node(
|
|
422
|
-
"decision_api_design",
|
|
423
|
-
"RESTful API with JSON responses",
|
|
424
|
-
type: :decision,
|
|
425
|
-
importance: 8.0,
|
|
426
|
-
tags: [
|
|
427
|
-
"api", # Domain
|
|
428
|
-
"rest", # Approach
|
|
429
|
-
"architecture", # Category
|
|
430
|
-
"backend", # Layer
|
|
431
|
-
"json", # Format
|
|
432
|
-
"http" # Protocol
|
|
433
|
-
]
|
|
434
|
-
)
|
|
124
|
+
# Tags are used in hybrid search for relevance boosting
|
|
125
|
+
# A recall for "postgresql" will boost nodes with matching tags
|
|
435
126
|
```
|
|
436
127
|
|
|
437
128
|
### Tag Naming Conventions
|
|
438
129
|
|
|
439
130
|
```ruby
|
|
440
|
-
# Good: Consistent, lowercase,
|
|
441
|
-
tags: ["
|
|
131
|
+
# Good: Consistent, lowercase, hierarchical
|
|
132
|
+
tags: ["database:postgresql", "architecture:api", "security:authentication"]
|
|
442
133
|
|
|
443
|
-
# Avoid: Inconsistent casing, vague terms
|
|
444
|
-
tags: ["
|
|
134
|
+
# Avoid: Inconsistent casing, flat tags, vague terms
|
|
135
|
+
tags: ["PostgreSQL", "stuff", "misc"]
|
|
445
136
|
```
|
|
446
137
|
|
|
447
138
|
### Common Tag Patterns
|
|
448
139
|
|
|
449
140
|
```ruby
|
|
450
141
|
# Domain tags
|
|
451
|
-
tags: ["database", "api", "
|
|
142
|
+
tags: ["database:postgresql", "api:rest", "auth:jwt"]
|
|
452
143
|
|
|
453
144
|
# Layer tags
|
|
454
|
-
tags: ["frontend", "backend", "infrastructure"
|
|
455
|
-
|
|
456
|
-
# Status tags
|
|
457
|
-
tags: ["active", "deprecated", "experimental", "stable"]
|
|
145
|
+
tags: ["layer:frontend", "layer:backend", "layer:infrastructure"]
|
|
458
146
|
|
|
459
|
-
#
|
|
460
|
-
tags: ["
|
|
147
|
+
# Technology tags
|
|
148
|
+
tags: ["tech:ruby", "tech:javascript", "tech:docker"]
|
|
461
149
|
|
|
462
150
|
# Project tags
|
|
463
|
-
tags: ["project
|
|
151
|
+
tags: ["project:alpha", "project:beta"]
|
|
464
152
|
```
|
|
465
153
|
|
|
466
|
-
|
|
154
|
+
### Automatic Tag Extraction
|
|
467
155
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
Create time-series logs:
|
|
156
|
+
When a node is created, a background job (GenerateTagsJob) automatically extracts hierarchical tags from the content using an LLM. This happens asynchronously.
|
|
471
157
|
|
|
472
158
|
```ruby
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
htm.add_node(
|
|
477
|
-
"event_#{event_type}_#{timestamp}",
|
|
478
|
-
"#{event_type.upcase}: #{description}",
|
|
479
|
-
type: :context,
|
|
480
|
-
importance: 5.0,
|
|
481
|
-
tags: ["event", event_type, "log"]
|
|
482
|
-
)
|
|
483
|
-
end
|
|
484
|
-
|
|
485
|
-
log_event("error", "Database connection timeout")
|
|
486
|
-
log_event("performance", "Query took 3.2 seconds")
|
|
159
|
+
# Just provide content, tags are auto-extracted
|
|
160
|
+
htm.remember("We're using Redis for session caching with a 24-hour TTL")
|
|
161
|
+
# Background job might extract: ["database:redis", "caching:session", "performance"]
|
|
487
162
|
```
|
|
488
163
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
Track changes over time:
|
|
164
|
+
## Content Deduplication
|
|
492
165
|
|
|
493
|
-
|
|
494
|
-
def update_fact(base_key, new_value, version)
|
|
495
|
-
# Add versioned node
|
|
496
|
-
htm.add_node(
|
|
497
|
-
"#{base_key}_v#{version}",
|
|
498
|
-
new_value,
|
|
499
|
-
type: :fact,
|
|
500
|
-
importance: 8.0,
|
|
501
|
-
tags: ["versioned", "v#{version}"],
|
|
502
|
-
related_to: version > 1 ? ["#{base_key}_v#{version-1}"] : []
|
|
503
|
-
)
|
|
504
|
-
end
|
|
505
|
-
|
|
506
|
-
update_fact("user_email", "alice@example.com", 1)
|
|
507
|
-
update_fact("user_email", "alice@newdomain.com", 2)
|
|
508
|
-
```
|
|
166
|
+
HTM automatically deduplicates content across all robots using SHA-256 hashing.
|
|
509
167
|
|
|
510
|
-
###
|
|
511
|
-
|
|
512
|
-
Store structured information:
|
|
168
|
+
### How It Works
|
|
513
169
|
|
|
514
170
|
```ruby
|
|
515
|
-
#
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
"user_profile_001",
|
|
525
|
-
user_profile,
|
|
526
|
-
type: :fact,
|
|
527
|
-
importance: 9.0,
|
|
528
|
-
tags: ["user", "profile", "complete"]
|
|
529
|
-
)
|
|
171
|
+
# Robot 1 remembers something
|
|
172
|
+
robot1 = HTM.new(robot_name: "assistant_1")
|
|
173
|
+
node_id = robot1.remember("Ruby 3.3 supports YJIT by default")
|
|
174
|
+
# => 123 (new node)
|
|
175
|
+
|
|
176
|
+
# Robot 2 remembers the same thing
|
|
177
|
+
robot2 = HTM.new(robot_name: "assistant_2")
|
|
178
|
+
node_id = robot2.remember("Ruby 3.3 supports YJIT by default")
|
|
179
|
+
# => 123 (same node_id! Content matched by hash)
|
|
530
180
|
```
|
|
531
181
|
|
|
532
|
-
###
|
|
182
|
+
### Robot-Node Association
|
|
533
183
|
|
|
534
|
-
|
|
184
|
+
Each robot-node relationship is tracked in `robot_nodes`:
|
|
535
185
|
|
|
536
186
|
```ruby
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
htm.add_node(
|
|
544
|
-
key,
|
|
545
|
-
value,
|
|
546
|
-
type: type,
|
|
547
|
-
importance: importance,
|
|
548
|
-
tags: [current_project, type.to_s]
|
|
549
|
-
)
|
|
550
|
-
end
|
|
187
|
+
# Check how many times a robot has "remembered" content
|
|
188
|
+
rn = HTM::Models::RobotNode.find_by(robot_id: htm.robot_id, node_id: node_id)
|
|
189
|
+
rn.remember_count # => 3 (remembered 3 times)
|
|
190
|
+
rn.first_remembered_at # => When first encountered
|
|
191
|
+
rn.last_remembered_at # => When last tried to remember
|
|
551
192
|
```
|
|
552
193
|
|
|
553
194
|
## Best Practices
|
|
554
195
|
|
|
555
|
-
### 1.
|
|
556
|
-
|
|
557
|
-
```ruby
|
|
558
|
-
# Good: Descriptive and namespaced
|
|
559
|
-
"user_profile_alice_001"
|
|
560
|
-
"decision_database_selection"
|
|
561
|
-
"code_authentication_jwt"
|
|
562
|
-
|
|
563
|
-
# Bad: Vague or collision-prone
|
|
564
|
-
"profile"
|
|
565
|
-
"dec1"
|
|
566
|
-
"code"
|
|
567
|
-
```
|
|
568
|
-
|
|
569
|
-
### 2. Be Consistent with Categories
|
|
196
|
+
### 1. Make Content Self-Contained
|
|
570
197
|
|
|
571
198
|
```ruby
|
|
572
|
-
#
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
security: "security",
|
|
576
|
-
performance: "performance",
|
|
577
|
-
ui: "user-interface"
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
htm.add_node(
|
|
581
|
-
key, value,
|
|
582
|
-
category: CATEGORIES[:architecture]
|
|
199
|
+
# Good: Self-contained, understandable without context
|
|
200
|
+
htm.remember(
|
|
201
|
+
"Decided to use Redis for session storage because it provides fast access and automatic expiration"
|
|
583
202
|
)
|
|
203
|
+
|
|
204
|
+
# Bad: Requires external context
|
|
205
|
+
htm.remember("Use Redis") # Why? For what?
|
|
584
206
|
```
|
|
585
207
|
|
|
586
|
-
###
|
|
208
|
+
### 2. Include Rich Context
|
|
587
209
|
|
|
588
210
|
```ruby
|
|
589
|
-
# Good:
|
|
590
|
-
htm.
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
211
|
+
# Good: Includes rationale and alternatives
|
|
212
|
+
htm.remember(<<~DECISION)
|
|
213
|
+
Decision: Use OAuth 2.0 for authentication
|
|
214
|
+
|
|
215
|
+
Rationale:
|
|
216
|
+
- Industry standard
|
|
217
|
+
- Better security than basic auth
|
|
218
|
+
- Supports SSO
|
|
219
|
+
|
|
220
|
+
Alternatives considered:
|
|
221
|
+
- Basic auth (rejected: security concerns)
|
|
222
|
+
- Custom tokens (rejected: maintenance burden)
|
|
223
|
+
DECISION
|
|
602
224
|
```
|
|
603
225
|
|
|
604
|
-
###
|
|
226
|
+
### 3. Use Hierarchical Tags
|
|
605
227
|
|
|
606
228
|
```ruby
|
|
607
229
|
# Good: Rich tags for multiple retrieval paths
|
|
608
|
-
htm.
|
|
609
|
-
"
|
|
610
|
-
"
|
|
611
|
-
tags: ["api", "authentication", "security", "jwt", "middleware", "ruby"]
|
|
230
|
+
htm.remember(
|
|
231
|
+
"JWT tokens are stateless authentication tokens",
|
|
232
|
+
tags: ["auth:jwt", "security:tokens", "architecture:stateless"]
|
|
612
233
|
)
|
|
613
234
|
|
|
614
|
-
# Suboptimal:
|
|
615
|
-
htm.
|
|
616
|
-
"code_api_auth",
|
|
617
|
-
"...",
|
|
618
|
-
tags: ["code"]
|
|
619
|
-
)
|
|
235
|
+
# Suboptimal: Flat or minimal tags
|
|
236
|
+
htm.remember("JWT info", tags: ["jwt"])
|
|
620
237
|
```
|
|
621
238
|
|
|
622
|
-
###
|
|
239
|
+
### 4. Keep Content Focused
|
|
623
240
|
|
|
624
241
|
```ruby
|
|
625
|
-
#
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
htm.add_node(
|
|
629
|
-
"question_api",
|
|
630
|
-
"How to handle file uploads in GraphQL?",
|
|
631
|
-
type: :question,
|
|
632
|
-
related_to: ["decision_api"]
|
|
633
|
-
)
|
|
242
|
+
# Good: One concept per memory
|
|
243
|
+
htm.remember("PostgreSQL's EXPLAIN ANALYZE shows actual execution times")
|
|
244
|
+
htm.remember("PostgreSQL's EXPLAIN shows the query plan without executing")
|
|
634
245
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
"GraphQL upload implementation",
|
|
638
|
-
type: :code,
|
|
639
|
-
related_to: ["decision_api", "question_api"]
|
|
640
|
-
)
|
|
246
|
+
# Suboptimal: Multiple unrelated concepts
|
|
247
|
+
htm.remember("PostgreSQL has EXPLAIN and also supports JSON and has good performance")
|
|
641
248
|
```
|
|
642
249
|
|
|
643
|
-
##
|
|
644
|
-
|
|
645
|
-
### Pitfall 1: Duplicate Keys
|
|
250
|
+
## Async Processing
|
|
646
251
|
|
|
647
|
-
|
|
648
|
-
# This will fail - keys must be unique
|
|
649
|
-
htm.add_node("user_001", "Alice")
|
|
650
|
-
htm.add_node("user_001", "Bob") # Error: key already exists
|
|
651
|
-
```
|
|
252
|
+
Embedding generation and tag extraction happen asynchronously:
|
|
652
253
|
|
|
653
|
-
|
|
254
|
+
### Workflow
|
|
654
255
|
|
|
655
256
|
```ruby
|
|
656
|
-
|
|
257
|
+
# 1. Node created immediately (~15ms)
|
|
258
|
+
node_id = htm.remember("Important fact about databases")
|
|
259
|
+
# Returns immediately with node_id
|
|
657
260
|
|
|
658
|
-
|
|
659
|
-
|
|
261
|
+
# 2. Background jobs enqueue (async)
|
|
262
|
+
# - GenerateEmbeddingJob runs (~100ms)
|
|
263
|
+
# - GenerateTagsJob runs (~1 second)
|
|
264
|
+
|
|
265
|
+
# 3. Node is eventually enriched
|
|
266
|
+
# - embedding field populated (enables vector search)
|
|
267
|
+
# - tags associated (enables tag navigation and boosting)
|
|
660
268
|
```
|
|
661
269
|
|
|
662
|
-
###
|
|
270
|
+
### Immediate vs Eventual Capabilities
|
|
663
271
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
272
|
+
| Capability | Available | Notes |
|
|
273
|
+
|------------|-----------|-------|
|
|
274
|
+
| Full-text search | Immediately | Works on content |
|
|
275
|
+
| Basic retrieval | Immediately | By node ID |
|
|
276
|
+
| Vector search | After ~100ms | Needs embedding |
|
|
277
|
+
| Tag-enhanced search | After ~1s | Needs tags |
|
|
278
|
+
| Hybrid search | After ~1s | Needs embedding + tags |
|
|
668
279
|
|
|
669
|
-
|
|
280
|
+
## Working Memory Integration
|
|
670
281
|
|
|
671
|
-
|
|
282
|
+
When you `remember()`, the node is automatically added to working memory:
|
|
672
283
|
|
|
673
284
|
```ruby
|
|
674
|
-
#
|
|
675
|
-
htm.
|
|
676
|
-
|
|
677
|
-
#
|
|
678
|
-
htm.
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
type: :decision
|
|
682
|
-
)
|
|
285
|
+
# Remember adds to both LTM and WM
|
|
286
|
+
htm.remember("Important fact")
|
|
287
|
+
|
|
288
|
+
# Check working memory
|
|
289
|
+
stats = htm.working_memory.stats
|
|
290
|
+
puts "Nodes in WM: #{stats[:node_count]}"
|
|
291
|
+
puts "Token usage: #{stats[:utilization]}%"
|
|
683
292
|
```
|
|
684
293
|
|
|
685
|
-
###
|
|
294
|
+
### Eviction
|
|
295
|
+
|
|
296
|
+
If working memory is full, older/less important nodes are evicted to make room:
|
|
686
297
|
|
|
687
298
|
```ruby
|
|
688
|
-
#
|
|
689
|
-
htm.
|
|
690
|
-
|
|
691
|
-
#
|
|
692
|
-
|
|
693
|
-
"code_001",
|
|
694
|
-
"def foo...",
|
|
695
|
-
type: :code,
|
|
696
|
-
tags: ["ruby", "functions", "utilities"]
|
|
697
|
-
)
|
|
299
|
+
# Working memory has a token budget
|
|
300
|
+
htm = HTM.new(working_memory_size: 128_000) # 128K tokens
|
|
301
|
+
|
|
302
|
+
# As you remember more, older items may be evicted from WM
|
|
303
|
+
# They remain in LTM and can be recalled later
|
|
698
304
|
```
|
|
699
305
|
|
|
700
306
|
## Performance Considerations
|
|
701
307
|
|
|
702
308
|
### Batch Operations
|
|
703
309
|
|
|
704
|
-
|
|
310
|
+
Each `remember()` call is a database operation. For bulk inserts:
|
|
705
311
|
|
|
706
312
|
```ruby
|
|
707
|
-
#
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
313
|
+
# Multiple memories
|
|
314
|
+
facts = [
|
|
315
|
+
"PostgreSQL supports JSONB",
|
|
316
|
+
"PostgreSQL has excellent indexing",
|
|
317
|
+
"PostgreSQL handles concurrent writes well"
|
|
712
318
|
]
|
|
713
319
|
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
htm.add_node(m[:key], m[:value], type: m[:type], importance: m[:importance])
|
|
320
|
+
facts.each do |fact|
|
|
321
|
+
htm.remember(fact)
|
|
717
322
|
end
|
|
718
323
|
```
|
|
719
324
|
|
|
720
|
-
|
|
721
|
-
Each `add_node` call generates embeddings via Ollama. For large batches, this can take time. Consider adding in the background or showing progress.
|
|
722
|
-
|
|
723
|
-
### Embedding Generation
|
|
325
|
+
### Content Length
|
|
724
326
|
|
|
725
|
-
|
|
327
|
+
Longer content takes more time to process:
|
|
726
328
|
|
|
727
329
|
```ruby
|
|
728
|
-
# Short text: Fast (~
|
|
729
|
-
htm.
|
|
330
|
+
# Short text: Fast (~15ms save, ~100ms embedding)
|
|
331
|
+
htm.remember("User name is Alice")
|
|
730
332
|
|
|
731
|
-
# Long text: Slower (~500ms)
|
|
732
|
-
htm.
|
|
333
|
+
# Long text: Slower (~15ms save, ~500ms embedding)
|
|
334
|
+
htm.remember("..." * 1000) # 1000 chars
|
|
733
335
|
```
|
|
734
336
|
|
|
735
|
-
|
|
736
|
-
For very long content (>1000 tokens), consider splitting into multiple nodes or summarizing.
|
|
337
|
+
For very long content (>1000 tokens), consider splitting into multiple memories.
|
|
737
338
|
|
|
738
339
|
## Next Steps
|
|
739
340
|
|
|
740
341
|
Now that you know how to add memories effectively, learn about:
|
|
741
342
|
|
|
742
|
-
- [**Recalling Memories**](recalling-memories.md) - Search and retrieve memories
|
|
743
343
|
- [**Search Strategies**](search-strategies.md) - Optimize retrieval with different strategies
|
|
744
|
-
- [**
|
|
344
|
+
- [**Recalling Memories**](recalling-memories.md) - Search and retrieve memories
|
|
745
345
|
|
|
746
346
|
## Complete Example
|
|
747
347
|
|
|
@@ -750,75 +350,41 @@ require 'htm'
|
|
|
750
350
|
|
|
751
351
|
htm = HTM.new(robot_name: "Memory Demo")
|
|
752
352
|
|
|
753
|
-
# Add a fact
|
|
754
|
-
htm.
|
|
755
|
-
"
|
|
756
|
-
"Alice Thompson is a senior software engineer specializing in distributed systems",
|
|
757
|
-
type: :fact,
|
|
758
|
-
category: "user",
|
|
759
|
-
importance: 9.0,
|
|
760
|
-
tags: ["user", "profile", "engineering"]
|
|
353
|
+
# Add a fact
|
|
354
|
+
htm.remember(
|
|
355
|
+
"Alice Thompson is a senior software engineer specializing in distributed systems"
|
|
761
356
|
)
|
|
762
357
|
|
|
763
|
-
# Add a
|
|
764
|
-
htm.
|
|
765
|
-
"
|
|
766
|
-
"Alice prefers Vim for editing and tmux for terminal management",
|
|
767
|
-
type: :preference,
|
|
768
|
-
importance: 7.0,
|
|
769
|
-
tags: ["user", "tools", "preferences"],
|
|
770
|
-
related_to: ["user_profile"]
|
|
358
|
+
# Add a preference
|
|
359
|
+
htm.remember(
|
|
360
|
+
"Alice prefers Vim for editing and tmux for terminal management"
|
|
771
361
|
)
|
|
772
362
|
|
|
773
363
|
# Add a decision with context
|
|
774
|
-
htm.
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
- Kafka (overkill for our scale)
|
|
787
|
-
DECISION
|
|
788
|
-
type: :decision,
|
|
789
|
-
category: "architecture",
|
|
790
|
-
importance: 8.5,
|
|
791
|
-
tags: ["architecture", "messaging", "rabbitmq", "async"]
|
|
792
|
-
)
|
|
364
|
+
htm.remember(<<~DECISION, tags: ["architecture", "messaging"])
|
|
365
|
+
Decision: Use RabbitMQ for async job processing
|
|
366
|
+
|
|
367
|
+
Rationale:
|
|
368
|
+
- Need reliable message delivery
|
|
369
|
+
- Support for multiple consumer patterns
|
|
370
|
+
- Excellent Ruby client library
|
|
371
|
+
|
|
372
|
+
Alternatives:
|
|
373
|
+
- Redis (simpler but less reliable)
|
|
374
|
+
- Kafka (overkill for our scale)
|
|
375
|
+
DECISION
|
|
793
376
|
|
|
794
377
|
# Add implementation code
|
|
795
|
-
htm.
|
|
796
|
-
|
|
797
|
-
<<~RUBY,
|
|
798
|
-
require 'bunny'
|
|
799
|
-
|
|
800
|
-
connection = Bunny.new(ENV['RABBITMQ_URL'])
|
|
801
|
-
connection.start
|
|
802
|
-
|
|
803
|
-
channel = connection.create_channel
|
|
804
|
-
queue = channel.queue('jobs', durable: true)
|
|
805
|
-
RUBY
|
|
806
|
-
type: :code,
|
|
807
|
-
importance: 6.0,
|
|
808
|
-
tags: ["ruby", "rabbitmq", "setup", "code"],
|
|
809
|
-
related_to: ["decision_messaging"]
|
|
810
|
-
)
|
|
378
|
+
htm.remember(<<~RUBY, tags: ["code:ruby", "messaging:rabbitmq"])
|
|
379
|
+
require 'bunny'
|
|
811
380
|
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
tags: ["rabbitmq", "scaling", "performance", "open"],
|
|
819
|
-
related_to: ["decision_messaging"]
|
|
820
|
-
)
|
|
381
|
+
connection = Bunny.new(ENV['RABBITMQ_URL'])
|
|
382
|
+
connection.start
|
|
383
|
+
|
|
384
|
+
channel = connection.create_channel
|
|
385
|
+
queue = channel.queue('jobs', durable: true)
|
|
386
|
+
RUBY
|
|
821
387
|
|
|
822
|
-
puts "Added
|
|
823
|
-
puts "Stats: #{
|
|
388
|
+
puts "Added memories with relationships and rich metadata"
|
|
389
|
+
puts "Stats: #{HTM::Models::Node.count} total nodes"
|
|
824
390
|
```
|