htm 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. checksums.yaml +7 -0
  2. data/.architecture/decisions/adrs/001-use-postgresql-timescaledb-storage.md +227 -0
  3. data/.architecture/decisions/adrs/002-two-tier-memory-architecture.md +322 -0
  4. data/.architecture/decisions/adrs/003-ollama-default-embedding-provider.md +339 -0
  5. data/.architecture/decisions/adrs/004-multi-robot-shared-memory-hive-mind.md +374 -0
  6. data/.architecture/decisions/adrs/005-rag-based-retrieval-with-hybrid-search.md +443 -0
  7. data/.architecture/decisions/adrs/006-context-assembly-strategies.md +444 -0
  8. data/.architecture/decisions/adrs/007-working-memory-eviction-strategy.md +461 -0
  9. data/.architecture/decisions/adrs/008-robot-identification-system.md +550 -0
  10. data/.architecture/decisions/adrs/009-never-forget-explicit-deletion-only.md +570 -0
  11. data/.architecture/decisions/adrs/010-redis-working-memory-rejected.md +323 -0
  12. data/.architecture/decisions/adrs/011-database-side-embedding-generation-with-pgai.md +585 -0
  13. data/.architecture/decisions/adrs/012-llm-driven-ontology-topic-extraction.md +583 -0
  14. data/.architecture/decisions/adrs/013-activerecord-orm-and-many-to-many-tagging.md +299 -0
  15. data/.architecture/decisions/adrs/014-client-side-embedding-generation-workflow.md +569 -0
  16. data/.architecture/decisions/adrs/015-hierarchical-tag-ontology-and-llm-extraction.md +701 -0
  17. data/.architecture/decisions/adrs/016-async-embedding-and-tag-generation.md +694 -0
  18. data/.architecture/members.yml +144 -0
  19. data/.architecture/reviews/2025-10-29-llm-configuration-and-async-processing-review.md +1137 -0
  20. data/.architecture/reviews/initial-system-analysis.md +330 -0
  21. data/.envrc +32 -0
  22. data/.irbrc +145 -0
  23. data/CHANGELOG.md +150 -0
  24. data/COMMITS.md +196 -0
  25. data/LICENSE +21 -0
  26. data/README.md +1347 -0
  27. data/Rakefile +51 -0
  28. data/SETUP.md +268 -0
  29. data/config/database.yml +67 -0
  30. data/db/migrate/20250101000001_enable_extensions.rb +14 -0
  31. data/db/migrate/20250101000002_create_robots.rb +14 -0
  32. data/db/migrate/20250101000003_create_nodes.rb +42 -0
  33. data/db/migrate/20250101000005_create_tags.rb +38 -0
  34. data/db/migrate/20250101000007_add_node_vector_indexes.rb +30 -0
  35. data/db/schema.sql +473 -0
  36. data/db/seed_data/README.md +100 -0
  37. data/db/seed_data/presidents.md +136 -0
  38. data/db/seed_data/states.md +151 -0
  39. data/db/seeds.rb +208 -0
  40. data/dbdoc/README.md +173 -0
  41. data/dbdoc/public.node_stats.md +48 -0
  42. data/dbdoc/public.node_stats.svg +41 -0
  43. data/dbdoc/public.node_tags.md +40 -0
  44. data/dbdoc/public.node_tags.svg +112 -0
  45. data/dbdoc/public.nodes.md +54 -0
  46. data/dbdoc/public.nodes.svg +118 -0
  47. data/dbdoc/public.nodes_tags.md +39 -0
  48. data/dbdoc/public.nodes_tags.svg +112 -0
  49. data/dbdoc/public.ontology_structure.md +48 -0
  50. data/dbdoc/public.ontology_structure.svg +38 -0
  51. data/dbdoc/public.operations_log.md +42 -0
  52. data/dbdoc/public.operations_log.svg +130 -0
  53. data/dbdoc/public.relationships.md +39 -0
  54. data/dbdoc/public.relationships.svg +41 -0
  55. data/dbdoc/public.robot_activity.md +46 -0
  56. data/dbdoc/public.robot_activity.svg +35 -0
  57. data/dbdoc/public.robots.md +35 -0
  58. data/dbdoc/public.robots.svg +90 -0
  59. data/dbdoc/public.schema_migrations.md +29 -0
  60. data/dbdoc/public.schema_migrations.svg +26 -0
  61. data/dbdoc/public.tags.md +35 -0
  62. data/dbdoc/public.tags.svg +60 -0
  63. data/dbdoc/public.topic_relationships.md +45 -0
  64. data/dbdoc/public.topic_relationships.svg +32 -0
  65. data/dbdoc/schema.json +1437 -0
  66. data/dbdoc/schema.svg +154 -0
  67. data/docs/api/database.md +806 -0
  68. data/docs/api/embedding-service.md +532 -0
  69. data/docs/api/htm.md +797 -0
  70. data/docs/api/index.md +259 -0
  71. data/docs/api/long-term-memory.md +1096 -0
  72. data/docs/api/working-memory.md +665 -0
  73. data/docs/architecture/adrs/001-postgresql-timescaledb.md +314 -0
  74. data/docs/architecture/adrs/002-two-tier-memory.md +411 -0
  75. data/docs/architecture/adrs/003-ollama-embeddings.md +421 -0
  76. data/docs/architecture/adrs/004-hive-mind.md +437 -0
  77. data/docs/architecture/adrs/005-rag-retrieval.md +531 -0
  78. data/docs/architecture/adrs/006-context-assembly.md +496 -0
  79. data/docs/architecture/adrs/007-eviction-strategy.md +645 -0
  80. data/docs/architecture/adrs/008-robot-identification.md +625 -0
  81. data/docs/architecture/adrs/009-never-forget.md +648 -0
  82. data/docs/architecture/adrs/010-redis-working-memory-rejected.md +323 -0
  83. data/docs/architecture/adrs/011-pgai-integration.md +494 -0
  84. data/docs/architecture/adrs/index.md +215 -0
  85. data/docs/architecture/hive-mind.md +736 -0
  86. data/docs/architecture/index.md +351 -0
  87. data/docs/architecture/overview.md +538 -0
  88. data/docs/architecture/two-tier-memory.md +873 -0
  89. data/docs/assets/css/custom.css +83 -0
  90. data/docs/assets/images/htm-core-components.svg +63 -0
  91. data/docs/assets/images/htm-database-schema.svg +93 -0
  92. data/docs/assets/images/htm-hive-mind-architecture.svg +125 -0
  93. data/docs/assets/images/htm-importance-scoring-framework.svg +83 -0
  94. data/docs/assets/images/htm-layered-architecture.svg +71 -0
  95. data/docs/assets/images/htm-long-term-memory-architecture.svg +115 -0
  96. data/docs/assets/images/htm-working-memory-architecture.svg +120 -0
  97. data/docs/assets/images/htm.jpg +0 -0
  98. data/docs/assets/images/htm_demo.gif +0 -0
  99. data/docs/assets/js/mathjax.js +18 -0
  100. data/docs/assets/videos/htm_video.mp4 +0 -0
  101. data/docs/database_rake_tasks.md +322 -0
  102. data/docs/development/contributing.md +787 -0
  103. data/docs/development/index.md +336 -0
  104. data/docs/development/schema.md +596 -0
  105. data/docs/development/setup.md +719 -0
  106. data/docs/development/testing.md +819 -0
  107. data/docs/guides/adding-memories.md +824 -0
  108. data/docs/guides/context-assembly.md +1009 -0
  109. data/docs/guides/getting-started.md +577 -0
  110. data/docs/guides/index.md +118 -0
  111. data/docs/guides/long-term-memory.md +941 -0
  112. data/docs/guides/multi-robot.md +866 -0
  113. data/docs/guides/recalling-memories.md +927 -0
  114. data/docs/guides/search-strategies.md +953 -0
  115. data/docs/guides/working-memory.md +717 -0
  116. data/docs/index.md +214 -0
  117. data/docs/installation.md +477 -0
  118. data/docs/multi_framework_support.md +519 -0
  119. data/docs/quick-start.md +655 -0
  120. data/docs/setup_local_database.md +302 -0
  121. data/docs/using_rake_tasks_in_your_app.md +383 -0
  122. data/examples/basic_usage.rb +93 -0
  123. data/examples/cli_app/README.md +317 -0
  124. data/examples/cli_app/htm_cli.rb +270 -0
  125. data/examples/custom_llm_configuration.rb +183 -0
  126. data/examples/example_app/Rakefile +71 -0
  127. data/examples/example_app/app.rb +206 -0
  128. data/examples/sinatra_app/Gemfile +21 -0
  129. data/examples/sinatra_app/app.rb +335 -0
  130. data/lib/htm/active_record_config.rb +113 -0
  131. data/lib/htm/configuration.rb +342 -0
  132. data/lib/htm/database.rb +594 -0
  133. data/lib/htm/embedding_service.rb +115 -0
  134. data/lib/htm/errors.rb +34 -0
  135. data/lib/htm/job_adapter.rb +154 -0
  136. data/lib/htm/jobs/generate_embedding_job.rb +65 -0
  137. data/lib/htm/jobs/generate_tags_job.rb +82 -0
  138. data/lib/htm/long_term_memory.rb +965 -0
  139. data/lib/htm/models/node.rb +109 -0
  140. data/lib/htm/models/node_tag.rb +33 -0
  141. data/lib/htm/models/robot.rb +52 -0
  142. data/lib/htm/models/tag.rb +76 -0
  143. data/lib/htm/railtie.rb +76 -0
  144. data/lib/htm/sinatra.rb +157 -0
  145. data/lib/htm/tag_service.rb +135 -0
  146. data/lib/htm/tasks.rb +38 -0
  147. data/lib/htm/version.rb +5 -0
  148. data/lib/htm/working_memory.rb +182 -0
  149. data/lib/htm.rb +400 -0
  150. data/lib/tasks/db.rake +19 -0
  151. data/lib/tasks/htm.rake +147 -0
  152. data/lib/tasks/jobs.rake +312 -0
  153. data/mkdocs.yml +190 -0
  154. data/scripts/install_local_database.sh +309 -0
  155. metadata +341 -0
@@ -0,0 +1,583 @@
1
+ # ADR-012: LLM-Driven Ontology Topic Extraction
2
+
3
+ **Status**: ~~Accepted~~ **PARTIALLY SUPERSEDED** (2025-10-27)
4
+
5
+ **Date**: 2025-10-26
6
+
7
+ **Decision Makers**: Dewayne VanHoozer, Claude (Anthropic)
8
+
9
+ ---
10
+
11
+ ## ⚠️ IMPLEMENTATION STATUS (2025-10-27)
12
+
13
+ **Database-side topic extraction via pgai has been removed** following the reversal of ADR-011.
14
+
15
+ **What Remains**:
16
+ - The ontology concept (hierarchical topics in `root:level1:level2` format)
17
+ - Database schema support (tags table, ontology views)
18
+ - Manual topic tagging capability
19
+
20
+ **What Was Removed**:
21
+ - Automatic LLM-driven topic extraction via pgai triggers
22
+ - Database-side LLM calls using `ai.ollama_generate()`
23
+ - Automatic topic generation on INSERT/UPDATE
24
+
25
+ **Future Consideration**: Client-side LLM topic extraction may be implemented in the future, but the database-side approach proved impractical due to pgai installation issues (see ADR-011 reversal).
26
+
27
+ **Related Change (2025-10-28)**: The TimescaleDB extension was also removed from HTM as it was not providing sufficient value for proof-of-concept applications. See [ADR-001](001-use-postgresql-timescaledb-storage.md) for details.
28
+
29
+ ---
30
+
31
+ ## Original Quick Summary (Historical)
32
+
33
+ HTM uses **LLM-driven hierarchical topic extraction** via pgai database triggers to automatically generate ontological tags from node content, creating an emergent knowledge structure.
34
+
35
+ **Why**: Automatic topic extraction builds a browsable ontology that complements vector embeddings, enabling both structured navigation and semantic discovery. Database-side extraction via pgai follows the same proven pattern as embedding generation (ADR-011).
36
+
37
+ **Impact**: Nodes automatically tagged with hierarchical topics (e.g., `database:postgresql:performance`), enabling ontology-based navigation alongside vector similarity search.
38
+
39
+ ---
40
+
41
+ ## Context
42
+
43
+ ### Problem: Limited Content Organization
44
+
45
+ HTM currently provides two organization mechanisms:
46
+
47
+ 1. **Vector embeddings** (ADR-011): Semantic similarity search
48
+ - Excellent for: "Find things like this"
49
+ - Limitation: No visible structure, discovery-only
50
+
51
+ 2. **Flat category field**: Single-level classification
52
+ - Simple but limiting: Cannot represent hierarchies
53
+ - Inconsistent: Manual categorization prone to errors
54
+ - Redundant: Duplicates root-level topic information
55
+
56
+ ### The Ontology Gap
57
+
58
+ **Symbolic vs. Sub-symbolic Retrieval**:
59
+
60
+ | Aspect | Vector Embeddings (Sub-symbolic) | Ontological Tags (Symbolic) |
61
+ |--------|----------------------------------|----------------------------|
62
+ | **Nature** | Implicit semantic representation | Explicit hierarchical structure |
63
+ | **Structure** | No visible structure | Browsable hierarchy |
64
+ | **Retrieval** | Fuzzy, associative | Precise, categorical |
65
+ | **Use Case** | "Find similar content" | "Show me all about X" |
66
+ | **Interpretability** | Opaque numbers | Clear semantic meaning |
67
+
68
+ **They are complementary, not redundant**:
69
+ - Tags = Vision (see the structure)
70
+ - Embeddings = Intuition (feel the similarity)
71
+
72
+ ### Hierarchical Topics Concept
73
+
74
+ Format: `root:level1:level2:level3...`
75
+
76
+ Examples:
77
+ ```
78
+ database:postgresql:timescaledb:hypertables
79
+ database:postgresql:pgvector:indexes
80
+ ai:embeddings:ollama:nomic-embed-text
81
+ ai:llm:providers:anthropic
82
+ programming:ruby:gems:htm
83
+ performance:optimization:database
84
+ ```
85
+
86
+ Benefits:
87
+ - **Multiple classification paths**: Same content can have multiple topic hierarchies
88
+ - **Browsable structure**: Navigate from broad to specific
89
+ - **Semantic relationships**: Hierarchy encodes is-a relationships
90
+ - **Machine-readable**: Colon-separated format enables programmatic processing
91
+
92
+ ---
93
+
94
+ ## Decision
95
+
96
+ We will implement **automatic LLM-driven topic extraction** using pgai database triggers, following the same architectural pattern as embedding generation (ADR-011).
97
+
98
+ ### Implementation Strategy
99
+
100
+ **1. Database Trigger for Topic Extraction**
101
+
102
+ ```sql
103
+ CREATE OR REPLACE FUNCTION extract_ontology_topics()
104
+ RETURNS TRIGGER AS $$
105
+ DECLARE
106
+ topic_provider TEXT;
107
+ topic_model TEXT;
108
+ base_url TEXT;
109
+ llm_prompt TEXT;
110
+ llm_response TEXT;
111
+ extracted_topics TEXT[];
112
+ topic TEXT;
113
+ BEGIN
114
+ -- Get configuration from session variables
115
+ topic_provider := COALESCE(current_setting('htm.topic_provider', true), 'ollama');
116
+ topic_model := COALESCE(current_setting('htm.topic_model', true), 'llama3');
117
+ base_url := COALESCE(current_setting('htm.topic_base_url', true), 'http://localhost:11434');
118
+
119
+ -- Build prompt for LLM
120
+ llm_prompt := 'Extract hierarchical topic tags from this text.
121
+ Format as colon-separated paths (e.g., database:postgresql:performance).
122
+ Use lowercase with hyphens for multi-word terms.
123
+ Return ONLY the topic tags, one per line, no explanations.
124
+ Maximum depth: 5 levels.
125
+
126
+ Text: ' || NEW.value;
127
+
128
+ -- Call LLM via pgai to extract topics
129
+ IF topic_provider = 'ollama' THEN
130
+ llm_response := ai.ollama_generate(
131
+ topic_model,
132
+ llm_prompt,
133
+ system_prompt => 'You are a precise topic extraction system. Output only topic tags in the format root:subtopic:detail.',
134
+ host => base_url
135
+ )->>'response';
136
+ END IF;
137
+
138
+ -- Parse response and insert validated topics into tags table
139
+ extracted_topics := string_to_array(trim(llm_response), E'\n');
140
+
141
+ FOREACH topic IN ARRAY extracted_topics LOOP
142
+ topic := trim(topic);
143
+ IF topic ~ '^[a-z0-9\-]+(:[a-z0-9\-]+)*$' THEN
144
+ INSERT INTO tags (node_id, tag, created_at)
145
+ VALUES (NEW.id, topic, CURRENT_TIMESTAMP)
146
+ ON CONFLICT (node_id, tag) DO NOTHING;
147
+ END IF;
148
+ END LOOP;
149
+
150
+ RETURN NEW;
151
+ EXCEPTION
152
+ WHEN OTHERS THEN
153
+ RAISE WARNING 'Topic extraction failed for node %: %', NEW.id, SQLERRM;
154
+ RETURN NEW;
155
+ END;
156
+ $$ LANGUAGE plpgsql;
157
+
158
+ CREATE TRIGGER nodes_extract_topics
159
+ AFTER INSERT OR UPDATE OF value ON nodes
160
+ FOR EACH ROW
161
+ EXECUTE FUNCTION extract_ontology_topics();
162
+ ```
163
+
164
+ **2. Configuration via Session Variables**
165
+
166
+ ```ruby
167
+ # In LongTermMemory#initialize
168
+ def configure_topic_settings(conn)
169
+ provider = ENV.fetch('HTM_TOPIC_PROVIDER', 'ollama')
170
+ model = ENV.fetch('HTM_TOPIC_MODEL', 'llama3')
171
+ base_url = ENV.fetch('HTM_TOPIC_BASE_URL', 'http://localhost:11434')
172
+
173
+ conn.exec_params("SELECT set_config('htm.topic_provider', $1, false)", [provider])
174
+ conn.exec_params("SELECT set_config('htm.topic_model', $1, false)", [model])
175
+ conn.exec_params("SELECT set_config('htm.topic_base_url', $1, false)", [base_url])
176
+ end
177
+ ```
178
+
179
+ **3. Environment Variables**
180
+
181
+ ```bash
182
+ # .envrc
183
+ export HTM_TOPIC_PROVIDER=ollama
184
+ export HTM_TOPIC_MODEL=llama3
185
+ export HTM_TOPIC_BASE_URL=http://localhost:11434
186
+ ```
187
+
188
+ **4. Ontology Exploration Views**
189
+
190
+ ```sql
191
+ -- View the ontology structure
192
+ CREATE OR REPLACE VIEW ontology_structure AS
193
+ SELECT
194
+ split_part(tag, ':', 1) AS root_topic,
195
+ split_part(tag, ':', 2) AS level1_topic,
196
+ split_part(tag, ':', 3) AS level2_topic,
197
+ tag AS full_path,
198
+ COUNT(DISTINCT node_id) AS node_count
199
+ FROM tags
200
+ WHERE tag ~ '^[a-z0-9\-]+(:[a-z0-9\-]+)*$'
201
+ GROUP BY tag
202
+ ORDER BY root_topic, level1_topic, level2_topic;
203
+
204
+ -- View topic relationships (co-occurrence)
205
+ CREATE OR REPLACE VIEW topic_relationships AS
206
+ SELECT
207
+ t1.tag AS topic1,
208
+ t2.tag AS topic2,
209
+ COUNT(DISTINCT t1.node_id) AS shared_nodes
210
+ FROM tags t1
211
+ JOIN tags t2 ON t1.node_id = t2.node_id AND t1.tag < t2.tag
212
+ GROUP BY t1.tag, t2.tag
213
+ HAVING COUNT(DISTINCT t1.node_id) >= 2
214
+ ORDER BY shared_nodes DESC;
215
+ ```
216
+
217
+ ---
218
+
219
+ ## Rationale
220
+
221
+ ### Why LLM-Driven Extraction?
222
+
223
+ **Consistency**:
224
+ - LLM applies uniform reasoning about topic hierarchies
225
+ - No human inconsistency in categorization
226
+ - Same text always produces same topics (with temperature=0)
227
+
228
+ **Depth**:
229
+ - Identifies topics at multiple abstraction levels simultaneously
230
+ - Captures nuanced semantic relationships
231
+ - Represents complex subjects with multiple classification paths
232
+
233
+ **Discovery**:
234
+ - Finds classifications humans might miss
235
+ - Identifies cross-domain connections
236
+ - Reveals implicit semantic structure
237
+
238
+ **Evolution**:
239
+ - Ontology emerges organically from collective knowledge
240
+ - No manual taxonomy maintenance
241
+ - Adapts to new domains automatically
242
+
243
+ ### Why Database-Side via pgai?
244
+
245
+ Following the proven pattern from ADR-011 (embedding generation):
246
+
247
+ **Performance**:
248
+ - No Ruby HTTP overhead
249
+ - Connection reuse to LLM provider
250
+ - Parallel execution via connection pool
251
+
252
+ **Simplicity**:
253
+ - Automatic on INSERT/UPDATE
254
+ - No application-side topic management
255
+ - Fewer bugs (can't forget to extract topics)
256
+
257
+ **Consistency**:
258
+ - Same LLM model for all operations
259
+ - Centralized configuration
260
+ - Automatic for all nodes
261
+
262
+ ### Complementary to Vector Embeddings
263
+
264
+ **Use Both Together**:
265
+
266
+ 1. **Filtered Similarity**: "Find similar nodes, but only within `database:postgresql`"
267
+ - Topic filtering provides scope
268
+ - Embedding search finds semantic matches within scope
269
+
270
+ 2. **Cross-Topic Discovery**: "This node is about database performance, find similar concepts in other domains"
271
+ - Embedding search finds semantically similar concepts
272
+ - Topic tags reveal they're in unexpected domains (e.g., `ai:model-optimization`)
273
+
274
+ 3. **Ontology Validation**:
275
+ - If nodes with very different topics have similar embeddings
276
+ - Suggests ontology might need refinement
277
+ - Indicates concepts are more related than tags suggest
278
+
279
+ ---
280
+
281
+ ## Consequences
282
+
283
+ ### Positive
284
+
285
+ - **Emergent ontology**: Knowledge structure discovered through AI analysis
286
+ - **Automatic tagging**: No manual categorization required
287
+ - **Hierarchical navigation**: Browse from broad to specific
288
+ - **Multi-perspective**: Same content tagged from different angles
289
+ - **Complementary to embeddings**: Symbolic + sub-symbolic retrieval
290
+ - **Pattern recognition**: Ontology reveals knowledge base emphasis
291
+ - **Cross-pollination**: Unexpected connections across topics
292
+ - **Consistency**: LLM applies uniform topic extraction logic
293
+
294
+ ### Negative
295
+
296
+ - **LLM dependency**: Requires running LLM (Ollama/OpenAI)
297
+ - **Cost**: API calls for topic extraction (if using OpenAI)
298
+ - **Latency**: LLM call adds time to INSERT operations
299
+ - **Quality variation**: LLM mistakes in topic extraction
300
+ - **Ontology churn**: Inconsistent topics if LLM behavior changes
301
+ - **Debugging complexity**: Topic extraction errors in database triggers
302
+
303
+ ### Neutral
304
+
305
+ - **Configuration**: Environment variables for topic settings
306
+ - **Storage**: Topics stored in existing tags table
307
+ - **Category field**: Becomes redundant with root topics (future: remove)
308
+
309
+ ---
310
+
311
+ ## Migration Path
312
+
313
+ ### For New Installations
314
+
315
+ ```bash
316
+ # 1. Set environment variables
317
+ export HTM_TOPIC_PROVIDER=ollama
318
+ export HTM_TOPIC_MODEL=llama3
319
+ export HTM_TOPIC_BASE_URL=http://localhost:11434
320
+
321
+ # 2. Run migrations
322
+ HTM::Database.migrate
323
+
324
+ # 3. Use HTM normally - topics extracted automatically!
325
+ htm.add_node('memory_001', 'PostgreSQL with TimescaleDB handles time-series data efficiently')
326
+ # Topics automatically extracted: database:postgresql, database:timescaledb, performance:time-series
327
+ ```
328
+
329
+ ### For Existing Installations
330
+
331
+ ```bash
332
+ # 1. Run migration to add topic extraction trigger
333
+ HTM::Database.migrate
334
+
335
+ # 2. (Optional) Re-extract topics for existing nodes
336
+ psql $HTM_DBURL -c "UPDATE nodes SET value = value;"
337
+ # Triggers topic extraction for all existing nodes
338
+ ```
339
+
340
+ ---
341
+
342
+ ## Risks and Mitigations
343
+
344
+ ### Risk: LLM Produces Inconsistent Topics
345
+
346
+ **Likelihood**: Medium (LLM output varies)
347
+
348
+ **Impact**: Medium (ontology quality degrades)
349
+
350
+ **Mitigation**:
351
+ - Use temperature=0 for deterministic output
352
+ - Validate topic format with regex before inserting
353
+ - Log invalid topics as warnings
354
+ - Provide ontology refinement tools (future)
355
+
356
+ ### Risk: Topic Extraction Fails
357
+
358
+ **Likelihood**: Medium (LLM downtime, errors)
359
+
360
+ **Impact**: Low (node insert succeeds without topics)
361
+
362
+ **Mitigation**:
363
+ - Exception handler in trigger (warn but don't fail)
364
+ - Retry logic for transient failures (future)
365
+ - Batch re-extraction for failed nodes (future)
366
+
367
+ ### Risk: Performance Degradation on Bulk Inserts
368
+
369
+ **Likelihood**: High (LLM call per node)
370
+
371
+ **Impact**: High (batch operations much slower)
372
+
373
+ **Mitigation**:
374
+ - Disable trigger for bulk imports (ALTER TABLE ... DISABLE TRIGGER)
375
+ - Batch topic extraction after import
376
+ - Document bulk import best practices
377
+
378
+ ### Risk: Ontology Quality Issues
379
+
380
+ **Likelihood**: Medium (LLM interpretation varies)
381
+
382
+ **Impact**: Medium (navigation less useful)
383
+
384
+ **Mitigation**:
385
+ - Provide ontology visualization tools
386
+ - Enable manual topic correction/refinement
387
+ - Ontology review and consolidation process (future)
388
+ - Allow providing existing ontology context to LLM (future)
389
+
390
+ ---
391
+
392
+ ## Future Enhancements
393
+
394
+ ### 1. Ontology-Aware Extraction
395
+
396
+ ```sql
397
+ -- Provide existing ontology to LLM for consistency
398
+ llm_prompt := 'Existing topics in ontology: ' || existing_topics_summary || '
399
+
400
+ Extract hierarchical topics for this text...';
401
+ ```
402
+
403
+ ### 2. Topic Confidence Scores
404
+
405
+ ```sql
406
+ -- Store confidence alongside topics
407
+ ALTER TABLE tags ADD COLUMN confidence REAL;
408
+ INSERT INTO tags (node_id, tag, confidence) VALUES (...);
409
+ ```
410
+
411
+ ### 3. Ontology Refinement Tools
412
+
413
+ ```ruby
414
+ class OntologyRefiner
415
+ def suggest_merges # Identify similar topic branches to merge
416
+ def detect_orphans # Find single-use topics to consolidate
417
+ def validate_hierarchy # Check for hierarchy inconsistencies
418
+ end
419
+ ```
420
+
421
+ ### 4. Category Field Removal
422
+
423
+ ```sql
424
+ -- Remove redundant category field
425
+ ALTER TABLE nodes DROP COLUMN category;
426
+
427
+ -- Derive category from root topic
428
+ CREATE VIEW nodes_with_category AS
429
+ SELECT *, split_part(tags.tag, ':', 1) AS category
430
+ FROM nodes
431
+ LEFT JOIN tags ON nodes.id = tags.node_id;
432
+ ```
433
+
434
+ ### 5. Batch Topic Extraction Mode
435
+
436
+ ```sql
437
+ -- Async topic extraction for performance
438
+ CREATE TABLE topic_extraction_queue (
439
+ node_id BIGINT,
440
+ status TEXT,
441
+ created_at TIMESTAMP
442
+ );
443
+
444
+ -- Background worker processes queue
445
+ ```
446
+
447
+ ---
448
+
449
+ ## Alternatives Comparison
450
+
451
+ | Approach | Quality | Performance | Maintainability | Decision |
452
+ |----------|---------|-------------|-----------------|----------|
453
+ | **LLM Database Triggers** | **High** | **Medium** | **Good** | **ACCEPTED** |
454
+ | Manual categorization | Low | Fast | Poor | Rejected |
455
+ | Keyword extraction | Medium | Fast | Good | Rejected |
456
+ | Topic modeling (LDA) | Medium | Slow | Poor | Rejected |
457
+ | Ruby-side LLM extraction | High | Slow | Medium | Rejected |
458
+
459
+ ---
460
+
461
+ ## References
462
+
463
+ - [ontology_notes.md](../../../ontology_notes.md) - Detailed ontology design discussion
464
+ - [ADR-011: Database-Side Embedding Generation](011-database-side-embedding-generation-with-pgai.md) - Same architectural pattern
465
+ - [pgai Documentation](https://github.com/timescale/pgai) - LLM integration via pgai
466
+ - [Knowledge Graph Concepts](https://en.wikipedia.org/wiki/Ontology_(information_science))
467
+ - [Folksonomy](https://en.wikipedia.org/wiki/Folksonomy) - Emergent classification systems
468
+
469
+ ---
470
+
471
+ ## Review Notes
472
+
473
+ **AI Engineer**: LLM-driven ontology is the right approach. Emergent structure beats manual taxonomy.
474
+
475
+ **Database Architect**: Following ADR-011 pattern is smart. Consider async extraction for bulk operations.
476
+
477
+ **Knowledge Engineer**: Hierarchical topics + vector embeddings = powerful combination. Validates ontology with similarity.
478
+
479
+ **Systems Architect**: Database-side extraction maintains consistency. Separation of concerns.
480
+
481
+ **Ruby Developer**: Environment variable configuration is clean. Session variables pattern works well.
482
+
483
+ ---
484
+
485
+ ## Related ADRs
486
+
487
+ Updates:
488
+ - [ADR-011: Database-Side Embedding Generation](011-database-side-embedding-generation-with-pgai.md) - Same architectural pattern
489
+
490
+ Future:
491
+ - ADR-013: Category Field Removal (pending - after ontology validation)
492
+
493
+ ---
494
+
495
+ ## Reversal Details (2025-10-27)
496
+
497
+ ### Why the Partial Reversal?
498
+
499
+ Following the reversal of ADR-011, the pgai-based automatic topic extraction was also removed because:
500
+
501
+ 1. **Dependency on pgai**: Topic extraction relied on `ai.ollama_generate()` function from pgai
502
+ 2. **Installation Issues**: Same pgai installation problems that affected embedding generation
503
+ 3. **Consistency**: Better to have a unified approach (no database-side LLM calls)
504
+ 4. **Architectural Simplicity**: Avoid split between database-side and client-side processing
505
+
506
+ ### What Remains
507
+
508
+ The **ontology concept is still valid and valuable**:
509
+ - Tags table supports hierarchical topic paths (`root:level1:level2`)
510
+ - Ontology structure views provide navigation
511
+ - Manual topic tagging works
512
+ - Applications can still implement topic extraction client-side
513
+
514
+ ### Future Direction: Client-Side Topic Extraction
515
+
516
+ A future implementation may add client-side LLM-driven topic extraction:
517
+
518
+ ```ruby
519
+ class TopicExtractor
520
+ def initialize(llm_provider: :ollama, model: 'llama3')
521
+ @provider = llm_provider
522
+ @model = model
523
+ end
524
+
525
+ def extract_topics(content)
526
+ # Generate prompt
527
+ prompt = build_extraction_prompt(content)
528
+
529
+ # Call LLM (Ruby-side)
530
+ response = call_llm(prompt)
531
+
532
+ # Parse and validate topics
533
+ parse_topics(response)
534
+ end
535
+
536
+ private
537
+
538
+ def build_extraction_prompt(content)
539
+ <<~PROMPT
540
+ Extract hierarchical topic tags from this text.
541
+ Format: root:level1:level2 (lowercase, hyphens for spaces)
542
+ Maximum depth: 5 levels
543
+
544
+ Text: #{content}
545
+ PROMPT
546
+ end
547
+ end
548
+
549
+ # Usage in HTM
550
+ topics = topic_extractor.extract_topics(content)
551
+ htm.add_message(content, tags: topics, ...)
552
+ ```
553
+
554
+ **Benefits of Client-Side Approach**:
555
+ - ✅ No database extension dependencies
556
+ - ✅ Easier debugging (errors in Ruby)
557
+ - ✅ More flexible (can modify extraction logic easily)
558
+ - ✅ Works on all platforms
559
+ - ✅ Can provide context (existing ontology) to LLM more easily
560
+
561
+ **Trade-offs**:
562
+ - ❌ Slightly slower (HTTP call from Ruby)
563
+ - ❌ Not automatic on UPDATE (must be called explicitly)
564
+ - ❌ Application must manage topic extraction lifecycle
565
+
566
+ ### Current Recommendation
567
+
568
+ **For now**: Use manual topic tagging via the `tags` parameter:
569
+ ```ruby
570
+ htm.add_message(
571
+ "PostgreSQL with TimescaleDB handles time-series efficiently",
572
+ tags: ["database:postgresql", "database:timescaledb", "performance:time-series"]
573
+ )
574
+ ```
575
+
576
+ **Future**: Implement optional client-side topic extraction for automatic tagging
577
+
578
+ ---
579
+
580
+ ## Changelog
581
+
582
+ - **2025-10-27**: **DATABASE-SIDE EXTRACTION REMOVED** - Removed pgai-based automatic extraction following ADR-011 reversal. Ontology concept and schema remain.
583
+ - **2025-10-26**: Initial version - LLM-driven ontology topic extraction with pgai triggers