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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +1 -1
- data/lib/robot_lab/mcp/transports/streamable_http.rb +1 -1
- data/lib/robot_lab/version.rb +1 -1
- metadata +3 -253
- data/.architecture/AGENTS.md +0 -32
- data/.architecture/config.yml +0 -8
- data/.architecture/members.yml +0 -60
- data/.architecture/reviews/feature-free-will.md +0 -490
- data/.architecture/reviews/overall-codebase.md +0 -427
- data/.claude/settings.local.json +0 -57
- data/.codex/config.toml +0 -2
- data/.envrc +0 -6
- data/.github/workflows/deploy-github-pages.yml +0 -52
- data/.irbrc +0 -6
- data/.loki +0 -65
- data/.quality/reek_baseline.txt +0 -43
- data/.rubocop.yml +0 -10
- data/Archspec.rb +0 -44
- data/CLAUDE.md +0 -141
- data/COMMITS.md +0 -196
- data/Rakefile +0 -193
- data/_typos.toml +0 -21
- data/agent2agent_review.md +0 -192
- data/agentf_improvements.md +0 -253
- data/agents.md +0 -14
- data/docs/api/core/index.md +0 -147
- data/docs/api/core/memory.md +0 -578
- data/docs/api/core/network.md +0 -553
- data/docs/api/core/result.md +0 -211
- data/docs/api/core/robot.md +0 -1667
- data/docs/api/core/state.md +0 -145
- data/docs/api/core/tool.md +0 -520
- data/docs/api/errors.md +0 -323
- data/docs/api/hooks.md +0 -469
- data/docs/api/index.md +0 -137
- data/docs/api/mcp/client.md +0 -387
- data/docs/api/mcp/index.md +0 -267
- data/docs/api/mcp/server.md +0 -252
- data/docs/api/mcp/transports.md +0 -339
- data/docs/api/messages/index.md +0 -126
- data/docs/api/messages/text-message.md +0 -170
- data/docs/api/messages/tool-call-message.md +0 -206
- data/docs/api/messages/tool-result-message.md +0 -242
- data/docs/api/messages/user-message.md +0 -232
- data/docs/api/skills.md +0 -423
- data/docs/api/streaming/context.md +0 -280
- data/docs/api/streaming/events.md +0 -185
- data/docs/api/streaming/index.md +0 -156
- data/docs/api/support.md +0 -846
- data/docs/architecture/core-concepts.md +0 -575
- data/docs/architecture/index.md +0 -252
- data/docs/architecture/message-flow.md +0 -366
- data/docs/architecture/network-orchestration.md +0 -478
- data/docs/architecture/robot-execution.md +0 -396
- data/docs/architecture/state-management.md +0 -410
- data/docs/assets/css/custom.css +0 -56
- data/docs/assets/images/robot_lab.jpg +0 -0
- data/docs/concepts.md +0 -641
- data/docs/examples/basic-chat.md +0 -258
- data/docs/examples/index.md +0 -425
- data/docs/examples/mcp-server.md +0 -403
- data/docs/examples/multi-robot-network.md +0 -476
- data/docs/examples/tool-usage.md +0 -468
- data/docs/getting-started/configuration.md +0 -645
- data/docs/getting-started/index.md +0 -56
- data/docs/getting-started/installation.md +0 -197
- data/docs/getting-started/quick-start.md +0 -293
- data/docs/guides/building-robots.md +0 -1187
- data/docs/guides/creating-networks.md +0 -736
- data/docs/guides/hooks.md +0 -1206
- data/docs/guides/index.md +0 -78
- data/docs/guides/knowledge.md +0 -219
- data/docs/guides/mcp-integration.md +0 -740
- data/docs/guides/memory.md +0 -458
- data/docs/guides/observability.md +0 -733
- data/docs/guides/streaming.md +0 -323
- data/docs/guides/using-tools.md +0 -780
- data/docs/index.md +0 -280
- data/docs/superpowers/plans/2026-05-06-agentskills.md +0 -1303
- data/docs/superpowers/specs/2026-05-06-agentskills-design.md +0 -247
- data/examples/.envrc +0 -3
- data/examples/01_simple_robot.rb +0 -31
- data/examples/02_tools.rb +0 -109
- data/examples/03_network.rb +0 -118
- data/examples/04_mcp.rb +0 -217
- data/examples/05_streaming.rb +0 -137
- data/examples/06_prompt_templates.rb +0 -325
- data/examples/07_network_memory.rb +0 -313
- data/examples/08_llm_config.rb +0 -162
- data/examples/09_chaining.rb +0 -251
- data/examples/10_memory.rb +0 -315
- data/examples/11_network_introspection.rb +0 -239
- data/examples/12_message_bus.rb +0 -67
- data/examples/13_spawn.rb +0 -85
- data/examples/14_rusty_circuit/.envrc +0 -1
- data/examples/14_rusty_circuit/.gitignore +0 -1
- data/examples/14_rusty_circuit/README.md +0 -1
- data/examples/14_rusty_circuit/comic.rb +0 -149
- data/examples/14_rusty_circuit/display.rb +0 -217
- data/examples/14_rusty_circuit/heckler.rb +0 -65
- data/examples/14_rusty_circuit/open_mic.rb +0 -132
- data/examples/14_rusty_circuit/prompts/open_mic_comic.md +0 -20
- data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +0 -23
- data/examples/14_rusty_circuit/prompts/open_mic_scout.md +0 -20
- data/examples/14_rusty_circuit/scout.rb +0 -165
- data/examples/14_rusty_circuit/show.log +0 -234
- data/examples/15_memory_network_and_bus/.envrc +0 -1
- data/examples/15_memory_network_and_bus/README.md +0 -66
- data/examples/15_memory_network_and_bus/editor_in_chief.rb +0 -24
- data/examples/15_memory_network_and_bus/editorial_pipeline.rb +0 -213
- data/examples/15_memory_network_and_bus/linux_writer.rb +0 -80
- data/examples/15_memory_network_and_bus/os_editor.rb +0 -48
- data/examples/15_memory_network_and_bus/os_writer.rb +0 -49
- data/examples/15_memory_network_and_bus/output/combined_article.md +0 -11
- data/examples/15_memory_network_and_bus/output/final_article.md +0 -8
- data/examples/15_memory_network_and_bus/output/linux_draft.md +0 -7
- data/examples/15_memory_network_and_bus/output/mac_draft.md +0 -7
- data/examples/15_memory_network_and_bus/output/memory.json +0 -13
- data/examples/15_memory_network_and_bus/output/revision_1.md +0 -29
- data/examples/15_memory_network_and_bus/output/revision_2.md +0 -50
- data/examples/15_memory_network_and_bus/output/revision_3.md +0 -8
- data/examples/15_memory_network_and_bus/output/windows_draft.md +0 -7
- data/examples/15_memory_network_and_bus/prompts/os_advocate.md +0 -13
- data/examples/15_memory_network_and_bus/prompts/os_chief.md +0 -13
- data/examples/15_memory_network_and_bus/prompts/os_editor.md +0 -13
- data/examples/16_writers_room/.envrc +0 -1
- data/examples/16_writers_room/display.rb +0 -158
- data/examples/16_writers_room/output/.gitignore +0 -4
- data/examples/16_writers_room/output/README.md +0 -69
- data/examples/16_writers_room/output/opus_001.md +0 -263
- data/examples/16_writers_room/output/opus_001_notes.log +0 -470
- data/examples/16_writers_room/output/opus_002.md +0 -245
- data/examples/16_writers_room/output/opus_002_notes.log +0 -546
- data/examples/16_writers_room/output/opus_002_screenplay.md +0 -7989
- data/examples/16_writers_room/output/opus_002_screenplay_notes.md +0 -993
- data/examples/16_writers_room/prompts/screenplay_writer.md +0 -66
- data/examples/16_writers_room/prompts/writer.md +0 -37
- data/examples/16_writers_room/room.rb +0 -186
- data/examples/16_writers_room/tools.rb +0 -173
- data/examples/16_writers_room/writer.rb +0 -121
- data/examples/16_writers_room/writers_room.rb +0 -256
- data/examples/17_skills.rb +0 -234
- data/examples/18_rails/.envrc +0 -3
- data/examples/18_rails/.gitignore +0 -5
- data/examples/18_rails/Gemfile +0 -11
- data/examples/18_rails/README.md +0 -67
- data/examples/18_rails/Rakefile +0 -4
- data/examples/18_rails/app/controllers/application_controller.rb +0 -4
- data/examples/18_rails/app/controllers/chat_controller.rb +0 -50
- data/examples/18_rails/app/jobs/application_job.rb +0 -4
- data/examples/18_rails/app/jobs/robot_run_job.rb +0 -25
- data/examples/18_rails/app/models/application_record.rb +0 -5
- data/examples/18_rails/app/models/robot_lab_result.rb +0 -36
- data/examples/18_rails/app/models/robot_lab_thread.rb +0 -23
- data/examples/18_rails/app/robots/chat_robot.rb +0 -25
- data/examples/18_rails/app/tools/time_tool.rb +0 -9
- data/examples/18_rails/app/views/chat/_user_message.html.erb +0 -1
- data/examples/18_rails/app/views/chat/index.html.erb +0 -67
- data/examples/18_rails/app/views/layouts/application.html.erb +0 -49
- data/examples/18_rails/bin/dev +0 -7
- data/examples/18_rails/bin/rails +0 -6
- data/examples/18_rails/bin/setup +0 -15
- data/examples/18_rails/config/application.rb +0 -33
- data/examples/18_rails/config/cable.yml +0 -2
- data/examples/18_rails/config/database.yml +0 -5
- data/examples/18_rails/config/environment.rb +0 -4
- data/examples/18_rails/config/initializers/robot_lab.rb +0 -11
- data/examples/18_rails/config/routes.rb +0 -6
- data/examples/18_rails/config.ru +0 -4
- data/examples/18_rails/db/migrate/001_create_robot_lab_tables.rb +0 -32
- data/examples/19_token_tracking.rb +0 -138
- data/examples/20_circuit_breaker.rb +0 -147
- data/examples/21_learning_loop.rb +0 -181
- data/examples/22_context_compression.rb +0 -172
- data/examples/23_convergence.rb +0 -133
- data/examples/24_structured_delegation.rb +0 -151
- data/examples/25_history_search/conversation.jsonl +0 -30
- data/examples/25_history_search.rb +0 -133
- data/examples/26_document_store/api_versioning_adr.md +0 -52
- data/examples/26_document_store/incident_postmortem.md +0 -46
- data/examples/26_document_store/postgres_runbook.md +0 -49
- data/examples/26_document_store/redis_caching_guide.md +0 -48
- data/examples/26_document_store/sidekiq_guide.md +0 -51
- data/examples/26_document_store.rb +0 -140
- data/examples/27_incident_response/README.md +0 -65
- data/examples/27_incident_response/incident_response.rb +0 -261
- data/examples/28_mcp_discovery.rb +0 -113
- data/examples/29_ractor_tools.rb +0 -240
- data/examples/30_ractor_network.rb +0 -252
- data/examples/31_launch_assessment.rb +0 -246
- data/examples/32_newsletter_reader.rb +0 -193
- data/examples/33_stock_generator.rb +0 -80
- data/examples/33_stock_predictor.rb +0 -327
- data/examples/34_agentskills.rb +0 -76
- data/examples/35_hooks.rb +0 -266
- data/examples/README.md +0 -550
- data/examples/common.rb +0 -180
- data/examples/prompts/assistant.md +0 -5
- data/examples/prompts/audit_trail.md +0 -8
- data/examples/prompts/billing.md +0 -10
- data/examples/prompts/classifier.md +0 -7
- data/examples/prompts/comedian.md +0 -6
- data/examples/prompts/comedy_critic.md +0 -10
- data/examples/prompts/configurable.md +0 -9
- data/examples/prompts/dispatcher.md +0 -12
- data/examples/prompts/entity_extractor.md +0 -14
- data/examples/prompts/escalation.md +0 -42
- data/examples/prompts/frontmatter_mcp_test.md +0 -9
- data/examples/prompts/frontmatter_named_test.md +0 -5
- data/examples/prompts/frontmatter_tools_test.md +0 -6
- data/examples/prompts/general.md +0 -7
- data/examples/prompts/github_assistant.md +0 -14
- data/examples/prompts/helper.md +0 -4
- data/examples/prompts/incident_responder.md +0 -18
- data/examples/prompts/keyword_extractor.md +0 -11
- data/examples/prompts/llm_config_demo.md +0 -20
- data/examples/prompts/order_support.md +0 -35
- data/examples/prompts/os_advocate.md +0 -13
- data/examples/prompts/os_chief.md +0 -13
- data/examples/prompts/os_editor.md +0 -13
- data/examples/prompts/parameterized_main_test.md +0 -6
- data/examples/prompts/pii_redactor.md +0 -7
- data/examples/prompts/product_support.md +0 -37
- data/examples/prompts/runbook_protocol.md +0 -12
- data/examples/prompts/sentiment_analyzer.md +0 -12
- data/examples/prompts/skill_a_test.md +0 -4
- data/examples/prompts/skill_b_test.md +0 -4
- data/examples/prompts/skill_config_test.md +0 -5
- data/examples/prompts/skill_cycle_a_test.md +0 -6
- data/examples/prompts/skill_cycle_b_test.md +0 -6
- data/examples/prompts/skill_description_test.md +0 -4
- data/examples/prompts/skill_leaf_test.md +0 -4
- data/examples/prompts/skill_nested_test.md +0 -6
- data/examples/prompts/skill_refs_main_test.md +0 -6
- data/examples/prompts/skill_self_ref_test.md +0 -6
- data/examples/prompts/skill_with_mcp_test.md +0 -9
- data/examples/prompts/skill_with_params_test.md +0 -6
- data/examples/prompts/skill_with_robot_name_test.md +0 -5
- data/examples/prompts/skill_with_tools_test.md +0 -6
- data/examples/prompts/sre_compliance.md +0 -10
- data/examples/prompts/structured_output.md +0 -7
- data/examples/prompts/synthesizer.md +0 -17
- data/examples/prompts/technical.md +0 -10
- data/examples/prompts/template_with_skills_test.md +0 -6
- data/examples/prompts/triage.md +0 -22
- data/examples/ruboruby.md +0 -423
- data/examples/run_all.rb +0 -60
- data/examples/xyzzy.rb +0 -97
- data/logfile +0 -8
- data/mkdocs.yml +0 -209
- data/robot_concurrency.md +0 -38
- data/simple_acp_review.md +0 -298
- data/temp.md +0 -6
- 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
|
data/examples/34_agentskills.rb
DELETED
|
@@ -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
|