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.
Files changed (155) hide show
  1. checksums.yaml +7 -0
  2. data/.architecture/decisions/adrs/001-use-postgresql-timescaledb-storage.md +227 -0
  3. data/.architecture/decisions/adrs/002-two-tier-memory-architecture.md +322 -0
  4. data/.architecture/decisions/adrs/003-ollama-default-embedding-provider.md +339 -0
  5. data/.architecture/decisions/adrs/004-multi-robot-shared-memory-hive-mind.md +374 -0
  6. data/.architecture/decisions/adrs/005-rag-based-retrieval-with-hybrid-search.md +443 -0
  7. data/.architecture/decisions/adrs/006-context-assembly-strategies.md +444 -0
  8. data/.architecture/decisions/adrs/007-working-memory-eviction-strategy.md +461 -0
  9. data/.architecture/decisions/adrs/008-robot-identification-system.md +550 -0
  10. data/.architecture/decisions/adrs/009-never-forget-explicit-deletion-only.md +570 -0
  11. data/.architecture/decisions/adrs/010-redis-working-memory-rejected.md +323 -0
  12. data/.architecture/decisions/adrs/011-database-side-embedding-generation-with-pgai.md +585 -0
  13. data/.architecture/decisions/adrs/012-llm-driven-ontology-topic-extraction.md +583 -0
  14. data/.architecture/decisions/adrs/013-activerecord-orm-and-many-to-many-tagging.md +299 -0
  15. data/.architecture/decisions/adrs/014-client-side-embedding-generation-workflow.md +569 -0
  16. data/.architecture/decisions/adrs/015-hierarchical-tag-ontology-and-llm-extraction.md +701 -0
  17. data/.architecture/decisions/adrs/016-async-embedding-and-tag-generation.md +694 -0
  18. data/.architecture/members.yml +144 -0
  19. data/.architecture/reviews/2025-10-29-llm-configuration-and-async-processing-review.md +1137 -0
  20. data/.architecture/reviews/initial-system-analysis.md +330 -0
  21. data/.envrc +32 -0
  22. data/.irbrc +145 -0
  23. data/CHANGELOG.md +150 -0
  24. data/COMMITS.md +196 -0
  25. data/LICENSE +21 -0
  26. data/README.md +1347 -0
  27. data/Rakefile +51 -0
  28. data/SETUP.md +268 -0
  29. data/config/database.yml +67 -0
  30. data/db/migrate/20250101000001_enable_extensions.rb +14 -0
  31. data/db/migrate/20250101000002_create_robots.rb +14 -0
  32. data/db/migrate/20250101000003_create_nodes.rb +42 -0
  33. data/db/migrate/20250101000005_create_tags.rb +38 -0
  34. data/db/migrate/20250101000007_add_node_vector_indexes.rb +30 -0
  35. data/db/schema.sql +473 -0
  36. data/db/seed_data/README.md +100 -0
  37. data/db/seed_data/presidents.md +136 -0
  38. data/db/seed_data/states.md +151 -0
  39. data/db/seeds.rb +208 -0
  40. data/dbdoc/README.md +173 -0
  41. data/dbdoc/public.node_stats.md +48 -0
  42. data/dbdoc/public.node_stats.svg +41 -0
  43. data/dbdoc/public.node_tags.md +40 -0
  44. data/dbdoc/public.node_tags.svg +112 -0
  45. data/dbdoc/public.nodes.md +54 -0
  46. data/dbdoc/public.nodes.svg +118 -0
  47. data/dbdoc/public.nodes_tags.md +39 -0
  48. data/dbdoc/public.nodes_tags.svg +112 -0
  49. data/dbdoc/public.ontology_structure.md +48 -0
  50. data/dbdoc/public.ontology_structure.svg +38 -0
  51. data/dbdoc/public.operations_log.md +42 -0
  52. data/dbdoc/public.operations_log.svg +130 -0
  53. data/dbdoc/public.relationships.md +39 -0
  54. data/dbdoc/public.relationships.svg +41 -0
  55. data/dbdoc/public.robot_activity.md +46 -0
  56. data/dbdoc/public.robot_activity.svg +35 -0
  57. data/dbdoc/public.robots.md +35 -0
  58. data/dbdoc/public.robots.svg +90 -0
  59. data/dbdoc/public.schema_migrations.md +29 -0
  60. data/dbdoc/public.schema_migrations.svg +26 -0
  61. data/dbdoc/public.tags.md +35 -0
  62. data/dbdoc/public.tags.svg +60 -0
  63. data/dbdoc/public.topic_relationships.md +45 -0
  64. data/dbdoc/public.topic_relationships.svg +32 -0
  65. data/dbdoc/schema.json +1437 -0
  66. data/dbdoc/schema.svg +154 -0
  67. data/docs/api/database.md +806 -0
  68. data/docs/api/embedding-service.md +532 -0
  69. data/docs/api/htm.md +797 -0
  70. data/docs/api/index.md +259 -0
  71. data/docs/api/long-term-memory.md +1096 -0
  72. data/docs/api/working-memory.md +665 -0
  73. data/docs/architecture/adrs/001-postgresql-timescaledb.md +314 -0
  74. data/docs/architecture/adrs/002-two-tier-memory.md +411 -0
  75. data/docs/architecture/adrs/003-ollama-embeddings.md +421 -0
  76. data/docs/architecture/adrs/004-hive-mind.md +437 -0
  77. data/docs/architecture/adrs/005-rag-retrieval.md +531 -0
  78. data/docs/architecture/adrs/006-context-assembly.md +496 -0
  79. data/docs/architecture/adrs/007-eviction-strategy.md +645 -0
  80. data/docs/architecture/adrs/008-robot-identification.md +625 -0
  81. data/docs/architecture/adrs/009-never-forget.md +648 -0
  82. data/docs/architecture/adrs/010-redis-working-memory-rejected.md +323 -0
  83. data/docs/architecture/adrs/011-pgai-integration.md +494 -0
  84. data/docs/architecture/adrs/index.md +215 -0
  85. data/docs/architecture/hive-mind.md +736 -0
  86. data/docs/architecture/index.md +351 -0
  87. data/docs/architecture/overview.md +538 -0
  88. data/docs/architecture/two-tier-memory.md +873 -0
  89. data/docs/assets/css/custom.css +83 -0
  90. data/docs/assets/images/htm-core-components.svg +63 -0
  91. data/docs/assets/images/htm-database-schema.svg +93 -0
  92. data/docs/assets/images/htm-hive-mind-architecture.svg +125 -0
  93. data/docs/assets/images/htm-importance-scoring-framework.svg +83 -0
  94. data/docs/assets/images/htm-layered-architecture.svg +71 -0
  95. data/docs/assets/images/htm-long-term-memory-architecture.svg +115 -0
  96. data/docs/assets/images/htm-working-memory-architecture.svg +120 -0
  97. data/docs/assets/images/htm.jpg +0 -0
  98. data/docs/assets/images/htm_demo.gif +0 -0
  99. data/docs/assets/js/mathjax.js +18 -0
  100. data/docs/assets/videos/htm_video.mp4 +0 -0
  101. data/docs/database_rake_tasks.md +322 -0
  102. data/docs/development/contributing.md +787 -0
  103. data/docs/development/index.md +336 -0
  104. data/docs/development/schema.md +596 -0
  105. data/docs/development/setup.md +719 -0
  106. data/docs/development/testing.md +819 -0
  107. data/docs/guides/adding-memories.md +824 -0
  108. data/docs/guides/context-assembly.md +1009 -0
  109. data/docs/guides/getting-started.md +577 -0
  110. data/docs/guides/index.md +118 -0
  111. data/docs/guides/long-term-memory.md +941 -0
  112. data/docs/guides/multi-robot.md +866 -0
  113. data/docs/guides/recalling-memories.md +927 -0
  114. data/docs/guides/search-strategies.md +953 -0
  115. data/docs/guides/working-memory.md +717 -0
  116. data/docs/index.md +214 -0
  117. data/docs/installation.md +477 -0
  118. data/docs/multi_framework_support.md +519 -0
  119. data/docs/quick-start.md +655 -0
  120. data/docs/setup_local_database.md +302 -0
  121. data/docs/using_rake_tasks_in_your_app.md +383 -0
  122. data/examples/basic_usage.rb +93 -0
  123. data/examples/cli_app/README.md +317 -0
  124. data/examples/cli_app/htm_cli.rb +270 -0
  125. data/examples/custom_llm_configuration.rb +183 -0
  126. data/examples/example_app/Rakefile +71 -0
  127. data/examples/example_app/app.rb +206 -0
  128. data/examples/sinatra_app/Gemfile +21 -0
  129. data/examples/sinatra_app/app.rb +335 -0
  130. data/lib/htm/active_record_config.rb +113 -0
  131. data/lib/htm/configuration.rb +342 -0
  132. data/lib/htm/database.rb +594 -0
  133. data/lib/htm/embedding_service.rb +115 -0
  134. data/lib/htm/errors.rb +34 -0
  135. data/lib/htm/job_adapter.rb +154 -0
  136. data/lib/htm/jobs/generate_embedding_job.rb +65 -0
  137. data/lib/htm/jobs/generate_tags_job.rb +82 -0
  138. data/lib/htm/long_term_memory.rb +965 -0
  139. data/lib/htm/models/node.rb +109 -0
  140. data/lib/htm/models/node_tag.rb +33 -0
  141. data/lib/htm/models/robot.rb +52 -0
  142. data/lib/htm/models/tag.rb +76 -0
  143. data/lib/htm/railtie.rb +76 -0
  144. data/lib/htm/sinatra.rb +157 -0
  145. data/lib/htm/tag_service.rb +135 -0
  146. data/lib/htm/tasks.rb +38 -0
  147. data/lib/htm/version.rb +5 -0
  148. data/lib/htm/working_memory.rb +182 -0
  149. data/lib/htm.rb +400 -0
  150. data/lib/tasks/db.rake +19 -0
  151. data/lib/tasks/htm.rake +147 -0
  152. data/lib/tasks/jobs.rake +312 -0
  153. data/mkdocs.yml +190 -0
  154. data/scripts/install_local_database.sh +309 -0
  155. metadata +341 -0
@@ -0,0 +1,1009 @@
1
+ # Context Assembly
2
+
3
+ Context assembly is the process of converting working memory into a formatted string that can be used with your LLM. This guide covers the three assembly strategies, optimization techniques, and best practices for creating high-quality context.
4
+
5
+ ## What is Context Assembly?
6
+
7
+ Context assembly transforms working memory into LLM-ready context:
8
+
9
+ <svg viewBox="0 0 900 550" xmlns="http://www.w3.org/2000/svg" style="background: transparent;">
10
+ <defs>
11
+ <style>
12
+ .box { fill: rgba(33, 150, 243, 0.2); stroke: #2196F3; stroke-width: 2; }
13
+ .strategy-box { fill: rgba(76, 175, 80, 0.2); stroke: #4CAF50; stroke-width: 2; }
14
+ .output-box { fill: rgba(255, 152, 0, 0.2); stroke: #FF9800; stroke-width: 2; }
15
+ .llm-box { fill: rgba(156, 39, 176, 0.2); stroke: #9C27B0; stroke-width: 3; }
16
+ .text-header { fill: #E0E0E0; font-size: 16px; font-weight: bold; }
17
+ .text-label { fill: #E0E0E0; font-size: 13px; }
18
+ .text-small { fill: #B0B0B0; font-size: 11px; }
19
+ .arrow { stroke: #4A9EFF; stroke-width: 2; fill: none; marker-end: url(#arrowhead); }
20
+ </style>
21
+ <marker id="arrowhead" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
22
+ <polygon points="0 0, 10 3, 0 6" fill="#4A9EFF" />
23
+ </marker>
24
+ </defs>
25
+
26
+ <!-- Title -->
27
+ <text x="450" y="25" text-anchor="middle" class="text-header" fill="#E0E0E0">Context Assembly Process</text>
28
+
29
+ <!-- Working Memory Box -->
30
+ <rect x="50" y="60" width="200" height="200" class="box" rx="5"/>
31
+ <text x="150" y="85" text-anchor="middle" class="text-header">Working Memory</text>
32
+ <text x="150" y="105" text-anchor="middle" class="text-small">(Nodes)</text>
33
+
34
+ <text x="60" y="135" class="text-label">Node 1</text>
35
+ <text x="180" y="135" text-anchor="end" class="text-small">1000 tok</text>
36
+ <text x="60" y="155" class="text-label">Node 2</text>
37
+ <text x="180" y="155" text-anchor="end" class="text-small">500 tok</text>
38
+ <text x="60" y="175" class="text-label">Node 3</text>
39
+ <text x="180" y="175" text-anchor="end" class="text-small">2000 tok</text>
40
+ <text x="60" y="195" class="text-label">Node 4</text>
41
+ <text x="180" y="195" text-anchor="end" class="text-small">800 tok</text>
42
+ <text x="60" y="215" class="text-label">Node 5</text>
43
+ <text x="180" y="215" text-anchor="end" class="text-small">1200 tok</text>
44
+ <text x="60" y="235" class="text-label">...</text>
45
+
46
+ <!-- Assembly Strategies -->
47
+ <rect x="330" y="60" width="240" height="200" class="strategy-box" rx="5"/>
48
+ <text x="450" y="85" text-anchor="middle" class="text-header">Assembly Strategy</text>
49
+
50
+ <!-- Strategy 1: Recent -->
51
+ <rect x="340" y="100" width="220" height="35" fill="rgba(76, 175, 80, 0.3)" rx="3"/>
52
+ <text x="350" y="120" class="text-label">:recent</text>
53
+ <text x="550" y="120" text-anchor="end" class="text-small">Sort by access time</text>
54
+
55
+ <!-- Strategy 2: Important -->
56
+ <rect x="340" y="145" width="220" height="35" fill="rgba(76, 175, 80, 0.3)" rx="3"/>
57
+ <text x="350" y="165" class="text-label">:important</text>
58
+ <text x="550" y="165" text-anchor="end" class="text-small">Sort by importance</text>
59
+
60
+ <!-- Strategy 3: Balanced -->
61
+ <rect x="340" y="190" width="220" height="35" fill="rgba(76, 175, 80, 0.3)" rx="3"/>
62
+ <text x="350" y="210" class="text-label">:balanced</text>
63
+ <text x="550" y="210" text-anchor="end" class="text-small">Weighted formula</text>
64
+
65
+ <text x="450" y="250" text-anchor="middle" class="text-small">Assembles until max_tokens reached</text>
66
+
67
+ <!-- Context String Box -->
68
+ <rect x="650" y="60" width="200" height="200" class="output-box" rx="5"/>
69
+ <text x="750" y="85" text-anchor="middle" class="text-header">Context String</text>
70
+ <text x="750" y="105" text-anchor="middle" class="text-small">(Ordered)</text>
71
+
72
+ <text x="660" y="135" class="text-label">Node 3</text>
73
+ <text x="840" y="135" text-anchor="end" class="text-small">(important)</text>
74
+ <text x="660" y="155" class="text-label">Node 1</text>
75
+ <text x="840" y="155" text-anchor="end" class="text-small">(recent)</text>
76
+ <text x="660" y="175" class="text-label">Node 5</text>
77
+ <text x="840" y="175" text-anchor="end" class="text-small">(balanced)</text>
78
+ <text x="660" y="195" class="text-label">Node 2</text>
79
+ <text x="840" y="195" text-anchor="end" class="text-small">(fits)</text>
80
+ <text x="660" y="215" class="text-small" fill="#808080">...</text>
81
+ <text x="750" y="245" text-anchor="middle" class="text-small" fill="#66BB6A">✓ Within token limit</text>
82
+
83
+ <!-- LLM Prompt Box -->
84
+ <rect x="250" y="330" width="400" height="180" class="llm-box" rx="5"/>
85
+ <text x="450" y="355" text-anchor="middle" class="text-header">LLM Prompt</text>
86
+
87
+ <text x="260" y="385" class="text-small" fill="#BB86FC">System: You are a helpful assistant...</text>
88
+ <text x="260" y="410" class="text-small" fill="#BB86FC">Context from memory:</text>
89
+ <text x="270" y="430" class="text-small" fill="#E0E0E0">[Assembled Context String]</text>
90
+ <text x="260" y="455" class="text-small" fill="#BB86FC">User: How do we handle auth?</text>
91
+ <text x="260" y="480" class="text-small" fill="#BB86FC">Assistant:</text>
92
+
93
+ <!-- Arrows -->
94
+ <path d="M 250 160 L 330 160" class="arrow"/>
95
+ <text x="290" y="150" text-anchor="middle" class="text-small">select &amp;</text>
96
+ <text x="290" y="163" text-anchor="middle" class="text-small">sort</text>
97
+
98
+ <path d="M 570 160 L 650 160" class="arrow"/>
99
+ <text x="610" y="150" text-anchor="middle" class="text-small">assemble</text>
100
+
101
+ <path d="M 750 260 L 750 330" class="arrow"/>
102
+ <text x="770" y="300" class="text-small">insert into</text>
103
+ <text x="770" y="313" class="text-small">prompt</text>
104
+
105
+ <!-- Token count indicator -->
106
+ <rect x="650" y="275" width="200" height="30" fill="rgba(255, 152, 0, 0.1)" stroke="#FF9800" stroke-width="1" rx="3"/>
107
+ <rect x="650" y="275" width="140" height="30" fill="rgba(76, 175, 80, 0.3)" rx="3"/>
108
+ <text x="750" y="293" text-anchor="middle" class="text-small">4700 / 5000 tokens</text>
109
+ </svg>
110
+
111
+ ## Basic Usage
112
+
113
+ The `create_context` method assembles context from working memory:
114
+
115
+ ```ruby
116
+ # Basic context assembly
117
+ context = htm.create_context(
118
+ strategy: :balanced, # Assembly strategy
119
+ max_tokens: nil # Optional token limit
120
+ )
121
+
122
+ # Use with your LLM
123
+ prompt = <<~PROMPT
124
+ Context from memory:
125
+ #{context}
126
+
127
+ User question: How do we handle authentication?
128
+
129
+ Assistant:
130
+ PROMPT
131
+
132
+ # Send to LLM...
133
+ response = llm.complete(prompt)
134
+ ```
135
+
136
+ ## Assembly Strategies
137
+
138
+ HTM provides three strategies for assembling context, each optimized for different use cases.
139
+
140
+ ### Recent Strategy
141
+
142
+ The `:recent` strategy prioritizes newest memories first.
143
+
144
+ ```ruby
145
+ context = htm.create_context(strategy: :recent)
146
+ ```
147
+
148
+ **How it works:**
149
+
150
+ 1. Sort memories by access time (most recent first)
151
+ 2. Add memories in order until token limit reached
152
+ 3. Return assembled context
153
+
154
+ **Best for:**
155
+
156
+ - Continuing recent conversations
157
+ - Session-based interactions
158
+ - Short-term context tracking
159
+ - Real-time applications
160
+
161
+ **Example:**
162
+
163
+ ```ruby
164
+ # Chat application
165
+ class ChatBot
166
+ def initialize
167
+ @htm = HTM.new(robot_name: "Chat", working_memory_size: 128_000)
168
+ @turn = 0
169
+ end
170
+
171
+ def chat(user_message)
172
+ @turn += 1
173
+
174
+ # Add user message
175
+ @htm.add_node(
176
+ "turn_#{@turn}_user",
177
+ "User: #{user_message}",
178
+ type: :context,
179
+ importance: 6.0
180
+ )
181
+
182
+ # Get recent context
183
+ context = @htm.create_context(
184
+ strategy: :recent,
185
+ max_tokens: 10_000
186
+ )
187
+
188
+ # Generate response
189
+ response = llm_generate(context, user_message)
190
+
191
+ # Store assistant response
192
+ @htm.add_node(
193
+ "turn_#{@turn}_assistant",
194
+ "Assistant: #{response}",
195
+ type: :context,
196
+ importance: 6.0
197
+ )
198
+
199
+ response
200
+ end
201
+
202
+ private
203
+
204
+ def llm_generate(context, message)
205
+ # Your LLM integration here
206
+ "Generated response based on context"
207
+ end
208
+ end
209
+ ```
210
+
211
+ ### Important Strategy
212
+
213
+ The `:important` strategy prioritizes high-importance memories.
214
+
215
+ ```ruby
216
+ context = htm.create_context(strategy: :important)
217
+ ```
218
+
219
+ **How it works:**
220
+
221
+ 1. Sort memories by importance (highest first)
222
+ 2. Add memories in order until token limit reached
223
+ 3. Return assembled context
224
+
225
+ **Best for:**
226
+
227
+ - Critical information retention
228
+ - System constraints and rules
229
+ - User preferences
230
+ - Core knowledge base
231
+ - Decision-making support
232
+
233
+ **Example:**
234
+
235
+ ```ruby
236
+ # Knowledge base with priorities
237
+ class KnowledgeBot
238
+ def initialize
239
+ @htm = HTM.new(robot_name: "Knowledge")
240
+
241
+ # Add critical system constraints
242
+ @htm.add_node(
243
+ "constraint_001",
244
+ "CRITICAL: Never expose API keys in responses",
245
+ type: :fact,
246
+ importance: 10.0
247
+ )
248
+
249
+ # Add important user preferences
250
+ @htm.add_node(
251
+ "pref_001",
252
+ "User prefers concise explanations",
253
+ type: :preference,
254
+ importance: 8.0
255
+ )
256
+
257
+ # Add general knowledge
258
+ @htm.add_node(
259
+ "fact_001",
260
+ "Python uses indentation for code blocks",
261
+ type: :fact,
262
+ importance: 5.0
263
+ )
264
+ end
265
+
266
+ def answer_question(question)
267
+ # Get most important context first
268
+ context = @htm.create_context(
269
+ strategy: :important,
270
+ max_tokens: 5_000
271
+ )
272
+
273
+ # Critical constraints and preferences are included first
274
+ generate_answer(context, question)
275
+ end
276
+
277
+ private
278
+
279
+ def generate_answer(context, question)
280
+ # LLM integration
281
+ "Answer based on important context"
282
+ end
283
+ end
284
+ ```
285
+
286
+ ### Balanced Strategy (Recommended)
287
+
288
+ The `:balanced` strategy combines importance and recency using a weighted formula.
289
+
290
+ ```ruby
291
+ context = htm.create_context(strategy: :balanced)
292
+ ```
293
+
294
+ **How it works:**
295
+
296
+ 1. Calculate score: `importance × (1 / (1 + age_in_hours))`
297
+ 2. Sort by score (highest first)
298
+ 3. Add memories until token limit reached
299
+ 4. Return assembled context
300
+
301
+ **Scoring examples:**
302
+
303
+ ```ruby
304
+ # Recent + Important: High score
305
+ # Importance: 9.0, Age: 1 hour
306
+ # Score: 9.0 × (1 / (1 + 1)) = 4.5 ✓ Included
307
+
308
+ # Old + Important: Medium score
309
+ # Importance: 9.0, Age: 24 hours
310
+ # Score: 9.0 × (1 / (1 + 24)) = 0.36 ≈ Maybe
311
+
312
+ # Recent + Unimportant: Low score
313
+ # Importance: 2.0, Age: 1 hour
314
+ # Score: 2.0 × (1 / (1 + 1)) = 1.0 ≈ Maybe
315
+
316
+ # Old + Unimportant: Very low score
317
+ # Importance: 2.0, Age: 24 hours
318
+ # Score: 2.0 × (1 / (1 + 24)) = 0.08 ✗ Excluded
319
+ ```
320
+
321
+ **Best for:**
322
+
323
+ - General-purpose applications (recommended default)
324
+ - Mixed temporal needs
325
+ - Production systems
326
+ - Balanced context requirements
327
+
328
+ **Example:**
329
+
330
+ ```ruby
331
+ # General-purpose assistant
332
+ class Assistant
333
+ def initialize
334
+ @htm = HTM.new(
335
+ robot_name: "Assistant",
336
+ working_memory_size: 128_000
337
+ )
338
+ end
339
+
340
+ def process(user_input)
341
+ # Add user input
342
+ @htm.add_node(
343
+ "input_#{Time.now.to_i}",
344
+ user_input,
345
+ type: :context,
346
+ importance: 7.0
347
+ )
348
+
349
+ # Get balanced context (recent + important)
350
+ context = @htm.create_context(
351
+ strategy: :balanced,
352
+ max_tokens: 50_000
353
+ )
354
+
355
+ # Use context with LLM
356
+ generate_response(context, user_input)
357
+ end
358
+
359
+ private
360
+
361
+ def generate_response(context, input)
362
+ prompt = <<~PROMPT
363
+ You are a helpful assistant with access to memory.
364
+
365
+ Context from memory:
366
+ #{context}
367
+
368
+ User: #{input}
369
+
370
+ Assistant:
371
+ PROMPT
372
+
373
+ # Send to LLM
374
+ llm_complete(prompt)
375
+ end
376
+
377
+ def llm_complete(prompt)
378
+ # Your LLM integration
379
+ "Generated response"
380
+ end
381
+ end
382
+ ```
383
+
384
+ ## Token Limits
385
+
386
+ Control context size with token limits:
387
+
388
+ ```ruby
389
+ # Use default (working memory size)
390
+ context = htm.create_context(strategy: :balanced)
391
+
392
+ # Custom limit
393
+ context = htm.create_context(
394
+ strategy: :balanced,
395
+ max_tokens: 50_000
396
+ )
397
+
398
+ # Small context for simple queries
399
+ context = htm.create_context(
400
+ strategy: :recent,
401
+ max_tokens: 5_000
402
+ )
403
+
404
+ # Large context for complex tasks
405
+ context = htm.create_context(
406
+ strategy: :important,
407
+ max_tokens: 200_000
408
+ )
409
+ ```
410
+
411
+ **Choosing token limits:**
412
+
413
+ | Limit | Use Case |
414
+ |-------|----------|
415
+ | 2K-5K | Simple Q&A, quick lookups |
416
+ | 10K-20K | Standard conversations |
417
+ | 50K-100K | Complex analysis, code generation |
418
+ | 100K+ | Document processing, extensive context |
419
+
420
+ !!! warning "LLM Context Windows"
421
+ Don't exceed your LLM's context window:
422
+ - GPT-3.5: 4K-16K tokens
423
+ - GPT-4: 8K-128K tokens
424
+ - Claude: 100K-200K tokens
425
+ - Llama 2: 4K tokens
426
+
427
+ ## Strategy Comparison
428
+
429
+ ### Performance
430
+
431
+ ```ruby
432
+ require 'benchmark'
433
+
434
+ # Add 1000 test memories
435
+ 1000.times do |i|
436
+ htm.add_node(
437
+ "test_#{i}",
438
+ "Memory #{i}",
439
+ importance: rand(1.0..10.0)
440
+ )
441
+ end
442
+
443
+ # Benchmark strategies
444
+ Benchmark.bm(15) do |x|
445
+ x.report("Recent:") do
446
+ 100.times { htm.create_context(strategy: :recent) }
447
+ end
448
+
449
+ x.report("Important:") do
450
+ 100.times { htm.create_context(strategy: :important) }
451
+ end
452
+
453
+ x.report("Balanced:") do
454
+ 100.times { htm.create_context(strategy: :balanced) }
455
+ end
456
+ end
457
+
458
+ # Typical results:
459
+ # user system total real
460
+ # Recent: 0.050000 0.000000 0.050000 ( 0.051234)
461
+ # Important: 0.045000 0.000000 0.045000 ( 0.047891)
462
+ # Balanced: 0.080000 0.000000 0.080000 ( 0.082456)
463
+ ```
464
+
465
+ **Notes:**
466
+
467
+ - `:recent` is fastest (simple sort)
468
+ - `:important` is fast (simple sort)
469
+ - `:balanced` is slower (complex calculation)
470
+ - All are typically < 100ms for normal working memory sizes
471
+
472
+ ### Quality Comparison
473
+
474
+ ```ruby
475
+ # Test scenario: Mix of old important and recent unimportant data
476
+
477
+ # Setup
478
+ htm = HTM.new(robot_name: "Test")
479
+
480
+ # Add old important data
481
+ htm.add_node("old_critical", "Critical system constraint", importance: 10.0)
482
+ sleep 1 # Simulate age
483
+
484
+ # Add recent unimportant data
485
+ 20.times do |i|
486
+ htm.add_node("recent_#{i}", "Recent note #{i}", importance: 2.0)
487
+ end
488
+
489
+ # Compare strategies
490
+ puts "=== Recent Strategy ==="
491
+ context = htm.create_context(strategy: :recent, max_tokens: 1000)
492
+ puts context.include?("Critical system constraint") ? "✓ Has critical" : "✗ Missing critical"
493
+
494
+ puts "\n=== Important Strategy ==="
495
+ context = htm.create_context(strategy: :important, max_tokens: 1000)
496
+ puts context.include?("Critical system constraint") ? "✓ Has critical" : "✗ Missing critical"
497
+
498
+ puts "\n=== Balanced Strategy ==="
499
+ context = htm.create_context(strategy: :balanced, max_tokens: 1000)
500
+ puts context.include?("Critical system constraint") ? "✓ Has critical" : "✗ Missing critical"
501
+
502
+ # Results:
503
+ # Recent: ✗ Missing critical (prioritized recent notes)
504
+ # Important: ✓ Has critical (prioritized by importance)
505
+ # Balanced: ✓ Has critical (balanced approach)
506
+ ```
507
+
508
+ ## Advanced Techniques
509
+
510
+ ### 1. Multi-Strategy Context
511
+
512
+ Use multiple strategies for comprehensive context:
513
+
514
+ ```ruby
515
+ def multi_strategy_context(max_tokens_per_strategy: 10_000)
516
+ # Get different perspectives
517
+ recent = htm.create_context(
518
+ strategy: :recent,
519
+ max_tokens: max_tokens_per_strategy
520
+ )
521
+
522
+ important = htm.create_context(
523
+ strategy: :important,
524
+ max_tokens: max_tokens_per_strategy
525
+ )
526
+
527
+ # Combine (you might want to deduplicate)
528
+ combined = <<~CONTEXT
529
+ === Recent Context ===
530
+ #{recent}
531
+
532
+ === Important Context ===
533
+ #{important}
534
+ CONTEXT
535
+
536
+ combined
537
+ end
538
+ ```
539
+
540
+ ### 2. Dynamic Strategy Selection
541
+
542
+ Choose strategy based on query type:
543
+
544
+ ```ruby
545
+ def smart_context(query)
546
+ strategy = if query.match?(/recent|latest|current/)
547
+ :recent
548
+ elsif query.match?(/important|critical|must/)
549
+ :important
550
+ else
551
+ :balanced
552
+ end
553
+
554
+ htm.create_context(strategy: strategy, max_tokens: 20_000)
555
+ end
556
+
557
+ # Usage
558
+ context = smart_context("What are the recent changes?") # Uses :recent
559
+ context = smart_context("What are critical constraints?") # Uses :important
560
+ context = smart_context("How do we handle auth?") # Uses :balanced
561
+ ```
562
+
563
+ ### 3. Filtered Context
564
+
565
+ Include only specific types of memories:
566
+
567
+ ```ruby
568
+ def filtered_context(type:, strategy: :balanced)
569
+ # This requires custom implementation
570
+ # HTM doesn't expose working memory internals directly
571
+
572
+ # Workaround: Recall specific types
573
+ memories = htm.recall(
574
+ timeframe: "last 24 hours",
575
+ topic: "type:#{type}", # Pseudo-filter
576
+ strategy: :hybrid,
577
+ limit: 50
578
+ ).select { |m| m['type'] == type.to_s }
579
+
580
+ # Manually assemble context
581
+ memories.map { |m| m['value'] }.join("\n\n")
582
+ end
583
+
584
+ # Usage
585
+ facts_only = filtered_context(type: :fact)
586
+ decisions_only = filtered_context(type: :decision)
587
+ ```
588
+
589
+ ### 4. Sectioned Context
590
+
591
+ Organize context into sections:
592
+
593
+ ```ruby
594
+ def sectioned_context
595
+ # Get different types of context
596
+ facts = htm.recall(timeframe: "all time", topic: "fact")
597
+ .select { |m| m['type'] == 'fact' }
598
+ .first(5)
599
+
600
+ decisions = htm.recall(timeframe: "all time", topic: "decision")
601
+ .select { |m| m['type'] == 'decision' }
602
+ .first(5)
603
+
604
+ recent = htm.recall(timeframe: "last hour", topic: "", limit: 5)
605
+
606
+ # Format as sections
607
+ <<~CONTEXT
608
+ === Core Facts ===
609
+ #{facts.map { |f| "- #{f['value']}" }.join("\n")}
610
+
611
+ === Key Decisions ===
612
+ #{decisions.map { |d| "- #{d['value']}" }.join("\n")}
613
+
614
+ === Recent Activity ===
615
+ #{recent.map { |r| "- #{r['value']}" }.join("\n")}
616
+ CONTEXT
617
+ end
618
+ ```
619
+
620
+ ### 5. Token-Aware Context
621
+
622
+ Ensure context fits LLM limits:
623
+
624
+ ```ruby
625
+ class TokenAwareContext
626
+ def initialize(htm, embedding_service)
627
+ @htm = htm
628
+ @embedding_service = embedding_service
629
+ end
630
+
631
+ def create(strategy:, llm_context_window:, reserve_for_prompt: 1000)
632
+ # Calculate available tokens
633
+ available = llm_context_window - reserve_for_prompt
634
+
635
+ # Get context
636
+ context = @htm.create_context(
637
+ strategy: strategy,
638
+ max_tokens: available
639
+ )
640
+
641
+ # Verify token count
642
+ actual_tokens = @embedding_service.count_tokens(context)
643
+
644
+ if actual_tokens > available
645
+ warn "Context exceeded limit! Truncating..."
646
+ # Retry with smaller limit
647
+ context = @htm.create_context(
648
+ strategy: strategy,
649
+ max_tokens: available * 0.9 # 90% to be safe
650
+ )
651
+ end
652
+
653
+ context
654
+ end
655
+ end
656
+
657
+ # Usage
658
+ embedding_service = HTM::EmbeddingService.new
659
+ context_builder = TokenAwareContext.new(htm, embedding_service)
660
+
661
+ context = context_builder.create(
662
+ strategy: :balanced,
663
+ llm_context_window: 100_000, # Claude 100K
664
+ reserve_for_prompt: 2_000
665
+ )
666
+ ```
667
+
668
+ ## Using Context with LLMs
669
+
670
+ ### Pattern 1: System Prompt + Context
671
+
672
+ ```ruby
673
+ def generate_with_context(user_query)
674
+ context = htm.create_context(strategy: :balanced, max_tokens: 50_000)
675
+
676
+ system_prompt = <<~SYSTEM
677
+ You are a helpful AI assistant with access to memory.
678
+ Use the provided context to answer questions accurately.
679
+ SYSTEM
680
+
681
+ user_prompt = <<~USER
682
+ Context from memory:
683
+ #{context}
684
+
685
+ ---
686
+
687
+ User question: #{user_query}
688
+
689
+ Please answer based on the context above.
690
+ USER
691
+
692
+ # Send to LLM with system + user prompts
693
+ llm.chat(system: system_prompt, user: user_prompt)
694
+ end
695
+ ```
696
+
697
+ ### Pattern 2: Conversation History
698
+
699
+ ```ruby
700
+ class ConversationManager
701
+ def initialize
702
+ @htm = HTM.new(robot_name: "Chat")
703
+ @conversation_id = SecureRandom.uuid
704
+ end
705
+
706
+ def add_turn(user_msg, assistant_msg)
707
+ timestamp = Time.now.to_i
708
+
709
+ @htm.add_node(
710
+ "#{@conversation_id}_#{timestamp}_user",
711
+ user_msg,
712
+ type: :context,
713
+ importance: 6.0,
714
+ tags: ["conversation", @conversation_id]
715
+ )
716
+
717
+ @htm.add_node(
718
+ "#{@conversation_id}_#{timestamp}_assistant",
719
+ assistant_msg,
720
+ type: :context,
721
+ importance: 6.0,
722
+ tags: ["conversation", @conversation_id]
723
+ )
724
+ end
725
+
726
+ def get_context_for_llm
727
+ # Get recent conversation
728
+ @htm.create_context(
729
+ strategy: :recent,
730
+ max_tokens: 10_000
731
+ )
732
+ end
733
+ end
734
+ ```
735
+
736
+ ### Pattern 3: RAG with Context
737
+
738
+ ```ruby
739
+ def rag_query(question)
740
+ # 1. Retrieve relevant memories
741
+ relevant = htm.recall(
742
+ timeframe: "last month",
743
+ topic: question,
744
+ strategy: :hybrid,
745
+ limit: 10
746
+ )
747
+
748
+ # 2. Create context from working memory (includes retrieved + existing)
749
+ context = htm.create_context(
750
+ strategy: :balanced,
751
+ max_tokens: 30_000
752
+ )
753
+
754
+ # 3. Generate answer
755
+ prompt = <<~PROMPT
756
+ Context:
757
+ #{context}
758
+
759
+ Question: #{question}
760
+
761
+ Answer based on the context above:
762
+ PROMPT
763
+
764
+ llm.complete(prompt)
765
+ end
766
+ ```
767
+
768
+ ## Optimization Tips
769
+
770
+ ### 1. Cache Context
771
+
772
+ ```ruby
773
+ class ContextCache
774
+ def initialize(htm, ttl: 60)
775
+ @htm = htm
776
+ @ttl = ttl
777
+ @cache = {}
778
+ end
779
+
780
+ def get_context(strategy:, max_tokens: nil)
781
+ cache_key = "#{strategy}_#{max_tokens}"
782
+
783
+ # Check cache
784
+ if cached = @cache[cache_key]
785
+ if Time.now - cached[:time] < @ttl
786
+ return cached[:context]
787
+ end
788
+ end
789
+
790
+ # Generate new context
791
+ context = @htm.create_context(
792
+ strategy: strategy,
793
+ max_tokens: max_tokens
794
+ )
795
+
796
+ # Cache it
797
+ @cache[cache_key] = {
798
+ context: context,
799
+ time: Time.now
800
+ }
801
+
802
+ context
803
+ end
804
+
805
+ def invalidate
806
+ @cache.clear
807
+ end
808
+ end
809
+
810
+ # Usage
811
+ cache = ContextCache.new(htm, ttl: 30) # 30 second TTL
812
+ context = cache.get_context(strategy: :balanced) # Cached for 30s
813
+ ```
814
+
815
+ ### 2. Progressive Context Loading
816
+
817
+ ```ruby
818
+ def progressive_context(start_tokens: 5_000, max_tokens: 50_000)
819
+ # Start small
820
+ context = htm.create_context(strategy: :balanced, max_tokens: start_tokens)
821
+
822
+ # Check if more context needed (based on your logic)
823
+ if needs_more_context?(context)
824
+ # Expand gradually
825
+ context = htm.create_context(strategy: :balanced, max_tokens: start_tokens * 2)
826
+ end
827
+
828
+ if still_needs_more?(context)
829
+ # Expand to max
830
+ context = htm.create_context(strategy: :balanced, max_tokens: max_tokens)
831
+ end
832
+
833
+ context
834
+ end
835
+
836
+ def needs_more_context?(context)
837
+ # Your logic here
838
+ context.length < 1000 # Example: too short
839
+ end
840
+
841
+ def still_needs_more?(context)
842
+ # Your logic here
843
+ false # Example
844
+ end
845
+ ```
846
+
847
+ ### 3. Selective Inclusion
848
+
849
+ ```ruby
850
+ def selective_context(query)
851
+ # Determine what's relevant
852
+ include_facts = query.match?(/fact|truth|information/)
853
+ include_decisions = query.match?(/decision|choice|why/)
854
+ include_code = query.match?(/code|implement|example/)
855
+
856
+ # Build custom context
857
+ parts = []
858
+
859
+ if include_facts
860
+ facts = htm.recall(timeframe: "all time", topic: query)
861
+ .select { |m| m['type'] == 'fact' }
862
+ .first(5)
863
+ parts << "Facts:\n" + facts.map { |f| "- #{f['value']}" }.join("\n")
864
+ end
865
+
866
+ if include_decisions
867
+ decisions = htm.recall(timeframe: "all time", topic: query)
868
+ .select { |m| m['type'] == 'decision' }
869
+ .first(5)
870
+ parts << "Decisions:\n" + decisions.map { |d| "- #{d['value']}" }.join("\n")
871
+ end
872
+
873
+ if include_code
874
+ code = htm.recall(timeframe: "all time", topic: query)
875
+ .select { |m| m['type'] == 'code' }
876
+ .first(3)
877
+ parts << "Code Examples:\n" + code.map { |c| c['value'] }.join("\n\n")
878
+ end
879
+
880
+ parts.join("\n\n")
881
+ end
882
+ ```
883
+
884
+ ## Best Practices
885
+
886
+ ### 1. Choose the Right Strategy
887
+
888
+ ```ruby
889
+ # Use :recent for conversations
890
+ context = htm.create_context(strategy: :recent)
891
+
892
+ # Use :important for critical operations
893
+ context = htm.create_context(strategy: :important)
894
+
895
+ # Use :balanced as default (recommended)
896
+ context = htm.create_context(strategy: :balanced)
897
+ ```
898
+
899
+ ### 2. Set Appropriate Token Limits
900
+
901
+ ```ruby
902
+ # Don't exceed LLM context window
903
+ context = htm.create_context(
904
+ strategy: :balanced,
905
+ max_tokens: 100_000 # Leave room for prompt
906
+ )
907
+
908
+ # Smaller contexts are faster
909
+ context = htm.create_context(
910
+ strategy: :recent,
911
+ max_tokens: 5_000 # Quick queries
912
+ )
913
+ ```
914
+
915
+ ### 3. Monitor Context Quality
916
+
917
+ ```ruby
918
+ def monitor_context
919
+ context = htm.create_context(strategy: :balanced)
920
+
921
+ puts "Context length: #{context.length} characters"
922
+
923
+ # Count token estimate
924
+ embedding_service = HTM::EmbeddingService.new
925
+ tokens = embedding_service.count_tokens(context)
926
+ puts "Estimated tokens: #{tokens}"
927
+
928
+ # Check if too small or too large
929
+ warn "Context very small!" if tokens < 500
930
+ warn "Context very large!" if tokens > 100_000
931
+ end
932
+ ```
933
+
934
+ ### 4. Include Metadata
935
+
936
+ ```ruby
937
+ def context_with_metadata
938
+ context = htm.create_context(strategy: :balanced, max_tokens: 20_000)
939
+
940
+ # Add metadata header
941
+ stats = htm.memory_stats
942
+
943
+ <<~CONTEXT
944
+ [Context assembled at #{Time.now}]
945
+ [Strategy: balanced]
946
+ [Working memory: #{stats[:working_memory][:node_count]} nodes]
947
+ [Robot: #{htm.robot_name}]
948
+
949
+ #{context}
950
+ CONTEXT
951
+ end
952
+ ```
953
+
954
+ ## Complete Example
955
+
956
+ ```ruby
957
+ require 'htm'
958
+
959
+ # Initialize HTM
960
+ htm = HTM.new(
961
+ robot_name: "Context Demo",
962
+ working_memory_size: 128_000
963
+ )
964
+
965
+ # Add various memories
966
+ htm.add_node("fact_001", "User prefers Ruby", type: :fact, importance: 9.0)
967
+ htm.add_node("decision_001", "Use PostgreSQL", type: :decision, importance: 8.0)
968
+ htm.add_node("context_001", "Currently debugging auth", type: :context, importance: 7.0)
969
+ htm.add_node("code_001", "def auth...", type: :code, importance: 6.0)
970
+ htm.add_node("note_001", "Check logs later", type: :context, importance: 2.0)
971
+
972
+ puts "=== Recent Strategy ==="
973
+ recent = htm.create_context(strategy: :recent, max_tokens: 5_000)
974
+ puts recent
975
+ puts "\n(Newest first)"
976
+
977
+ puts "\n=== Important Strategy ==="
978
+ important = htm.create_context(strategy: :important, max_tokens: 5_000)
979
+ puts important
980
+ puts "\n(Most important first)"
981
+
982
+ puts "\n=== Balanced Strategy ==="
983
+ balanced = htm.create_context(strategy: :balanced, max_tokens: 5_000)
984
+ puts balanced
985
+ puts "\n(Recent + important)"
986
+
987
+ # Use with LLM
988
+ def ask_llm(context, question)
989
+ prompt = <<~PROMPT
990
+ Context:
991
+ #{context}
992
+
993
+ Question: #{question}
994
+ Answer:
995
+ PROMPT
996
+
997
+ # Send to your LLM here
998
+ puts "\n=== LLM Prompt ==="
999
+ puts prompt
1000
+ end
1001
+
1002
+ ask_llm(balanced, "What database are we using?")
1003
+ ```
1004
+
1005
+ ## Next Steps
1006
+
1007
+ - [**Recalling Memories**](recalling-memories.md) - Populate working memory effectively
1008
+ - [**Working Memory**](working-memory.md) - Understand memory management
1009
+ - [**Search Strategies**](search-strategies.md) - Optimize retrieval for context