robot_lab 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/.envrc +1 -0
- data/.github/workflows/deploy-github-pages.yml +52 -0
- data/.github/workflows/deploy-yard-docs.yml +52 -0
- data/CHANGELOG.md +55 -0
- data/COMMITS.md +196 -0
- data/LICENSE.txt +21 -0
- data/README.md +332 -0
- data/Rakefile +67 -0
- data/docs/api/adapters/anthropic.md +121 -0
- data/docs/api/adapters/gemini.md +133 -0
- data/docs/api/adapters/index.md +104 -0
- data/docs/api/adapters/openai.md +134 -0
- data/docs/api/core/index.md +113 -0
- data/docs/api/core/memory.md +314 -0
- data/docs/api/core/network.md +291 -0
- data/docs/api/core/robot.md +273 -0
- data/docs/api/core/state.md +273 -0
- data/docs/api/core/tool.md +353 -0
- data/docs/api/history/active-record-adapter.md +195 -0
- data/docs/api/history/config.md +191 -0
- data/docs/api/history/index.md +132 -0
- data/docs/api/history/thread-manager.md +144 -0
- data/docs/api/index.md +82 -0
- data/docs/api/mcp/client.md +221 -0
- data/docs/api/mcp/index.md +111 -0
- data/docs/api/mcp/server.md +225 -0
- data/docs/api/mcp/transports.md +264 -0
- data/docs/api/messages/index.md +67 -0
- data/docs/api/messages/text-message.md +102 -0
- data/docs/api/messages/tool-call-message.md +144 -0
- data/docs/api/messages/tool-result-message.md +154 -0
- data/docs/api/messages/user-message.md +171 -0
- data/docs/api/streaming/context.md +174 -0
- data/docs/api/streaming/events.md +237 -0
- data/docs/api/streaming/index.md +108 -0
- data/docs/architecture/core-concepts.md +243 -0
- data/docs/architecture/index.md +138 -0
- data/docs/architecture/message-flow.md +320 -0
- data/docs/architecture/network-orchestration.md +216 -0
- data/docs/architecture/robot-execution.md +243 -0
- data/docs/architecture/state-management.md +323 -0
- data/docs/assets/css/custom.css +56 -0
- data/docs/assets/images/robot_lab.jpg +0 -0
- data/docs/concepts.md +216 -0
- data/docs/examples/basic-chat.md +193 -0
- data/docs/examples/index.md +129 -0
- data/docs/examples/mcp-server.md +290 -0
- data/docs/examples/multi-robot-network.md +312 -0
- data/docs/examples/rails-application.md +420 -0
- data/docs/examples/tool-usage.md +310 -0
- data/docs/getting-started/configuration.md +230 -0
- data/docs/getting-started/index.md +56 -0
- data/docs/getting-started/installation.md +179 -0
- data/docs/getting-started/quick-start.md +203 -0
- data/docs/guides/building-robots.md +376 -0
- data/docs/guides/creating-networks.md +366 -0
- data/docs/guides/history.md +359 -0
- data/docs/guides/index.md +68 -0
- data/docs/guides/mcp-integration.md +356 -0
- data/docs/guides/memory.md +309 -0
- data/docs/guides/rails-integration.md +432 -0
- data/docs/guides/streaming.md +314 -0
- data/docs/guides/using-tools.md +394 -0
- data/docs/index.md +160 -0
- data/examples/01_simple_robot.rb +38 -0
- data/examples/02_tools.rb +106 -0
- data/examples/03_network.rb +103 -0
- data/examples/04_mcp.rb +219 -0
- data/examples/05_streaming.rb +124 -0
- data/examples/06_prompt_templates.rb +324 -0
- data/examples/07_network_memory.rb +329 -0
- data/examples/prompts/assistant/system.txt.erb +2 -0
- data/examples/prompts/assistant/user.txt.erb +1 -0
- data/examples/prompts/billing/system.txt.erb +7 -0
- data/examples/prompts/billing/user.txt.erb +1 -0
- data/examples/prompts/classifier/system.txt.erb +4 -0
- data/examples/prompts/classifier/user.txt.erb +1 -0
- data/examples/prompts/entity_extractor/system.txt.erb +11 -0
- data/examples/prompts/entity_extractor/user.txt.erb +3 -0
- data/examples/prompts/escalation/system.txt.erb +35 -0
- data/examples/prompts/escalation/user.txt.erb +34 -0
- data/examples/prompts/general/system.txt.erb +4 -0
- data/examples/prompts/general/user.txt.erb +1 -0
- data/examples/prompts/github_assistant/system.txt.erb +6 -0
- data/examples/prompts/github_assistant/user.txt.erb +1 -0
- data/examples/prompts/helper/system.txt.erb +1 -0
- data/examples/prompts/helper/user.txt.erb +1 -0
- data/examples/prompts/keyword_extractor/system.txt.erb +8 -0
- data/examples/prompts/keyword_extractor/user.txt.erb +3 -0
- data/examples/prompts/order_support/system.txt.erb +27 -0
- data/examples/prompts/order_support/user.txt.erb +22 -0
- data/examples/prompts/product_support/system.txt.erb +30 -0
- data/examples/prompts/product_support/user.txt.erb +32 -0
- data/examples/prompts/sentiment_analyzer/system.txt.erb +9 -0
- data/examples/prompts/sentiment_analyzer/user.txt.erb +3 -0
- data/examples/prompts/synthesizer/system.txt.erb +14 -0
- data/examples/prompts/synthesizer/user.txt.erb +15 -0
- data/examples/prompts/technical/system.txt.erb +7 -0
- data/examples/prompts/technical/user.txt.erb +1 -0
- data/examples/prompts/triage/system.txt.erb +16 -0
- data/examples/prompts/triage/user.txt.erb +17 -0
- data/lib/generators/robot_lab/install_generator.rb +78 -0
- data/lib/generators/robot_lab/robot_generator.rb +55 -0
- data/lib/generators/robot_lab/templates/initializer.rb.tt +41 -0
- data/lib/generators/robot_lab/templates/migration.rb.tt +32 -0
- data/lib/generators/robot_lab/templates/result_model.rb.tt +52 -0
- data/lib/generators/robot_lab/templates/robot.rb.tt +46 -0
- data/lib/generators/robot_lab/templates/robot_test.rb.tt +32 -0
- data/lib/generators/robot_lab/templates/routing_robot.rb.tt +53 -0
- data/lib/generators/robot_lab/templates/thread_model.rb.tt +40 -0
- data/lib/robot_lab/adapters/anthropic.rb +163 -0
- data/lib/robot_lab/adapters/base.rb +85 -0
- data/lib/robot_lab/adapters/gemini.rb +193 -0
- data/lib/robot_lab/adapters/openai.rb +159 -0
- data/lib/robot_lab/adapters/registry.rb +81 -0
- data/lib/robot_lab/configuration.rb +143 -0
- data/lib/robot_lab/error.rb +32 -0
- data/lib/robot_lab/errors.rb +70 -0
- data/lib/robot_lab/history/active_record_adapter.rb +146 -0
- data/lib/robot_lab/history/config.rb +115 -0
- data/lib/robot_lab/history/thread_manager.rb +93 -0
- data/lib/robot_lab/mcp/client.rb +210 -0
- data/lib/robot_lab/mcp/server.rb +84 -0
- data/lib/robot_lab/mcp/transports/base.rb +56 -0
- data/lib/robot_lab/mcp/transports/sse.rb +117 -0
- data/lib/robot_lab/mcp/transports/stdio.rb +133 -0
- data/lib/robot_lab/mcp/transports/streamable_http.rb +139 -0
- data/lib/robot_lab/mcp/transports/websocket.rb +108 -0
- data/lib/robot_lab/memory.rb +882 -0
- data/lib/robot_lab/memory_change.rb +123 -0
- data/lib/robot_lab/message.rb +357 -0
- data/lib/robot_lab/network.rb +350 -0
- data/lib/robot_lab/rails/engine.rb +29 -0
- data/lib/robot_lab/rails/railtie.rb +42 -0
- data/lib/robot_lab/robot.rb +560 -0
- data/lib/robot_lab/robot_result.rb +205 -0
- data/lib/robot_lab/robotic_model.rb +324 -0
- data/lib/robot_lab/state_proxy.rb +188 -0
- data/lib/robot_lab/streaming/context.rb +144 -0
- data/lib/robot_lab/streaming/events.rb +95 -0
- data/lib/robot_lab/streaming/sequence_counter.rb +48 -0
- data/lib/robot_lab/task.rb +117 -0
- data/lib/robot_lab/tool.rb +223 -0
- data/lib/robot_lab/tool_config.rb +112 -0
- data/lib/robot_lab/tool_manifest.rb +234 -0
- data/lib/robot_lab/user_message.rb +118 -0
- data/lib/robot_lab/version.rb +5 -0
- data/lib/robot_lab/waiter.rb +73 -0
- data/lib/robot_lab.rb +195 -0
- data/mkdocs.yml +214 -0
- data/sig/robot_lab.rbs +4 -0
- metadata +442 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
# State Management
|
|
2
|
+
|
|
3
|
+
State in RobotLab tracks all data and history for a conversation or workflow.
|
|
4
|
+
|
|
5
|
+
## State Structure
|
|
6
|
+
|
|
7
|
+
The `State` class holds:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
state = RobotLab.create_state(
|
|
11
|
+
message: "Hello!", # Current user message
|
|
12
|
+
data: { user_id: "123" } # Custom workflow data
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
state.data # StateProxy - custom key-value data
|
|
16
|
+
state.results # Array<RobotResult> - execution history
|
|
17
|
+
state.messages # Array<Message> - formatted conversation
|
|
18
|
+
state.thread_id # String - optional persistence ID
|
|
19
|
+
state.memory # Memory - shared key-value store
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Creating State
|
|
23
|
+
|
|
24
|
+
### Basic Creation
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
state = RobotLab.create_state(message: "What's the weather?")
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### With Custom Data
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
state = RobotLab.create_state(
|
|
34
|
+
message: "Process my order",
|
|
35
|
+
data: {
|
|
36
|
+
user_id: "user_123",
|
|
37
|
+
order_id: "ord_456",
|
|
38
|
+
priority: "high"
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### From Existing Results
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
state = RobotLab.create_state(
|
|
47
|
+
message: "Continue our conversation",
|
|
48
|
+
results: previous_results,
|
|
49
|
+
thread_id: "thread_abc"
|
|
50
|
+
)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## StateProxy
|
|
54
|
+
|
|
55
|
+
The `data` attribute is a `StateProxy` that provides convenient access:
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
state.data[:user_id] # Hash-style access
|
|
59
|
+
state.data[:user_id] = "456" # Assignment
|
|
60
|
+
|
|
61
|
+
state.data.user_id # Method-style access
|
|
62
|
+
state.data.user_id = "456" # Method-style assignment
|
|
63
|
+
|
|
64
|
+
state.data.key?(:user_id) # Check existence
|
|
65
|
+
state.data.keys # Get all keys
|
|
66
|
+
state.data.to_h # Convert to plain hash
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Change Tracking
|
|
70
|
+
|
|
71
|
+
StateProxy can track changes:
|
|
72
|
+
|
|
73
|
+
```ruby
|
|
74
|
+
state = State.new(
|
|
75
|
+
data: { count: 0 },
|
|
76
|
+
on_change: ->(key, old_val, new_val) {
|
|
77
|
+
puts "#{key}: #{old_val} -> #{new_val}"
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
state.data[:count] = 1 # Prints: "count: 0 -> 1"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Memory
|
|
85
|
+
|
|
86
|
+
Memory provides a shared key-value store across robots:
|
|
87
|
+
|
|
88
|
+
```ruby
|
|
89
|
+
# Store values
|
|
90
|
+
state.memory.remember("user_name", "Alice")
|
|
91
|
+
state.memory.remember("preferences", { theme: "dark" })
|
|
92
|
+
|
|
93
|
+
# Retrieve values
|
|
94
|
+
name = state.memory.recall("user_name") # => "Alice"
|
|
95
|
+
|
|
96
|
+
# Check existence
|
|
97
|
+
state.memory.exists?("user_name") # => true
|
|
98
|
+
|
|
99
|
+
# Remove values
|
|
100
|
+
state.memory.forget("user_name")
|
|
101
|
+
|
|
102
|
+
# List all
|
|
103
|
+
state.memory.all # => { "user_name" => "Alice", ... }
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Scoped Memory
|
|
107
|
+
|
|
108
|
+
Organize memory with namespaces:
|
|
109
|
+
|
|
110
|
+
```ruby
|
|
111
|
+
# Create scoped view
|
|
112
|
+
user_memory = state.memory.scoped("user:123")
|
|
113
|
+
user_memory.remember("last_login", Time.now)
|
|
114
|
+
|
|
115
|
+
# Access scoped data
|
|
116
|
+
user_memory.recall("last_login")
|
|
117
|
+
|
|
118
|
+
# Full key is "user:123:last_login"
|
|
119
|
+
state.memory.recall("user:123:last_login")
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Shared Memory
|
|
123
|
+
|
|
124
|
+
Use the `SHARED` namespace for cross-robot data:
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
# In first robot
|
|
128
|
+
state.memory.remember("SHARED:context", important_data)
|
|
129
|
+
|
|
130
|
+
# In second robot (same or different network run)
|
|
131
|
+
data = state.memory.recall("SHARED:context")
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Memory Operations
|
|
135
|
+
|
|
136
|
+
```ruby
|
|
137
|
+
# Search by pattern
|
|
138
|
+
matches = state.memory.search("user:*")
|
|
139
|
+
|
|
140
|
+
# Get statistics
|
|
141
|
+
state.memory.stats
|
|
142
|
+
# => { total_keys: 15, namespaces: ["user", "session"] }
|
|
143
|
+
|
|
144
|
+
# Clear namespace
|
|
145
|
+
state.memory.scoped("temp").clear
|
|
146
|
+
|
|
147
|
+
# Clear everything
|
|
148
|
+
state.memory.clear_all
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Results
|
|
152
|
+
|
|
153
|
+
Results track the history of robot executions:
|
|
154
|
+
|
|
155
|
+
```ruby
|
|
156
|
+
# Append a result
|
|
157
|
+
state.append_result(robot_result)
|
|
158
|
+
|
|
159
|
+
# Get all results
|
|
160
|
+
state.results
|
|
161
|
+
|
|
162
|
+
# Get results from index
|
|
163
|
+
state.results_from(5) # Results starting at index 5
|
|
164
|
+
|
|
165
|
+
# Format for LLM conversation
|
|
166
|
+
state.format_history
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Result History
|
|
170
|
+
|
|
171
|
+
Each `RobotResult` contains:
|
|
172
|
+
|
|
173
|
+
```ruby
|
|
174
|
+
result.robot_name # Which robot produced this
|
|
175
|
+
result.output # Array<Message> - response content
|
|
176
|
+
result.tool_calls # Array<ToolMessage> - tools called
|
|
177
|
+
result.stop_reason # "stop", "tool", etc.
|
|
178
|
+
result.created_at # When it was created
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Messages
|
|
182
|
+
|
|
183
|
+
The `messages` method formats state for LLM consumption:
|
|
184
|
+
|
|
185
|
+
```ruby
|
|
186
|
+
messages = state.messages
|
|
187
|
+
|
|
188
|
+
# Returns Array<Message> with:
|
|
189
|
+
# - System message (if present)
|
|
190
|
+
# - Alternating user/assistant messages
|
|
191
|
+
# - Tool calls and results
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Thread ID
|
|
195
|
+
|
|
196
|
+
For persistent conversations:
|
|
197
|
+
|
|
198
|
+
```ruby
|
|
199
|
+
# Set thread ID
|
|
200
|
+
state.thread_id = "thread_123"
|
|
201
|
+
|
|
202
|
+
# Or via UserMessage
|
|
203
|
+
message = UserMessage.new(
|
|
204
|
+
"Continue",
|
|
205
|
+
thread_id: "thread_123"
|
|
206
|
+
)
|
|
207
|
+
state = RobotLab.create_state(message: message)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## State Cloning
|
|
211
|
+
|
|
212
|
+
Create independent copies:
|
|
213
|
+
|
|
214
|
+
```ruby
|
|
215
|
+
original = RobotLab.create_state(data: { count: 1 })
|
|
216
|
+
clone = original.clone
|
|
217
|
+
|
|
218
|
+
clone.data[:count] = 2
|
|
219
|
+
original.data[:count] # Still 1
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Serialization
|
|
223
|
+
|
|
224
|
+
Convert state to/from hash:
|
|
225
|
+
|
|
226
|
+
```ruby
|
|
227
|
+
# To hash
|
|
228
|
+
hash = state.to_h
|
|
229
|
+
json = state.to_json
|
|
230
|
+
|
|
231
|
+
# From hash
|
|
232
|
+
state = State.from_hash(hash)
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Hash Structure
|
|
236
|
+
|
|
237
|
+
```ruby
|
|
238
|
+
{
|
|
239
|
+
data: { ... },
|
|
240
|
+
results: [
|
|
241
|
+
{
|
|
242
|
+
robot_name: "assistant",
|
|
243
|
+
output: [...],
|
|
244
|
+
tool_calls: [...],
|
|
245
|
+
stop_reason: "stop"
|
|
246
|
+
}
|
|
247
|
+
],
|
|
248
|
+
thread_id: "thread_123"
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## UserMessage
|
|
253
|
+
|
|
254
|
+
Enhanced message with metadata:
|
|
255
|
+
|
|
256
|
+
```ruby
|
|
257
|
+
message = UserMessage.new(
|
|
258
|
+
"What's the status of my order?",
|
|
259
|
+
thread_id: "thread_123",
|
|
260
|
+
system_prompt: "Respond in Spanish", # Augment system prompt
|
|
261
|
+
metadata: {
|
|
262
|
+
user_id: "user_456",
|
|
263
|
+
source: "web_chat"
|
|
264
|
+
}
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
state = RobotLab.create_state(message: message)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### UserMessage Properties
|
|
271
|
+
|
|
272
|
+
| Property | Description |
|
|
273
|
+
|----------|-------------|
|
|
274
|
+
| `content` | The message text |
|
|
275
|
+
| `thread_id` | Conversation thread ID |
|
|
276
|
+
| `system_prompt` | Additional system instructions |
|
|
277
|
+
| `metadata` | Custom key-value data |
|
|
278
|
+
| `id` | Unique message identifier |
|
|
279
|
+
| `created_at` | Timestamp |
|
|
280
|
+
|
|
281
|
+
## Best Practices
|
|
282
|
+
|
|
283
|
+
### 1. Use Memory for Cross-Robot Data
|
|
284
|
+
|
|
285
|
+
```ruby
|
|
286
|
+
# Don't pass data through routing
|
|
287
|
+
router = ->(args) {
|
|
288
|
+
# Bad: parsing previous output for data
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
# Do: use memory
|
|
292
|
+
state.memory.remember("classification", "billing")
|
|
293
|
+
# Later robot reads it directly
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### 2. Scope Memory Appropriately
|
|
297
|
+
|
|
298
|
+
```ruby
|
|
299
|
+
# Session data
|
|
300
|
+
session = state.memory.scoped("session:#{session_id}")
|
|
301
|
+
|
|
302
|
+
# User preferences
|
|
303
|
+
user = state.memory.scoped("user:#{user_id}")
|
|
304
|
+
|
|
305
|
+
# Temporary working data
|
|
306
|
+
temp = state.memory.scoped("temp")
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### 3. Keep Data Minimal
|
|
310
|
+
|
|
311
|
+
```ruby
|
|
312
|
+
# Don't store large objects
|
|
313
|
+
state.data[:huge_response] = api_response # Bad
|
|
314
|
+
|
|
315
|
+
# Store references instead
|
|
316
|
+
state.data[:response_id] = response.id # Good
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## Next Steps
|
|
320
|
+
|
|
321
|
+
- [Memory System](../guides/memory.md) - Advanced memory patterns
|
|
322
|
+
- [History Guide](../guides/history.md) - Persisting state
|
|
323
|
+
- [Message Flow](message-flow.md) - How messages are processed
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/* Custom styles for RobotLab documentation */
|
|
2
|
+
|
|
3
|
+
/* Robot-themed accent colors */
|
|
4
|
+
:root {
|
|
5
|
+
--md-primary-fg-color: #673ab7;
|
|
6
|
+
--md-primary-fg-color--light: #9575cd;
|
|
7
|
+
--md-primary-fg-color--dark: #512da8;
|
|
8
|
+
--md-accent-fg-color: #ffc107;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/* Code block styling */
|
|
12
|
+
.md-typeset code {
|
|
13
|
+
background-color: rgba(103, 58, 183, 0.1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* Admonition customization */
|
|
17
|
+
.md-typeset .admonition.robot,
|
|
18
|
+
.md-typeset details.robot {
|
|
19
|
+
border-color: #673ab7;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.md-typeset .robot > .admonition-title,
|
|
23
|
+
.md-typeset .robot > summary {
|
|
24
|
+
background-color: rgba(103, 58, 183, 0.1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.md-typeset .robot > .admonition-title::before,
|
|
28
|
+
.md-typeset .robot > summary::before {
|
|
29
|
+
background-color: #673ab7;
|
|
30
|
+
-webkit-mask-image: var(--md-admonition-icon--robot);
|
|
31
|
+
mask-image: var(--md-admonition-icon--robot);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* Grid cards spacing */
|
|
35
|
+
.md-typeset .grid.cards > ul > li {
|
|
36
|
+
margin-bottom: 1em;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* API reference table styling */
|
|
40
|
+
.md-typeset table:not([class]) th {
|
|
41
|
+
background-color: rgba(103, 58, 183, 0.1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* Mermaid diagram styling */
|
|
45
|
+
.mermaid {
|
|
46
|
+
background: transparent !important;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Dark mode adjustments */
|
|
50
|
+
[data-md-color-scheme="slate"] {
|
|
51
|
+
--md-code-bg-color: #1e1e2e;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
[data-md-color-scheme="slate"] .md-typeset code {
|
|
55
|
+
background-color: rgba(149, 117, 205, 0.2);
|
|
56
|
+
}
|
|
Binary file
|
data/docs/concepts.md
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# Core Concepts
|
|
2
|
+
|
|
3
|
+
Understanding the fundamental concepts in RobotLab will help you build effective AI applications.
|
|
4
|
+
|
|
5
|
+
## Robot
|
|
6
|
+
|
|
7
|
+
A **Robot** is an LLM-powered agent with a specific personality, capabilities, and tools. Each robot has:
|
|
8
|
+
|
|
9
|
+
- **Name**: A unique identifier within a network
|
|
10
|
+
- **Description**: What the robot does (used for routing decisions)
|
|
11
|
+
- **Template/System Prompt**: Instructions that define the robot's behavior
|
|
12
|
+
- **Model**: The LLM model to use (e.g., `claude-sonnet-4`)
|
|
13
|
+
- **Tools**: Custom functions the robot can call
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
robot = RobotLab.build do
|
|
17
|
+
name "support_agent"
|
|
18
|
+
description "Handles customer support inquiries"
|
|
19
|
+
model "claude-sonnet-4"
|
|
20
|
+
template "You are a friendly customer support agent..."
|
|
21
|
+
|
|
22
|
+
tool :lookup_order do
|
|
23
|
+
description "Look up order details by order ID"
|
|
24
|
+
parameter :order_id, type: :string, required: true
|
|
25
|
+
handler { |order_id:| Order.find(order_id).to_h }
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Network
|
|
31
|
+
|
|
32
|
+
A **Network** is a collection of robots orchestrated using [SimpleFlow](https://github.com/MadBomber/simple_flow) pipelines. Networks provide:
|
|
33
|
+
|
|
34
|
+
- **Task-Based Orchestration**: Define tasks with dependencies and routing
|
|
35
|
+
- **Parallel Execution**: Tasks with the same dependencies run concurrently
|
|
36
|
+
- **Optional Task Activation**: Dynamic routing based on robot output
|
|
37
|
+
- **Per-Task Configuration**: Each task can have its own context, tools, and MCP servers
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
network = RobotLab.create_network(name: "customer_service") do
|
|
41
|
+
task :classifier, classifier_robot, depends_on: :none
|
|
42
|
+
task :billing, billing_robot,
|
|
43
|
+
context: { department: "billing" },
|
|
44
|
+
depends_on: :optional
|
|
45
|
+
task :technical, technical_robot,
|
|
46
|
+
context: { department: "technical" },
|
|
47
|
+
depends_on: :optional
|
|
48
|
+
end
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Task
|
|
52
|
+
|
|
53
|
+
A **Task** wraps a robot for use in a network pipeline with per-task configuration:
|
|
54
|
+
|
|
55
|
+
- **Context**: Task-specific context deep-merged with network run params
|
|
56
|
+
- **MCP**: MCP servers available to this task
|
|
57
|
+
- **Tools**: Tools available to this task
|
|
58
|
+
- **Memory**: Task-specific memory
|
|
59
|
+
- **Dependencies**: `:none`, `[:task1, :task2]`, or `:optional`
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
task :billing, billing_robot,
|
|
63
|
+
context: { department: "billing", escalation_level: 2 },
|
|
64
|
+
tools: [RefundTool, InvoiceTool],
|
|
65
|
+
depends_on: :optional
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## SimpleFlow::Result
|
|
69
|
+
|
|
70
|
+
Networks use `SimpleFlow::Result` for data flow between tasks:
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
result.value # Current task's output (RobotResult)
|
|
74
|
+
result.context # Accumulated context from all tasks
|
|
75
|
+
result.halted? # Whether execution stopped early
|
|
76
|
+
result.continued? # Whether execution continues
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Result Methods
|
|
80
|
+
|
|
81
|
+
| Method | Purpose |
|
|
82
|
+
|--------|---------|
|
|
83
|
+
| `continue(value)` | Continue to next tasks |
|
|
84
|
+
| `halt(value)` | Stop pipeline execution |
|
|
85
|
+
| `with_context(key, val)` | Add data to context |
|
|
86
|
+
| `activate(task_name)` | Enable optional task |
|
|
87
|
+
|
|
88
|
+
## Tool
|
|
89
|
+
|
|
90
|
+
**Tools** are functions that robots can call to interact with external systems:
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
tool = RobotLab::Tool.new(
|
|
94
|
+
name: "get_weather",
|
|
95
|
+
description: "Get current weather for a location",
|
|
96
|
+
parameters: {
|
|
97
|
+
location: { type: "string", description: "City name" }
|
|
98
|
+
},
|
|
99
|
+
handler: ->(location:, **_context) {
|
|
100
|
+
WeatherService.current(location)
|
|
101
|
+
}
|
|
102
|
+
)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Message Types
|
|
106
|
+
|
|
107
|
+
RobotLab uses several message types to represent conversation content:
|
|
108
|
+
|
|
109
|
+
| Type | Purpose |
|
|
110
|
+
|------|---------|
|
|
111
|
+
| `TextMessage` | User or assistant text content |
|
|
112
|
+
| `ToolMessage` | Tool definition with name and parameters |
|
|
113
|
+
| `ToolCallMessage` | Request from LLM to execute a tool |
|
|
114
|
+
| `ToolResultMessage` | Result returned from tool execution |
|
|
115
|
+
|
|
116
|
+
## Memory
|
|
117
|
+
|
|
118
|
+
**Memory** provides persistent storage across robot executions:
|
|
119
|
+
|
|
120
|
+
```ruby
|
|
121
|
+
# Robot with inherent memory
|
|
122
|
+
robot = RobotLab.build(name: "assistant", system_prompt: "You are helpful.")
|
|
123
|
+
robot.run(message: "My name is Alice")
|
|
124
|
+
robot.run(message: "What's my name?") # Memory persists
|
|
125
|
+
|
|
126
|
+
# Access robot's memory
|
|
127
|
+
robot.memory[:user_id] = 123
|
|
128
|
+
robot.memory.data[:category] = "billing"
|
|
129
|
+
|
|
130
|
+
# Runtime memory injection
|
|
131
|
+
robot.run(message: "Help me", memory: { session_id: "abc123" })
|
|
132
|
+
|
|
133
|
+
# Reset memory
|
|
134
|
+
robot.reset_memory
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## MCP (Model Context Protocol)
|
|
138
|
+
|
|
139
|
+
**MCP** allows robots to connect to external tool servers:
|
|
140
|
+
|
|
141
|
+
```ruby
|
|
142
|
+
robot = RobotLab.build(
|
|
143
|
+
name: "developer",
|
|
144
|
+
mcp: [
|
|
145
|
+
{ name: "filesystem", transport: { type: "stdio", command: "mcp-server-filesystem" } },
|
|
146
|
+
{ name: "github", transport: { type: "stdio", command: "mcp-server-github" } }
|
|
147
|
+
]
|
|
148
|
+
)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Execution Flow
|
|
152
|
+
|
|
153
|
+
```mermaid
|
|
154
|
+
sequenceDiagram
|
|
155
|
+
participant User
|
|
156
|
+
participant Network
|
|
157
|
+
participant Pipeline
|
|
158
|
+
participant Task
|
|
159
|
+
participant Robot
|
|
160
|
+
participant LLM
|
|
161
|
+
participant Tool
|
|
162
|
+
|
|
163
|
+
User->>Network: run(message, context)
|
|
164
|
+
Network->>Pipeline: call(initial_result)
|
|
165
|
+
Pipeline->>Task: call(result)
|
|
166
|
+
Task->>Robot: call(enhanced_result)
|
|
167
|
+
Robot->>LLM: inference(messages, tools)
|
|
168
|
+
|
|
169
|
+
alt Tool Call
|
|
170
|
+
LLM-->>Robot: tool_call
|
|
171
|
+
Robot->>Tool: execute(params)
|
|
172
|
+
Tool-->>Robot: result
|
|
173
|
+
Robot->>LLM: continue with result
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
LLM-->>Robot: response
|
|
177
|
+
Robot-->>Task: RobotResult
|
|
178
|
+
Task-->>Pipeline: result.continue(value)
|
|
179
|
+
|
|
180
|
+
alt Optional Task Activated
|
|
181
|
+
Pipeline->>Task: call activated task
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
Pipeline-->>Network: final result
|
|
185
|
+
Network-->>User: SimpleFlow::Result
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Conditional Routing
|
|
189
|
+
|
|
190
|
+
Use custom Robot subclasses to implement intelligent routing:
|
|
191
|
+
|
|
192
|
+
```ruby
|
|
193
|
+
class ClassifierRobot < RobotLab::Robot
|
|
194
|
+
def call(result)
|
|
195
|
+
robot_result = run(**extract_run_context(result))
|
|
196
|
+
|
|
197
|
+
new_result = result
|
|
198
|
+
.with_context(@name.to_sym, robot_result)
|
|
199
|
+
.continue(robot_result)
|
|
200
|
+
|
|
201
|
+
# Activate appropriate specialist
|
|
202
|
+
category = robot_result.last_text_content.to_s.downcase
|
|
203
|
+
case category
|
|
204
|
+
when /billing/ then new_result.activate(:billing)
|
|
205
|
+
when /technical/ then new_result.activate(:technical)
|
|
206
|
+
else new_result.activate(:general)
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Next Steps
|
|
213
|
+
|
|
214
|
+
- [Quick Start Guide](getting-started/quick-start.md) - Build your first robot
|
|
215
|
+
- [Building Robots](guides/building-robots.md) - Detailed robot creation guide
|
|
216
|
+
- [Creating Networks](guides/creating-networks.md) - Network orchestration patterns
|