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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +59 -1
- data/README.md +12 -0
- data/db/seeds.rb +1 -1
- data/docs/api/embedding-service.md +140 -110
- data/docs/api/yard/HTM/ActiveRecordConfig.md +6 -0
- data/docs/api/yard/HTM/Config.md +173 -0
- data/docs/api/yard/HTM/ConfigSection.md +28 -0
- data/docs/api/yard/HTM/Database.md +1 -1
- data/docs/api/yard/HTM/Railtie.md +2 -2
- data/docs/api/yard/HTM.md +0 -57
- data/docs/api/yard/index.csv +76 -61
- data/docs/api/yard-reference.md +2 -1
- data/docs/architecture/adrs/003-ollama-embeddings.md +45 -36
- data/docs/architecture/adrs/004-hive-mind.md +1 -1
- data/docs/architecture/adrs/008-robot-identification.md +1 -1
- data/docs/architecture/index.md +11 -9
- data/docs/architecture/overview.md +11 -7
- data/docs/assets/images/balanced-strategy-decay.svg +41 -0
- data/docs/assets/images/class-hierarchy.svg +1 -1
- data/docs/assets/images/eviction-priority.svg +43 -0
- data/docs/assets/images/exception-hierarchy.svg +2 -2
- data/docs/assets/images/hive-mind-shared-memory.svg +52 -0
- data/docs/assets/images/htm-architecture-overview.svg +3 -3
- data/docs/assets/images/htm-core-components.svg +4 -4
- data/docs/assets/images/htm-layered-architecture.svg +1 -1
- data/docs/assets/images/htm-memory-addition-flow.svg +2 -2
- data/docs/assets/images/htm-memory-recall-flow.svg +2 -2
- data/docs/assets/images/memory-topology.svg +53 -0
- data/docs/assets/images/two-tier-memory-architecture.svg +55 -0
- data/docs/development/setup.md +76 -44
- data/docs/examples/basic-usage.md +133 -0
- data/docs/examples/config-files.md +170 -0
- data/docs/examples/file-loading.md +208 -0
- data/docs/examples/index.md +116 -0
- data/docs/examples/llm-configuration.md +168 -0
- data/docs/examples/mcp-client.md +172 -0
- data/docs/examples/rails-integration.md +173 -0
- data/docs/examples/robot-groups.md +210 -0
- data/docs/examples/sinatra-integration.md +218 -0
- data/docs/examples/standalone-app.md +216 -0
- data/docs/examples/telemetry.md +224 -0
- data/docs/examples/timeframes.md +143 -0
- data/docs/getting-started/installation.md +97 -40
- data/docs/getting-started/quick-start.md +28 -11
- data/docs/guides/configuration.md +515 -0
- data/docs/guides/file-loading.md +322 -0
- data/docs/guides/getting-started.md +40 -9
- data/docs/guides/index.md +3 -3
- data/docs/guides/mcp-server.md +30 -12
- data/docs/guides/propositions.md +264 -0
- data/docs/guides/recalling-memories.md +4 -4
- data/docs/guides/search-strategies.md +3 -3
- data/docs/guides/tags.md +318 -0
- data/docs/guides/telemetry.md +229 -0
- data/docs/index.md +8 -16
- data/docs/{architecture → robots}/hive-mind.md +8 -111
- data/docs/robots/index.md +73 -0
- data/docs/{guides → robots}/multi-robot.md +3 -3
- data/docs/{guides → robots}/robot-groups.md +8 -7
- data/docs/{architecture → robots}/two-tier-memory.md +13 -149
- data/docs/robots/why-robots.md +85 -0
- data/lib/htm/config/defaults.yml +4 -4
- data/lib/htm/config.rb +2 -2
- data/lib/htm/job_adapter.rb +75 -1
- data/lib/htm/version.rb +1 -1
- data/lib/htm/workflows/remember_workflow.rb +212 -0
- data/lib/htm.rb +1 -0
- data/mkdocs.yml +33 -8
- metadata +60 -7
- data/docs/api/yard/HTM/Configuration.md +0 -240
- 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">
|
|
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
|
-
###
|
|
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
|
|
111
|
+
Embedding via RubyLLM (Ollama, OpenAI, etc.)
|
|
112
112
|
↓
|
|
113
|
-
[0.234, -0.567, 0.123, ...] ←
|
|
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
|
|
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
|
data/docs/guides/tags.md
ADDED
|
@@ -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)
|