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