robot_lab 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.envrc +1 -0
- data/.github/workflows/deploy-github-pages.yml +52 -0
- data/.github/workflows/deploy-yard-docs.yml +52 -0
- data/CHANGELOG.md +55 -0
- data/COMMITS.md +196 -0
- data/LICENSE.txt +21 -0
- data/README.md +332 -0
- data/Rakefile +67 -0
- data/docs/api/adapters/anthropic.md +121 -0
- data/docs/api/adapters/gemini.md +133 -0
- data/docs/api/adapters/index.md +104 -0
- data/docs/api/adapters/openai.md +134 -0
- data/docs/api/core/index.md +113 -0
- data/docs/api/core/memory.md +314 -0
- data/docs/api/core/network.md +291 -0
- data/docs/api/core/robot.md +273 -0
- data/docs/api/core/state.md +273 -0
- data/docs/api/core/tool.md +353 -0
- data/docs/api/history/active-record-adapter.md +195 -0
- data/docs/api/history/config.md +191 -0
- data/docs/api/history/index.md +132 -0
- data/docs/api/history/thread-manager.md +144 -0
- data/docs/api/index.md +82 -0
- data/docs/api/mcp/client.md +221 -0
- data/docs/api/mcp/index.md +111 -0
- data/docs/api/mcp/server.md +225 -0
- data/docs/api/mcp/transports.md +264 -0
- data/docs/api/messages/index.md +67 -0
- data/docs/api/messages/text-message.md +102 -0
- data/docs/api/messages/tool-call-message.md +144 -0
- data/docs/api/messages/tool-result-message.md +154 -0
- data/docs/api/messages/user-message.md +171 -0
- data/docs/api/streaming/context.md +174 -0
- data/docs/api/streaming/events.md +237 -0
- data/docs/api/streaming/index.md +108 -0
- data/docs/architecture/core-concepts.md +243 -0
- data/docs/architecture/index.md +138 -0
- data/docs/architecture/message-flow.md +320 -0
- data/docs/architecture/network-orchestration.md +216 -0
- data/docs/architecture/robot-execution.md +243 -0
- data/docs/architecture/state-management.md +323 -0
- data/docs/assets/css/custom.css +56 -0
- data/docs/assets/images/robot_lab.jpg +0 -0
- data/docs/concepts.md +216 -0
- data/docs/examples/basic-chat.md +193 -0
- data/docs/examples/index.md +129 -0
- data/docs/examples/mcp-server.md +290 -0
- data/docs/examples/multi-robot-network.md +312 -0
- data/docs/examples/rails-application.md +420 -0
- data/docs/examples/tool-usage.md +310 -0
- data/docs/getting-started/configuration.md +230 -0
- data/docs/getting-started/index.md +56 -0
- data/docs/getting-started/installation.md +179 -0
- data/docs/getting-started/quick-start.md +203 -0
- data/docs/guides/building-robots.md +376 -0
- data/docs/guides/creating-networks.md +366 -0
- data/docs/guides/history.md +359 -0
- data/docs/guides/index.md +68 -0
- data/docs/guides/mcp-integration.md +356 -0
- data/docs/guides/memory.md +309 -0
- data/docs/guides/rails-integration.md +432 -0
- data/docs/guides/streaming.md +314 -0
- data/docs/guides/using-tools.md +394 -0
- data/docs/index.md +160 -0
- data/examples/01_simple_robot.rb +38 -0
- data/examples/02_tools.rb +106 -0
- data/examples/03_network.rb +103 -0
- data/examples/04_mcp.rb +219 -0
- data/examples/05_streaming.rb +124 -0
- data/examples/06_prompt_templates.rb +324 -0
- data/examples/07_network_memory.rb +329 -0
- data/examples/prompts/assistant/system.txt.erb +2 -0
- data/examples/prompts/assistant/user.txt.erb +1 -0
- data/examples/prompts/billing/system.txt.erb +7 -0
- data/examples/prompts/billing/user.txt.erb +1 -0
- data/examples/prompts/classifier/system.txt.erb +4 -0
- data/examples/prompts/classifier/user.txt.erb +1 -0
- data/examples/prompts/entity_extractor/system.txt.erb +11 -0
- data/examples/prompts/entity_extractor/user.txt.erb +3 -0
- data/examples/prompts/escalation/system.txt.erb +35 -0
- data/examples/prompts/escalation/user.txt.erb +34 -0
- data/examples/prompts/general/system.txt.erb +4 -0
- data/examples/prompts/general/user.txt.erb +1 -0
- data/examples/prompts/github_assistant/system.txt.erb +6 -0
- data/examples/prompts/github_assistant/user.txt.erb +1 -0
- data/examples/prompts/helper/system.txt.erb +1 -0
- data/examples/prompts/helper/user.txt.erb +1 -0
- data/examples/prompts/keyword_extractor/system.txt.erb +8 -0
- data/examples/prompts/keyword_extractor/user.txt.erb +3 -0
- data/examples/prompts/order_support/system.txt.erb +27 -0
- data/examples/prompts/order_support/user.txt.erb +22 -0
- data/examples/prompts/product_support/system.txt.erb +30 -0
- data/examples/prompts/product_support/user.txt.erb +32 -0
- data/examples/prompts/sentiment_analyzer/system.txt.erb +9 -0
- data/examples/prompts/sentiment_analyzer/user.txt.erb +3 -0
- data/examples/prompts/synthesizer/system.txt.erb +14 -0
- data/examples/prompts/synthesizer/user.txt.erb +15 -0
- data/examples/prompts/technical/system.txt.erb +7 -0
- data/examples/prompts/technical/user.txt.erb +1 -0
- data/examples/prompts/triage/system.txt.erb +16 -0
- data/examples/prompts/triage/user.txt.erb +17 -0
- data/lib/generators/robot_lab/install_generator.rb +78 -0
- data/lib/generators/robot_lab/robot_generator.rb +55 -0
- data/lib/generators/robot_lab/templates/initializer.rb.tt +41 -0
- data/lib/generators/robot_lab/templates/migration.rb.tt +32 -0
- data/lib/generators/robot_lab/templates/result_model.rb.tt +52 -0
- data/lib/generators/robot_lab/templates/robot.rb.tt +46 -0
- data/lib/generators/robot_lab/templates/robot_test.rb.tt +32 -0
- data/lib/generators/robot_lab/templates/routing_robot.rb.tt +53 -0
- data/lib/generators/robot_lab/templates/thread_model.rb.tt +40 -0
- data/lib/robot_lab/adapters/anthropic.rb +163 -0
- data/lib/robot_lab/adapters/base.rb +85 -0
- data/lib/robot_lab/adapters/gemini.rb +193 -0
- data/lib/robot_lab/adapters/openai.rb +159 -0
- data/lib/robot_lab/adapters/registry.rb +81 -0
- data/lib/robot_lab/configuration.rb +143 -0
- data/lib/robot_lab/error.rb +32 -0
- data/lib/robot_lab/errors.rb +70 -0
- data/lib/robot_lab/history/active_record_adapter.rb +146 -0
- data/lib/robot_lab/history/config.rb +115 -0
- data/lib/robot_lab/history/thread_manager.rb +93 -0
- data/lib/robot_lab/mcp/client.rb +210 -0
- data/lib/robot_lab/mcp/server.rb +84 -0
- data/lib/robot_lab/mcp/transports/base.rb +56 -0
- data/lib/robot_lab/mcp/transports/sse.rb +117 -0
- data/lib/robot_lab/mcp/transports/stdio.rb +133 -0
- data/lib/robot_lab/mcp/transports/streamable_http.rb +139 -0
- data/lib/robot_lab/mcp/transports/websocket.rb +108 -0
- data/lib/robot_lab/memory.rb +882 -0
- data/lib/robot_lab/memory_change.rb +123 -0
- data/lib/robot_lab/message.rb +357 -0
- data/lib/robot_lab/network.rb +350 -0
- data/lib/robot_lab/rails/engine.rb +29 -0
- data/lib/robot_lab/rails/railtie.rb +42 -0
- data/lib/robot_lab/robot.rb +560 -0
- data/lib/robot_lab/robot_result.rb +205 -0
- data/lib/robot_lab/robotic_model.rb +324 -0
- data/lib/robot_lab/state_proxy.rb +188 -0
- data/lib/robot_lab/streaming/context.rb +144 -0
- data/lib/robot_lab/streaming/events.rb +95 -0
- data/lib/robot_lab/streaming/sequence_counter.rb +48 -0
- data/lib/robot_lab/task.rb +117 -0
- data/lib/robot_lab/tool.rb +223 -0
- data/lib/robot_lab/tool_config.rb +112 -0
- data/lib/robot_lab/tool_manifest.rb +234 -0
- data/lib/robot_lab/user_message.rb +118 -0
- data/lib/robot_lab/version.rb +5 -0
- data/lib/robot_lab/waiter.rb +73 -0
- data/lib/robot_lab.rb +195 -0
- data/mkdocs.yml +214 -0
- data/sig/robot_lab.rbs +4 -0
- metadata +442 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example 5: Streaming Events
|
|
5
|
+
#
|
|
6
|
+
# Demonstrates real-time streaming of robot responses.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# ANTHROPIC_API_KEY=your_key ruby examples/05_streaming.rb
|
|
10
|
+
|
|
11
|
+
require_relative "../lib/robot_lab"
|
|
12
|
+
|
|
13
|
+
# Configure RobotLab
|
|
14
|
+
RobotLab.configure do |config|
|
|
15
|
+
config.anthropic_api_key = ENV.fetch("ANTHROPIC_API_KEY", nil)
|
|
16
|
+
config.template_path = File.join(__dir__, "prompts")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Create a streaming handler
|
|
20
|
+
streaming_handler = lambda do |event|
|
|
21
|
+
case event[:event]
|
|
22
|
+
when RobotLab::Streaming::Events::RUN_STARTED
|
|
23
|
+
puts "[#{event[:data][:scope]}] Run started: #{event[:data][:run_id]}"
|
|
24
|
+
puts "-" * 40
|
|
25
|
+
|
|
26
|
+
when RobotLab::Streaming::Events::TEXT_DELTA
|
|
27
|
+
# Print text deltas without newline for streaming effect
|
|
28
|
+
print event[:data][:delta]
|
|
29
|
+
$stdout.flush
|
|
30
|
+
|
|
31
|
+
when RobotLab::Streaming::Events::TOOL_CALL_ARGUMENTS_DELTA
|
|
32
|
+
puts "[Tool call] #{event[:data][:tool_name]}: #{event[:data][:delta]}"
|
|
33
|
+
|
|
34
|
+
when RobotLab::Streaming::Events::TOOL_CALL_OUTPUT_DELTA
|
|
35
|
+
puts "[Tool output] #{event[:data][:delta]}"
|
|
36
|
+
|
|
37
|
+
when RobotLab::Streaming::Events::RUN_COMPLETED
|
|
38
|
+
puts ""
|
|
39
|
+
puts "-" * 40
|
|
40
|
+
puts "[#{event[:data][:scope]}] Run completed"
|
|
41
|
+
|
|
42
|
+
when RobotLab::Streaming::Events::RUN_FAILED
|
|
43
|
+
puts ""
|
|
44
|
+
puts "[ERROR] Run failed: #{event[:data][:error]}"
|
|
45
|
+
|
|
46
|
+
else
|
|
47
|
+
# Log other events at debug level
|
|
48
|
+
# puts "[DEBUG] #{event[:event]}: #{event[:data].keys.join(', ')}"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Create streaming context for testing
|
|
53
|
+
context = RobotLab::Streaming::Context.new(
|
|
54
|
+
run_id: SecureRandom.uuid,
|
|
55
|
+
message_id: SecureRandom.uuid,
|
|
56
|
+
scope: "robot",
|
|
57
|
+
publish: streaming_handler
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
puts "Streaming Events Example"
|
|
61
|
+
puts "=" * 40
|
|
62
|
+
puts ""
|
|
63
|
+
|
|
64
|
+
# Simulate streaming events
|
|
65
|
+
puts "Simulating streaming events:"
|
|
66
|
+
puts ""
|
|
67
|
+
|
|
68
|
+
# Simulate run started
|
|
69
|
+
context.publish_event(
|
|
70
|
+
event: RobotLab::Streaming::Events::RUN_STARTED,
|
|
71
|
+
data: {}
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Simulate text streaming
|
|
75
|
+
text = "Hello! I'm demonstrating streaming output. Each word appears as it's generated, creating a real-time effect."
|
|
76
|
+
text.split(" ").each do |word|
|
|
77
|
+
context.publish_event(
|
|
78
|
+
event: RobotLab::Streaming::Events::TEXT_DELTA,
|
|
79
|
+
data: { delta: word + " " }
|
|
80
|
+
)
|
|
81
|
+
sleep 0.1 # Simulate generation delay
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Simulate completion
|
|
85
|
+
context.publish_event(
|
|
86
|
+
event: RobotLab::Streaming::Events::RUN_COMPLETED,
|
|
87
|
+
data: {}
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
puts ""
|
|
91
|
+
puts "=" * 40
|
|
92
|
+
puts ""
|
|
93
|
+
puts "Using streaming with a robot:"
|
|
94
|
+
puts ""
|
|
95
|
+
puts <<~CODE
|
|
96
|
+
# Create robot with template
|
|
97
|
+
robot = RobotLab.build(
|
|
98
|
+
name: "streamer",
|
|
99
|
+
template: :helper,
|
|
100
|
+
model: "claude-sonnet-4"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Run with streaming callback
|
|
104
|
+
result = robot.run(message: "Tell me a story") do |event|
|
|
105
|
+
case event[:event]
|
|
106
|
+
when "text.delta"
|
|
107
|
+
print event[:data][:delta]
|
|
108
|
+
when "run.completed"
|
|
109
|
+
puts "\\nDone!"
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
CODE
|
|
113
|
+
|
|
114
|
+
puts ""
|
|
115
|
+
puts "Or with a network:"
|
|
116
|
+
puts ""
|
|
117
|
+
puts <<~CODE
|
|
118
|
+
streaming_handler = ->(event) { broadcast_to_websocket(event) }
|
|
119
|
+
|
|
120
|
+
network.run(
|
|
121
|
+
message: "Process this request",
|
|
122
|
+
streaming: streaming_handler
|
|
123
|
+
)
|
|
124
|
+
CODE
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example 6: Prompt Templates with ruby_llm-template
|
|
5
|
+
#
|
|
6
|
+
# Demonstrates using ruby_llm-template for organized, reusable prompts
|
|
7
|
+
# within a RobotLab network. This example shows an e-commerce support
|
|
8
|
+
# system with dynamic context injection using SimpleFlow's optional task routing.
|
|
9
|
+
#
|
|
10
|
+
# Usage:
|
|
11
|
+
# ANTHROPIC_API_KEY=your_key ruby examples/06_prompt_templates.rb
|
|
12
|
+
#
|
|
13
|
+
# Template Structure:
|
|
14
|
+
# examples/prompts/
|
|
15
|
+
# ├── triage/
|
|
16
|
+
# │ ├── system.txt.erb
|
|
17
|
+
# │ └── user.txt.erb
|
|
18
|
+
# ├── order_support/
|
|
19
|
+
# │ ├── system.txt.erb
|
|
20
|
+
# │ └── user.txt.erb
|
|
21
|
+
# ├── product_support/
|
|
22
|
+
# │ ├── system.txt.erb
|
|
23
|
+
# │ └── user.txt.erb
|
|
24
|
+
# └── escalation/
|
|
25
|
+
# ├── system.txt.erb
|
|
26
|
+
# └── user.txt.erb
|
|
27
|
+
|
|
28
|
+
require_relative "../lib/robot_lab"
|
|
29
|
+
|
|
30
|
+
# =============================================================================
|
|
31
|
+
# Sample Data
|
|
32
|
+
# =============================================================================
|
|
33
|
+
# Simulated customer and business data that would come from your database.
|
|
34
|
+
|
|
35
|
+
COMPANY_NAME = "TechGear Pro"
|
|
36
|
+
|
|
37
|
+
SAMPLE_CUSTOMER = {
|
|
38
|
+
name: "Sarah Johnson",
|
|
39
|
+
email: "sarah.johnson@example.com",
|
|
40
|
+
account_type: "Premium",
|
|
41
|
+
member_since: "2022-03-15",
|
|
42
|
+
vip: true,
|
|
43
|
+
lifetime_value: 4_250.00,
|
|
44
|
+
recent_orders: [
|
|
45
|
+
{ id: "ORD-2024-1847", date: "2024-01-10", status: "Delivered", total: 299.99 },
|
|
46
|
+
{ id: "ORD-2024-1923", date: "2024-01-15", status: "Processing", total: 149.50 }
|
|
47
|
+
],
|
|
48
|
+
open_tickets: [
|
|
49
|
+
{ id: "TKT-5521", subject: "Delayed shipment inquiry", status: "Open" }
|
|
50
|
+
],
|
|
51
|
+
purchase_history: [
|
|
52
|
+
{ category: "Electronics" },
|
|
53
|
+
{ category: "Accessories" },
|
|
54
|
+
{ category: "Audio" }
|
|
55
|
+
],
|
|
56
|
+
escalation_history: []
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
CATEGORIES = [
|
|
60
|
+
{ name: "order", description: "Order status, shipping, returns, refunds" },
|
|
61
|
+
{ name: "product", description: "Product questions, specifications, recommendations" },
|
|
62
|
+
{ name: "escalation", description: "Complex issues, complaints, special requests" }
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
ORDERS = [
|
|
66
|
+
{
|
|
67
|
+
id: "ORD-2024-1847",
|
|
68
|
+
date: "2024-01-10",
|
|
69
|
+
status: "Delivered",
|
|
70
|
+
total: 299.99,
|
|
71
|
+
tracking: "1Z999AA10123456784",
|
|
72
|
+
items: [
|
|
73
|
+
{ name: "Wireless Noise-Canceling Headphones", quantity: 1, price: 249.99 },
|
|
74
|
+
{ name: "Premium Carrying Case", quantity: 1, price: 49.99 }
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: "ORD-2024-1923",
|
|
79
|
+
date: "2024-01-15",
|
|
80
|
+
status: "Processing",
|
|
81
|
+
total: 149.50,
|
|
82
|
+
tracking: nil,
|
|
83
|
+
items: [
|
|
84
|
+
{ name: "USB-C Hub Pro", quantity: 1, price: 89.50 },
|
|
85
|
+
{ name: "Braided USB-C Cable 2m", quantity: 2, price: 30.00 }
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
PRODUCTS = [
|
|
91
|
+
{
|
|
92
|
+
name: "Wireless Noise-Canceling Headphones XR500",
|
|
93
|
+
sku: "AUDIO-XR500",
|
|
94
|
+
price: 249.99,
|
|
95
|
+
category: "Audio",
|
|
96
|
+
in_stock: true,
|
|
97
|
+
quantity: 45,
|
|
98
|
+
features: ["40hr battery life", "Active noise cancellation", "Bluetooth 5.2", "Hi-Res Audio"],
|
|
99
|
+
compatible_with: ["All Bluetooth devices", "3.5mm audio jack"]
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: "USB-C Hub Pro 7-in-1",
|
|
103
|
+
sku: "ACC-HUB7",
|
|
104
|
+
price: 89.50,
|
|
105
|
+
category: "Accessories",
|
|
106
|
+
in_stock: true,
|
|
107
|
+
quantity: 120,
|
|
108
|
+
features: ["4K HDMI output", "100W Power Delivery", "SD/MicroSD slots", "USB 3.0 ports"],
|
|
109
|
+
compatible_with: ["MacBook Pro", "MacBook Air", "iPad Pro", "Windows laptops"]
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
PROMOTIONS = [
|
|
114
|
+
{ name: "New Year Sale", description: "15% off all audio products", code: "AUDIO15" },
|
|
115
|
+
{ name: "Free Shipping", description: "Free shipping on orders over $75", code: "FREESHIP" }
|
|
116
|
+
]
|
|
117
|
+
|
|
118
|
+
PRODUCT_CATEGORIES = [
|
|
119
|
+
{ name: "Electronics", description: "Laptops, tablets, smartphones" },
|
|
120
|
+
{ name: "Audio", description: "Headphones, speakers, microphones" },
|
|
121
|
+
{ name: "Accessories", description: "Cables, hubs, cases, chargers" }
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
POLICIES = {
|
|
125
|
+
refund_window: 30,
|
|
126
|
+
free_shipping_threshold: 75,
|
|
127
|
+
express_fee: "$12.99"
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
ORDER_CAPABILITIES = [
|
|
131
|
+
"Check order status and tracking",
|
|
132
|
+
"Process returns and exchanges",
|
|
133
|
+
"Issue refunds (up to $500 without manager approval)",
|
|
134
|
+
"Modify pending orders",
|
|
135
|
+
"Apply shipping upgrades"
|
|
136
|
+
]
|
|
137
|
+
|
|
138
|
+
ESCALATION_AUTHORITIES = [
|
|
139
|
+
{ name: "Courtesy Credit", description: "Account credit for inconvenience", limit: "$50" },
|
|
140
|
+
{ name: "Expedited Shipping", description: "Free upgrade to express shipping", limit: "Unlimited" },
|
|
141
|
+
{ name: "Extended Return Window", description: "Extend return period", limit: "60 days" },
|
|
142
|
+
{ name: "Price Match", description: "Match competitor pricing", limit: "20% max discount" }
|
|
143
|
+
]
|
|
144
|
+
|
|
145
|
+
# =============================================================================
|
|
146
|
+
# Triage Robot with Routing Logic
|
|
147
|
+
# =============================================================================
|
|
148
|
+
|
|
149
|
+
# Custom triage robot that classifies and activates appropriate specialist
|
|
150
|
+
class TriageRobot < RobotLab::Robot
|
|
151
|
+
def call(result)
|
|
152
|
+
robot_result = run(**extract_run_context(result))
|
|
153
|
+
|
|
154
|
+
new_result = result
|
|
155
|
+
.with_context(@name.to_sym, robot_result)
|
|
156
|
+
.continue(robot_result)
|
|
157
|
+
|
|
158
|
+
# Examine LLM output and activate appropriate specialist
|
|
159
|
+
classification = robot_result.last_text_content.to_s.strip.downcase
|
|
160
|
+
|
|
161
|
+
case classification
|
|
162
|
+
when /order/
|
|
163
|
+
new_result.activate(:order)
|
|
164
|
+
when /product/
|
|
165
|
+
new_result.activate(:product)
|
|
166
|
+
when /escalat/
|
|
167
|
+
new_result.activate(:escalation)
|
|
168
|
+
else
|
|
169
|
+
# Default to escalation for unclear cases
|
|
170
|
+
new_result.activate(:escalation)
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# =============================================================================
|
|
176
|
+
# Main Demo
|
|
177
|
+
# =============================================================================
|
|
178
|
+
|
|
179
|
+
puts "=" * 70
|
|
180
|
+
puts "RobotLab + Prompt Templates Demo"
|
|
181
|
+
puts "E-Commerce Support Network with Dynamic Context"
|
|
182
|
+
puts "=" * 70
|
|
183
|
+
puts
|
|
184
|
+
|
|
185
|
+
# Configure RobotLab
|
|
186
|
+
RobotLab.configure do |config|
|
|
187
|
+
config.anthropic_api_key = ENV.fetch("ANTHROPIC_API_KEY", nil)
|
|
188
|
+
config.template_path = File.join(__dir__, "prompts")
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# -----------------------------------------------------------------------------
|
|
192
|
+
# Build Robots with Template-Based Prompts
|
|
193
|
+
# -----------------------------------------------------------------------------
|
|
194
|
+
|
|
195
|
+
# Triage Robot - Classifies incoming requests
|
|
196
|
+
triage_robot = TriageRobot.new(
|
|
197
|
+
name: "triage",
|
|
198
|
+
description: "Classifies incoming requests to route to specialists",
|
|
199
|
+
template: :triage,
|
|
200
|
+
context: {
|
|
201
|
+
company_name: COMPANY_NAME,
|
|
202
|
+
categories: CATEGORIES
|
|
203
|
+
},
|
|
204
|
+
model: "claude-sonnet-4"
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# Order Support Robot
|
|
208
|
+
order_robot = RobotLab.build(
|
|
209
|
+
name: "order",
|
|
210
|
+
description: "Handles order-related inquiries with full order history",
|
|
211
|
+
template: :order_support,
|
|
212
|
+
context: {
|
|
213
|
+
company_name: COMPANY_NAME,
|
|
214
|
+
policies: POLICIES,
|
|
215
|
+
capabilities: ORDER_CAPABILITIES
|
|
216
|
+
},
|
|
217
|
+
model: "claude-sonnet-4"
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
# Product Support Robot
|
|
221
|
+
product_robot = RobotLab.build(
|
|
222
|
+
name: "product",
|
|
223
|
+
description: "Answers product questions with catalog knowledge",
|
|
224
|
+
template: :product_support,
|
|
225
|
+
context: {
|
|
226
|
+
company_name: COMPANY_NAME,
|
|
227
|
+
products: PRODUCTS,
|
|
228
|
+
promotions: PROMOTIONS,
|
|
229
|
+
product_categories: PRODUCT_CATEGORIES
|
|
230
|
+
},
|
|
231
|
+
model: "claude-sonnet-4"
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
# Escalation Robot
|
|
235
|
+
escalation_robot = RobotLab.build(
|
|
236
|
+
name: "escalation",
|
|
237
|
+
description: "Handles complex cases requiring special authority",
|
|
238
|
+
template: :escalation,
|
|
239
|
+
context: {
|
|
240
|
+
company_name: COMPANY_NAME,
|
|
241
|
+
authorities: ESCALATION_AUTHORITIES
|
|
242
|
+
},
|
|
243
|
+
model: "claude-sonnet-4"
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
# -----------------------------------------------------------------------------
|
|
247
|
+
# Create Network with Optional Task Routing
|
|
248
|
+
# -----------------------------------------------------------------------------
|
|
249
|
+
|
|
250
|
+
network = RobotLab.create_network(name: "ecommerce_support") do
|
|
251
|
+
task :triage, triage_robot, depends_on: :none
|
|
252
|
+
task :order, order_robot, depends_on: :optional
|
|
253
|
+
task :product, product_robot, depends_on: :optional
|
|
254
|
+
task :escalation, escalation_robot, depends_on: :optional
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# -----------------------------------------------------------------------------
|
|
258
|
+
# Run Demo Scenarios
|
|
259
|
+
# -----------------------------------------------------------------------------
|
|
260
|
+
|
|
261
|
+
demo_queries = [
|
|
262
|
+
{
|
|
263
|
+
label: "Order Inquiry",
|
|
264
|
+
message: "Where is my order ORD-2024-1923? It's been 5 days and still shows processing."
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
label: "Product Question",
|
|
268
|
+
message: "Are the XR500 headphones compatible with my iPhone? What's the battery life?"
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
label: "Escalation Case",
|
|
272
|
+
message: "This is ridiculous! I've been waiting 2 weeks for my order and nobody can help me. I want a refund AND compensation for this terrible experience!"
|
|
273
|
+
}
|
|
274
|
+
]
|
|
275
|
+
|
|
276
|
+
demo_queries.each_with_index do |query, index|
|
|
277
|
+
puts
|
|
278
|
+
puts "-" * 70
|
|
279
|
+
puts "Scenario #{index + 1}: #{query[:label]}"
|
|
280
|
+
puts "-" * 70
|
|
281
|
+
puts "Customer: #{query[:message]}"
|
|
282
|
+
puts
|
|
283
|
+
|
|
284
|
+
# Run the network with context
|
|
285
|
+
result = network.run(
|
|
286
|
+
message: query[:message],
|
|
287
|
+
customer: SAMPLE_CUSTOMER,
|
|
288
|
+
orders: ORDERS
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
# Display triage classification
|
|
292
|
+
if result.context[:triage]
|
|
293
|
+
triage_result = result.context[:triage]
|
|
294
|
+
puts "Classification: #{triage_result.last_text_content}"
|
|
295
|
+
puts
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# Display specialist response (the final value)
|
|
299
|
+
if result.value.is_a?(RobotLab::RobotResult)
|
|
300
|
+
puts "Routed to: #{result.value.robot_name.upcase}"
|
|
301
|
+
content = result.value.last_text_content.to_s
|
|
302
|
+
# Truncate long responses for display
|
|
303
|
+
if content.length > 300
|
|
304
|
+
puts "Response: #{content[0..300]}..."
|
|
305
|
+
else
|
|
306
|
+
puts "Response: #{content}"
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
puts
|
|
311
|
+
puts "=" * 70
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
puts
|
|
315
|
+
puts "Demo Complete!"
|
|
316
|
+
puts
|
|
317
|
+
puts "This example demonstrates:"
|
|
318
|
+
puts " - ruby_llm-template for organized, reusable prompts"
|
|
319
|
+
puts " - Build-time context (robot identity/capabilities)"
|
|
320
|
+
puts " - Run-time context (customer data, order history)"
|
|
321
|
+
puts " - Multi-robot network with optional task routing"
|
|
322
|
+
puts " - SimpleFlow::Result for passing context between robots"
|
|
323
|
+
puts
|
|
324
|
+
puts "Template files are located in: #{File.join(__dir__, 'prompts')}"
|