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
|
@@ -4,24 +4,26 @@ This guide covers everything you need to know about creating robots in RobotLab.
|
|
|
4
4
|
|
|
5
5
|
## Basic Robot
|
|
6
6
|
|
|
7
|
-
Create a
|
|
7
|
+
Create a robot using the `RobotLab.build` factory method with keyword arguments:
|
|
8
8
|
|
|
9
9
|
```ruby
|
|
10
|
-
robot = RobotLab.build
|
|
11
|
-
name "assistant"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
robot = RobotLab.build(
|
|
11
|
+
name: "assistant",
|
|
12
|
+
system_prompt: "You are a helpful assistant."
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
result = robot.run("Hello!")
|
|
16
|
+
puts result.last_text_content
|
|
15
17
|
```
|
|
16
18
|
|
|
17
19
|
## Robot Properties
|
|
18
20
|
|
|
19
21
|
### Name
|
|
20
22
|
|
|
21
|
-
A unique identifier used for routing and logging:
|
|
23
|
+
A unique identifier used for routing and logging. If omitted, an auto-generated name is used:
|
|
22
24
|
|
|
23
25
|
```ruby
|
|
24
|
-
name "support_agent"
|
|
26
|
+
robot = RobotLab.build(name: "support_agent", system_prompt: "...")
|
|
25
27
|
```
|
|
26
28
|
|
|
27
29
|
### Description
|
|
@@ -29,348 +31,552 @@ name "support_agent"
|
|
|
29
31
|
Describes what the robot does (useful for routing decisions):
|
|
30
32
|
|
|
31
33
|
```ruby
|
|
32
|
-
|
|
34
|
+
robot = RobotLab.build(
|
|
35
|
+
name: "support_agent",
|
|
36
|
+
description: "Handles customer support inquiries about orders and refunds",
|
|
37
|
+
system_prompt: "..."
|
|
38
|
+
)
|
|
33
39
|
```
|
|
34
40
|
|
|
35
41
|
### Model
|
|
36
42
|
|
|
37
|
-
The LLM model to use
|
|
43
|
+
The LLM model to use. Defaults to the value in `RobotLab.config.ruby_llm.model`:
|
|
38
44
|
|
|
39
45
|
```ruby
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
model "
|
|
46
|
+
robot = RobotLab.build(
|
|
47
|
+
name: "writer",
|
|
48
|
+
model: "claude-sonnet-4",
|
|
49
|
+
system_prompt: "You are a creative writer."
|
|
50
|
+
)
|
|
43
51
|
```
|
|
44
52
|
|
|
45
|
-
###
|
|
53
|
+
### System Prompt
|
|
46
54
|
|
|
47
|
-
|
|
55
|
+
An inline string that defines the robot's personality and behavior:
|
|
48
56
|
|
|
49
57
|
```ruby
|
|
50
|
-
|
|
51
|
-
|
|
58
|
+
robot = RobotLab.build(
|
|
59
|
+
name: "support",
|
|
60
|
+
system_prompt: <<~PROMPT
|
|
61
|
+
You are a customer support specialist for TechCo.
|
|
52
62
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
63
|
+
Your responsibilities:
|
|
64
|
+
- Answer questions about products and services
|
|
65
|
+
- Help resolve order issues
|
|
66
|
+
- Provide friendly, professional assistance
|
|
57
67
|
|
|
58
|
-
|
|
59
|
-
PROMPT
|
|
68
|
+
Always be polite and acknowledge the customer's concerns.
|
|
69
|
+
PROMPT
|
|
70
|
+
)
|
|
60
71
|
```
|
|
61
72
|
|
|
62
|
-
##
|
|
73
|
+
## Template Files
|
|
63
74
|
|
|
64
|
-
|
|
75
|
+
Templates are `.md` files managed by [prompt_manager](https://github.com/MadBomber/prompt_manager). Reference a template by symbol; RobotLab resolves it through the configured template path.
|
|
65
76
|
|
|
66
77
|
```ruby
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
Order.find_by(id: order_id)&.to_h || { error: "Order not found" }
|
|
75
|
-
end
|
|
76
|
-
end
|
|
78
|
+
# Reference template by symbol (loads prompts/support.md)
|
|
79
|
+
robot = RobotLab.build(
|
|
80
|
+
name: "support",
|
|
81
|
+
template: :support,
|
|
82
|
+
context: { company: "TechCo", tone: "friendly" }
|
|
83
|
+
)
|
|
84
|
+
```
|
|
77
85
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
### Template Format
|
|
87
|
+
|
|
88
|
+
Templates use `.md` files with YAML front matter:
|
|
89
|
+
|
|
90
|
+
```markdown title="prompts/support.md"
|
|
91
|
+
---
|
|
92
|
+
description: Customer support assistant
|
|
93
|
+
parameters:
|
|
94
|
+
company: "Acme"
|
|
95
|
+
tone: "professional"
|
|
96
|
+
model: claude-sonnet-4
|
|
97
|
+
temperature: 0.7
|
|
98
|
+
---
|
|
99
|
+
You are a support agent for <%= company %>.
|
|
100
|
+
Your tone should be <%= tone %>.
|
|
87
101
|
```
|
|
88
102
|
|
|
89
|
-
###
|
|
103
|
+
### Front Matter Configuration
|
|
104
|
+
|
|
105
|
+
The following YAML front matter keys are applied to the robot's chat automatically:
|
|
106
|
+
|
|
107
|
+
**LLM Configuration:**
|
|
108
|
+
|
|
109
|
+
| Key | Description |
|
|
110
|
+
|-----|-------------|
|
|
111
|
+
| `model` | Override the LLM model |
|
|
112
|
+
| `temperature` | Controls randomness (0.0 - 1.0) |
|
|
113
|
+
| `top_p` | Nucleus sampling threshold |
|
|
114
|
+
| `top_k` | Top-k sampling |
|
|
115
|
+
| `max_tokens` | Maximum tokens in response |
|
|
116
|
+
| `presence_penalty` | Penalize based on presence |
|
|
117
|
+
| `frequency_penalty` | Penalize based on frequency |
|
|
118
|
+
| `stop` | Stop sequences |
|
|
119
|
+
|
|
120
|
+
**Robot Identity and Capabilities:**
|
|
121
|
+
|
|
122
|
+
| Key | Description |
|
|
123
|
+
|-----|-------------|
|
|
124
|
+
| `robot_name` | Override the robot's name (when constructor uses the default) |
|
|
125
|
+
| `description` | Human-readable description of the robot |
|
|
126
|
+
| `tools` | Array of tool class names (resolved via `Object.const_get`) |
|
|
127
|
+
| `mcp` | Array of MCP server configurations |
|
|
128
|
+
|
|
129
|
+
Constructor-provided values always take precedence over frontmatter values.
|
|
130
|
+
|
|
131
|
+
### Self-Contained Templates
|
|
132
|
+
|
|
133
|
+
Templates can declare everything a robot needs — identity, tools, MCP servers, and LLM config — making the `.md` file a complete robot definition:
|
|
134
|
+
|
|
135
|
+
```markdown title="prompts/github_assistant.md"
|
|
136
|
+
---
|
|
137
|
+
description: GitHub assistant with MCP tool access
|
|
138
|
+
robot_name: github_bot
|
|
139
|
+
mcp:
|
|
140
|
+
- name: github
|
|
141
|
+
transport: stdio
|
|
142
|
+
command: npx
|
|
143
|
+
args: ["-y", "@modelcontextprotocol/server-github"]
|
|
144
|
+
model: claude-sonnet-4
|
|
145
|
+
temperature: 0.3
|
|
146
|
+
---
|
|
147
|
+
You are a helpful GitHub assistant with access to GitHub tools via MCP.
|
|
148
|
+
Use the available tools to help answer questions about GitHub repositories.
|
|
149
|
+
```
|
|
90
150
|
|
|
91
|
-
|
|
151
|
+
Build the robot with minimal constructor arguments:
|
|
92
152
|
|
|
93
153
|
```ruby
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
parameter :limit, type: :integer, default: 10, description: "Max results"
|
|
97
|
-
parameter :category, type: :string, enum: %w[books movies music]
|
|
98
|
-
end
|
|
154
|
+
# Template provides name, description, MCP config, model, and temperature
|
|
155
|
+
robot = RobotLab.build(template: :github_assistant)
|
|
99
156
|
```
|
|
100
157
|
|
|
101
|
-
|
|
102
|
-
|--------|-------------|
|
|
103
|
-
| `type` | Parameter type (`:string`, `:integer`, `:boolean`, `:number`, `:array`, `:object`) |
|
|
104
|
-
| `required` | Whether the parameter is required |
|
|
105
|
-
| `default` | Default value if not provided |
|
|
106
|
-
| `description` | Description for the LLM |
|
|
107
|
-
| `enum` | List of allowed values |
|
|
158
|
+
### Tools in Front Matter
|
|
108
159
|
|
|
109
|
-
|
|
160
|
+
Declare tool classes by name in the `tools:` key. RobotLab resolves each string to a Ruby constant and instantiates it:
|
|
110
161
|
|
|
111
|
-
|
|
162
|
+
```markdown title="prompts/order_support.md"
|
|
163
|
+
---
|
|
164
|
+
description: Order support specialist
|
|
165
|
+
tools:
|
|
166
|
+
- OrderLookup
|
|
167
|
+
- RefundProcessor
|
|
168
|
+
---
|
|
169
|
+
You help customers with order inquiries and refunds.
|
|
170
|
+
```
|
|
112
171
|
|
|
113
172
|
```ruby
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
# state - Current State
|
|
173
|
+
# Tools are loaded from frontmatter — no local_tools: needed
|
|
174
|
+
robot = RobotLab.build(template: :order_support)
|
|
175
|
+
```
|
|
118
176
|
|
|
119
|
-
|
|
120
|
-
user_id = state.data[:user_id]
|
|
177
|
+
Tool classes must be defined and loaded before the robot is built. If a tool name cannot be resolved, it is skipped with a warning.
|
|
121
178
|
|
|
122
|
-
|
|
123
|
-
state.memory.remember("last_search", param1)
|
|
179
|
+
Constructor `local_tools:` overrides frontmatter `tools:` when provided:
|
|
124
180
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
181
|
+
```ruby
|
|
182
|
+
# Constructor tools take precedence over frontmatter tools
|
|
183
|
+
robot = RobotLab.build(
|
|
184
|
+
template: :order_support,
|
|
185
|
+
local_tools: [OrderLookup] # Only OrderLookup, not RefundProcessor
|
|
186
|
+
)
|
|
128
187
|
```
|
|
129
188
|
|
|
130
|
-
|
|
131
|
-
Use `**_context` to accept but ignore context:
|
|
132
|
-
```ruby
|
|
133
|
-
handler { |query:, **_context| search(query) }
|
|
134
|
-
```
|
|
189
|
+
### MCP in Front Matter
|
|
135
190
|
|
|
136
|
-
|
|
191
|
+
Declare MCP server configurations directly in the template:
|
|
137
192
|
|
|
138
|
-
|
|
193
|
+
```markdown title="prompts/developer.md"
|
|
194
|
+
---
|
|
195
|
+
description: Developer assistant with filesystem access
|
|
196
|
+
mcp:
|
|
197
|
+
- name: filesystem
|
|
198
|
+
transport: stdio
|
|
199
|
+
command: mcp-server-filesystem
|
|
200
|
+
args: ["--root", "/home/user/projects"]
|
|
201
|
+
---
|
|
202
|
+
You are a developer assistant with filesystem access.
|
|
203
|
+
```
|
|
139
204
|
|
|
140
205
|
```ruby
|
|
141
|
-
|
|
142
|
-
RobotLab.configure do |config|
|
|
143
|
-
config.template_path = "prompts" # or "app/prompts" in Rails
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
# Reference template by name
|
|
147
|
-
robot = RobotLab.build do
|
|
148
|
-
name "support"
|
|
149
|
-
template "support_agent" # Loads prompts/support_agent.erb
|
|
150
|
-
end
|
|
206
|
+
robot = RobotLab.build(template: :developer)
|
|
151
207
|
```
|
|
152
208
|
|
|
153
|
-
|
|
209
|
+
Constructor `mcp:` overrides frontmatter `mcp:` when provided.
|
|
210
|
+
|
|
211
|
+
### Template with System Prompt
|
|
154
212
|
|
|
155
|
-
|
|
213
|
+
You can combine a template and an inline system prompt. Both are applied to the chat -- the template first, then the system prompt is appended as additional instructions:
|
|
156
214
|
|
|
157
215
|
```ruby
|
|
158
|
-
robot = RobotLab.build
|
|
159
|
-
name "support"
|
|
160
|
-
template
|
|
161
|
-
|
|
216
|
+
robot = RobotLab.build(
|
|
217
|
+
name: "support",
|
|
218
|
+
template: :support,
|
|
219
|
+
context: { company: "TechCo" },
|
|
220
|
+
system_prompt: "Always respond in Spanish."
|
|
221
|
+
)
|
|
162
222
|
```
|
|
163
223
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
224
|
+
## Adding Tools
|
|
225
|
+
|
|
226
|
+
Give robots capabilities via the `local_tools:` parameter. Tools can be `RubyLLM::Tool` subclasses or `RobotLab::Tool` instances:
|
|
227
|
+
|
|
228
|
+
```ruby
|
|
229
|
+
robot = RobotLab.build(
|
|
230
|
+
name: "order_assistant",
|
|
231
|
+
system_prompt: "You help customers with orders.",
|
|
232
|
+
local_tools: [OrderLookup, InventoryCheck]
|
|
233
|
+
)
|
|
167
234
|
```
|
|
168
235
|
|
|
236
|
+
See the [Using Tools](using-tools.md) guide for details on defining tools.
|
|
237
|
+
|
|
169
238
|
## MCP Configuration
|
|
170
239
|
|
|
171
|
-
Connect to MCP servers:
|
|
240
|
+
Connect to MCP (Model Context Protocol) servers via the `mcp:` parameter:
|
|
172
241
|
|
|
173
242
|
```ruby
|
|
174
|
-
robot = RobotLab.build
|
|
175
|
-
name "coder"
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
mcp [
|
|
243
|
+
robot = RobotLab.build(
|
|
244
|
+
name: "coder",
|
|
245
|
+
template: :developer,
|
|
246
|
+
mcp: [
|
|
179
247
|
{
|
|
180
248
|
name: "filesystem",
|
|
181
249
|
transport: { type: "stdio", command: "mcp-server-fs", args: ["--root", "/data"] }
|
|
182
250
|
}
|
|
183
251
|
]
|
|
252
|
+
)
|
|
253
|
+
```
|
|
184
254
|
|
|
185
|
-
|
|
186
|
-
mcp :inherit
|
|
255
|
+
MCP configuration supports hierarchical resolution:
|
|
187
256
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
257
|
+
| Value | Behavior |
|
|
258
|
+
|-------|----------|
|
|
259
|
+
| `:none` | No MCP servers (default) |
|
|
260
|
+
| `:inherit` | Use parent network/config MCP servers |
|
|
261
|
+
| `[...]` | Explicit array of server configurations |
|
|
192
262
|
|
|
193
|
-
|
|
263
|
+
See the [MCP Integration](mcp-integration.md) guide for transport types and advanced patterns.
|
|
194
264
|
|
|
195
|
-
|
|
265
|
+
## Chaining Configuration
|
|
266
|
+
|
|
267
|
+
Robots support `with_*` method chaining for runtime reconfiguration. Each method returns `self` for fluent usage:
|
|
196
268
|
|
|
197
269
|
```ruby
|
|
198
|
-
robot = RobotLab.build
|
|
199
|
-
name "reader"
|
|
270
|
+
robot = RobotLab.build(name: "bot")
|
|
200
271
|
|
|
201
|
-
|
|
202
|
-
|
|
272
|
+
result = robot
|
|
273
|
+
.with_instructions("Be concise and direct.")
|
|
274
|
+
.with_temperature(0.9)
|
|
275
|
+
.with_model("claude-sonnet-4")
|
|
276
|
+
.run("Summarize quantum computing in one sentence.")
|
|
277
|
+
```
|
|
203
278
|
|
|
204
|
-
|
|
205
|
-
tools :inherit
|
|
279
|
+
### Available Chain Methods
|
|
206
280
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
281
|
+
| Method | Description |
|
|
282
|
+
|--------|-------------|
|
|
283
|
+
| `with_model(id)` | Change the LLM model |
|
|
284
|
+
| `with_instructions(text)` | Set system instructions |
|
|
285
|
+
| `with_temperature(val)` | Set temperature |
|
|
286
|
+
| `with_top_p(val)` | Set nucleus sampling |
|
|
287
|
+
| `with_top_k(val)` | Set top-k sampling |
|
|
288
|
+
| `with_max_tokens(val)` | Set max output tokens |
|
|
289
|
+
| `with_presence_penalty(val)` | Set presence penalty |
|
|
290
|
+
| `with_frequency_penalty(val)` | Set frequency penalty |
|
|
291
|
+
| `with_stop(sequences)` | Set stop sequences |
|
|
292
|
+
| `with_tool(tool)` | Add a single tool |
|
|
293
|
+
| `with_tools(*tools)` | Add multiple tools |
|
|
294
|
+
| `with_template(id, **ctx)` | Apply a prompt template |
|
|
295
|
+
| `with_schema(schema)` | Set structured output schema |
|
|
296
|
+
| `with_thinking(config)` | Enable extended thinking |
|
|
297
|
+
| `with_bus(bus)` | Connect to a message bus (creates one if nil) |
|
|
211
298
|
|
|
212
299
|
## Running Robots
|
|
213
300
|
|
|
214
301
|
### Standalone
|
|
215
302
|
|
|
216
|
-
Run a robot directly:
|
|
303
|
+
Run a robot directly with a string message:
|
|
304
|
+
|
|
305
|
+
```ruby
|
|
306
|
+
result = robot.run("Hello!")
|
|
307
|
+
puts result.last_text_content
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
The `run` method returns a `RobotResult` with:
|
|
217
311
|
|
|
218
312
|
```ruby
|
|
219
|
-
|
|
220
|
-
result
|
|
313
|
+
result.last_text_content # => "Hi there! How can I help?"
|
|
314
|
+
result.output # => Array of output messages
|
|
315
|
+
result.tool_calls # => Array of tool call results
|
|
316
|
+
result.robot_name # => "assistant"
|
|
317
|
+
result.stop_reason # => stop reason from the LLM
|
|
318
|
+
```
|
|
221
319
|
|
|
222
|
-
|
|
320
|
+
### With Runtime Memory
|
|
321
|
+
|
|
322
|
+
Inject memory values for a single run:
|
|
323
|
+
|
|
324
|
+
```ruby
|
|
325
|
+
result = robot.run("What's my account status?", memory: { user_id: 123 })
|
|
223
326
|
```
|
|
224
327
|
|
|
225
328
|
### In a Network
|
|
226
329
|
|
|
227
|
-
Run through a network for
|
|
330
|
+
Run through a network for orchestration:
|
|
228
331
|
|
|
229
332
|
```ruby
|
|
230
|
-
network = RobotLab.create_network do
|
|
231
|
-
|
|
333
|
+
network = RobotLab.create_network(name: "pipeline") do
|
|
334
|
+
task :assistant, robot, depends_on: :none
|
|
232
335
|
end
|
|
233
336
|
|
|
234
|
-
|
|
235
|
-
result
|
|
337
|
+
result = network.run(message: "Hello!")
|
|
338
|
+
puts result.value.last_text_content
|
|
236
339
|
```
|
|
237
340
|
|
|
238
341
|
### With Streaming
|
|
239
342
|
|
|
240
|
-
Stream responses in real-time
|
|
343
|
+
Stream responses in real-time by registering callbacks before calling `run`:
|
|
241
344
|
|
|
242
345
|
```ruby
|
|
243
|
-
robot.
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
end
|
|
253
|
-
}
|
|
254
|
-
)
|
|
346
|
+
robot.on_new_message do |message|
|
|
347
|
+
print message.content if message.content
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
robot.on_tool_call do |tool_call|
|
|
351
|
+
puts "\nCalling tool: #{tool_call.name}"
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
result = robot.run("Tell me a story")
|
|
255
355
|
```
|
|
256
356
|
|
|
257
357
|
## Robot Patterns
|
|
258
358
|
|
|
259
359
|
### Classifier Robot
|
|
260
360
|
|
|
261
|
-
Route requests to specialized handlers:
|
|
361
|
+
Route requests to specialized handlers. Subclass `RobotLab::Robot` and override `call` for custom pipeline behavior:
|
|
262
362
|
|
|
263
363
|
```ruby
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
364
|
+
class ClassifierRobot < RobotLab::Robot
|
|
365
|
+
def call(result)
|
|
366
|
+
context = extract_run_context(result)
|
|
367
|
+
message = context.delete(:message)
|
|
368
|
+
robot_result = run(message, **context)
|
|
369
|
+
|
|
370
|
+
new_result = result
|
|
371
|
+
.with_context(@name.to_sym, robot_result)
|
|
372
|
+
.continue(robot_result)
|
|
373
|
+
|
|
374
|
+
category = robot_result.last_text_content.to_s.strip.downcase
|
|
375
|
+
|
|
376
|
+
case category
|
|
377
|
+
when /billing/ then new_result.activate(:billing)
|
|
378
|
+
when /technical/ then new_result.activate(:technical)
|
|
379
|
+
else new_result.activate(:general)
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
end
|
|
273
383
|
|
|
384
|
+
classifier = ClassifierRobot.new(
|
|
385
|
+
name: "classifier",
|
|
386
|
+
system_prompt: <<~PROMPT
|
|
387
|
+
Classify the user's message into exactly one category:
|
|
388
|
+
- billing
|
|
389
|
+
- technical
|
|
390
|
+
- general
|
|
274
391
|
Respond with only the category name, nothing else.
|
|
275
392
|
PROMPT
|
|
276
|
-
|
|
393
|
+
)
|
|
277
394
|
```
|
|
278
395
|
|
|
279
396
|
### Specialist Robot
|
|
280
397
|
|
|
281
|
-
Handle specific domains:
|
|
398
|
+
Handle specific domains with template and tools:
|
|
282
399
|
|
|
283
400
|
```ruby
|
|
284
|
-
billing_specialist = RobotLab.build
|
|
285
|
-
name "billing_specialist"
|
|
286
|
-
description "Handles billing and payment inquiries"
|
|
401
|
+
billing_specialist = RobotLab.build(
|
|
402
|
+
name: "billing_specialist",
|
|
403
|
+
description: "Handles billing and payment inquiries",
|
|
404
|
+
template: :billing,
|
|
405
|
+
context: { department: "billing" },
|
|
406
|
+
local_tools: [InvoiceLookup, RefundProcessor]
|
|
407
|
+
)
|
|
408
|
+
```
|
|
287
409
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
- Payment issues
|
|
292
|
-
- Subscription management
|
|
410
|
+
### Summarizer Robot
|
|
411
|
+
|
|
412
|
+
Condense information:
|
|
293
413
|
|
|
294
|
-
|
|
414
|
+
```ruby
|
|
415
|
+
summarizer = RobotLab.build(
|
|
416
|
+
name: "summarizer",
|
|
417
|
+
description: "Summarizes conversations and documents",
|
|
418
|
+
system_prompt: <<~PROMPT
|
|
419
|
+
Create concise summaries of the provided content.
|
|
420
|
+
Focus on key points and actionable items.
|
|
421
|
+
Use bullet points for clarity.
|
|
295
422
|
PROMPT
|
|
423
|
+
)
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### Bus-Connected Robot
|
|
427
|
+
|
|
428
|
+
Enable bidirectional communication between robots using a message bus. This pattern supports negotiation loops and convergence:
|
|
429
|
+
|
|
430
|
+
```ruby
|
|
431
|
+
bus = TypedBus::MessageBus.new
|
|
432
|
+
|
|
433
|
+
class Comedian < RobotLab::Robot
|
|
434
|
+
def initialize(bus:)
|
|
435
|
+
super(name: "bob", template: :comedian, bus: bus)
|
|
436
|
+
on_message do |message|
|
|
437
|
+
joke = run(message.content.to_s).last_text_content.strip
|
|
438
|
+
send_reply(to: message.from.to_sym, content: joke, in_reply_to: message.key)
|
|
439
|
+
end
|
|
440
|
+
end
|
|
441
|
+
end
|
|
296
442
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
443
|
+
class ComedyCritic < RobotLab::Robot
|
|
444
|
+
def initialize(bus:)
|
|
445
|
+
super(name: "alice", template: :comedy_critic, bus: bus)
|
|
446
|
+
@accepted = false
|
|
447
|
+
on_message do |message|
|
|
448
|
+
verdict = run("Evaluate: #{message.content}").last_text_content.strip
|
|
449
|
+
@accepted = verdict.start_with?("FUNNY")
|
|
450
|
+
send_message(to: :bob, content: "Try again.") unless @accepted
|
|
451
|
+
end
|
|
301
452
|
end
|
|
453
|
+
attr_reader :accepted
|
|
302
454
|
end
|
|
455
|
+
|
|
456
|
+
bob = Comedian.new(bus: bus)
|
|
457
|
+
alice = ComedyCritic.new(bus: bus)
|
|
458
|
+
alice.send_message(to: :bob, content: "Tell me a funny robot joke.")
|
|
303
459
|
```
|
|
304
460
|
|
|
305
|
-
|
|
461
|
+
The `on_message` block arity controls delivery handling:
|
|
462
|
+
- **1 argument** `|message|` — auto-acknowledges before calling
|
|
463
|
+
- **2 arguments** `|delivery, message|` — manual `delivery.ack!` / `delivery.nack!`
|
|
306
464
|
|
|
307
|
-
|
|
465
|
+
See [Message Bus](../architecture/core-concepts.md#message-bus) for details.
|
|
466
|
+
|
|
467
|
+
### Spawning Robots Dynamically
|
|
468
|
+
|
|
469
|
+
Create new robots at runtime using `spawn`. The bus is created lazily — no upfront wiring required:
|
|
308
470
|
|
|
309
471
|
```ruby
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
description "Summarizes conversations and documents"
|
|
472
|
+
class Dispatcher < RobotLab::Robot
|
|
473
|
+
attr_reader :spawned
|
|
313
474
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
475
|
+
def initialize(bus: nil)
|
|
476
|
+
super(name: "dispatcher", template: :dispatcher, bus: bus)
|
|
477
|
+
@spawned = {}
|
|
478
|
+
|
|
479
|
+
on_message do |message|
|
|
480
|
+
puts "#{message.from} replied: #{message.content.to_s.lines.first&.strip}"
|
|
481
|
+
end
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
def dispatch(question)
|
|
485
|
+
# Ask LLM what specialist to create
|
|
486
|
+
plan = run(question).last_text_content.strip
|
|
487
|
+
role, instruction = plan.split("\n", 2)
|
|
488
|
+
role = role.strip.downcase.gsub(/\s+/, "_")
|
|
489
|
+
|
|
490
|
+
# Spawn (or reuse) a specialist
|
|
491
|
+
specialist = @spawned[role] ||= spawn(
|
|
492
|
+
name: role,
|
|
493
|
+
system_prompt: instruction&.strip || "You are a helpful #{role}."
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
# Have the specialist answer and reply
|
|
497
|
+
answer = specialist.run(question).last_text_content.strip
|
|
498
|
+
specialist.send_message(to: :dispatcher, content: answer)
|
|
499
|
+
end
|
|
319
500
|
end
|
|
320
501
|
```
|
|
321
502
|
|
|
322
|
-
|
|
503
|
+
Key features of `spawn`:
|
|
504
|
+
|
|
505
|
+
- Creates a child robot on the same bus as the parent
|
|
506
|
+
- Creates a bus lazily if the parent doesn't have one
|
|
507
|
+
- Spawned robots can immediately send and receive messages
|
|
508
|
+
- Multiple robots with the same name enable fan-out messaging
|
|
323
509
|
|
|
324
|
-
|
|
510
|
+
Robots can also join a bus after creation:
|
|
325
511
|
|
|
326
512
|
```ruby
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
- Security vulnerabilities
|
|
331
|
-
- Performance issues
|
|
332
|
-
- Best practice violations
|
|
513
|
+
bot = RobotLab.build(name: "latecomer", system_prompt: "Hello.")
|
|
514
|
+
bot.with_bus(existing_bus) # now connected and can send/receive messages
|
|
515
|
+
```
|
|
333
516
|
|
|
334
|
-
|
|
335
|
-
PROMPT
|
|
517
|
+
## Configuration
|
|
336
518
|
|
|
337
|
-
|
|
338
|
-
|
|
519
|
+
RobotLab uses `MywayConfig` for configuration. Access the config object directly -- there is no `RobotLab.configure` block:
|
|
520
|
+
|
|
521
|
+
```ruby
|
|
522
|
+
RobotLab.config.ruby_llm.model # => "claude-sonnet-4"
|
|
523
|
+
RobotLab.config.ruby_llm.request_timeout # => 120
|
|
339
524
|
```
|
|
340
525
|
|
|
341
|
-
|
|
526
|
+
Configuration is loaded from:
|
|
527
|
+
|
|
528
|
+
- Bundled defaults (`lib/robot_lab/config/defaults.yml`)
|
|
529
|
+
- Environment-specific overrides (development, test, production)
|
|
530
|
+
- XDG config files (`~/.config/robot_lab/config.yml`)
|
|
531
|
+
- Project config (`./config/robot_lab.yml`)
|
|
532
|
+
- Environment variables (`ROBOT_LAB_*` prefix)
|
|
533
|
+
|
|
534
|
+
## Best Practices
|
|
535
|
+
|
|
536
|
+
### 1. Clear, Focused Prompts
|
|
342
537
|
|
|
343
538
|
```ruby
|
|
344
|
-
# Good:
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
539
|
+
# Good: Specific and focused
|
|
540
|
+
robot = RobotLab.build(
|
|
541
|
+
name: "reviewer",
|
|
542
|
+
system_prompt: <<~PROMPT
|
|
543
|
+
You are a code reviewer. Review code for:
|
|
544
|
+
- Security vulnerabilities
|
|
545
|
+
- Performance issues
|
|
546
|
+
- Best practice violations
|
|
547
|
+
|
|
548
|
+
Provide specific line numbers and suggestions.
|
|
549
|
+
PROMPT
|
|
550
|
+
)
|
|
350
551
|
|
|
351
|
-
# Bad:
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
552
|
+
# Bad: Vague and unfocused
|
|
553
|
+
robot = RobotLab.build(
|
|
554
|
+
name: "reviewer",
|
|
555
|
+
system_prompt: "You help with code stuff."
|
|
556
|
+
)
|
|
355
557
|
```
|
|
356
558
|
|
|
357
|
-
###
|
|
559
|
+
### 2. Use Templates for Reusable Prompts
|
|
560
|
+
|
|
561
|
+
Templates keep prompts in version-controlled files and allow parameterization:
|
|
358
562
|
|
|
359
563
|
```ruby
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
{ success: false, error: "User not found", user_id: user_id }
|
|
366
|
-
end
|
|
367
|
-
rescue ActiveRecord::ConnectionError => e
|
|
368
|
-
{ success: false, error: "Database unavailable", retry: true }
|
|
369
|
-
end
|
|
564
|
+
robot = RobotLab.build(
|
|
565
|
+
name: "support",
|
|
566
|
+
template: :support,
|
|
567
|
+
context: { company: "TechCo", language: "English" }
|
|
568
|
+
)
|
|
370
569
|
```
|
|
371
570
|
|
|
571
|
+
### 3. Handle Tool Errors Gracefully
|
|
572
|
+
|
|
573
|
+
See [Using Tools: Error Handling](using-tools.md#error-handling) for patterns.
|
|
574
|
+
|
|
372
575
|
## Next Steps
|
|
373
576
|
|
|
374
577
|
- [Creating Networks](creating-networks.md) - Orchestrate multiple robots
|
|
578
|
+
- [Message Bus](../architecture/core-concepts.md#message-bus) - Bidirectional robot communication
|
|
579
|
+
- [Dynamic Spawning](../architecture/core-concepts.md#dynamic-spawning) - Robots creating robots at runtime
|
|
375
580
|
- [Using Tools](using-tools.md) - Advanced tool patterns
|
|
581
|
+
- [Memory Guide](memory.md) - Share data between runs and robots
|
|
376
582
|
- [API Reference: Robot](../api/core/robot.md) - Complete API documentation
|