robot_lab 0.3.0 → 0.3.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.
Files changed (256) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/README.md +1 -1
  4. data/lib/robot_lab/mcp/transports/streamable_http.rb +1 -1
  5. data/lib/robot_lab/version.rb +1 -1
  6. metadata +3 -253
  7. data/.architecture/AGENTS.md +0 -32
  8. data/.architecture/config.yml +0 -8
  9. data/.architecture/members.yml +0 -60
  10. data/.architecture/reviews/feature-free-will.md +0 -490
  11. data/.architecture/reviews/overall-codebase.md +0 -427
  12. data/.claude/settings.local.json +0 -57
  13. data/.codex/config.toml +0 -2
  14. data/.envrc +0 -6
  15. data/.github/workflows/deploy-github-pages.yml +0 -52
  16. data/.irbrc +0 -6
  17. data/.loki +0 -65
  18. data/.quality/reek_baseline.txt +0 -43
  19. data/.rubocop.yml +0 -10
  20. data/Archspec.rb +0 -44
  21. data/CLAUDE.md +0 -141
  22. data/COMMITS.md +0 -196
  23. data/Rakefile +0 -193
  24. data/_typos.toml +0 -21
  25. data/agent2agent_review.md +0 -192
  26. data/agentf_improvements.md +0 -253
  27. data/agents.md +0 -14
  28. data/docs/api/core/index.md +0 -147
  29. data/docs/api/core/memory.md +0 -578
  30. data/docs/api/core/network.md +0 -553
  31. data/docs/api/core/result.md +0 -211
  32. data/docs/api/core/robot.md +0 -1667
  33. data/docs/api/core/state.md +0 -145
  34. data/docs/api/core/tool.md +0 -520
  35. data/docs/api/errors.md +0 -323
  36. data/docs/api/hooks.md +0 -469
  37. data/docs/api/index.md +0 -137
  38. data/docs/api/mcp/client.md +0 -387
  39. data/docs/api/mcp/index.md +0 -267
  40. data/docs/api/mcp/server.md +0 -252
  41. data/docs/api/mcp/transports.md +0 -339
  42. data/docs/api/messages/index.md +0 -126
  43. data/docs/api/messages/text-message.md +0 -170
  44. data/docs/api/messages/tool-call-message.md +0 -206
  45. data/docs/api/messages/tool-result-message.md +0 -242
  46. data/docs/api/messages/user-message.md +0 -232
  47. data/docs/api/skills.md +0 -423
  48. data/docs/api/streaming/context.md +0 -280
  49. data/docs/api/streaming/events.md +0 -185
  50. data/docs/api/streaming/index.md +0 -156
  51. data/docs/api/support.md +0 -846
  52. data/docs/architecture/core-concepts.md +0 -575
  53. data/docs/architecture/index.md +0 -252
  54. data/docs/architecture/message-flow.md +0 -366
  55. data/docs/architecture/network-orchestration.md +0 -478
  56. data/docs/architecture/robot-execution.md +0 -396
  57. data/docs/architecture/state-management.md +0 -410
  58. data/docs/assets/css/custom.css +0 -56
  59. data/docs/assets/images/robot_lab.jpg +0 -0
  60. data/docs/concepts.md +0 -641
  61. data/docs/examples/basic-chat.md +0 -258
  62. data/docs/examples/index.md +0 -425
  63. data/docs/examples/mcp-server.md +0 -403
  64. data/docs/examples/multi-robot-network.md +0 -476
  65. data/docs/examples/tool-usage.md +0 -468
  66. data/docs/getting-started/configuration.md +0 -645
  67. data/docs/getting-started/index.md +0 -56
  68. data/docs/getting-started/installation.md +0 -197
  69. data/docs/getting-started/quick-start.md +0 -293
  70. data/docs/guides/building-robots.md +0 -1187
  71. data/docs/guides/creating-networks.md +0 -736
  72. data/docs/guides/hooks.md +0 -1206
  73. data/docs/guides/index.md +0 -78
  74. data/docs/guides/knowledge.md +0 -219
  75. data/docs/guides/mcp-integration.md +0 -740
  76. data/docs/guides/memory.md +0 -458
  77. data/docs/guides/observability.md +0 -733
  78. data/docs/guides/streaming.md +0 -323
  79. data/docs/guides/using-tools.md +0 -780
  80. data/docs/index.md +0 -280
  81. data/docs/superpowers/plans/2026-05-06-agentskills.md +0 -1303
  82. data/docs/superpowers/specs/2026-05-06-agentskills-design.md +0 -247
  83. data/examples/.envrc +0 -3
  84. data/examples/01_simple_robot.rb +0 -31
  85. data/examples/02_tools.rb +0 -109
  86. data/examples/03_network.rb +0 -118
  87. data/examples/04_mcp.rb +0 -217
  88. data/examples/05_streaming.rb +0 -137
  89. data/examples/06_prompt_templates.rb +0 -325
  90. data/examples/07_network_memory.rb +0 -313
  91. data/examples/08_llm_config.rb +0 -162
  92. data/examples/09_chaining.rb +0 -251
  93. data/examples/10_memory.rb +0 -315
  94. data/examples/11_network_introspection.rb +0 -239
  95. data/examples/12_message_bus.rb +0 -67
  96. data/examples/13_spawn.rb +0 -85
  97. data/examples/14_rusty_circuit/.envrc +0 -1
  98. data/examples/14_rusty_circuit/.gitignore +0 -1
  99. data/examples/14_rusty_circuit/README.md +0 -1
  100. data/examples/14_rusty_circuit/comic.rb +0 -149
  101. data/examples/14_rusty_circuit/display.rb +0 -217
  102. data/examples/14_rusty_circuit/heckler.rb +0 -65
  103. data/examples/14_rusty_circuit/open_mic.rb +0 -132
  104. data/examples/14_rusty_circuit/prompts/open_mic_comic.md +0 -20
  105. data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +0 -23
  106. data/examples/14_rusty_circuit/prompts/open_mic_scout.md +0 -20
  107. data/examples/14_rusty_circuit/scout.rb +0 -165
  108. data/examples/14_rusty_circuit/show.log +0 -234
  109. data/examples/15_memory_network_and_bus/.envrc +0 -1
  110. data/examples/15_memory_network_and_bus/README.md +0 -66
  111. data/examples/15_memory_network_and_bus/editor_in_chief.rb +0 -24
  112. data/examples/15_memory_network_and_bus/editorial_pipeline.rb +0 -213
  113. data/examples/15_memory_network_and_bus/linux_writer.rb +0 -80
  114. data/examples/15_memory_network_and_bus/os_editor.rb +0 -48
  115. data/examples/15_memory_network_and_bus/os_writer.rb +0 -49
  116. data/examples/15_memory_network_and_bus/output/combined_article.md +0 -11
  117. data/examples/15_memory_network_and_bus/output/final_article.md +0 -8
  118. data/examples/15_memory_network_and_bus/output/linux_draft.md +0 -7
  119. data/examples/15_memory_network_and_bus/output/mac_draft.md +0 -7
  120. data/examples/15_memory_network_and_bus/output/memory.json +0 -13
  121. data/examples/15_memory_network_and_bus/output/revision_1.md +0 -29
  122. data/examples/15_memory_network_and_bus/output/revision_2.md +0 -50
  123. data/examples/15_memory_network_and_bus/output/revision_3.md +0 -8
  124. data/examples/15_memory_network_and_bus/output/windows_draft.md +0 -7
  125. data/examples/15_memory_network_and_bus/prompts/os_advocate.md +0 -13
  126. data/examples/15_memory_network_and_bus/prompts/os_chief.md +0 -13
  127. data/examples/15_memory_network_and_bus/prompts/os_editor.md +0 -13
  128. data/examples/16_writers_room/.envrc +0 -1
  129. data/examples/16_writers_room/display.rb +0 -158
  130. data/examples/16_writers_room/output/.gitignore +0 -4
  131. data/examples/16_writers_room/output/README.md +0 -69
  132. data/examples/16_writers_room/output/opus_001.md +0 -263
  133. data/examples/16_writers_room/output/opus_001_notes.log +0 -470
  134. data/examples/16_writers_room/output/opus_002.md +0 -245
  135. data/examples/16_writers_room/output/opus_002_notes.log +0 -546
  136. data/examples/16_writers_room/output/opus_002_screenplay.md +0 -7989
  137. data/examples/16_writers_room/output/opus_002_screenplay_notes.md +0 -993
  138. data/examples/16_writers_room/prompts/screenplay_writer.md +0 -66
  139. data/examples/16_writers_room/prompts/writer.md +0 -37
  140. data/examples/16_writers_room/room.rb +0 -186
  141. data/examples/16_writers_room/tools.rb +0 -173
  142. data/examples/16_writers_room/writer.rb +0 -121
  143. data/examples/16_writers_room/writers_room.rb +0 -256
  144. data/examples/17_skills.rb +0 -234
  145. data/examples/18_rails/.envrc +0 -3
  146. data/examples/18_rails/.gitignore +0 -5
  147. data/examples/18_rails/Gemfile +0 -11
  148. data/examples/18_rails/README.md +0 -67
  149. data/examples/18_rails/Rakefile +0 -4
  150. data/examples/18_rails/app/controllers/application_controller.rb +0 -4
  151. data/examples/18_rails/app/controllers/chat_controller.rb +0 -50
  152. data/examples/18_rails/app/jobs/application_job.rb +0 -4
  153. data/examples/18_rails/app/jobs/robot_run_job.rb +0 -25
  154. data/examples/18_rails/app/models/application_record.rb +0 -5
  155. data/examples/18_rails/app/models/robot_lab_result.rb +0 -36
  156. data/examples/18_rails/app/models/robot_lab_thread.rb +0 -23
  157. data/examples/18_rails/app/robots/chat_robot.rb +0 -25
  158. data/examples/18_rails/app/tools/time_tool.rb +0 -9
  159. data/examples/18_rails/app/views/chat/_user_message.html.erb +0 -1
  160. data/examples/18_rails/app/views/chat/index.html.erb +0 -67
  161. data/examples/18_rails/app/views/layouts/application.html.erb +0 -49
  162. data/examples/18_rails/bin/dev +0 -7
  163. data/examples/18_rails/bin/rails +0 -6
  164. data/examples/18_rails/bin/setup +0 -15
  165. data/examples/18_rails/config/application.rb +0 -33
  166. data/examples/18_rails/config/cable.yml +0 -2
  167. data/examples/18_rails/config/database.yml +0 -5
  168. data/examples/18_rails/config/environment.rb +0 -4
  169. data/examples/18_rails/config/initializers/robot_lab.rb +0 -11
  170. data/examples/18_rails/config/routes.rb +0 -6
  171. data/examples/18_rails/config.ru +0 -4
  172. data/examples/18_rails/db/migrate/001_create_robot_lab_tables.rb +0 -32
  173. data/examples/19_token_tracking.rb +0 -138
  174. data/examples/20_circuit_breaker.rb +0 -147
  175. data/examples/21_learning_loop.rb +0 -181
  176. data/examples/22_context_compression.rb +0 -172
  177. data/examples/23_convergence.rb +0 -133
  178. data/examples/24_structured_delegation.rb +0 -151
  179. data/examples/25_history_search/conversation.jsonl +0 -30
  180. data/examples/25_history_search.rb +0 -133
  181. data/examples/26_document_store/api_versioning_adr.md +0 -52
  182. data/examples/26_document_store/incident_postmortem.md +0 -46
  183. data/examples/26_document_store/postgres_runbook.md +0 -49
  184. data/examples/26_document_store/redis_caching_guide.md +0 -48
  185. data/examples/26_document_store/sidekiq_guide.md +0 -51
  186. data/examples/26_document_store.rb +0 -140
  187. data/examples/27_incident_response/README.md +0 -65
  188. data/examples/27_incident_response/incident_response.rb +0 -261
  189. data/examples/28_mcp_discovery.rb +0 -113
  190. data/examples/29_ractor_tools.rb +0 -240
  191. data/examples/30_ractor_network.rb +0 -252
  192. data/examples/31_launch_assessment.rb +0 -246
  193. data/examples/32_newsletter_reader.rb +0 -193
  194. data/examples/33_stock_generator.rb +0 -80
  195. data/examples/33_stock_predictor.rb +0 -327
  196. data/examples/34_agentskills.rb +0 -76
  197. data/examples/35_hooks.rb +0 -266
  198. data/examples/README.md +0 -550
  199. data/examples/common.rb +0 -180
  200. data/examples/prompts/assistant.md +0 -5
  201. data/examples/prompts/audit_trail.md +0 -8
  202. data/examples/prompts/billing.md +0 -10
  203. data/examples/prompts/classifier.md +0 -7
  204. data/examples/prompts/comedian.md +0 -6
  205. data/examples/prompts/comedy_critic.md +0 -10
  206. data/examples/prompts/configurable.md +0 -9
  207. data/examples/prompts/dispatcher.md +0 -12
  208. data/examples/prompts/entity_extractor.md +0 -14
  209. data/examples/prompts/escalation.md +0 -42
  210. data/examples/prompts/frontmatter_mcp_test.md +0 -9
  211. data/examples/prompts/frontmatter_named_test.md +0 -5
  212. data/examples/prompts/frontmatter_tools_test.md +0 -6
  213. data/examples/prompts/general.md +0 -7
  214. data/examples/prompts/github_assistant.md +0 -14
  215. data/examples/prompts/helper.md +0 -4
  216. data/examples/prompts/incident_responder.md +0 -18
  217. data/examples/prompts/keyword_extractor.md +0 -11
  218. data/examples/prompts/llm_config_demo.md +0 -20
  219. data/examples/prompts/order_support.md +0 -35
  220. data/examples/prompts/os_advocate.md +0 -13
  221. data/examples/prompts/os_chief.md +0 -13
  222. data/examples/prompts/os_editor.md +0 -13
  223. data/examples/prompts/parameterized_main_test.md +0 -6
  224. data/examples/prompts/pii_redactor.md +0 -7
  225. data/examples/prompts/product_support.md +0 -37
  226. data/examples/prompts/runbook_protocol.md +0 -12
  227. data/examples/prompts/sentiment_analyzer.md +0 -12
  228. data/examples/prompts/skill_a_test.md +0 -4
  229. data/examples/prompts/skill_b_test.md +0 -4
  230. data/examples/prompts/skill_config_test.md +0 -5
  231. data/examples/prompts/skill_cycle_a_test.md +0 -6
  232. data/examples/prompts/skill_cycle_b_test.md +0 -6
  233. data/examples/prompts/skill_description_test.md +0 -4
  234. data/examples/prompts/skill_leaf_test.md +0 -4
  235. data/examples/prompts/skill_nested_test.md +0 -6
  236. data/examples/prompts/skill_refs_main_test.md +0 -6
  237. data/examples/prompts/skill_self_ref_test.md +0 -6
  238. data/examples/prompts/skill_with_mcp_test.md +0 -9
  239. data/examples/prompts/skill_with_params_test.md +0 -6
  240. data/examples/prompts/skill_with_robot_name_test.md +0 -5
  241. data/examples/prompts/skill_with_tools_test.md +0 -6
  242. data/examples/prompts/sre_compliance.md +0 -10
  243. data/examples/prompts/structured_output.md +0 -7
  244. data/examples/prompts/synthesizer.md +0 -17
  245. data/examples/prompts/technical.md +0 -10
  246. data/examples/prompts/template_with_skills_test.md +0 -6
  247. data/examples/prompts/triage.md +0 -22
  248. data/examples/ruboruby.md +0 -423
  249. data/examples/run_all.rb +0 -60
  250. data/examples/xyzzy.rb +0 -97
  251. data/logfile +0 -8
  252. data/mkdocs.yml +0 -209
  253. data/robot_concurrency.md +0 -38
  254. data/simple_acp_review.md +0 -298
  255. data/temp.md +0 -6
  256. data/tool_manifest_plan.md +0 -155
@@ -1,80 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Example 33: XYZZY Stock Price Generator
5
- #
6
- # Publishes fake streaming prices for ticker XYZZY to a Redis channel
7
- # using Geometric Brownian Motion with occasional volatility regime shifts.
8
- #
9
- # Prerequisites:
10
- # gem install redis
11
- # Redis server running on localhost:6379
12
- #
13
- # Usage:
14
- # ruby examples/33_stock_generator.rb
15
-
16
- require "redis"
17
- require "json"
18
- require "time"
19
-
20
- CHANNEL = "stock:xyzzy"
21
- START_PRICE = 100.0
22
- BASE_VOL = 0.008 # baseline volatility per tick (~0.8%)
23
- DRIFT = 0.0001 # slight upward drift per tick
24
-
25
- # Box-Muller transform — standard normal sample
26
- def randn
27
- Math.sqrt(-2.0 * Math.log(rand)) * Math.cos(2.0 * Math::PI * rand)
28
- end
29
-
30
- # Occasionally shift the volatility regime to create interesting price dynamics
31
- def current_volatility(tick)
32
- case tick % 60
33
- when 0..10 then BASE_VOL * 2.0 # high volatility burst
34
- when 30..35 then BASE_VOL * 0.4 # low volatility squeeze
35
- else BASE_VOL
36
- end
37
- end
38
-
39
- # ── Main ──────────────────────────────────────────────────────────────────────
40
-
41
- redis = Redis.new
42
- price = START_PRICE
43
- tick = 0
44
-
45
- trap("INT") { puts "\nGenerator stopped."; exit }
46
-
47
- puts "=" * 50
48
- puts "XYZZY Stock Generator"
49
- puts "=" * 50
50
- puts "Channel : #{CHANNEL}"
51
- puts "Interval: 5 seconds per tick"
52
- puts "Model : Geometric Brownian Motion"
53
- puts "Press Ctrl-C to stop."
54
- puts "-" * 50
55
-
56
- loop do
57
- tick += 1
58
-
59
- vol = current_volatility(tick)
60
- price = (price * Math.exp((DRIFT - 0.5 * vol**2) + vol * randn)).round(2)
61
- price = [price, 1.0].max # floor at $1.00
62
-
63
- regime = case vol
64
- when BASE_VOL * 2.0 then " [HIGH VOL]"
65
- when BASE_VOL * 0.4 then " [low vol]"
66
- else ""
67
- end
68
-
69
- payload = JSON.generate(
70
- ticker: "XYZZY",
71
- price: price,
72
- tick: tick,
73
- timestamp: Time.now.iso8601
74
- )
75
-
76
- redis.publish(CHANNEL, payload)
77
- puts "Tick %5d $%8.2f vol=%.3f%s" % [tick, price, vol, regime]
78
-
79
- sleep 5
80
- end
@@ -1,327 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Example 33: XYZZY Stock Price Predictor
5
- #
6
- # Consumes fake streaming prices for ticker XYZZY from a Redis channel,
7
- # predicts the high and low over the next price window using an SMA + EMA
8
- # ensemble, and uses a RobotLab learning robot to tune predictor parameters
9
- # after each window closes.
10
- #
11
- # Run alongside:
12
- # ruby examples/33_stock_generator.rb (in a separate terminal)
13
- #
14
- # Prerequisites:
15
- # gem install redis
16
- # Redis server running on localhost:6379
17
- # LM Studio running with the models from common.rb downloaded
18
- #
19
- # Usage:
20
- # ruby examples/33_stock_predictor.rb
21
-
22
- require_relative "common"
23
- require "robot_lab/durable"
24
- require "redis"
25
- require "json"
26
-
27
- CHANNEL = "stock:xyzzy"
28
- WINDOW_SIZE = 12 # ticks per prediction window
29
-
30
- # ── Mutable predictor parameters ──────────────────────────────────────────────
31
-
32
- module PredictorConfig
33
- @sma_window = 10
34
- @sma_std_multiplier = 1.5
35
- @ema_alpha = 0.2
36
- @ema_vol_multiplier = 2.0
37
- @sma_weight = 0.5
38
-
39
- class << self
40
- attr_accessor :sma_window, :sma_std_multiplier, :ema_alpha,
41
- :ema_vol_multiplier, :sma_weight
42
-
43
- def summary
44
- format(
45
- "sma_window=%d sma_std=%.2f ema_alpha=%.2f ema_vol=%.2f sma_weight=%.2f",
46
- sma_window, sma_std_multiplier, ema_alpha, ema_vol_multiplier, sma_weight
47
- )
48
- end
49
- end
50
- end
51
-
52
- # ── SMA predictor ──────────────────────────────────────────────────────────────
53
-
54
- module SMAPredictor
55
- def self.predict(prices)
56
- window = prices.last(PredictorConfig.sma_window)
57
- mean = window.sum / window.size.to_f
58
- var = window.sum { |p| (p - mean)**2 } / window.size.to_f
59
- std = Math.sqrt(var)
60
- mult = PredictorConfig.sma_std_multiplier
61
-
62
- {
63
- high: (mean + mult * std).round(2),
64
- low: [mean - mult * std, 1.0].max.round(2)
65
- }
66
- end
67
- end
68
-
69
- # ── EMA predictor (stateful — updated every tick) ──────────────────────────────
70
-
71
- module EMAPredictor
72
- @ema = nil
73
- @var_ema = nil
74
-
75
- class << self
76
- def update(price)
77
- alpha = PredictorConfig.ema_alpha
78
- if @ema.nil?
79
- @ema = price
80
- @var_ema = 0.0
81
- else
82
- delta = price - @ema
83
- @ema = alpha * price + (1 - alpha) * @ema
84
- @var_ema = alpha * delta**2 + (1 - alpha) * @var_ema
85
- end
86
- end
87
-
88
- def predict
89
- return nil if @ema.nil?
90
-
91
- vol = Math.sqrt(@var_ema) * PredictorConfig.ema_vol_multiplier
92
- {
93
- high: (@ema + vol).round(2),
94
- low: [@ema - vol, 1.0].max.round(2)
95
- }
96
- end
97
- end
98
- end
99
-
100
- # ── Ensemble predictor ─────────────────────────────────────────────────────────
101
-
102
- module EnsemblePredictor
103
- def self.predict(prices)
104
- sma = SMAPredictor.predict(prices)
105
- ema = EMAPredictor.predict
106
- return sma unless ema
107
-
108
- w = PredictorConfig.sma_weight
109
- {
110
- high: (w * sma[:high] + (1 - w) * ema[:high]).round(2),
111
- low: (w * sma[:low] + (1 - w) * ema[:low]).round(2)
112
- }
113
- end
114
- end
115
-
116
- # ── AdjustParameters tool ──────────────────────────────────────────────────────
117
-
118
- class AdjustParameters < RobotLab::Tool
119
- description "Adjust one predictor parameter to improve future prediction accuracy. " \
120
- "Make at most one or two targeted changes per window."
121
-
122
- parameter :parameter, type: "string",
123
- description: "Parameter to adjust: sma_window, sma_std_multiplier, ema_alpha, ema_vol_multiplier, sma_weight"
124
- parameter :value, type: "number",
125
- description: "New value (sma_window: 3-30 int; std/vol multipliers: 0.5-4.0; ema_alpha: 0.05-0.5; sma_weight: 0.0-1.0)"
126
- parameter :reasoning, type: "string",
127
- description: "Why this change should reduce prediction error"
128
-
129
- LIMITS = {
130
- "sma_window" => { min: 3, max: 30, integer: true },
131
- "sma_std_multiplier" => { min: 0.5, max: 4.0, integer: false },
132
- "ema_alpha" => { min: 0.05, max: 0.5, integer: false },
133
- "ema_vol_multiplier" => { min: 0.5, max: 4.0, integer: false },
134
- "sma_weight" => { min: 0.0, max: 1.0, integer: false }
135
- }.freeze
136
-
137
- def execute(parameter:, value:, reasoning:)
138
- spec = LIMITS[parameter]
139
- return "Unknown parameter '#{parameter}'. Valid: #{LIMITS.keys.join(", ")}" unless spec
140
-
141
- clamped = value.to_f.clamp(spec[:min], spec[:max])
142
- clamped = clamped.round if spec[:integer]
143
-
144
- PredictorConfig.send(:"#{parameter}=", clamped)
145
-
146
- "Set #{parameter} = #{clamped}. #{reasoning}"
147
- end
148
- end
149
-
150
- # ── Error metrics ──────────────────────────────────────────────────────────────
151
-
152
- WindowResult = Data.define(
153
- :window_num,
154
- :predicted_high, :predicted_low,
155
- :actual_high, :actual_low,
156
- :high_err, :low_err, :mean_err
157
- )
158
-
159
- def evaluate_window(window_num, predicted, actuals)
160
- actual_high = actuals.max.round(2)
161
- actual_low = actuals.min.round(2)
162
- high_err = (predicted[:high] - actual_high).abs.round(2)
163
- low_err = (predicted[:low] - actual_low).abs.round(2)
164
- mean_err = ((high_err + low_err) / 2.0).round(2)
165
-
166
- WindowResult.new(
167
- window_num:,
168
- predicted_high: predicted[:high], predicted_low: predicted[:low],
169
- actual_high:, actual_low:,
170
- high_err:, low_err:, mean_err:
171
- )
172
- end
173
-
174
- def tuner_prompt(result)
175
- <<~PROMPT
176
- Window #{result.window_num} just closed.
177
-
178
- Prediction vs Actual:
179
- Predicted: high=$#{result.predicted_high} low=$#{result.predicted_low}
180
- Actual: high=$#{result.actual_high} low=$#{result.actual_low}
181
- Error: high_err=$#{result.high_err} low_err=$#{result.low_err} mean_err=$#{result.mean_err}
182
-
183
- Current parameters:
184
- #{PredictorConfig.summary}
185
-
186
- Window size: #{WINDOW_SIZE} ticks.
187
-
188
- First call RecallKnowledge to check what has worked before.
189
- Then decide whether to adjust a parameter via AdjustParameters.
190
- If the error is acceptable or you are uncertain, do nothing.
191
- If you notice a clear pattern worth preserving, call RecordKnowledge.
192
- PROMPT
193
- end
194
-
195
- # ── Main ──────────────────────────────────────────────────────────────────────
196
-
197
- puts "=" * 60
198
- puts "XYZZY Stock Predictor"
199
- puts "=" * 60
200
- puts "Channel : #{CHANNEL}"
201
- puts "Window : #{WINDOW_SIZE} ticks"
202
- puts "Model : SMA + EMA Ensemble with Durable Learning"
203
- puts "Warmup : #{PredictorConfig.sma_window} ticks"
204
- puts "Press Ctrl-C to stop."
205
- puts "-" * 60
206
-
207
- require_lms!
208
-
209
- redis = Redis.new
210
- prices = []
211
-
212
- robot = RobotLab.build(
213
- **llm_opts,
214
- name: "predictor_tuner",
215
- system_prompt: <<~PROMPT,
216
- You are a quantitative analyst tuning an ensemble stock price range
217
- predictor for ticker XYZZY. Each prediction covers the high and low
218
- price over the next #{WINDOW_SIZE} ticks.
219
-
220
- The ensemble combines a Simple Moving Average (SMA) band and an
221
- Exponential Moving Average (EMA) band. Adjustable parameters:
222
-
223
- sma_window (3-30 int) — lookback period for SMA
224
- sma_std_multiplier (0.5-4.0) — band width relative to SMA stddev
225
- ema_alpha (0.05-0.5) — EMA smoothing (higher = more reactive)
226
- ema_vol_multiplier (0.5-4.0) — band width relative to EMA volatility
227
- sma_weight (0.0-1.0) — SMA share in ensemble (EMA = 1 - weight)
228
-
229
- Workflow per window:
230
- 1. Call RecallKnowledge to check past findings before acting.
231
- 2. If the error is clearly too high/low in one direction, adjust the
232
- relevant band multiplier via AdjustParameters.
233
- 3. Make at most two adjustments per window to isolate cause and effect.
234
- 4. If you observe a reliable pattern, call RecordKnowledge to preserve it.
235
- 5. When uncertain, do nothing rather than guess.
236
- PROMPT
237
- local_tools: [AdjustParameters]
238
- )
239
-
240
- # Durable learning is enabled AFTER construction. Robot#initialize takes a
241
- # fixed keyword list with no **rest, so `learn:`/`learn_domain:` constructor
242
- # params raise ArgumentError; the Durable::Learning mixin exposes
243
- # setup_durable_learning instead. It seeds @learnings from
244
- # ~/.robot_lab/durable/<domain>.yml and appends the RecallKnowledge and
245
- # RecordKnowledge tools to local_tools.
246
- robot.setup_durable_learning(domain: "xyzzy stock prediction")
247
-
248
- puts "Durable domain : xyzzy stock prediction"
249
- puts "Tools : #{robot.local_tools.map(&:name).join(', ')}"
250
- puts "Seeded learnings: #{robot.learnings.size}"
251
- puts "-" * 60
252
-
253
- warmed_up = false
254
- pending_pred = nil # { prediction: {high:, low:}, window_prices: [] }
255
- window_num = 0
256
-
257
- trap("INT") { puts "\nPredictor stopped."; exit }
258
-
259
- puts "Connecting to Redis and subscribing to #{CHANNEL}..."
260
-
261
- redis.subscribe(CHANNEL) do |on|
262
- on.message do |_channel, payload|
263
- data = JSON.parse(payload, symbolize_names: true)
264
- tick = data[:tick]
265
- price = data[:price].to_f
266
-
267
- EMAPredictor.update(price)
268
- prices << price
269
-
270
- # ── Warmup phase ──────────────────────────────────────────────
271
- unless warmed_up
272
- if prices.size < PredictorConfig.sma_window
273
- puts "Tick %5d $%8.2f [warming up %d/%d]" % [tick, price, prices.size, PredictorConfig.sma_window]
274
- next
275
- end
276
-
277
- warmed_up = true
278
- pred = EnsemblePredictor.predict(prices)
279
- pending_pred = { prediction: pred, window_prices: [] }
280
-
281
- puts "Tick %5d $%8.2f [warmup done]" % [tick, price]
282
- puts " First prediction → high=$#{pred[:high]} low=$#{pred[:low]}"
283
- next
284
- end
285
-
286
- # ── Accumulate current window ──────────────────────────────────
287
- pending_pred[:window_prices] << price
288
- progress = pending_pred[:window_prices].size
289
- pred = pending_pred[:prediction]
290
-
291
- puts "Tick %5d $%8.2f [%2d/#{WINDOW_SIZE}] (pred high=$#{pred[:high]} low=$#{pred[:low]})" %
292
- [tick, price, progress]
293
-
294
- next unless progress >= WINDOW_SIZE
295
-
296
- # ── Window closed — evaluate ───────────────────────────────────
297
- window_num += 1
298
- result = evaluate_window(window_num, pred, pending_pred[:window_prices])
299
-
300
- puts "\n#{"─" * 60}"
301
- puts " Window #{result.window_num} result:"
302
- puts " Predicted high=$%-8.2f low=$%-.2f" % [result.predicted_high, result.predicted_low]
303
- puts " Actual high=$%-8.2f low=$%-.2f" % [result.actual_high, result.actual_low]
304
- puts " Error high=%-8.2f low=%-8.2f mean=%.2f" % [result.high_err, result.low_err, result.mean_err]
305
- puts "#{"─" * 60}"
306
-
307
- print " [tuner] analyzing window #{window_num}..."
308
- # tools: :inherit is required — run() defaults to :none, which sends the
309
- # provider an empty tool list, so AdjustParameters/RecallKnowledge/
310
- # RecordKnowledge would never be callable.
311
- tuner_response = robot.run(tuner_prompt(result), tools: :inherit)
312
- tuner_line = tuner_response.reply.lines.first&.chomp || "(no response)"
313
- puts "\r [tuner] #{tuner_line}#{" " * 20}"
314
- puts " Params: #{PredictorConfig.summary}"
315
-
316
- # Promote this session's learnings into the durable YAML store. Core
317
- # Robot#run does not invoke the reflector, so the caller drives it.
318
- robot.run_reflector
319
- puts
320
-
321
- # ── Start next window ──────────────────────────────────────────
322
- new_pred = EnsemblePredictor.predict(prices)
323
- pending_pred = { prediction: new_pred, window_prices: [] }
324
- puts " Next prediction → high=$#{new_pred[:high]} low=$#{new_pred[:low]}"
325
- puts
326
- end
327
- end
@@ -1,76 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Example 34: AgentSkills.io Integration
5
- #
6
- # Demonstrates the unified skills: param detecting AgentSkills folder format.
7
- # Skills in ~/.prompts/skills/ are matched at runtime via embedding similarity
8
- # before each run() call — only relevant skills are injected.
9
- #
10
- # Usage:
11
- # mkdir -p ~/.prompts/skills/code_reviewer
12
- # # (create SKILL.md as shown in the example header)
13
- # ruby examples/34_agentskills.rb
14
-
15
- require_relative "common"
16
- require "robot_lab/document_store"
17
-
18
- require "logger"
19
- log_file = File.join(__dir__, "34.log")
20
- RobotLab.config.logger = Logger.new(log_file)
21
- RubyLLM.configure { |c| c.logger = Logger.new(log_file) }
22
-
23
- banner "RobotLab — AgentSkills.io Integration Demo"
24
-
25
- # Check if the skill is installed
26
- skill_path = File.expand_path("~/.prompts/skills/code_reviewer/SKILL.md")
27
- unless File.exist?(skill_path)
28
- puts "Demo skill not found at #{skill_path}"
29
- puts "Create it with:"
30
- puts " mkdir -p ~/.prompts/skills/code_reviewer"
31
- puts " # Then add SKILL.md with name: code_reviewer"
32
- exit 1
33
- end
34
-
35
- # Build a robot that lists code_reviewer as a candidate skill.
36
- # At runtime, if the user message is semantically similar to
37
- # "Review Ruby code for quality, style, and potential bugs",
38
- # the skill's instructions are injected into the system prompt.
39
- robot = RobotLab.build(
40
- **llm_opts,
41
- name: "assistant",
42
- system_prompt: "You are a helpful Ruby programming assistant.",
43
- skills: [:code_reviewer]
44
- )
45
-
46
- puts "Declared skills: #{robot.skills.inspect}"
47
- puts "(Folder-format skills are matched per-run by embedding similarity, so"
48
- puts " whether one is injected depends on the message — watch the two queries"
49
- puts " below.)"
50
- puts
51
-
52
- # Message semantically related to code review — skill should activate
53
- code_question = <<~MSG
54
- Please review this Ruby method for quality issues:
55
-
56
- def process(data)
57
- begin
58
- result = data.map { |item| transform(item) }
59
- save(result)
60
- rescue => e
61
- puts e.message
62
- end
63
- end
64
- MSG
65
-
66
- puts "Query: code review (skill should activate)"
67
- result = robot.run(code_question)
68
- puts result.reply
69
- puts
70
- hr
71
-
72
- # Message unrelated to code review — skill should NOT activate
73
- puts "Query: general question (skill should NOT activate)"
74
- result = robot.run("What is the capital of France?")
75
- puts result.reply
76
- puts