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/architecture/index.md
CHANGED
|
@@ -8,28 +8,30 @@ RobotLab is designed around a few core architectural principles that enable flex
|
|
|
8
8
|
|
|
9
9
|
Each component has a single, well-defined responsibility:
|
|
10
10
|
|
|
11
|
-
- **Robot**:
|
|
12
|
-
- **Network**: Orchestrates robot execution
|
|
13
|
-
- **
|
|
14
|
-
- **Tool**: Provides external capabilities to robots
|
|
11
|
+
- **Robot**: LLM-powered agent (subclass of `RubyLLM::Agent`) with personality, tools, and memory
|
|
12
|
+
- **Network**: Orchestrates robot execution as a DAG pipeline via SimpleFlow
|
|
13
|
+
- **Memory**: Reactive key-value store for robot and network data
|
|
14
|
+
- **Tool**: Provides external capabilities to robots (inherits from `RubyLLM::Tool`)
|
|
15
|
+
- **Task**: Wraps a robot for pipeline execution with per-task configuration
|
|
15
16
|
|
|
16
17
|
### 2. Composability
|
|
17
18
|
|
|
18
19
|
Components are designed to be mixed and matched:
|
|
19
20
|
|
|
20
|
-
- Robots can be
|
|
21
|
-
- Tools can be shared or robot
|
|
22
|
-
- Networks
|
|
23
|
-
-
|
|
21
|
+
- Robots can be used standalone or within networks
|
|
22
|
+
- Tools can be shared across robots or scoped per-robot via `local_tools:`
|
|
23
|
+
- Networks define DAG pipelines with sequential, parallel, and optional execution
|
|
24
|
+
- Memory can be standalone (per-robot) or shared (per-network)
|
|
25
|
+
- `with_*` methods return `self` for fluent chaining
|
|
24
26
|
|
|
25
27
|
### 3. Provider Agnostic
|
|
26
28
|
|
|
27
|
-
RobotLab abstracts away LLM provider differences:
|
|
29
|
+
RobotLab abstracts away LLM provider differences through RubyLLM:
|
|
28
30
|
|
|
29
|
-
- Unified
|
|
31
|
+
- Unified interface across Anthropic, OpenAI, Gemini, DeepSeek, Mistral, and others
|
|
30
32
|
- Consistent tool calling interface
|
|
31
33
|
- Automatic provider detection from model names
|
|
32
|
-
- Easy switching between providers
|
|
34
|
+
- Easy switching between providers via configuration
|
|
33
35
|
|
|
34
36
|
## System Architecture
|
|
35
37
|
|
|
@@ -41,98 +43,134 @@ graph TB
|
|
|
41
43
|
|
|
42
44
|
subgraph "RobotLab Core"
|
|
43
45
|
B[Network]
|
|
44
|
-
C[
|
|
45
|
-
D[Robot]
|
|
46
|
-
E[
|
|
47
|
-
F[
|
|
46
|
+
C[Task]
|
|
47
|
+
D[Robot < RubyLLM::Agent]
|
|
48
|
+
E[Memory]
|
|
49
|
+
F[RobotResult]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
subgraph "Configuration"
|
|
53
|
+
G[Config < MywayConfig::Base]
|
|
48
54
|
end
|
|
49
55
|
|
|
50
56
|
subgraph "Integration Layer"
|
|
51
|
-
G[Adapters]
|
|
52
57
|
H[MCP Client]
|
|
53
|
-
I[Tools]
|
|
58
|
+
I[Tools < RubyLLM::Tool]
|
|
59
|
+
J[Templates / prompt_manager]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
subgraph "Execution Layer"
|
|
63
|
+
K[SimpleFlow::Pipeline]
|
|
64
|
+
L[RubyLLM Chat]
|
|
54
65
|
end
|
|
55
66
|
|
|
56
67
|
subgraph "Provider Layer"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
68
|
+
M[Anthropic]
|
|
69
|
+
N[OpenAI]
|
|
70
|
+
O[Gemini]
|
|
71
|
+
P[MCP Servers]
|
|
61
72
|
end
|
|
62
73
|
|
|
63
74
|
A --> B
|
|
75
|
+
A --> D
|
|
64
76
|
B --> C
|
|
65
|
-
|
|
77
|
+
C --> D
|
|
78
|
+
B --> K
|
|
66
79
|
B --> E
|
|
67
|
-
|
|
68
|
-
D -->
|
|
80
|
+
D --> E
|
|
81
|
+
D --> L
|
|
69
82
|
D --> H
|
|
70
83
|
D --> I
|
|
71
|
-
|
|
72
|
-
|
|
84
|
+
D --> J
|
|
85
|
+
D --> F
|
|
86
|
+
G --> D
|
|
73
87
|
G --> L
|
|
74
|
-
|
|
88
|
+
L --> M
|
|
89
|
+
L --> N
|
|
90
|
+
L --> O
|
|
91
|
+
H --> P
|
|
75
92
|
```
|
|
76
93
|
|
|
77
94
|
## Core Components
|
|
78
95
|
|
|
79
96
|
| Component | Description | Documentation |
|
|
80
97
|
|-----------|-------------|---------------|
|
|
81
|
-
| **Robot** | LLM
|
|
82
|
-
| **Network** | Orchestrates multiple robots | [Network Orchestration](network-orchestration.md) |
|
|
83
|
-
| **
|
|
84
|
-
| **
|
|
85
|
-
| **
|
|
86
|
-
| **
|
|
98
|
+
| **Robot** | LLM agent (subclass of `RubyLLM::Agent`) with template-based prompts, tools, and memory | [Core Concepts](core-concepts.md) |
|
|
99
|
+
| **Network** | Orchestrates multiple robots as a SimpleFlow pipeline | [Network Orchestration](network-orchestration.md) |
|
|
100
|
+
| **Memory** | Reactive key-value store with pub/sub and blocking reads | [Memory Management](state-management.md) |
|
|
101
|
+
| **Task** | Wraps a robot for pipeline execution with per-task config | [Network Orchestration](network-orchestration.md) |
|
|
102
|
+
| **RobotResult** | Captures LLM output, tool calls, and metadata from a run | [Message Flow](message-flow.md) |
|
|
103
|
+
| **Config** | MywayConfig-based configuration with env var and file support | [Configuration](#configuration) |
|
|
104
|
+
|
|
105
|
+
## Configuration
|
|
106
|
+
|
|
107
|
+
RobotLab uses MywayConfig (`Config < MywayConfig::Base`) instead of a `configure` block. Configuration is loaded from multiple sources in priority order:
|
|
108
|
+
|
|
109
|
+
1. **Bundled defaults** (`lib/robot_lab/config/defaults.yml`)
|
|
110
|
+
2. **Environment overrides** (development, test, production sections)
|
|
111
|
+
3. **XDG user config** (`~/.config/robot_lab/config.yml`)
|
|
112
|
+
4. **Project config** (`./config/robot_lab.yml`)
|
|
113
|
+
5. **Environment variables** (`ROBOT_LAB_*` prefix, double underscore for nesting)
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
# Access configuration
|
|
117
|
+
RobotLab.config.ruby_llm.model #=> "claude-sonnet-4"
|
|
118
|
+
RobotLab.config.ruby_llm.request_timeout #=> 120
|
|
119
|
+
```
|
|
87
120
|
|
|
88
121
|
## Data Flow
|
|
89
122
|
|
|
90
|
-
1. **Input**: User message
|
|
91
|
-
2. **
|
|
92
|
-
3. **
|
|
93
|
-
4. **Tools**: Robot
|
|
94
|
-
5. **
|
|
95
|
-
6. **
|
|
96
|
-
7. **
|
|
123
|
+
1. **Input**: User calls `robot.run("message")` or `network.run(message: "...")`
|
|
124
|
+
2. **Memory**: Robot resolves active memory (standalone or network-shared)
|
|
125
|
+
3. **MCP**: Robot resolves and initializes MCP clients from hierarchical config
|
|
126
|
+
4. **Tools**: Robot resolves and filters tools from hierarchical config
|
|
127
|
+
5. **Execution**: Robot delegates to `Agent#ask` which calls `@chat.ask` on RubyLLM
|
|
128
|
+
6. **Tool Loop**: LLM may invoke tools; RubyLLM handles the tool call/result loop
|
|
129
|
+
7. **Result**: Robot builds and returns a `RobotResult`
|
|
130
|
+
8. **Network**: If in a network, result flows to dependent tasks via SimpleFlow
|
|
97
131
|
|
|
98
132
|
## Key Patterns
|
|
99
133
|
|
|
100
|
-
###
|
|
134
|
+
### Factory Methods
|
|
101
135
|
|
|
102
|
-
Robots and networks are
|
|
136
|
+
Robots and networks are created via factory methods on the `RobotLab` module:
|
|
103
137
|
|
|
104
138
|
```ruby
|
|
105
|
-
robot = RobotLab.build
|
|
106
|
-
name "assistant"
|
|
107
|
-
|
|
108
|
-
|
|
139
|
+
robot = RobotLab.build(
|
|
140
|
+
name: "assistant",
|
|
141
|
+
template: :assistant,
|
|
142
|
+
context: { tone: "friendly" }
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
network = RobotLab.create_network(name: "pipeline") do
|
|
146
|
+
task :analyst, analyst_robot, depends_on: :none
|
|
147
|
+
task :writer, writer_robot, depends_on: [:analyst]
|
|
109
148
|
end
|
|
110
149
|
```
|
|
111
150
|
|
|
112
|
-
###
|
|
151
|
+
### Fluent Chaining
|
|
113
152
|
|
|
114
|
-
|
|
153
|
+
`with_*` methods on Robot delegate to the underlying `@chat` and return `self`:
|
|
115
154
|
|
|
116
155
|
```ruby
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
156
|
+
robot = RobotLab.build(name: "bot")
|
|
157
|
+
.with_instructions("Be concise.")
|
|
158
|
+
.with_temperature(0.3)
|
|
159
|
+
.with_model("gpt-4o")
|
|
121
160
|
```
|
|
122
161
|
|
|
123
|
-
###
|
|
162
|
+
### Hierarchical Configuration
|
|
124
163
|
|
|
125
|
-
|
|
164
|
+
Tools and MCP servers use hierarchical resolution: `runtime > robot build > network > global config`. Values can be `:none`, `:inherit`, or explicit arrays.
|
|
126
165
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
```
|
|
166
|
+
### SimpleFlow Pipeline
|
|
167
|
+
|
|
168
|
+
Networks are thin wrappers around `SimpleFlow::Pipeline`. Each robot is wrapped in a `Task` that implements the `call(result)` interface. Tasks define dependencies (`:none`, `[:task_names]`, or `:optional`) to control execution order.
|
|
131
169
|
|
|
132
170
|
## Next Steps
|
|
133
171
|
|
|
134
172
|
- [Core Concepts](core-concepts.md) - Deep dive into robots and tools
|
|
135
173
|
- [Robot Execution](robot-execution.md) - How robots process messages
|
|
136
174
|
- [Network Orchestration](network-orchestration.md) - Multi-robot workflows
|
|
137
|
-
- [
|
|
175
|
+
- [Memory Management](state-management.md) - Managing memory and reactive features
|
|
138
176
|
- [Message Flow](message-flow.md) - How messages move through the system
|
|
@@ -41,9 +41,10 @@ classDiagram
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
Message <|-- TextMessage
|
|
44
|
-
Message <|--
|
|
45
|
-
|
|
46
|
-
ToolMessage
|
|
44
|
+
Message <|-- ToolCallMessage
|
|
45
|
+
Message <|-- ToolResultMessage
|
|
46
|
+
ToolMessage -- ToolCallMessage
|
|
47
|
+
ToolMessage -- ToolResultMessage
|
|
47
48
|
```
|
|
48
49
|
|
|
49
50
|
### TextMessage
|
|
@@ -58,14 +59,14 @@ TextMessage.new(
|
|
|
58
59
|
|
|
59
60
|
TextMessage.new(
|
|
60
61
|
role: "assistant",
|
|
61
|
-
content: "The weather in Paris is sunny and 22
|
|
62
|
+
content: "The weather in Paris is sunny and 22 degrees C.",
|
|
62
63
|
stop_reason: "stop"
|
|
63
64
|
)
|
|
64
65
|
```
|
|
65
66
|
|
|
66
67
|
### ToolMessage
|
|
67
68
|
|
|
68
|
-
|
|
69
|
+
Represents a tool invocation with its parameters:
|
|
69
70
|
|
|
70
71
|
```ruby
|
|
71
72
|
ToolMessage.new(
|
|
@@ -77,7 +78,7 @@ ToolMessage.new(
|
|
|
77
78
|
|
|
78
79
|
### ToolCallMessage
|
|
79
80
|
|
|
80
|
-
LLM's request to execute tools:
|
|
81
|
+
LLM's request to execute one or more tools:
|
|
81
82
|
|
|
82
83
|
```ruby
|
|
83
84
|
ToolCallMessage.new(
|
|
@@ -101,147 +102,159 @@ ToolResultMessage.new(
|
|
|
101
102
|
)
|
|
102
103
|
```
|
|
103
104
|
|
|
104
|
-
## Message Flow
|
|
105
|
+
## Message Flow: Standalone Robot
|
|
106
|
+
|
|
107
|
+
The primary execution path is `robot.run("message")`:
|
|
105
108
|
|
|
106
109
|
```mermaid
|
|
107
110
|
sequenceDiagram
|
|
108
111
|
participant User
|
|
109
|
-
participant State
|
|
110
112
|
participant Robot
|
|
111
|
-
participant
|
|
113
|
+
participant Memory
|
|
114
|
+
participant MCP
|
|
115
|
+
participant Tools
|
|
116
|
+
participant Agent
|
|
117
|
+
participant Chat
|
|
112
118
|
participant LLM
|
|
113
119
|
|
|
114
|
-
User->>
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
Robot->>Adapter: Convert messages
|
|
118
|
-
Adapter->>LLM: Provider-specific format
|
|
119
|
-
|
|
120
|
-
LLM-->>Adapter: Response
|
|
121
|
-
Adapter-->>Robot: Parse response
|
|
122
|
-
Robot->>Robot: Execute tools (if any)
|
|
123
|
-
Robot-->>State: RobotResult
|
|
124
|
-
State->>State: Append result
|
|
125
|
-
State-->>User: Response
|
|
126
|
-
```
|
|
120
|
+
User->>Robot: robot.run("message")
|
|
121
|
+
Robot->>Memory: resolve_active_memory
|
|
122
|
+
Memory-->>Robot: active memory
|
|
127
123
|
|
|
128
|
-
|
|
124
|
+
Robot->>MCP: resolve_mcp_hierarchy
|
|
125
|
+
MCP-->>Robot: resolved MCP config
|
|
126
|
+
Robot->>Robot: ensure_mcp_clients
|
|
129
127
|
|
|
130
|
-
|
|
128
|
+
Robot->>Tools: resolve_tools_hierarchy
|
|
129
|
+
Tools-->>Robot: filtered tools
|
|
130
|
+
Robot->>Chat: @chat.with_tools(...)
|
|
131
131
|
|
|
132
|
-
|
|
132
|
+
Robot->>Agent: ask("message")
|
|
133
|
+
Agent->>Chat: @chat.ask("message")
|
|
134
|
+
Chat->>LLM: Provider API call
|
|
133
135
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
136
|
+
loop Tool Loop (handled by RubyLLM)
|
|
137
|
+
LLM-->>Chat: Tool call response
|
|
138
|
+
Chat->>Tools: Execute tool
|
|
139
|
+
Tools-->>Chat: Tool result
|
|
140
|
+
Chat->>LLM: Send tool result
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
LLM-->>Chat: Final response
|
|
144
|
+
Chat-->>Agent: RubyLLM::Response
|
|
145
|
+
Agent-->>Robot: response
|
|
146
|
+
|
|
147
|
+
Robot->>Robot: build_result(response, memory)
|
|
148
|
+
Robot-->>User: RobotResult
|
|
146
149
|
```
|
|
147
150
|
|
|
148
|
-
###
|
|
151
|
+
### Step-by-Step
|
|
149
152
|
|
|
150
|
-
|
|
153
|
+
1. **`robot.run("message")`**: Entry point. Accepts a positional string argument.
|
|
151
154
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
TextMessage.new(role: "user", content: "Hello")
|
|
157
|
-
]
|
|
158
|
-
|
|
159
|
-
# Anthropic: system extracted separately
|
|
160
|
-
# OpenAI: system message stays in array
|
|
161
|
-
# Gemini: converted to context
|
|
162
|
-
```
|
|
155
|
+
2. **Resolve Memory**: Determines which memory to use:
|
|
156
|
+
- `network_memory` if provided (network execution)
|
|
157
|
+
- `network.memory` if in a network context
|
|
158
|
+
- `robot.memory` (standalone, the default)
|
|
163
159
|
|
|
164
|
-
|
|
160
|
+
3. **Merge Runtime Memory**: If a `memory:` keyword argument is passed, it is merged into the active memory.
|
|
165
161
|
|
|
166
|
-
|
|
162
|
+
4. **Set Current Writer**: Sets `memory.current_writer = robot.name` so subscription callbacks know which robot wrote a value.
|
|
167
163
|
|
|
168
|
-
|
|
164
|
+
5. **Resolve MCP Hierarchy**: Resolves MCP server configuration through the hierarchy: `runtime > robot build > network > global config`.
|
|
169
165
|
|
|
170
|
-
|
|
171
|
-
{
|
|
172
|
-
type: "tool_use",
|
|
173
|
-
id: "tool_123",
|
|
174
|
-
name: "get_weather",
|
|
175
|
-
input: { location: "Paris" }
|
|
176
|
-
}
|
|
177
|
-
```
|
|
166
|
+
6. **Ensure MCP Clients**: Initializes or updates MCP client connections. Discovers tools from connected MCP servers.
|
|
178
167
|
|
|
179
|
-
|
|
168
|
+
7. **Resolve Tools Hierarchy**: Resolves which tools are available through the same hierarchy.
|
|
180
169
|
|
|
181
|
-
|
|
182
|
-
{
|
|
183
|
-
type: "function",
|
|
184
|
-
function: {
|
|
185
|
-
name: "get_weather",
|
|
186
|
-
arguments: '{"location":"Paris"}'
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
```
|
|
170
|
+
8. **Filter Tools**: Applies the resolved tool list to `@chat.with_tools(...)`.
|
|
190
171
|
|
|
191
|
-
|
|
172
|
+
9. **Agent#ask**: Delegates to the parent class `RubyLLM::Agent#ask`, which calls `@chat.ask(message)`.
|
|
192
173
|
|
|
193
|
-
|
|
174
|
+
10. **LLM Interaction**: RubyLLM handles the provider-specific API call, including the tool call/result loop.
|
|
194
175
|
|
|
195
|
-
|
|
196
|
-
state = RobotLab.create_state(
|
|
197
|
-
message: "What's the weather?",
|
|
198
|
-
data: { location: "Paris" }
|
|
199
|
-
)
|
|
176
|
+
11. **Build Result**: Wraps the LLM response in a `RobotResult` containing output messages, tool calls, and metadata.
|
|
200
177
|
|
|
201
|
-
|
|
202
|
-
# => [TextMessage(role: "user", content: "What's the weather?")]
|
|
203
|
-
```
|
|
178
|
+
12. **Return**: Returns the `RobotResult` to the caller.
|
|
204
179
|
|
|
205
|
-
|
|
180
|
+
## Message Flow: Network Execution
|
|
206
181
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
182
|
+
When running through a network, the flow adds pipeline orchestration:
|
|
183
|
+
|
|
184
|
+
```mermaid
|
|
185
|
+
sequenceDiagram
|
|
186
|
+
participant User
|
|
187
|
+
participant Network
|
|
188
|
+
participant Pipeline
|
|
189
|
+
participant Task
|
|
190
|
+
participant Robot
|
|
191
|
+
participant LLM
|
|
192
|
+
|
|
193
|
+
User->>Network: network.run(message: "...")
|
|
194
|
+
Network->>Network: Inject network_memory into run_context
|
|
195
|
+
Network->>Pipeline: SimpleFlow::Pipeline.call_parallel(initial_result)
|
|
196
|
+
|
|
197
|
+
loop For each ready task
|
|
198
|
+
Pipeline->>Task: task.call(result)
|
|
199
|
+
Task->>Task: Deep merge task context with run_params
|
|
200
|
+
Task->>Robot: robot.call(enhanced_result)
|
|
201
|
+
Robot->>Robot: extract_run_context(result)
|
|
202
|
+
Robot->>Robot: run(message, network_memory: ...)
|
|
203
|
+
Robot->>LLM: Agent#ask -> @chat.ask
|
|
204
|
+
LLM-->>Robot: Response
|
|
205
|
+
Robot-->>Task: result.with_context(:name, robot_result).continue(robot_result)
|
|
206
|
+
Task-->>Pipeline: SimpleFlow::Result
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
Pipeline-->>Network: Final SimpleFlow::Result
|
|
210
|
+
Network-->>User: result
|
|
216
211
|
```
|
|
217
212
|
|
|
218
|
-
###
|
|
213
|
+
### Key Points
|
|
214
|
+
|
|
215
|
+
- **Network creates initial result**: `SimpleFlow::Result.new(run_context, context: { run_params: run_context })`
|
|
216
|
+
- **Task wraps robot**: Each `Task` deep-merges its own context with the run params before delegating to the robot
|
|
217
|
+
- **Robot extracts context**: `extract_run_context(result)` pulls the message, MCP, tools, and memory from the SimpleFlow result
|
|
218
|
+
- **Shared memory**: All robots use `network.memory` during network execution
|
|
219
|
+
- **Result accumulation**: Each task stores its `RobotResult` in `result.context[:task_name]`
|
|
220
|
+
|
|
221
|
+
## RobotResult
|
|
222
|
+
|
|
223
|
+
The return value of `robot.run("message")`:
|
|
219
224
|
|
|
220
225
|
```ruby
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
226
|
+
result = robot.run("What is Ruby?")
|
|
227
|
+
|
|
228
|
+
result.last_text_content #=> "Ruby is a dynamic programming language..."
|
|
229
|
+
result.has_tool_calls? #=> false
|
|
230
|
+
result.robot_name #=> "assistant"
|
|
231
|
+
result.output #=> [TextMessage(role: "assistant", content: "...")]
|
|
232
|
+
result.tool_calls #=> []
|
|
233
|
+
result.stop_reason #=> "stop"
|
|
234
|
+
result.created_at #=> Time
|
|
235
|
+
result.id #=> "uuid"
|
|
236
|
+
result.checksum #=> "sha256-hex"
|
|
228
237
|
```
|
|
229
238
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
The `format_history` method prepares messages for LLM:
|
|
239
|
+
### Result Serialization
|
|
233
240
|
|
|
234
241
|
```ruby
|
|
235
|
-
#
|
|
236
|
-
|
|
242
|
+
# Export for persistence (excludes debug fields)
|
|
243
|
+
hash = result.export
|
|
244
|
+
|
|
245
|
+
# Full hash including debug fields
|
|
246
|
+
hash = result.to_h
|
|
247
|
+
|
|
248
|
+
# JSON
|
|
249
|
+
json = result.to_json
|
|
237
250
|
|
|
238
|
-
#
|
|
239
|
-
|
|
251
|
+
# Reconstruct from hash
|
|
252
|
+
result = RobotResult.from_hash(hash)
|
|
240
253
|
```
|
|
241
254
|
|
|
242
255
|
## Message Predicates
|
|
243
256
|
|
|
244
|
-
Check message types
|
|
257
|
+
Check message types:
|
|
245
258
|
|
|
246
259
|
```ruby
|
|
247
260
|
message.text? # Is it a TextMessage?
|
|
@@ -274,14 +287,6 @@ Message.from_hash(
|
|
|
274
287
|
)
|
|
275
288
|
```
|
|
276
289
|
|
|
277
|
-
### From UserMessage
|
|
278
|
-
|
|
279
|
-
```ruby
|
|
280
|
-
user_msg = UserMessage.new("Hello", thread_id: "123")
|
|
281
|
-
text_msg = user_msg.to_message
|
|
282
|
-
# => TextMessage(role: "user", content: "Hello")
|
|
283
|
-
```
|
|
284
|
-
|
|
285
290
|
## Serialization
|
|
286
291
|
|
|
287
292
|
Messages can be serialized:
|
|
@@ -289,32 +294,36 @@ Messages can be serialized:
|
|
|
289
294
|
```ruby
|
|
290
295
|
# To hash
|
|
291
296
|
hash = message.to_h
|
|
292
|
-
|
|
297
|
+
#=> { type: "text", role: "user", content: "Hello" }
|
|
293
298
|
|
|
294
299
|
# To JSON
|
|
295
300
|
json = message.to_json
|
|
296
|
-
# => '{"type":"text","role":"user","content":"Hello"}'
|
|
297
301
|
|
|
298
302
|
# From hash
|
|
299
303
|
message = Message.from_hash(hash)
|
|
300
304
|
```
|
|
301
305
|
|
|
302
|
-
##
|
|
306
|
+
## Template Resolution
|
|
303
307
|
|
|
304
|
-
|
|
308
|
+
When a robot has a template, it is resolved at build time via prompt_manager:
|
|
305
309
|
|
|
306
310
|
```ruby
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
Adapters::Registry.for(:azure_openai) # => Adapters::OpenAI
|
|
313
|
-
Adapters::Registry.for(:bedrock) # => Adapters::Anthropic
|
|
311
|
+
robot = RobotLab.build(
|
|
312
|
+
name: "helper",
|
|
313
|
+
template: :helper,
|
|
314
|
+
context: { tone: "friendly" }
|
|
315
|
+
)
|
|
314
316
|
```
|
|
315
317
|
|
|
318
|
+
The template resolution process:
|
|
319
|
+
1. `PM.parse(:helper)` loads the template file from the configured prompts directory
|
|
320
|
+
2. YAML front matter is extracted and applied to the chat (model, temperature, etc.)
|
|
321
|
+
3. The template body is rendered with the provided context
|
|
322
|
+
4. The rendered text is set as system instructions via `@chat.with_instructions(rendered)`
|
|
323
|
+
|
|
324
|
+
If both `template:` and `system_prompt:` are provided, the template is applied first, then the system prompt is appended via a second `@chat.with_instructions` call.
|
|
325
|
+
|
|
316
326
|
## Next Steps
|
|
317
327
|
|
|
318
|
-
- [
|
|
319
|
-
- [
|
|
320
|
-
- [API Reference: Messages](../api/messages/index.md) - Detailed message API
|
|
328
|
+
- [Memory Management](state-management.md) - How memory stores conversation data
|
|
329
|
+
- [Network Orchestration](network-orchestration.md) - Multi-robot pipeline execution
|