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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -0
- data/README.md +85 -57
- data/docs/advanced/performance.md +109 -76
- data/docs/advanced/testing.md +399 -263
- data/docs/api/blackboard.md +1 -1
- data/docs/api/engine.md +77 -8
- data/docs/api/facts.md +3 -3
- data/docs/api/rules.md +110 -40
- data/docs/architecture/blackboard.md +108 -117
- data/docs/assets/images/fact-rule-relationship.svg +65 -0
- data/docs/assets/images/fact-structure.svg +42 -0
- data/docs/assets/images/inference-cycle.svg +47 -0
- data/docs/assets/images/kb-components.svg +43 -0
- data/docs/assets/images/rule-structure.svg +44 -0
- data/docs/assets/images/trading-signal-network.svg +1 -1
- data/docs/examples/index.md +219 -5
- data/docs/guides/blackboard-memory.md +89 -58
- data/docs/guides/dsl.md +24 -24
- data/docs/guides/getting-started.md +109 -107
- data/docs/guides/writing-rules.md +470 -311
- data/docs/index.md +16 -18
- data/docs/quick-start.md +92 -99
- data/docs/what-is-a-fact.md +694 -0
- data/docs/what-is-a-knowledge-base.md +350 -0
- data/docs/what-is-a-rule.md +833 -0
- data/examples/.gitignore +1 -0
- data/examples/advanced_example_dsl.rb +1 -1
- data/examples/ai_enhanced_kbs_dsl.rb +1 -1
- data/examples/car_diagnostic_dsl.rb +1 -1
- data/examples/concurrent_inference_demo.rb +0 -1
- data/examples/concurrent_inference_demo_dsl.rb +0 -1
- data/examples/csv_trading_system_dsl.rb +1 -1
- data/examples/iot_demo_using_dsl.rb +1 -1
- data/examples/portfolio_rebalancing_system_dsl.rb +1 -1
- data/examples/rule_source_demo.rb +123 -0
- data/examples/stock_trading_advanced_dsl.rb +1 -1
- data/examples/temp_dsl.txt +6214 -5269
- data/examples/timestamped_trading_dsl.rb +1 -1
- data/examples/trading_demo_dsl.rb +1 -1
- data/examples/working_demo_dsl.rb +1 -1
- data/lib/kbs/decompiler.rb +204 -0
- data/lib/kbs/dsl/knowledge_base.rb +100 -1
- data/lib/kbs/dsl.rb +3 -1
- data/lib/kbs/engine.rb +41 -0
- data/lib/kbs/version.rb +1 -1
- data/lib/kbs.rb +14 -12
- data/mkdocs.yml +30 -30
- metadata +15 -10
- data/docs/DOCUMENTATION_STATUS.md +0 -158
- data/docs/examples/expert-systems.md +0 -1031
- data/docs/examples/multi-agent.md +0 -1335
- data/docs/examples/stock-trading.md +0 -488
- data/examples/knowledge_base.db +0 -0
- 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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
39
|
-
|
|
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
|
-
|
|
43
|
-
]
|
|
40
|
+
without :order, symbol: :symbol?
|
|
44
41
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
54
|
-
|
|
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
|
-
|
|
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
|
|
9
|
+
### Step 1: Create a Knowledge Base and Define Rules
|
|
10
10
|
|
|
11
11
|
```ruby
|
|
12
12
|
require 'kbs'
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
|
40
|
+
### Step 2: Add Facts
|
|
54
41
|
|
|
55
42
|
```ruby
|
|
56
43
|
# Add sensor readings
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
49
|
+
kb.fact :cooling, id: "server_rack", status: "offline"
|
|
63
50
|
```
|
|
64
51
|
|
|
65
|
-
### Step
|
|
52
|
+
### Step 3: Run Rules
|
|
66
53
|
|
|
67
54
|
```ruby
|
|
68
|
-
|
|
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. **
|
|
77
|
-
2. **Rule
|
|
78
|
-
3. **Fact Assertion**:
|
|
79
|
-
4. **Rule Firing**: `
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
97
|
-
]
|
|
83
|
+
without :sensor, id: :id?
|
|
98
84
|
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
108
|
-
|
|
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
|
-
|
|
94
|
+
# Only add reading for room_101
|
|
95
|
+
fact :sensor, id: "room_101", temp: 70
|
|
112
96
|
|
|
113
|
-
|
|
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
|
-
|
|
128
|
-
|
|
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
|
-
|
|
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
|
|
147
|
-
- **[Expert
|
|
148
|
-
- **[Multi-Agent
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
```
|