htm 0.0.18 → 0.0.20

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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +59 -1
  3. data/README.md +12 -0
  4. data/db/seeds.rb +1 -1
  5. data/docs/api/embedding-service.md +140 -110
  6. data/docs/api/yard/HTM/ActiveRecordConfig.md +6 -0
  7. data/docs/api/yard/HTM/Config.md +173 -0
  8. data/docs/api/yard/HTM/ConfigSection.md +28 -0
  9. data/docs/api/yard/HTM/Database.md +1 -1
  10. data/docs/api/yard/HTM/Railtie.md +2 -2
  11. data/docs/api/yard/HTM.md +0 -57
  12. data/docs/api/yard/index.csv +76 -61
  13. data/docs/api/yard-reference.md +2 -1
  14. data/docs/architecture/adrs/003-ollama-embeddings.md +45 -36
  15. data/docs/architecture/adrs/004-hive-mind.md +1 -1
  16. data/docs/architecture/adrs/008-robot-identification.md +1 -1
  17. data/docs/architecture/index.md +11 -9
  18. data/docs/architecture/overview.md +11 -7
  19. data/docs/assets/images/balanced-strategy-decay.svg +41 -0
  20. data/docs/assets/images/class-hierarchy.svg +1 -1
  21. data/docs/assets/images/eviction-priority.svg +43 -0
  22. data/docs/assets/images/exception-hierarchy.svg +2 -2
  23. data/docs/assets/images/hive-mind-shared-memory.svg +52 -0
  24. data/docs/assets/images/htm-architecture-overview.svg +3 -3
  25. data/docs/assets/images/htm-core-components.svg +4 -4
  26. data/docs/assets/images/htm-layered-architecture.svg +1 -1
  27. data/docs/assets/images/htm-memory-addition-flow.svg +2 -2
  28. data/docs/assets/images/htm-memory-recall-flow.svg +2 -2
  29. data/docs/assets/images/memory-topology.svg +53 -0
  30. data/docs/assets/images/two-tier-memory-architecture.svg +55 -0
  31. data/docs/development/setup.md +76 -44
  32. data/docs/examples/basic-usage.md +133 -0
  33. data/docs/examples/config-files.md +170 -0
  34. data/docs/examples/file-loading.md +208 -0
  35. data/docs/examples/index.md +116 -0
  36. data/docs/examples/llm-configuration.md +168 -0
  37. data/docs/examples/mcp-client.md +172 -0
  38. data/docs/examples/rails-integration.md +173 -0
  39. data/docs/examples/robot-groups.md +210 -0
  40. data/docs/examples/sinatra-integration.md +218 -0
  41. data/docs/examples/standalone-app.md +216 -0
  42. data/docs/examples/telemetry.md +224 -0
  43. data/docs/examples/timeframes.md +143 -0
  44. data/docs/getting-started/installation.md +97 -40
  45. data/docs/getting-started/quick-start.md +28 -11
  46. data/docs/guides/configuration.md +515 -0
  47. data/docs/guides/file-loading.md +322 -0
  48. data/docs/guides/getting-started.md +40 -9
  49. data/docs/guides/index.md +3 -3
  50. data/docs/guides/mcp-server.md +30 -12
  51. data/docs/guides/propositions.md +264 -0
  52. data/docs/guides/recalling-memories.md +4 -4
  53. data/docs/guides/search-strategies.md +3 -3
  54. data/docs/guides/tags.md +318 -0
  55. data/docs/guides/telemetry.md +229 -0
  56. data/docs/index.md +8 -16
  57. data/docs/{architecture → robots}/hive-mind.md +8 -111
  58. data/docs/robots/index.md +73 -0
  59. data/docs/{guides → robots}/multi-robot.md +3 -3
  60. data/docs/{guides → robots}/robot-groups.md +8 -7
  61. data/docs/{architecture → robots}/two-tier-memory.md +13 -149
  62. data/docs/robots/why-robots.md +85 -0
  63. data/lib/htm/config/defaults.yml +4 -4
  64. data/lib/htm/config.rb +2 -2
  65. data/lib/htm/job_adapter.rb +75 -1
  66. data/lib/htm/version.rb +1 -1
  67. data/lib/htm/workflows/remember_workflow.rb +212 -0
  68. data/lib/htm.rb +1 -0
  69. data/mkdocs.yml +33 -8
  70. metadata +60 -7
  71. data/docs/api/yard/HTM/Configuration.md +0 -240
  72. data/docs/telemetry.md +0 -391
@@ -0,0 +1,264 @@
1
+ # Propositions: Atomic Fact Extraction
2
+
3
+ Proposition extraction breaks complex text into atomic, self-contained factual statements. This improves RAG retrieval accuracy by storing granular facts that can be matched more precisely.
4
+
5
+ ## Overview
6
+
7
+ When proposition extraction is enabled, HTM:
8
+
9
+ 1. Stores the original content as a node
10
+ 2. Extracts atomic propositions from the content
11
+ 3. Creates independent nodes for each proposition
12
+ 4. Each proposition gets its own embedding and tags
13
+
14
+ ## What is a Proposition?
15
+
16
+ A proposition is an atomic factual statement that:
17
+
18
+ - Expresses a **single fact** or claim
19
+ - Is **understandable without context**
20
+ - Uses **full names**, not pronouns
21
+ - Includes relevant **dates, times, and qualifiers**
22
+ - Contains **one subject-predicate relationship**
23
+
24
+ ### Example
25
+
26
+ **Original text:**
27
+ > "In 1969, Neil Armstrong became the first person to walk on the Moon during Apollo 11."
28
+
29
+ **Extracted propositions:**
30
+ - "Neil Armstrong was an astronaut."
31
+ - "Neil Armstrong walked on the Moon in 1969."
32
+ - "Neil Armstrong was the first person to walk on the Moon."
33
+ - "Neil Armstrong walked on the Moon during the Apollo 11 mission."
34
+ - "The Apollo 11 mission occurred in 1969."
35
+
36
+ ## Configuration
37
+
38
+ ### Enable Proposition Extraction
39
+
40
+ ```ruby
41
+ # Via configuration block
42
+ HTM.configure do |config|
43
+ config.extract_propositions = true
44
+ config.proposition_provider = :ollama # or :openai, :anthropic, etc.
45
+ config.proposition_model = 'gemma3:latest'
46
+ end
47
+
48
+ # Or via environment variable
49
+ # HTM_EXTRACT_PROPOSITIONS=true
50
+ ```
51
+
52
+ ### Provider Options
53
+
54
+ Proposition extraction uses LLM chat completion. Configure your preferred provider:
55
+
56
+ | Provider | Model Examples |
57
+ |----------|----------------|
58
+ | `:ollama` (default) | `gemma3:latest`, `llama3`, `mistral` |
59
+ | `:openai` | `gpt-4o-mini`, `gpt-4o` |
60
+ | `:anthropic` | `claude-3-haiku-20240307` |
61
+ | `:gemini` | `gemini-1.5-flash` |
62
+
63
+ ```ruby
64
+ HTM.configure do |config|
65
+ config.extract_propositions = true
66
+
67
+ # Use OpenAI for higher quality extraction
68
+ config.proposition_provider = :openai
69
+ config.proposition_model = 'gpt-4o-mini'
70
+ end
71
+ ```
72
+
73
+ ## How It Works
74
+
75
+ ### Workflow
76
+
77
+ ```mermaid
78
+ sequenceDiagram
79
+ participant User
80
+ participant HTM
81
+ participant PropositionService
82
+ participant LLM
83
+ participant Database
84
+
85
+ User->>HTM: remember("Complex text...")
86
+ HTM->>Database: Save original node
87
+ HTM->>PropositionService: Extract propositions
88
+ PropositionService->>LLM: Parse into atomic facts
89
+ LLM-->>PropositionService: ["Prop 1", "Prop 2", ...]
90
+ loop For each proposition
91
+ PropositionService->>Database: Create proposition node
92
+ end
93
+ HTM-->>User: node_id
94
+ ```
95
+
96
+ ### Proposition Nodes
97
+
98
+ Proposition nodes are stored with special metadata:
99
+
100
+ ```ruby
101
+ {
102
+ "is_proposition" => true,
103
+ "source_node_id" => 123 # ID of the original node
104
+ }
105
+ ```
106
+
107
+ This metadata allows you to:
108
+ - Identify proposition nodes
109
+ - Trace propositions back to source
110
+ - Filter propositions in queries
111
+
112
+ ## Usage
113
+
114
+ ### Basic Usage
115
+
116
+ ```ruby
117
+ htm = HTM.new(robot_name: "Proposition Demo")
118
+
119
+ # With extraction enabled, this creates multiple nodes
120
+ node_id = htm.remember(
121
+ "PostgreSQL 16 was released in September 2023 with improved query performance and new JSON features."
122
+ )
123
+
124
+ # The original node plus propositions are created:
125
+ # - "PostgreSQL 16 was released in September 2023."
126
+ # - "PostgreSQL 16 includes improved query performance."
127
+ # - "PostgreSQL 16 includes new JSON features."
128
+ ```
129
+
130
+ ### Direct Extraction
131
+
132
+ You can extract propositions without storing them:
133
+
134
+ ```ruby
135
+ propositions = HTM.extract_propositions(
136
+ "Ruby 3.3 introduced YJIT improvements and the Prism parser."
137
+ )
138
+ # => [
139
+ # "Ruby 3.3 introduced YJIT improvements.",
140
+ # "Ruby 3.3 introduced the Prism parser.",
141
+ # "YJIT is a just-in-time compiler for Ruby.",
142
+ # "Prism is a parser for Ruby."
143
+ # ]
144
+
145
+ # Manually store if needed
146
+ propositions.each { |p| htm.remember(p) }
147
+ ```
148
+
149
+ ### Querying Propositions
150
+
151
+ ```ruby
152
+ # Find all proposition nodes
153
+ propositions = HTM::Models::Node.where("metadata->>'is_proposition' = ?", 'true')
154
+
155
+ # Find propositions from a specific source
156
+ source_node_id = 123
157
+ related = HTM::Models::Node.where(
158
+ "metadata->>'source_node_id' = ?",
159
+ source_node_id.to_s
160
+ )
161
+
162
+ # Include propositions in recall (default behavior)
163
+ results = htm.recall("PostgreSQL features", strategy: :hybrid)
164
+ ```
165
+
166
+ ## Recursion Prevention
167
+
168
+ Proposition nodes do **not** trigger further proposition extraction. This prevents infinite recursion:
169
+
170
+ ```ruby
171
+ # Original node → triggers proposition extraction
172
+ htm.remember("Complex statement about many things.")
173
+
174
+ # Proposition nodes → do NOT trigger extraction
175
+ # (metadata.is_proposition = true prevents this)
176
+ ```
177
+
178
+ ## Performance Considerations
179
+
180
+ ### Processing Time
181
+
182
+ Proposition extraction adds latency:
183
+
184
+ | Provider | Typical Latency |
185
+ |----------|-----------------|
186
+ | Ollama (local) | 1-3 seconds |
187
+ | OpenAI | 0.5-1 second |
188
+ | Anthropic | 0.5-1 second |
189
+
190
+ ### Async Processing
191
+
192
+ With async job backend, extraction happens in background:
193
+
194
+ ```ruby
195
+ HTM.configure do |config|
196
+ config.extract_propositions = true
197
+ config.job.backend = :thread # or :sidekiq
198
+ end
199
+
200
+ # Returns immediately, propositions created async
201
+ node_id = htm.remember("Complex content...")
202
+ ```
203
+
204
+ ### Storage Impact
205
+
206
+ Proposition extraction increases storage:
207
+
208
+ - Original node: 1 record
209
+ - Propositions: 3-10 additional records (typical)
210
+ - Each proposition gets its own embedding
211
+
212
+ ## Best Practices
213
+
214
+ ### When to Use Propositions
215
+
216
+ **Good use cases:**
217
+ - Dense factual content (Wikipedia, documentation)
218
+ - Complex statements with multiple facts
219
+ - Content that will be queried for specific facts
220
+
221
+ **Less suitable:**
222
+ - Simple, atomic statements
223
+ - Conversational content
224
+ - Content where context is critical
225
+
226
+ ### Quality Tuning
227
+
228
+ Use a capable model for better extraction:
229
+
230
+ ```ruby
231
+ # Higher quality (slower, costs more)
232
+ config.proposition_provider = :openai
233
+ config.proposition_model = 'gpt-4o'
234
+
235
+ # Balanced (faster, local)
236
+ config.proposition_provider = :ollama
237
+ config.proposition_model = 'gemma3:latest'
238
+ ```
239
+
240
+ ### Selective Extraction
241
+
242
+ Enable/disable per operation if needed:
243
+
244
+ ```ruby
245
+ # Temporarily disable for specific content
246
+ original_setting = HTM.configuration.extract_propositions
247
+ HTM.configuration.extract_propositions = false
248
+ htm.remember("Simple fact that doesn't need decomposition.")
249
+ HTM.configuration.extract_propositions = original_setting
250
+ ```
251
+
252
+ ## Rake Tasks
253
+
254
+ ```bash
255
+ # Rebuild all propositions (clears and regenerates)
256
+ rake htm:db:rebuild:propositions
257
+ ```
258
+
259
+ ## Related Documentation
260
+
261
+ - [Adding Memories](adding-memories.md) - Core memory operations
262
+ - [Search Strategies](search-strategies.md) - Querying memories
263
+ - [Tags](tags.md) - Hierarchical tagging (propositions get tags too)
264
+ - [API Reference: PropositionService](../api/yard/HTM/PropositionService.md)
@@ -39,7 +39,7 @@ end
39
39
  <!-- Step 2: Generate Embedding -->
40
40
  <rect x="290" y="70" width="200" height="80" fill="rgba(33, 150, 243, 0.2)" stroke="#2196F3" stroke-width="2" rx="5"/>
41
41
  <text x="390" y="95" text-anchor="middle" fill="#2196F3" font-size="14" font-weight="bold">2. Generate Embedding</text>
42
- <text x="390" y="120" text-anchor="middle" fill="#B0B0B0" font-size="10">Ollama/OpenAI</text>
42
+ <text x="390" y="120" text-anchor="middle" fill="#B0B0B0" font-size="10">LLM Provider (RubyLLM)</text>
43
43
  <text x="390" y="135" text-anchor="middle" fill="#B0B0B0" font-size="10">[0.23, -0.57, ...]</text>
44
44
 
45
45
  <!-- Arrow 2 to 3 -->
@@ -221,7 +221,7 @@ memories = htm.recall(
221
221
 
222
222
  **How it works**:
223
223
 
224
- 1. Converts your topic to a vector embedding via Ollama
224
+ 1. Converts your topic to a vector embedding via your configured provider (Ollama, OpenAI, etc.)
225
225
  2. Finds memories with similar embeddings using cosine similarity
226
226
  3. Returns results ordered by semantic similarity
227
227
 
@@ -865,9 +865,9 @@ htm.recall(timeframe: "...", topic: "...", limit: 100)
865
865
  .first(10)
866
866
  ```
867
867
 
868
- ### Ollama Connection Issues
868
+ ### LLM Provider Connection Issues
869
869
 
870
- If vector search fails:
870
+ If vector search fails (Ollama not running, API key invalid, etc.):
871
871
 
872
872
  ```ruby
873
873
  begin
@@ -108,9 +108,9 @@ Vector search finds memories based on semantic similarity using embeddings.
108
108
  ```
109
109
  User Query: "database optimization techniques"
110
110
 
111
- Ollama Embedding (gpt-oss)
111
+ Embedding via RubyLLM (Ollama, OpenAI, etc.)
112
112
 
113
- [0.234, -0.567, 0.123, ...] ← 1536-dimensional vector
113
+ [0.234, -0.567, 0.123, ...] ← Vector representation
114
114
 
115
115
  PostgreSQL + pgvector
116
116
 
@@ -927,7 +927,7 @@ end
927
927
 
928
928
  ```ruby
929
929
  # If vector search returns nothing:
930
- # 1. Check Ollama is running
930
+ # 1. Check your LLM provider is accessible (Ollama running, API key set, etc.)
931
931
  # 2. Try broader query
932
932
  # 3. Widen timeframe
933
933
  # 4. Fall back to full-text
@@ -0,0 +1,318 @@
1
+ # Hierarchical Tags
2
+
3
+ HTM uses a hierarchical tagging system to organize memories semantically. Tags use colon-separated namespaces (like `database:postgresql:extensions`) enabling both specific and broad queries.
4
+
5
+ ## Overview
6
+
7
+ The tagging system provides:
8
+
9
+ - **Hierarchical organization**: `category:subcategory:topic`
10
+ - **LLM-powered extraction**: Tags auto-generated from content
11
+ - **Ontology awareness**: New tags consider existing taxonomy
12
+ - **Prefix queries**: Find all `database:*` tags easily
13
+ - **Visualization**: Export as text tree, Mermaid, or SVG
14
+
15
+ ## Quick Start
16
+
17
+ ```ruby
18
+ htm = HTM.new(robot_name: "Tag Demo")
19
+
20
+ # Tags are auto-extracted from content
21
+ htm.remember("PostgreSQL supports JSON and vector search via pgvector.")
22
+ # Auto-tags: ["database:postgresql", "database:postgresql:json",
23
+ # "database:postgresql:pgvector", "search:vector"]
24
+
25
+ # Or specify tags manually
26
+ htm.remember(
27
+ "Redis is an in-memory data store.",
28
+ tags: ["database:redis", "database:nosql", "caching"]
29
+ )
30
+
31
+ # Query by tag
32
+ results = htm.recall("database features", tags: ["database:postgresql"])
33
+ ```
34
+
35
+ ## Tag Format
36
+
37
+ ### Hierarchical Structure
38
+
39
+ Tags use colon (`:`) as the hierarchy separator:
40
+
41
+ ```
42
+ category:subcategory:topic
43
+ │ │ │
44
+ └─────────┴────────┴── More specific →
45
+ ```
46
+
47
+ **Examples:**
48
+ - `database:postgresql`
49
+ - `database:postgresql:extensions`
50
+ - `database:postgresql:extensions:pgvector`
51
+ - `programming:ruby:gems`
52
+ - `api:rest:authentication`
53
+
54
+ ### Naming Conventions
55
+
56
+ - **Lowercase**: Use lowercase for consistency
57
+ - **Singular nouns**: `database` not `databases`
58
+ - **Hierarchical**: Most general → most specific
59
+ - **Descriptive**: Clear, semantic meaning
60
+
61
+ ## Automatic Tag Extraction
62
+
63
+ HTM uses LLM to automatically extract relevant tags from content:
64
+
65
+ ```ruby
66
+ HTM.configure do |config|
67
+ config.tag.provider = :ollama # or :openai, :anthropic, etc.
68
+ config.tag.model = 'gemma3:latest'
69
+ end
70
+
71
+ # Tags extracted automatically
72
+ htm.remember("Ruby on Rails uses ActiveRecord for database access.")
73
+ # Extracted: ["programming:ruby:rails", "database:orm:activerecord",
74
+ # "web:framework:rails"]
75
+ ```
76
+
77
+ ### Ontology Awareness
78
+
79
+ The tag extractor receives existing tags to maintain consistency:
80
+
81
+ ```ruby
82
+ # First memory creates initial tags
83
+ htm.remember("PostgreSQL is a relational database.")
84
+ # Tags: ["database:postgresql", "database:relational"]
85
+
86
+ # Later memories align with existing ontology
87
+ htm.remember("MySQL is also a relational database.")
88
+ # Tags: ["database:mysql", "database:relational"] # Reuses existing structure
89
+ ```
90
+
91
+ ### Custom Tag Extractor
92
+
93
+ Provide your own tag extraction logic:
94
+
95
+ ```ruby
96
+ HTM.configure do |config|
97
+ config.tag_extractor = lambda do |text, existing_ontology|
98
+ # Your custom logic here
99
+ # Must return Array<String>
100
+ ["custom:tag:one", "custom:tag:two"]
101
+ end
102
+ end
103
+ ```
104
+
105
+ ## Manual Tag Operations
106
+
107
+ ### Adding Tags
108
+
109
+ ```ruby
110
+ # Via remember
111
+ htm.remember("Content here", tags: ["topic:subtopic"])
112
+
113
+ # Via long-term memory directly
114
+ htm.long_term_memory.add_tag(node_id: node.id, tag: "new:tag")
115
+ ```
116
+
117
+ ### Querying Tags
118
+
119
+ ```ruby
120
+ # Get tags for a node
121
+ tags = htm.long_term_memory.node_topics(node.id)
122
+ # => ["database:postgresql", "search:vector"]
123
+
124
+ # Find nodes by tag
125
+ nodes = HTM::Models::Node.joins(:tags).where(tags: { name: "database:postgresql" })
126
+
127
+ # Find by tag prefix
128
+ nodes = HTM::Models::Node.joins(:tags).where("tags.name LIKE ?", "database:%")
129
+ ```
130
+
131
+ ### Tag Relationships
132
+
133
+ Find tags that co-occur frequently:
134
+
135
+ ```ruby
136
+ relationships = htm.long_term_memory.topic_relationships(min_shared_nodes: 2)
137
+ # => [
138
+ # { tag1: "database:postgresql", tag2: "search:vector", shared_count: 15 },
139
+ # { tag1: "programming:ruby", tag2: "web:rails", shared_count: 12 }
140
+ # ]
141
+ ```
142
+
143
+ ## Tag Visualization
144
+
145
+ ### Text Tree
146
+
147
+ ```ruby
148
+ # All tags as directory-style tree
149
+ puts HTM::Models::Tag.all.tree_string
150
+ ```
151
+
152
+ Output:
153
+ ```
154
+ database
155
+ ├── postgresql
156
+ │ ├── extensions
157
+ │ │ └── pgvector
158
+ │ └── json
159
+ ├── mysql
160
+ └── redis
161
+ programming
162
+ ├── ruby
163
+ │ ├── rails
164
+ │ └── gems
165
+ └── python
166
+ ```
167
+
168
+ ### Mermaid Flowchart
169
+
170
+ ```ruby
171
+ # Generate Mermaid diagram
172
+ mermaid = HTM::Models::Tag.all.tree_mermaid
173
+ File.write("tags.md", "```mermaid\n#{mermaid}\n```")
174
+
175
+ # Left-to-right orientation
176
+ mermaid = HTM::Models::Tag.all.tree_mermaid(direction: 'LR')
177
+ ```
178
+
179
+ ### SVG Diagram
180
+
181
+ ```ruby
182
+ # Generate SVG (dark theme, transparent background)
183
+ svg = HTM::Models::Tag.all.tree_svg
184
+ File.write("tags.svg", svg)
185
+
186
+ # With custom title
187
+ svg = HTM::Models::Tag.all.tree_svg(title: "Knowledge Taxonomy")
188
+ ```
189
+
190
+ ## Rake Tasks
191
+
192
+ ```bash
193
+ # Display text tree (all tags)
194
+ rake htm:tags:tree
195
+
196
+ # Display tags with prefix
197
+ rake 'htm:tags:tree[database]'
198
+
199
+ # Export to Mermaid format
200
+ rake htm:tags:mermaid
201
+ rake 'htm:tags:mermaid[api]'
202
+
203
+ # Export to SVG
204
+ rake htm:tags:svg
205
+ rake 'htm:tags:svg[web]'
206
+
207
+ # Export all formats
208
+ rake htm:tags:export
209
+ rake 'htm:tags:export[database]'
210
+
211
+ # Rebuild all tags (regenerate via LLM)
212
+ rake htm:tags:rebuild
213
+ ```
214
+
215
+ ## Filtering by Tags
216
+
217
+ ### In Recall
218
+
219
+ ```ruby
220
+ # Filter by specific tag
221
+ results = htm.recall("query", tags: ["database:postgresql"])
222
+
223
+ # Filter by multiple tags (AND)
224
+ results = htm.recall("query", tags: ["database:postgresql", "search:vector"])
225
+
226
+ # Combine with other filters
227
+ results = htm.recall(
228
+ "performance optimization",
229
+ tags: ["database:postgresql"],
230
+ timeframe: "last week",
231
+ strategy: :hybrid,
232
+ limit: 10
233
+ )
234
+ ```
235
+
236
+ ### Direct Queries
237
+
238
+ ```ruby
239
+ # Find all nodes with a tag
240
+ HTM::Models::Node.with_tag("database:postgresql")
241
+
242
+ # Find nodes with any of several tags
243
+ HTM::Models::Node.with_any_tags(["database:postgresql", "database:mysql"])
244
+
245
+ # Find nodes with all specified tags
246
+ HTM::Models::Node.with_all_tags(["database:postgresql", "search:vector"])
247
+ ```
248
+
249
+ ## Database Schema
250
+
251
+ ### Tags Table
252
+
253
+ ```sql
254
+ CREATE TABLE tags (
255
+ id SERIAL PRIMARY KEY,
256
+ name VARCHAR(255) NOT NULL UNIQUE,
257
+ created_at TIMESTAMP DEFAULT NOW()
258
+ );
259
+
260
+ CREATE INDEX idx_tags_name ON tags(name);
261
+ CREATE INDEX idx_tags_name_prefix ON tags USING btree (name text_pattern_ops);
262
+ ```
263
+
264
+ ### Node-Tag Association
265
+
266
+ ```sql
267
+ CREATE TABLE node_tags (
268
+ node_id INTEGER REFERENCES nodes(id),
269
+ tag_id INTEGER REFERENCES tags(id),
270
+ created_at TIMESTAMP DEFAULT NOW(),
271
+ PRIMARY KEY (node_id, tag_id)
272
+ );
273
+ ```
274
+
275
+ ## Best Practices
276
+
277
+ ### Design a Consistent Hierarchy
278
+
279
+ Plan your top-level categories:
280
+
281
+ ```
282
+ database: # Database-related
283
+ programming: # Programming languages and frameworks
284
+ api: # API design and integration
285
+ infrastructure: # DevOps, cloud, servers
286
+ concept: # Abstract concepts and patterns
287
+ ```
288
+
289
+ ### Use Appropriate Depth
290
+
291
+ - **2-3 levels**: Typical for most use cases
292
+ - **4+ levels**: Only for highly specialized domains
293
+
294
+ ```ruby
295
+ # Good
296
+ "database:postgresql:extensions"
297
+
298
+ # Too deep (usually)
299
+ "database:sql:relational:postgresql:extensions:pgvector:hnsw"
300
+ ```
301
+
302
+ ### Combine Auto and Manual Tags
303
+
304
+ ```ruby
305
+ # Let LLM extract, but add specific tags you need
306
+ htm.remember(
307
+ "PostgreSQL 16 introduces new parallel query features.",
308
+ tags: ["version:postgresql:16", "release:2023"] # Manual additions
309
+ )
310
+ # LLM will also add: ["database:postgresql", "performance:parallel"]
311
+ ```
312
+
313
+ ## Related Documentation
314
+
315
+ - [Adding Memories](adding-memories.md) - Core memory operations
316
+ - [Search Strategies](search-strategies.md) - Using tags in queries
317
+ - [Propositions](propositions.md) - Proposition nodes get tags too
318
+ - [API Reference: TagService](../api/yard/HTM/TagService.md)