kbs 0.0.1 → 0.2.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 (93) 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 +291 -362
  5. data/docs/advanced/custom-persistence.md +775 -0
  6. data/docs/advanced/debugging.md +726 -0
  7. data/docs/advanced/index.md +8 -0
  8. data/docs/advanced/performance.md +865 -0
  9. data/docs/advanced/testing.md +827 -0
  10. data/docs/api/blackboard.md +1157 -0
  11. data/docs/api/engine.md +1047 -0
  12. data/docs/api/facts.md +1212 -0
  13. data/docs/api/index.md +12 -0
  14. data/docs/api/rules.md +1104 -0
  15. data/docs/architecture/blackboard.md +544 -0
  16. data/docs/architecture/index.md +277 -0
  17. data/docs/architecture/network-structure.md +343 -0
  18. data/docs/architecture/rete-algorithm.md +737 -0
  19. data/docs/assets/css/custom.css +83 -0
  20. data/docs/assets/images/blackboard-architecture.svg +136 -0
  21. data/docs/assets/images/compiled-network.svg +101 -0
  22. data/docs/assets/images/fact-assertion-flow.svg +117 -0
  23. data/docs/assets/images/fact-rule-relationship.svg +65 -0
  24. data/docs/assets/images/fact-structure.svg +42 -0
  25. data/docs/assets/images/inference-cycle.svg +47 -0
  26. data/docs/assets/images/kb-components.svg +43 -0
  27. data/docs/assets/images/kbs.jpg +0 -0
  28. data/docs/assets/images/pattern-matching-trace.svg +136 -0
  29. data/docs/assets/images/rete-network-layers.svg +96 -0
  30. data/docs/assets/images/rule-structure.svg +44 -0
  31. data/docs/assets/images/system-layers.svg +69 -0
  32. data/docs/assets/images/trading-signal-network.svg +139 -0
  33. data/docs/assets/js/mathjax.js +17 -0
  34. data/docs/examples/index.md +223 -0
  35. data/docs/guides/blackboard-memory.md +589 -0
  36. data/docs/guides/dsl.md +1321 -0
  37. data/docs/guides/facts.md +652 -0
  38. data/docs/guides/getting-started.md +385 -0
  39. data/docs/guides/index.md +23 -0
  40. data/docs/guides/negation.md +529 -0
  41. data/docs/guides/pattern-matching.md +561 -0
  42. data/docs/guides/persistence.md +451 -0
  43. data/docs/guides/variable-binding.md +491 -0
  44. data/docs/guides/writing-rules.md +914 -0
  45. data/docs/index.md +155 -0
  46. data/docs/installation.md +156 -0
  47. data/docs/quick-start.md +221 -0
  48. data/docs/what-is-a-fact.md +694 -0
  49. data/docs/what-is-a-knowledge-base.md +350 -0
  50. data/docs/what-is-a-rule.md +833 -0
  51. data/examples/.gitignore +1 -0
  52. data/examples/README.md +2 -2
  53. data/examples/advanced_example.rb +2 -2
  54. data/examples/advanced_example_dsl.rb +224 -0
  55. data/examples/ai_enhanced_kbs.rb +1 -1
  56. data/examples/ai_enhanced_kbs_dsl.rb +538 -0
  57. data/examples/blackboard_demo_dsl.rb +50 -0
  58. data/examples/car_diagnostic.rb +1 -1
  59. data/examples/car_diagnostic_dsl.rb +54 -0
  60. data/examples/concurrent_inference_demo.rb +5 -6
  61. data/examples/concurrent_inference_demo_dsl.rb +362 -0
  62. data/examples/csv_trading_system.rb +1 -1
  63. data/examples/csv_trading_system_dsl.rb +525 -0
  64. data/examples/iot_demo_using_dsl.rb +1 -1
  65. data/examples/portfolio_rebalancing_system.rb +2 -2
  66. data/examples/portfolio_rebalancing_system_dsl.rb +613 -0
  67. data/examples/redis_trading_demo_dsl.rb +177 -0
  68. data/examples/rule_source_demo.rb +123 -0
  69. data/examples/run_all.rb +50 -0
  70. data/examples/run_all_dsl.rb +49 -0
  71. data/examples/stock_trading_advanced.rb +1 -1
  72. data/examples/stock_trading_advanced_dsl.rb +404 -0
  73. data/examples/temp_dsl.txt +9392 -0
  74. data/examples/timestamped_trading.rb +1 -1
  75. data/examples/timestamped_trading_dsl.rb +258 -0
  76. data/examples/trading_demo.rb +1 -1
  77. data/examples/trading_demo_dsl.rb +322 -0
  78. data/examples/working_demo.rb +1 -1
  79. data/examples/working_demo_dsl.rb +160 -0
  80. data/lib/kbs/blackboard/engine.rb +3 -3
  81. data/lib/kbs/blackboard/fact.rb +1 -1
  82. data/lib/kbs/condition.rb +1 -1
  83. data/lib/kbs/decompiler.rb +204 -0
  84. data/lib/kbs/dsl/knowledge_base.rb +101 -2
  85. data/lib/kbs/dsl/variable.rb +1 -1
  86. data/lib/kbs/dsl.rb +3 -1
  87. data/lib/kbs/{rete_engine.rb → engine.rb} +42 -1
  88. data/lib/kbs/fact.rb +1 -1
  89. data/lib/kbs/version.rb +1 -1
  90. data/lib/kbs.rb +15 -13
  91. data/mkdocs.yml +181 -0
  92. metadata +74 -9
  93. data/examples/stock_trading_system.rb.bak +0 -563
@@ -0,0 +1,385 @@
1
+ # Getting Started
2
+
3
+ Build your first knowledge-based system in 10 minutes. This hands-on tutorial teaches the fundamentals by creating a temperature monitoring system that alerts when sensors exceed thresholds.
4
+
5
+ ## What You'll Learn
6
+
7
+ - Creating rules and facts
8
+ - Variable binding across conditions
9
+ - Using negation to prevent duplicate alerts
10
+ - Persisting facts with blackboard memory
11
+ - Controlling rule execution with priorities
12
+
13
+ ## Installation
14
+
15
+ Add KBS to your Gemfile:
16
+
17
+ ```ruby
18
+ gem 'kbs'
19
+ ```
20
+
21
+ Or install directly:
22
+
23
+ ```bash
24
+ gem install kbs
25
+ ```
26
+
27
+ ## Your First Rule
28
+
29
+ Let's create a simple rule that fires when temperature exceeds a threshold.
30
+
31
+ ### Step 1: Create a Knowledge Base
32
+
33
+ ```ruby
34
+ require 'kbs'
35
+
36
+ # Create a knowledge base with DSL
37
+ kb = KBS.knowledge_base do
38
+ # Rules will be defined here
39
+ end
40
+ ```
41
+
42
+ The knowledge base manages rules, facts, and executes the pattern matching algorithm.
43
+
44
+ ### Step 2: Define a Rule
45
+
46
+ ```ruby
47
+ kb = KBS.knowledge_base do
48
+ # Define a rule for high temperature alerts
49
+ rule "high_temperature_alert" do
50
+ on :sensor, id: :sensor_id?, temp: :temp?
51
+ on :threshold, id: :sensor_id?, max: :max?
52
+
53
+ perform do |facts, bindings|
54
+ if bindings[:temp?] > bindings[:max?]
55
+ puts "🚨 ALERT: Sensor #{bindings[:sensor_id?]} at #{bindings[:temp?]}°C"
56
+ end
57
+ end
58
+ end
59
+ end
60
+ ```
61
+
62
+ **What this rule does:**
63
+
64
+ - **Condition 1**: Match any `:sensor` fact, binding its `id` to `:sensor_id?` and `temp` to `:temp?`
65
+ - **Condition 2**: Match a `:threshold` fact with the same `id`, binding `max` to `:max?`
66
+ - **Action**: When both conditions match, compare temperature against threshold
67
+
68
+ **Variable binding** (`:sensor_id?`) ensures we only compare sensors with their own thresholds.
69
+
70
+ ### Step 3: Add Facts and Run
71
+
72
+ ```ruby
73
+ kb = KBS.knowledge_base do
74
+ rule "high_temperature_alert" do
75
+ on :sensor, id: :sensor_id?, temp: :temp?
76
+ on :threshold, id: :sensor_id?, max: :max?
77
+
78
+ perform do |facts, bindings|
79
+ if bindings[:temp?] > bindings[:max?]
80
+ puts "🚨 ALERT: Sensor #{bindings[:sensor_id?]} at #{bindings[:temp?]}°C"
81
+ end
82
+ end
83
+ end
84
+
85
+ # Add facts
86
+ fact :sensor, id: "bedroom", temp: 28
87
+ fact :threshold, id: "bedroom", max: 25
88
+
89
+ # Run inference
90
+ run
91
+ end
92
+ ```
93
+
94
+ Facts are observations about the world. The knowledge base automatically matches them against rule conditions.
95
+
96
+ **Output:**
97
+ ```
98
+ 🚨 ALERT: Sensor bedroom at 28°C
99
+ ```
100
+
101
+ The rule fired because the bedroom temperature (28°C) exceeds its threshold (25°C).
102
+
103
+ ## Understanding Variable Binding
104
+
105
+ Variable binding connects facts across conditions. Here's how it works:
106
+
107
+ ```ruby
108
+ rule "example" do
109
+ on :sensor, id: :sensor_id?, temp: :temp?
110
+ on :threshold, id: :sensor_id?, max: :max?
111
+ end
112
+ ```
113
+
114
+ **Binding Process:**
115
+
116
+ 1. Engine finds a `:sensor` fact: `{ id: "bedroom", temp: 28 }`
117
+ 2. Binds `:sensor_id?` → `"bedroom"`, `:temp?` → `28`
118
+ 3. Searches for `:threshold` fact where `id` also equals `"bedroom"`
119
+ 4. Finds `{ id: "bedroom", max: 25 }`
120
+ 5. Binds `:max?` → `25`
121
+ 6. Both conditions satisfied → rule fires with bindings: `{ :sensor_id? => "bedroom", :temp? => 28, :max? => 25 }`
122
+
123
+ Without variable binding, the rule would incorrectly match bedroom sensors with kitchen thresholds.
124
+
125
+ ## Preventing Duplicate Alerts with Negation
126
+
127
+ Let's prevent the same alert from firing repeatedly:
128
+
129
+ ```ruby
130
+ kb = KBS.knowledge_base do
131
+ rule "smart_temperature_alert" do
132
+ on :sensor, id: :sensor_id?, temp: :temp?
133
+ on :threshold, id: :sensor_id?, max: :max?
134
+ # Only fire if no alert already exists for this sensor
135
+ without :alert, sensor_id: :sensor_id?
136
+
137
+ perform do |facts, bindings|
138
+ if bindings[:temp?] > bindings[:max?]
139
+ puts "🚨 ALERT: Sensor #{bindings[:sensor_id?]} at #{bindings[:temp?]}°C"
140
+ # Record that we sent this alert
141
+ fact :alert, sensor_id: bindings[:sensor_id?]
142
+ end
143
+ end
144
+ end
145
+ end
146
+ ```
147
+
148
+ **Negated condition** (`negated: true`): Rule fires only when NO `:alert` fact exists for this sensor.
149
+
150
+ **Flow:**
151
+
152
+ 1. First execution: No `:alert` fact → rule fires, creates `:alert` fact
153
+ 2. Second execution: `:alert` fact exists → rule doesn't fire (negation blocks it)
154
+
155
+ ## Persisting Facts with Blackboard Memory
156
+
157
+ So far, facts disappear when your program exits. Use blackboard memory for persistence:
158
+
159
+ ```ruby
160
+ require 'kbs'
161
+
162
+ # Create engine with SQLite persistence
163
+ engine = KBS::Blackboard::Engine.new(db_path: 'sensors.db')
164
+
165
+ # Add rules (same as before)
166
+ engine.add_rule(smart_alert_rule)
167
+
168
+ # Add facts - these are saved to database
169
+ engine.add_fact(:sensor, id: "bedroom", temp: 28)
170
+ engine.add_fact(:threshold, id: "bedroom", max: 25)
171
+
172
+ engine.run
173
+
174
+ # Facts survive program restart
175
+ engine.close
176
+ ```
177
+
178
+ **Next time you run:**
179
+
180
+ ```ruby
181
+ engine = KBS::Blackboard::Engine.new(db_path: 'sensors.db')
182
+ # Facts automatically loaded from database
183
+ puts engine.facts.size # => 2 (sensor + threshold)
184
+ ```
185
+
186
+ Blackboard provides:
187
+ - **Persistence**: Facts saved to SQLite/Redis
188
+ - **Audit Trail**: Complete history of changes
189
+ - **Transactions**: ACID guarantees for multi-fact updates
190
+
191
+ Learn more: [Blackboard Memory Guide](blackboard-memory.md)
192
+
193
+ ## Controlling Execution with Priorities
194
+
195
+ When multiple rules match, control firing order with priorities:
196
+
197
+ ```ruby
198
+ kb = KBS.knowledge_base do
199
+ rule "critical_alert", priority: 100 do
200
+ on :sensor, temp: :temp?
201
+
202
+ perform do |facts, bindings|
203
+ if bindings[:temp?] > 50
204
+ puts "🔥 CRITICAL: Immediate shutdown required!"
205
+ exit(1)
206
+ end
207
+ end
208
+ end
209
+
210
+ rule "normal_alert", priority: 10 do
211
+ on :sensor, temp: :temp?
212
+ # ... (less urgent alerts)
213
+ perform { |facts| puts "Normal alert" }
214
+ end
215
+ end
216
+ ```
217
+
218
+ **Priority:** Higher numbers fire first. Default is `0`.
219
+
220
+ **Execution order:**
221
+ 1. `critical_alert` (priority 100) - checks for emergency shutdown
222
+ 2. `normal_alert` (priority 10) - handles routine alerts
223
+
224
+ ## Complete Working Example
225
+
226
+ Here's a complete temperature monitoring system:
227
+
228
+ ```ruby
229
+ require 'kbs'
230
+
231
+ class TemperatureMonitor
232
+ def initialize
233
+ @engine = KBS::Blackboard::Engine.new(db_path: 'sensors.db')
234
+ @kb = setup_rules
235
+ end
236
+
237
+ def setup_rules
238
+ engine = @engine
239
+ monitor = self
240
+
241
+ KBS.knowledge_base(engine: engine) do
242
+ # Rule 1: Send alert when temp exceeds threshold
243
+ rule "temperature_alert", priority: 50 do
244
+ on :sensor, id: :id?, temp: :temp?
245
+ on :threshold, id: :id?, max: :max?
246
+ without :alert, sensor_id: :id?
247
+
248
+ perform do |facts, bindings|
249
+ if bindings[:temp?] > bindings[:max?]
250
+ monitor.send_alert(bindings[:id?], bindings[:temp?], bindings[:max?])
251
+ fact :alert, sensor_id: bindings[:id?]
252
+ end
253
+ end
254
+ end
255
+
256
+ # Rule 2: Clear alert when temp drops below threshold
257
+ rule "clear_alert", priority: 40 do
258
+ on :sensor, id: :id?, temp: :temp?
259
+ on :threshold, id: :id?, max: :max?
260
+ on :alert, sensor_id: :id?
261
+
262
+ perform do |facts, bindings|
263
+ if bindings[:temp?] <= bindings[:max?]
264
+ monitor.clear_alert(bindings[:id?])
265
+ # Find and retract the alert fact
266
+ alert_fact = query(:alert, sensor_id: bindings[:id?]).first
267
+ retract alert_fact if alert_fact
268
+ end
269
+ end
270
+ end
271
+
272
+ # Rule 3: Emergency shutdown for extreme temps
273
+ rule "emergency_shutdown", priority: 100 do
274
+ on :sensor, temp: :temp?
275
+
276
+ perform do |facts, bindings|
277
+ if bindings[:temp?] > 60
278
+ monitor.emergency_shutdown(bindings[:temp?])
279
+ end
280
+ end
281
+ end
282
+ end
283
+ end
284
+
285
+ def add_sensor(id, max_temp)
286
+ @kb.fact :threshold, id: id, max: max_temp
287
+ end
288
+
289
+ def update_reading(id, temp)
290
+ # Find and remove old reading
291
+ old = @kb.query(:sensor, id: id).first
292
+ @kb.retract old if old
293
+
294
+ # Add new reading
295
+ @kb.fact :sensor, id: id, temp: temp
296
+ @kb.run
297
+ end
298
+
299
+ def send_alert(sensor_id, temp, threshold)
300
+ puts "🚨 ALERT: #{sensor_id} at #{temp}°C (threshold: #{threshold}°C)"
301
+ end
302
+
303
+ def clear_alert(sensor_id)
304
+ puts "✅ CLEAR: #{sensor_id} back to normal"
305
+ end
306
+
307
+ def emergency_shutdown(temp)
308
+ puts "🔥 EMERGENCY SHUTDOWN: Temperature #{temp}°C!"
309
+ exit(1)
310
+ end
311
+
312
+ def close
313
+ @engine.close
314
+ end
315
+ end
316
+
317
+ # Usage
318
+ monitor = TemperatureMonitor.new
319
+
320
+ # Register sensors with thresholds
321
+ monitor.add_sensor("bedroom", 25)
322
+ monitor.add_sensor("server_room", 30)
323
+
324
+ # Simulate sensor readings
325
+ monitor.update_reading("bedroom", 28) # => 🚨 ALERT
326
+ monitor.update_reading("server_room", 45) # => 🚨 ALERT
327
+ monitor.update_reading("bedroom", 22) # => ✅ CLEAR
328
+ monitor.update_reading("server_room", 65) # => 🔥 EMERGENCY SHUTDOWN
329
+
330
+ monitor.close
331
+ ```
332
+
333
+ ## Key Concepts Learned
334
+
335
+ ✅ **Rules** - Define patterns and actions
336
+ ✅ **Facts** - Observations stored in working memory
337
+ ✅ **Conditions** - Patterns that match facts
338
+ ✅ **Variable Binding** - Connect facts across conditions using `:variable?`
339
+ ✅ **Negation** - Match when patterns are absent
340
+ ✅ **Priorities** - Control rule firing order
341
+ ✅ **Persistence** - Save facts to database with blackboard memory
342
+
343
+ ## Troubleshooting
344
+
345
+ ### Rule Not Firing
346
+
347
+ **Problem**: Added facts but rule doesn't fire
348
+
349
+ **Checklist**:
350
+ 1. Did you call `engine.run`?
351
+ 2. Do variable bindings match? (`:sensor_id?` must appear in both conditions)
352
+ 3. Check negated conditions - is there a blocking fact?
353
+ 4. Verify fact types match condition types exactly (`:sensor` vs `:sensors`)
354
+
355
+ ### Performance Issues
356
+
357
+ **Problem**: Slow when adding many facts
358
+
359
+ **Solutions**:
360
+ - Order conditions from most selective to least selective
361
+ - Use Redis store for high-frequency updates: `KBS::Blackboard::Engine.new(store: KBS::Blackboard::Persistence::RedisStore.new)`
362
+ - Minimize negated conditions
363
+
364
+ ### Facts Not Persisting
365
+
366
+ **Problem**: Facts disappear after restart
367
+
368
+ **Check**:
369
+ - Using `KBS::Blackboard::Engine` (not `KBS::Engine`)?
370
+ - Provided `db_path` parameter?
371
+ - Called `engine.close` before exit?
372
+
373
+ ## Next Steps
374
+
375
+ Now that you understand the basics, explore:
376
+
377
+ - **[Writing Rules](writing-rules.md)** - Advanced rule patterns and techniques
378
+ - **[Pattern Matching](pattern-matching.md)** - Deep dive into condition syntax
379
+ - **[Blackboard Memory](blackboard-memory.md)** - Multi-agent collaboration
380
+ - **[Stock Trading Examples](../examples/index.md#stock-trading-systems)** - Real-world applications
381
+ - **[API Reference](../api/engine.md)** - Complete method documentation
382
+
383
+ ---
384
+
385
+ **Questions?** Open an issue at [github.com/madbomber/kbs](https://github.com/madbomber/kbs)
@@ -0,0 +1,23 @@
1
+ # Guides
2
+
3
+ Practical guides for using KBS effectively.
4
+
5
+ ## Getting Started
6
+ - **[Getting Started](getting-started.md)** - Your first rule-based system
7
+ - **[Writing Rules](writing-rules.md)** - Rule authoring best practices
8
+ - **[DSL Reference](dsl.md)** - Complete syntax guide
9
+
10
+ ## Core Concepts
11
+ - **[Facts](facts.md)** - Working with facts and queries
12
+ - **[Pattern Matching](pattern-matching.md)** - Pattern syntax and operators
13
+ - **[Variable Binding](variable-binding.md)** - Using variables in rules
14
+ - **[Negation](negation.md)** - Matching absent patterns
15
+
16
+ ## Persistence
17
+ - **[Blackboard Memory](blackboard-memory.md)** - Persistent shared memory
18
+ - **[Persistence Options](persistence.md)** - SQLite, Redis, and Hybrid backends
19
+
20
+ ## Next Steps
21
+ - **[Examples](../examples/index.md)** - See KBS in action
22
+ - **[Advanced Topics](../advanced/index.md)** - Production optimization
23
+ - **[API Reference](../api/index.md)** - Complete class documentation