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,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
|