robot_lab 0.0.1 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/deploy-github-pages.yml +9 -9
- data/.irbrc +6 -0
- data/CHANGELOG.md +140 -0
- data/README.md +263 -48
- data/Rakefile +71 -1
- data/docs/api/core/index.md +53 -46
- data/docs/api/core/memory.md +200 -154
- data/docs/api/core/network.md +13 -3
- data/docs/api/core/robot.md +490 -130
- data/docs/api/core/state.md +55 -73
- data/docs/api/core/tool.md +205 -209
- data/docs/api/index.md +7 -28
- data/docs/api/mcp/client.md +119 -48
- data/docs/api/mcp/index.md +75 -60
- data/docs/api/mcp/server.md +120 -136
- data/docs/api/mcp/transports.md +172 -184
- data/docs/api/messages/index.md +35 -20
- data/docs/api/messages/text-message.md +67 -21
- data/docs/api/messages/tool-call-message.md +80 -41
- data/docs/api/messages/tool-result-message.md +119 -50
- data/docs/api/messages/user-message.md +48 -24
- data/docs/api/streaming/context.md +157 -74
- data/docs/api/streaming/events.md +114 -166
- data/docs/api/streaming/index.md +74 -72
- data/docs/architecture/core-concepts.md +360 -116
- data/docs/architecture/index.md +97 -59
- data/docs/architecture/message-flow.md +138 -129
- data/docs/architecture/network-orchestration.md +197 -50
- data/docs/architecture/robot-execution.md +199 -146
- data/docs/architecture/state-management.md +255 -187
- data/docs/concepts.md +311 -49
- data/docs/examples/basic-chat.md +89 -77
- data/docs/examples/index.md +222 -47
- data/docs/examples/mcp-server.md +207 -203
- data/docs/examples/multi-robot-network.md +129 -35
- data/docs/examples/rails-application.md +159 -160
- data/docs/examples/tool-usage.md +295 -204
- data/docs/getting-started/configuration.md +347 -154
- data/docs/getting-started/index.md +1 -1
- data/docs/getting-started/installation.md +22 -13
- data/docs/getting-started/quick-start.md +166 -121
- data/docs/guides/building-robots.md +418 -212
- data/docs/guides/creating-networks.md +143 -24
- data/docs/guides/index.md +0 -5
- data/docs/guides/mcp-integration.md +152 -113
- data/docs/guides/memory.md +220 -164
- data/docs/guides/rails-integration.md +244 -162
- data/docs/guides/streaming.md +137 -187
- data/docs/guides/using-tools.md +259 -212
- data/docs/index.md +46 -41
- data/examples/01_simple_robot.rb +6 -9
- data/examples/02_tools.rb +6 -9
- data/examples/03_network.rb +19 -17
- data/examples/04_mcp.rb +5 -8
- data/examples/05_streaming.rb +5 -8
- data/examples/06_prompt_templates.rb +42 -37
- data/examples/07_network_memory.rb +13 -14
- data/examples/08_llm_config.rb +169 -0
- data/examples/09_chaining.rb +262 -0
- data/examples/10_memory.rb +331 -0
- data/examples/11_network_introspection.rb +253 -0
- data/examples/12_message_bus.rb +74 -0
- data/examples/13_spawn.rb +90 -0
- data/examples/14_rusty_circuit/comic.rb +143 -0
- data/examples/14_rusty_circuit/display.rb +203 -0
- data/examples/14_rusty_circuit/heckler.rb +63 -0
- data/examples/14_rusty_circuit/open_mic.rb +123 -0
- data/examples/14_rusty_circuit/prompts/open_mic_comic.md +20 -0
- data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +23 -0
- data/examples/14_rusty_circuit/prompts/open_mic_scout.md +20 -0
- data/examples/14_rusty_circuit/scout.rb +156 -0
- data/examples/14_rusty_circuit/scout_notes.md +89 -0
- data/examples/14_rusty_circuit/show.log +234 -0
- data/examples/15_memory_network_and_bus/editor_in_chief.rb +24 -0
- data/examples/15_memory_network_and_bus/editorial_pipeline.rb +206 -0
- data/examples/15_memory_network_and_bus/linux_writer.rb +80 -0
- data/examples/15_memory_network_and_bus/os_editor.rb +46 -0
- data/examples/15_memory_network_and_bus/os_writer.rb +46 -0
- data/examples/15_memory_network_and_bus/output/combined_article.md +13 -0
- data/examples/15_memory_network_and_bus/output/final_article.md +15 -0
- data/examples/15_memory_network_and_bus/output/linux_draft.md +5 -0
- data/examples/15_memory_network_and_bus/output/mac_draft.md +7 -0
- data/examples/15_memory_network_and_bus/output/memory.json +13 -0
- data/examples/15_memory_network_and_bus/output/revision_1.md +19 -0
- data/examples/15_memory_network_and_bus/output/revision_2.md +15 -0
- data/examples/15_memory_network_and_bus/output/windows_draft.md +7 -0
- data/examples/15_memory_network_and_bus/prompts/os_advocate.md +13 -0
- data/examples/15_memory_network_and_bus/prompts/os_chief.md +13 -0
- data/examples/15_memory_network_and_bus/prompts/os_editor.md +13 -0
- data/examples/16_writers_room/display.rb +158 -0
- data/examples/16_writers_room/output/.gitignore +2 -0
- data/examples/16_writers_room/output/opus_001.md +263 -0
- data/examples/16_writers_room/output/opus_001_notes.log +470 -0
- data/examples/16_writers_room/prompts/writer.md +37 -0
- data/examples/16_writers_room/room.rb +150 -0
- data/examples/16_writers_room/tools.rb +162 -0
- data/examples/16_writers_room/writer.rb +121 -0
- data/examples/16_writers_room/writers_room.rb +162 -0
- data/examples/README.md +197 -0
- data/examples/prompts/{assistant/system.txt.erb → assistant.md} +3 -0
- data/examples/prompts/{billing/system.txt.erb → billing.md} +3 -0
- data/examples/prompts/{classifier/system.txt.erb → classifier.md} +3 -0
- data/examples/prompts/comedian.md +6 -0
- data/examples/prompts/comedy_critic.md +10 -0
- data/examples/prompts/configurable.md +9 -0
- data/examples/prompts/dispatcher.md +12 -0
- data/examples/prompts/{entity_extractor/system.txt.erb → entity_extractor.md} +3 -0
- data/examples/prompts/{escalation/system.txt.erb → escalation.md} +7 -0
- data/examples/prompts/frontmatter_mcp_test.md +9 -0
- data/examples/prompts/frontmatter_named_test.md +5 -0
- data/examples/prompts/frontmatter_tools_test.md +6 -0
- data/examples/prompts/{general/system.txt.erb → general.md} +3 -0
- data/examples/prompts/{github_assistant/system.txt.erb → github_assistant.md} +8 -0
- data/examples/prompts/{helper/system.txt.erb → helper.md} +3 -0
- data/examples/prompts/{keyword_extractor/system.txt.erb → keyword_extractor.md} +3 -0
- data/examples/prompts/llm_config_demo.md +20 -0
- data/examples/prompts/{order_support/system.txt.erb → order_support.md} +8 -0
- data/examples/prompts/os_advocate.md +13 -0
- data/examples/prompts/os_chief.md +13 -0
- data/examples/prompts/os_editor.md +13 -0
- data/examples/prompts/{product_support/system.txt.erb → product_support.md} +7 -0
- data/examples/prompts/{sentiment_analyzer/system.txt.erb → sentiment_analyzer.md} +3 -0
- data/examples/prompts/{synthesizer/system.txt.erb → synthesizer.md} +3 -0
- data/examples/prompts/{technical/system.txt.erb → technical.md} +3 -0
- data/examples/prompts/{triage/system.txt.erb → triage.md} +6 -0
- data/lib/generators/robot_lab/templates/initializer.rb.tt +0 -13
- data/lib/robot_lab/ask_user.rb +75 -0
- data/lib/robot_lab/config/defaults.yml +121 -0
- data/lib/robot_lab/config.rb +183 -0
- data/lib/robot_lab/error.rb +6 -0
- data/lib/robot_lab/mcp/client.rb +1 -1
- data/lib/robot_lab/memory.rb +10 -34
- data/lib/robot_lab/network.rb +13 -20
- data/lib/robot_lab/robot/bus_messaging.rb +239 -0
- data/lib/robot_lab/robot/mcp_management.rb +88 -0
- data/lib/robot_lab/robot/template_rendering.rb +130 -0
- data/lib/robot_lab/robot.rb +240 -330
- data/lib/robot_lab/robot_message.rb +44 -0
- data/lib/robot_lab/robot_result.rb +1 -0
- data/lib/robot_lab/run_config.rb +184 -0
- data/lib/robot_lab/state_proxy.rb +2 -12
- data/lib/robot_lab/streaming/context.rb +1 -1
- data/lib/robot_lab/task.rb +8 -1
- data/lib/robot_lab/tool.rb +108 -172
- data/lib/robot_lab/tool_config.rb +1 -1
- data/lib/robot_lab/tool_manifest.rb +2 -18
- data/lib/robot_lab/utils.rb +39 -0
- data/lib/robot_lab/version.rb +1 -1
- data/lib/robot_lab.rb +89 -57
- data/mkdocs.yml +0 -11
- metadata +121 -135
- data/docs/api/adapters/anthropic.md +0 -121
- data/docs/api/adapters/gemini.md +0 -133
- data/docs/api/adapters/index.md +0 -104
- data/docs/api/adapters/openai.md +0 -134
- data/docs/api/history/active-record-adapter.md +0 -195
- data/docs/api/history/config.md +0 -191
- data/docs/api/history/index.md +0 -132
- data/docs/api/history/thread-manager.md +0 -144
- data/docs/guides/history.md +0 -359
- data/examples/prompts/assistant/user.txt.erb +0 -1
- data/examples/prompts/billing/user.txt.erb +0 -1
- data/examples/prompts/classifier/user.txt.erb +0 -1
- data/examples/prompts/entity_extractor/user.txt.erb +0 -3
- data/examples/prompts/escalation/user.txt.erb +0 -34
- data/examples/prompts/general/user.txt.erb +0 -1
- data/examples/prompts/github_assistant/user.txt.erb +0 -1
- data/examples/prompts/helper/user.txt.erb +0 -1
- data/examples/prompts/keyword_extractor/user.txt.erb +0 -3
- data/examples/prompts/order_support/user.txt.erb +0 -22
- data/examples/prompts/product_support/user.txt.erb +0 -32
- data/examples/prompts/sentiment_analyzer/user.txt.erb +0 -3
- data/examples/prompts/synthesizer/user.txt.erb +0 -15
- data/examples/prompts/technical/user.txt.erb +0 -1
- data/examples/prompts/triage/user.txt.erb +0 -17
- data/lib/robot_lab/adapters/anthropic.rb +0 -163
- data/lib/robot_lab/adapters/base.rb +0 -85
- data/lib/robot_lab/adapters/gemini.rb +0 -193
- data/lib/robot_lab/adapters/openai.rb +0 -159
- data/lib/robot_lab/adapters/registry.rb +0 -81
- data/lib/robot_lab/configuration.rb +0 -143
- data/lib/robot_lab/errors.rb +0 -70
- data/lib/robot_lab/history/active_record_adapter.rb +0 -146
- data/lib/robot_lab/history/config.rb +0 -115
- data/lib/robot_lab/history/thread_manager.rb +0 -93
- data/lib/robot_lab/robotic_model.rb +0 -324
data/docs/guides/memory.md
CHANGED
|
@@ -1,259 +1,315 @@
|
|
|
1
1
|
# Memory System
|
|
2
2
|
|
|
3
|
-
The memory system
|
|
3
|
+
The memory system provides key-value storage for robots, supporting both standalone and network execution modes.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
Memory provides:
|
|
7
|
+
Memory is a reactive key-value store that provides:
|
|
8
8
|
|
|
9
|
-
- Key-value storage
|
|
10
|
-
-
|
|
11
|
-
-
|
|
9
|
+
- Key-value storage with `[]` and `[]=` accessors
|
|
10
|
+
- Reserved keys for structured data (`:data`, `:results`, `:messages`, `:session_id`, `:cache`)
|
|
11
|
+
- Reactive subscriptions and blocking reads for inter-robot communication
|
|
12
|
+
- Optional Redis backend for persistence
|
|
13
|
+
- Semantic caching via `RubyLLM::SemanticCache`
|
|
12
14
|
|
|
13
|
-
##
|
|
15
|
+
## Standalone Robot Memory
|
|
16
|
+
|
|
17
|
+
Every robot has its own inherent memory that persists across runs:
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
robot = RobotLab.build(
|
|
21
|
+
name: "assistant",
|
|
22
|
+
system_prompt: "You are helpful."
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# Memory persists across runs
|
|
26
|
+
robot.memory[:user_name] = "Alice"
|
|
27
|
+
robot.memory[:preferences] = { theme: "dark", language: "en" }
|
|
28
|
+
|
|
29
|
+
result = robot.run("Hello!")
|
|
30
|
+
|
|
31
|
+
# Read it back later
|
|
32
|
+
robot.memory[:user_name] # => "Alice"
|
|
33
|
+
robot.memory[:preferences] # => { theme: "dark", language: "en" }
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Basic Operations
|
|
14
37
|
|
|
15
38
|
### Store Values
|
|
16
39
|
|
|
17
40
|
```ruby
|
|
18
|
-
|
|
19
|
-
|
|
41
|
+
robot.memory[:key] = "value"
|
|
42
|
+
robot.memory[:count] = 42
|
|
43
|
+
robot.memory[:config] = { timeout: 30, retries: 3 }
|
|
20
44
|
```
|
|
21
45
|
|
|
22
46
|
### Retrieve Values
|
|
23
47
|
|
|
24
48
|
```ruby
|
|
25
|
-
name =
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
# Returns nil if not found
|
|
29
|
-
missing = state.memory.recall("unknown") # => nil
|
|
49
|
+
name = robot.memory[:user_name] # => "Alice"
|
|
50
|
+
missing = robot.memory[:unknown] # => nil
|
|
30
51
|
```
|
|
31
52
|
|
|
32
53
|
### Check Existence
|
|
33
54
|
|
|
34
55
|
```ruby
|
|
35
|
-
|
|
36
|
-
|
|
56
|
+
robot.memory.key?(:user_name) # => true
|
|
57
|
+
robot.memory.key?(:unknown) # => false
|
|
37
58
|
```
|
|
38
59
|
|
|
39
|
-
###
|
|
60
|
+
### Delete Values
|
|
40
61
|
|
|
41
62
|
```ruby
|
|
42
|
-
|
|
63
|
+
robot.memory.delete(:temp_data)
|
|
43
64
|
```
|
|
44
65
|
|
|
45
|
-
|
|
66
|
+
### List Keys
|
|
46
67
|
|
|
47
|
-
|
|
68
|
+
```ruby
|
|
69
|
+
robot.memory.keys # => [:user_name, :preferences] (excludes reserved keys)
|
|
70
|
+
robot.memory.all_keys # => [:data, :results, :messages, :session_id, :cache, :user_name, ...]
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Merge Values
|
|
48
74
|
|
|
49
75
|
```ruby
|
|
50
|
-
|
|
51
|
-
|
|
76
|
+
robot.memory.merge!(user_id: 123, session: "abc")
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Reserved Keys
|
|
52
80
|
|
|
53
|
-
|
|
54
|
-
user_memory.remember("name", "Alice")
|
|
55
|
-
user_memory.remember("email", "alice@example.com")
|
|
81
|
+
Memory has reserved keys with special behavior:
|
|
56
82
|
|
|
57
|
-
|
|
58
|
-
|
|
83
|
+
| Key | Type | Description |
|
|
84
|
+
|-----|------|-------------|
|
|
85
|
+
| `:data` | Hash (StateProxy) | Runtime data with method-style access |
|
|
86
|
+
| `:results` | Array | Accumulated robot results |
|
|
87
|
+
| `:messages` | Array | Conversation history |
|
|
88
|
+
| `:session_id` | String | Session identifier for history persistence |
|
|
89
|
+
| `:cache` | SemanticCache | Semantic cache (read-only after init) |
|
|
59
90
|
|
|
60
|
-
|
|
61
|
-
|
|
91
|
+
### The Data Hash
|
|
92
|
+
|
|
93
|
+
The `:data` key provides a `StateProxy` for method-style access:
|
|
94
|
+
|
|
95
|
+
```ruby
|
|
96
|
+
robot.memory.data[:category] = "billing"
|
|
97
|
+
robot.memory.data.category # => "billing" (method-style access)
|
|
98
|
+
robot.memory.data.to_h # => { category: "billing" }
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Results and Messages
|
|
102
|
+
|
|
103
|
+
```ruby
|
|
104
|
+
robot.memory.results # => Array of RobotResult objects
|
|
105
|
+
robot.memory.messages # => Array of Message objects
|
|
106
|
+
robot.memory.session_id # => "abc123" or nil
|
|
62
107
|
```
|
|
63
108
|
|
|
64
|
-
|
|
109
|
+
## Runtime Memory Injection
|
|
110
|
+
|
|
111
|
+
Pass memory values for a single run using the `memory:` keyword:
|
|
65
112
|
|
|
66
113
|
```ruby
|
|
67
|
-
|
|
68
|
-
|
|
114
|
+
# Inject a hash -- values are merged into the active memory
|
|
115
|
+
result = robot.run("What's my order status?", memory: { user_id: 123, order_id: "ORD-456" })
|
|
69
116
|
|
|
70
|
-
|
|
71
|
-
#
|
|
117
|
+
# The robot's memory now contains those keys
|
|
118
|
+
robot.memory[:user_id] # => 123
|
|
119
|
+
robot.memory[:order_id] # => "ORD-456"
|
|
72
120
|
```
|
|
73
121
|
|
|
74
|
-
|
|
122
|
+
You can also pass a full `Memory` object to replace the active memory for that run:
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
custom_memory = RobotLab.create_memory(data: { user_id: 123 })
|
|
126
|
+
custom_memory[:context] = "billing inquiry"
|
|
75
127
|
|
|
76
|
-
|
|
128
|
+
result = robot.run("Help me", memory: custom_memory)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Resetting Memory
|
|
132
|
+
|
|
133
|
+
Clear a robot's memory back to its initial state:
|
|
77
134
|
|
|
78
135
|
```ruby
|
|
79
|
-
|
|
80
|
-
# =>
|
|
81
|
-
# "user_name" => "Alice",
|
|
82
|
-
# "user:123:email" => "alice@example.com",
|
|
83
|
-
# ...
|
|
84
|
-
# }
|
|
136
|
+
robot.reset_memory
|
|
137
|
+
robot.memory.keys # => [] (custom keys cleared, reserved keys reset)
|
|
85
138
|
```
|
|
86
139
|
|
|
87
|
-
|
|
140
|
+
You can also clear just the custom keys without resetting reserved keys:
|
|
88
141
|
|
|
89
142
|
```ruby
|
|
90
|
-
|
|
91
|
-
# => ["user:123", "session:abc", ...]
|
|
143
|
+
robot.memory.clear # Clears non-reserved keys only
|
|
92
144
|
```
|
|
93
145
|
|
|
94
|
-
|
|
146
|
+
## Network Shared Memory
|
|
147
|
+
|
|
148
|
+
When robots run in a network, they share the network's memory instead of using their own inherent memory. This allows robots to communicate through shared state:
|
|
95
149
|
|
|
96
150
|
```ruby
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
151
|
+
network = RobotLab.create_network(name: "pipeline") do
|
|
152
|
+
task :analyzer, analyzer_robot, depends_on: :none
|
|
153
|
+
task :writer, writer_robot, depends_on: [:analyzer]
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# All robots in the network share this memory
|
|
157
|
+
network.memory[:project] = "quarterly_report"
|
|
158
|
+
|
|
159
|
+
result = network.run(message: "Analyze sales data")
|
|
160
|
+
|
|
161
|
+
# After the run, shared memory contains values written by all robots
|
|
162
|
+
network.memory[:analysis_result] # Written by analyzer
|
|
163
|
+
network.memory[:draft] # Written by writer
|
|
100
164
|
```
|
|
101
165
|
|
|
102
|
-
###
|
|
166
|
+
### Resetting Network Memory
|
|
103
167
|
|
|
104
168
|
```ruby
|
|
105
|
-
|
|
106
|
-
# => { total_keys: 15, namespaces: ["user:123", "session"] }
|
|
169
|
+
network.reset_memory # Clear shared memory between runs
|
|
107
170
|
```
|
|
108
171
|
|
|
109
|
-
|
|
172
|
+
## Reactive Memory
|
|
173
|
+
|
|
174
|
+
Memory supports reactive features for concurrent robot execution.
|
|
175
|
+
|
|
176
|
+
### Blocking Reads
|
|
177
|
+
|
|
178
|
+
Wait for a value to become available (useful in parallel pipelines):
|
|
110
179
|
|
|
111
180
|
```ruby
|
|
112
|
-
#
|
|
113
|
-
|
|
181
|
+
# In robot A (writer)
|
|
182
|
+
memory.set(:sentiment, { score: 0.8, confidence: 0.95 })
|
|
183
|
+
|
|
184
|
+
# In robot B (reader, may run concurrently)
|
|
185
|
+
result = memory.get(:sentiment, wait: true) # Blocks until available
|
|
186
|
+
result = memory.get(:sentiment, wait: 30) # Blocks up to 30 seconds
|
|
114
187
|
|
|
115
|
-
#
|
|
116
|
-
|
|
188
|
+
# Multiple keys
|
|
189
|
+
results = memory.get(:sentiment, :entities, :keywords, wait: 60)
|
|
190
|
+
# => { sentiment: {...}, entities: [...], keywords: [...] }
|
|
117
191
|
```
|
|
118
192
|
|
|
119
|
-
|
|
193
|
+
### Subscriptions
|
|
120
194
|
|
|
121
|
-
|
|
195
|
+
Subscribe to key changes with asynchronous callbacks:
|
|
122
196
|
|
|
123
197
|
```ruby
|
|
124
|
-
#
|
|
125
|
-
|
|
198
|
+
# Subscribe to a single key
|
|
199
|
+
memory.subscribe(:raw_data) do |change|
|
|
200
|
+
puts "#{change.key} changed by #{change.writer}"
|
|
201
|
+
puts "Old: #{change.previous}, New: #{change.value}"
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Subscribe to multiple keys
|
|
205
|
+
memory.subscribe(:sentiment, :entities) do |change|
|
|
206
|
+
update_dashboard(change.key, change.value)
|
|
207
|
+
end
|
|
126
208
|
|
|
127
|
-
#
|
|
128
|
-
|
|
209
|
+
# Pattern subscriptions (glob-style)
|
|
210
|
+
memory.subscribe_pattern("analysis:*") do |change|
|
|
211
|
+
puts "Analysis key #{change.key} updated"
|
|
212
|
+
end
|
|
129
213
|
```
|
|
130
214
|
|
|
131
|
-
###
|
|
215
|
+
### Unsubscribe
|
|
132
216
|
|
|
133
217
|
```ruby
|
|
134
|
-
|
|
135
|
-
|
|
218
|
+
sub_id = memory.subscribe(:status) { |c| puts c.value }
|
|
219
|
+
memory.unsubscribe(sub_id)
|
|
136
220
|
```
|
|
137
221
|
|
|
138
|
-
##
|
|
222
|
+
## Creating Standalone Memory
|
|
139
223
|
|
|
140
|
-
|
|
224
|
+
Use the factory method for standalone memory objects:
|
|
141
225
|
|
|
142
226
|
```ruby
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
prefs.remember(key, value)
|
|
151
|
-
{ success: true, key: key, value: value }
|
|
152
|
-
end
|
|
153
|
-
end
|
|
227
|
+
memory = RobotLab.create_memory(
|
|
228
|
+
data: { user_id: 123, category: nil },
|
|
229
|
+
enable_cache: true
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
memory[:session_id] = "abc123"
|
|
233
|
+
memory[:custom_key] = "custom_value"
|
|
154
234
|
```
|
|
155
235
|
|
|
156
|
-
##
|
|
236
|
+
## Serialization
|
|
157
237
|
|
|
158
|
-
|
|
238
|
+
Memory can be exported and reconstructed:
|
|
159
239
|
|
|
160
240
|
```ruby
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
when "technical" then :tech_agent
|
|
171
|
-
else :general_agent
|
|
172
|
-
end
|
|
173
|
-
else
|
|
174
|
-
nil
|
|
175
|
-
end
|
|
176
|
-
}
|
|
241
|
+
# Export to hash
|
|
242
|
+
hash = robot.memory.to_h
|
|
243
|
+
# => { data: {...}, results: [...], messages: [...], session_id: "...", custom: {...} }
|
|
244
|
+
|
|
245
|
+
# Export to JSON
|
|
246
|
+
json = robot.memory.to_json
|
|
247
|
+
|
|
248
|
+
# Reconstruct from hash
|
|
249
|
+
restored = RobotLab::Memory.from_hash(hash)
|
|
177
250
|
```
|
|
178
251
|
|
|
179
252
|
## Patterns
|
|
180
253
|
|
|
181
|
-
### Accumulating Data
|
|
254
|
+
### Accumulating Data Across Robots
|
|
182
255
|
|
|
183
256
|
```ruby
|
|
184
|
-
# In each robot
|
|
185
|
-
def
|
|
186
|
-
findings =
|
|
257
|
+
# In each robot's processing
|
|
258
|
+
def accumulate_finding(memory, finding)
|
|
259
|
+
findings = memory[:findings] || []
|
|
187
260
|
findings << finding
|
|
188
|
-
|
|
261
|
+
memory[:findings] = findings
|
|
189
262
|
end
|
|
190
263
|
|
|
191
|
-
# In final robot
|
|
192
|
-
all_findings =
|
|
264
|
+
# In the final robot
|
|
265
|
+
all_findings = memory[:findings]
|
|
193
266
|
```
|
|
194
267
|
|
|
195
268
|
### Tracking Progress
|
|
196
269
|
|
|
197
270
|
```ruby
|
|
198
|
-
|
|
199
|
-
state.memory.remember("stage", "intake")
|
|
271
|
+
memory[:stage] = "intake"
|
|
200
272
|
# ... processing ...
|
|
201
|
-
|
|
273
|
+
memory[:stage] = "analysis"
|
|
202
274
|
# ... processing ...
|
|
203
|
-
|
|
275
|
+
memory[:stage] = "response"
|
|
204
276
|
```
|
|
205
277
|
|
|
206
278
|
### Caching Expensive Operations
|
|
207
279
|
|
|
208
280
|
```ruby
|
|
209
|
-
|
|
210
|
-
|
|
281
|
+
class FetchUser < RubyLLM::Tool
|
|
282
|
+
description "Fetch user details by ID"
|
|
283
|
+
param :user_id, type: :string, desc: "User ID"
|
|
284
|
+
|
|
285
|
+
def execute(user_id:)
|
|
211
286
|
cache_key = "cache:user:#{user_id}"
|
|
212
287
|
|
|
213
|
-
# Check
|
|
214
|
-
|
|
288
|
+
# Check robot's memory for cached value
|
|
289
|
+
# (In practice, you'd access memory through the robot's context)
|
|
290
|
+
cached = Thread.current[:robot_memory]&.[](cache_key.to_sym)
|
|
215
291
|
return cached if cached
|
|
216
292
|
|
|
217
293
|
# Fetch and cache
|
|
218
294
|
user = User.find(user_id).to_h
|
|
219
|
-
|
|
295
|
+
Thread.current[:robot_memory]&.[]=(cache_key.to_sym, user)
|
|
220
296
|
user
|
|
221
297
|
end
|
|
222
298
|
end
|
|
223
299
|
```
|
|
224
300
|
|
|
225
|
-
###
|
|
226
|
-
|
|
227
|
-
```ruby
|
|
228
|
-
# Store session data
|
|
229
|
-
session = state.memory.scoped("session:#{session_id}")
|
|
230
|
-
session.remember("started_at", Time.now.iso8601)
|
|
231
|
-
session.remember("page_views", 0)
|
|
232
|
-
|
|
233
|
-
# Update during conversation
|
|
234
|
-
views = session.recall("page_views") || 0
|
|
235
|
-
session.remember("page_views", views + 1)
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
## Memory vs State.data
|
|
301
|
+
### Semantic Caching
|
|
239
302
|
|
|
240
|
-
|
|
241
|
-
|---------|--------|------------|
|
|
242
|
-
| Purpose | Robot-to-robot sharing | Input/output data |
|
|
243
|
-
| Scope | Namespaced | Flat hash |
|
|
244
|
-
| Typical Use | Intermediate results | User input, workflow config |
|
|
245
|
-
| Persistence | Within run | Can be serialized |
|
|
303
|
+
Memory includes a semantic cache for LLM response caching:
|
|
246
304
|
|
|
247
305
|
```ruby
|
|
248
|
-
#
|
|
249
|
-
|
|
250
|
-
message: "Process order",
|
|
251
|
-
data: { order_id: "123", priority: "high" }
|
|
252
|
-
)
|
|
306
|
+
# Access the semantic cache
|
|
307
|
+
cache = robot.memory.cache # => RubyLLM::SemanticCache
|
|
253
308
|
|
|
254
|
-
# Use
|
|
255
|
-
|
|
256
|
-
|
|
309
|
+
# Use it to cache semantically similar queries
|
|
310
|
+
response = cache.fetch("What is Ruby?") do
|
|
311
|
+
robot.run("What is Ruby?")
|
|
312
|
+
end
|
|
257
313
|
```
|
|
258
314
|
|
|
259
315
|
## Best Practices
|
|
@@ -262,48 +318,48 @@ state.memory.remember("processing_steps", ["validated", "charged"])
|
|
|
262
318
|
|
|
263
319
|
```ruby
|
|
264
320
|
# Good
|
|
265
|
-
|
|
266
|
-
|
|
321
|
+
robot.memory[:classification_intent] = "billing"
|
|
322
|
+
robot.memory[:user_last_order_id] = "ord_456"
|
|
267
323
|
|
|
268
324
|
# Bad
|
|
269
|
-
|
|
270
|
-
|
|
325
|
+
robot.memory[:x] = "billing"
|
|
326
|
+
robot.memory[:temp1] = "ord_456"
|
|
271
327
|
```
|
|
272
328
|
|
|
273
|
-
### 2.
|
|
329
|
+
### 2. Use Data Hash for Structured Runtime Input
|
|
274
330
|
|
|
275
331
|
```ruby
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
#
|
|
283
|
-
|
|
284
|
-
state.memory.remember("user_email", email)
|
|
285
|
-
state.memory.remember("user_plan", plan)
|
|
332
|
+
memory = RobotLab.create_memory(
|
|
333
|
+
data: { order_id: "123", priority: "high", customer_tier: "gold" }
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
# Access via data proxy
|
|
337
|
+
memory.data.order_id # => "123"
|
|
338
|
+
memory.data.priority # => "high"
|
|
339
|
+
memory.data.customer_tier # => "gold"
|
|
286
340
|
```
|
|
287
341
|
|
|
288
|
-
### 3. Clean Up Temporary
|
|
342
|
+
### 3. Clean Up Temporary Values
|
|
289
343
|
|
|
290
344
|
```ruby
|
|
291
|
-
#
|
|
292
|
-
|
|
345
|
+
# After processing is done
|
|
346
|
+
robot.memory.delete(:temp_calculation)
|
|
347
|
+
robot.memory.delete(:intermediate_result)
|
|
293
348
|
```
|
|
294
349
|
|
|
295
350
|
### 4. Document Memory Keys
|
|
296
351
|
|
|
297
352
|
```ruby
|
|
298
|
-
# In your robot definitions, document expected keys
|
|
299
|
-
#
|
|
300
|
-
#
|
|
301
|
-
# -
|
|
302
|
-
# -
|
|
353
|
+
# In your robot definitions, document expected keys:
|
|
354
|
+
#
|
|
355
|
+
# Memory keys used by this pipeline:
|
|
356
|
+
# - :intent - Classification result (set by classifier)
|
|
357
|
+
# - :entities - Extracted entities (set by entity_extractor)
|
|
358
|
+
# - :response - Final response draft (set by responder)
|
|
303
359
|
```
|
|
304
360
|
|
|
305
361
|
## Next Steps
|
|
306
362
|
|
|
307
|
-
- [State Management](../architecture/state-management.md) - Full state details
|
|
308
363
|
- [Building Robots](building-robots.md) - Using memory in robots
|
|
364
|
+
- [Creating Networks](creating-networks.md) - Shared memory in networks
|
|
309
365
|
- [API Reference: Memory](../api/core/memory.md) - Complete API
|