kbs 0.1.0 → 0.2.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 +4 -4
  2. data/CHANGELOG.md +19 -0
  3. data/README.md +85 -57
  4. data/docs/advanced/performance.md +109 -76
  5. data/docs/advanced/testing.md +399 -263
  6. data/docs/api/blackboard.md +1 -1
  7. data/docs/api/engine.md +77 -8
  8. data/docs/api/facts.md +3 -3
  9. data/docs/api/rules.md +110 -40
  10. data/docs/architecture/blackboard.md +108 -117
  11. data/docs/assets/images/fact-rule-relationship.svg +65 -0
  12. data/docs/assets/images/fact-structure.svg +42 -0
  13. data/docs/assets/images/inference-cycle.svg +47 -0
  14. data/docs/assets/images/kb-components.svg +43 -0
  15. data/docs/assets/images/rule-structure.svg +44 -0
  16. data/docs/assets/images/trading-signal-network.svg +1 -1
  17. data/docs/examples/index.md +219 -5
  18. data/docs/guides/blackboard-memory.md +89 -58
  19. data/docs/guides/dsl.md +24 -24
  20. data/docs/guides/getting-started.md +109 -107
  21. data/docs/guides/writing-rules.md +470 -311
  22. data/docs/index.md +16 -18
  23. data/docs/quick-start.md +92 -99
  24. data/docs/what-is-a-fact.md +694 -0
  25. data/docs/what-is-a-knowledge-base.md +350 -0
  26. data/docs/what-is-a-rule.md +833 -0
  27. data/examples/.gitignore +1 -0
  28. data/examples/advanced_example_dsl.rb +1 -1
  29. data/examples/ai_enhanced_kbs_dsl.rb +1 -1
  30. data/examples/car_diagnostic_dsl.rb +1 -1
  31. data/examples/concurrent_inference_demo.rb +0 -1
  32. data/examples/concurrent_inference_demo_dsl.rb +0 -1
  33. data/examples/csv_trading_system_dsl.rb +1 -1
  34. data/examples/iot_demo_using_dsl.rb +1 -1
  35. data/examples/portfolio_rebalancing_system_dsl.rb +1 -1
  36. data/examples/rule_source_demo.rb +123 -0
  37. data/examples/stock_trading_advanced_dsl.rb +1 -1
  38. data/examples/temp_dsl.txt +6214 -5269
  39. data/examples/timestamped_trading_dsl.rb +1 -1
  40. data/examples/trading_demo_dsl.rb +1 -1
  41. data/examples/working_demo_dsl.rb +1 -1
  42. data/lib/kbs/decompiler.rb +204 -0
  43. data/lib/kbs/dsl/knowledge_base.rb +100 -1
  44. data/lib/kbs/dsl.rb +3 -1
  45. data/lib/kbs/engine.rb +41 -0
  46. data/lib/kbs/version.rb +1 -1
  47. data/lib/kbs.rb +14 -12
  48. data/mkdocs.yml +30 -30
  49. metadata +15 -10
  50. data/docs/DOCUMENTATION_STATUS.md +0 -158
  51. data/docs/examples/expert-systems.md +0 -1031
  52. data/docs/examples/multi-agent.md +0 -1335
  53. data/docs/examples/stock-trading.md +0 -488
  54. data/examples/knowledge_base.db +0 -0
  55. data/examples/temp.txt +0 -7693
data/docs/index.md CHANGED
@@ -29,32 +29,30 @@ KBS (Knowledge-Based Systems) is a powerful Ruby gem that brings production rule
29
29
  require 'kbs'
30
30
 
31
31
  # Create a rule-based trading system
32
- engine = KBS::Engine.new
33
-
34
- # Define a rule using the DSL
35
- engine.add_rule(Rule.new("buy_signal") do |r|
36
- r.conditions = [
32
+ kb = KBS.knowledge_base do
33
+ # Define a rule using the DSL
34
+ rule "buy_signal" do
37
35
  # Stock price is below threshold
38
- Condition.new(:stock, { symbol: :symbol?, price: :price? }),
39
- Condition.new(:threshold, { symbol: :symbol?, buy_below: :threshold? }),
36
+ on :stock, symbol: :symbol?, price: :price?
37
+ on :threshold, symbol: :symbol?, buy_below: :threshold?
40
38
 
41
39
  # No pending order exists (negation)
42
- Condition.new(:order, { symbol: :symbol? }, negated: true)
43
- ]
40
+ without :order, symbol: :symbol?
44
41
 
45
- r.action = lambda do |facts, bindings|
46
- if bindings[:price?] < bindings[:threshold?]
47
- puts "BUY #{bindings[:symbol?]} at #{bindings[:price?]}"
42
+ perform do |facts, bindings|
43
+ if bindings[:price?] < bindings[:threshold?]
44
+ puts "BUY #{bindings[:symbol?]} at #{bindings[:price?]}"
45
+ end
48
46
  end
49
47
  end
50
- end)
51
48
 
52
- # Add facts to working memory
53
- engine.add_fact(:stock, symbol: "AAPL", price: 145.50)
54
- engine.add_fact(:threshold, symbol: "AAPL", buy_below: 150.0)
49
+ # Add facts to working memory
50
+ fact :stock, symbol: "AAPL", price: 145.50
51
+ fact :threshold, symbol: "AAPL", buy_below: 150.0
55
52
 
56
- # Fire matching rules
57
- engine.run # => BUY AAPL at 145.5
53
+ # Fire matching rules
54
+ run # => BUY AAPL at 145.5
55
+ end
58
56
  ```
59
57
 
60
58
  ## Why RETE?
data/docs/quick-start.md CHANGED
@@ -6,66 +6,53 @@ Get up and running with KBS in 5 minutes.
6
6
 
7
7
  Let's build a simple temperature monitoring system that alerts when readings are abnormal.
8
8
 
9
- ### Step 1: Create the Engine
9
+ ### Step 1: Create a Knowledge Base and Define Rules
10
10
 
11
11
  ```ruby
12
12
  require 'kbs'
13
13
 
14
- # Create a RETE engine
15
- engine = KBS::Engine.new
16
- ```
17
-
18
- ### Step 2: Define Rules
14
+ kb = KBS.knowledge_base do
15
+ # Rule 1: Alert on high temperature
16
+ rule "high_temperature_alert" do
17
+ on :sensor, id: :id?, temp: :temp?
19
18
 
20
- ```ruby
21
- # Rule 1: Alert on high temperature
22
- high_temp_rule = KBS::Rule.new("high_temperature_alert") do |r|
23
- r.conditions = [
24
- KBS::Condition.new(:sensor, { id: :id?, temp: :temp? })
25
- ]
26
-
27
- r.action = lambda do |facts, bindings|
28
- if bindings[:temp?] > 75
29
- puts "⚠️ HIGH TEMP Alert: Sensor #{bindings[:id?]} at #{bindings[:temp?]}°F"
19
+ perform do |facts, bindings|
20
+ if bindings[:temp?] > 75
21
+ puts "⚠️ HIGH TEMP Alert: Sensor #{bindings[:id?]} at #{bindings[:temp?]}°F"
22
+ end
30
23
  end
31
24
  end
32
- end
33
-
34
- engine.add_rule(high_temp_rule)
35
25
 
36
- # Rule 2: Alert when cooling system is offline AND temp is high
37
- critical_rule = KBS::Rule.new("critical_condition", priority: 10) do |r|
38
- r.conditions = [
39
- KBS::Condition.new(:sensor, { id: :id?, temp: :temp? }),
40
- KBS::Condition.new(:cooling, { id: :id?, status: "offline" })
41
- ]
26
+ # Rule 2: Alert when cooling system is offline AND temp is high
27
+ rule "critical_condition", priority: 10 do
28
+ on :sensor, id: :id?, temp: :temp?
29
+ on :cooling, id: :id?, status: "offline"
42
30
 
43
- r.action = lambda do |facts, bindings|
44
- if bindings[:temp?] > 75
45
- puts "🚨 CRITICAL: Sensor #{bindings[:id?]} at #{bindings[:temp?]}°F with cooling OFFLINE!"
31
+ perform do |facts, bindings|
32
+ if bindings[:temp?] > 75
33
+ puts "🚨 CRITICAL: Sensor #{bindings[:id?]} at #{bindings[:temp?]}°F with cooling OFFLINE!"
34
+ end
46
35
  end
47
36
  end
48
37
  end
49
-
50
- engine.add_rule(critical_rule)
51
38
  ```
52
39
 
53
- ### Step 3: Add Facts
40
+ ### Step 2: Add Facts
54
41
 
55
42
  ```ruby
56
43
  # Add sensor readings
57
- engine.add_fact(:sensor, id: "room_101", temp: 72)
58
- engine.add_fact(:sensor, id: "server_rack", temp: 82)
59
- engine.add_fact(:sensor, id: "storage", temp: 65)
44
+ kb.fact :sensor, id: "room_101", temp: 72
45
+ kb.fact :sensor, id: "server_rack", temp: 82
46
+ kb.fact :sensor, id: "storage", temp: 65
60
47
 
61
48
  # Add cooling system status
62
- engine.add_fact(:cooling, id: "server_rack", status: "offline")
49
+ kb.fact :cooling, id: "server_rack", status: "offline"
63
50
  ```
64
51
 
65
- ### Step 4: Run Rules
52
+ ### Step 3: Run Rules
66
53
 
67
54
  ```ruby
68
- engine.run
55
+ kb.run
69
56
  # Output:
70
57
  # => ⚠️ HIGH TEMP Alert: Sensor server_rack at 82°F
71
58
  # => 🚨 CRITICAL: Sensor server_rack at 82°F with cooling OFFLINE!
@@ -73,10 +60,10 @@ engine.run
73
60
 
74
61
  ## Understanding What Happened
75
62
 
76
- 1. **Engine Creation**: `Engine.new` builds an empty RETE network
77
- 2. **Rule Addition**: Rules are compiled into the discrimination network
78
- 3. **Fact Assertion**: Facts propagate through the network, creating partial matches
79
- 4. **Rule Firing**: `engine.run()` executes actions for all complete matches
63
+ 1. **Knowledge Base Creation**: `KBS.knowledge_base do...end` creates the RETE network and defines rules
64
+ 2. **Rule Definition**: Rules are compiled into the discrimination network using the DSL
65
+ 3. **Fact Assertion**: `kb.fact` adds facts that propagate through the network, creating partial matches
66
+ 4. **Rule Firing**: `kb.run` executes actions for all complete matches
80
67
 
81
68
  The critical rule fires because:
82
69
  - Sensor "server_rack" temp (82°F) > 75
@@ -88,30 +75,28 @@ The critical rule fires because:
88
75
  Rules can match on the **absence** of facts:
89
76
 
90
77
  ```ruby
91
- # Alert when sensor has NO recent reading
92
- stale_sensor_rule = KBS::Rule.new("stale_sensor") do |r|
93
- r.conditions = [
94
- KBS::Condition.new(:sensor_registered, { id: :id? }),
78
+ kb = KBS.knowledge_base do
79
+ # Alert when sensor has NO recent reading
80
+ rule "stale_sensor" do
81
+ on :sensor_registered, id: :id?
95
82
  # No recent reading exists (negation!)
96
- KBS::Condition.new(:sensor, { id: :id? }, negated: true)
97
- ]
83
+ without :sensor, id: :id?
98
84
 
99
- r.action = lambda do |facts, bindings|
100
- puts "⚠️ No reading from sensor #{bindings[:id?]}"
85
+ perform do |facts, bindings|
86
+ puts "⚠️ No reading from sensor #{bindings[:id?]}"
87
+ end
101
88
  end
102
- end
103
-
104
- engine.add_rule(stale_sensor_rule)
105
89
 
106
- # Register sensors
107
- engine.add_fact(:sensor_registered, id: "room_101")
108
- engine.add_fact(:sensor_registered, id: "room_102")
90
+ # Register sensors
91
+ fact :sensor_registered, id: "room_101"
92
+ fact :sensor_registered, id: "room_102"
109
93
 
110
- # Only add reading for room_101
111
- engine.add_fact(:sensor, id: "room_101", temp: 70)
94
+ # Only add reading for room_101
95
+ fact :sensor, id: "room_101", temp: 70
112
96
 
113
- engine.run
114
- # => ⚠️ No reading from sensor room_102
97
+ run
98
+ # => ⚠️ No reading from sensor room_102
99
+ end
115
100
  ```
116
101
 
117
102
  ## Persistent Blackboard Memory
@@ -124,12 +109,22 @@ require 'kbs/blackboard'
124
109
  # SQLite backend (default)
125
110
  engine = KBS::Blackboard::Engine.new(db_path: 'monitoring.db')
126
111
 
127
- # Facts survive restarts
128
- engine.add_fact(:sensor, id: "room_101", temp: 72)
112
+ kb = KBS.knowledge_base(engine: engine) do
113
+ rule "temperature_monitor" do
114
+ on :sensor, temp: greater_than(75)
115
+ perform do |facts|
116
+ puts "High temp alert!"
117
+ end
118
+ end
119
+
120
+ # Facts survive restarts
121
+ fact :sensor, id: "room_101", temp: 72
122
+
123
+ run
124
+ end
129
125
 
130
126
  # Query historical data
131
- memory = engine.working_memory
132
- audit = memory.audit_log.recent_changes(limit: 10)
127
+ audit = engine.blackboard.get_history(limit: 10)
133
128
  ```
134
129
 
135
130
  ## Next Steps
@@ -143,9 +138,9 @@ audit = memory.audit_log.recent_changes(limit: 10)
143
138
 
144
139
  ### Explore Examples
145
140
 
146
- - **[Stock Trading](examples/stock-trading.md)** - Build a trading signal system
147
- - **[Expert Systems](examples/expert-systems.md)** - Diagnostic and decision support
148
- - **[Multi-Agent Systems](examples/multi-agent.md)** - Collaborative problem-solving
141
+ - **[Stock Trading Examples](examples/index.md#stock-trading-systems)** - Build a trading signal system
142
+ - **[Expert System Examples](examples/index.md#expert-systems)** - Diagnostic and decision support
143
+ - **[Blackboard & Multi-Agent Examples](examples/index.md#advanced-features)** - Collaborative problem-solving
149
144
 
150
145
  ### Advanced Topics
151
146
 
@@ -164,17 +159,16 @@ audit = memory.audit_log.recent_changes(limit: 10)
164
159
  ### Time-Based Rules
165
160
 
166
161
  ```ruby
167
- rule = KBS::Rule.new("recent_spike") do |r|
168
- r.conditions = [
169
- KBS::Condition.new(:reading, {
170
- sensor: :id?,
171
- temp: :temp?,
172
- timestamp: ->(ts) { Time.now - ts < 300 } # Within 5 minutes
173
- })
174
- ]
175
-
176
- r.action = lambda do |facts, bindings|
177
- puts "Recent spike: #{bindings[:temp?]}°F"
162
+ kb = KBS.knowledge_base do
163
+ rule "recent_spike" do
164
+ on :reading,
165
+ sensor: :id?,
166
+ temp: :temp?,
167
+ timestamp: ->(ts) { Time.now - ts < 300 } # Within 5 minutes
168
+
169
+ perform do |facts, bindings|
170
+ puts "Recent spike: #{bindings[:temp?]}°F"
171
+ end
178
172
  end
179
173
  end
180
174
  ```
@@ -182,15 +176,15 @@ end
182
176
  ### Threshold Comparison
183
177
 
184
178
  ```ruby
185
- rule = KBS::Rule.new("above_threshold") do |r|
186
- r.conditions = [
187
- KBS::Condition.new(:reading, { sensor: :id?, value: :val? }),
188
- KBS::Condition.new(:threshold, { sensor: :id?, max: :max? })
189
- ]
190
-
191
- r.action = lambda do |facts, bindings|
192
- if bindings[:val?] > bindings[:max?]
193
- puts "Threshold exceeded!"
179
+ kb = KBS.knowledge_base do
180
+ rule "above_threshold" do
181
+ on :reading, sensor: :id?, value: :val?
182
+ on :threshold, sensor: :id?, max: :max?
183
+
184
+ perform do |facts, bindings|
185
+ if bindings[:val?] > bindings[:max?]
186
+ puts "Threshold exceeded!"
187
+ end
194
188
  end
195
189
  end
196
190
  end
@@ -199,20 +193,19 @@ end
199
193
  ### State Machine
200
194
 
201
195
  ```ruby
202
- # Transition from "init" to "ready"
203
- transition_rule = KBS::Rule.new("init_to_ready") do |r|
204
- r.conditions = [
205
- KBS::Condition.new(:state, { current: "init" }),
206
- KBS::Condition.new(:sensor, { initialized: true }),
196
+ kb = KBS.knowledge_base do
197
+ # Transition from "init" to "ready"
198
+ rule "init_to_ready" do
199
+ on :state, current: "init"
200
+ on :sensor, initialized: true
207
201
  # No "ready" state exists yet
208
- KBS::Condition.new(:state, { current: "ready" }, negated: true)
209
- ]
210
-
211
- r.action = lambda do |facts|
212
- # Remove old state
213
- engine.remove_fact(facts[0])
214
- # Add new state
215
- engine.add_fact(:state, current: "ready")
202
+ without :state, current: "ready"
203
+
204
+ perform do |facts|
205
+ # Note: For state transitions, you'd typically use engine methods
206
+ # This is a simplified example
207
+ puts "Transitioning to ready state"
208
+ end
216
209
  end
217
210
  end
218
211
  ```