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,329 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example 7: Network Memory with Concurrent Robots
|
|
5
|
+
#
|
|
6
|
+
# Demonstrates the reactive shared memory system where:
|
|
7
|
+
# - Multiple robots run concurrently and write to shared memory
|
|
8
|
+
# - Robots can wait for values written by other robots
|
|
9
|
+
# - Subscriptions provide real-time notifications of memory changes
|
|
10
|
+
# - Network broadcast sends messages to all robots
|
|
11
|
+
#
|
|
12
|
+
# Architecture:
|
|
13
|
+
# ┌─────────────────────────────────────────────────────────────┐
|
|
14
|
+
# │ PARALLEL ANALYSIS │
|
|
15
|
+
# │ │
|
|
16
|
+
# │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
17
|
+
# │ │ Sentiment │ │ Entities │ │ Keywords │ │
|
|
18
|
+
# │ │ Analyzer │ │ Extractor │ │ Extractor │ │
|
|
19
|
+
# │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
|
|
20
|
+
# │ │ │ │ │
|
|
21
|
+
# │ ▼ ▼ ▼ │
|
|
22
|
+
# │ memory.set( memory.set( memory.set( │
|
|
23
|
+
# │ :sentiment) :entities) :keywords) │
|
|
24
|
+
# │ │ │ │ │
|
|
25
|
+
# │ └────────────────┼────────────────┘ │
|
|
26
|
+
# │ ▼ │
|
|
27
|
+
# │ ┌──────────────────────────────────────────────────────┐ │
|
|
28
|
+
# │ │ SHARED MEMORY │ │
|
|
29
|
+
# │ │ { sentiment: {...}, entities: {...}, keywords: {...} │ │
|
|
30
|
+
# │ └──────────────────────────────────────────────────────┘ │
|
|
31
|
+
# │ │ │
|
|
32
|
+
# │ ▼ │
|
|
33
|
+
# │ ┌──────────────────────────────────────────────────────┐ │
|
|
34
|
+
# │ │ Synthesizer │ │
|
|
35
|
+
# │ │ memory.get(:sentiment, :entities, :keywords, │ │
|
|
36
|
+
# │ │ wait: true) │ │
|
|
37
|
+
# │ └──────────────────────────────────────────────────────┘ │
|
|
38
|
+
# │ │
|
|
39
|
+
# └─────────────────────────────────────────────────────────────┘
|
|
40
|
+
#
|
|
41
|
+
# Usage:
|
|
42
|
+
# ANTHROPIC_API_KEY=your_key ruby examples/07_network_memory.rb
|
|
43
|
+
|
|
44
|
+
require_relative "../lib/robot_lab"
|
|
45
|
+
require "json"
|
|
46
|
+
|
|
47
|
+
# Configure RobotLab
|
|
48
|
+
RobotLab.configure do |config|
|
|
49
|
+
config.anthropic_api_key = ENV.fetch("ANTHROPIC_API_KEY", nil)
|
|
50
|
+
config.template_path = File.join(__dir__, "prompts")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
puts "=" * 60
|
|
54
|
+
puts "Example 7: Network Memory with Concurrent Robots"
|
|
55
|
+
puts "=" * 60
|
|
56
|
+
puts
|
|
57
|
+
|
|
58
|
+
# -----------------------------------------------------------------------------
|
|
59
|
+
# Custom Robot Classes that Write to Shared Memory
|
|
60
|
+
# -----------------------------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
# Base class for analysis robots that write results to memory
|
|
63
|
+
class AnalysisRobot < RobotLab::Robot
|
|
64
|
+
def initialize(memory_key:, **opts)
|
|
65
|
+
super(**opts)
|
|
66
|
+
@memory_key = memory_key
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def call(result)
|
|
70
|
+
run_context = extract_run_context(result)
|
|
71
|
+
network_memory = run_context.delete(:network_memory)
|
|
72
|
+
|
|
73
|
+
robot_result = run(network_memory: network_memory, **run_context)
|
|
74
|
+
|
|
75
|
+
# Parse the JSON response and write to shared memory
|
|
76
|
+
if network_memory
|
|
77
|
+
content = robot_result.last_text_content.to_s
|
|
78
|
+
|
|
79
|
+
# Set writer before writing to memory
|
|
80
|
+
network_memory.current_writer = @name
|
|
81
|
+
|
|
82
|
+
begin
|
|
83
|
+
parsed = JSON.parse(content)
|
|
84
|
+
network_memory.set(@memory_key, parsed)
|
|
85
|
+
puts " [#{@name}] Wrote JSON to memory[:#{@memory_key}]"
|
|
86
|
+
rescue JSON::ParserError
|
|
87
|
+
# If not valid JSON, store the raw text
|
|
88
|
+
network_memory.set(@memory_key, content)
|
|
89
|
+
puts " [#{@name}] Wrote text to memory[:#{@memory_key}]"
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
result
|
|
94
|
+
.with_context(@name.to_sym, robot_result)
|
|
95
|
+
.continue(robot_result)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Synthesizer robot that reads from shared memory and synthesizes results
|
|
100
|
+
class SynthesizerRobot < RobotLab::Robot
|
|
101
|
+
def call(result)
|
|
102
|
+
run_context = extract_run_context(result)
|
|
103
|
+
network_memory = run_context.delete(:network_memory)
|
|
104
|
+
|
|
105
|
+
puts " [#{@name}] Reading analysis results from memory..."
|
|
106
|
+
|
|
107
|
+
if network_memory
|
|
108
|
+
# Read results from memory - they should already be there since
|
|
109
|
+
# SimpleFlow ensures our dependencies completed first
|
|
110
|
+
sentiment = network_memory.get(:sentiment)
|
|
111
|
+
entities = network_memory.get(:entities)
|
|
112
|
+
keywords = network_memory.get(:keywords)
|
|
113
|
+
|
|
114
|
+
puts " [#{@name}] Got sentiment: #{sentiment.nil? ? 'nil' : 'present'}"
|
|
115
|
+
puts " [#{@name}] Got entities: #{entities.nil? ? 'nil' : 'present'}"
|
|
116
|
+
puts " [#{@name}] Got keywords: #{keywords.nil? ? 'nil' : 'present'}"
|
|
117
|
+
|
|
118
|
+
# Format for the template
|
|
119
|
+
run_context[:sentiment] = format_for_template(sentiment)
|
|
120
|
+
run_context[:entities] = format_for_template(entities)
|
|
121
|
+
run_context[:keywords] = format_for_template(keywords)
|
|
122
|
+
else
|
|
123
|
+
run_context[:sentiment] = "Not available"
|
|
124
|
+
run_context[:entities] = "Not available"
|
|
125
|
+
run_context[:keywords] = "Not available"
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
robot_result = run(network_memory: network_memory, **run_context)
|
|
129
|
+
|
|
130
|
+
result
|
|
131
|
+
.with_context(@name.to_sym, robot_result)
|
|
132
|
+
.continue(robot_result)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
private
|
|
136
|
+
|
|
137
|
+
def format_for_template(value)
|
|
138
|
+
case value
|
|
139
|
+
when Hash, Array
|
|
140
|
+
JSON.pretty_generate(value)
|
|
141
|
+
when nil
|
|
142
|
+
"Not available"
|
|
143
|
+
else
|
|
144
|
+
value.to_s
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# -----------------------------------------------------------------------------
|
|
150
|
+
# Create the Robots
|
|
151
|
+
# -----------------------------------------------------------------------------
|
|
152
|
+
|
|
153
|
+
sentiment_robot = AnalysisRobot.new(
|
|
154
|
+
name: "sentiment_analyzer",
|
|
155
|
+
template: :sentiment_analyzer,
|
|
156
|
+
memory_key: :sentiment,
|
|
157
|
+
model: "claude-sonnet-4"
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
entity_robot = AnalysisRobot.new(
|
|
161
|
+
name: "entity_extractor",
|
|
162
|
+
template: :entity_extractor,
|
|
163
|
+
memory_key: :entities,
|
|
164
|
+
model: "claude-sonnet-4"
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
keyword_robot = AnalysisRobot.new(
|
|
168
|
+
name: "keyword_extractor",
|
|
169
|
+
template: :keyword_extractor,
|
|
170
|
+
memory_key: :keywords,
|
|
171
|
+
model: "claude-sonnet-4"
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
synthesizer = SynthesizerRobot.new(
|
|
175
|
+
name: "synthesizer",
|
|
176
|
+
template: :synthesizer,
|
|
177
|
+
model: "claude-sonnet-4"
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# -----------------------------------------------------------------------------
|
|
181
|
+
# Create the Network with Shared Memory
|
|
182
|
+
# -----------------------------------------------------------------------------
|
|
183
|
+
|
|
184
|
+
network = RobotLab.create_network(name: "parallel_analysis") do
|
|
185
|
+
# Three analysis robots run in parallel (all depend on nothing)
|
|
186
|
+
task :sentiment, sentiment_robot, depends_on: :none
|
|
187
|
+
task :entities, entity_robot, depends_on: :none
|
|
188
|
+
task :keywords, keyword_robot, depends_on: :none
|
|
189
|
+
|
|
190
|
+
# Synthesizer waits for all three to complete
|
|
191
|
+
task :synthesize, synthesizer, depends_on: [:sentiment, :entities, :keywords]
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# -----------------------------------------------------------------------------
|
|
195
|
+
# Set Up Memory Subscriptions (for monitoring)
|
|
196
|
+
# -----------------------------------------------------------------------------
|
|
197
|
+
|
|
198
|
+
puts "Setting up memory subscriptions for monitoring..."
|
|
199
|
+
puts
|
|
200
|
+
|
|
201
|
+
# Note: In concurrent execution, change.writer may be unreliable due to race
|
|
202
|
+
# conditions. For production use, consider including writer info in the value.
|
|
203
|
+
network.memory.subscribe(:sentiment, :entities, :keywords) do |change|
|
|
204
|
+
value_preview = case change.value
|
|
205
|
+
when Hash then change.value.keys.join(", ")
|
|
206
|
+
when String then change.value[0..50]
|
|
207
|
+
else change.value.class.name
|
|
208
|
+
end
|
|
209
|
+
puts " [MONITOR] Memory[:#{change.key}] updated with keys: #{value_preview}"
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# -----------------------------------------------------------------------------
|
|
213
|
+
# Set Up Broadcast Handler
|
|
214
|
+
# -----------------------------------------------------------------------------
|
|
215
|
+
|
|
216
|
+
network.on_broadcast do |message|
|
|
217
|
+
puts " [BROADCAST] #{message[:payload][:event]}: #{message[:payload][:details]}"
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# -----------------------------------------------------------------------------
|
|
221
|
+
# Run the Network
|
|
222
|
+
# -----------------------------------------------------------------------------
|
|
223
|
+
|
|
224
|
+
sample_text = <<~TEXT
|
|
225
|
+
Apple Inc. announced today that CEO Tim Cook will be presenting the new iPhone 15
|
|
226
|
+
at their headquarters in Cupertino, California on September 12th, 2024.
|
|
227
|
+
Industry analysts are extremely excited about the new features, though some
|
|
228
|
+
consumer advocates have expressed concerns about the expected price increase.
|
|
229
|
+
Samsung and Google are reportedly preparing competitive responses for later this year.
|
|
230
|
+
TEXT
|
|
231
|
+
|
|
232
|
+
puts "Network structure:"
|
|
233
|
+
puts network.visualize
|
|
234
|
+
puts
|
|
235
|
+
puts "-" * 60
|
|
236
|
+
puts "Input text:"
|
|
237
|
+
puts sample_text.strip
|
|
238
|
+
puts "-" * 60
|
|
239
|
+
puts
|
|
240
|
+
|
|
241
|
+
# Send a broadcast before starting
|
|
242
|
+
network.broadcast(event: "analysis_started", details: "Beginning parallel analysis")
|
|
243
|
+
|
|
244
|
+
puts "Running parallel analysis..."
|
|
245
|
+
puts
|
|
246
|
+
start_time = Time.now
|
|
247
|
+
|
|
248
|
+
result = network.run(message: sample_text)
|
|
249
|
+
|
|
250
|
+
elapsed = Time.now - start_time
|
|
251
|
+
puts
|
|
252
|
+
puts "-" * 60
|
|
253
|
+
puts "Analysis complete in #{elapsed.round(2)} seconds"
|
|
254
|
+
puts "-" * 60
|
|
255
|
+
|
|
256
|
+
# Send completion broadcast
|
|
257
|
+
network.broadcast(event: "analysis_complete", details: "All robots finished")
|
|
258
|
+
|
|
259
|
+
# -----------------------------------------------------------------------------
|
|
260
|
+
# Display Results
|
|
261
|
+
# -----------------------------------------------------------------------------
|
|
262
|
+
|
|
263
|
+
puts
|
|
264
|
+
puts "=" * 60
|
|
265
|
+
puts "FINAL SYNTHESIS"
|
|
266
|
+
puts "=" * 60
|
|
267
|
+
puts
|
|
268
|
+
|
|
269
|
+
if result.value.is_a?(RobotLab::RobotResult)
|
|
270
|
+
puts result.value.last_text_content
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
puts
|
|
274
|
+
puts "=" * 60
|
|
275
|
+
puts "MEMORY STATE"
|
|
276
|
+
puts "=" * 60
|
|
277
|
+
puts
|
|
278
|
+
|
|
279
|
+
# Show what's in shared memory
|
|
280
|
+
memory = network.memory
|
|
281
|
+
puts "Sentiment: #{memory.get(:sentiment)&.to_json}"
|
|
282
|
+
puts
|
|
283
|
+
puts "Entities: #{memory.get(:entities)&.to_json}"
|
|
284
|
+
puts
|
|
285
|
+
puts "Keywords: #{memory.get(:keywords)&.to_json}"
|
|
286
|
+
|
|
287
|
+
# -----------------------------------------------------------------------------
|
|
288
|
+
# Demonstrate Blocking Wait (without pipeline dependencies)
|
|
289
|
+
# -----------------------------------------------------------------------------
|
|
290
|
+
|
|
291
|
+
puts
|
|
292
|
+
puts "=" * 60
|
|
293
|
+
puts "DEMONSTRATING BLOCKING WAIT"
|
|
294
|
+
puts "=" * 60
|
|
295
|
+
puts
|
|
296
|
+
|
|
297
|
+
# Create a fresh memory for this demo
|
|
298
|
+
demo_memory = RobotLab::Memory.new(network_name: "wait_demo")
|
|
299
|
+
|
|
300
|
+
# Simulate a concurrent scenario where one thread waits for another
|
|
301
|
+
puts "Starting writer thread (will write after 1 second)..."
|
|
302
|
+
puts "Starting reader thread (will wait for value)..."
|
|
303
|
+
|
|
304
|
+
reader_result = nil
|
|
305
|
+
writer_done = false
|
|
306
|
+
|
|
307
|
+
reader = Thread.new do
|
|
308
|
+
start = Time.now
|
|
309
|
+
puts " [reader] Waiting for :delayed_result..."
|
|
310
|
+
value = demo_memory.get(:delayed_result, wait: 10)
|
|
311
|
+
elapsed = Time.now - start
|
|
312
|
+
reader_result = value
|
|
313
|
+
puts " [reader] Got value after #{elapsed.round(2)}s: #{value}"
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
writer = Thread.new do
|
|
317
|
+
sleep 1
|
|
318
|
+
demo_memory.set(:delayed_result, { status: "complete", data: [1, 2, 3] })
|
|
319
|
+
writer_done = true
|
|
320
|
+
puts " [writer] Wrote :delayed_result to memory"
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# Wait for both threads
|
|
324
|
+
writer.join
|
|
325
|
+
reader.join
|
|
326
|
+
|
|
327
|
+
puts
|
|
328
|
+
puts "Blocking wait demonstration complete!"
|
|
329
|
+
puts "Reader received: #{reader_result.inspect}"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= message %>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= message %>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= message %>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
You are an entity extraction expert. Your job is to identify named entities in text.
|
|
2
|
+
|
|
3
|
+
Analyze the given text and respond with a JSON object containing:
|
|
4
|
+
- people: array of person names mentioned
|
|
5
|
+
- organizations: array of company/organization names
|
|
6
|
+
- locations: array of place names
|
|
7
|
+
- products: array of product/service names
|
|
8
|
+
- dates: array of date/time references
|
|
9
|
+
- other: array of other notable entities
|
|
10
|
+
|
|
11
|
+
Respond ONLY with the JSON object, no additional text.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
You are a senior customer experience specialist for <%= company_name %>, handling escalated and complex cases.
|
|
2
|
+
|
|
3
|
+
## Your Role
|
|
4
|
+
You handle situations that require:
|
|
5
|
+
- Special authorization or exceptions
|
|
6
|
+
- Complex multi-issue resolutions
|
|
7
|
+
- Sensitive customer situations
|
|
8
|
+
- Cases requiring human judgment
|
|
9
|
+
|
|
10
|
+
## Escalation Authorities
|
|
11
|
+
<% authorities.each do |auth| %>
|
|
12
|
+
- **<%= auth[:name] %>**: <%= auth[:description] %> (Limit: <%= auth[:limit] %>)
|
|
13
|
+
<% end %>
|
|
14
|
+
|
|
15
|
+
## Customer Information
|
|
16
|
+
- Name: <%= customer[:name] %>
|
|
17
|
+
- Account Type: <%= customer[:account_type] %>
|
|
18
|
+
- Customer Since: <%= customer[:member_since] %>
|
|
19
|
+
- Lifetime Value: $<%= customer[:lifetime_value] %>
|
|
20
|
+
<% if customer[:vip] %>
|
|
21
|
+
- VIP Status: Yes
|
|
22
|
+
<% end %>
|
|
23
|
+
<% if customer[:escalation_history]&.any? %>
|
|
24
|
+
- Previous Escalations: <%= customer[:escalation_history].count %>
|
|
25
|
+
<% end %>
|
|
26
|
+
|
|
27
|
+
## Guidelines
|
|
28
|
+
1. Acknowledge the customer's frustration immediately
|
|
29
|
+
2. Take ownership of the issue
|
|
30
|
+
3. Explain what you CAN do, not what you can't
|
|
31
|
+
4. Use your authorities to resolve issues when justified
|
|
32
|
+
5. If truly beyond scope, prepare a detailed handoff for human agents
|
|
33
|
+
6. Document all actions and decisions
|
|
34
|
+
|
|
35
|
+
Your goal is to turn a frustrated customer into a loyal advocate.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Customer <%= customer[:name] %> (escalated case): <%= message %>
|
|
2
|
+
|
|
3
|
+
## Case Context
|
|
4
|
+
<% if defined?(escalation_context) && escalation_context %>
|
|
5
|
+
<% if escalation_context[:previous_interactions]&.any? %>
|
|
6
|
+
### Previous Interactions
|
|
7
|
+
<% escalation_context[:previous_interactions].each do |interaction| %>
|
|
8
|
+
- <%= interaction[:date] %> (<%= interaction[:channel] %>): <%= interaction[:summary] %>
|
|
9
|
+
<% end %>
|
|
10
|
+
<% end %>
|
|
11
|
+
|
|
12
|
+
<% if escalation_context[:related_orders]&.any? %>
|
|
13
|
+
### Related Orders
|
|
14
|
+
<% escalation_context[:related_orders].each do |order| %>
|
|
15
|
+
- Order #<%= order[:id] %>: $<%= order[:total] %> - <%= order[:status] %>
|
|
16
|
+
<% end %>
|
|
17
|
+
<% end %>
|
|
18
|
+
|
|
19
|
+
<% if escalation_context[:compensation_history]&.any? %>
|
|
20
|
+
### Previous Compensation
|
|
21
|
+
<% escalation_context[:compensation_history].each do |comp| %>
|
|
22
|
+
- <%= comp[:date] %>: <%= comp[:type] %> - $<%= comp[:amount] %> (<%= comp[:reason] %>)
|
|
23
|
+
<% end %>
|
|
24
|
+
<% end %>
|
|
25
|
+
|
|
26
|
+
### Escalation Reason
|
|
27
|
+
<%= escalation_context[:escalation_reason] || "Not specified" %>
|
|
28
|
+
|
|
29
|
+
### Sentiment Analysis
|
|
30
|
+
- Detected Sentiment: <%= escalation_context[:sentiment] || "Unknown" %>
|
|
31
|
+
- Urgency Level: <%= escalation_context[:urgency] || "Normal" %>
|
|
32
|
+
<% end %>
|
|
33
|
+
|
|
34
|
+
Please resolve this escalated case with empathy and authority.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= message %>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
You are a helpful GitHub assistant with access to GitHub tools via MCP.
|
|
2
|
+
|
|
3
|
+
You can search repositories, view issues, read file contents, and more.
|
|
4
|
+
Use the available tools to help answer questions about GitHub repositories.
|
|
5
|
+
|
|
6
|
+
Be concise and provide relevant information from the tools when needed.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= message %>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
You are a helpful assistant. Be concise and friendly in your responses.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= message %>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
You are a keyword extraction expert. Your job is to identify the most important keywords and topics in text.
|
|
2
|
+
|
|
3
|
+
Analyze the given text and respond with a JSON object containing:
|
|
4
|
+
- keywords: array of the top 5-10 most important keywords/phrases
|
|
5
|
+
- topics: array of 2-3 main topics or themes
|
|
6
|
+
- category: the primary category (e.g., "technology", "business", "health", "entertainment")
|
|
7
|
+
|
|
8
|
+
Respond ONLY with the JSON object, no additional text.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
You are a friendly and efficient order support specialist for <%= company_name %>.
|
|
2
|
+
|
|
3
|
+
## Your Capabilities
|
|
4
|
+
<% capabilities.each do |capability| %>
|
|
5
|
+
- <%= capability %>
|
|
6
|
+
<% end %>
|
|
7
|
+
|
|
8
|
+
## Policies
|
|
9
|
+
- Refunds: <%= policies[:refund_window] %> day window for returns
|
|
10
|
+
- Shipping: Free shipping on orders over $<%= policies[:free_shipping_threshold] %>
|
|
11
|
+
- Express Processing: Available for <%= policies[:express_fee] %> extra
|
|
12
|
+
|
|
13
|
+
## Customer Information
|
|
14
|
+
- Name: <%= customer[:name] %>
|
|
15
|
+
- Account Type: <%= customer[:account_type] %>
|
|
16
|
+
<% if customer[:vip] %>
|
|
17
|
+
- VIP Customer: Apply priority handling and extended policies
|
|
18
|
+
<% end %>
|
|
19
|
+
|
|
20
|
+
## Guidelines
|
|
21
|
+
1. Always greet the customer by name
|
|
22
|
+
2. Reference specific order numbers when discussing issues
|
|
23
|
+
3. Offer solutions, not just explanations
|
|
24
|
+
4. If a refund is appropriate, process it proactively
|
|
25
|
+
5. For VIP customers, be extra accommodating
|
|
26
|
+
|
|
27
|
+
Be helpful, empathetic, and solution-oriented.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Customer <%= customer[:name] %> asks: <%= message %>
|
|
2
|
+
|
|
3
|
+
## Order History
|
|
4
|
+
<% if orders&.any? %>
|
|
5
|
+
<% orders.each do |order| %>
|
|
6
|
+
### Order #<%= order[:id] %>
|
|
7
|
+
- Date: <%= order[:date] %>
|
|
8
|
+
- Status: <%= order[:status] %>
|
|
9
|
+
- Total: $<%= order[:total] %>
|
|
10
|
+
- Items:
|
|
11
|
+
<% order[:items].each do |item| %>
|
|
12
|
+
- <%= item[:name] %> (x<%= item[:quantity] %>) - $<%= item[:price] %>
|
|
13
|
+
<% end %>
|
|
14
|
+
<% if order[:tracking] %>
|
|
15
|
+
- Tracking: <%= order[:tracking] %>
|
|
16
|
+
<% end %>
|
|
17
|
+
<% end %>
|
|
18
|
+
<% else %>
|
|
19
|
+
No orders found for this customer.
|
|
20
|
+
<% end %>
|
|
21
|
+
|
|
22
|
+
Please help the customer with their order inquiry.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
You are a knowledgeable product specialist for <%= company_name %>.
|
|
2
|
+
|
|
3
|
+
## Your Expertise
|
|
4
|
+
You have deep knowledge of our product catalog and can help customers with:
|
|
5
|
+
- Product features and specifications
|
|
6
|
+
- Compatibility questions
|
|
7
|
+
- Usage recommendations
|
|
8
|
+
- Product comparisons
|
|
9
|
+
- Availability and restocking information
|
|
10
|
+
|
|
11
|
+
## Product Categories We Carry
|
|
12
|
+
<% product_categories.each do |category| %>
|
|
13
|
+
- **<%= category[:name] %>**: <%= category[:description] %>
|
|
14
|
+
<% end %>
|
|
15
|
+
|
|
16
|
+
## Customer Context
|
|
17
|
+
- Name: <%= customer[:name] %>
|
|
18
|
+
- Account Type: <%= customer[:account_type] %>
|
|
19
|
+
<% if customer[:purchase_history]&.any? %>
|
|
20
|
+
- Previous Purchases: <%= customer[:purchase_history].map { |p| p[:category] }.uniq.join(", ") %>
|
|
21
|
+
<% end %>
|
|
22
|
+
|
|
23
|
+
## Guidelines
|
|
24
|
+
1. Provide accurate, detailed product information
|
|
25
|
+
2. Make personalized recommendations based on their history
|
|
26
|
+
3. If a product is out of stock, suggest alternatives
|
|
27
|
+
4. Highlight any current promotions or bundles
|
|
28
|
+
5. Be enthusiastic but honest about product capabilities
|
|
29
|
+
|
|
30
|
+
Help the customer find the perfect product for their needs.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
Customer <%= customer[:name] %> asks: <%= message %>
|
|
2
|
+
|
|
3
|
+
## Relevant Products
|
|
4
|
+
<% if products&.any? %>
|
|
5
|
+
<% products.each do |product| %>
|
|
6
|
+
### <%= product[:name] %>
|
|
7
|
+
- SKU: <%= product[:sku] %>
|
|
8
|
+
- Price: $<%= product[:price] %>
|
|
9
|
+
- Category: <%= product[:category] %>
|
|
10
|
+
- In Stock: <%= product[:in_stock] ? "Yes (#{product[:quantity]} available)" : "No" %>
|
|
11
|
+
<% if product[:features]&.any? %>
|
|
12
|
+
- Features:
|
|
13
|
+
<% product[:features].each do |feature| %>
|
|
14
|
+
- <%= feature %>
|
|
15
|
+
<% end %>
|
|
16
|
+
<% end %>
|
|
17
|
+
<% if product[:compatible_with]&.any? %>
|
|
18
|
+
- Compatible With: <%= product[:compatible_with].join(", ") %>
|
|
19
|
+
<% end %>
|
|
20
|
+
<% end %>
|
|
21
|
+
<% else %>
|
|
22
|
+
No specific products matched the query. Provide general guidance.
|
|
23
|
+
<% end %>
|
|
24
|
+
|
|
25
|
+
<% if promotions&.any? %>
|
|
26
|
+
## Current Promotions
|
|
27
|
+
<% promotions.each do |promo| %>
|
|
28
|
+
- **<%= promo[:name] %>**: <%= promo[:description] %> (Code: <%= promo[:code] %>)
|
|
29
|
+
<% end %>
|
|
30
|
+
<% end %>
|
|
31
|
+
|
|
32
|
+
Please help the customer with their product question.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
You are a sentiment analysis expert. Your job is to analyze the emotional tone of text.
|
|
2
|
+
|
|
3
|
+
Analyze the given text and respond with a JSON object containing:
|
|
4
|
+
- score: a number from -1.0 (very negative) to 1.0 (very positive)
|
|
5
|
+
- confidence: a number from 0.0 to 1.0 indicating your confidence
|
|
6
|
+
- primary_emotion: the dominant emotion (e.g., "joy", "anger", "sadness", "fear", "surprise", "neutral")
|
|
7
|
+
- summary: a brief one-sentence explanation
|
|
8
|
+
|
|
9
|
+
Respond ONLY with the JSON object, no additional text.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
You are a content synthesis expert. Your job is to combine analysis results from multiple sources into a coherent summary.
|
|
2
|
+
|
|
3
|
+
You will receive:
|
|
4
|
+
- Sentiment analysis results
|
|
5
|
+
- Entity extraction results
|
|
6
|
+
- Keyword extraction results
|
|
7
|
+
|
|
8
|
+
Create a comprehensive summary that:
|
|
9
|
+
1. Describes the overall tone and emotional context
|
|
10
|
+
2. Highlights key entities and their relationships
|
|
11
|
+
3. Identifies the main themes and topics
|
|
12
|
+
4. Provides actionable insights or recommendations
|
|
13
|
+
|
|
14
|
+
Be concise but thorough.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Please synthesize these analysis results into a comprehensive summary:
|
|
2
|
+
|
|
3
|
+
## Original Text
|
|
4
|
+
<%= message %>
|
|
5
|
+
|
|
6
|
+
## Sentiment Analysis
|
|
7
|
+
<%= sentiment %>
|
|
8
|
+
|
|
9
|
+
## Entities Found
|
|
10
|
+
<%= entities %>
|
|
11
|
+
|
|
12
|
+
## Keywords & Topics
|
|
13
|
+
<%= keywords %>
|
|
14
|
+
|
|
15
|
+
Provide a unified analysis combining all these perspectives.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= message %>
|