kbs 0.1.0 → 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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +85 -57
  3. data/docs/advanced/performance.md +109 -76
  4. data/docs/advanced/testing.md +399 -263
  5. data/docs/api/blackboard.md +1 -1
  6. data/docs/api/engine.md +77 -8
  7. data/docs/api/facts.md +3 -3
  8. data/docs/api/rules.md +110 -40
  9. data/docs/architecture/blackboard.md +108 -117
  10. data/docs/assets/images/fact-rule-relationship.svg +65 -0
  11. data/docs/assets/images/fact-structure.svg +42 -0
  12. data/docs/assets/images/inference-cycle.svg +47 -0
  13. data/docs/assets/images/kb-components.svg +43 -0
  14. data/docs/assets/images/rule-structure.svg +44 -0
  15. data/docs/assets/images/trading-signal-network.svg +1 -1
  16. data/docs/examples/index.md +219 -5
  17. data/docs/guides/blackboard-memory.md +89 -58
  18. data/docs/guides/dsl.md +24 -24
  19. data/docs/guides/getting-started.md +109 -107
  20. data/docs/guides/writing-rules.md +470 -311
  21. data/docs/index.md +16 -18
  22. data/docs/quick-start.md +92 -99
  23. data/docs/what-is-a-fact.md +694 -0
  24. data/docs/what-is-a-knowledge-base.md +350 -0
  25. data/docs/what-is-a-rule.md +833 -0
  26. data/examples/.gitignore +1 -0
  27. data/examples/advanced_example_dsl.rb +1 -1
  28. data/examples/ai_enhanced_kbs_dsl.rb +1 -1
  29. data/examples/car_diagnostic_dsl.rb +1 -1
  30. data/examples/concurrent_inference_demo.rb +0 -1
  31. data/examples/concurrent_inference_demo_dsl.rb +0 -1
  32. data/examples/csv_trading_system_dsl.rb +1 -1
  33. data/examples/iot_demo_using_dsl.rb +1 -1
  34. data/examples/portfolio_rebalancing_system_dsl.rb +1 -1
  35. data/examples/rule_source_demo.rb +123 -0
  36. data/examples/stock_trading_advanced_dsl.rb +1 -1
  37. data/examples/temp_dsl.txt +6214 -5269
  38. data/examples/timestamped_trading_dsl.rb +1 -1
  39. data/examples/trading_demo_dsl.rb +1 -1
  40. data/examples/working_demo_dsl.rb +1 -1
  41. data/lib/kbs/decompiler.rb +204 -0
  42. data/lib/kbs/dsl/knowledge_base.rb +100 -1
  43. data/lib/kbs/dsl.rb +3 -1
  44. data/lib/kbs/engine.rb +41 -0
  45. data/lib/kbs/version.rb +1 -1
  46. data/lib/kbs.rb +14 -12
  47. data/mkdocs.yml +30 -30
  48. metadata +15 -10
  49. data/docs/DOCUMENTATION_STATUS.md +0 -158
  50. data/docs/examples/expert-systems.md +0 -1031
  51. data/docs/examples/multi-agent.md +0 -1335
  52. data/docs/examples/stock-trading.md +0 -488
  53. data/examples/knowledge_base.db +0 -0
  54. data/examples/temp.txt +0 -7693
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aef07173eec74d0aa657562330a8fda502d390e14e181fddcd2cfb948d158057
4
- data.tar.gz: da2018b9fb70043a9b12c57d50ea71dcaa60e54f1ba1129f3411b4c603e7a860
3
+ metadata.gz: c07983aed0abb377d018730407ad0afb8280c228558d991d20d693adaccb6ba7
4
+ data.tar.gz: 2ac8ae8057eca02225fc7756e6f4c47201488a6c58a6f3d05707f32bdf12924b
5
5
  SHA512:
6
- metadata.gz: 18c9bb6a21154803ea89139d8cbb1f7d86583cff83f8f827c63ec29b9dea859569ee18fe7e1d13211b78b9217658331ca128561a0b49ff580b20aef8ff00f215
7
- data.tar.gz: 7e93213dfc62adf43740c4ac7bc35d4e9abf71445aa02cca59ed112624030a95465c2ec0729deeb3baa4f34b8cc0781dd6c51eea86270b643b51a5bed4da16e8
6
+ metadata.gz: e0cf306fc89d5d31a0887d753c975e6f6a875aa38216d846c0ba15110dd9d05a7f6d549bfd68a96383d094ca26fd469ba23775582fe28ef60e7c4c06d674260e
7
+ data.tar.gz: c6b9bd27687f2d220f0167b111057b76e2a3d70d9cef3ae9b112034951d926bd4180f2e3d1c035ce3228064499974ec5e66db80e8e88293e6192fcf9a143f5a5
data/README.md CHANGED
@@ -71,30 +71,25 @@ gem install kbs
71
71
  ```ruby
72
72
  require 'kbs'
73
73
 
74
- # Create inference engine
75
- engine = KBS::ReteEngine.new
76
-
77
- # Define a rule
78
- rule = KBS::Rule.new(
79
- "high_temperature_alert",
80
- conditions: [
81
- KBS::Condition.new(:sensor, { type: "temperature" }),
82
- KBS::Condition.new(:reading, { value: ->(v) { v > 100 } })
83
- ],
84
- action: lambda do |facts, bindings|
85
- reading = facts.find { |f| f.type == :reading }
86
- puts "🚨 HIGH TEMPERATURE: #{reading[:value]}°C"
87
- end
88
- )
74
+ # Create knowledge base with DSL
75
+ kb = KBS.knowledge_base do
76
+ rule "high_temperature_alert" do
77
+ on :sensor, type: "temperature"
78
+ on :reading, value: greater_than(100)
89
79
 
90
- engine.add_rule(rule)
80
+ perform do |facts, bindings|
81
+ reading = facts.find { |f| f.type == :reading }
82
+ puts "🚨 HIGH TEMPERATURE: #{reading[:value]}°C"
83
+ end
84
+ end
91
85
 
92
- # Add facts
93
- engine.add_fact(:sensor, { type: "temperature", location: "reactor" })
94
- engine.add_fact(:reading, { value: 105, unit: "celsius" })
86
+ # Add facts
87
+ fact :sensor, type: "temperature", location: "reactor"
88
+ fact :reading, value: 105, unit: "celsius"
95
89
 
96
- # Run inference
97
- engine.run
90
+ # Run inference
91
+ run
92
+ end
98
93
  # => 🚨 HIGH TEMPERATURE: 105°C
99
94
  ```
100
95
 
@@ -102,7 +97,6 @@ engine.run
102
97
 
103
98
  ```ruby
104
99
  require 'kbs'
105
- require 'kbs/dsl'
106
100
 
107
101
  kb = KBS.knowledge_base do
108
102
  rule "momentum_breakout" do
@@ -130,18 +124,29 @@ kb.run
130
124
  ```ruby
131
125
  require 'kbs/blackboard'
132
126
 
133
- # Create persistent blackboard
134
- engine = KBS::Blackboard::Engine.new(
135
- store: KBS::Blackboard::Persistence::SQLiteStore.new(db_path: 'knowledge.db')
136
- )
127
+ # Create persistent blackboard with DSL
128
+ engine = KBS::Blackboard::Engine.new(db_path: 'knowledge.db')
129
+
130
+ kb = KBS.knowledge_base(engine: engine) do
131
+ rule "temperature_monitor" do
132
+ on :sensor, type: "temperature", value: greater_than(25)
133
+
134
+ perform do |facts|
135
+ sensor = facts.first
136
+ puts "⚠️ High temperature at #{sensor[:location]}: #{sensor[:value]}°C"
137
+ end
138
+ end
137
139
 
138
- # Add persistent facts
139
- sensor = engine.add_fact(:sensor, { type: "temperature", location: "room1" })
140
- puts "Fact UUID: #{sensor.uuid}"
140
+ # Add persistent facts
141
+ fact :sensor, type: "temperature", location: "room1", value: 22
142
+ fact :sensor, type: "temperature", location: "room2", value: 28
143
+
144
+ run
145
+ end
141
146
 
142
147
  # Query facts
143
148
  sensors = engine.blackboard.get_facts(:sensor)
144
- sensors.each { |s| puts "#{s[:type]} at #{s[:location]}" }
149
+ sensors.each { |s| puts "#{s[:type]} at #{s[:location]}: #{s[:value]}°C" }
145
150
 
146
151
  # View audit history
147
152
  history = engine.blackboard.get_history(limit: 10)
@@ -155,51 +160,74 @@ end
155
160
  ```ruby
156
161
  require 'kbs/blackboard'
157
162
 
158
- # High-frequency trading with Redis
159
- engine = KBS::Blackboard::Engine.new(
160
- store: KBS::Blackboard::Persistence::RedisStore.new(
161
- url: 'redis://localhost:6379/0'
162
- )
163
- )
163
+ # High-frequency trading with Redis and DSL
164
+ store = KBS::Blackboard::Persistence::RedisStore.new(url: 'redis://localhost:6379/0')
165
+ engine = KBS::Blackboard::Engine.new(store: store)
166
+
167
+ kb = KBS.knowledge_base(engine: engine) do
168
+ rule "price_alert" do
169
+ on :market_price, volume: greater_than(500_000)
170
+
171
+ perform do |facts|
172
+ price = facts.first
173
+ puts "📊 High volume: #{price[:symbol]} - #{price[:volume]} shares"
174
+ # Post message for other components
175
+ engine.post_message("PriceAlert", "high_volume",
176
+ { symbol: price[:symbol], volume: price[:volume] },
177
+ priority: 10
178
+ )
179
+ end
180
+ end
164
181
 
165
- # Fast in-memory fact storage
166
- engine.add_fact(:market_price, { symbol: "AAPL", price: 150.25, volume: 1_000_000 })
182
+ # Fast in-memory fact storage
183
+ fact :market_price, symbol: "AAPL", price: 150.25, volume: 1_000_000
167
184
 
168
- # Message queue for real-time coordination
169
- engine.post_message("MarketDataFeed", "prices",
170
- { symbol: "AAPL", bid: 150.24, ask: 150.26 },
171
- priority: 10
172
- )
185
+ run
186
+ end
173
187
 
174
- message = engine.consume_message("prices", "TradingStrategy")
175
- puts "Received: #{message[:content]}"
188
+ # Consume messages
189
+ message = engine.consume_message("high_volume", "TradingStrategy")
190
+ puts "Received: #{message[:content]}" if message
176
191
  ```
177
192
 
178
193
  ### AI-Enhanced Reasoning
179
194
 
180
195
  ```ruby
181
196
  require 'kbs'
182
- require 'kbs/examples/ai_enhanced_kbs'
197
+ require 'ruby_llm'
183
198
 
184
199
  # Requires Ollama with a model installed
185
200
  # export OLLAMA_MODEL=gpt-oss:latest
186
201
 
187
- system = AIEnhancedKBS::AIKnowledgeSystem.new
202
+ kb = KBS.knowledge_base do
203
+ rule "ai_sentiment_analysis" do
204
+ on :news_data, symbol: satisfies { |s| s && s.length > 0 }
205
+
206
+ perform do |facts|
207
+ news = facts.first
208
+ # AI-powered sentiment analysis
209
+ client = RubyLLM::Chat.new(provider: :ollama, model: 'gpt-oss:latest')
210
+ response = client.ask("Analyze sentiment: #{news[:headline]}")
211
+
212
+ puts "🤖 AI SENTIMENT ANALYSIS: #{news[:symbol]}"
213
+ puts " Headline: #{news[:headline]}"
214
+ puts " AI Analysis: #{response.content}"
215
+ end
216
+ end
188
217
 
189
- # Add news for AI sentiment analysis
190
- system.engine.add_fact(:news_data, {
191
- symbol: "AAPL",
192
- headline: "Apple Reports Record Q4 Earnings, Beats Expectations by 15%",
193
- content: "Apple Inc. announced exceptional results..."
194
- })
218
+ # Add news for AI sentiment analysis
219
+ fact :news_data,
220
+ symbol: "AAPL",
221
+ headline: "Apple Reports Record Q4 Earnings, Beats Expectations by 15%",
222
+ content: "Apple Inc. announced exceptional results..."
195
223
 
196
- system.engine.run
224
+ run
225
+ end
197
226
 
198
227
  # Output:
199
228
  # 🤖 AI SENTIMENT ANALYSIS: AAPL
200
- # AI Sentiment: positive (92%)
201
- # Key Themes: strong earnings growth, share buyback program
202
- # Market Impact: bullish
229
+ # Headline: Apple Reports Record Q4 Earnings, Beats Expectations by 15%
230
+ # AI Analysis: positive (92%) - strong earnings growth, bullish outlook
203
231
  ```
204
232
 
205
233
  ## 📚 Examples
@@ -20,25 +20,19 @@ KBS performance depends on:
20
20
  require 'benchmark'
21
21
  require 'kbs'
22
22
 
23
- engine = KBS::Engine.new
24
-
25
- # Add rules
26
- rule = KBS::Rule.new("simple_rule") do |r|
27
- r.conditions = [
28
- KBS::Condition.new(:fact, { value: :v? })
29
- ]
30
-
31
- r.action = lambda do |facts, bindings|
32
- # Simple action
23
+ kb = KBS.knowledge_base do
24
+ rule "simple_rule" do
25
+ on :fact, value: :v?
26
+ perform do |facts, bindings|
27
+ # Simple action
28
+ end
33
29
  end
34
30
  end
35
31
 
36
- engine.add_rule(rule)
37
-
38
32
  # Benchmark fact addition
39
33
  time = Benchmark.measure do
40
34
  10_000.times do |i|
41
- engine.add_fact(:fact, { value: i })
35
+ kb.fact :fact, value: i
42
36
  end
43
37
  end
44
38
 
@@ -47,7 +41,7 @@ puts "#{(10_000 / time.real).round(2)} facts/second"
47
41
 
48
42
  # Benchmark engine run
49
43
  time = Benchmark.measure do
50
- engine.run
44
+ kb.run
51
45
  end
52
46
 
53
47
  puts "Ran engine in #{time.real} seconds"
@@ -67,24 +61,39 @@ class KBSBenchmark
67
61
  def setup_engine
68
62
  case @engine_type
69
63
  when :memory
70
- KBS::Engine.new
64
+ # Return a new knowledge base context
65
+ @kb_context = :memory
71
66
  when :blackboard_sqlite
72
- KBS::Blackboard::Engine.new(db_path: ':memory:')
67
+ @kb_context = :blackboard_sqlite
68
+ @db_path = ':memory:'
73
69
  when :blackboard_redis
74
70
  require 'kbs/blackboard/persistence/redis_store'
75
- store = KBS::Blackboard::Persistence::RedisStore.new(
71
+ @kb_context = :blackboard_redis
72
+ @store = KBS::Blackboard::Persistence::RedisStore.new(
76
73
  url: 'redis://localhost:6379/15' # Test database
77
74
  )
78
- KBS::Blackboard::Engine.new(store: store)
79
75
  end
80
76
  end
81
77
 
82
78
  def benchmark_fact_addition(count: 10_000)
83
- engine = setup_engine
79
+ setup_engine
80
+
81
+ kb = case @kb_context
82
+ when :memory
83
+ KBS.knowledge_base do
84
+ # Add facts in benchmark block
85
+ end
86
+ when :blackboard_sqlite
87
+ engine = KBS::Blackboard::Engine.new(db_path: @db_path)
88
+ KBS.knowledge_base(engine: engine)
89
+ when :blackboard_redis
90
+ engine = KBS::Blackboard::Engine.new(store: @store)
91
+ KBS.knowledge_base(engine: engine)
92
+ end
84
93
 
85
94
  time = Benchmark.measure do
86
95
  count.times do |i|
87
- engine.add_fact(:fact, { id: i, value: rand(1000) })
96
+ kb.fact :fact, id: i, value: rand(1000)
88
97
  end
89
98
  end
90
99
 
@@ -96,30 +105,57 @@ class KBSBenchmark
96
105
  end
97
106
 
98
107
  def benchmark_simple_rules(fact_count: 1000, rule_count: 10)
99
- engine = setup_engine
108
+ setup_engine
100
109
 
101
- # Add rules
102
- rule_count.times do |i|
103
- rule = KBS::Rule.new("rule_#{i}") do |r|
104
- r.conditions = [
105
- KBS::Condition.new(:fact, { value: :v? })
106
- ]
110
+ kb = case @kb_context
111
+ when :memory
112
+ KBS.knowledge_base do
113
+ # Add rules
114
+ rule_count.times do |i|
115
+ rule "rule_#{i}" do
116
+ on :fact, value: :v?
117
+ perform do |facts, bindings|
118
+ # Minimal action
119
+ end
120
+ end
121
+ end
107
122
 
108
- r.action = lambda do |facts, bindings|
109
- # Minimal action
123
+ # Add facts
124
+ fact_count.times do |i|
125
+ fact :fact, value: i
126
+ end
127
+ end
128
+ when :blackboard_sqlite
129
+ engine = KBS::Blackboard::Engine.new(db_path: @db_path)
130
+ kb = KBS.knowledge_base(engine: engine) do
131
+ rule_count.times do |i|
132
+ rule "rule_#{i}" do
133
+ on :fact, value: :v?
134
+ perform { }
135
+ end
136
+ end
137
+ fact_count.times do |i|
138
+ fact :fact, value: i
139
+ end
140
+ end
141
+ when :blackboard_redis
142
+ engine = KBS::Blackboard::Engine.new(store: @store)
143
+ kb = KBS.knowledge_base(engine: engine) do
144
+ rule_count.times do |i|
145
+ rule "rule_#{i}" do
146
+ on :fact, value: :v?
147
+ perform { }
148
+ end
149
+ end
150
+ fact_count.times do |i|
151
+ fact :fact, value: i
110
152
  end
111
153
  end
112
- engine.add_rule(rule)
113
- end
114
-
115
- # Add facts
116
- fact_count.times do |i|
117
- engine.add_fact(:fact, { value: i })
118
154
  end
119
155
 
120
156
  # Benchmark engine run
121
157
  time = Benchmark.measure do
122
- engine.run
158
+ kb.run
123
159
  end
124
160
 
125
161
  @results[:simple_rules] = {
@@ -130,33 +166,33 @@ class KBSBenchmark
130
166
  end
131
167
 
132
168
  def benchmark_complex_joins(fact_count: 500)
133
- engine = setup_engine
134
-
135
- # Rule with 3-way join
136
- rule = KBS::Rule.new("complex_join") do |r|
137
- r.conditions = [
138
- KBS::Condition.new(:a, { id: :id?, value: :v? }),
139
- KBS::Condition.new(:b, { a_id: :id?, score: :s? }),
140
- KBS::Condition.new(:c, { b_score: :s? })
141
- ]
142
-
143
- r.action = lambda do |facts, bindings|
144
- # Action
145
- end
146
- end
169
+ setup_engine
147
170
 
148
- engine.add_rule(rule)
171
+ kb = case @kb_context
172
+ when :memory
173
+ KBS.knowledge_base do
174
+ # Rule with 3-way join
175
+ rule "complex_join" do
176
+ on :a, id: :id?, value: :v?
177
+ on :b, a_id: :id?, score: :s?
178
+ on :c, b_score: :s?
179
+ perform do |facts, bindings|
180
+ # Action
181
+ end
182
+ end
149
183
 
150
- # Add facts
151
- fact_count.times do |i|
152
- engine.add_fact(:a, { id: i, value: rand(100) })
153
- engine.add_fact(:b, { a_id: i, score: rand(100) })
154
- engine.add_fact(:c, { b_score: i })
184
+ # Add facts
185
+ fact_count.times do |i|
186
+ fact :a, id: i, value: rand(100)
187
+ fact :b, a_id: i, score: rand(100)
188
+ fact :c, b_score: i
189
+ end
190
+ end
155
191
  end
156
192
 
157
193
  # Benchmark
158
194
  time = Benchmark.measure do
159
- engine.run
195
+ kb.run
160
196
  end
161
197
 
162
198
  @results[:complex_joins] = {
@@ -166,31 +202,28 @@ class KBSBenchmark
166
202
  end
167
203
 
168
204
  def benchmark_negation(fact_count: 1000)
169
- engine = setup_engine
170
-
171
- # Rule with negation
172
- rule = KBS::Rule.new("negation_rule") do |r|
173
- r.conditions = [
174
- KBS::Condition.new(:positive, { id: :id? }),
175
- KBS::Condition.new(:negative, { id: :id? }, negated: true)
176
- ]
177
-
178
- r.action = lambda do |facts, bindings|
179
- # Action
205
+ setup_engine
206
+
207
+ kb = KBS.knowledge_base do
208
+ # Rule with negation
209
+ rule "negation_rule" do
210
+ on :positive, id: :id?
211
+ without :negative, id: :id?
212
+ perform do |facts, bindings|
213
+ # Action
214
+ end
180
215
  end
181
- end
182
216
 
183
- engine.add_rule(rule)
184
-
185
- # Add facts (50% will match)
186
- fact_count.times do |i|
187
- engine.add_fact(:positive, { id: i })
188
- engine.add_fact(:negative, { id: i }) if i.even?
217
+ # Add facts (50% will match)
218
+ fact_count.times do |i|
219
+ fact :positive, id: i
220
+ fact :negative, id: i if i.even?
221
+ end
189
222
  end
190
223
 
191
224
  # Benchmark
192
225
  time = Benchmark.measure do
193
- engine.run
226
+ kb.run
194
227
  end
195
228
 
196
229
  @results[:negation] = {