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,866 @@
|
|
|
1
|
+
# Multi-Robot Usage
|
|
2
|
+
|
|
3
|
+
HTM's "hive mind" architecture enables multiple robots to share knowledge through a common long-term memory. This guide covers setting up multi-robot systems, attribution tracking, and collaboration patterns.
|
|
4
|
+
|
|
5
|
+
## Understanding the Hive Mind
|
|
6
|
+
|
|
7
|
+
In HTM, all robots share the same long-term memory database but maintain separate working memories:
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
**Key Principles:**
|
|
12
|
+
|
|
13
|
+
- **Shared Knowledge**: All memories are accessible to all robots
|
|
14
|
+
- **Private Working Memory**: Each robot has its own active context
|
|
15
|
+
- **Full Attribution**: Track which robot added each memory
|
|
16
|
+
- **Collective Intelligence**: Robots learn from each other's experiences
|
|
17
|
+
|
|
18
|
+
## Setting Up Multiple Robots
|
|
19
|
+
|
|
20
|
+
### Basic Multi-Robot Setup
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
# Robot 1: Research Assistant
|
|
24
|
+
research_bot = HTM.new(
|
|
25
|
+
robot_name: "Research Assistant",
|
|
26
|
+
robot_id: "research-001",
|
|
27
|
+
working_memory_size: 128_000
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# Robot 2: Code Helper
|
|
31
|
+
code_bot = HTM.new(
|
|
32
|
+
robot_name: "Code Helper",
|
|
33
|
+
robot_id: "code-001",
|
|
34
|
+
working_memory_size: 128_000
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Robot 3: Documentation Writer
|
|
38
|
+
docs_bot = HTM.new(
|
|
39
|
+
robot_name: "Docs Writer",
|
|
40
|
+
robot_id: "docs-001",
|
|
41
|
+
working_memory_size: 64_000
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# Each robot can access shared knowledge
|
|
45
|
+
research_bot.add_node(
|
|
46
|
+
"finding_001",
|
|
47
|
+
"Research shows PostgreSQL outperforms MongoDB for ACID workloads",
|
|
48
|
+
type: :fact,
|
|
49
|
+
importance: 8.0,
|
|
50
|
+
tags: ["research", "database"]
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Code bot can access this finding
|
|
54
|
+
findings = code_bot.recall(
|
|
55
|
+
timeframe: "last hour",
|
|
56
|
+
topic: "database performance"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Docs bot can document it
|
|
60
|
+
docs_bot.add_node(
|
|
61
|
+
"doc_001",
|
|
62
|
+
"PostgreSQL performance documented based on research findings",
|
|
63
|
+
type: :context,
|
|
64
|
+
importance: 6.0,
|
|
65
|
+
tags: ["documentation", "database"],
|
|
66
|
+
related_to: ["finding_001"]
|
|
67
|
+
)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Robot Identification
|
|
71
|
+
|
|
72
|
+
### Session IDs vs Persistent IDs
|
|
73
|
+
|
|
74
|
+
Choose the right identification strategy:
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
# Strategy 1: Persistent Robot (recommended for production)
|
|
78
|
+
persistent_bot = HTM.new(
|
|
79
|
+
robot_name: "Production Assistant",
|
|
80
|
+
robot_id: "prod-assistant-001" # Fixed, reusable
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Strategy 2: Session-based Robot (for temporary workflows)
|
|
84
|
+
session_id = SecureRandom.uuid
|
|
85
|
+
session_bot = HTM.new(
|
|
86
|
+
robot_name: "Temp Session",
|
|
87
|
+
robot_id: "session-#{session_id}" # Unique per session
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# Strategy 3: User-specific Robot
|
|
91
|
+
user_id = "alice"
|
|
92
|
+
user_bot = HTM.new(
|
|
93
|
+
robot_name: "Alice's Assistant",
|
|
94
|
+
robot_id: "user-#{user_id}-assistant"
|
|
95
|
+
)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
!!! tip "Naming Conventions"
|
|
99
|
+
- **Production robots**: `service-purpose-001` (e.g., `api-assistant-001`)
|
|
100
|
+
- **User robots**: `user-{user_id}-{purpose}` (e.g., `user-alice-assistant`)
|
|
101
|
+
- **Session robots**: `session-{uuid}` (e.g., `session-abc123...`)
|
|
102
|
+
- **Team robots**: `team-{name}-{purpose}` (e.g., `team-eng-reviewer`)
|
|
103
|
+
|
|
104
|
+
### Robot Registry
|
|
105
|
+
|
|
106
|
+
All robots are automatically registered:
|
|
107
|
+
|
|
108
|
+
```ruby
|
|
109
|
+
# Robots are registered when created
|
|
110
|
+
bot = HTM.new(robot_name: "My Bot", robot_id: "bot-001")
|
|
111
|
+
|
|
112
|
+
# Query robot registry
|
|
113
|
+
config = HTM::Database.default_config
|
|
114
|
+
conn = PG.connect(config)
|
|
115
|
+
|
|
116
|
+
result = conn.exec("SELECT * FROM robots ORDER BY last_active DESC")
|
|
117
|
+
|
|
118
|
+
puts "Registered robots:"
|
|
119
|
+
result.each do |row|
|
|
120
|
+
puts "#{row['name']} (#{row['id']})"
|
|
121
|
+
puts " Created: #{row['created_at']}"
|
|
122
|
+
puts " Last active: #{row['last_active']}"
|
|
123
|
+
puts
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
conn.close
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Attribution Tracking
|
|
130
|
+
|
|
131
|
+
### Who Said What?
|
|
132
|
+
|
|
133
|
+
Track which robot contributed which memories:
|
|
134
|
+
|
|
135
|
+
```ruby
|
|
136
|
+
# Add memories from different robots
|
|
137
|
+
alpha = HTM.new(robot_name: "Alpha", robot_id: "alpha")
|
|
138
|
+
beta = HTM.new(robot_name: "Beta", robot_id: "beta")
|
|
139
|
+
|
|
140
|
+
alpha.add_node("alpha_001", "Alpha's insight about caching", type: :fact)
|
|
141
|
+
beta.add_node("beta_001", "Beta's approach to testing", type: :fact)
|
|
142
|
+
|
|
143
|
+
# Query by robot
|
|
144
|
+
def memories_by_robot(robot_id)
|
|
145
|
+
config = HTM::Database.default_config
|
|
146
|
+
conn = PG.connect(config)
|
|
147
|
+
|
|
148
|
+
result = conn.exec_params(
|
|
149
|
+
"SELECT key, value, type FROM nodes WHERE robot_id = $1",
|
|
150
|
+
[robot_id]
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
memories = result.to_a
|
|
154
|
+
conn.close
|
|
155
|
+
memories
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
alpha_memories = memories_by_robot("alpha")
|
|
159
|
+
puts "Alpha contributed #{alpha_memories.length} memories"
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Which Robot Said...?
|
|
163
|
+
|
|
164
|
+
Use HTM's built-in attribution tracking:
|
|
165
|
+
|
|
166
|
+
```ruby
|
|
167
|
+
# Find which robots discussed a topic
|
|
168
|
+
breakdown = htm.which_robot_said("PostgreSQL")
|
|
169
|
+
|
|
170
|
+
puts "Robots that discussed PostgreSQL:"
|
|
171
|
+
breakdown.each do |robot_id, count|
|
|
172
|
+
puts " #{robot_id}: #{count} mentions"
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Example output:
|
|
176
|
+
# Robots that discussed PostgreSQL:
|
|
177
|
+
# research-001: 15 mentions
|
|
178
|
+
# code-001: 8 mentions
|
|
179
|
+
# docs-001: 3 mentions
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Conversation Timeline
|
|
183
|
+
|
|
184
|
+
See the chronological conversation across robots:
|
|
185
|
+
|
|
186
|
+
```ruby
|
|
187
|
+
timeline = htm.conversation_timeline("architecture decisions", limit: 50)
|
|
188
|
+
|
|
189
|
+
puts "Architecture discussion timeline:"
|
|
190
|
+
timeline.each do |entry|
|
|
191
|
+
puts "#{entry[:timestamp]} - #{entry[:robot]}"
|
|
192
|
+
puts " [#{entry[:type]}] #{entry[:content][0..100]}..."
|
|
193
|
+
puts
|
|
194
|
+
end
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Collaboration Patterns
|
|
198
|
+
|
|
199
|
+
### Pattern 1: Specialized Roles
|
|
200
|
+
|
|
201
|
+
Each robot has a specific role and expertise:
|
|
202
|
+
|
|
203
|
+
```ruby
|
|
204
|
+
class MultiRobotSystem
|
|
205
|
+
def initialize
|
|
206
|
+
@researcher = HTM.new(
|
|
207
|
+
robot_name: "Researcher",
|
|
208
|
+
robot_id: "researcher-001"
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
@developer = HTM.new(
|
|
212
|
+
robot_name: "Developer",
|
|
213
|
+
robot_id: "developer-001"
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
@reviewer = HTM.new(
|
|
217
|
+
robot_name: "Reviewer",
|
|
218
|
+
robot_id: "reviewer-001"
|
|
219
|
+
)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def process_feature_request(feature)
|
|
223
|
+
# 1. Researcher gathers requirements
|
|
224
|
+
@researcher.add_node(
|
|
225
|
+
"research_#{feature}",
|
|
226
|
+
"Research findings for #{feature}",
|
|
227
|
+
type: :fact,
|
|
228
|
+
importance: 8.0,
|
|
229
|
+
tags: ["research", feature]
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
# 2. Developer recalls research and implements
|
|
233
|
+
research = @developer.recall(
|
|
234
|
+
timeframe: "last hour",
|
|
235
|
+
topic: "research #{feature}"
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
@developer.add_node(
|
|
239
|
+
"impl_#{feature}",
|
|
240
|
+
"Implementation plan based on research",
|
|
241
|
+
type: :decision,
|
|
242
|
+
importance: 9.0,
|
|
243
|
+
tags: ["implementation", feature],
|
|
244
|
+
related_to: ["research_#{feature}"]
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
# 3. Reviewer checks work
|
|
248
|
+
work = @reviewer.recall(
|
|
249
|
+
timeframe: "last hour",
|
|
250
|
+
topic: feature
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
@reviewer.add_node(
|
|
254
|
+
"review_#{feature}",
|
|
255
|
+
"Code review findings",
|
|
256
|
+
type: :context,
|
|
257
|
+
importance: 7.0,
|
|
258
|
+
tags: ["review", feature]
|
|
259
|
+
)
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
system = MultiRobotSystem.new
|
|
264
|
+
system.process_feature_request("user-authentication")
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Pattern 2: Shift Handoff
|
|
268
|
+
|
|
269
|
+
Robots pass context between shifts:
|
|
270
|
+
|
|
271
|
+
```ruby
|
|
272
|
+
class ShiftHandoff
|
|
273
|
+
def initialize
|
|
274
|
+
@current_shift = nil
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def start_shift(shift_name)
|
|
278
|
+
@current_shift = HTM.new(
|
|
279
|
+
robot_name: "#{shift_name} Bot",
|
|
280
|
+
robot_id: "shift-#{shift_name.downcase}"
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
# Recall context from previous shift
|
|
284
|
+
handoff = @current_shift.recall(
|
|
285
|
+
timeframe: "last 24 hours",
|
|
286
|
+
topic: "shift handoff urgent",
|
|
287
|
+
strategy: :hybrid,
|
|
288
|
+
limit: 20
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
puts "#{shift_name} shift starting"
|
|
292
|
+
puts "Received #{handoff.length} items from previous shift"
|
|
293
|
+
|
|
294
|
+
handoff
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def end_shift(summary)
|
|
298
|
+
# Document shift handoff
|
|
299
|
+
@current_shift.add_node(
|
|
300
|
+
"handoff_#{Time.now.to_i}",
|
|
301
|
+
summary,
|
|
302
|
+
type: :context,
|
|
303
|
+
importance: 9.0,
|
|
304
|
+
tags: ["shift-handoff", "urgent"]
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
puts "Shift handoff documented"
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# Usage
|
|
312
|
+
handoff = ShiftHandoff.new
|
|
313
|
+
|
|
314
|
+
# Morning shift
|
|
315
|
+
morning = handoff.start_shift("Morning")
|
|
316
|
+
# ... do morning work
|
|
317
|
+
handoff.end_shift("Three critical bugs fixed, deploy scheduled for 2pm")
|
|
318
|
+
|
|
319
|
+
# Afternoon shift
|
|
320
|
+
afternoon = handoff.start_shift("Afternoon")
|
|
321
|
+
# ... receives morning's summary
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Pattern 3: Expert Consultation
|
|
325
|
+
|
|
326
|
+
Specialized experts provide knowledge:
|
|
327
|
+
|
|
328
|
+
```ruby
|
|
329
|
+
class ExpertSystem
|
|
330
|
+
def initialize
|
|
331
|
+
@experts = {
|
|
332
|
+
database: HTM.new(
|
|
333
|
+
robot_name: "Database Expert",
|
|
334
|
+
robot_id: "expert-database"
|
|
335
|
+
),
|
|
336
|
+
security: HTM.new(
|
|
337
|
+
robot_name: "Security Expert",
|
|
338
|
+
robot_id: "expert-security"
|
|
339
|
+
),
|
|
340
|
+
performance: HTM.new(
|
|
341
|
+
robot_name: "Performance Expert",
|
|
342
|
+
robot_id: "expert-performance"
|
|
343
|
+
)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
@general = HTM.new(
|
|
347
|
+
robot_name: "General Assistant",
|
|
348
|
+
robot_id: "assistant-general"
|
|
349
|
+
)
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def consult(topic)
|
|
353
|
+
# Determine which expert to consult
|
|
354
|
+
expert_type = determine_expert(topic)
|
|
355
|
+
expert = @experts[expert_type]
|
|
356
|
+
|
|
357
|
+
# Get expert knowledge
|
|
358
|
+
knowledge = expert.recall(
|
|
359
|
+
timeframe: "all time",
|
|
360
|
+
topic: topic,
|
|
361
|
+
strategy: :hybrid,
|
|
362
|
+
limit: 10
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
# General assistant learns from expert
|
|
366
|
+
knowledge.each do |k|
|
|
367
|
+
@general.add_node(
|
|
368
|
+
"learned_#{SecureRandom.hex(4)}",
|
|
369
|
+
"Learned from #{expert_type} expert: #{k['value']}",
|
|
370
|
+
type: :fact,
|
|
371
|
+
importance: k['importance'],
|
|
372
|
+
tags: ["learned", expert_type.to_s],
|
|
373
|
+
related_to: [k['key']]
|
|
374
|
+
)
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
knowledge
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
private
|
|
381
|
+
|
|
382
|
+
def determine_expert(topic)
|
|
383
|
+
# Simple keyword matching
|
|
384
|
+
case topic.downcase
|
|
385
|
+
when /database|sql|query/
|
|
386
|
+
:database
|
|
387
|
+
when /security|auth|encryption/
|
|
388
|
+
:security
|
|
389
|
+
when /performance|speed|optimization/
|
|
390
|
+
:performance
|
|
391
|
+
else
|
|
392
|
+
:database # default
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
system = ExpertSystem.new
|
|
398
|
+
knowledge = system.consult("PostgreSQL query optimization")
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### Pattern 4: Collaborative Decision Making
|
|
402
|
+
|
|
403
|
+
Multiple robots contribute to decisions:
|
|
404
|
+
|
|
405
|
+
```ruby
|
|
406
|
+
class CollaborativeDecision
|
|
407
|
+
def initialize(topic)
|
|
408
|
+
@topic = topic
|
|
409
|
+
@participants = []
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
def add_participant(name, role)
|
|
413
|
+
bot = HTM.new(
|
|
414
|
+
robot_name: "#{name} (#{role})",
|
|
415
|
+
robot_id: "decision-#{role.downcase}-#{SecureRandom.hex(4)}"
|
|
416
|
+
)
|
|
417
|
+
@participants << { name: name, role: role, bot: bot }
|
|
418
|
+
bot
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
def gather_input(bot, opinion)
|
|
422
|
+
bot.add_node(
|
|
423
|
+
"opinion_#{SecureRandom.hex(4)}",
|
|
424
|
+
opinion,
|
|
425
|
+
type: :context,
|
|
426
|
+
importance: 8.0,
|
|
427
|
+
tags: ["decision", @topic, "opinion"]
|
|
428
|
+
)
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def make_decision(decision_maker)
|
|
432
|
+
# Recall all opinions
|
|
433
|
+
opinions = decision_maker.recall(
|
|
434
|
+
timeframe: "last hour",
|
|
435
|
+
topic: "decision #{@topic} opinion",
|
|
436
|
+
strategy: :hybrid,
|
|
437
|
+
limit: 50
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
puts "#{decision_maker.robot_name} considering:"
|
|
441
|
+
opinions.each do |opinion|
|
|
442
|
+
puts "- #{opinion['value'][0..100]}..."
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
# Document final decision
|
|
446
|
+
decision_maker.add_node(
|
|
447
|
+
"decision_#{@topic}_final",
|
|
448
|
+
"Final decision on #{@topic} after considering team input",
|
|
449
|
+
type: :decision,
|
|
450
|
+
importance: 10.0,
|
|
451
|
+
tags: ["decision", @topic, "final"]
|
|
452
|
+
)
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
# Usage
|
|
457
|
+
decision = CollaborativeDecision.new("database-choice")
|
|
458
|
+
|
|
459
|
+
# Gather input
|
|
460
|
+
developer = decision.add_participant("Alice", "Developer")
|
|
461
|
+
decision.gather_input(developer, "PostgreSQL for reliability")
|
|
462
|
+
|
|
463
|
+
architect = decision.add_participant("Bob", "Architect")
|
|
464
|
+
decision.gather_input(architect, "PostgreSQL for ACID compliance")
|
|
465
|
+
|
|
466
|
+
dba = decision.add_participant("Carol", "DBA")
|
|
467
|
+
decision.gather_input(dba, "PostgreSQL for operational maturity")
|
|
468
|
+
|
|
469
|
+
# Make decision
|
|
470
|
+
lead = decision.add_participant("Dave", "TechLead")
|
|
471
|
+
decision.make_decision(lead)
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
## Shared vs Private Knowledge
|
|
475
|
+
|
|
476
|
+
### Sharing Strategies
|
|
477
|
+
|
|
478
|
+
Control what gets shared:
|
|
479
|
+
|
|
480
|
+
```ruby
|
|
481
|
+
class SmartSharing
|
|
482
|
+
def initialize(robot_id)
|
|
483
|
+
@htm = HTM.new(robot_name: "Smart Bot", robot_id: robot_id)
|
|
484
|
+
@private_prefix = "private_#{robot_id}_"
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
def add_shared(key, value, **opts)
|
|
488
|
+
# Shared with all robots
|
|
489
|
+
@htm.add_node(key, value, **opts.merge(
|
|
490
|
+
tags: (opts[:tags] || []) + ["shared"]
|
|
491
|
+
))
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
def add_private(key, value, **opts)
|
|
495
|
+
# Use robot-specific key prefix
|
|
496
|
+
private_key = "#{@private_prefix}#{key}"
|
|
497
|
+
@htm.add_node(private_key, value, **opts.merge(
|
|
498
|
+
tags: (opts[:tags] || []) + ["private"],
|
|
499
|
+
importance: (opts[:importance] || 5.0)
|
|
500
|
+
))
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
def recall_shared(topic)
|
|
504
|
+
# Only shared knowledge
|
|
505
|
+
@htm.recall(
|
|
506
|
+
timeframe: "all time",
|
|
507
|
+
topic: "shared #{topic}",
|
|
508
|
+
strategy: :hybrid
|
|
509
|
+
).select { |m| m['tags']&.include?("shared") }
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
def recall_private(topic)
|
|
513
|
+
# Only my private knowledge
|
|
514
|
+
@htm.recall(
|
|
515
|
+
timeframe: "all time",
|
|
516
|
+
topic: topic,
|
|
517
|
+
strategy: :hybrid
|
|
518
|
+
).select { |m| m['key'].start_with?(@private_prefix) }
|
|
519
|
+
end
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
# Usage
|
|
523
|
+
bot1 = SmartSharing.new("bot-001")
|
|
524
|
+
bot1.add_shared("shared_fact", "Everyone should know this", type: :fact)
|
|
525
|
+
bot1.add_private("my_thought", "Private thought", type: :context)
|
|
526
|
+
|
|
527
|
+
bot2 = SmartSharing.new("bot-002")
|
|
528
|
+
shared = bot2.recall_shared("fact") # Can see shared_fact
|
|
529
|
+
private = bot2.recall_private("thought") # Won't see bot1's private thoughts
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
## Cross-Robot Queries
|
|
533
|
+
|
|
534
|
+
### Finding Robot Activity
|
|
535
|
+
|
|
536
|
+
```ruby
|
|
537
|
+
# Get all robots and their activity
|
|
538
|
+
def get_robot_activity
|
|
539
|
+
config = HTM::Database.default_config
|
|
540
|
+
conn = PG.connect(config)
|
|
541
|
+
|
|
542
|
+
result = conn.exec(
|
|
543
|
+
<<~SQL
|
|
544
|
+
SELECT
|
|
545
|
+
r.id,
|
|
546
|
+
r.name,
|
|
547
|
+
COUNT(n.id) as memory_count,
|
|
548
|
+
MAX(n.created_at) as last_memory,
|
|
549
|
+
r.last_active
|
|
550
|
+
FROM robots r
|
|
551
|
+
LEFT JOIN nodes n ON r.id = n.robot_id
|
|
552
|
+
GROUP BY r.id, r.name, r.last_active
|
|
553
|
+
ORDER BY r.last_active DESC
|
|
554
|
+
SQL
|
|
555
|
+
)
|
|
556
|
+
|
|
557
|
+
robots = result.to_a
|
|
558
|
+
conn.close
|
|
559
|
+
robots
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
# Display activity
|
|
563
|
+
robots = get_robot_activity
|
|
564
|
+
puts "Robot Activity Report:"
|
|
565
|
+
robots.each do |r|
|
|
566
|
+
puts "\n#{r['name']} (#{r['id']})"
|
|
567
|
+
puts " Memories: #{r['memory_count']}"
|
|
568
|
+
puts " Last memory: #{r['last_memory']}"
|
|
569
|
+
puts " Last active: #{r['last_active']}"
|
|
570
|
+
end
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
### Cross-Robot Search
|
|
574
|
+
|
|
575
|
+
```ruby
|
|
576
|
+
def search_across_robots(topic, limit_per_robot: 5)
|
|
577
|
+
config = HTM::Database.default_config
|
|
578
|
+
conn = PG.connect(config)
|
|
579
|
+
|
|
580
|
+
# Get all robots
|
|
581
|
+
robots = conn.exec("SELECT id, name FROM robots")
|
|
582
|
+
|
|
583
|
+
results = {}
|
|
584
|
+
|
|
585
|
+
robots.each do |robot|
|
|
586
|
+
# Search memories from this robot
|
|
587
|
+
stmt = conn.prepare(
|
|
588
|
+
"search_#{robot['id']}",
|
|
589
|
+
<<~SQL
|
|
590
|
+
SELECT key, value, type, importance, created_at
|
|
591
|
+
FROM nodes
|
|
592
|
+
WHERE robot_id = $1
|
|
593
|
+
AND to_tsvector('english', value) @@ plainto_tsquery('english', $2)
|
|
594
|
+
ORDER BY importance DESC
|
|
595
|
+
LIMIT $3
|
|
596
|
+
SQL
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
robot_results = conn.exec_prepared(
|
|
600
|
+
"search_#{robot['id']}",
|
|
601
|
+
[robot['id'], topic, limit_per_robot]
|
|
602
|
+
)
|
|
603
|
+
|
|
604
|
+
results[robot['name']] = robot_results.to_a
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
conn.close
|
|
608
|
+
results
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
# Usage
|
|
612
|
+
results = search_across_robots("authentication")
|
|
613
|
+
results.each do |robot_name, memories|
|
|
614
|
+
puts "\n=== #{robot_name} ==="
|
|
615
|
+
memories.each do |m|
|
|
616
|
+
puts "- [#{m['type']}] #{m['value'][0..80]}..."
|
|
617
|
+
end
|
|
618
|
+
end
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
## Monitoring Multi-Robot Systems
|
|
622
|
+
|
|
623
|
+
### Dashboard
|
|
624
|
+
|
|
625
|
+
```ruby
|
|
626
|
+
class MultiRobotDashboard
|
|
627
|
+
def initialize
|
|
628
|
+
@config = HTM::Database.default_config
|
|
629
|
+
end
|
|
630
|
+
|
|
631
|
+
def summary
|
|
632
|
+
conn = PG.connect(@config)
|
|
633
|
+
|
|
634
|
+
# Total stats
|
|
635
|
+
total_robots = conn.exec("SELECT COUNT(*) FROM robots").first['count'].to_i
|
|
636
|
+
total_memories = conn.exec("SELECT COUNT(*) FROM nodes").first['count'].to_i
|
|
637
|
+
|
|
638
|
+
# Per-robot breakdown
|
|
639
|
+
breakdown = conn.exec(
|
|
640
|
+
<<~SQL
|
|
641
|
+
SELECT
|
|
642
|
+
r.name,
|
|
643
|
+
COUNT(n.id) as memories,
|
|
644
|
+
AVG(n.importance) as avg_importance,
|
|
645
|
+
MAX(n.created_at) as last_contribution
|
|
646
|
+
FROM robots r
|
|
647
|
+
LEFT JOIN nodes n ON r.id = n.robot_id
|
|
648
|
+
GROUP BY r.id, r.name
|
|
649
|
+
ORDER BY memories DESC
|
|
650
|
+
SQL
|
|
651
|
+
).to_a
|
|
652
|
+
|
|
653
|
+
conn.close
|
|
654
|
+
|
|
655
|
+
{
|
|
656
|
+
total_robots: total_robots,
|
|
657
|
+
total_memories: total_memories,
|
|
658
|
+
breakdown: breakdown
|
|
659
|
+
}
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
def print_summary
|
|
663
|
+
data = summary
|
|
664
|
+
|
|
665
|
+
puts "=== Multi-Robot System Dashboard ==="
|
|
666
|
+
puts "Total robots: #{data[:total_robots]}"
|
|
667
|
+
puts "Total memories: #{data[:total_memories]}"
|
|
668
|
+
puts "\nPer-robot breakdown:"
|
|
669
|
+
|
|
670
|
+
data[:breakdown].each do |robot|
|
|
671
|
+
puts "\n#{robot['name']}"
|
|
672
|
+
puts " Memories: #{robot['memories']}"
|
|
673
|
+
puts " Avg importance: #{robot['avg_importance'].to_f.round(2)}"
|
|
674
|
+
puts " Last contribution: #{robot['last_contribution']}"
|
|
675
|
+
end
|
|
676
|
+
end
|
|
677
|
+
end
|
|
678
|
+
|
|
679
|
+
dashboard = MultiRobotDashboard.new
|
|
680
|
+
dashboard.print_summary
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
## Best Practices
|
|
684
|
+
|
|
685
|
+
### 1. Clear Robot Roles
|
|
686
|
+
|
|
687
|
+
```ruby
|
|
688
|
+
# Good: Clear, specific roles
|
|
689
|
+
researcher = HTM.new(robot_name: "Research Specialist", robot_id: "research-001")
|
|
690
|
+
coder = HTM.new(robot_name: "Code Generator", robot_id: "coder-001")
|
|
691
|
+
|
|
692
|
+
# Avoid: Vague roles
|
|
693
|
+
bot1 = HTM.new(robot_name: "Bot 1", robot_id: "bot1")
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
### 2. Consistent Naming
|
|
697
|
+
|
|
698
|
+
```ruby
|
|
699
|
+
# Good: Consistent naming scheme
|
|
700
|
+
class RobotFactory
|
|
701
|
+
def self.create(service, purpose, instance = "001")
|
|
702
|
+
HTM.new(
|
|
703
|
+
robot_name: "#{service.capitalize} #{purpose.capitalize}",
|
|
704
|
+
robot_id: "#{service}-#{purpose}-#{instance}"
|
|
705
|
+
)
|
|
706
|
+
end
|
|
707
|
+
end
|
|
708
|
+
|
|
709
|
+
api_assistant = RobotFactory.create("api", "assistant", "001")
|
|
710
|
+
api_validator = RobotFactory.create("api", "validator", "001")
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
### 3. Attribution in Content
|
|
714
|
+
|
|
715
|
+
```ruby
|
|
716
|
+
# Include attribution in the content itself
|
|
717
|
+
bot.add_node(
|
|
718
|
+
"finding_001",
|
|
719
|
+
"Research by #{bot.robot_name}: PostgreSQL outperforms MongoDB",
|
|
720
|
+
type: :fact,
|
|
721
|
+
importance: 8.0
|
|
722
|
+
)
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
### 4. Regular Reconciliation
|
|
726
|
+
|
|
727
|
+
```ruby
|
|
728
|
+
# Periodically sync understanding across robots
|
|
729
|
+
def sync_robots(*robots)
|
|
730
|
+
# Find recent high-importance memories
|
|
731
|
+
shared_knowledge = robots.first.recall(
|
|
732
|
+
timeframe: "last 24 hours",
|
|
733
|
+
topic: "important shared",
|
|
734
|
+
strategy: :hybrid,
|
|
735
|
+
limit: 50
|
|
736
|
+
).select { |m| m['importance'].to_f >= 8.0 }
|
|
737
|
+
|
|
738
|
+
puts "Syncing #{shared_knowledge.length} important memories across #{robots.length} robots"
|
|
739
|
+
end
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
### 5. Clean Up Inactive Robots
|
|
743
|
+
|
|
744
|
+
```ruby
|
|
745
|
+
def cleanup_inactive_robots(days: 30)
|
|
746
|
+
config = HTM::Database.default_config
|
|
747
|
+
conn = PG.connect(config)
|
|
748
|
+
|
|
749
|
+
cutoff = Time.now - (days * 24 * 3600)
|
|
750
|
+
|
|
751
|
+
result = conn.exec_params(
|
|
752
|
+
"SELECT id, name FROM robots WHERE last_active < $1",
|
|
753
|
+
[cutoff]
|
|
754
|
+
)
|
|
755
|
+
|
|
756
|
+
puts "Inactive robots (last active > #{days} days):"
|
|
757
|
+
result.each do |robot|
|
|
758
|
+
puts "- #{robot['name']} (#{robot['id']})"
|
|
759
|
+
end
|
|
760
|
+
|
|
761
|
+
conn.close
|
|
762
|
+
end
|
|
763
|
+
|
|
764
|
+
cleanup_inactive_robots(days: 90)
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
## Complete Example
|
|
768
|
+
|
|
769
|
+
```ruby
|
|
770
|
+
require 'htm'
|
|
771
|
+
|
|
772
|
+
# Create a multi-robot development team
|
|
773
|
+
class DevTeam
|
|
774
|
+
def initialize
|
|
775
|
+
@analyst = HTM.new(
|
|
776
|
+
robot_name: "Requirements Analyst",
|
|
777
|
+
robot_id: "team-analyst-001"
|
|
778
|
+
)
|
|
779
|
+
|
|
780
|
+
@developer = HTM.new(
|
|
781
|
+
robot_name: "Senior Developer",
|
|
782
|
+
robot_id: "team-developer-001"
|
|
783
|
+
)
|
|
784
|
+
|
|
785
|
+
@tester = HTM.new(
|
|
786
|
+
robot_name: "QA Tester",
|
|
787
|
+
robot_id: "team-tester-001"
|
|
788
|
+
)
|
|
789
|
+
end
|
|
790
|
+
|
|
791
|
+
def process_feature(feature_name)
|
|
792
|
+
puts "\n=== Processing Feature: #{feature_name} ==="
|
|
793
|
+
|
|
794
|
+
# 1. Analyst documents requirements
|
|
795
|
+
puts "\n1. Analyst gathering requirements..."
|
|
796
|
+
@analyst.add_node(
|
|
797
|
+
"req_#{feature_name}",
|
|
798
|
+
"Requirements for #{feature_name}: Must support OAuth2",
|
|
799
|
+
type: :fact,
|
|
800
|
+
importance: 9.0,
|
|
801
|
+
tags: ["requirements", feature_name]
|
|
802
|
+
)
|
|
803
|
+
|
|
804
|
+
# 2. Developer recalls requirements and designs
|
|
805
|
+
puts "\n2. Developer reviewing requirements..."
|
|
806
|
+
requirements = @developer.recall(
|
|
807
|
+
timeframe: "last hour",
|
|
808
|
+
topic: "requirements #{feature_name}"
|
|
809
|
+
)
|
|
810
|
+
|
|
811
|
+
puts "Found #{requirements.length} requirements"
|
|
812
|
+
|
|
813
|
+
@developer.add_node(
|
|
814
|
+
"design_#{feature_name}",
|
|
815
|
+
"Design for #{feature_name} based on requirements",
|
|
816
|
+
type: :decision,
|
|
817
|
+
importance: 9.0,
|
|
818
|
+
tags: ["design", feature_name],
|
|
819
|
+
related_to: ["req_#{feature_name}"]
|
|
820
|
+
)
|
|
821
|
+
|
|
822
|
+
# 3. Tester recalls everything and creates test plan
|
|
823
|
+
puts "\n3. Tester creating test plan..."
|
|
824
|
+
context = @tester.recall(
|
|
825
|
+
timeframe: "last hour",
|
|
826
|
+
topic: feature_name,
|
|
827
|
+
strategy: :hybrid
|
|
828
|
+
)
|
|
829
|
+
|
|
830
|
+
puts "Tester reviewed #{context.length} items"
|
|
831
|
+
|
|
832
|
+
@tester.add_node(
|
|
833
|
+
"test_#{feature_name}",
|
|
834
|
+
"Test plan for #{feature_name}",
|
|
835
|
+
type: :context,
|
|
836
|
+
importance: 8.0,
|
|
837
|
+
tags: ["testing", feature_name],
|
|
838
|
+
related_to: ["design_#{feature_name}", "req_#{feature_name}"]
|
|
839
|
+
)
|
|
840
|
+
|
|
841
|
+
# 4. Show collaboration
|
|
842
|
+
puts "\n4. Collaboration summary:"
|
|
843
|
+
timeline = @analyst.conversation_timeline(feature_name)
|
|
844
|
+
timeline.each do |entry|
|
|
845
|
+
puts "- #{entry[:robot]}: #{entry[:type]}"
|
|
846
|
+
end
|
|
847
|
+
|
|
848
|
+
# 5. Show attribution
|
|
849
|
+
puts "\n5. Who contributed:"
|
|
850
|
+
breakdown = @analyst.which_robot_said(feature_name)
|
|
851
|
+
breakdown.each do |robot_id, count|
|
|
852
|
+
puts "- #{robot_id}: #{count} memories"
|
|
853
|
+
end
|
|
854
|
+
end
|
|
855
|
+
end
|
|
856
|
+
|
|
857
|
+
# Run the team
|
|
858
|
+
team = DevTeam.new
|
|
859
|
+
team.process_feature("oauth-integration")
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
## Next Steps
|
|
863
|
+
|
|
864
|
+
- [**Context Assembly**](context-assembly.md) - Build context from multi-robot memories
|
|
865
|
+
- [**Long-term Memory**](long-term-memory.md) - Understand the shared storage layer
|
|
866
|
+
- [**Search Strategies**](search-strategies.md) - Find relevant memories across robots
|