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,1009 @@
|
|
|
1
|
+
# Context Assembly
|
|
2
|
+
|
|
3
|
+
Context assembly is the process of converting working memory into a formatted string that can be used with your LLM. This guide covers the three assembly strategies, optimization techniques, and best practices for creating high-quality context.
|
|
4
|
+
|
|
5
|
+
## What is Context Assembly?
|
|
6
|
+
|
|
7
|
+
Context assembly transforms working memory into LLM-ready context:
|
|
8
|
+
|
|
9
|
+
<svg viewBox="0 0 900 550" xmlns="http://www.w3.org/2000/svg" style="background: transparent;">
|
|
10
|
+
<defs>
|
|
11
|
+
<style>
|
|
12
|
+
.box { fill: rgba(33, 150, 243, 0.2); stroke: #2196F3; stroke-width: 2; }
|
|
13
|
+
.strategy-box { fill: rgba(76, 175, 80, 0.2); stroke: #4CAF50; stroke-width: 2; }
|
|
14
|
+
.output-box { fill: rgba(255, 152, 0, 0.2); stroke: #FF9800; stroke-width: 2; }
|
|
15
|
+
.llm-box { fill: rgba(156, 39, 176, 0.2); stroke: #9C27B0; stroke-width: 3; }
|
|
16
|
+
.text-header { fill: #E0E0E0; font-size: 16px; font-weight: bold; }
|
|
17
|
+
.text-label { fill: #E0E0E0; font-size: 13px; }
|
|
18
|
+
.text-small { fill: #B0B0B0; font-size: 11px; }
|
|
19
|
+
.arrow { stroke: #4A9EFF; stroke-width: 2; fill: none; marker-end: url(#arrowhead); }
|
|
20
|
+
</style>
|
|
21
|
+
<marker id="arrowhead" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
|
|
22
|
+
<polygon points="0 0, 10 3, 0 6" fill="#4A9EFF" />
|
|
23
|
+
</marker>
|
|
24
|
+
</defs>
|
|
25
|
+
|
|
26
|
+
<!-- Title -->
|
|
27
|
+
<text x="450" y="25" text-anchor="middle" class="text-header" fill="#E0E0E0">Context Assembly Process</text>
|
|
28
|
+
|
|
29
|
+
<!-- Working Memory Box -->
|
|
30
|
+
<rect x="50" y="60" width="200" height="200" class="box" rx="5"/>
|
|
31
|
+
<text x="150" y="85" text-anchor="middle" class="text-header">Working Memory</text>
|
|
32
|
+
<text x="150" y="105" text-anchor="middle" class="text-small">(Nodes)</text>
|
|
33
|
+
|
|
34
|
+
<text x="60" y="135" class="text-label">Node 1</text>
|
|
35
|
+
<text x="180" y="135" text-anchor="end" class="text-small">1000 tok</text>
|
|
36
|
+
<text x="60" y="155" class="text-label">Node 2</text>
|
|
37
|
+
<text x="180" y="155" text-anchor="end" class="text-small">500 tok</text>
|
|
38
|
+
<text x="60" y="175" class="text-label">Node 3</text>
|
|
39
|
+
<text x="180" y="175" text-anchor="end" class="text-small">2000 tok</text>
|
|
40
|
+
<text x="60" y="195" class="text-label">Node 4</text>
|
|
41
|
+
<text x="180" y="195" text-anchor="end" class="text-small">800 tok</text>
|
|
42
|
+
<text x="60" y="215" class="text-label">Node 5</text>
|
|
43
|
+
<text x="180" y="215" text-anchor="end" class="text-small">1200 tok</text>
|
|
44
|
+
<text x="60" y="235" class="text-label">...</text>
|
|
45
|
+
|
|
46
|
+
<!-- Assembly Strategies -->
|
|
47
|
+
<rect x="330" y="60" width="240" height="200" class="strategy-box" rx="5"/>
|
|
48
|
+
<text x="450" y="85" text-anchor="middle" class="text-header">Assembly Strategy</text>
|
|
49
|
+
|
|
50
|
+
<!-- Strategy 1: Recent -->
|
|
51
|
+
<rect x="340" y="100" width="220" height="35" fill="rgba(76, 175, 80, 0.3)" rx="3"/>
|
|
52
|
+
<text x="350" y="120" class="text-label">:recent</text>
|
|
53
|
+
<text x="550" y="120" text-anchor="end" class="text-small">Sort by access time</text>
|
|
54
|
+
|
|
55
|
+
<!-- Strategy 2: Important -->
|
|
56
|
+
<rect x="340" y="145" width="220" height="35" fill="rgba(76, 175, 80, 0.3)" rx="3"/>
|
|
57
|
+
<text x="350" y="165" class="text-label">:important</text>
|
|
58
|
+
<text x="550" y="165" text-anchor="end" class="text-small">Sort by importance</text>
|
|
59
|
+
|
|
60
|
+
<!-- Strategy 3: Balanced -->
|
|
61
|
+
<rect x="340" y="190" width="220" height="35" fill="rgba(76, 175, 80, 0.3)" rx="3"/>
|
|
62
|
+
<text x="350" y="210" class="text-label">:balanced</text>
|
|
63
|
+
<text x="550" y="210" text-anchor="end" class="text-small">Weighted formula</text>
|
|
64
|
+
|
|
65
|
+
<text x="450" y="250" text-anchor="middle" class="text-small">Assembles until max_tokens reached</text>
|
|
66
|
+
|
|
67
|
+
<!-- Context String Box -->
|
|
68
|
+
<rect x="650" y="60" width="200" height="200" class="output-box" rx="5"/>
|
|
69
|
+
<text x="750" y="85" text-anchor="middle" class="text-header">Context String</text>
|
|
70
|
+
<text x="750" y="105" text-anchor="middle" class="text-small">(Ordered)</text>
|
|
71
|
+
|
|
72
|
+
<text x="660" y="135" class="text-label">Node 3</text>
|
|
73
|
+
<text x="840" y="135" text-anchor="end" class="text-small">(important)</text>
|
|
74
|
+
<text x="660" y="155" class="text-label">Node 1</text>
|
|
75
|
+
<text x="840" y="155" text-anchor="end" class="text-small">(recent)</text>
|
|
76
|
+
<text x="660" y="175" class="text-label">Node 5</text>
|
|
77
|
+
<text x="840" y="175" text-anchor="end" class="text-small">(balanced)</text>
|
|
78
|
+
<text x="660" y="195" class="text-label">Node 2</text>
|
|
79
|
+
<text x="840" y="195" text-anchor="end" class="text-small">(fits)</text>
|
|
80
|
+
<text x="660" y="215" class="text-small" fill="#808080">...</text>
|
|
81
|
+
<text x="750" y="245" text-anchor="middle" class="text-small" fill="#66BB6A">✓ Within token limit</text>
|
|
82
|
+
|
|
83
|
+
<!-- LLM Prompt Box -->
|
|
84
|
+
<rect x="250" y="330" width="400" height="180" class="llm-box" rx="5"/>
|
|
85
|
+
<text x="450" y="355" text-anchor="middle" class="text-header">LLM Prompt</text>
|
|
86
|
+
|
|
87
|
+
<text x="260" y="385" class="text-small" fill="#BB86FC">System: You are a helpful assistant...</text>
|
|
88
|
+
<text x="260" y="410" class="text-small" fill="#BB86FC">Context from memory:</text>
|
|
89
|
+
<text x="270" y="430" class="text-small" fill="#E0E0E0">[Assembled Context String]</text>
|
|
90
|
+
<text x="260" y="455" class="text-small" fill="#BB86FC">User: How do we handle auth?</text>
|
|
91
|
+
<text x="260" y="480" class="text-small" fill="#BB86FC">Assistant:</text>
|
|
92
|
+
|
|
93
|
+
<!-- Arrows -->
|
|
94
|
+
<path d="M 250 160 L 330 160" class="arrow"/>
|
|
95
|
+
<text x="290" y="150" text-anchor="middle" class="text-small">select &</text>
|
|
96
|
+
<text x="290" y="163" text-anchor="middle" class="text-small">sort</text>
|
|
97
|
+
|
|
98
|
+
<path d="M 570 160 L 650 160" class="arrow"/>
|
|
99
|
+
<text x="610" y="150" text-anchor="middle" class="text-small">assemble</text>
|
|
100
|
+
|
|
101
|
+
<path d="M 750 260 L 750 330" class="arrow"/>
|
|
102
|
+
<text x="770" y="300" class="text-small">insert into</text>
|
|
103
|
+
<text x="770" y="313" class="text-small">prompt</text>
|
|
104
|
+
|
|
105
|
+
<!-- Token count indicator -->
|
|
106
|
+
<rect x="650" y="275" width="200" height="30" fill="rgba(255, 152, 0, 0.1)" stroke="#FF9800" stroke-width="1" rx="3"/>
|
|
107
|
+
<rect x="650" y="275" width="140" height="30" fill="rgba(76, 175, 80, 0.3)" rx="3"/>
|
|
108
|
+
<text x="750" y="293" text-anchor="middle" class="text-small">4700 / 5000 tokens</text>
|
|
109
|
+
</svg>
|
|
110
|
+
|
|
111
|
+
## Basic Usage
|
|
112
|
+
|
|
113
|
+
The `create_context` method assembles context from working memory:
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
# Basic context assembly
|
|
117
|
+
context = htm.create_context(
|
|
118
|
+
strategy: :balanced, # Assembly strategy
|
|
119
|
+
max_tokens: nil # Optional token limit
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Use with your LLM
|
|
123
|
+
prompt = <<~PROMPT
|
|
124
|
+
Context from memory:
|
|
125
|
+
#{context}
|
|
126
|
+
|
|
127
|
+
User question: How do we handle authentication?
|
|
128
|
+
|
|
129
|
+
Assistant:
|
|
130
|
+
PROMPT
|
|
131
|
+
|
|
132
|
+
# Send to LLM...
|
|
133
|
+
response = llm.complete(prompt)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Assembly Strategies
|
|
137
|
+
|
|
138
|
+
HTM provides three strategies for assembling context, each optimized for different use cases.
|
|
139
|
+
|
|
140
|
+
### Recent Strategy
|
|
141
|
+
|
|
142
|
+
The `:recent` strategy prioritizes newest memories first.
|
|
143
|
+
|
|
144
|
+
```ruby
|
|
145
|
+
context = htm.create_context(strategy: :recent)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**How it works:**
|
|
149
|
+
|
|
150
|
+
1. Sort memories by access time (most recent first)
|
|
151
|
+
2. Add memories in order until token limit reached
|
|
152
|
+
3. Return assembled context
|
|
153
|
+
|
|
154
|
+
**Best for:**
|
|
155
|
+
|
|
156
|
+
- Continuing recent conversations
|
|
157
|
+
- Session-based interactions
|
|
158
|
+
- Short-term context tracking
|
|
159
|
+
- Real-time applications
|
|
160
|
+
|
|
161
|
+
**Example:**
|
|
162
|
+
|
|
163
|
+
```ruby
|
|
164
|
+
# Chat application
|
|
165
|
+
class ChatBot
|
|
166
|
+
def initialize
|
|
167
|
+
@htm = HTM.new(robot_name: "Chat", working_memory_size: 128_000)
|
|
168
|
+
@turn = 0
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def chat(user_message)
|
|
172
|
+
@turn += 1
|
|
173
|
+
|
|
174
|
+
# Add user message
|
|
175
|
+
@htm.add_node(
|
|
176
|
+
"turn_#{@turn}_user",
|
|
177
|
+
"User: #{user_message}",
|
|
178
|
+
type: :context,
|
|
179
|
+
importance: 6.0
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
# Get recent context
|
|
183
|
+
context = @htm.create_context(
|
|
184
|
+
strategy: :recent,
|
|
185
|
+
max_tokens: 10_000
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
# Generate response
|
|
189
|
+
response = llm_generate(context, user_message)
|
|
190
|
+
|
|
191
|
+
# Store assistant response
|
|
192
|
+
@htm.add_node(
|
|
193
|
+
"turn_#{@turn}_assistant",
|
|
194
|
+
"Assistant: #{response}",
|
|
195
|
+
type: :context,
|
|
196
|
+
importance: 6.0
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
response
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
private
|
|
203
|
+
|
|
204
|
+
def llm_generate(context, message)
|
|
205
|
+
# Your LLM integration here
|
|
206
|
+
"Generated response based on context"
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Important Strategy
|
|
212
|
+
|
|
213
|
+
The `:important` strategy prioritizes high-importance memories.
|
|
214
|
+
|
|
215
|
+
```ruby
|
|
216
|
+
context = htm.create_context(strategy: :important)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**How it works:**
|
|
220
|
+
|
|
221
|
+
1. Sort memories by importance (highest first)
|
|
222
|
+
2. Add memories in order until token limit reached
|
|
223
|
+
3. Return assembled context
|
|
224
|
+
|
|
225
|
+
**Best for:**
|
|
226
|
+
|
|
227
|
+
- Critical information retention
|
|
228
|
+
- System constraints and rules
|
|
229
|
+
- User preferences
|
|
230
|
+
- Core knowledge base
|
|
231
|
+
- Decision-making support
|
|
232
|
+
|
|
233
|
+
**Example:**
|
|
234
|
+
|
|
235
|
+
```ruby
|
|
236
|
+
# Knowledge base with priorities
|
|
237
|
+
class KnowledgeBot
|
|
238
|
+
def initialize
|
|
239
|
+
@htm = HTM.new(robot_name: "Knowledge")
|
|
240
|
+
|
|
241
|
+
# Add critical system constraints
|
|
242
|
+
@htm.add_node(
|
|
243
|
+
"constraint_001",
|
|
244
|
+
"CRITICAL: Never expose API keys in responses",
|
|
245
|
+
type: :fact,
|
|
246
|
+
importance: 10.0
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
# Add important user preferences
|
|
250
|
+
@htm.add_node(
|
|
251
|
+
"pref_001",
|
|
252
|
+
"User prefers concise explanations",
|
|
253
|
+
type: :preference,
|
|
254
|
+
importance: 8.0
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# Add general knowledge
|
|
258
|
+
@htm.add_node(
|
|
259
|
+
"fact_001",
|
|
260
|
+
"Python uses indentation for code blocks",
|
|
261
|
+
type: :fact,
|
|
262
|
+
importance: 5.0
|
|
263
|
+
)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def answer_question(question)
|
|
267
|
+
# Get most important context first
|
|
268
|
+
context = @htm.create_context(
|
|
269
|
+
strategy: :important,
|
|
270
|
+
max_tokens: 5_000
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
# Critical constraints and preferences are included first
|
|
274
|
+
generate_answer(context, question)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
private
|
|
278
|
+
|
|
279
|
+
def generate_answer(context, question)
|
|
280
|
+
# LLM integration
|
|
281
|
+
"Answer based on important context"
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Balanced Strategy (Recommended)
|
|
287
|
+
|
|
288
|
+
The `:balanced` strategy combines importance and recency using a weighted formula.
|
|
289
|
+
|
|
290
|
+
```ruby
|
|
291
|
+
context = htm.create_context(strategy: :balanced)
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
**How it works:**
|
|
295
|
+
|
|
296
|
+
1. Calculate score: `importance × (1 / (1 + age_in_hours))`
|
|
297
|
+
2. Sort by score (highest first)
|
|
298
|
+
3. Add memories until token limit reached
|
|
299
|
+
4. Return assembled context
|
|
300
|
+
|
|
301
|
+
**Scoring examples:**
|
|
302
|
+
|
|
303
|
+
```ruby
|
|
304
|
+
# Recent + Important: High score
|
|
305
|
+
# Importance: 9.0, Age: 1 hour
|
|
306
|
+
# Score: 9.0 × (1 / (1 + 1)) = 4.5 ✓ Included
|
|
307
|
+
|
|
308
|
+
# Old + Important: Medium score
|
|
309
|
+
# Importance: 9.0, Age: 24 hours
|
|
310
|
+
# Score: 9.0 × (1 / (1 + 24)) = 0.36 ≈ Maybe
|
|
311
|
+
|
|
312
|
+
# Recent + Unimportant: Low score
|
|
313
|
+
# Importance: 2.0, Age: 1 hour
|
|
314
|
+
# Score: 2.0 × (1 / (1 + 1)) = 1.0 ≈ Maybe
|
|
315
|
+
|
|
316
|
+
# Old + Unimportant: Very low score
|
|
317
|
+
# Importance: 2.0, Age: 24 hours
|
|
318
|
+
# Score: 2.0 × (1 / (1 + 24)) = 0.08 ✗ Excluded
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**Best for:**
|
|
322
|
+
|
|
323
|
+
- General-purpose applications (recommended default)
|
|
324
|
+
- Mixed temporal needs
|
|
325
|
+
- Production systems
|
|
326
|
+
- Balanced context requirements
|
|
327
|
+
|
|
328
|
+
**Example:**
|
|
329
|
+
|
|
330
|
+
```ruby
|
|
331
|
+
# General-purpose assistant
|
|
332
|
+
class Assistant
|
|
333
|
+
def initialize
|
|
334
|
+
@htm = HTM.new(
|
|
335
|
+
robot_name: "Assistant",
|
|
336
|
+
working_memory_size: 128_000
|
|
337
|
+
)
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def process(user_input)
|
|
341
|
+
# Add user input
|
|
342
|
+
@htm.add_node(
|
|
343
|
+
"input_#{Time.now.to_i}",
|
|
344
|
+
user_input,
|
|
345
|
+
type: :context,
|
|
346
|
+
importance: 7.0
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
# Get balanced context (recent + important)
|
|
350
|
+
context = @htm.create_context(
|
|
351
|
+
strategy: :balanced,
|
|
352
|
+
max_tokens: 50_000
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
# Use context with LLM
|
|
356
|
+
generate_response(context, user_input)
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
private
|
|
360
|
+
|
|
361
|
+
def generate_response(context, input)
|
|
362
|
+
prompt = <<~PROMPT
|
|
363
|
+
You are a helpful assistant with access to memory.
|
|
364
|
+
|
|
365
|
+
Context from memory:
|
|
366
|
+
#{context}
|
|
367
|
+
|
|
368
|
+
User: #{input}
|
|
369
|
+
|
|
370
|
+
Assistant:
|
|
371
|
+
PROMPT
|
|
372
|
+
|
|
373
|
+
# Send to LLM
|
|
374
|
+
llm_complete(prompt)
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
def llm_complete(prompt)
|
|
378
|
+
# Your LLM integration
|
|
379
|
+
"Generated response"
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
## Token Limits
|
|
385
|
+
|
|
386
|
+
Control context size with token limits:
|
|
387
|
+
|
|
388
|
+
```ruby
|
|
389
|
+
# Use default (working memory size)
|
|
390
|
+
context = htm.create_context(strategy: :balanced)
|
|
391
|
+
|
|
392
|
+
# Custom limit
|
|
393
|
+
context = htm.create_context(
|
|
394
|
+
strategy: :balanced,
|
|
395
|
+
max_tokens: 50_000
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
# Small context for simple queries
|
|
399
|
+
context = htm.create_context(
|
|
400
|
+
strategy: :recent,
|
|
401
|
+
max_tokens: 5_000
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
# Large context for complex tasks
|
|
405
|
+
context = htm.create_context(
|
|
406
|
+
strategy: :important,
|
|
407
|
+
max_tokens: 200_000
|
|
408
|
+
)
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
**Choosing token limits:**
|
|
412
|
+
|
|
413
|
+
| Limit | Use Case |
|
|
414
|
+
|-------|----------|
|
|
415
|
+
| 2K-5K | Simple Q&A, quick lookups |
|
|
416
|
+
| 10K-20K | Standard conversations |
|
|
417
|
+
| 50K-100K | Complex analysis, code generation |
|
|
418
|
+
| 100K+ | Document processing, extensive context |
|
|
419
|
+
|
|
420
|
+
!!! warning "LLM Context Windows"
|
|
421
|
+
Don't exceed your LLM's context window:
|
|
422
|
+
- GPT-3.5: 4K-16K tokens
|
|
423
|
+
- GPT-4: 8K-128K tokens
|
|
424
|
+
- Claude: 100K-200K tokens
|
|
425
|
+
- Llama 2: 4K tokens
|
|
426
|
+
|
|
427
|
+
## Strategy Comparison
|
|
428
|
+
|
|
429
|
+
### Performance
|
|
430
|
+
|
|
431
|
+
```ruby
|
|
432
|
+
require 'benchmark'
|
|
433
|
+
|
|
434
|
+
# Add 1000 test memories
|
|
435
|
+
1000.times do |i|
|
|
436
|
+
htm.add_node(
|
|
437
|
+
"test_#{i}",
|
|
438
|
+
"Memory #{i}",
|
|
439
|
+
importance: rand(1.0..10.0)
|
|
440
|
+
)
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
# Benchmark strategies
|
|
444
|
+
Benchmark.bm(15) do |x|
|
|
445
|
+
x.report("Recent:") do
|
|
446
|
+
100.times { htm.create_context(strategy: :recent) }
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
x.report("Important:") do
|
|
450
|
+
100.times { htm.create_context(strategy: :important) }
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
x.report("Balanced:") do
|
|
454
|
+
100.times { htm.create_context(strategy: :balanced) }
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
# Typical results:
|
|
459
|
+
# user system total real
|
|
460
|
+
# Recent: 0.050000 0.000000 0.050000 ( 0.051234)
|
|
461
|
+
# Important: 0.045000 0.000000 0.045000 ( 0.047891)
|
|
462
|
+
# Balanced: 0.080000 0.000000 0.080000 ( 0.082456)
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
**Notes:**
|
|
466
|
+
|
|
467
|
+
- `:recent` is fastest (simple sort)
|
|
468
|
+
- `:important` is fast (simple sort)
|
|
469
|
+
- `:balanced` is slower (complex calculation)
|
|
470
|
+
- All are typically < 100ms for normal working memory sizes
|
|
471
|
+
|
|
472
|
+
### Quality Comparison
|
|
473
|
+
|
|
474
|
+
```ruby
|
|
475
|
+
# Test scenario: Mix of old important and recent unimportant data
|
|
476
|
+
|
|
477
|
+
# Setup
|
|
478
|
+
htm = HTM.new(robot_name: "Test")
|
|
479
|
+
|
|
480
|
+
# Add old important data
|
|
481
|
+
htm.add_node("old_critical", "Critical system constraint", importance: 10.0)
|
|
482
|
+
sleep 1 # Simulate age
|
|
483
|
+
|
|
484
|
+
# Add recent unimportant data
|
|
485
|
+
20.times do |i|
|
|
486
|
+
htm.add_node("recent_#{i}", "Recent note #{i}", importance: 2.0)
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
# Compare strategies
|
|
490
|
+
puts "=== Recent Strategy ==="
|
|
491
|
+
context = htm.create_context(strategy: :recent, max_tokens: 1000)
|
|
492
|
+
puts context.include?("Critical system constraint") ? "✓ Has critical" : "✗ Missing critical"
|
|
493
|
+
|
|
494
|
+
puts "\n=== Important Strategy ==="
|
|
495
|
+
context = htm.create_context(strategy: :important, max_tokens: 1000)
|
|
496
|
+
puts context.include?("Critical system constraint") ? "✓ Has critical" : "✗ Missing critical"
|
|
497
|
+
|
|
498
|
+
puts "\n=== Balanced Strategy ==="
|
|
499
|
+
context = htm.create_context(strategy: :balanced, max_tokens: 1000)
|
|
500
|
+
puts context.include?("Critical system constraint") ? "✓ Has critical" : "✗ Missing critical"
|
|
501
|
+
|
|
502
|
+
# Results:
|
|
503
|
+
# Recent: ✗ Missing critical (prioritized recent notes)
|
|
504
|
+
# Important: ✓ Has critical (prioritized by importance)
|
|
505
|
+
# Balanced: ✓ Has critical (balanced approach)
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
## Advanced Techniques
|
|
509
|
+
|
|
510
|
+
### 1. Multi-Strategy Context
|
|
511
|
+
|
|
512
|
+
Use multiple strategies for comprehensive context:
|
|
513
|
+
|
|
514
|
+
```ruby
|
|
515
|
+
def multi_strategy_context(max_tokens_per_strategy: 10_000)
|
|
516
|
+
# Get different perspectives
|
|
517
|
+
recent = htm.create_context(
|
|
518
|
+
strategy: :recent,
|
|
519
|
+
max_tokens: max_tokens_per_strategy
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
important = htm.create_context(
|
|
523
|
+
strategy: :important,
|
|
524
|
+
max_tokens: max_tokens_per_strategy
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
# Combine (you might want to deduplicate)
|
|
528
|
+
combined = <<~CONTEXT
|
|
529
|
+
=== Recent Context ===
|
|
530
|
+
#{recent}
|
|
531
|
+
|
|
532
|
+
=== Important Context ===
|
|
533
|
+
#{important}
|
|
534
|
+
CONTEXT
|
|
535
|
+
|
|
536
|
+
combined
|
|
537
|
+
end
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### 2. Dynamic Strategy Selection
|
|
541
|
+
|
|
542
|
+
Choose strategy based on query type:
|
|
543
|
+
|
|
544
|
+
```ruby
|
|
545
|
+
def smart_context(query)
|
|
546
|
+
strategy = if query.match?(/recent|latest|current/)
|
|
547
|
+
:recent
|
|
548
|
+
elsif query.match?(/important|critical|must/)
|
|
549
|
+
:important
|
|
550
|
+
else
|
|
551
|
+
:balanced
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
htm.create_context(strategy: strategy, max_tokens: 20_000)
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
# Usage
|
|
558
|
+
context = smart_context("What are the recent changes?") # Uses :recent
|
|
559
|
+
context = smart_context("What are critical constraints?") # Uses :important
|
|
560
|
+
context = smart_context("How do we handle auth?") # Uses :balanced
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
### 3. Filtered Context
|
|
564
|
+
|
|
565
|
+
Include only specific types of memories:
|
|
566
|
+
|
|
567
|
+
```ruby
|
|
568
|
+
def filtered_context(type:, strategy: :balanced)
|
|
569
|
+
# This requires custom implementation
|
|
570
|
+
# HTM doesn't expose working memory internals directly
|
|
571
|
+
|
|
572
|
+
# Workaround: Recall specific types
|
|
573
|
+
memories = htm.recall(
|
|
574
|
+
timeframe: "last 24 hours",
|
|
575
|
+
topic: "type:#{type}", # Pseudo-filter
|
|
576
|
+
strategy: :hybrid,
|
|
577
|
+
limit: 50
|
|
578
|
+
).select { |m| m['type'] == type.to_s }
|
|
579
|
+
|
|
580
|
+
# Manually assemble context
|
|
581
|
+
memories.map { |m| m['value'] }.join("\n\n")
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
# Usage
|
|
585
|
+
facts_only = filtered_context(type: :fact)
|
|
586
|
+
decisions_only = filtered_context(type: :decision)
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
### 4. Sectioned Context
|
|
590
|
+
|
|
591
|
+
Organize context into sections:
|
|
592
|
+
|
|
593
|
+
```ruby
|
|
594
|
+
def sectioned_context
|
|
595
|
+
# Get different types of context
|
|
596
|
+
facts = htm.recall(timeframe: "all time", topic: "fact")
|
|
597
|
+
.select { |m| m['type'] == 'fact' }
|
|
598
|
+
.first(5)
|
|
599
|
+
|
|
600
|
+
decisions = htm.recall(timeframe: "all time", topic: "decision")
|
|
601
|
+
.select { |m| m['type'] == 'decision' }
|
|
602
|
+
.first(5)
|
|
603
|
+
|
|
604
|
+
recent = htm.recall(timeframe: "last hour", topic: "", limit: 5)
|
|
605
|
+
|
|
606
|
+
# Format as sections
|
|
607
|
+
<<~CONTEXT
|
|
608
|
+
=== Core Facts ===
|
|
609
|
+
#{facts.map { |f| "- #{f['value']}" }.join("\n")}
|
|
610
|
+
|
|
611
|
+
=== Key Decisions ===
|
|
612
|
+
#{decisions.map { |d| "- #{d['value']}" }.join("\n")}
|
|
613
|
+
|
|
614
|
+
=== Recent Activity ===
|
|
615
|
+
#{recent.map { |r| "- #{r['value']}" }.join("\n")}
|
|
616
|
+
CONTEXT
|
|
617
|
+
end
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
### 5. Token-Aware Context
|
|
621
|
+
|
|
622
|
+
Ensure context fits LLM limits:
|
|
623
|
+
|
|
624
|
+
```ruby
|
|
625
|
+
class TokenAwareContext
|
|
626
|
+
def initialize(htm, embedding_service)
|
|
627
|
+
@htm = htm
|
|
628
|
+
@embedding_service = embedding_service
|
|
629
|
+
end
|
|
630
|
+
|
|
631
|
+
def create(strategy:, llm_context_window:, reserve_for_prompt: 1000)
|
|
632
|
+
# Calculate available tokens
|
|
633
|
+
available = llm_context_window - reserve_for_prompt
|
|
634
|
+
|
|
635
|
+
# Get context
|
|
636
|
+
context = @htm.create_context(
|
|
637
|
+
strategy: strategy,
|
|
638
|
+
max_tokens: available
|
|
639
|
+
)
|
|
640
|
+
|
|
641
|
+
# Verify token count
|
|
642
|
+
actual_tokens = @embedding_service.count_tokens(context)
|
|
643
|
+
|
|
644
|
+
if actual_tokens > available
|
|
645
|
+
warn "Context exceeded limit! Truncating..."
|
|
646
|
+
# Retry with smaller limit
|
|
647
|
+
context = @htm.create_context(
|
|
648
|
+
strategy: strategy,
|
|
649
|
+
max_tokens: available * 0.9 # 90% to be safe
|
|
650
|
+
)
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
context
|
|
654
|
+
end
|
|
655
|
+
end
|
|
656
|
+
|
|
657
|
+
# Usage
|
|
658
|
+
embedding_service = HTM::EmbeddingService.new
|
|
659
|
+
context_builder = TokenAwareContext.new(htm, embedding_service)
|
|
660
|
+
|
|
661
|
+
context = context_builder.create(
|
|
662
|
+
strategy: :balanced,
|
|
663
|
+
llm_context_window: 100_000, # Claude 100K
|
|
664
|
+
reserve_for_prompt: 2_000
|
|
665
|
+
)
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
## Using Context with LLMs
|
|
669
|
+
|
|
670
|
+
### Pattern 1: System Prompt + Context
|
|
671
|
+
|
|
672
|
+
```ruby
|
|
673
|
+
def generate_with_context(user_query)
|
|
674
|
+
context = htm.create_context(strategy: :balanced, max_tokens: 50_000)
|
|
675
|
+
|
|
676
|
+
system_prompt = <<~SYSTEM
|
|
677
|
+
You are a helpful AI assistant with access to memory.
|
|
678
|
+
Use the provided context to answer questions accurately.
|
|
679
|
+
SYSTEM
|
|
680
|
+
|
|
681
|
+
user_prompt = <<~USER
|
|
682
|
+
Context from memory:
|
|
683
|
+
#{context}
|
|
684
|
+
|
|
685
|
+
---
|
|
686
|
+
|
|
687
|
+
User question: #{user_query}
|
|
688
|
+
|
|
689
|
+
Please answer based on the context above.
|
|
690
|
+
USER
|
|
691
|
+
|
|
692
|
+
# Send to LLM with system + user prompts
|
|
693
|
+
llm.chat(system: system_prompt, user: user_prompt)
|
|
694
|
+
end
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
### Pattern 2: Conversation History
|
|
698
|
+
|
|
699
|
+
```ruby
|
|
700
|
+
class ConversationManager
|
|
701
|
+
def initialize
|
|
702
|
+
@htm = HTM.new(robot_name: "Chat")
|
|
703
|
+
@conversation_id = SecureRandom.uuid
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
def add_turn(user_msg, assistant_msg)
|
|
707
|
+
timestamp = Time.now.to_i
|
|
708
|
+
|
|
709
|
+
@htm.add_node(
|
|
710
|
+
"#{@conversation_id}_#{timestamp}_user",
|
|
711
|
+
user_msg,
|
|
712
|
+
type: :context,
|
|
713
|
+
importance: 6.0,
|
|
714
|
+
tags: ["conversation", @conversation_id]
|
|
715
|
+
)
|
|
716
|
+
|
|
717
|
+
@htm.add_node(
|
|
718
|
+
"#{@conversation_id}_#{timestamp}_assistant",
|
|
719
|
+
assistant_msg,
|
|
720
|
+
type: :context,
|
|
721
|
+
importance: 6.0,
|
|
722
|
+
tags: ["conversation", @conversation_id]
|
|
723
|
+
)
|
|
724
|
+
end
|
|
725
|
+
|
|
726
|
+
def get_context_for_llm
|
|
727
|
+
# Get recent conversation
|
|
728
|
+
@htm.create_context(
|
|
729
|
+
strategy: :recent,
|
|
730
|
+
max_tokens: 10_000
|
|
731
|
+
)
|
|
732
|
+
end
|
|
733
|
+
end
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
### Pattern 3: RAG with Context
|
|
737
|
+
|
|
738
|
+
```ruby
|
|
739
|
+
def rag_query(question)
|
|
740
|
+
# 1. Retrieve relevant memories
|
|
741
|
+
relevant = htm.recall(
|
|
742
|
+
timeframe: "last month",
|
|
743
|
+
topic: question,
|
|
744
|
+
strategy: :hybrid,
|
|
745
|
+
limit: 10
|
|
746
|
+
)
|
|
747
|
+
|
|
748
|
+
# 2. Create context from working memory (includes retrieved + existing)
|
|
749
|
+
context = htm.create_context(
|
|
750
|
+
strategy: :balanced,
|
|
751
|
+
max_tokens: 30_000
|
|
752
|
+
)
|
|
753
|
+
|
|
754
|
+
# 3. Generate answer
|
|
755
|
+
prompt = <<~PROMPT
|
|
756
|
+
Context:
|
|
757
|
+
#{context}
|
|
758
|
+
|
|
759
|
+
Question: #{question}
|
|
760
|
+
|
|
761
|
+
Answer based on the context above:
|
|
762
|
+
PROMPT
|
|
763
|
+
|
|
764
|
+
llm.complete(prompt)
|
|
765
|
+
end
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
## Optimization Tips
|
|
769
|
+
|
|
770
|
+
### 1. Cache Context
|
|
771
|
+
|
|
772
|
+
```ruby
|
|
773
|
+
class ContextCache
|
|
774
|
+
def initialize(htm, ttl: 60)
|
|
775
|
+
@htm = htm
|
|
776
|
+
@ttl = ttl
|
|
777
|
+
@cache = {}
|
|
778
|
+
end
|
|
779
|
+
|
|
780
|
+
def get_context(strategy:, max_tokens: nil)
|
|
781
|
+
cache_key = "#{strategy}_#{max_tokens}"
|
|
782
|
+
|
|
783
|
+
# Check cache
|
|
784
|
+
if cached = @cache[cache_key]
|
|
785
|
+
if Time.now - cached[:time] < @ttl
|
|
786
|
+
return cached[:context]
|
|
787
|
+
end
|
|
788
|
+
end
|
|
789
|
+
|
|
790
|
+
# Generate new context
|
|
791
|
+
context = @htm.create_context(
|
|
792
|
+
strategy: strategy,
|
|
793
|
+
max_tokens: max_tokens
|
|
794
|
+
)
|
|
795
|
+
|
|
796
|
+
# Cache it
|
|
797
|
+
@cache[cache_key] = {
|
|
798
|
+
context: context,
|
|
799
|
+
time: Time.now
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
context
|
|
803
|
+
end
|
|
804
|
+
|
|
805
|
+
def invalidate
|
|
806
|
+
@cache.clear
|
|
807
|
+
end
|
|
808
|
+
end
|
|
809
|
+
|
|
810
|
+
# Usage
|
|
811
|
+
cache = ContextCache.new(htm, ttl: 30) # 30 second TTL
|
|
812
|
+
context = cache.get_context(strategy: :balanced) # Cached for 30s
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
### 2. Progressive Context Loading
|
|
816
|
+
|
|
817
|
+
```ruby
|
|
818
|
+
def progressive_context(start_tokens: 5_000, max_tokens: 50_000)
|
|
819
|
+
# Start small
|
|
820
|
+
context = htm.create_context(strategy: :balanced, max_tokens: start_tokens)
|
|
821
|
+
|
|
822
|
+
# Check if more context needed (based on your logic)
|
|
823
|
+
if needs_more_context?(context)
|
|
824
|
+
# Expand gradually
|
|
825
|
+
context = htm.create_context(strategy: :balanced, max_tokens: start_tokens * 2)
|
|
826
|
+
end
|
|
827
|
+
|
|
828
|
+
if still_needs_more?(context)
|
|
829
|
+
# Expand to max
|
|
830
|
+
context = htm.create_context(strategy: :balanced, max_tokens: max_tokens)
|
|
831
|
+
end
|
|
832
|
+
|
|
833
|
+
context
|
|
834
|
+
end
|
|
835
|
+
|
|
836
|
+
def needs_more_context?(context)
|
|
837
|
+
# Your logic here
|
|
838
|
+
context.length < 1000 # Example: too short
|
|
839
|
+
end
|
|
840
|
+
|
|
841
|
+
def still_needs_more?(context)
|
|
842
|
+
# Your logic here
|
|
843
|
+
false # Example
|
|
844
|
+
end
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
### 3. Selective Inclusion
|
|
848
|
+
|
|
849
|
+
```ruby
|
|
850
|
+
def selective_context(query)
|
|
851
|
+
# Determine what's relevant
|
|
852
|
+
include_facts = query.match?(/fact|truth|information/)
|
|
853
|
+
include_decisions = query.match?(/decision|choice|why/)
|
|
854
|
+
include_code = query.match?(/code|implement|example/)
|
|
855
|
+
|
|
856
|
+
# Build custom context
|
|
857
|
+
parts = []
|
|
858
|
+
|
|
859
|
+
if include_facts
|
|
860
|
+
facts = htm.recall(timeframe: "all time", topic: query)
|
|
861
|
+
.select { |m| m['type'] == 'fact' }
|
|
862
|
+
.first(5)
|
|
863
|
+
parts << "Facts:\n" + facts.map { |f| "- #{f['value']}" }.join("\n")
|
|
864
|
+
end
|
|
865
|
+
|
|
866
|
+
if include_decisions
|
|
867
|
+
decisions = htm.recall(timeframe: "all time", topic: query)
|
|
868
|
+
.select { |m| m['type'] == 'decision' }
|
|
869
|
+
.first(5)
|
|
870
|
+
parts << "Decisions:\n" + decisions.map { |d| "- #{d['value']}" }.join("\n")
|
|
871
|
+
end
|
|
872
|
+
|
|
873
|
+
if include_code
|
|
874
|
+
code = htm.recall(timeframe: "all time", topic: query)
|
|
875
|
+
.select { |m| m['type'] == 'code' }
|
|
876
|
+
.first(3)
|
|
877
|
+
parts << "Code Examples:\n" + code.map { |c| c['value'] }.join("\n\n")
|
|
878
|
+
end
|
|
879
|
+
|
|
880
|
+
parts.join("\n\n")
|
|
881
|
+
end
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
## Best Practices
|
|
885
|
+
|
|
886
|
+
### 1. Choose the Right Strategy
|
|
887
|
+
|
|
888
|
+
```ruby
|
|
889
|
+
# Use :recent for conversations
|
|
890
|
+
context = htm.create_context(strategy: :recent)
|
|
891
|
+
|
|
892
|
+
# Use :important for critical operations
|
|
893
|
+
context = htm.create_context(strategy: :important)
|
|
894
|
+
|
|
895
|
+
# Use :balanced as default (recommended)
|
|
896
|
+
context = htm.create_context(strategy: :balanced)
|
|
897
|
+
```
|
|
898
|
+
|
|
899
|
+
### 2. Set Appropriate Token Limits
|
|
900
|
+
|
|
901
|
+
```ruby
|
|
902
|
+
# Don't exceed LLM context window
|
|
903
|
+
context = htm.create_context(
|
|
904
|
+
strategy: :balanced,
|
|
905
|
+
max_tokens: 100_000 # Leave room for prompt
|
|
906
|
+
)
|
|
907
|
+
|
|
908
|
+
# Smaller contexts are faster
|
|
909
|
+
context = htm.create_context(
|
|
910
|
+
strategy: :recent,
|
|
911
|
+
max_tokens: 5_000 # Quick queries
|
|
912
|
+
)
|
|
913
|
+
```
|
|
914
|
+
|
|
915
|
+
### 3. Monitor Context Quality
|
|
916
|
+
|
|
917
|
+
```ruby
|
|
918
|
+
def monitor_context
|
|
919
|
+
context = htm.create_context(strategy: :balanced)
|
|
920
|
+
|
|
921
|
+
puts "Context length: #{context.length} characters"
|
|
922
|
+
|
|
923
|
+
# Count token estimate
|
|
924
|
+
embedding_service = HTM::EmbeddingService.new
|
|
925
|
+
tokens = embedding_service.count_tokens(context)
|
|
926
|
+
puts "Estimated tokens: #{tokens}"
|
|
927
|
+
|
|
928
|
+
# Check if too small or too large
|
|
929
|
+
warn "Context very small!" if tokens < 500
|
|
930
|
+
warn "Context very large!" if tokens > 100_000
|
|
931
|
+
end
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
### 4. Include Metadata
|
|
935
|
+
|
|
936
|
+
```ruby
|
|
937
|
+
def context_with_metadata
|
|
938
|
+
context = htm.create_context(strategy: :balanced, max_tokens: 20_000)
|
|
939
|
+
|
|
940
|
+
# Add metadata header
|
|
941
|
+
stats = htm.memory_stats
|
|
942
|
+
|
|
943
|
+
<<~CONTEXT
|
|
944
|
+
[Context assembled at #{Time.now}]
|
|
945
|
+
[Strategy: balanced]
|
|
946
|
+
[Working memory: #{stats[:working_memory][:node_count]} nodes]
|
|
947
|
+
[Robot: #{htm.robot_name}]
|
|
948
|
+
|
|
949
|
+
#{context}
|
|
950
|
+
CONTEXT
|
|
951
|
+
end
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
## Complete Example
|
|
955
|
+
|
|
956
|
+
```ruby
|
|
957
|
+
require 'htm'
|
|
958
|
+
|
|
959
|
+
# Initialize HTM
|
|
960
|
+
htm = HTM.new(
|
|
961
|
+
robot_name: "Context Demo",
|
|
962
|
+
working_memory_size: 128_000
|
|
963
|
+
)
|
|
964
|
+
|
|
965
|
+
# Add various memories
|
|
966
|
+
htm.add_node("fact_001", "User prefers Ruby", type: :fact, importance: 9.0)
|
|
967
|
+
htm.add_node("decision_001", "Use PostgreSQL", type: :decision, importance: 8.0)
|
|
968
|
+
htm.add_node("context_001", "Currently debugging auth", type: :context, importance: 7.0)
|
|
969
|
+
htm.add_node("code_001", "def auth...", type: :code, importance: 6.0)
|
|
970
|
+
htm.add_node("note_001", "Check logs later", type: :context, importance: 2.0)
|
|
971
|
+
|
|
972
|
+
puts "=== Recent Strategy ==="
|
|
973
|
+
recent = htm.create_context(strategy: :recent, max_tokens: 5_000)
|
|
974
|
+
puts recent
|
|
975
|
+
puts "\n(Newest first)"
|
|
976
|
+
|
|
977
|
+
puts "\n=== Important Strategy ==="
|
|
978
|
+
important = htm.create_context(strategy: :important, max_tokens: 5_000)
|
|
979
|
+
puts important
|
|
980
|
+
puts "\n(Most important first)"
|
|
981
|
+
|
|
982
|
+
puts "\n=== Balanced Strategy ==="
|
|
983
|
+
balanced = htm.create_context(strategy: :balanced, max_tokens: 5_000)
|
|
984
|
+
puts balanced
|
|
985
|
+
puts "\n(Recent + important)"
|
|
986
|
+
|
|
987
|
+
# Use with LLM
|
|
988
|
+
def ask_llm(context, question)
|
|
989
|
+
prompt = <<~PROMPT
|
|
990
|
+
Context:
|
|
991
|
+
#{context}
|
|
992
|
+
|
|
993
|
+
Question: #{question}
|
|
994
|
+
Answer:
|
|
995
|
+
PROMPT
|
|
996
|
+
|
|
997
|
+
# Send to your LLM here
|
|
998
|
+
puts "\n=== LLM Prompt ==="
|
|
999
|
+
puts prompt
|
|
1000
|
+
end
|
|
1001
|
+
|
|
1002
|
+
ask_llm(balanced, "What database are we using?")
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
## Next Steps
|
|
1006
|
+
|
|
1007
|
+
- [**Recalling Memories**](recalling-memories.md) - Populate working memory effectively
|
|
1008
|
+
- [**Working Memory**](working-memory.md) - Understand memory management
|
|
1009
|
+
- [**Search Strategies**](search-strategies.md) - Optimize retrieval for context
|