kbs 0.0.1 → 0.1.0

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 (86) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/deploy-github-pages.yml +52 -0
  3. data/CHANGELOG.md +68 -2
  4. data/README.md +235 -334
  5. data/docs/DOCUMENTATION_STATUS.md +158 -0
  6. data/docs/advanced/custom-persistence.md +775 -0
  7. data/docs/advanced/debugging.md +726 -0
  8. data/docs/advanced/index.md +8 -0
  9. data/docs/advanced/performance.md +832 -0
  10. data/docs/advanced/testing.md +691 -0
  11. data/docs/api/blackboard.md +1157 -0
  12. data/docs/api/engine.md +978 -0
  13. data/docs/api/facts.md +1212 -0
  14. data/docs/api/index.md +12 -0
  15. data/docs/api/rules.md +1034 -0
  16. data/docs/architecture/blackboard.md +553 -0
  17. data/docs/architecture/index.md +277 -0
  18. data/docs/architecture/network-structure.md +343 -0
  19. data/docs/architecture/rete-algorithm.md +737 -0
  20. data/docs/assets/css/custom.css +83 -0
  21. data/docs/assets/images/blackboard-architecture.svg +136 -0
  22. data/docs/assets/images/compiled-network.svg +101 -0
  23. data/docs/assets/images/fact-assertion-flow.svg +117 -0
  24. data/docs/assets/images/kbs.jpg +0 -0
  25. data/docs/assets/images/pattern-matching-trace.svg +136 -0
  26. data/docs/assets/images/rete-network-layers.svg +96 -0
  27. data/docs/assets/images/system-layers.svg +69 -0
  28. data/docs/assets/images/trading-signal-network.svg +139 -0
  29. data/docs/assets/js/mathjax.js +17 -0
  30. data/docs/examples/expert-systems.md +1031 -0
  31. data/docs/examples/index.md +9 -0
  32. data/docs/examples/multi-agent.md +1335 -0
  33. data/docs/examples/stock-trading.md +488 -0
  34. data/docs/guides/blackboard-memory.md +558 -0
  35. data/docs/guides/dsl.md +1321 -0
  36. data/docs/guides/facts.md +652 -0
  37. data/docs/guides/getting-started.md +383 -0
  38. data/docs/guides/index.md +23 -0
  39. data/docs/guides/negation.md +529 -0
  40. data/docs/guides/pattern-matching.md +561 -0
  41. data/docs/guides/persistence.md +451 -0
  42. data/docs/guides/variable-binding.md +491 -0
  43. data/docs/guides/writing-rules.md +755 -0
  44. data/docs/index.md +157 -0
  45. data/docs/installation.md +156 -0
  46. data/docs/quick-start.md +228 -0
  47. data/examples/README.md +2 -2
  48. data/examples/advanced_example.rb +2 -2
  49. data/examples/advanced_example_dsl.rb +224 -0
  50. data/examples/ai_enhanced_kbs.rb +1 -1
  51. data/examples/ai_enhanced_kbs_dsl.rb +538 -0
  52. data/examples/blackboard_demo_dsl.rb +50 -0
  53. data/examples/car_diagnostic.rb +1 -1
  54. data/examples/car_diagnostic_dsl.rb +54 -0
  55. data/examples/concurrent_inference_demo.rb +5 -5
  56. data/examples/concurrent_inference_demo_dsl.rb +363 -0
  57. data/examples/csv_trading_system.rb +1 -1
  58. data/examples/csv_trading_system_dsl.rb +525 -0
  59. data/examples/knowledge_base.db +0 -0
  60. data/examples/portfolio_rebalancing_system.rb +2 -2
  61. data/examples/portfolio_rebalancing_system_dsl.rb +613 -0
  62. data/examples/redis_trading_demo_dsl.rb +177 -0
  63. data/examples/run_all.rb +50 -0
  64. data/examples/run_all_dsl.rb +49 -0
  65. data/examples/stock_trading_advanced.rb +1 -1
  66. data/examples/stock_trading_advanced_dsl.rb +404 -0
  67. data/examples/temp.txt +7693 -0
  68. data/examples/temp_dsl.txt +8447 -0
  69. data/examples/timestamped_trading.rb +1 -1
  70. data/examples/timestamped_trading_dsl.rb +258 -0
  71. data/examples/trading_demo.rb +1 -1
  72. data/examples/trading_demo_dsl.rb +322 -0
  73. data/examples/working_demo.rb +1 -1
  74. data/examples/working_demo_dsl.rb +160 -0
  75. data/lib/kbs/blackboard/engine.rb +3 -3
  76. data/lib/kbs/blackboard/fact.rb +1 -1
  77. data/lib/kbs/condition.rb +1 -1
  78. data/lib/kbs/dsl/knowledge_base.rb +1 -1
  79. data/lib/kbs/dsl/variable.rb +1 -1
  80. data/lib/kbs/{rete_engine.rb → engine.rb} +1 -1
  81. data/lib/kbs/fact.rb +1 -1
  82. data/lib/kbs/version.rb +1 -1
  83. data/lib/kbs.rb +2 -2
  84. data/mkdocs.yml +181 -0
  85. metadata +66 -6
  86. data/examples/stock_trading_system.rb.bak +0 -563
@@ -0,0 +1,538 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ruby_llm'
4
+ require 'ruby_llm/mcp'
5
+ require_relative '../lib/kbs/dsl'
6
+ require 'json'
7
+ require 'date'
8
+
9
+ # Configure RubyLLM for Ollama
10
+ RubyLLM.configure do |config|
11
+ config.ollama_api_base = ENV['OLLAMA_API_BASE'] || 'http://localhost:11434'
12
+ end
13
+
14
+ module AIEnhancedKBS
15
+ class AIKnowledgeSystem
16
+ include KBS::DSL::ConditionHelpers
17
+
18
+ attr_reader :kb
19
+
20
+ def initialize
21
+ @kb = nil
22
+ @ai_client = setup_ai_client
23
+ @mcp_agent = setup_mcp_agent
24
+ @sentiment_cache = {}
25
+ @explanation_cache = {}
26
+ setup_ai_enhanced_rules
27
+ end
28
+
29
+ def setup_ai_client
30
+ model = ENV['OLLAMA_MODEL'] || 'gpt-oss:latest'
31
+ puts "šŸ¤– Initializing RubyLLM::Chat with Ollama model: #{model}"
32
+ RubyLLM::Chat.new(provider: :ollama, model: model)
33
+ end
34
+
35
+ def setup_mcp_agent
36
+ if defined?(RubyLLM::MCP::Agent)
37
+ RubyLLM::MCP::Agent.new(
38
+ name: "market_analyst",
39
+ description: "AI agent for market analysis and trading insights"
40
+ )
41
+ else
42
+ nil
43
+ end
44
+ end
45
+
46
+ def setup_ai_enhanced_rules
47
+ ai_sys = self # Capture self for use in perform blocks
48
+ @kb = KBS.knowledge_base do
49
+ rule "ai_sentiment_analysis" do
50
+ priority 20
51
+ on :news_data,
52
+ symbol: satisfies { |s| s && s.length > 0 },
53
+ headline: satisfies { |h| h && h.length > 10 },
54
+ content: satisfies { |c| c && c.length > 50 }
55
+
56
+ perform do |facts|
57
+ news = facts.find { |f| f.type == :news_data }
58
+ symbol = news[:symbol]
59
+
60
+ # AI-powered sentiment analysis
61
+ sentiment = ai_sys.analyze_sentiment_with_ai(news[:headline], news[:content])
62
+
63
+ puts "šŸ¤– AI SENTIMENT ANALYSIS: #{symbol}"
64
+ puts " Headline: #{news[:headline][0..80]}..."
65
+ puts " AI Sentiment: #{sentiment[:sentiment]} (#{sentiment[:confidence]}%)"
66
+ puts " Key Themes: #{sentiment[:themes].join(', ')}"
67
+ puts " Market Impact: #{sentiment[:market_impact]}"
68
+
69
+ # Add sentiment fact to working memory
70
+ ai_sys.kb.fact :ai_sentiment, {
71
+ symbol: symbol,
72
+ sentiment_score: sentiment[:score],
73
+ confidence: sentiment[:confidence],
74
+ themes: sentiment[:themes],
75
+ market_impact: sentiment[:market_impact],
76
+ timestamp: Time.now
77
+ }
78
+ end
79
+ end
80
+
81
+ rule "ai_strategy_generation" do
82
+ priority 15
83
+ on :market_conditions,
84
+ volatility: satisfies { |v| v && v > 25 },
85
+ trend: satisfies { |t| t && t.length > 0 }
86
+ on :portfolio_state, cash_ratio: satisfies { |c| c && c > 0.2 }
87
+
88
+ perform do |facts|
89
+ market = facts.find { |f| f.type == :market_conditions }
90
+ portfolio = facts.find { |f| f.type == :portfolio_state }
91
+
92
+ # Generate AI strategy
93
+ strategy = ai_sys.generate_ai_trading_strategy(market, portfolio)
94
+
95
+ puts "🧠 AI TRADING STRATEGY"
96
+ puts " Market Context: #{market[:trend]} trend, #{market[:volatility]}% volatility"
97
+ puts " Strategy: #{strategy[:name]}"
98
+ puts " Rationale: #{strategy[:rationale]}"
99
+ puts " Actions: #{strategy[:actions].join(', ')}"
100
+ puts " Risk Level: #{strategy[:risk_level]}"
101
+ end
102
+ end
103
+
104
+ rule "dynamic_rule_creation" do
105
+ priority 12
106
+ on :pattern_anomaly,
107
+ pattern_type: satisfies { |p| p && p.length > 0 },
108
+ confidence: satisfies { |c| c && c > 0.8 },
109
+ occurrences: satisfies { |o| o && o > 5 }
110
+
111
+ perform do |facts|
112
+ anomaly = facts.find { |f| f.type == :pattern_anomaly }
113
+
114
+ # AI generates new trading rule
115
+ new_rule_spec = generate_rule_with_ai(anomaly)
116
+
117
+ puts "šŸŽÆ AI RULE GENERATION"
118
+ puts " Pattern: #{anomaly[:pattern_type]}"
119
+ puts " New Rule: #{new_rule_spec[:name]}"
120
+ puts " Logic: #{new_rule_spec[:description]}"
121
+
122
+ # Dynamically add new rule to engine
123
+ if new_rule_spec[:valid]
124
+ dynamic_rule = create_rule_from_spec(new_rule_spec)
125
+ @kb.engine.add_rule(dynamic_rule)
126
+ puts " āœ… Rule added to knowledge base"
127
+ end
128
+ end
129
+ end
130
+
131
+ rule "ai_risk_assessment" do
132
+ priority 18
133
+ on :position, unrealized_pnl: satisfies { |pnl| pnl && pnl.abs > 1000 }
134
+ on :market_data, symbol: satisfies { |s| s && s.length > 0 }
135
+
136
+ perform do |facts|
137
+ position = facts.find { |f| f.type == :position }
138
+ market_data = facts.find { |f| f.type == :market_data }
139
+
140
+ # AI-powered risk analysis
141
+ risk_analysis = ai_sys.analyze_position_risk_with_ai(position, market_data)
142
+
143
+ puts "āš ļø AI RISK ASSESSMENT: #{position[:symbol]}"
144
+ puts " Current P&L: $#{position[:unrealized_pnl]}"
145
+ puts " Risk Level: #{risk_analysis[:risk_level]}"
146
+ puts " Key Risks: #{risk_analysis[:risks].join(', ')}"
147
+ puts " Recommendation: #{risk_analysis[:recommendation]}"
148
+ puts " Confidence: #{risk_analysis[:confidence]}%"
149
+
150
+ # Act on high-risk situations
151
+ if risk_analysis[:risk_level] == "HIGH" && risk_analysis[:confidence] > 80
152
+ puts " 🚨 HIGH RISK DETECTED - Consider position adjustment"
153
+ end
154
+ end
155
+ end
156
+
157
+ rule "ai_explanation_generator" do
158
+ priority 5
159
+ on :trade_recommendation,
160
+ action: satisfies { |a| ["BUY", "SELL", "HOLD"].include?(a) },
161
+ symbol: satisfies { |s| s && s.length > 0 }
162
+
163
+ perform do |facts|
164
+ recommendation = facts.find { |f| f.type == :trade_recommendation }
165
+
166
+ # Generate natural language explanation
167
+ explanation = ai_sys.generate_trade_explanation(recommendation, facts)
168
+
169
+ puts "šŸ’¬ AI EXPLANATION: #{recommendation[:symbol]} #{recommendation[:action]}"
170
+ puts " Reasoning: #{explanation[:reasoning]}"
171
+ puts " Context: #{explanation[:context]}"
172
+ puts " Confidence: #{explanation[:confidence]}%"
173
+ puts " Alternative View: #{explanation[:alternative]}"
174
+ end
175
+ end
176
+
177
+ rule "ai_pattern_recognition" do
178
+ priority 10
179
+ on :price_history,
180
+ symbol: satisfies { |s| s && s.length > 0 },
181
+ data_points: satisfies { |d| d && d.length >= 30 }
182
+
183
+ perform do |facts|
184
+ price_data = facts.find { |f| f.type == :price_history }
185
+
186
+ # AI identifies patterns
187
+ patterns = ai_sys.identify_patterns_with_ai(price_data[:data_points])
188
+
189
+ if patterns.any?
190
+ puts "šŸ“Š AI PATTERN RECOGNITION: #{price_data[:symbol]}"
191
+ patterns.each do |pattern|
192
+ puts " Pattern: #{pattern[:name]} (#{pattern[:confidence]}%)"
193
+ puts " Prediction: #{pattern[:prediction]}"
194
+ puts " Time Horizon: #{pattern[:time_horizon]}"
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
201
+
202
+ def analyze_sentiment_with_ai(headline, content)
203
+ cache_key = "#{headline[0..50]}_#{content[0..100]}".hash
204
+ return @sentiment_cache[cache_key] if @sentiment_cache[cache_key]
205
+
206
+ prompt = build_sentiment_prompt(headline, content)
207
+ puts "\nšŸ”— Calling RubyLLM for sentiment analysis..."
208
+
209
+ message = @ai_client.ask(prompt)
210
+ response_text = message.content.to_s
211
+ puts "āœ… Got response from Ollama (#{response_text.length} chars)"
212
+ puts "šŸ“ Response: #{response_text[0..200]}..." if response_text.length > 200
213
+
214
+ result = parse_sentiment_response(response_text)
215
+
216
+ @sentiment_cache[cache_key] = result
217
+ result
218
+ end
219
+
220
+ def generate_ai_trading_strategy(market_conditions, portfolio_state)
221
+ prompt = build_strategy_prompt(market_conditions, portfolio_state)
222
+ puts "\nšŸ”— Calling RubyLLM for trading strategy..."
223
+
224
+ message = @ai_client.ask(prompt)
225
+ response_text = message.content.to_s
226
+ puts "āœ… Got response from Ollama (#{response_text.length} chars)"
227
+ puts "šŸ“ Response: #{response_text[0..200]}..." if response_text.length > 200
228
+
229
+ parse_strategy_response(response_text)
230
+ end
231
+
232
+ def generate_rule_with_ai(anomaly_data)
233
+ prompt = build_rule_generation_prompt(anomaly_data)
234
+ puts "\nšŸ”— Calling RubyLLM for rule generation..."
235
+
236
+ message = @ai_client.ask(prompt)
237
+ response_text = message.content.to_s
238
+ puts "āœ… Got response from Ollama (#{response_text.length} chars)"
239
+ puts "šŸ“ Response: #{response_text[0..200]}..." if response_text.length > 200
240
+
241
+ parse_rule_specification(response_text)
242
+ end
243
+
244
+ def analyze_position_risk_with_ai(position, market_data)
245
+ prompt = build_risk_analysis_prompt(position, market_data)
246
+ puts "\nšŸ”— Calling RubyLLM for risk analysis..."
247
+
248
+ message = @ai_client.ask(prompt)
249
+ response_text = message.content.to_s
250
+ puts "āœ… Got response from Ollama (#{response_text.length} chars)"
251
+ puts "šŸ“ Response: #{response_text[0..200]}..." if response_text.length > 200
252
+
253
+ parse_risk_analysis(response_text)
254
+ end
255
+
256
+ def generate_trade_explanation(recommendation, context_facts)
257
+ cache_key = "#{recommendation[:symbol]}_#{recommendation[:action]}_#{context_facts.length}".hash
258
+ return @explanation_cache[cache_key] if @explanation_cache[cache_key]
259
+
260
+ prompt = build_explanation_prompt(recommendation, context_facts)
261
+ puts "\nšŸ”— Calling RubyLLM for trade explanation..."
262
+
263
+ message = @ai_client.ask(prompt)
264
+ response_text = message.content.to_s
265
+ puts "āœ… Got response from Ollama (#{response_text.length} chars)"
266
+ puts "šŸ“ Response: #{response_text[0..200]}..." if response_text.length > 200
267
+
268
+ result = parse_explanation_response(response_text)
269
+
270
+ @explanation_cache[cache_key] = result
271
+ result
272
+ end
273
+
274
+ def identify_patterns_with_ai(price_data)
275
+ prompt = build_pattern_recognition_prompt(price_data)
276
+ puts "\nšŸ”— Calling RubyLLM for pattern recognition..."
277
+
278
+ message = @ai_client.ask(prompt)
279
+ response_text = message.content.to_s
280
+ puts "āœ… Got response from Ollama (#{response_text.length} chars)"
281
+ puts "šŸ“ Response: #{response_text[0..200]}..." if response_text.length > 200
282
+
283
+ parse_pattern_response(response_text)
284
+ end
285
+
286
+ # Prompt builders
287
+ def build_sentiment_prompt(headline, content)
288
+ <<~PROMPT
289
+ Analyze the sentiment of this financial news for trading implications:
290
+
291
+ Headline: #{headline}
292
+ Content: #{content[0..500]}...
293
+
294
+ Provide a JSON response with:
295
+ {
296
+ "sentiment": "positive|negative|neutral",
297
+ "score": -1.0 to 1.0,
298
+ "confidence": 0-100,
299
+ "themes": ["theme1", "theme2"],
300
+ "market_impact": "bullish|bearish|neutral"
301
+ }
302
+ PROMPT
303
+ end
304
+
305
+ def build_strategy_prompt(market_conditions, portfolio_state)
306
+ <<~PROMPT
307
+ Generate a trading strategy for these conditions:
308
+
309
+ Market: #{market_conditions[:trend]} trend, #{market_conditions[:volatility]}% volatility
310
+ Portfolio: #{(portfolio_state[:cash_ratio] * 100).round(1)}% cash
311
+
312
+ Provide a JSON strategy with:
313
+ {
314
+ "name": "strategy_name",
315
+ "rationale": "why this strategy fits",
316
+ "actions": ["action1", "action2"],
317
+ "risk_level": "LOW|MEDIUM|HIGH"
318
+ }
319
+ PROMPT
320
+ end
321
+
322
+ def build_risk_analysis_prompt(position, market_data)
323
+ <<~PROMPT
324
+ Analyze the risk of this trading position:
325
+
326
+ Position: #{position[:symbol]}, P&L: $#{position[:unrealized_pnl]}
327
+ Market Data: #{market_data.to_json}
328
+
329
+ Provide risk assessment as JSON:
330
+ {
331
+ "risk_level": "LOW|MEDIUM|HIGH",
332
+ "risks": ["risk1", "risk2"],
333
+ "recommendation": "hold|reduce|exit",
334
+ "confidence": 0-100
335
+ }
336
+ PROMPT
337
+ end
338
+
339
+ def build_explanation_prompt(recommendation, context_facts)
340
+ <<~PROMPT
341
+ Explain this trading recommendation in simple terms:
342
+
343
+ Recommendation: #{recommendation[:action]} #{recommendation[:symbol]}
344
+ Context: #{context_facts.length} supporting facts
345
+
346
+ Provide explanation as JSON:
347
+ {
348
+ "explanation": "clear explanation",
349
+ "reasoning": "why this makes sense",
350
+ "risks": ["risk1", "risk2"]
351
+ }
352
+ PROMPT
353
+ end
354
+
355
+ def build_pattern_recognition_prompt(price_data)
356
+ <<~PROMPT
357
+ Identify trading patterns in this price data:
358
+
359
+ Data: #{price_data.to_json}
360
+
361
+ Return JSON array of patterns:
362
+ [
363
+ {
364
+ "pattern": "pattern_name",
365
+ "confidence": 0-100,
366
+ "description": "what this means"
367
+ }
368
+ ]
369
+ PROMPT
370
+ end
371
+
372
+ def build_rule_generation_prompt(anomaly_data)
373
+ <<~PROMPT
374
+ Generate a trading rule specification for this anomaly pattern:
375
+
376
+ Pattern Type: #{anomaly_data[:pattern_type]}
377
+ Confidence: #{anomaly_data[:confidence]}
378
+ Occurrences: #{anomaly_data[:occurrences]}
379
+
380
+ Return JSON with:
381
+ {
382
+ "name": "rule_name",
383
+ "description": "what this rule does",
384
+ "valid": true|false
385
+ }
386
+ PROMPT
387
+ end
388
+
389
+ def create_rule_from_spec(spec)
390
+ # Simplified rule creation - in production would parse spec more thoroughly
391
+ KBS::Rule.new(
392
+ spec[:name],
393
+ conditions: [],
394
+ action: lambda { |facts, bindings| puts "Dynamic rule fired: #{spec[:name]}" }
395
+ )
396
+ end
397
+
398
+ # Response parsers
399
+ def extract_json(response)
400
+ # Strip markdown code fences if present
401
+ json_text = response.strip
402
+ json_text = json_text.gsub(/^```json\s*/, '').gsub(/```\s*$/, '').strip
403
+ json_text
404
+ end
405
+
406
+ def parse_sentiment_response(response)
407
+ JSON.parse(extract_json(response), symbolize_names: true)
408
+ end
409
+
410
+ def parse_strategy_response(response)
411
+ JSON.parse(extract_json(response), symbolize_names: true)
412
+ end
413
+
414
+ def parse_explanation_response(response)
415
+ result = JSON.parse(extract_json(response), symbolize_names: true)
416
+ # Ensure all expected keys exist
417
+ {
418
+ reasoning: result[:reasoning] || result[:explanation] || "No reasoning provided",
419
+ context: result[:context] || "No context provided",
420
+ confidence: result[:confidence] || 50,
421
+ alternative: result[:alternative] || result[:risks]&.join(", ") || "No alternatives provided"
422
+ }
423
+ end
424
+
425
+ def parse_pattern_response(response)
426
+ patterns = JSON.parse(extract_json(response), symbolize_names: true)
427
+ # Ensure array format and normalize keys
428
+ patterns = [patterns] unless patterns.is_a?(Array)
429
+ patterns.map do |p|
430
+ {
431
+ name: p[:pattern] || p[:name] || "Unknown Pattern",
432
+ confidence: p[:confidence] || 50,
433
+ prediction: p[:prediction] || p[:description] || "No prediction",
434
+ time_horizon: p[:time_horizon] || "Unknown"
435
+ }
436
+ end
437
+ end
438
+
439
+ def parse_risk_analysis(response)
440
+ JSON.parse(extract_json(response), symbolize_names: true)
441
+ end
442
+
443
+ def parse_rule_specification(response)
444
+ JSON.parse(extract_json(response), symbolize_names: true)
445
+ end
446
+
447
+ def demonstrate_ai_enhancements
448
+ puts "šŸ¤– AI-ENHANCED KNOWLEDGE-BASED SYSTEM"
449
+ puts "=" * 70
450
+ puts "Integrating #{@ai_client.class.name} and #{@mcp_agent.class.name if @mcp_agent}"
451
+ puts "=" * 70
452
+
453
+ # Scenario 1: AI Sentiment Analysis
454
+ puts "\nšŸ“° SCENARIO 1: AI-Powered News Sentiment"
455
+ puts "-" * 50
456
+ @kb.reset
457
+
458
+ @kb.fact :news_data, {
459
+ symbol: "AAPL",
460
+ headline: "Apple Reports Record Q4 Earnings, Beats Expectations by 15%",
461
+ content: "Apple Inc. announced exceptional fourth quarter results today, with revenue growing 12% year-over-year to $94.9 billion. iPhone sales exceeded analysts' expectations, driven by strong demand for the iPhone 15 Pro models. The company also announced a new $90 billion share buyback program and increased its dividend by 4%. CEO Tim Cook expressed optimism about the AI integration roadmap and services growth trajectory.",
462
+ published_at: Time.now
463
+ }
464
+ @kb.run
465
+
466
+ # Scenario 2: AI Strategy Generation
467
+ puts "\n🧠 SCENARIO 2: AI Trading Strategy Generation"
468
+ puts "-" * 50
469
+ @kb.reset
470
+
471
+ @kb.fact :market_conditions, {
472
+ volatility: 28.5,
473
+ trend: "sideways",
474
+ sector_rotation: "technology_to_healthcare"
475
+ }
476
+
477
+ @kb.fact :portfolio_state, {
478
+ cash_ratio: 0.25,
479
+ largest_position: "AAPL",
480
+ sector_concentration: 0.45
481
+ }
482
+ @kb.run
483
+
484
+ # Scenario 3: AI Risk Assessment
485
+ puts "\nāš ļø SCENARIO 3: AI Risk Assessment"
486
+ puts "-" * 50
487
+ @kb.reset
488
+
489
+ @kb.fact :position, {
490
+ symbol: "TSLA",
491
+ shares: 100,
492
+ entry_price: 250.00,
493
+ current_price: 235.00,
494
+ unrealized_pnl: -1500
495
+ }
496
+
497
+ @kb.fact :market_data, {
498
+ symbol: "TSLA",
499
+ volatility: 45.2,
500
+ beta: 2.1,
501
+ sector: "Consumer Discretionary"
502
+ }
503
+ @kb.run
504
+
505
+ # Scenario 4: Trade Explanation
506
+ puts "\nšŸ’¬ SCENARIO 4: AI Trade Explanation"
507
+ puts "-" * 50
508
+ @kb.reset
509
+
510
+ @kb.fact :trade_recommendation, {
511
+ symbol: "GOOGL",
512
+ action: "BUY",
513
+ quantity: 50,
514
+ confidence: 85
515
+ }
516
+
517
+ @kb.fact :technical_analysis, {
518
+ symbol: "GOOGL",
519
+ rsi: 35,
520
+ moving_average_signal: "golden_cross",
521
+ volume_trend: "increasing"
522
+ }
523
+ @kb.run
524
+
525
+ puts "\n" + "=" * 70
526
+ puts "AI ENHANCEMENT DEMONSTRATION COMPLETE"
527
+ puts "šŸŽÆ The system now combines rule-based logic with AI insights"
528
+ puts "🧠 Dynamic pattern recognition and strategy generation"
529
+ puts "šŸ’¬ Natural language explanations for all decisions"
530
+ puts "⚔ Real-time sentiment analysis and risk assessment"
531
+ end
532
+ end
533
+ end
534
+
535
+ if __FILE__ == $0
536
+ system = AIEnhancedKBS::AIKnowledgeSystem.new
537
+ system.demonstrate_ai_enhancements
538
+ end
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/kbs/blackboard'
4
+
5
+ puts "Blackboard Memory System Demonstration"
6
+ puts "=" * 70
7
+
8
+ engine = KBS::Blackboard::Engine.new(db_path: 'knowledge_base.db')
9
+
10
+ puts "\nAdding persistent facts..."
11
+ sensor1 = engine.add_fact(:sensor, { location: "room_1", type: "temperature", value: 22 })
12
+ sensor2 = engine.add_fact(:sensor, { location: "room_2", type: "humidity", value: 65 })
13
+ alert = engine.add_fact(:alert, { level: "warning", message: "Check sensors" })
14
+
15
+ puts "Facts added with UUIDs:"
16
+ puts " Sensor 1: #{sensor1.uuid}"
17
+ puts " Sensor 2: #{sensor2.uuid}"
18
+ puts " Alert: #{alert.uuid}"
19
+
20
+ puts "\nPosting messages to blackboard..."
21
+ engine.post_message("TemperatureMonitor", "sensor_data", { reading: 25, timestamp: Time.now }, priority: 5)
22
+ engine.post_message("HumidityMonitor", "sensor_data", { reading: 70, timestamp: Time.now }, priority: 3)
23
+ engine.post_message("SystemController", "commands", { action: "calibrate", target: "all_sensors" }, priority: 10)
24
+
25
+ puts "\nConsuming high-priority message..."
26
+ message = engine.consume_message("commands", "MainController")
27
+ puts " Received: #{message[:content]}" if message
28
+
29
+ puts "\nUpdating sensor value..."
30
+ sensor1[:value] = 28
31
+
32
+ puts "\nDatabase Statistics:"
33
+ stats = engine.stats
34
+ stats.each do |key, value|
35
+ puts " #{key.to_s.gsub('_', ' ').capitalize}: #{value}"
36
+ end
37
+
38
+ puts "\nFact History (last 5 entries):"
39
+ history = engine.blackboard.get_history(limit: 5)
40
+ history.each do |entry|
41
+ puts " [#{entry[:timestamp].strftime('%H:%M:%S')}] #{entry[:action]}: #{entry[:fact_type]}(#{entry[:attributes]})"
42
+ end
43
+
44
+ puts "\nQuerying facts by type..."
45
+ sensors = engine.blackboard.get_facts(:sensor)
46
+ puts " Found #{sensors.size} sensor(s)"
47
+ sensors.each { |s| puts " - #{s}" }
48
+
49
+ puts "\n" + "=" * 70
50
+ puts "Blackboard persisted to: knowledge_base.db"
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative '../lib/kbs'
4
4
 
5
- engine = KBS::ReteEngine.new
5
+ engine = KBS::Engine.new
6
6
 
7
7
  puts "Creating a simple expert system for diagnosing car problems..."
8
8
  puts "-" * 60
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/kbs/dsl'
4
+
5
+ puts "Creating a simple expert system for diagnosing car problems..."
6
+ puts "-" * 60
7
+
8
+ kb = KBS.knowledge_base do
9
+ rule "dead_battery" do
10
+ on :symptom, problem: "won't start"
11
+ on :symptom, problem: "no lights"
12
+
13
+ perform do |facts, bindings|
14
+ puts "DIAGNOSIS: Dead battery - The car won't start and has no lights"
15
+ puts "RECOMMENDATION: Jump start the battery or replace it"
16
+ end
17
+ end
18
+
19
+ rule "flat_tire" do
20
+ on :symptom, problem: "pulling to side"
21
+ on :symptom, problem: "low tire pressure"
22
+
23
+ perform do |facts, bindings|
24
+ puts "DIAGNOSIS: Flat or low tire"
25
+ puts "RECOMMENDATION: Check tire pressure and inflate or replace tire"
26
+ end
27
+ end
28
+
29
+ rule "overheating" do
30
+ on :symptom, problem: "high temperature"
31
+ on :symptom, problem: "steam from hood"
32
+ without.on :symptom, problem: "coolant leak"
33
+
34
+ perform do |facts, bindings|
35
+ puts "DIAGNOSIS: Engine overheating (no coolant leak detected)"
36
+ puts "RECOMMENDATION: Check radiator and cooling system"
37
+ end
38
+ end
39
+ end
40
+
41
+ puts "\nAdding symptoms..."
42
+ kb.fact :symptom, { problem: "won't start", severity: "high" }
43
+ kb.fact :symptom, { problem: "no lights", severity: "high" }
44
+
45
+ puts "\nRunning inference engine..."
46
+ kb.run
47
+
48
+ puts "\n" + "-" * 60
49
+ puts "\nAdding more symptoms..."
50
+ kb.fact :symptom, { problem: "high temperature", severity: "critical" }
51
+ kb.fact :symptom, { problem: "steam from hood", severity: "high" }
52
+
53
+ puts "\nRunning inference engine again..."
54
+ kb.run
@@ -9,7 +9,7 @@ require 'thread'
9
9
  # Rules fire immediately when facts are added
10
10
  # =============================================================================
11
11
 
12
- class ReactiveEngine < KBS::ReteEngine
12
+ class ReactiveEngine < KBS::Engine
13
13
  attr_accessor :auto_inference
14
14
 
15
15
  def initialize(auto_inference: true)
@@ -37,7 +37,7 @@ end
37
37
  # Inference runs continuously in a background thread
38
38
  # =============================================================================
39
39
 
40
- class BackgroundInferenceEngine < KBS::ReteEngine
40
+ class BackgroundInferenceEngine < KBS::Engine
41
41
  def initialize
42
42
  super()
43
43
  @running = false
@@ -103,7 +103,7 @@ end
103
103
  # Execute callbacks immediately when rules fire
104
104
  # =============================================================================
105
105
 
106
- class EventDrivenEngine < KBS::ReteEngine
106
+ class EventDrivenEngine < KBS::Engine
107
107
  def initialize
108
108
  super()
109
109
  @rule_callbacks = {}
@@ -285,7 +285,7 @@ def demo_queue_based
285
285
  puts "="*80
286
286
  puts "Facts accumulate and are processed in batches\n\n"
287
287
 
288
- engine = KBS::ReteEngine.new
288
+ engine = KBS::Engine.new
289
289
  fact_queue = Queue.new
290
290
 
291
291
  # Define rule
@@ -344,7 +344,7 @@ end
344
344
  # =============================================================================
345
345
 
346
346
  if __FILE__ == $0
347
- puts "\nšŸš€ CONCURRENT INFERENCE PATTERNS FOR RETE II\n"
347
+ puts "\nšŸš€ CONCURRENT INFERENCE PATTERNS FOR RETE\n"
348
348
 
349
349
  demo_reactive_inference
350
350
  sleep 1