robot_lab 0.0.1 → 0.0.4
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 +90 -0
- data/README.md +203 -46
- data/Rakefile +70 -1
- data/docs/api/core/index.md +12 -0
- data/docs/api/core/robot.md +478 -130
- data/docs/api/core/tool.md +205 -209
- data/docs/api/history/active-record-adapter.md +174 -94
- data/docs/api/history/config.md +186 -93
- data/docs/api/history/index.md +57 -61
- data/docs/api/history/thread-manager.md +123 -73
- 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/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 +361 -112
- 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 +312 -48
- 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 +275 -162
- 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 +417 -212
- data/docs/guides/creating-networks.md +94 -24
- data/docs/guides/mcp-integration.md +152 -113
- data/docs/guides/memory.md +220 -164
- data/docs/guides/streaming.md +80 -110
- data/docs/guides/using-tools.md +259 -212
- data/docs/index.md +50 -37
- data/examples/01_simple_robot.rb +6 -9
- data/examples/02_tools.rb +6 -9
- data/examples/03_network.rb +13 -14
- 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 +140 -0
- data/examples/09_chaining.rb +223 -0
- data/examples/10_memory.rb +331 -0
- data/examples/11_network_introspection.rb +230 -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 +57 -0
- data/examples/14_rusty_circuit/open_mic.rb +121 -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 +173 -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/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 +1 -1
- data/lib/robot_lab/adapters/openai.rb +2 -1
- 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 +2 -2
- data/lib/robot_lab/robot.rb +523 -249
- data/lib/robot_lab/robot_message.rb +44 -0
- data/lib/robot_lab/robot_result.rb +1 -0
- data/lib/robot_lab/robotic_model.rb +1 -1
- data/lib/robot_lab/streaming/context.rb +1 -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/version.rb +1 -1
- data/lib/robot_lab.rb +66 -55
- metadata +107 -116
- 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/configuration.rb +0 -143
data/docs/api/core/robot.md
CHANGED
|
@@ -1,269 +1,617 @@
|
|
|
1
1
|
# Robot
|
|
2
2
|
|
|
3
|
-
LLM-powered agent with
|
|
3
|
+
LLM-powered agent with template-based prompts, tools, memory, and MCP integration.
|
|
4
4
|
|
|
5
|
-
## Class
|
|
5
|
+
## Class Hierarchy
|
|
6
6
|
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
model "claude-sonnet-4"
|
|
12
|
-
template "You are helpful."
|
|
13
|
-
end
|
|
7
|
+
```
|
|
8
|
+
RubyLLM::Agent
|
|
9
|
+
└── RobotLab::Robot
|
|
10
|
+
└── Your custom subclasses (e.g., ClassifierRobot)
|
|
14
11
|
```
|
|
15
12
|
|
|
16
|
-
|
|
13
|
+
`Robot` inherits from `RubyLLM::Agent`, which creates a persistent `@chat` on initialization. The robot adds template-based prompts, shared memory, hierarchical MCP configuration, and SimpleFlow pipeline integration on top of the base agent.
|
|
17
14
|
|
|
18
|
-
|
|
15
|
+
## Constructor
|
|
19
16
|
|
|
20
17
|
```ruby
|
|
21
|
-
|
|
18
|
+
Robot.new(
|
|
19
|
+
name:,
|
|
20
|
+
template: nil,
|
|
21
|
+
system_prompt: nil,
|
|
22
|
+
context: {},
|
|
23
|
+
description: nil,
|
|
24
|
+
local_tools: [],
|
|
25
|
+
model: nil,
|
|
26
|
+
mcp_servers: [],
|
|
27
|
+
mcp: :none,
|
|
28
|
+
tools: :none,
|
|
29
|
+
on_tool_call: nil,
|
|
30
|
+
on_tool_result: nil,
|
|
31
|
+
enable_cache: true,
|
|
32
|
+
bus: nil,
|
|
33
|
+
temperature: nil,
|
|
34
|
+
top_p: nil,
|
|
35
|
+
top_k: nil,
|
|
36
|
+
max_tokens: nil,
|
|
37
|
+
presence_penalty: nil,
|
|
38
|
+
frequency_penalty: nil,
|
|
39
|
+
stop: nil
|
|
40
|
+
)
|
|
22
41
|
```
|
|
23
42
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
43
|
+
### Parameters
|
|
44
|
+
|
|
45
|
+
| Name | Type | Default | Description |
|
|
46
|
+
|------|------|---------|-------------|
|
|
47
|
+
| `name` | `String` | **required** | Unique identifier for the robot |
|
|
48
|
+
| `template` | `Symbol`, `nil` | `nil` | Prompt template (e.g., `:assistant` loads `prompts/assistant.md`) |
|
|
49
|
+
| `system_prompt` | `String`, `nil` | `nil` | Inline system prompt (appended after template if both given) |
|
|
50
|
+
| `context` | `Hash`, `Proc` | `{}` | Variables passed to the template |
|
|
51
|
+
| `description` | `String`, `nil` | `nil` | Human-readable description of what the robot does |
|
|
52
|
+
| `local_tools` | `Array` | `[]` | Tools defined locally (`RubyLLM::Tool` subclasses or `RobotLab::Tool` instances) |
|
|
53
|
+
| `model` | `String`, `nil` | `nil` | LLM model ID (falls back to `RobotLab.config.ruby_llm.model`) |
|
|
54
|
+
| `mcp_servers` | `Array` | `[]` | Legacy MCP server configurations |
|
|
55
|
+
| `mcp` | `Symbol`, `Array` | `:none` | Hierarchical MCP config (`:none`, `:inherit`, or server array) |
|
|
56
|
+
| `tools` | `Symbol`, `Array` | `:none` | Hierarchical tools config (`:none`, `:inherit`, or tool name array) |
|
|
57
|
+
| `on_tool_call` | `Proc`, `nil` | `nil` | Callback invoked when a tool is called |
|
|
58
|
+
| `on_tool_result` | `Proc`, `nil` | `nil` | Callback invoked when a tool returns a result |
|
|
59
|
+
| `enable_cache` | `Boolean` | `true` | Whether to enable semantic caching |
|
|
60
|
+
| `bus` | `TypedBus::MessageBus`, `nil` | `nil` | Optional message bus for inter-robot communication |
|
|
61
|
+
| `temperature` | `Float`, `nil` | `nil` | Controls randomness (0.0-1.0) |
|
|
62
|
+
| `top_p` | `Float`, `nil` | `nil` | Nucleus sampling threshold |
|
|
63
|
+
| `top_k` | `Integer`, `nil` | `nil` | Top-k sampling |
|
|
64
|
+
| `max_tokens` | `Integer`, `nil` | `nil` | Maximum tokens in response |
|
|
65
|
+
| `presence_penalty` | `Float`, `nil` | `nil` | Penalize based on presence |
|
|
66
|
+
| `frequency_penalty` | `Float`, `nil` | `nil` | Penalize based on frequency |
|
|
67
|
+
| `stop` | `String`, `Array`, `nil` | `nil` | Stop sequences |
|
|
68
|
+
|
|
69
|
+
## Factory Method
|
|
27
70
|
|
|
28
71
|
```ruby
|
|
29
|
-
robot
|
|
72
|
+
robot = RobotLab.build(
|
|
73
|
+
name: "robot", # Defaults to "robot"
|
|
74
|
+
template: nil,
|
|
75
|
+
system_prompt: nil,
|
|
76
|
+
context: {},
|
|
77
|
+
enable_cache: true,
|
|
78
|
+
bus: nil, # Optional TypedBus::MessageBus
|
|
79
|
+
**options # All other Robot.new parameters
|
|
80
|
+
)
|
|
81
|
+
# => RobotLab::Robot
|
|
30
82
|
```
|
|
31
83
|
|
|
32
|
-
|
|
84
|
+
If `name` is omitted, it defaults to `"robot"`.
|
|
33
85
|
|
|
34
|
-
|
|
86
|
+
## Attributes (Read-Only)
|
|
35
87
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
88
|
+
| Attribute | Type | Description |
|
|
89
|
+
|-----------|------|-------------|
|
|
90
|
+
| `name` | `String` | Unique identifier |
|
|
91
|
+
| `description` | `String`, `nil` | Human-readable description |
|
|
92
|
+
| `template` | `Symbol`, `nil` | Prompt template identifier |
|
|
93
|
+
| `system_prompt` | `String`, `nil` | Inline system prompt |
|
|
94
|
+
| `local_tools` | `Array` | Locally defined tools |
|
|
95
|
+
| `mcp_clients` | `Hash<String, MCP::Client>` | Connected MCP clients, keyed by server name |
|
|
96
|
+
| `mcp_tools` | `Array<Tool>` | Tools discovered from MCP servers |
|
|
97
|
+
| `memory` | `Memory` | Inherent memory (used when standalone, not in network) |
|
|
98
|
+
| `bus` | `TypedBus::MessageBus`, `nil` | Message bus instance (nil if not configured) |
|
|
99
|
+
| `outbox` | `Hash` | Sent messages tracked by composite key with status and replies |
|
|
100
|
+
| `mcp_config` | `Symbol`, `Array` | Build-time MCP configuration (raw, unresolved) |
|
|
101
|
+
| `tools_config` | `Symbol`, `Array` | Build-time tools configuration (raw, unresolved) |
|
|
39
102
|
|
|
40
|
-
|
|
103
|
+
## Attributes (Read-Write)
|
|
41
104
|
|
|
42
|
-
|
|
105
|
+
| Attribute | Type | Default | Description |
|
|
106
|
+
|-----------|------|---------|-------------|
|
|
107
|
+
| `input` | `IO`, `nil` | `nil` | Input stream for user interaction (falls back to `$stdin`) |
|
|
108
|
+
| `output` | `IO`, `nil` | `nil` | Output stream for user interaction (falls back to `$stdout`) |
|
|
109
|
+
|
|
110
|
+
Used by tools like [`AskUser`](tool.md#built-in-askuser) that need terminal IO. Set to `StringIO` for testing.
|
|
111
|
+
|
|
112
|
+
## Methods
|
|
113
|
+
|
|
114
|
+
### run
|
|
43
115
|
|
|
44
116
|
```ruby
|
|
45
|
-
robot.
|
|
117
|
+
result = robot.run(message, **kwargs)
|
|
118
|
+
# => RobotResult
|
|
46
119
|
```
|
|
47
120
|
|
|
48
|
-
|
|
121
|
+
Primary execution method. Sends a message to the LLM with memory/MCP/tools resolution and returns a `RobotResult`.
|
|
122
|
+
|
|
123
|
+
**Parameters:**
|
|
49
124
|
|
|
50
|
-
|
|
125
|
+
| Name | Type | Default | Description |
|
|
126
|
+
|------|------|---------|-------------|
|
|
127
|
+
| `message` | `String` | **required** | The user message to send |
|
|
128
|
+
| `network` | `NetworkRun`, `nil` | `nil` | Network context (passed internally) |
|
|
129
|
+
| `network_memory` | `Memory`, `nil` | `nil` | Shared network memory |
|
|
130
|
+
| `memory` | `Memory`, `Hash`, `nil` | `nil` | Runtime memory to merge |
|
|
131
|
+
| `mcp` | `Symbol`, `Array` | `:none` | Runtime MCP override |
|
|
132
|
+
| `tools` | `Symbol`, `Array` | `:none` | Runtime tools override |
|
|
133
|
+
| `**kwargs` | `Hash` | `{}` | Additional keyword arguments passed to `Agent#ask` |
|
|
134
|
+
|
|
135
|
+
**Returns:** `RobotResult`
|
|
136
|
+
|
|
137
|
+
**Examples:**
|
|
51
138
|
|
|
52
139
|
```ruby
|
|
53
|
-
|
|
54
|
-
|
|
140
|
+
# Simple message
|
|
141
|
+
result = robot.run("What is 2+2?")
|
|
55
142
|
|
|
56
|
-
|
|
143
|
+
# With runtime memory
|
|
144
|
+
result = robot.run("Summarize the data", memory: { data: report })
|
|
57
145
|
|
|
58
|
-
|
|
146
|
+
# With streaming block
|
|
147
|
+
result = robot.run("Tell me a story") { |event| print event.text }
|
|
148
|
+
|
|
149
|
+
# With runtime overrides
|
|
150
|
+
result = robot.run("Help me", mcp: :none, tools: :none)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### model
|
|
59
154
|
|
|
60
155
|
```ruby
|
|
61
|
-
robot.
|
|
156
|
+
robot.model # => "claude-sonnet-4" or nil
|
|
62
157
|
```
|
|
63
158
|
|
|
64
|
-
|
|
159
|
+
Returns the model ID string. Resolves through the underlying chat object.
|
|
65
160
|
|
|
66
|
-
###
|
|
161
|
+
### update
|
|
67
162
|
|
|
68
163
|
```ruby
|
|
69
|
-
robot.
|
|
164
|
+
robot.update(
|
|
165
|
+
template: nil,
|
|
166
|
+
context: nil,
|
|
167
|
+
system_prompt: nil,
|
|
168
|
+
model: nil,
|
|
169
|
+
temperature: nil,
|
|
170
|
+
**kwargs
|
|
171
|
+
)
|
|
172
|
+
# => self
|
|
70
173
|
```
|
|
71
174
|
|
|
72
|
-
|
|
175
|
+
Reconfigure the robot after construction. Returns `self` for chaining.
|
|
176
|
+
|
|
177
|
+
### with_* Methods (Chaining)
|
|
178
|
+
|
|
179
|
+
All `with_*` methods delegate to the persistent `@chat` and return `self` for chaining:
|
|
180
|
+
|
|
181
|
+
| Method | Description |
|
|
182
|
+
|--------|-------------|
|
|
183
|
+
| `with_model(model_id)` | Change the LLM model |
|
|
184
|
+
| `with_temperature(temp)` | Set temperature |
|
|
185
|
+
| `with_top_p(value)` | Set nucleus sampling |
|
|
186
|
+
| `with_top_k(value)` | Set top-k sampling |
|
|
187
|
+
| `with_max_tokens(value)` | Set max response tokens |
|
|
188
|
+
| `with_presence_penalty(value)` | Set presence penalty |
|
|
189
|
+
| `with_frequency_penalty(value)` | Set frequency penalty |
|
|
190
|
+
| `with_stop(sequences)` | Set stop sequences |
|
|
191
|
+
| `with_instructions(prompt)` | Set system instructions |
|
|
192
|
+
| `with_tool(tool)` | Add a single tool |
|
|
193
|
+
| `with_tools(*tools)` | Add multiple tools |
|
|
194
|
+
| `with_params(**params)` | Set additional parameters |
|
|
195
|
+
| `with_headers(**headers)` | Set custom headers |
|
|
196
|
+
| `with_schema(schema)` | Set output schema |
|
|
197
|
+
| `with_context(**ctx)` | Set context |
|
|
198
|
+
| `with_thinking(opts)` | Enable extended thinking |
|
|
199
|
+
| `with_bus(bus)` | Connect to a message bus (creates one if nil) |
|
|
200
|
+
|
|
201
|
+
**Example:**
|
|
73
202
|
|
|
74
|
-
|
|
203
|
+
```ruby
|
|
204
|
+
robot = RobotLab.build(name: "bot")
|
|
205
|
+
robot
|
|
206
|
+
.with_model("claude-sonnet-4")
|
|
207
|
+
.with_temperature(0.7)
|
|
208
|
+
.with_instructions("Be concise.")
|
|
209
|
+
.run("Hello")
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### with_template
|
|
75
213
|
|
|
76
214
|
```ruby
|
|
77
|
-
robot.
|
|
215
|
+
robot.with_template(:assistant, tone: "friendly")
|
|
216
|
+
# => self
|
|
78
217
|
```
|
|
79
218
|
|
|
80
|
-
|
|
219
|
+
Apply a prompt_manager template. Separate from the delegated `with_*` methods because it handles template parsing and front matter config.
|
|
81
220
|
|
|
82
|
-
###
|
|
221
|
+
### call
|
|
83
222
|
|
|
84
223
|
```ruby
|
|
85
|
-
robot.
|
|
224
|
+
robot.call(result)
|
|
225
|
+
# => SimpleFlow::Result
|
|
86
226
|
```
|
|
87
227
|
|
|
88
|
-
|
|
228
|
+
SimpleFlow step interface. Extracts the message from `result.context[:run_params]`, calls `run`, and wraps the output in a continued `SimpleFlow::Result`.
|
|
89
229
|
|
|
90
|
-
|
|
230
|
+
Override this method in subclasses for custom routing logic (e.g., classifiers).
|
|
91
231
|
|
|
92
|
-
###
|
|
232
|
+
### reset_memory
|
|
93
233
|
|
|
94
234
|
```ruby
|
|
95
|
-
robot.
|
|
235
|
+
robot.reset_memory
|
|
236
|
+
# => self
|
|
96
237
|
```
|
|
97
238
|
|
|
98
|
-
|
|
239
|
+
Reset the robot's inherent memory to its initial state.
|
|
99
240
|
|
|
100
|
-
###
|
|
241
|
+
### send_message
|
|
101
242
|
|
|
102
243
|
```ruby
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
network: network,
|
|
106
|
-
streaming: nil,
|
|
107
|
-
**context
|
|
108
|
-
)
|
|
109
|
-
# => RobotResult
|
|
244
|
+
message = robot.send_message(to: :bob, content: "Tell me a joke.")
|
|
245
|
+
# => RobotMessage
|
|
110
246
|
```
|
|
111
247
|
|
|
112
|
-
|
|
248
|
+
Publish a message to another robot's bus channel. Increments the internal message counter, creates a `RobotMessage`, tracks it in the outbox, and publishes to the target channel.
|
|
113
249
|
|
|
114
250
|
**Parameters:**
|
|
115
251
|
|
|
116
252
|
| Name | Type | Description |
|
|
117
253
|
|------|------|-------------|
|
|
118
|
-
| `
|
|
119
|
-
| `
|
|
120
|
-
| `streaming` | `Proc`, `nil` | Streaming callback |
|
|
121
|
-
| `**context` | `Hash` | Additional context |
|
|
254
|
+
| `to` | `String`, `Symbol` | Target robot's channel name |
|
|
255
|
+
| `content` | `String`, `Hash` | Message payload |
|
|
122
256
|
|
|
123
|
-
**Returns:** `
|
|
257
|
+
**Returns:** `RobotMessage`
|
|
124
258
|
|
|
125
|
-
|
|
259
|
+
**Raises:** `BusError` if no bus is configured.
|
|
260
|
+
|
|
261
|
+
### send_reply
|
|
126
262
|
|
|
127
263
|
```ruby
|
|
128
|
-
robot.
|
|
264
|
+
reply = robot.send_reply(to: :alice, content: "Here's a joke...", in_reply_to: "alice:1")
|
|
265
|
+
# => RobotMessage
|
|
129
266
|
```
|
|
130
267
|
|
|
131
|
-
|
|
268
|
+
Publish a correlated reply to a specific message. The `in_reply_to` composite key links this reply to the original message.
|
|
132
269
|
|
|
133
|
-
|
|
270
|
+
**Parameters:**
|
|
271
|
+
|
|
272
|
+
| Name | Type | Description |
|
|
273
|
+
|------|------|-------------|
|
|
274
|
+
| `to` | `String`, `Symbol` | Target robot's channel name |
|
|
275
|
+
| `content` | `String`, `Hash` | Reply payload |
|
|
276
|
+
| `in_reply_to` | `String` | Composite key of the original message (e.g., `"alice:1"`) |
|
|
277
|
+
|
|
278
|
+
**Returns:** `RobotMessage`
|
|
279
|
+
|
|
280
|
+
**Raises:** `BusError` if no bus is configured.
|
|
281
|
+
|
|
282
|
+
### reply
|
|
134
283
|
|
|
135
284
|
```ruby
|
|
136
|
-
robot.
|
|
285
|
+
robot.reply(message, "Here's my response")
|
|
286
|
+
# => RobotMessage
|
|
137
287
|
```
|
|
138
288
|
|
|
139
|
-
|
|
289
|
+
Convenience method that wraps `send_reply`. Extracts the `from` and `key` from the incoming `RobotMessage` automatically.
|
|
290
|
+
|
|
291
|
+
**Parameters:**
|
|
292
|
+
|
|
293
|
+
| Name | Type | Description |
|
|
294
|
+
|------|------|-------------|
|
|
295
|
+
| `message` | `RobotMessage` | The message being replied to |
|
|
296
|
+
| `content` | `String`, `Hash` | Reply payload |
|
|
140
297
|
|
|
141
|
-
|
|
298
|
+
**Returns:** `RobotMessage`
|
|
142
299
|
|
|
143
|
-
###
|
|
300
|
+
### on_message
|
|
144
301
|
|
|
145
302
|
```ruby
|
|
146
|
-
|
|
303
|
+
robot.on_message { |message| puts message.content }
|
|
304
|
+
# => self
|
|
147
305
|
```
|
|
148
306
|
|
|
149
|
-
|
|
307
|
+
Register a custom handler for incoming bus messages. Block arity controls delivery handling:
|
|
308
|
+
|
|
309
|
+
- **1 argument** `|message|` — auto-acknowledges the delivery before calling the block
|
|
310
|
+
- **2 arguments** `|delivery, message|` — manual mode; you call `delivery.ack!` or `delivery.nack!`
|
|
311
|
+
|
|
312
|
+
**Examples:**
|
|
150
313
|
|
|
151
|
-
|
|
314
|
+
```ruby
|
|
315
|
+
# Auto-ack mode (1 arg)
|
|
316
|
+
robot.on_message do |message|
|
|
317
|
+
joke = run(message.content.to_s).last_text_content
|
|
318
|
+
reply(message, joke)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
# Manual mode (2 args)
|
|
322
|
+
robot.on_message do |delivery, message|
|
|
323
|
+
if message.content.to_s.length > 10
|
|
324
|
+
delivery.ack!
|
|
325
|
+
reply(message, "Got it!")
|
|
326
|
+
else
|
|
327
|
+
delivery.nack!
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### spawn
|
|
152
333
|
|
|
153
334
|
```ruby
|
|
154
|
-
|
|
335
|
+
child = robot.spawn(
|
|
336
|
+
name: "specialist",
|
|
337
|
+
system_prompt: "You are a specialist."
|
|
338
|
+
)
|
|
339
|
+
# => RobotLab::Robot (connected to same bus)
|
|
155
340
|
```
|
|
156
341
|
|
|
157
|
-
|
|
342
|
+
Create a new robot on the same message bus. If the parent has no bus, one is created automatically and the parent is connected to it.
|
|
158
343
|
|
|
159
|
-
|
|
344
|
+
**Parameters:**
|
|
345
|
+
|
|
346
|
+
| Name | Type | Default | Description |
|
|
347
|
+
|------|------|---------|-------------|
|
|
348
|
+
| `name` | `String` | `"robot"` | Name for the new robot |
|
|
349
|
+
| `system_prompt` | `String`, `nil` | `nil` | Inline system prompt |
|
|
350
|
+
| `template` | `Symbol`, `nil` | `nil` | Prompt template |
|
|
351
|
+
| `local_tools` | `Array` | `[]` | Tools for the new robot |
|
|
352
|
+
| `**options` | `Hash` | `{}` | Additional options passed to `RobotLab.build` |
|
|
353
|
+
|
|
354
|
+
**Returns:** `Robot`
|
|
355
|
+
|
|
356
|
+
**Examples:**
|
|
160
357
|
|
|
161
358
|
```ruby
|
|
162
|
-
|
|
359
|
+
# Minimal spawn (bus created automatically)
|
|
360
|
+
bot = RobotLab.build
|
|
361
|
+
bot2 = bot.spawn(system_prompt: "You are helpful.")
|
|
362
|
+
|
|
363
|
+
# Spawn with template
|
|
364
|
+
specialist = dispatcher.spawn(
|
|
365
|
+
name: "billing",
|
|
366
|
+
template: :billing,
|
|
367
|
+
local_tools: [InvoiceLookup]
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
# Fan-out: multiple robots with the same name
|
|
371
|
+
worker1 = bot.spawn(name: "worker", system_prompt: "Worker 1")
|
|
372
|
+
worker2 = bot.spawn(name: "worker", system_prompt: "Worker 2")
|
|
373
|
+
# Messages sent to :worker are delivered to both
|
|
163
374
|
```
|
|
164
375
|
|
|
165
|
-
|
|
376
|
+
### with_bus
|
|
166
377
|
|
|
167
|
-
|
|
378
|
+
```ruby
|
|
379
|
+
robot.with_bus(bus)
|
|
380
|
+
# => self
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
Connect the robot to a message bus after creation. If called without an argument and the robot has no bus, a new one is created. Returns `self` for chaining.
|
|
384
|
+
|
|
385
|
+
**Parameters:**
|
|
386
|
+
|
|
387
|
+
| Name | Type | Default | Description |
|
|
388
|
+
|------|------|---------|-------------|
|
|
389
|
+
| `bus` | `TypedBus::MessageBus`, `nil` | `nil` | Bus to join (creates one if nil and robot has no bus) |
|
|
390
|
+
|
|
391
|
+
**Returns:** `self`
|
|
392
|
+
|
|
393
|
+
**Examples:**
|
|
168
394
|
|
|
169
395
|
```ruby
|
|
170
|
-
#
|
|
171
|
-
|
|
396
|
+
# Join an existing bus
|
|
397
|
+
bot = RobotLab.build(name: "bot")
|
|
398
|
+
bot.with_bus(some_bus)
|
|
172
399
|
|
|
173
|
-
#
|
|
174
|
-
|
|
400
|
+
# Create a bus on demand
|
|
401
|
+
bot = RobotLab.build(name: "bot").with_bus
|
|
175
402
|
|
|
176
|
-
#
|
|
177
|
-
|
|
403
|
+
# Switch buses
|
|
404
|
+
bot.with_bus(bus1) # joins bus1
|
|
405
|
+
bot.with_bus(bus2) # leaves bus1, joins bus2
|
|
178
406
|
```
|
|
179
407
|
|
|
180
|
-
|
|
408
|
+
### disconnect
|
|
181
409
|
|
|
182
|
-
|
|
410
|
+
```ruby
|
|
411
|
+
robot.disconnect
|
|
412
|
+
# => self
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
Disconnect from all MCP servers and bus channels.
|
|
416
|
+
|
|
417
|
+
### to_h
|
|
183
418
|
|
|
184
419
|
```ruby
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
parameter :param, type: :string, required: true
|
|
188
|
-
handler { |param:, **_| do_something(param) }
|
|
189
|
-
end
|
|
420
|
+
robot.to_h
|
|
421
|
+
# => Hash
|
|
190
422
|
```
|
|
191
423
|
|
|
192
|
-
|
|
424
|
+
Returns a hash representation of the robot including name, description, template, system_prompt, local_tools, mcp_tools, mcp_config, tools_config, mcp_servers, model, and bus (true if configured, omitted otherwise).
|
|
425
|
+
|
|
426
|
+
## Memory Behavior
|
|
193
427
|
|
|
194
|
-
|
|
428
|
+
- **Standalone**: Robot uses its own inherent `Memory` instance (`robot.memory`).
|
|
429
|
+
- **In a Network**: Robot uses the network's shared memory (passed via `network_memory:`).
|
|
195
430
|
|
|
196
431
|
```ruby
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
432
|
+
# Standalone memory access
|
|
433
|
+
robot.memory[:user_id] = 123
|
|
434
|
+
robot.memory[:user_id] # => 123
|
|
435
|
+
|
|
436
|
+
# Reset standalone memory
|
|
437
|
+
robot.reset_memory
|
|
202
438
|
```
|
|
203
439
|
|
|
204
|
-
|
|
440
|
+
## Templates
|
|
205
441
|
|
|
206
|
-
|
|
442
|
+
Templates are `.md` files with optional YAML front matter, loaded via `prompt_manager`. The `template:` parameter maps to a file path relative to the configured template directory:
|
|
207
443
|
|
|
208
444
|
```ruby
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
445
|
+
# template: :assistant => prompts/assistant.md
|
|
446
|
+
robot = RobotLab.build(name: "bot", template: :assistant, context: { tone: "friendly" })
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
Front matter supports two categories of keys:
|
|
450
|
+
|
|
451
|
+
**LLM Config:** `model`, `temperature`, `top_p`, `top_k`, `max_tokens`, `presence_penalty`, `frequency_penalty`, `stop` — applied to the underlying chat.
|
|
452
|
+
|
|
453
|
+
**Robot Extras:** `robot_name`, `description`, `tools`, `mcp` — applied to the robot's identity and capabilities. Constructor-provided values always take precedence.
|
|
454
|
+
|
|
455
|
+
| Key | Type | Description |
|
|
456
|
+
|-----|------|-------------|
|
|
457
|
+
| `robot_name` | `String` | Override robot name (when constructor uses the default `"robot"`) |
|
|
458
|
+
| `description` | `String` | Human-readable description |
|
|
459
|
+
| `tools` | `Array<String>` | Tool class names resolved via `Object.const_get` |
|
|
460
|
+
| `mcp` | `Array<Hash>` | MCP server configurations |
|
|
461
|
+
|
|
462
|
+
## Configuration Hierarchy
|
|
463
|
+
|
|
464
|
+
Tools and MCP servers use hierarchical resolution: **runtime > robot > network > global config**.
|
|
465
|
+
|
|
212
466
|
```
|
|
467
|
+
RobotLab.config (global)
|
|
468
|
+
|
|
|
469
|
+
+-- Network
|
|
470
|
+
| |
|
|
471
|
+
| +-- Robot (build-time mcp:, tools:)
|
|
472
|
+
| |
|
|
473
|
+
| +-- run() call (runtime mcp:, tools:)
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
Values at each level:
|
|
213
477
|
|
|
214
|
-
|
|
478
|
+
- `:none` -- no tools/MCP at this level
|
|
479
|
+
- `:inherit` -- inherit from parent level
|
|
480
|
+
- `Array` -- explicit list of tool names or MCP server configs
|
|
215
481
|
|
|
216
482
|
## Examples
|
|
217
483
|
|
|
218
484
|
### Basic Robot
|
|
219
485
|
|
|
220
486
|
```ruby
|
|
221
|
-
robot = RobotLab.build
|
|
222
|
-
name "greeter"
|
|
223
|
-
|
|
224
|
-
|
|
487
|
+
robot = RobotLab.build(
|
|
488
|
+
name: "greeter",
|
|
489
|
+
system_prompt: "You greet users warmly."
|
|
490
|
+
)
|
|
491
|
+
result = robot.run("Hello!")
|
|
492
|
+
puts result.last_text_content
|
|
225
493
|
```
|
|
226
494
|
|
|
227
|
-
### Robot with
|
|
495
|
+
### Robot with Template
|
|
228
496
|
|
|
229
497
|
```ruby
|
|
230
|
-
robot = RobotLab.build
|
|
231
|
-
name "
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
handler { |a:, b:, **_| a + b }
|
|
240
|
-
end
|
|
498
|
+
robot = RobotLab.build(
|
|
499
|
+
name: "support",
|
|
500
|
+
template: :support,
|
|
501
|
+
context: { company: "Acme Corp" }
|
|
502
|
+
)
|
|
503
|
+
result = robot.run("I need help with my order")
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### Robot with Tools
|
|
241
507
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
508
|
+
```ruby
|
|
509
|
+
class Calculator < RubyLLM::Tool
|
|
510
|
+
description "Performs basic arithmetic"
|
|
511
|
+
param :operation, type: "string", desc: "add, subtract, multiply, divide"
|
|
512
|
+
param :a, type: "number", desc: "First operand"
|
|
513
|
+
param :b, type: "number", desc: "Second operand"
|
|
514
|
+
|
|
515
|
+
def execute(operation:, a:, b:)
|
|
516
|
+
case operation
|
|
517
|
+
when "add" then a + b
|
|
518
|
+
when "subtract" then a - b
|
|
519
|
+
when "multiply" then a * b
|
|
520
|
+
when "divide" then a.to_f / b
|
|
521
|
+
end
|
|
247
522
|
end
|
|
248
523
|
end
|
|
524
|
+
|
|
525
|
+
robot = RobotLab.build(
|
|
526
|
+
name: "math_bot",
|
|
527
|
+
system_prompt: "You help with math.",
|
|
528
|
+
local_tools: [Calculator]
|
|
529
|
+
)
|
|
530
|
+
result = robot.run("What is 15 * 7?")
|
|
249
531
|
```
|
|
250
532
|
|
|
251
533
|
### Robot with MCP
|
|
252
534
|
|
|
253
535
|
```ruby
|
|
254
|
-
robot = RobotLab.build
|
|
255
|
-
name "developer"
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
mcp [
|
|
536
|
+
robot = RobotLab.build(
|
|
537
|
+
name: "developer",
|
|
538
|
+
system_prompt: "You help with coding tasks.",
|
|
539
|
+
mcp: [
|
|
259
540
|
{
|
|
260
541
|
name: "github",
|
|
261
|
-
transport: { type: "stdio", command: "mcp-server
|
|
542
|
+
transport: { type: "stdio", command: "github-mcp-server", args: ["stdio"] }
|
|
262
543
|
}
|
|
263
544
|
]
|
|
545
|
+
)
|
|
546
|
+
result = robot.run("Search for popular Ruby repos")
|
|
547
|
+
robot.disconnect
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### Bare Robot with Chaining
|
|
551
|
+
|
|
552
|
+
```ruby
|
|
553
|
+
robot = RobotLab.build(name: "bot")
|
|
554
|
+
result = robot
|
|
555
|
+
.with_instructions("Be concise.")
|
|
556
|
+
.with_temperature(0.3)
|
|
557
|
+
.run("Explain quantum computing")
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
### Robot with Message Bus
|
|
561
|
+
|
|
562
|
+
```ruby
|
|
563
|
+
bus = TypedBus::MessageBus.new
|
|
564
|
+
|
|
565
|
+
bob = RobotLab.build(name: "bob", system_prompt: "You tell jokes.", bus: bus)
|
|
566
|
+
|
|
567
|
+
alice = RobotLab.build(name: "alice", system_prompt: "You evaluate jokes.", bus: bus)
|
|
568
|
+
alice.on_message do |message|
|
|
569
|
+
verdict = alice.run("Is this funny? #{message.content}").last_text_content
|
|
570
|
+
puts verdict
|
|
571
|
+
end
|
|
264
572
|
|
|
265
|
-
|
|
573
|
+
bob.on_message do |message|
|
|
574
|
+
joke = bob.run(message.content.to_s).last_text_content
|
|
575
|
+
bob.reply(message, joke)
|
|
266
576
|
end
|
|
577
|
+
|
|
578
|
+
alice.send_message(to: :bob, content: "Tell me a robot joke.")
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
### Spawning Robots Dynamically
|
|
582
|
+
|
|
583
|
+
```ruby
|
|
584
|
+
# Parent robot spawns specialists on demand
|
|
585
|
+
dispatcher = RobotLab.build(
|
|
586
|
+
name: "dispatcher",
|
|
587
|
+
system_prompt: "You delegate work."
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
dispatcher.on_message do |message|
|
|
591
|
+
puts "Reply from #{message.from}: #{message.content}"
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
# spawn creates child on same bus (bus created lazily)
|
|
595
|
+
helper = dispatcher.spawn(
|
|
596
|
+
name: "helper",
|
|
597
|
+
system_prompt: "You answer questions concisely."
|
|
598
|
+
)
|
|
599
|
+
|
|
600
|
+
answer = helper.run("What is 2+2?").last_text_content
|
|
601
|
+
helper.send_message(to: :dispatcher, content: answer)
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
### Connecting to a Bus After Creation
|
|
605
|
+
|
|
606
|
+
```ruby
|
|
607
|
+
bot = RobotLab.build(name: "latecomer", system_prompt: "Hi there.")
|
|
608
|
+
|
|
609
|
+
# Join a bus later
|
|
610
|
+
bus = TypedBus::MessageBus.new
|
|
611
|
+
bot.with_bus(bus)
|
|
612
|
+
|
|
613
|
+
# Now bot can send/receive messages
|
|
614
|
+
bot.send_message(to: :someone, content: "Hello!")
|
|
267
615
|
```
|
|
268
616
|
|
|
269
617
|
## See Also
|