kbs 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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.envrc +3 -0
  3. data/CHANGELOG.md +5 -0
  4. data/COMMITS.md +196 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +481 -0
  7. data/Rakefile +8 -0
  8. data/examples/README.md +531 -0
  9. data/examples/advanced_example.rb +270 -0
  10. data/examples/ai_enhanced_kbs.rb +523 -0
  11. data/examples/blackboard_demo.rb +50 -0
  12. data/examples/car_diagnostic.rb +64 -0
  13. data/examples/concurrent_inference_demo.rb +363 -0
  14. data/examples/csv_trading_system.rb +559 -0
  15. data/examples/iot_demo_using_dsl.rb +83 -0
  16. data/examples/portfolio_rebalancing_system.rb +651 -0
  17. data/examples/redis_trading_demo.rb +177 -0
  18. data/examples/sample_stock_data.csv +46 -0
  19. data/examples/stock_trading_advanced.rb +469 -0
  20. data/examples/stock_trading_system.rb.bak +563 -0
  21. data/examples/timestamped_trading.rb +286 -0
  22. data/examples/trading_demo.rb +334 -0
  23. data/examples/working_demo.rb +176 -0
  24. data/lib/kbs/alpha_memory.rb +37 -0
  25. data/lib/kbs/beta_memory.rb +57 -0
  26. data/lib/kbs/blackboard/audit_log.rb +115 -0
  27. data/lib/kbs/blackboard/engine.rb +83 -0
  28. data/lib/kbs/blackboard/fact.rb +65 -0
  29. data/lib/kbs/blackboard/memory.rb +191 -0
  30. data/lib/kbs/blackboard/message_queue.rb +96 -0
  31. data/lib/kbs/blackboard/persistence/hybrid_store.rb +118 -0
  32. data/lib/kbs/blackboard/persistence/redis_store.rb +218 -0
  33. data/lib/kbs/blackboard/persistence/sqlite_store.rb +242 -0
  34. data/lib/kbs/blackboard/persistence/store.rb +55 -0
  35. data/lib/kbs/blackboard/redis_audit_log.rb +107 -0
  36. data/lib/kbs/blackboard/redis_message_queue.rb +111 -0
  37. data/lib/kbs/blackboard.rb +23 -0
  38. data/lib/kbs/condition.rb +26 -0
  39. data/lib/kbs/dsl/condition_helpers.rb +57 -0
  40. data/lib/kbs/dsl/knowledge_base.rb +86 -0
  41. data/lib/kbs/dsl/pattern_evaluator.rb +69 -0
  42. data/lib/kbs/dsl/rule_builder.rb +115 -0
  43. data/lib/kbs/dsl/variable.rb +35 -0
  44. data/lib/kbs/dsl.rb +18 -0
  45. data/lib/kbs/fact.rb +43 -0
  46. data/lib/kbs/join_node.rb +117 -0
  47. data/lib/kbs/negation_node.rb +88 -0
  48. data/lib/kbs/production_node.rb +28 -0
  49. data/lib/kbs/rete_engine.rb +108 -0
  50. data/lib/kbs/rule.rb +46 -0
  51. data/lib/kbs/token.rb +37 -0
  52. data/lib/kbs/version.rb +5 -0
  53. data/lib/kbs/working_memory.rb +32 -0
  54. data/lib/kbs.rb +20 -0
  55. metadata +164 -0
@@ -0,0 +1,523 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ruby_llm'
4
+ require 'ruby_llm/mcp'
5
+ require_relative '../lib/kbs'
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
+ def initialize
17
+ @engine = KBS::ReteEngine.new
18
+ @ai_client = setup_ai_client
19
+ @mcp_agent = setup_mcp_agent
20
+ @sentiment_cache = {}
21
+ @explanation_cache = {}
22
+ setup_ai_enhanced_rules
23
+ end
24
+
25
+ def setup_ai_client
26
+ model = ENV['OLLAMA_MODEL'] || 'gpt-oss:latest'
27
+ puts "🤖 Initializing RubyLLM::Chat with Ollama model: #{model}"
28
+ RubyLLM::Chat.new(provider: :ollama, model: model)
29
+ end
30
+
31
+ def setup_mcp_agent
32
+ if defined?(RubyLLM::MCP::Agent)
33
+ RubyLLM::MCP::Agent.new(
34
+ name: "market_analyst",
35
+ description: "AI agent for market analysis and trading insights"
36
+ )
37
+ else
38
+ nil
39
+ end
40
+ end
41
+
42
+ def setup_ai_enhanced_rules
43
+ # Rule 1: AI-Powered Sentiment Analysis
44
+ sentiment_rule = KBS::Rule.new(
45
+ "ai_sentiment_analysis",
46
+ conditions: [
47
+ KBS::Condition.new(:news_data, {
48
+ symbol: ->(s) { s && s.length > 0 },
49
+ headline: ->(h) { h && h.length > 10 },
50
+ content: ->(c) { c && c.length > 50 }
51
+ })
52
+ ],
53
+ action: lambda do |facts, bindings|
54
+ news = facts.find { |f| f.type == :news_data }
55
+ symbol = news[:symbol]
56
+
57
+ # AI-powered sentiment analysis
58
+ sentiment = analyze_sentiment_with_ai(news[:headline], news[:content])
59
+
60
+ puts "🤖 AI SENTIMENT ANALYSIS: #{symbol}"
61
+ puts " Headline: #{news[:headline][0..80]}..."
62
+ puts " AI Sentiment: #{sentiment[:sentiment]} (#{sentiment[:confidence]}%)"
63
+ puts " Key Themes: #{sentiment[:themes].join(', ')}"
64
+ puts " Market Impact: #{sentiment[:market_impact]}"
65
+
66
+ # Add sentiment fact to working memory
67
+ @engine.add_fact(:ai_sentiment, {
68
+ symbol: symbol,
69
+ sentiment_score: sentiment[:score],
70
+ confidence: sentiment[:confidence],
71
+ themes: sentiment[:themes],
72
+ market_impact: sentiment[:market_impact],
73
+ timestamp: Time.now
74
+ })
75
+ end,
76
+ priority: 20
77
+ )
78
+
79
+ # Rule 2: AI-Generated Trading Strategy
80
+ ai_strategy_rule = KBS::Rule.new(
81
+ "ai_strategy_generation",
82
+ conditions: [
83
+ KBS::Condition.new(:market_conditions, {
84
+ volatility: ->(v) { v && v > 25 },
85
+ trend: ->(t) { t && t.length > 0 }
86
+ }),
87
+ KBS::Condition.new(:portfolio_state, {
88
+ cash_ratio: ->(c) { c && c > 0.2 }
89
+ })
90
+ ],
91
+ action: lambda do |facts, bindings|
92
+ market = facts.find { |f| f.type == :market_conditions }
93
+ portfolio = facts.find { |f| f.type == :portfolio_state }
94
+
95
+ # Generate AI strategy
96
+ strategy = generate_ai_trading_strategy(market, portfolio)
97
+
98
+ puts "🧠 AI TRADING STRATEGY"
99
+ puts " Market Context: #{market[:trend]} trend, #{market[:volatility]}% volatility"
100
+ puts " Strategy: #{strategy[:name]}"
101
+ puts " Rationale: #{strategy[:rationale]}"
102
+ puts " Actions: #{strategy[:actions].join(', ')}"
103
+ puts " Risk Level: #{strategy[:risk_level]}"
104
+
105
+ # Execute AI-suggested actions (would be implemented in production)
106
+ # execute_ai_strategy(strategy)
107
+ end,
108
+ priority: 15
109
+ )
110
+
111
+ # Rule 3: Dynamic Rule Generation
112
+ dynamic_rule_creation = KBS::Rule.new(
113
+ "dynamic_rule_creation",
114
+ conditions: [
115
+ KBS::Condition.new(:pattern_anomaly, {
116
+ pattern_type: ->(p) { p && p.length > 0 },
117
+ confidence: ->(c) { c && c > 0.8 },
118
+ occurrences: ->(o) { o && o > 5 }
119
+ })
120
+ ],
121
+ action: lambda do |facts, bindings|
122
+ anomaly = facts.find { |f| f.type == :pattern_anomaly }
123
+
124
+ # AI generates new trading rule
125
+ new_rule_spec = generate_rule_with_ai(anomaly)
126
+
127
+ puts "🎯 AI RULE GENERATION"
128
+ puts " Pattern: #{anomaly[:pattern_type]}"
129
+ puts " New Rule: #{new_rule_spec[:name]}"
130
+ puts " Logic: #{new_rule_spec[:description]}"
131
+
132
+ # Dynamically add new rule to engine
133
+ if new_rule_spec[:valid]
134
+ dynamic_rule = create_rule_from_spec(new_rule_spec)
135
+ @engine.add_rule(dynamic_rule)
136
+ puts " ✅ Rule added to knowledge base"
137
+ end
138
+ end,
139
+ priority: 12
140
+ )
141
+
142
+ # Rule 4: AI Risk Assessment
143
+ ai_risk_assessment = KBS::Rule.new(
144
+ "ai_risk_assessment",
145
+ conditions: [
146
+ KBS::Condition.new(:position, {
147
+ unrealized_pnl: ->(pnl) { pnl && pnl.abs > 1000 }
148
+ }),
149
+ KBS::Condition.new(:market_data, {
150
+ symbol: ->(s) { s && s.length > 0 }
151
+ })
152
+ ],
153
+ action: lambda do |facts, bindings|
154
+ position = facts.find { |f| f.type == :position }
155
+ market_data = facts.find { |f| f.type == :market_data }
156
+
157
+ # AI-powered risk analysis
158
+ risk_analysis = analyze_position_risk_with_ai(position, market_data)
159
+
160
+ puts "⚠️ AI RISK ASSESSMENT: #{position[:symbol]}"
161
+ puts " Current P&L: $#{position[:unrealized_pnl]}"
162
+ puts " Risk Level: #{risk_analysis[:risk_level]}"
163
+ puts " Key Risks: #{risk_analysis[:risks].join(', ')}"
164
+ puts " Recommendation: #{risk_analysis[:recommendation]}"
165
+ puts " Confidence: #{risk_analysis[:confidence]}%"
166
+
167
+ # Act on high-risk situations
168
+ if risk_analysis[:risk_level] == "HIGH" && risk_analysis[:confidence] > 80
169
+ puts " 🚨 HIGH RISK DETECTED - Consider position adjustment"
170
+ end
171
+ end,
172
+ priority: 18
173
+ )
174
+
175
+ # Rule 5: Natural Language Explanation Generator
176
+ explanation_rule = KBS::Rule.new(
177
+ "ai_explanation_generator",
178
+ conditions: [
179
+ KBS::Condition.new(:trade_recommendation, {
180
+ action: ->(a) { ["BUY", "SELL", "HOLD"].include?(a) },
181
+ symbol: ->(s) { s && s.length > 0 }
182
+ })
183
+ ],
184
+ action: lambda do |facts, bindings|
185
+ recommendation = facts.find { |f| f.type == :trade_recommendation }
186
+
187
+ # Generate natural language explanation
188
+ explanation = generate_trade_explanation(recommendation, facts)
189
+
190
+ puts "💬 AI EXPLANATION: #{recommendation[:symbol]} #{recommendation[:action]}"
191
+ puts " Reasoning: #{explanation[:reasoning]}"
192
+ puts " Context: #{explanation[:context]}"
193
+ puts " Confidence: #{explanation[:confidence]}%"
194
+ puts " Alternative View: #{explanation[:alternative]}"
195
+ end,
196
+ priority: 5
197
+ )
198
+
199
+ # Rule 6: AI Pattern Recognition
200
+ pattern_recognition_rule = KBS::Rule.new(
201
+ "ai_pattern_recognition",
202
+ conditions: [
203
+ KBS::Condition.new(:price_history, {
204
+ symbol: ->(s) { s && s.length > 0 },
205
+ data_points: ->(d) { d && d.length >= 30 }
206
+ })
207
+ ],
208
+ action: lambda do |facts, bindings|
209
+ price_data = facts.find { |f| f.type == :price_history }
210
+
211
+ # AI identifies patterns
212
+ patterns = identify_patterns_with_ai(price_data[:data_points])
213
+
214
+ if patterns.any?
215
+ puts "📊 AI PATTERN RECOGNITION: #{price_data[:symbol]}"
216
+ patterns.each do |pattern|
217
+ puts " Pattern: #{pattern[:name]} (#{pattern[:confidence]}%)"
218
+ puts " Prediction: #{pattern[:prediction]}"
219
+ puts " Time Horizon: #{pattern[:time_horizon]}"
220
+ end
221
+ end
222
+ end,
223
+ priority: 10
224
+ )
225
+
226
+ @engine.add_rule(sentiment_rule)
227
+ @engine.add_rule(ai_strategy_rule)
228
+ @engine.add_rule(dynamic_rule_creation)
229
+ @engine.add_rule(ai_risk_assessment)
230
+ @engine.add_rule(explanation_rule)
231
+ @engine.add_rule(pattern_recognition_rule)
232
+ end
233
+
234
+ def analyze_sentiment_with_ai(headline, content)
235
+ cache_key = "#{headline[0..50]}_#{content[0..100]}".hash
236
+ return @sentiment_cache[cache_key] if @sentiment_cache[cache_key]
237
+
238
+ prompt = build_sentiment_prompt(headline, content)
239
+ puts "\n🔗 Calling RubyLLM for sentiment analysis..."
240
+
241
+ message = @ai_client.ask(prompt)
242
+ response_text = message.content.to_s
243
+ puts "✅ Got response from Ollama (#{response_text.length} chars)"
244
+ puts "📝 Response: #{response_text[0..200]}..." if response_text.length > 200
245
+
246
+ result = parse_sentiment_response(response_text)
247
+
248
+ @sentiment_cache[cache_key] = result
249
+ result
250
+ end
251
+
252
+ def generate_ai_trading_strategy(market_conditions, portfolio_state)
253
+ prompt = build_strategy_prompt(market_conditions, portfolio_state)
254
+ puts "\n🔗 Calling RubyLLM for trading strategy..."
255
+
256
+ message = @ai_client.ask(prompt)
257
+ response_text = message.content.to_s
258
+ puts "✅ Got response from Ollama (#{response_text.length} chars)"
259
+ puts "📝 Response: #{response_text[0..200]}..." if response_text.length > 200
260
+
261
+ parse_strategy_response(response_text)
262
+ end
263
+
264
+ def generate_rule_with_ai(anomaly_data)
265
+ prompt = build_rule_generation_prompt(anomaly_data)
266
+ puts "\n🔗 Calling RubyLLM for rule generation..."
267
+
268
+ message = @ai_client.ask(prompt)
269
+ response_text = message.content.to_s
270
+ puts "✅ Got response from Ollama (#{response_text.length} chars)"
271
+ puts "📝 Response: #{response_text[0..200]}..." if response_text.length > 200
272
+
273
+ parse_rule_specification(response_text)
274
+ end
275
+
276
+ def analyze_position_risk_with_ai(position, market_data)
277
+ prompt = build_risk_analysis_prompt(position, market_data)
278
+ puts "\n🔗 Calling RubyLLM for risk analysis..."
279
+
280
+ message = @ai_client.ask(prompt)
281
+ response_text = message.content.to_s
282
+ puts "✅ Got response from Ollama (#{response_text.length} chars)"
283
+ puts "📝 Response: #{response_text[0..200]}..." if response_text.length > 200
284
+
285
+ parse_risk_analysis(response_text)
286
+ end
287
+
288
+ def generate_trade_explanation(recommendation, context_facts)
289
+ cache_key = "#{recommendation[:symbol]}_#{recommendation[:action]}_#{context_facts.length}".hash
290
+ return @explanation_cache[cache_key] if @explanation_cache[cache_key]
291
+
292
+ prompt = build_explanation_prompt(recommendation, context_facts)
293
+ puts "\n🔗 Calling RubyLLM for trade explanation..."
294
+
295
+ message = @ai_client.ask(prompt)
296
+ response_text = message.content.to_s
297
+ puts "✅ Got response from Ollama (#{response_text.length} chars)"
298
+ puts "📝 Response: #{response_text[0..200]}..." if response_text.length > 200
299
+
300
+ result = parse_explanation_response(response_text)
301
+
302
+ @explanation_cache[cache_key] = result
303
+ result
304
+ end
305
+
306
+ def identify_patterns_with_ai(price_data)
307
+ prompt = build_pattern_recognition_prompt(price_data)
308
+ puts "\n🔗 Calling RubyLLM for pattern recognition..."
309
+
310
+ message = @ai_client.ask(prompt)
311
+ response_text = message.content.to_s
312
+ puts "✅ Got response from Ollama (#{response_text.length} chars)"
313
+ puts "📝 Response: #{response_text[0..200]}..." if response_text.length > 200
314
+
315
+ parse_pattern_response(response_text)
316
+ end
317
+
318
+ # Prompt builders
319
+ def build_sentiment_prompt(headline, content)
320
+ <<~PROMPT
321
+ Analyze the sentiment of this financial news for trading implications:
322
+
323
+ Headline: #{headline}
324
+ Content: #{content[0..500]}...
325
+
326
+ Provide a JSON response with:
327
+ {
328
+ "sentiment": "positive|negative|neutral",
329
+ "score": -1.0 to 1.0,
330
+ "confidence": 0-100,
331
+ "themes": ["theme1", "theme2"],
332
+ "market_impact": "bullish|bearish|neutral"
333
+ }
334
+ PROMPT
335
+ end
336
+
337
+ def build_strategy_prompt(market_conditions, portfolio_state)
338
+ <<~PROMPT
339
+ Generate a trading strategy for these conditions:
340
+
341
+ Market: #{market_conditions[:trend]} trend, #{market_conditions[:volatility]}% volatility
342
+ Portfolio: #{(portfolio_state[:cash_ratio] * 100).round(1)}% cash
343
+
344
+ Provide a JSON strategy with:
345
+ {
346
+ "name": "strategy_name",
347
+ "rationale": "why this strategy fits",
348
+ "actions": ["action1", "action2"],
349
+ "risk_level": "LOW|MEDIUM|HIGH"
350
+ }
351
+ PROMPT
352
+ end
353
+
354
+ def build_risk_analysis_prompt(position, market_data)
355
+ <<~PROMPT
356
+ Analyze the risk of this trading position:
357
+
358
+ Position: #{position[:symbol]}, P&L: $#{position[:unrealized_pnl]}
359
+ Market Data: #{market_data.to_json}
360
+
361
+ Provide risk assessment as JSON:
362
+ {
363
+ "risk_level": "LOW|MEDIUM|HIGH",
364
+ "risks": ["risk1", "risk2"],
365
+ "recommendation": "hold|reduce|exit",
366
+ "confidence": 0-100
367
+ }
368
+ PROMPT
369
+ end
370
+
371
+ def build_explanation_prompt(recommendation, context_facts)
372
+ <<~PROMPT
373
+ Explain this trading recommendation in simple terms:
374
+
375
+ Recommendation: #{recommendation[:action]} #{recommendation[:symbol]}
376
+ Context: #{context_facts.length} supporting facts
377
+
378
+ Provide explanation as JSON:
379
+ {
380
+ "explanation": "clear explanation",
381
+ "reasoning": "why this makes sense",
382
+ "risks": ["risk1", "risk2"]
383
+ }
384
+ PROMPT
385
+ end
386
+
387
+ def build_pattern_recognition_prompt(price_data)
388
+ <<~PROMPT
389
+ Identify trading patterns in this price data:
390
+
391
+ Data: #{price_data.to_json}
392
+
393
+ Return JSON array of patterns:
394
+ [
395
+ {
396
+ "pattern": "pattern_name",
397
+ "confidence": 0-100,
398
+ "description": "what this means"
399
+ }
400
+ ]
401
+ PROMPT
402
+ end
403
+
404
+ # Response parsers
405
+ def extract_json(response)
406
+ # Strip markdown code fences if present
407
+ json_text = response.strip
408
+ json_text = json_text.gsub(/^```json\s*/, '').gsub(/```\s*$/, '').strip
409
+ json_text
410
+ end
411
+
412
+ def parse_sentiment_response(response)
413
+ JSON.parse(extract_json(response), symbolize_names: true)
414
+ end
415
+
416
+ def parse_strategy_response(response)
417
+ JSON.parse(extract_json(response), symbolize_names: true)
418
+ end
419
+
420
+ def parse_explanation_response(response)
421
+ JSON.parse(extract_json(response), symbolize_names: true)
422
+ end
423
+
424
+ def parse_pattern_response(response)
425
+ JSON.parse(extract_json(response), symbolize_names: true)
426
+ end
427
+
428
+ def parse_risk_analysis(response)
429
+ JSON.parse(extract_json(response), symbolize_names: true)
430
+ end
431
+
432
+ def demonstrate_ai_enhancements
433
+ puts "🤖 AI-ENHANCED KNOWLEDGE-BASED SYSTEM"
434
+ puts "=" * 70
435
+ puts "Integrating #{@ai_client.class.name} and #{@mcp_agent.class.name}"
436
+ puts "=" * 70
437
+
438
+ # Scenario 1: AI Sentiment Analysis
439
+ puts "\n📰 SCENARIO 1: AI-Powered News Sentiment"
440
+ puts "-" * 50
441
+ @engine.working_memory.facts.clear
442
+
443
+ @engine.add_fact(:news_data, {
444
+ symbol: "AAPL",
445
+ headline: "Apple Reports Record Q4 Earnings, Beats Expectations by 15%",
446
+ 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.",
447
+ published_at: Time.now
448
+ })
449
+ @engine.run
450
+
451
+ # Scenario 2: AI Strategy Generation
452
+ puts "\n🧠 SCENARIO 2: AI Trading Strategy Generation"
453
+ puts "-" * 50
454
+ @engine.working_memory.facts.clear
455
+
456
+ @engine.add_fact(:market_conditions, {
457
+ volatility: 28.5,
458
+ trend: "sideways",
459
+ sector_rotation: "technology_to_healthcare"
460
+ })
461
+
462
+ @engine.add_fact(:portfolio_state, {
463
+ cash_ratio: 0.25,
464
+ largest_position: "AAPL",
465
+ sector_concentration: 0.45
466
+ })
467
+ @engine.run
468
+
469
+ # Scenario 3: AI Risk Assessment
470
+ puts "\n⚠️ SCENARIO 3: AI Risk Assessment"
471
+ puts "-" * 50
472
+ @engine.working_memory.facts.clear
473
+
474
+ @engine.add_fact(:position, {
475
+ symbol: "TSLA",
476
+ shares: 100,
477
+ entry_price: 250.00,
478
+ current_price: 235.00,
479
+ unrealized_pnl: -1500
480
+ })
481
+
482
+ @engine.add_fact(:market_data, {
483
+ symbol: "TSLA",
484
+ volatility: 45.2,
485
+ beta: 2.1,
486
+ sector: "Consumer Discretionary"
487
+ })
488
+ @engine.run
489
+
490
+ # Scenario 4: Trade Explanation
491
+ puts "\n💬 SCENARIO 4: AI Trade Explanation"
492
+ puts "-" * 50
493
+ @engine.working_memory.facts.clear
494
+
495
+ @engine.add_fact(:trade_recommendation, {
496
+ symbol: "GOOGL",
497
+ action: "BUY",
498
+ quantity: 50,
499
+ confidence: 85
500
+ })
501
+
502
+ @engine.add_fact(:technical_analysis, {
503
+ symbol: "GOOGL",
504
+ rsi: 35,
505
+ moving_average_signal: "golden_cross",
506
+ volume_trend: "increasing"
507
+ })
508
+ @engine.run
509
+
510
+ puts "\n" + "=" * 70
511
+ puts "AI ENHANCEMENT DEMONSTRATION COMPLETE"
512
+ puts "🎯 The system now combines rule-based logic with AI insights"
513
+ puts "🧠 Dynamic pattern recognition and strategy generation"
514
+ puts "💬 Natural language explanations for all decisions"
515
+ puts "⚡ Real-time sentiment analysis and risk assessment"
516
+ end
517
+ end
518
+ end
519
+
520
+ if __FILE__ == $0
521
+ system = AIEnhancedKBS::AIKnowledgeSystem.new
522
+ system.demonstrate_ai_enhancements
523
+ 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"
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/kbs'
4
+
5
+ engine = KBS::ReteEngine.new
6
+
7
+ puts "Creating a simple expert system for diagnosing car problems..."
8
+ puts "-" * 60
9
+
10
+ rule1 = KBS::Rule.new(
11
+ "dead_battery",
12
+ conditions: [
13
+ KBS::Condition.new(:symptom, { problem: "won't start" }),
14
+ KBS::Condition.new(:symptom, { problem: "no lights" })
15
+ ],
16
+ action: lambda do |facts, bindings|
17
+ puts "DIAGNOSIS: Dead battery - The car won't start and has no lights"
18
+ puts "RECOMMENDATION: Jump start the battery or replace it"
19
+ end
20
+ )
21
+
22
+ rule2 = KBS::Rule.new(
23
+ "flat_tire",
24
+ conditions: [
25
+ KBS::Condition.new(:symptom, { problem: "pulling to side" }),
26
+ KBS::Condition.new(:symptom, { problem: "low tire pressure" })
27
+ ],
28
+ action: lambda do |facts, bindings|
29
+ puts "DIAGNOSIS: Flat or low tire"
30
+ puts "RECOMMENDATION: Check tire pressure and inflate or replace tire"
31
+ end
32
+ )
33
+
34
+ rule3 = KBS::Rule.new(
35
+ "overheating",
36
+ conditions: [
37
+ KBS::Condition.new(:symptom, { problem: "high temperature" }),
38
+ KBS::Condition.new(:symptom, { problem: "steam from hood" }, negated: false),
39
+ KBS::Condition.new(:symptom, { problem: "coolant leak" }, negated: true)
40
+ ],
41
+ action: lambda do |facts, bindings|
42
+ puts "DIAGNOSIS: Engine overheating (no coolant leak detected)"
43
+ puts "RECOMMENDATION: Check radiator and cooling system"
44
+ end
45
+ )
46
+
47
+ engine.add_rule(rule1)
48
+ engine.add_rule(rule2)
49
+ engine.add_rule(rule3)
50
+
51
+ puts "\nAdding symptoms..."
52
+ engine.add_fact(:symptom, { problem: "won't start", severity: "high" })
53
+ engine.add_fact(:symptom, { problem: "no lights", severity: "high" })
54
+
55
+ puts "\nRunning inference engine..."
56
+ engine.run
57
+
58
+ puts "\n" + "-" * 60
59
+ puts "\nAdding more symptoms..."
60
+ engine.add_fact(:symptom, { problem: "high temperature", severity: "critical" })
61
+ engine.add_fact(:symptom, { problem: "steam from hood", severity: "high" })
62
+
63
+ puts "\nRunning inference engine again..."
64
+ engine.run