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,953 @@
1
+ # Search Strategies Deep Dive
2
+
3
+ HTM provides three search strategies for retrieving memories: vector search, full-text search, and hybrid search. This guide explores each strategy in depth, when to use them, and how to optimize performance.
4
+
5
+ ## Overview
6
+
7
+ | Strategy | Method | Strength | Best For |
8
+ |----------|--------|----------|----------|
9
+ | **Vector** | Semantic similarity via embeddings | Understanding meaning | Conceptual queries, related topics |
10
+ | **Full-text** | PostgreSQL text search | Exact keyword matching | Specific terms, proper nouns |
11
+ | **Hybrid** | Combines both approaches | Best overall accuracy | General purpose queries |
12
+
13
+ <svg viewBox="0 0 900 650" xmlns="http://www.w3.org/2000/svg" style="background: transparent;">
14
+ <!-- Title -->
15
+ <text x="450" y="30" text-anchor="middle" fill="#E0E0E0" font-size="18" font-weight="bold">HTM Search Strategy Comparison</text>
16
+
17
+ <!-- Vector Search Strategy -->
18
+ <rect x="30" y="70" width="260" height="250" fill="rgba(33, 150, 243, 0.2)" stroke="#2196F3" stroke-width="3" rx="5"/>
19
+ <text x="160" y="100" text-anchor="middle" fill="#E0E0E0" font-size="16" font-weight="bold">Vector Search</text>
20
+ <text x="160" y="125" text-anchor="middle" fill="#2196F3" font-size="13" font-weight="bold">Semantic Similarity</text>
21
+
22
+ <text x="50" y="155" fill="#B0B0B0" font-size="12" font-weight="bold">How it works:</text>
23
+ <text x="50" y="175" fill="#B0B0B0" font-size="11">• Generate query embedding</text>
24
+ <text x="50" y="195" fill="#B0B0B0" font-size="11">• Find nearest neighbors</text>
25
+ <text x="50" y="215" fill="#B0B0B0" font-size="11">• Rank by cosine similarity</text>
26
+
27
+ <text x="50" y="245" fill="#4CAF50" font-size="12" font-weight="bold">Best for:</text>
28
+ <text x="50" y="265" fill="#4CAF50" font-size="11">✓ Conceptual queries</text>
29
+ <text x="50" y="285" fill="#4CAF50" font-size="11">✓ Related topics</text>
30
+ <text x="50" y="305" fill="#4CAF50" font-size="11">✓ Understanding intent</text>
31
+
32
+ <!-- Full-text Search Strategy -->
33
+ <rect x="320" y="70" width="260" height="250" fill="rgba(76, 175, 80, 0.2)" stroke="#4CAF50" stroke-width="3" rx="5"/>
34
+ <text x="450" y="100" text-anchor="middle" fill="#E0E0E0" font-size="16" font-weight="bold">Full-Text Search</text>
35
+ <text x="450" y="125" text-anchor="middle" fill="#4CAF50" font-size="13" font-weight="bold">Keyword Matching</text>
36
+
37
+ <text x="340" y="155" fill="#B0B0B0" font-size="12" font-weight="bold">How it works:</text>
38
+ <text x="340" y="175" fill="#B0B0B0" font-size="11">• Tokenize query</text>
39
+ <text x="340" y="195" fill="#B0B0B0" font-size="11">• Match against ts_vector</text>
40
+ <text x="340" y="215" fill="#B0B0B0" font-size="11">• Rank by tf-idf</text>
41
+
42
+ <text x="340" y="245" fill="#2196F3" font-size="12" font-weight="bold">Best for:</text>
43
+ <text x="340" y="265" fill="#2196F3" font-size="11">✓ Exact keywords</text>
44
+ <text x="340" y="285" fill="#2196F3" font-size="11">✓ Proper nouns</text>
45
+ <text x="340" y="305" fill="#2196F3" font-size="11">✓ Acronyms & commands</text>
46
+
47
+ <!-- Hybrid Search Strategy -->
48
+ <rect x="610" y="70" width="260" height="250" fill="rgba(156, 39, 176, 0.2)" stroke="#9C27B0" stroke-width="3" rx="5"/>
49
+ <text x="740" y="100" text-anchor="middle" fill="#E0E0E0" font-size="16" font-weight="bold">Hybrid Search</text>
50
+ <text x="740" y="125" text-anchor="middle" fill="#9C27B0" font-size="13" font-weight="bold">Best of Both Worlds</text>
51
+
52
+ <text x="630" y="155" fill="#B0B0B0" font-size="12" font-weight="bold">How it works:</text>
53
+ <text x="630" y="175" fill="#B0B0B0" font-size="11">• Run both searches</text>
54
+ <text x="630" y="195" fill="#B0B0B0" font-size="11">• Apply RRF scoring</text>
55
+ <text x="630" y="215" fill="#B0B0B0" font-size="11">• Merge & rank results</text>
56
+
57
+ <text x="630" y="245" fill="#FFC107" font-size="12" font-weight="bold">Best for:</text>
58
+ <text x="630" y="265" fill="#FFC107" font-size="11">✓ General queries</text>
59
+ <text x="630" y="285" fill="#FFC107" font-size="11">✓ Production default</text>
60
+ <text x="630" y="305" fill="#FFC107" font-size="11">✓ Mixed terminology</text>
61
+
62
+ <!-- Example Query -->
63
+ <text x="450" y="365" text-anchor="middle" fill="#E0E0E0" font-size="14" font-weight="bold">Example Query: "improve database performance"</text>
64
+
65
+ <!-- Vector Results -->
66
+ <rect x="30" y="390" width="260" height="230" fill="rgba(33, 150, 243, 0.1)" stroke="#2196F3" stroke-width="2" rx="3"/>
67
+ <text x="160" y="415" text-anchor="middle" fill="#2196F3" font-size="13" font-weight="bold">Vector Results</text>
68
+ <text x="50" y="440" fill="#B0B0B0" font-size="10">1. "Query optimization" (0.92)</text>
69
+ <text x="50" y="460" fill="#B0B0B0" font-size="10">2. "Caching strategies" (0.87)</text>
70
+ <text x="50" y="480" fill="#B0B0B0" font-size="10">3. "Index tuning" (0.85)</text>
71
+ <text x="50" y="500" fill="#B0B0B0" font-size="10">4. "Connection pooling" (0.82)</text>
72
+ <text x="160" y="530" text-anchor="middle" fill="#4CAF50" font-size="11" font-weight="bold">Finds conceptually</text>
73
+ <text x="160" y="550" text-anchor="middle" fill="#4CAF50" font-size="11" font-weight="bold">related memories</text>
74
+ <text x="160" y="570" text-anchor="middle" fill="#FF9800" font-size="10">(May miss exact terms)</text>
75
+ <text x="160" y="600" text-anchor="middle" fill="#B0B0B0" font-size="10">Speed: ~80ms</text>
76
+
77
+ <!-- Full-text Results -->
78
+ <rect x="320" y="390" width="260" height="230" fill="rgba(76, 175, 80, 0.1)" stroke="#4CAF50" stroke-width="2" rx="3"/>
79
+ <text x="450" y="415" text-anchor="middle" fill="#4CAF50" font-size="13" font-weight="bold">Full-Text Results</text>
80
+ <text x="340" y="440" fill="#B0B0B0" font-size="10">1. "Database performance" (0.95)</text>
81
+ <text x="340" y="460" fill="#B0B0B0" font-size="10">2. "Improve query speed" (0.88)</text>
82
+ <text x="340" y="480" fill="#B0B0B0" font-size="10">3. "Performance testing" (0.72)</text>
83
+ <text x="340" y="500" fill="#B0B0B0" font-size="10">(May miss related concepts)</text>
84
+ <text x="450" y="530" text-anchor="middle" fill="#2196F3" font-size="11" font-weight="bold">Finds exact keyword</text>
85
+ <text x="450" y="550" text-anchor="middle" fill="#2196F3" font-size="11" font-weight="bold">matches</text>
86
+ <text x="450" y="570" text-anchor="middle" fill="#FF9800" font-size="10">(Needs right words)</text>
87
+ <text x="450" y="600" text-anchor="middle" fill="#B0B0B0" font-size="10">Speed: ~30ms</text>
88
+
89
+ <!-- Hybrid Results -->
90
+ <rect x="610" y="390" width="260" height="230" fill="rgba(156, 39, 176, 0.1)" stroke="#9C27B0" stroke-width="2" rx="3"/>
91
+ <text x="740" y="415" text-anchor="middle" fill="#9C27B0" font-size="13" font-weight="bold">Hybrid Results</text>
92
+ <text x="630" y="440" fill="#B0B0B0" font-size="10">1. "Database performance" (0.96)</text>
93
+ <text x="630" y="460" fill="#B0B0B0" font-size="10">2. "Query optimization" (0.93)</text>
94
+ <text x="630" y="480" fill="#B0B0B0" font-size="10">3. "Improve query speed" (0.91)</text>
95
+ <text x="630" y="500" fill="#B0B0B0" font-size="10">4. "Caching strategies" (0.89)</text>
96
+ <text x="740" y="530" text-anchor="middle" fill="#FFC107" font-size="11" font-weight="bold">Balanced precision</text>
97
+ <text x="740" y="550" text-anchor="middle" fill="#FFC107" font-size="11" font-weight="bold">& recall</text>
98
+ <text x="740" y="570" text-anchor="middle" fill="#4CAF50" font-size="10">(Recommended!)</text>
99
+ <text x="740" y="600" text-anchor="middle" fill="#B0B0B0" font-size="10">Speed: ~120ms</text>
100
+ </svg>
101
+
102
+ ## Vector Search (Semantic)
103
+
104
+ Vector search finds memories based on semantic similarity using embeddings.
105
+
106
+ ### How It Works
107
+
108
+ ```
109
+ User Query: "database optimization techniques"
110
+
111
+ Ollama Embedding (gpt-oss)
112
+
113
+ [0.234, -0.567, 0.123, ...] ← 1536-dimensional vector
114
+
115
+ PostgreSQL + pgvector
116
+
117
+ Find nearest neighbors using cosine similarity
118
+
119
+ Results ranked by similarity score
120
+ ```
121
+
122
+ ### Basic Usage
123
+
124
+ ```ruby
125
+ memories = htm.recall(
126
+ timeframe: "last month",
127
+ topic: "improving application performance",
128
+ strategy: :vector,
129
+ limit: 10
130
+ )
131
+
132
+ memories.each do |m|
133
+ puts "#{m['value']}"
134
+ puts "Similarity: #{m['similarity']}" # 0.0 to 1.0
135
+ puts
136
+ end
137
+ ```
138
+
139
+ ### Understanding Similarity Scores
140
+
141
+ Similarity scores indicate how related the memory is to your query:
142
+
143
+ ```ruby
144
+ # High similarity (0.8-1.0): Very relevant
145
+ # - Query: "PostgreSQL optimization"
146
+ # - Result: "Optimizing PostgreSQL queries with indexes" (0.92)
147
+
148
+ # Medium similarity (0.6-0.8): Moderately relevant
149
+ # - Query: "database performance"
150
+ # - Result: "Caching strategies for web applications" (0.72)
151
+
152
+ # Low similarity (0.4-0.6): Loosely related
153
+ # - Query: "user authentication"
154
+ # - Result: "Session management best practices" (0.58)
155
+
156
+ # Very low similarity (<0.4): Probably not relevant
157
+ # - Query: "database backup"
158
+ # - Result: "Frontend styling with CSS" (0.23)
159
+ ```
160
+
161
+ ### When Vector Search Excels
162
+
163
+ **1. Conceptual Queries**
164
+
165
+ ```ruby
166
+ # Query about concepts, not specific keywords
167
+ memories = htm.recall(
168
+ timeframe: "last year",
169
+ topic: "ways to speed up slow applications",
170
+ strategy: :vector
171
+ )
172
+
173
+ # Finds:
174
+ # - "Database query optimization" (0.89)
175
+ # - "Caching strategies" (0.87)
176
+ # - "Code profiling techniques" (0.85)
177
+ # - "Load balancing approaches" (0.82)
178
+ ```
179
+
180
+ **2. Related Topics**
181
+
182
+ ```ruby
183
+ # Find related concepts even without exact keywords
184
+ memories = htm.recall(
185
+ timeframe: "all time",
186
+ topic: "machine learning",
187
+ strategy: :vector
188
+ )
189
+
190
+ # Finds:
191
+ # - "Neural network architecture" (no "ML" keyword!)
192
+ # - "Training data preparation"
193
+ # - "Model evaluation metrics"
194
+ # - "Predictive analytics"
195
+ ```
196
+
197
+ **3. Understanding Intent**
198
+
199
+ ```ruby
200
+ # Different phrasings of same intent
201
+ queries = [
202
+ "how to make code faster",
203
+ "performance optimization techniques",
204
+ "speeding up application execution",
205
+ "reducing runtime overhead"
206
+ ]
207
+
208
+ queries.each do |q|
209
+ results = htm.recall(timeframe: "all time", topic: q, strategy: :vector)
210
+ # All queries return similar results!
211
+ end
212
+ ```
213
+
214
+ **4. Multilingual Support**
215
+
216
+ ```ruby
217
+ # If embeddings support multiple languages
218
+ memories = htm.recall(
219
+ timeframe: "all time",
220
+ topic: "base de données", # French: database
221
+ strategy: :vector
222
+ )
223
+
224
+ # Can find English memories about databases
225
+ # (depends on embedding model's training)
226
+ ```
227
+
228
+ ### Vector Search Limitations
229
+
230
+ **1. Specific Terms**
231
+
232
+ ```ruby
233
+ # Bad for exact technical terms
234
+ memories = htm.recall(
235
+ timeframe: "all time",
236
+ topic: "JWT", # Specific acronym
237
+ strategy: :vector
238
+ )
239
+
240
+ # May miss exact "JWT" mentions
241
+ # Better to use full-text for acronyms
242
+ ```
243
+
244
+ **2. Proper Nouns**
245
+
246
+ ```ruby
247
+ # Not ideal for names
248
+ memories = htm.recall(
249
+ timeframe: "all time",
250
+ topic: "Alice Thompson",
251
+ strategy: :vector
252
+ )
253
+
254
+ # May not prioritize exact name matches
255
+ # Use full-text or hybrid instead
256
+ ```
257
+
258
+ ### Optimizing Vector Search
259
+
260
+ **1. Adjust Similarity Threshold**
261
+
262
+ ```ruby
263
+ def vector_search_with_threshold(topic, threshold: 0.7)
264
+ results = htm.recall(
265
+ timeframe: "all time",
266
+ topic: topic,
267
+ strategy: :vector,
268
+ limit: 50
269
+ )
270
+
271
+ # Filter by threshold
272
+ results.select { |m| m['similarity'].to_f >= threshold }
273
+ end
274
+
275
+ high_quality = vector_search_with_threshold("database", threshold: 0.8)
276
+ ```
277
+
278
+ **2. Use Descriptive Queries**
279
+
280
+ ```ruby
281
+ # Vague: Returns less relevant results
282
+ htm.recall(topic: "API", strategy: :vector)
283
+
284
+ # Descriptive: Returns more relevant results
285
+ htm.recall(topic: "RESTful API design patterns and best practices", strategy: :vector)
286
+ ```
287
+
288
+ **3. Query Expansion**
289
+
290
+ ```ruby
291
+ def expanded_vector_search(base_query, related_terms)
292
+ # Combine base query with related terms
293
+ expanded = "#{base_query} #{related_terms.join(' ')}"
294
+
295
+ htm.recall(
296
+ timeframe: "all time",
297
+ topic: expanded,
298
+ strategy: :vector,
299
+ limit: 20
300
+ )
301
+ end
302
+
303
+ results = expanded_vector_search(
304
+ "database",
305
+ ["PostgreSQL", "SQL", "relational", "ACID"]
306
+ )
307
+ ```
308
+
309
+ ## Full-text Search (Keywords)
310
+
311
+ Full-text search uses PostgreSQL's powerful text search capabilities for exact keyword matching.
312
+
313
+ ### How It Works
314
+
315
+ ```
316
+ User Query: "PostgreSQL indexing"
317
+
318
+ PostgreSQL ts_query
319
+
320
+ Tokenize: ["postgresql", "index"]
321
+
322
+ Match against ts_vector in database
323
+
324
+ Rank by relevance (tf-idf)
325
+
326
+ Results ranked by text rank
327
+ ```
328
+
329
+ ### Basic Usage
330
+
331
+ ```ruby
332
+ memories = htm.recall(
333
+ timeframe: "last month",
334
+ topic: "PostgreSQL indexing",
335
+ strategy: :fulltext,
336
+ limit: 10
337
+ )
338
+
339
+ memories.each do |m|
340
+ puts "#{m['value']}"
341
+ puts "Rank: #{m['rank']}" # Higher = better match
342
+ puts
343
+ end
344
+ ```
345
+
346
+ ### When Full-text Search Excels
347
+
348
+ **1. Exact Keywords**
349
+
350
+ ```ruby
351
+ # Finding specific technical terms
352
+ memories = htm.recall(
353
+ timeframe: "all time",
354
+ topic: "JWT OAuth2 authentication",
355
+ strategy: :fulltext
356
+ )
357
+
358
+ # Finds memories containing these exact terms
359
+ ```
360
+
361
+ **2. Proper Nouns**
362
+
363
+ ```ruby
364
+ # Finding people, places, products
365
+ memories = htm.recall(
366
+ timeframe: "all time",
367
+ topic: "Alice Thompson",
368
+ strategy: :fulltext
369
+ )
370
+
371
+ # Exact name matches prioritized
372
+ ```
373
+
374
+ **3. Acronyms**
375
+
376
+ ```ruby
377
+ # Technical acronyms
378
+ memories = htm.recall(
379
+ timeframe: "all time",
380
+ topic: "REST API CRUD SQL",
381
+ strategy: :fulltext
382
+ )
383
+
384
+ # Finds exact acronym matches
385
+ ```
386
+
387
+ **4. Code and Commands**
388
+
389
+ ```ruby
390
+ # Finding specific code or commands
391
+ memories = htm.recall(
392
+ timeframe: "all time",
393
+ topic: "pg_dump VACUUM",
394
+ strategy: :fulltext
395
+ )
396
+
397
+ # Exact command matches
398
+ ```
399
+
400
+ ### Full-text Search Features
401
+
402
+ **1. Boolean Operators**
403
+
404
+ ```ruby
405
+ # PostgreSQL supports AND, OR, NOT
406
+ memories = htm.recall(
407
+ timeframe: "all time",
408
+ topic: "PostgreSQL AND (indexing OR optimization)",
409
+ strategy: :fulltext
410
+ )
411
+ ```
412
+
413
+ **2. Phrase Matching**
414
+
415
+ ```ruby
416
+ # Find exact phrases
417
+ memories = htm.recall(
418
+ timeframe: "all time",
419
+ topic: '"database connection pool"', # Exact phrase
420
+ strategy: :fulltext
421
+ )
422
+ ```
423
+
424
+ **3. Stemming**
425
+
426
+ ```ruby
427
+ # PostgreSQL automatically stems words
428
+ # "running" matches "run", "runs", "runner"
429
+
430
+ memories = htm.recall(
431
+ timeframe: "all time",
432
+ topic: "optimize", # Matches "optimizing", "optimized", etc.
433
+ strategy: :fulltext
434
+ )
435
+ ```
436
+
437
+ ### Full-text Search Limitations
438
+
439
+ **1. No Semantic Understanding**
440
+
441
+ ```ruby
442
+ # Doesn't understand meaning
443
+ memories = htm.recall(
444
+ timeframe: "all time",
445
+ topic: "database",
446
+ strategy: :fulltext
447
+ )
448
+
449
+ # Won't find "PostgreSQL" unless query includes it
450
+ # (PostgreSQL doesn't match "database" keyword)
451
+ ```
452
+
453
+ **2. Keyword Dependency**
454
+
455
+ ```ruby
456
+ # Must use exact keywords
457
+ memories = htm.recall(
458
+ timeframe: "all time",
459
+ topic: "speed up application",
460
+ strategy: :fulltext
461
+ )
462
+
463
+ # Won't find "performance optimization"
464
+ # (different keywords, same concept)
465
+ ```
466
+
467
+ ### Optimizing Full-text Search
468
+
469
+ **1. Use Multiple Keywords**
470
+
471
+ ```ruby
472
+ # Include variations and synonyms
473
+ memories = htm.recall(
474
+ timeframe: "all time",
475
+ topic: "database PostgreSQL SQL relational",
476
+ strategy: :fulltext
477
+ )
478
+ ```
479
+
480
+ **2. Wildcard Searches**
481
+
482
+ ```ruby
483
+ # Use prefix matching (requires direct SQL)
484
+ config = HTM::Database.default_config
485
+ conn = PG.connect(config)
486
+
487
+ result = conn.exec_params(
488
+ <<~SQL,
489
+ SELECT key, value
490
+ FROM nodes
491
+ WHERE to_tsvector('english', value) @@ to_tsquery('english', $1)
492
+ SQL
493
+ ['postgres:*'] # Matches postgresql, postgres, etc.
494
+ )
495
+
496
+ conn.close
497
+ ```
498
+
499
+ ## Hybrid Search (Combined)
500
+
501
+ Hybrid search combines full-text and vector search for optimal results.
502
+
503
+ ### How It Works
504
+
505
+ ```
506
+ User Query: "PostgreSQL performance tuning"
507
+
508
+ Step 1: Full-text Search (Prefilter)
509
+ - Find all memories with keywords
510
+ - Limit to 100 candidates
511
+
512
+ Step 2: Vector Ranking
513
+ - Generate query embedding
514
+ - Rank candidates by similarity
515
+
516
+ Final Results
517
+ - Keyword precision + Semantic understanding
518
+ ```
519
+
520
+ ### Basic Usage
521
+
522
+ ```ruby
523
+ memories = htm.recall(
524
+ timeframe: "last month",
525
+ topic: "PostgreSQL performance optimization",
526
+ strategy: :hybrid,
527
+ limit: 10
528
+ )
529
+
530
+ # Results have both keyword matches AND semantic relevance
531
+ memories.each do |m|
532
+ puts "#{m['value']}"
533
+ puts "Similarity: #{m['similarity']}"
534
+ puts
535
+ end
536
+ ```
537
+
538
+ ### When Hybrid Search Excels
539
+
540
+ **1. General Purpose Queries**
541
+
542
+ ```ruby
543
+ # Best for most use cases
544
+ memories = htm.recall(
545
+ timeframe: "all time",
546
+ topic: "how to improve database query speed",
547
+ strategy: :hybrid
548
+ )
549
+
550
+ # Combines:
551
+ # - Keyword matches (database, query, speed)
552
+ # - Semantic understanding (optimization, performance)
553
+ ```
554
+
555
+ **2. Mixed Terminology**
556
+
557
+ ```ruby
558
+ # Query with both specific and general terms
559
+ memories = htm.recall(
560
+ timeframe: "all time",
561
+ topic: "JWT token authentication security best practices",
562
+ strategy: :hybrid
563
+ )
564
+
565
+ # Finds:
566
+ # - Exact "JWT" mentions (full-text)
567
+ # - Related security concepts (vector)
568
+ ```
569
+
570
+ **3. Production Applications**
571
+
572
+ ```ruby
573
+ # Recommended default for production
574
+ class ProductionSearch
575
+ def search(query)
576
+ htm.recall(
577
+ timeframe: "last 90 days",
578
+ topic: query,
579
+ strategy: :hybrid, # Best all-around
580
+ limit: 20
581
+ )
582
+ end
583
+ end
584
+ ```
585
+
586
+ ### Hybrid Search Parameters
587
+
588
+ **Prefilter Limit**
589
+
590
+ The number of candidates from full-text search:
591
+
592
+ ```ruby
593
+ # Internal parameter (not exposed in public API)
594
+ # Default: 100 candidates
595
+
596
+ # In LongTermMemory#search_hybrid:
597
+ # prefilter_limit: 100
598
+ ```
599
+
600
+ For very large databases, you might want to adjust this:
601
+
602
+ ```ruby
603
+ # Direct database query with custom prefilter
604
+ ltm = HTM::LongTermMemory.new(HTM::Database.default_config)
605
+ embedding_service = HTM::EmbeddingService.new
606
+
607
+ results = ltm.search_hybrid(
608
+ timeframe: Time.at(0)..Time.now,
609
+ query: "database optimization",
610
+ limit: 10,
611
+ embedding_service: embedding_service,
612
+ prefilter_limit: 200 # More candidates
613
+ )
614
+ ```
615
+
616
+ ### Optimizing Hybrid Search
617
+
618
+ **1. Balance Keywords and Concepts**
619
+
620
+ ```ruby
621
+ # Good: Mix of specific keywords and concepts
622
+ htm.recall(
623
+ topic: "PostgreSQL query optimization indexing performance",
624
+ strategy: :hybrid
625
+ )
626
+
627
+ # Suboptimal: Only keywords
628
+ htm.recall(topic: "PostgreSQL SQL", strategy: :hybrid)
629
+
630
+ # Suboptimal: Only concepts
631
+ htm.recall(topic: "making things faster", strategy: :hybrid)
632
+ ```
633
+
634
+ **2. Use Appropriate Timeframes**
635
+
636
+ ```ruby
637
+ # Narrow timeframe: Faster, more recent results
638
+ htm.recall(
639
+ timeframe: "last week",
640
+ topic: "recent errors",
641
+ strategy: :hybrid
642
+ )
643
+
644
+ # Wide timeframe: Comprehensive, slower
645
+ htm.recall(
646
+ timeframe: "last year",
647
+ topic: "architecture decisions",
648
+ strategy: :hybrid
649
+ )
650
+ ```
651
+
652
+ ## Strategy Comparison
653
+
654
+ ### Performance Benchmarks
655
+
656
+ Approximate performance on 10,000 nodes:
657
+
658
+ ```ruby
659
+ require 'benchmark'
660
+
661
+ Benchmark.bm(15) do |x|
662
+ x.report("Vector:") do
663
+ htm.recall(timeframe: "last month", topic: "database", strategy: :vector)
664
+ end
665
+
666
+ x.report("Full-text:") do
667
+ htm.recall(timeframe: "last month", topic: "database", strategy: :fulltext)
668
+ end
669
+
670
+ x.report("Hybrid:") do
671
+ htm.recall(timeframe: "last month", topic: "database", strategy: :hybrid)
672
+ end
673
+ end
674
+
675
+ # Typical results (vary by query and data):
676
+ # user system total real
677
+ # Vector: 0.150000 0.020000 0.170000 ( 0.210000)
678
+ # Full-text: 0.080000 0.010000 0.090000 ( 0.110000)
679
+ # Hybrid: 0.180000 0.025000 0.205000 ( 0.250000)
680
+ ```
681
+
682
+ ### Accuracy Comparison
683
+
684
+ ```ruby
685
+ # Test query: "improving application speed"
686
+
687
+ # Vector results (semantic understanding):
688
+ # 1. "Performance optimization techniques" (0.91)
689
+ # 2. "Code profiling for bottlenecks" (0.88)
690
+ # 3. "Caching strategies" (0.85)
691
+ # 4. "Database query optimization" (0.82)
692
+
693
+ # Full-text results (keyword matching):
694
+ # 1. "Application deployment speed" (0.95) - Has "application" & "speed"
695
+ # 2. "Improving code quality" (0.72) - Has "improving"
696
+ # (May miss relevant results without exact keywords)
697
+
698
+ # Hybrid results (best of both):
699
+ # 1. "Performance optimization techniques" (0.93)
700
+ # 2. "Application caching strategies" (0.91)
701
+ # 3. "Code profiling for bottlenecks" (0.89)
702
+ # 4. "Database query optimization" (0.86)
703
+ ```
704
+
705
+ ## Strategy Selection Guide
706
+
707
+ ### Decision Tree
708
+
709
+ ```
710
+ Start
711
+
712
+ Do you need exact keyword matches?
713
+ YES → Do you also need semantic understanding?
714
+ YES → Use HYBRID
715
+ NO → Use FULL-TEXT
716
+ NO → Do you need conceptual/semantic search?
717
+ YES → Use VECTOR
718
+ NO → Use HYBRID (default)
719
+ ```
720
+
721
+ ### Use Case Matrix
722
+
723
+ | Use Case | Recommended Strategy | Why |
724
+ |----------|---------------------|-----|
725
+ | General search | Hybrid | Best overall |
726
+ | Finding specific terms | Full-text | Exact matches |
727
+ | Conceptual queries | Vector | Understanding |
728
+ | Proper nouns/names | Full-text or Hybrid | Exact matching |
729
+ | Technical acronyms | Full-text | Keyword precision |
730
+ | Related topics | Vector | Semantic similarity |
731
+ | Production default | Hybrid | Balanced performance |
732
+ | Code/command search | Full-text | Exact syntax |
733
+ | Research queries | Vector | Conceptual understanding |
734
+
735
+ ### Code Examples
736
+
737
+ ```ruby
738
+ class SmartSearch
739
+ def initialize(htm)
740
+ @htm = htm
741
+ end
742
+
743
+ def search(query, timeframe: "last month")
744
+ # Automatically choose strategy based on query
745
+ strategy = detect_strategy(query)
746
+
747
+ @htm.recall(
748
+ timeframe: timeframe,
749
+ topic: query,
750
+ strategy: strategy,
751
+ limit: 20
752
+ )
753
+ end
754
+
755
+ private
756
+
757
+ def detect_strategy(query)
758
+ # Check for proper nouns (capital words)
759
+ has_proper_nouns = query.match?(/\b[A-Z][a-z]+\b/)
760
+
761
+ # Check for acronyms (all caps words)
762
+ has_acronyms = query.match?(/\b[A-Z]{2,}\b/)
763
+
764
+ # Check for specific technical terms
765
+ has_technical_terms = query.match?(/\b(JWT|OAuth|SQL|API|REST)\b/)
766
+
767
+ if has_acronyms || has_technical_terms
768
+ :fulltext # Use full-text for exact matches
769
+ elsif has_proper_nouns
770
+ :hybrid # Mix of exact and semantic
771
+ else
772
+ :vector # Conceptual search
773
+ end
774
+ end
775
+ end
776
+
777
+ # Usage
778
+ search = SmartSearch.new(htm)
779
+ search.search("JWT authentication") # → Uses :fulltext
780
+ search.search("Alice Thompson said") # → Uses :hybrid
781
+ search.search("performance issues") # → Uses :vector
782
+ ```
783
+
784
+ ## Advanced Techniques
785
+
786
+ ### 1. Multi-Strategy Search
787
+
788
+ ```ruby
789
+ def comprehensive_search(query, timeframe: "last month")
790
+ # Run all three strategies
791
+ vector_results = htm.recall(
792
+ timeframe: timeframe,
793
+ topic: query,
794
+ strategy: :vector,
795
+ limit: 10
796
+ )
797
+
798
+ fulltext_results = htm.recall(
799
+ timeframe: timeframe,
800
+ topic: query,
801
+ strategy: :fulltext,
802
+ limit: 10
803
+ )
804
+
805
+ hybrid_results = htm.recall(
806
+ timeframe: timeframe,
807
+ topic: query,
808
+ strategy: :hybrid,
809
+ limit: 10
810
+ )
811
+
812
+ # Combine and deduplicate
813
+ all_results = (vector_results + fulltext_results + hybrid_results)
814
+ .uniq { |m| m['key'] }
815
+
816
+ # Sort by best score
817
+ all_results.sort_by do |m|
818
+ -(m['similarity']&.to_f || m['rank']&.to_f || 0)
819
+ end.first(15)
820
+ end
821
+ ```
822
+
823
+ ### 2. Fallback Strategy
824
+
825
+ ```ruby
826
+ def search_with_fallback(query, timeframe: "last month")
827
+ # Try hybrid first
828
+ results = htm.recall(
829
+ timeframe: timeframe,
830
+ topic: query,
831
+ strategy: :hybrid,
832
+ limit: 10
833
+ )
834
+
835
+ # If no results, try vector (more flexible)
836
+ if results.empty?
837
+ warn "No hybrid results, trying vector search..."
838
+ results = htm.recall(
839
+ timeframe: timeframe,
840
+ topic: query,
841
+ strategy: :vector,
842
+ limit: 10
843
+ )
844
+ end
845
+
846
+ # If still no results, try full-text
847
+ if results.empty?
848
+ warn "No vector results, trying full-text search..."
849
+ results = htm.recall(
850
+ timeframe: timeframe,
851
+ topic: query,
852
+ strategy: :fulltext,
853
+ limit: 10
854
+ )
855
+ end
856
+
857
+ results
858
+ end
859
+ ```
860
+
861
+ ### 3. Confidence Scoring
862
+
863
+ ```ruby
864
+ def search_with_confidence(query)
865
+ results = htm.recall(
866
+ timeframe: "all time",
867
+ topic: query,
868
+ strategy: :hybrid,
869
+ limit: 20
870
+ )
871
+
872
+ # Add confidence scores
873
+ results.map do |m|
874
+ similarity = m['similarity'].to_f
875
+ importance = m['importance'].to_f
876
+
877
+ # Calculate confidence (0-100)
878
+ confidence = (
879
+ similarity * 60 + # 60% weight on similarity
880
+ (importance / 10.0) * 40 # 40% weight on importance
881
+ ).round(2)
882
+
883
+ m.merge('confidence' => confidence)
884
+ end.sort_by { |m| -m['confidence'] }
885
+ end
886
+ ```
887
+
888
+ ## Troubleshooting
889
+
890
+ ### No Results with Vector Search
891
+
892
+ ```ruby
893
+ # If vector search returns nothing:
894
+ # 1. Check Ollama is running
895
+ # 2. Try broader query
896
+ # 3. Widen timeframe
897
+ # 4. Fall back to full-text
898
+
899
+ if vector_results.empty?
900
+ # Try full-text as fallback
901
+ htm.recall(topic: query, strategy: :fulltext)
902
+ end
903
+ ```
904
+
905
+ ### Poor Quality Results
906
+
907
+ ```ruby
908
+ # Filter by quality threshold
909
+ def quality_search(query, min_similarity: 0.7)
910
+ results = htm.recall(
911
+ timeframe: "all time",
912
+ topic: query,
913
+ strategy: :hybrid,
914
+ limit: 50
915
+ )
916
+
917
+ results.select { |m| m['similarity'].to_f >= min_similarity }
918
+ end
919
+ ```
920
+
921
+ ## Complete Example
922
+
923
+ ```ruby
924
+ require 'htm'
925
+
926
+ htm = HTM.new(robot_name: "Search Demo")
927
+
928
+ # Add test data
929
+ htm.add_node("pg_001", "PostgreSQL indexing tutorial", type: :code, importance: 7.0)
930
+ htm.add_node("perf_001", "Performance optimization guide", type: :fact, importance: 8.0)
931
+ htm.add_node("cache_001", "Caching strategies for speed", type: :decision, importance: 9.0)
932
+
933
+ # Compare strategies
934
+ query = "how to make database faster"
935
+
936
+ puts "=== Vector Search (Semantic) ==="
937
+ vector = htm.recall(timeframe: "all time", topic: query, strategy: :vector)
938
+ vector.each { |m| puts "- #{m['value']} (#{m['similarity']})" }
939
+
940
+ puts "\n=== Full-text Search (Keywords) ==="
941
+ fulltext = htm.recall(timeframe: "all time", topic: query, strategy: :fulltext)
942
+ fulltext.each { |m| puts "- #{m['value']} (#{m['rank']})" }
943
+
944
+ puts "\n=== Hybrid Search (Combined) ==="
945
+ hybrid = htm.recall(timeframe: "all time", topic: query, strategy: :hybrid)
946
+ hybrid.each { |m| puts "- #{m['value']} (#{m['similarity']})" }
947
+ ```
948
+
949
+ ## Next Steps
950
+
951
+ - [**Recalling Memories**](recalling-memories.md) - Learn more about recall API
952
+ - [**Context Assembly**](context-assembly.md) - Use search results with LLMs
953
+ - [**Long-term Memory**](long-term-memory.md) - Understand the storage layer