htm 0.0.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.
- checksums.yaml +7 -0
- data/.architecture/decisions/adrs/001-use-postgresql-timescaledb-storage.md +227 -0
- data/.architecture/decisions/adrs/002-two-tier-memory-architecture.md +322 -0
- data/.architecture/decisions/adrs/003-ollama-default-embedding-provider.md +339 -0
- data/.architecture/decisions/adrs/004-multi-robot-shared-memory-hive-mind.md +374 -0
- data/.architecture/decisions/adrs/005-rag-based-retrieval-with-hybrid-search.md +443 -0
- data/.architecture/decisions/adrs/006-context-assembly-strategies.md +444 -0
- data/.architecture/decisions/adrs/007-working-memory-eviction-strategy.md +461 -0
- data/.architecture/decisions/adrs/008-robot-identification-system.md +550 -0
- data/.architecture/decisions/adrs/009-never-forget-explicit-deletion-only.md +570 -0
- data/.architecture/decisions/adrs/010-redis-working-memory-rejected.md +323 -0
- data/.architecture/decisions/adrs/011-database-side-embedding-generation-with-pgai.md +585 -0
- data/.architecture/decisions/adrs/012-llm-driven-ontology-topic-extraction.md +583 -0
- data/.architecture/decisions/adrs/013-activerecord-orm-and-many-to-many-tagging.md +299 -0
- data/.architecture/decisions/adrs/014-client-side-embedding-generation-workflow.md +569 -0
- data/.architecture/decisions/adrs/015-hierarchical-tag-ontology-and-llm-extraction.md +701 -0
- data/.architecture/decisions/adrs/016-async-embedding-and-tag-generation.md +694 -0
- data/.architecture/members.yml +144 -0
- data/.architecture/reviews/2025-10-29-llm-configuration-and-async-processing-review.md +1137 -0
- data/.architecture/reviews/initial-system-analysis.md +330 -0
- data/.envrc +32 -0
- data/.irbrc +145 -0
- data/CHANGELOG.md +150 -0
- data/COMMITS.md +196 -0
- data/LICENSE +21 -0
- data/README.md +1347 -0
- data/Rakefile +51 -0
- data/SETUP.md +268 -0
- data/config/database.yml +67 -0
- data/db/migrate/20250101000001_enable_extensions.rb +14 -0
- data/db/migrate/20250101000002_create_robots.rb +14 -0
- data/db/migrate/20250101000003_create_nodes.rb +42 -0
- data/db/migrate/20250101000005_create_tags.rb +38 -0
- data/db/migrate/20250101000007_add_node_vector_indexes.rb +30 -0
- data/db/schema.sql +473 -0
- data/db/seed_data/README.md +100 -0
- data/db/seed_data/presidents.md +136 -0
- data/db/seed_data/states.md +151 -0
- data/db/seeds.rb +208 -0
- data/dbdoc/README.md +173 -0
- data/dbdoc/public.node_stats.md +48 -0
- data/dbdoc/public.node_stats.svg +41 -0
- data/dbdoc/public.node_tags.md +40 -0
- data/dbdoc/public.node_tags.svg +112 -0
- data/dbdoc/public.nodes.md +54 -0
- data/dbdoc/public.nodes.svg +118 -0
- data/dbdoc/public.nodes_tags.md +39 -0
- data/dbdoc/public.nodes_tags.svg +112 -0
- data/dbdoc/public.ontology_structure.md +48 -0
- data/dbdoc/public.ontology_structure.svg +38 -0
- data/dbdoc/public.operations_log.md +42 -0
- data/dbdoc/public.operations_log.svg +130 -0
- data/dbdoc/public.relationships.md +39 -0
- data/dbdoc/public.relationships.svg +41 -0
- data/dbdoc/public.robot_activity.md +46 -0
- data/dbdoc/public.robot_activity.svg +35 -0
- data/dbdoc/public.robots.md +35 -0
- data/dbdoc/public.robots.svg +90 -0
- data/dbdoc/public.schema_migrations.md +29 -0
- data/dbdoc/public.schema_migrations.svg +26 -0
- data/dbdoc/public.tags.md +35 -0
- data/dbdoc/public.tags.svg +60 -0
- data/dbdoc/public.topic_relationships.md +45 -0
- data/dbdoc/public.topic_relationships.svg +32 -0
- data/dbdoc/schema.json +1437 -0
- data/dbdoc/schema.svg +154 -0
- data/docs/api/database.md +806 -0
- data/docs/api/embedding-service.md +532 -0
- data/docs/api/htm.md +797 -0
- data/docs/api/index.md +259 -0
- data/docs/api/long-term-memory.md +1096 -0
- data/docs/api/working-memory.md +665 -0
- data/docs/architecture/adrs/001-postgresql-timescaledb.md +314 -0
- data/docs/architecture/adrs/002-two-tier-memory.md +411 -0
- data/docs/architecture/adrs/003-ollama-embeddings.md +421 -0
- data/docs/architecture/adrs/004-hive-mind.md +437 -0
- data/docs/architecture/adrs/005-rag-retrieval.md +531 -0
- data/docs/architecture/adrs/006-context-assembly.md +496 -0
- data/docs/architecture/adrs/007-eviction-strategy.md +645 -0
- data/docs/architecture/adrs/008-robot-identification.md +625 -0
- data/docs/architecture/adrs/009-never-forget.md +648 -0
- data/docs/architecture/adrs/010-redis-working-memory-rejected.md +323 -0
- data/docs/architecture/adrs/011-pgai-integration.md +494 -0
- data/docs/architecture/adrs/index.md +215 -0
- data/docs/architecture/hive-mind.md +736 -0
- data/docs/architecture/index.md +351 -0
- data/docs/architecture/overview.md +538 -0
- data/docs/architecture/two-tier-memory.md +873 -0
- data/docs/assets/css/custom.css +83 -0
- data/docs/assets/images/htm-core-components.svg +63 -0
- data/docs/assets/images/htm-database-schema.svg +93 -0
- data/docs/assets/images/htm-hive-mind-architecture.svg +125 -0
- data/docs/assets/images/htm-importance-scoring-framework.svg +83 -0
- data/docs/assets/images/htm-layered-architecture.svg +71 -0
- data/docs/assets/images/htm-long-term-memory-architecture.svg +115 -0
- data/docs/assets/images/htm-working-memory-architecture.svg +120 -0
- data/docs/assets/images/htm.jpg +0 -0
- data/docs/assets/images/htm_demo.gif +0 -0
- data/docs/assets/js/mathjax.js +18 -0
- data/docs/assets/videos/htm_video.mp4 +0 -0
- data/docs/database_rake_tasks.md +322 -0
- data/docs/development/contributing.md +787 -0
- data/docs/development/index.md +336 -0
- data/docs/development/schema.md +596 -0
- data/docs/development/setup.md +719 -0
- data/docs/development/testing.md +819 -0
- data/docs/guides/adding-memories.md +824 -0
- data/docs/guides/context-assembly.md +1009 -0
- data/docs/guides/getting-started.md +577 -0
- data/docs/guides/index.md +118 -0
- data/docs/guides/long-term-memory.md +941 -0
- data/docs/guides/multi-robot.md +866 -0
- data/docs/guides/recalling-memories.md +927 -0
- data/docs/guides/search-strategies.md +953 -0
- data/docs/guides/working-memory.md +717 -0
- data/docs/index.md +214 -0
- data/docs/installation.md +477 -0
- data/docs/multi_framework_support.md +519 -0
- data/docs/quick-start.md +655 -0
- data/docs/setup_local_database.md +302 -0
- data/docs/using_rake_tasks_in_your_app.md +383 -0
- data/examples/basic_usage.rb +93 -0
- data/examples/cli_app/README.md +317 -0
- data/examples/cli_app/htm_cli.rb +270 -0
- data/examples/custom_llm_configuration.rb +183 -0
- data/examples/example_app/Rakefile +71 -0
- data/examples/example_app/app.rb +206 -0
- data/examples/sinatra_app/Gemfile +21 -0
- data/examples/sinatra_app/app.rb +335 -0
- data/lib/htm/active_record_config.rb +113 -0
- data/lib/htm/configuration.rb +342 -0
- data/lib/htm/database.rb +594 -0
- data/lib/htm/embedding_service.rb +115 -0
- data/lib/htm/errors.rb +34 -0
- data/lib/htm/job_adapter.rb +154 -0
- data/lib/htm/jobs/generate_embedding_job.rb +65 -0
- data/lib/htm/jobs/generate_tags_job.rb +82 -0
- data/lib/htm/long_term_memory.rb +965 -0
- data/lib/htm/models/node.rb +109 -0
- data/lib/htm/models/node_tag.rb +33 -0
- data/lib/htm/models/robot.rb +52 -0
- data/lib/htm/models/tag.rb +76 -0
- data/lib/htm/railtie.rb +76 -0
- data/lib/htm/sinatra.rb +157 -0
- data/lib/htm/tag_service.rb +135 -0
- data/lib/htm/tasks.rb +38 -0
- data/lib/htm/version.rb +5 -0
- data/lib/htm/working_memory.rb +182 -0
- data/lib/htm.rb +400 -0
- data/lib/tasks/db.rake +19 -0
- data/lib/tasks/htm.rake +147 -0
- data/lib/tasks/jobs.rake +312 -0
- data/mkdocs.yml +190 -0
- data/scripts/install_local_database.sh +309 -0
- metadata +341 -0
|
@@ -0,0 +1,824 @@
|
|
|
1
|
+
# Adding Memories to HTM
|
|
2
|
+
|
|
3
|
+
This guide covers everything you need to know about storing information in HTM effectively.
|
|
4
|
+
|
|
5
|
+
## Basic Usage
|
|
6
|
+
|
|
7
|
+
The primary method for adding memories is `add_node`:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
node_id = htm.add_node(
|
|
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
|
+
)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The method returns the database ID of the created node.
|
|
22
|
+
|
|
23
|
+
## Memory Types Deep Dive
|
|
24
|
+
|
|
25
|
+
HTM supports six memory types, each optimized for specific use cases.
|
|
26
|
+
|
|
27
|
+
### :fact - Immutable Facts
|
|
28
|
+
|
|
29
|
+
Facts are unchanging truths about the world, users, or systems.
|
|
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
|
+
```
|
|
59
|
+
|
|
60
|
+
!!! tip "When to Use :fact"
|
|
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
|
|
66
|
+
|
|
67
|
+
### :context - Conversation State
|
|
68
|
+
|
|
69
|
+
Context captures the current state of conversations or sessions.
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
# Current conversation topic
|
|
73
|
+
htm.add_node(
|
|
74
|
+
"context_#{session_id}_001",
|
|
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
|
+
)
|
|
89
|
+
|
|
90
|
+
# Current task
|
|
91
|
+
htm.add_node(
|
|
92
|
+
"context_task",
|
|
93
|
+
"Helping user optimize their PostgreSQL queries",
|
|
94
|
+
type: :context,
|
|
95
|
+
importance: 8.0,
|
|
96
|
+
tags: ["task", "active"]
|
|
97
|
+
)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
!!! tip "When to Use :context"
|
|
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.
|
|
109
|
+
|
|
110
|
+
### :code - Code Snippets and Patterns
|
|
111
|
+
|
|
112
|
+
Store code examples, patterns, and technical solutions.
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
# Function example
|
|
116
|
+
htm.add_node(
|
|
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
|
+
)
|
|
129
|
+
|
|
130
|
+
# SQL query pattern
|
|
131
|
+
htm.add_node(
|
|
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
|
+
)
|
|
145
|
+
|
|
146
|
+
# Configuration example
|
|
147
|
+
htm.add_node(
|
|
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
|
+
)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
!!! tip "When to Use :code"
|
|
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.
|
|
174
|
+
|
|
175
|
+
```ruby
|
|
176
|
+
# Communication style
|
|
177
|
+
htm.add_node(
|
|
178
|
+
"pref_communication",
|
|
179
|
+
"User prefers concise answers with bullet points",
|
|
180
|
+
type: :preference,
|
|
181
|
+
importance: 8.0,
|
|
182
|
+
tags: ["user", "communication", "style"]
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
# Technical preferences
|
|
186
|
+
htm.add_node(
|
|
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
|
+
)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
!!! tip "When to Use :preference"
|
|
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.
|
|
223
|
+
|
|
224
|
+
```ruby
|
|
225
|
+
# Technology choice
|
|
226
|
+
htm.add_node(
|
|
227
|
+
"decision_database",
|
|
228
|
+
<<~DECISION,
|
|
229
|
+
Decision: Use PostgreSQL with TimescaleDB for HTM storage
|
|
230
|
+
|
|
231
|
+
Rationale:
|
|
232
|
+
- Excellent time-series optimization
|
|
233
|
+
- Native vector search with pgvector
|
|
234
|
+
- Strong consistency guarantees
|
|
235
|
+
- Mature ecosystem
|
|
236
|
+
|
|
237
|
+
Alternatives considered:
|
|
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
|
+
)
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
!!! tip "When to Use :decision"
|
|
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.
|
|
293
|
+
|
|
294
|
+
```ruby
|
|
295
|
+
# Technical question
|
|
296
|
+
htm.add_node(
|
|
297
|
+
"question_caching",
|
|
298
|
+
"Should we implement Redis caching for frequently accessed memories?",
|
|
299
|
+
type: :question,
|
|
300
|
+
importance: 7.0,
|
|
301
|
+
tags: ["performance", "caching", "open"]
|
|
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
|
|
340
|
+
|
|
341
|
+
```ruby
|
|
342
|
+
# Critical (9.0-10.0): Must never lose
|
|
343
|
+
htm.add_node("api_key", "Production API key: ...", importance: 10.0)
|
|
344
|
+
htm.add_node("decision_architecture", "Core architecture decision", importance: 9.5)
|
|
345
|
+
|
|
346
|
+
# High (7.0-8.9): Very important, high retention
|
|
347
|
+
htm.add_node("user_identity", "User's name and email", importance: 8.0)
|
|
348
|
+
htm.add_node("major_decision", "Chose Rails for web framework", importance: 7.5)
|
|
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)
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Importance by Type
|
|
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 |
|
|
373
|
+
|
|
374
|
+
!!! warning "Importance Affects Eviction"
|
|
375
|
+
When working memory is full, HTM evicts memories with lower importance first. Set importance thoughtfully based on long-term value.
|
|
376
|
+
|
|
377
|
+
## Adding Relationships
|
|
378
|
+
|
|
379
|
+
Link related memories to build a knowledge graph:
|
|
380
|
+
|
|
381
|
+
```ruby
|
|
382
|
+
# Add a decision
|
|
383
|
+
htm.add_node(
|
|
384
|
+
"decision_database",
|
|
385
|
+
"Use PostgreSQL for data storage",
|
|
386
|
+
type: :decision,
|
|
387
|
+
importance: 9.0
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
# Add related implementation code
|
|
391
|
+
htm.add_node(
|
|
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
|
+
)
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### Tag Naming Conventions
|
|
438
|
+
|
|
439
|
+
```ruby
|
|
440
|
+
# Good: Consistent, lowercase, descriptive
|
|
441
|
+
tags: ["user", "authentication", "security", "oauth"]
|
|
442
|
+
|
|
443
|
+
# Avoid: Inconsistent casing, vague terms
|
|
444
|
+
tags: ["User", "auth", "stuff", "misc"]
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### Common Tag Patterns
|
|
448
|
+
|
|
449
|
+
```ruby
|
|
450
|
+
# Domain tags
|
|
451
|
+
tags: ["database", "api", "ui", "auth", "billing"]
|
|
452
|
+
|
|
453
|
+
# Layer tags
|
|
454
|
+
tags: ["frontend", "backend", "infrastructure", "data"]
|
|
455
|
+
|
|
456
|
+
# Status tags
|
|
457
|
+
tags: ["active", "deprecated", "experimental", "stable"]
|
|
458
|
+
|
|
459
|
+
# Priority tags
|
|
460
|
+
tags: ["critical", "high-priority", "low-priority"]
|
|
461
|
+
|
|
462
|
+
# Project tags
|
|
463
|
+
tags: ["project-alpha", "project-beta"]
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
## Advanced Patterns
|
|
467
|
+
|
|
468
|
+
### Timestamped Entries
|
|
469
|
+
|
|
470
|
+
Create time-series logs:
|
|
471
|
+
|
|
472
|
+
```ruby
|
|
473
|
+
def log_event(event_type, description)
|
|
474
|
+
timestamp = Time.now.to_i
|
|
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")
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### Versioned Information
|
|
490
|
+
|
|
491
|
+
Track changes over time:
|
|
492
|
+
|
|
493
|
+
```ruby
|
|
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
|
+
```
|
|
509
|
+
|
|
510
|
+
### Compound Memories
|
|
511
|
+
|
|
512
|
+
Store structured information:
|
|
513
|
+
|
|
514
|
+
```ruby
|
|
515
|
+
# User profile as compound memory
|
|
516
|
+
user_profile = {
|
|
517
|
+
name: "Alice Thompson",
|
|
518
|
+
email: "alice@example.com",
|
|
519
|
+
role: "Senior Engineer",
|
|
520
|
+
joined: "2023-01-15"
|
|
521
|
+
}.map { |k, v| "#{k}: #{v}" }.join("\n")
|
|
522
|
+
|
|
523
|
+
htm.add_node(
|
|
524
|
+
"user_profile_001",
|
|
525
|
+
user_profile,
|
|
526
|
+
type: :fact,
|
|
527
|
+
importance: 9.0,
|
|
528
|
+
tags: ["user", "profile", "complete"]
|
|
529
|
+
)
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
### Conditional Importance
|
|
533
|
+
|
|
534
|
+
Adjust importance based on context:
|
|
535
|
+
|
|
536
|
+
```ruby
|
|
537
|
+
def add_memory_with_context(key, value, type, base_importance, current_project)
|
|
538
|
+
# Boost importance for current project
|
|
539
|
+
importance = base_importance
|
|
540
|
+
importance += 2.0 if tags.include?(current_project)
|
|
541
|
+
importance = [importance, 10.0].min # Cap at 10.0
|
|
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
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
## Best Practices
|
|
554
|
+
|
|
555
|
+
### 1. Use Descriptive Keys
|
|
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
|
|
570
|
+
|
|
571
|
+
```ruby
|
|
572
|
+
# Define standard categories
|
|
573
|
+
CATEGORIES = {
|
|
574
|
+
architecture: "architecture",
|
|
575
|
+
security: "security",
|
|
576
|
+
performance: "performance",
|
|
577
|
+
ui: "user-interface"
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
htm.add_node(
|
|
581
|
+
key, value,
|
|
582
|
+
category: CATEGORIES[:architecture]
|
|
583
|
+
)
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
### 3. Include Context in Values
|
|
587
|
+
|
|
588
|
+
```ruby
|
|
589
|
+
# Good: Self-contained
|
|
590
|
+
htm.add_node(
|
|
591
|
+
"decision_001",
|
|
592
|
+
"Decided to use Redis for session storage because it provides fast access and automatic expiration",
|
|
593
|
+
type: :decision
|
|
594
|
+
)
|
|
595
|
+
|
|
596
|
+
# Bad: Requires external context
|
|
597
|
+
htm.add_node(
|
|
598
|
+
"decision_001",
|
|
599
|
+
"Use Redis", # Why? For what?
|
|
600
|
+
type: :decision
|
|
601
|
+
)
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
### 4. Tag Generously
|
|
605
|
+
|
|
606
|
+
```ruby
|
|
607
|
+
# Good: Rich tags for multiple retrieval paths
|
|
608
|
+
htm.add_node(
|
|
609
|
+
"code_api_auth",
|
|
610
|
+
"...",
|
|
611
|
+
tags: ["api", "authentication", "security", "jwt", "middleware", "ruby"]
|
|
612
|
+
)
|
|
613
|
+
|
|
614
|
+
# Suboptimal: Minimal tags
|
|
615
|
+
htm.add_node(
|
|
616
|
+
"code_api_auth",
|
|
617
|
+
"...",
|
|
618
|
+
tags: ["code"]
|
|
619
|
+
)
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
### 5. Use Relationships to Build Context
|
|
623
|
+
|
|
624
|
+
```ruby
|
|
625
|
+
# Create a narrative with relationships
|
|
626
|
+
decision_id = htm.add_node("decision_api", "Use GraphQL", type: :decision)
|
|
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
|
+
)
|
|
634
|
+
|
|
635
|
+
htm.add_node(
|
|
636
|
+
"code_upload",
|
|
637
|
+
"GraphQL upload implementation",
|
|
638
|
+
type: :code,
|
|
639
|
+
related_to: ["decision_api", "question_api"]
|
|
640
|
+
)
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
## Common Pitfalls
|
|
644
|
+
|
|
645
|
+
### Pitfall 1: Duplicate Keys
|
|
646
|
+
|
|
647
|
+
```ruby
|
|
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
|
+
```
|
|
652
|
+
|
|
653
|
+
**Solution**: Use unique keys with timestamps or UUIDs:
|
|
654
|
+
|
|
655
|
+
```ruby
|
|
656
|
+
require 'securerandom'
|
|
657
|
+
|
|
658
|
+
htm.add_node("user_#{SecureRandom.hex(4)}", "Alice")
|
|
659
|
+
htm.add_node("user_#{SecureRandom.hex(4)}", "Bob")
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
### Pitfall 2: Too-High Importance
|
|
663
|
+
|
|
664
|
+
```ruby
|
|
665
|
+
# Don't make everything critical
|
|
666
|
+
htm.add_node("note", "Random thought", importance: 10.0) # Too high!
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
**Solution**: Reserve high importance (9-10) for truly critical data.
|
|
670
|
+
|
|
671
|
+
### Pitfall 3: Missing Context
|
|
672
|
+
|
|
673
|
+
```ruby
|
|
674
|
+
# Bad: No context
|
|
675
|
+
htm.add_node("decision", "Chose option A", type: :decision)
|
|
676
|
+
|
|
677
|
+
# Good: Include rationale
|
|
678
|
+
htm.add_node(
|
|
679
|
+
"decision_auth",
|
|
680
|
+
"Chose OAuth 2.0 for authentication because it provides better security and is industry standard",
|
|
681
|
+
type: :decision
|
|
682
|
+
)
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
### Pitfall 4: No Tags
|
|
686
|
+
|
|
687
|
+
```ruby
|
|
688
|
+
# Harder to find later
|
|
689
|
+
htm.add_node("code_001", "def foo...", type: :code)
|
|
690
|
+
|
|
691
|
+
# Better: Tags enable multiple retrieval paths
|
|
692
|
+
htm.add_node(
|
|
693
|
+
"code_001",
|
|
694
|
+
"def foo...",
|
|
695
|
+
type: :code,
|
|
696
|
+
tags: ["ruby", "functions", "utilities"]
|
|
697
|
+
)
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
## Performance Considerations
|
|
701
|
+
|
|
702
|
+
### Batch Operations
|
|
703
|
+
|
|
704
|
+
When adding many memories, consider transaction efficiency:
|
|
705
|
+
|
|
706
|
+
```ruby
|
|
707
|
+
# Instead of many individual adds
|
|
708
|
+
memories = [
|
|
709
|
+
{key: "fact_001", value: "...", type: :fact},
|
|
710
|
+
{key: "fact_002", value: "...", type: :fact},
|
|
711
|
+
# ... many more
|
|
712
|
+
]
|
|
713
|
+
|
|
714
|
+
# Add them efficiently
|
|
715
|
+
memories.each do |m|
|
|
716
|
+
htm.add_node(m[:key], m[:value], type: m[:type], importance: m[:importance])
|
|
717
|
+
end
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
!!! note
|
|
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
|
|
724
|
+
|
|
725
|
+
Embedding generation has a cost:
|
|
726
|
+
|
|
727
|
+
```ruby
|
|
728
|
+
# Short text: Fast (~50ms)
|
|
729
|
+
htm.add_node("fact", "User name is Alice", ...)
|
|
730
|
+
|
|
731
|
+
# Long text: Slower (~500ms)
|
|
732
|
+
htm.add_node("code", "..." * 1000, ...) # 1000 chars
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
!!! tip
|
|
736
|
+
For very long content (>1000 tokens), consider splitting into multiple nodes or summarizing.
|
|
737
|
+
|
|
738
|
+
## Next Steps
|
|
739
|
+
|
|
740
|
+
Now that you know how to add memories effectively, learn about:
|
|
741
|
+
|
|
742
|
+
- [**Recalling Memories**](recalling-memories.md) - Search and retrieve memories
|
|
743
|
+
- [**Search Strategies**](search-strategies.md) - Optimize retrieval with different strategies
|
|
744
|
+
- [**Context Assembly**](context-assembly.md) - Use memories with your LLM
|
|
745
|
+
|
|
746
|
+
## Complete Example
|
|
747
|
+
|
|
748
|
+
```ruby
|
|
749
|
+
require 'htm'
|
|
750
|
+
|
|
751
|
+
htm = HTM.new(robot_name: "Memory Demo")
|
|
752
|
+
|
|
753
|
+
# Add a fact with rich metadata
|
|
754
|
+
htm.add_node(
|
|
755
|
+
"user_profile",
|
|
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"]
|
|
761
|
+
)
|
|
762
|
+
|
|
763
|
+
# Add a related preference
|
|
764
|
+
htm.add_node(
|
|
765
|
+
"user_pref_tools",
|
|
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"]
|
|
771
|
+
)
|
|
772
|
+
|
|
773
|
+
# Add a decision with context
|
|
774
|
+
htm.add_node(
|
|
775
|
+
"decision_messaging",
|
|
776
|
+
<<~DECISION,
|
|
777
|
+
Decision: Use RabbitMQ for async job processing
|
|
778
|
+
|
|
779
|
+
Rationale:
|
|
780
|
+
- Need reliable message delivery
|
|
781
|
+
- Support for multiple consumer patterns
|
|
782
|
+
- Excellent Ruby client library
|
|
783
|
+
|
|
784
|
+
Alternatives:
|
|
785
|
+
- Redis (simpler but less reliable)
|
|
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
|
+
)
|
|
793
|
+
|
|
794
|
+
# Add implementation code
|
|
795
|
+
htm.add_node(
|
|
796
|
+
"code_rabbitmq_setup",
|
|
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
|
+
)
|
|
811
|
+
|
|
812
|
+
# Add an open question
|
|
813
|
+
htm.add_node(
|
|
814
|
+
"question_scaling",
|
|
815
|
+
"Should we implement message partitioning for better scaling?",
|
|
816
|
+
type: :question,
|
|
817
|
+
importance: 7.0,
|
|
818
|
+
tags: ["rabbitmq", "scaling", "performance", "open"],
|
|
819
|
+
related_to: ["decision_messaging"]
|
|
820
|
+
)
|
|
821
|
+
|
|
822
|
+
puts "Added 5 memories with relationships and rich metadata"
|
|
823
|
+
puts "Stats: #{htm.memory_stats[:total_nodes]} total nodes"
|
|
824
|
+
```
|