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
@@ -62,30 +62,29 @@ RETE engine with persistent blackboard memory:
62
62
  # Create engine with blackboard
63
63
  engine = KBS::Blackboard::Engine.new(db_path: 'trading.db')
64
64
 
65
- # Define rules (persisted in the database)
66
- engine.add_rule(Rule.new("buy_signal") do |r|
67
- r.conditions = [
68
- Condition.new(:stock, { symbol: :sym?, price: :price? }),
69
- Condition.new(:threshold, { symbol: :sym?, max: :max? })
70
- ]
71
-
72
- r.action = lambda do |facts, bindings|
73
- if bindings[:price?] < bindings[:max?]
74
- # Write new fact to blackboard
75
- engine.add_fact(:order, {
76
- symbol: bindings[:sym?],
77
- action: "BUY",
78
- price: bindings[:price?]
79
- })
65
+ # Define rules using DSL (persisted in the database)
66
+ kb = KBS.knowledge_base(engine: engine) do
67
+ rule "buy_signal" do
68
+ on :stock, symbol: :sym?, price: :price?
69
+ on :threshold, symbol: :sym?, max: :max?
70
+
71
+ perform do |facts, bindings|
72
+ if bindings[:price?] < bindings[:max?]
73
+ # Write new fact to blackboard
74
+ fact :order,
75
+ symbol: bindings[:sym?],
76
+ action: "BUY",
77
+ price: bindings[:price?]
78
+ end
80
79
  end
81
80
  end
82
- end)
83
81
 
84
- # Facts trigger rules, which create new facts
85
- engine.add_fact(:stock, symbol: "AAPL", price: 145.0)
86
- engine.add_fact(:threshold, symbol: "AAPL", max: 150.0)
87
- engine.run
88
- # => Creates :order fact in blackboard
82
+ # Facts trigger rules, which create new facts
83
+ fact :stock, symbol: "AAPL", price: 145.0
84
+ fact :threshold, symbol: "AAPL", max: 150.0
85
+ run
86
+ # => Creates :order fact in blackboard
87
+ end
89
88
  ```
90
89
 
91
90
  **Implementation**: `lib/kbs/blackboard/engine.rb`
@@ -278,93 +277,77 @@ Trading system with four specialized agents:
278
277
 
279
278
  ```ruby
280
279
  # Shared blackboard
281
- blackboard = KBS::Blackboard::Engine.new(db_path: 'trading.db')
282
-
283
- # Agent 1: Market Data Collector
284
- data_agent = KBS::Rule.new("collect_data", priority: 5) do |r|
285
- r.conditions = [
286
- Condition.new(:market_open, { status: true }),
287
- Condition.new(:stock_data, { symbol: :sym? }, negated: true)
288
- ]
289
-
290
- r.action = lambda do |facts, bindings|
291
- price = fetch_current_price(bindings[:sym?])
292
- blackboard.add_fact(:stock_data, {
293
- symbol: bindings[:sym?],
294
- price: price,
295
- timestamp: Time.now
296
- })
297
- end
298
- end
280
+ engine = KBS::Blackboard::Engine.new(db_path: 'trading.db')
299
281
 
300
- # Agent 2: Signal Generator
301
- signal_agent = KBS::Rule.new("generate_signals", priority: 10) do |r|
302
- r.conditions = [
303
- Condition.new(:stock_data, { symbol: :sym?, price: :price? }),
304
- Condition.new(:sma_data, { symbol: :sym?, sma: :sma? })
305
- ]
282
+ kb = KBS.knowledge_base(engine: engine) do
283
+ # Agent 1: Market Data Collector
284
+ rule "collect_data", priority: 5 do
285
+ on :market_open, status: true
286
+ without :stock_data, symbol: :sym?
306
287
 
307
- r.action = lambda do |facts, bindings|
308
- if bindings[:price?] > bindings[:sma?]
309
- blackboard.add_fact(:signal, {
288
+ perform do |facts, bindings|
289
+ price = fetch_current_price(bindings[:sym?])
290
+ fact :stock_data,
310
291
  symbol: bindings[:sym?],
311
- direction: "BUY",
312
- strength: (bindings[:price?] / bindings[:sma?]) - 1.0
313
- })
292
+ price: price,
293
+ timestamp: Time.now
314
294
  end
315
295
  end
316
- end
317
-
318
- # Agent 3: Risk Manager
319
- risk_agent = KBS::Rule.new("check_risk", priority: 20) do |r|
320
- r.conditions = [
321
- Condition.new(:signal, { symbol: :sym?, direction: :dir? }),
322
- Condition.new(:portfolio, { symbol: :sym?, position: :pos? })
323
- ]
324
296
 
325
- r.action = lambda do |facts, bindings|
326
- if bindings[:pos?] > 1000 && bindings[:dir?] == "BUY"
327
- blackboard.add_fact(:risk_alert, {
328
- symbol: bindings[:sym?],
329
- reason: "Position limit exceeded"
330
- })
331
- else
332
- blackboard.add_fact(:approved_signal, {
333
- symbol: bindings[:sym?],
334
- direction: bindings[:dir?]
335
- })
297
+ # Agent 2: Signal Generator
298
+ rule "generate_signals", priority: 10 do
299
+ on :stock_data, symbol: :sym?, price: :price?
300
+ on :sma_data, symbol: :sym?, sma: :sma?
301
+
302
+ perform do |facts, bindings|
303
+ if bindings[:price?] > bindings[:sma?]
304
+ fact :signal,
305
+ symbol: bindings[:sym?],
306
+ direction: "BUY",
307
+ strength: (bindings[:price?] / bindings[:sma?]) - 1.0
308
+ end
336
309
  end
337
310
  end
338
- end
339
311
 
340
- # Agent 4: Order Executor
341
- exec_agent = KBS::Rule.new("execute_orders", priority: 30) do |r|
342
- r.conditions = [
343
- Condition.new(:approved_signal, { symbol: :sym?, direction: :dir? }),
344
- Condition.new(:risk_alert, { symbol: :sym? }, negated: true)
345
- ]
346
-
347
- r.action = lambda do |facts, bindings|
348
- execute_trade(bindings[:sym?], bindings[:dir?])
349
- blackboard.add_fact(:execution, {
350
- symbol: bindings[:sym?],
351
- direction: bindings[:dir?],
352
- timestamp: Time.now
353
- })
312
+ # Agent 3: Risk Manager
313
+ rule "check_risk", priority: 20 do
314
+ on :signal, symbol: :sym?, direction: :dir?
315
+ on :portfolio, symbol: :sym?, position: :pos?
316
+
317
+ perform do |facts, bindings|
318
+ if bindings[:pos?] > 1000 && bindings[:dir?] == "BUY"
319
+ fact :risk_alert,
320
+ symbol: bindings[:sym?],
321
+ reason: "Position limit exceeded"
322
+ else
323
+ fact :approved_signal,
324
+ symbol: bindings[:sym?],
325
+ direction: bindings[:dir?]
326
+ end
327
+ end
354
328
  end
355
- end
356
329
 
357
- # Register all agents
358
- [data_agent, signal_agent, risk_agent, exec_agent].each do |agent|
359
- blackboard.add_rule(agent)
360
- end
330
+ # Agent 4: Order Executor
331
+ rule "execute_orders", priority: 30 do
332
+ on :approved_signal, symbol: :sym?, direction: :dir?
333
+ without :risk_alert, symbol: :sym?
361
334
 
362
- # Trigger the system
363
- blackboard.add_fact(:market_open, status: true)
364
- blackboard.add_fact(:portfolio, symbol: "AAPL", position: 500)
335
+ perform do |facts, bindings|
336
+ execute_trade(bindings[:sym?], bindings[:dir?])
337
+ fact :execution,
338
+ symbol: bindings[:sym?],
339
+ direction: bindings[:dir?],
340
+ timestamp: Time.now
341
+ end
342
+ end
365
343
 
366
- # Agents collaborate through blackboard
367
- blackboard.run
344
+ # Trigger the system
345
+ fact :market_open, status: true
346
+ fact :portfolio, symbol: "AAPL", position: 500
347
+
348
+ # Agents collaborate through blackboard
349
+ run
350
+ end
368
351
  ```
369
352
 
370
353
  ## Transactions
@@ -499,13 +482,13 @@ Agents activate when their preconditions are met:
499
482
 
500
483
  ```ruby
501
484
  # Trigger fires only when specific fact exists
502
- trigger_rule = Rule.new("on_critical_alert") do |r|
503
- r.conditions = [
504
- Condition.new(:alert, { severity: "critical" })
505
- ]
485
+ kb = KBS.knowledge_base(engine: engine) do
486
+ rule "on_critical_alert" do
487
+ on :alert, severity: "critical"
506
488
 
507
- r.action = lambda { |facts|
508
- notify_team(facts[0])
489
+ perform do |facts, bindings|
490
+ notify_team(facts[0])
491
+ end
509
492
  end
510
493
  end
511
494
  ```
@@ -516,17 +499,17 @@ Limit agent attention to relevant facts:
516
499
 
517
500
  ```ruby
518
501
  # Agent only sees recent stock data
519
- recent_data_rule = Rule.new("analyze_recent") do |r|
520
- r.conditions = [
521
- Condition.new(:stock_data, {
502
+ kb = KBS.knowledge_base(engine: engine) do
503
+ rule "analyze_recent" do
504
+ on :stock_data,
522
505
  symbol: :sym?,
523
- timestamp: ->(ts) { Time.now - ts < 300 } # Last 5 minutes
524
- })
525
- ]
506
+ timestamp: :ts?,
507
+ predicate: lambda { |f| Time.now - f[:timestamp] < 300 } # Last 5 minutes
526
508
 
527
- r.action = lambda { |facts, bindings|
528
- # Process recent data only
529
- }
509
+ perform do |facts, bindings|
510
+ # Process recent data only
511
+ end
512
+ end
530
513
  end
531
514
  ```
532
515
 
@@ -535,19 +518,27 @@ end
535
518
  When multiple agents could act, use priorities:
536
519
 
537
520
  ```ruby
538
- # High priority: Stop-loss overrides everything
539
- stop_loss = Rule.new("stop_loss", priority: 100)
521
+ kb = KBS.knowledge_base(engine: engine) do
522
+ # High priority: Stop-loss overrides everything
523
+ rule "stop_loss", priority: 100 do
524
+ # ...
525
+ end
540
526
 
541
- # Medium priority: Risk management
542
- risk_check = Rule.new("risk_check", priority: 50)
527
+ # Medium priority: Risk management
528
+ rule "risk_check", priority: 50 do
529
+ # ...
530
+ end
543
531
 
544
- # Low priority: Normal trading signals
545
- buy_signal = Rule.new("buy", priority: 10)
532
+ # Low priority: Normal trading signals
533
+ rule "buy", priority: 10 do
534
+ # ...
535
+ end
536
+ end
546
537
  ```
547
538
 
548
539
  ## Next Steps
549
540
 
550
541
  - **[Network Structure](network-structure.md)** - How blackboard integrates with RETE
551
542
  - **[Persistence Guide](../guides/persistence.md)** - Choosing and configuring backends
552
- - **[Multi-Agent Example](../examples/multi-agent.md)** - Complete working system
543
+ - **[Blackboard Examples](../examples/index.md#advanced-features)** - Complete working systems
553
544
  - **[Custom Persistence](../advanced/custom-persistence.md)** - Build your own backend
@@ -0,0 +1,65 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 380">
2
+ <defs>
3
+ <style>
4
+ .box { fill: #1e293b; stroke: #60a5fa; stroke-width: 2; }
5
+ .text { fill: #e2e8f0; font-family: 'Courier New', monospace; font-size: 13px; }
6
+ .title { fill: #60a5fa; font-family: 'Courier New', monospace; font-size: 14px; font-weight: bold; }
7
+ .label { fill: #94a3b8; font-family: 'Courier New', monospace; font-size: 12px; }
8
+ .arrow { stroke: #60a5fa; stroke-width: 2; fill: none; marker-end: url(#arrowhead); }
9
+ .cycle-arrow { stroke: #22d3ee; stroke-width: 2; fill: none; marker-end: url(#arrowhead-cyan); }
10
+ </style>
11
+ <marker id="arrowhead" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
12
+ <polygon points="0 0, 10 3, 0 6" fill="#60a5fa" />
13
+ </marker>
14
+ <marker id="arrowhead-cyan" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
15
+ <polygon points="0 0, 10 3, 0 6" fill="#22d3ee" />
16
+ </marker>
17
+ </defs>
18
+
19
+ <!-- Left side: Facts (DATA) -->
20
+ <text x="100" y="30" text-anchor="middle" class="title">Facts (DATA)</text>
21
+ <rect x="30" y="50" width="140" height="80" rx="5" class="box"/>
22
+ <text x="100" y="75" text-anchor="middle" class="label">Current State</text>
23
+ <text x="100" y="95" text-anchor="middle" class="label">:stock</text>
24
+ <text x="100" y="110" text-anchor="middle" class="label">:portfolio</text>
25
+
26
+ <!-- Right side: Rules (LOGIC) -->
27
+ <text x="500" y="30" text-anchor="middle" class="title">Rules (LOGIC)</text>
28
+ <rect x="430" y="50" width="140" height="80" rx="5" class="box"/>
29
+ <text x="500" y="75" text-anchor="middle" class="label">IF-THEN Logic</text>
30
+ <text x="500" y="95" text-anchor="middle" class="label">Conditions</text>
31
+ <text x="500" y="110" text-anchor="middle" class="label">Actions</text>
32
+
33
+ <!-- Arrow from Facts to Pattern Match -->
34
+ <path d="M 170 90 L 240 90" class="arrow"/>
35
+
36
+ <!-- Arrow from Rules to Pattern Match -->
37
+ <path d="M 430 90 L 360 90" class="arrow"/>
38
+
39
+ <!-- Center: Pattern Match and Execution -->
40
+ <rect x="210" y="170" width="180" height="140" rx="5" class="box"/>
41
+ <text x="300" y="195" text-anchor="middle" class="title">IF conditions</text>
42
+ <text x="300" y="215" text-anchor="middle" class="title">match facts</text>
43
+
44
+ <line x1="230" y1="230" x2="370" y2="230" stroke="#60a5fa" stroke-width="1" opacity="0.3"/>
45
+
46
+ <text x="300" y="255" text-anchor="middle" class="title">THEN execute</text>
47
+ <text x="300" y="275" text-anchor="middle" class="title">action</text>
48
+
49
+ <!-- Arrow pointing down to Pattern Match box -->
50
+ <path d="M 300 130 L 300 170" class="arrow"/>
51
+
52
+ <!-- Arrow from action box to new facts -->
53
+ <path d="M 300 310 L 300 340" class="arrow"/>
54
+ <text x="380" y="328" class="label">May create</text>
55
+ <text x="380" y="345" class="label">new facts</text>
56
+
57
+ <!-- New facts box at bottom -->
58
+ <rect x="210" y="345" width="180" height="30" rx="3" class="box"/>
59
+ <text x="300" y="365" text-anchor="middle" class="label">Facts added/removed</text>
60
+
61
+ <!-- Cycle arrow going back up -->
62
+ <path d="M 390 360 L 550 360 L 550 90 L 570 90" class="cycle-arrow"/>
63
+ <text x="555" y="225" class="label" fill="#22d3ee">↻</text>
64
+ <text x="490" y="372" class="label" fill="#22d3ee">(cycle continues)</text>
65
+ </svg>
@@ -0,0 +1,42 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 450 280">
2
+ <defs>
3
+ <style>
4
+ .box { fill: #1e293b; stroke: #60a5fa; stroke-width: 2; }
5
+ .text { fill: #e2e8f0; font-family: 'Courier New', monospace; font-size: 14px; }
6
+ .title { fill: #60a5fa; font-family: 'Courier New', monospace; font-size: 16px; font-weight: bold; }
7
+ .label { fill: #94a3b8; font-family: 'Courier New', monospace; font-size: 13px; }
8
+ .value { fill: #a5f3fc; font-family: 'Courier New', monospace; font-size: 13px; }
9
+ </style>
10
+ </defs>
11
+
12
+ <!-- Main container box -->
13
+ <rect x="30" y="20" width="390" height="240" rx="5" class="box"/>
14
+
15
+ <!-- Title -->
16
+ <text x="225" y="50" text-anchor="middle" class="title">Fact</text>
17
+
18
+ <!-- Separator line -->
19
+ <line x1="50" y1="65" x2="400" y2="65" stroke="#60a5fa" stroke-width="1" opacity="0.3"/>
20
+
21
+ <!-- Type section -->
22
+ <text x="70" y="95" class="label">Type:</text>
23
+ <text x="140" y="95" class="value">:temperature</text>
24
+
25
+ <!-- Separator line -->
26
+ <line x1="50" y1="110" x2="400" y2="110" stroke="#60a5fa" stroke-width="1" opacity="0.3"/>
27
+
28
+ <!-- Attributes section -->
29
+ <text x="70" y="140" class="label">Attributes:</text>
30
+
31
+ <text x="90" y="170" class="label">location:</text>
32
+ <text x="200" y="170" class="value">"server_room"</text>
33
+
34
+ <text x="90" y="195" class="label">value:</text>
35
+ <text x="200" y="195" class="value">85</text>
36
+
37
+ <text x="90" y="220" class="label">timestamp:</text>
38
+ <text x="200" y="220" class="value">2025-01-15 10:30:00</text>
39
+
40
+ <text x="90" y="245" class="label">sensor_id:</text>
41
+ <text x="200" y="245" class="value">42</text>
42
+ </svg>
@@ -0,0 +1,47 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 420">
2
+ <defs>
3
+ <style>
4
+ .box { fill: #1e293b; stroke: #60a5fa; stroke-width: 2; }
5
+ .text { fill: #e2e8f0; font-family: 'Courier New', monospace; font-size: 14px; }
6
+ .title { fill: #60a5fa; font-family: 'Courier New', monospace; font-size: 12px; font-weight: bold; }
7
+ .arrow { stroke: #60a5fa; stroke-width: 2; fill: none; marker-end: url(#arrowhead); }
8
+ .code { fill: #94a3b8; font-family: 'Courier New', monospace; font-size: 11px; }
9
+ </style>
10
+ <marker id="arrowhead" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
11
+ <polygon points="0 0, 10 3, 0 6" fill="#60a5fa" />
12
+ </marker>
13
+ </defs>
14
+
15
+ <!-- Step 1: Add Facts -->
16
+ <rect x="50" y="20" width="400" height="60" rx="5" class="box"/>
17
+ <text x="250" y="40" text-anchor="middle" class="title">1. ADD FACTS</text>
18
+ <text x="250" y="62" text-anchor="middle" class="code">kb.fact :temperature, value: 85</text>
19
+
20
+ <!-- Arrow 1 -->
21
+ <path d="M 250 80 L 250 110" class="arrow"/>
22
+
23
+ <!-- Step 2: Pattern Matching -->
24
+ <rect x="50" y="110" width="400" height="60" rx="5" class="box"/>
25
+ <text x="250" y="130" text-anchor="middle" class="title">2. PATTERN MATCHING</text>
26
+ <text x="250" y="152" text-anchor="middle" class="code">RETE network finds matching rules</text>
27
+
28
+ <!-- Arrow 2 -->
29
+ <path d="M 250 170 L 250 200" class="arrow"/>
30
+
31
+ <!-- Step 3: Fire Rules -->
32
+ <rect x="50" y="200" width="400" height="60" rx="5" class="box"/>
33
+ <text x="250" y="220" text-anchor="middle" class="title">3. FIRE RULES</text>
34
+ <text x="250" y="242" text-anchor="middle" class="code">Execute actions for complete matches</text>
35
+
36
+ <!-- Arrow 3 -->
37
+ <path d="M 250 260 L 250 290" class="arrow"/>
38
+
39
+ <!-- Step 4: Actions May Add/Remove Facts -->
40
+ <rect x="50" y="290" width="400" height="80" rx="5" class="box"/>
41
+ <text x="250" y="310" text-anchor="middle" class="title">4. ACTIONS MAY ADD/REMOVE FACTS</text>
42
+ <text x="250" y="332" text-anchor="middle" class="code">Cycle continues until no rules fire</text>
43
+
44
+ <!-- Feedback arrow -->
45
+ <path d="M 450 320 L 480 320 L 480 50 L 460 50" class="arrow"/>
46
+ <text x="485" y="185" class="text" font-size="11">↻</text>
47
+ </svg>
@@ -0,0 +1,43 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 420">
2
+ <defs>
3
+ <style>
4
+ .outer-box { fill: none; stroke: #60a5fa; stroke-width: 2; }
5
+ .inner-box { fill: #1e293b; stroke: #94a3b8; stroke-width: 1.5; }
6
+ .text { fill: #e2e8f0; font-family: 'Courier New', monospace; font-size: 13px; }
7
+ .title { fill: #60a5fa; font-family: 'Courier New', monospace; font-size: 15px; font-weight: bold; }
8
+ .subtitle { fill: #94a3b8; font-family: 'Courier New', monospace; font-size: 11px; }
9
+ .arrow { stroke: #60a5fa; stroke-width: 2; fill: none; marker-end: url(#arrowhead); }
10
+ </style>
11
+ <marker id="arrowhead" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
12
+ <polygon points="0 0, 10 3, 0 6" fill="#60a5fa" />
13
+ </marker>
14
+ </defs>
15
+
16
+ <!-- Outer container -->
17
+ <rect x="20" y="20" width="460" height="380" rx="5" class="outer-box"/>
18
+ <text x="250" y="45" text-anchor="middle" class="title">Knowledge Base</text>
19
+
20
+ <!-- DSL Layer -->
21
+ <rect x="50" y="60" width="400" height="70" rx="5" class="inner-box"/>
22
+ <text x="250" y="80" text-anchor="middle" class="text">DSL Layer (Your Interface)</text>
23
+ <text x="250" y="100" text-anchor="middle" class="subtitle">- rule(), fact(), run()</text>
24
+
25
+ <!-- Arrow DSL to Working Memory -->
26
+ <path d="M 250 130 L 250 155" class="arrow"/>
27
+
28
+ <!-- Working Memory -->
29
+ <rect x="50" y="155" width="400" height="70" rx="5" class="inner-box"/>
30
+ <text x="250" y="175" text-anchor="middle" class="text">Working Memory</text>
31
+ <text x="250" y="195" text-anchor="middle" class="subtitle">- Stores facts</text>
32
+ <text x="250" y="210" text-anchor="middle" class="subtitle">- Notifies observers</text>
33
+
34
+ <!-- Arrow Working Memory to RETE Engine -->
35
+ <path d="M 250 225 L 250 250" class="arrow"/>
36
+
37
+ <!-- RETE Engine -->
38
+ <rect x="50" y="250" width="400" height="90" rx="5" class="inner-box"/>
39
+ <text x="250" y="270" text-anchor="middle" class="text">RETE Engine</text>
40
+ <text x="250" y="290" text-anchor="middle" class="subtitle">- Pattern matching</text>
41
+ <text x="250" y="305" text-anchor="middle" class="subtitle">- Rule compilation</text>
42
+ <text x="250" y="320" text-anchor="middle" class="subtitle">- Inference execution</text>
43
+ </svg>
@@ -0,0 +1,44 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 520 380">
2
+ <defs>
3
+ <style>
4
+ .outer-box { fill: none; stroke: #60a5fa; stroke-width: 2; }
5
+ .inner-box { fill: #1e293b; stroke: #94a3b8; stroke-width: 1.5; }
6
+ .text { fill: #e2e8f0; font-family: 'Courier New', monospace; font-size: 13px; }
7
+ .title { fill: #60a5fa; font-family: 'Courier New', monospace; font-size: 16px; font-weight: bold; }
8
+ .subtitle { fill: #94a3b8; font-family: 'Courier New', monospace; font-size: 12px; }
9
+ .check { fill: #22c55e; font-size: 16px; }
10
+ .arrow { stroke: #60a5fa; stroke-width: 2; fill: none; marker-end: url(#arrowhead); }
11
+ </style>
12
+ <marker id="arrowhead" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
13
+ <polygon points="0 0, 10 3, 0 6" fill="#60a5fa" />
14
+ </marker>
15
+ </defs>
16
+
17
+ <!-- Main container -->
18
+ <rect x="20" y="20" width="480" height="340" rx="5" class="outer-box"/>
19
+ <text x="260" y="45" text-anchor="middle" class="title">Rule: "high_temperature_alert"</text>
20
+
21
+ <!-- Metadata section -->
22
+ <rect x="40" y="60" width="440" height="80" rx="5" class="inner-box"/>
23
+ <text x="260" y="80" text-anchor="middle" class="title" font-size="14">METADATA</text>
24
+ <text x="80" y="105" class="subtitle">- Name: "high_temperature_alert"</text>
25
+ <text x="80" y="125" class="subtitle">- Description: "Alert when..."</text>
26
+ <text x="350" y="125" class="subtitle">- Priority: 10</text>
27
+
28
+ <!-- Conditions section -->
29
+ <rect x="40" y="155" width="440" height="80" rx="5" class="inner-box"/>
30
+ <text x="260" y="175" text-anchor="middle" class="title" font-size="14">CONDITIONS (IF)</text>
31
+ <text x="80" y="200" class="check">✓</text>
32
+ <text x="100" y="200" class="subtitle">temperature in server_room > 80°F</text>
33
+ <text x="80" y="220" class="check">✓</text>
34
+ <text x="100" y="220" class="subtitle">sensor in server_room is active</text>
35
+
36
+ <!-- Arrow -->
37
+ <path d="M 260 235 L 260 255" class="arrow"/>
38
+ <text x="290" y="250" class="subtitle">(when ALL match)</text>
39
+
40
+ <!-- Action section -->
41
+ <rect x="40" y="255" width="440" height="80" rx="5" class="inner-box"/>
42
+ <text x="260" y="275" text-anchor="middle" class="title" font-size="14">ACTION (THEN)</text>
43
+ <text x="80" y="300" class="subtitle">→ send_alert(...)</text>
44
+ </svg>
@@ -114,7 +114,7 @@
114
114
  <rect x="160" y="810" width="280" height="90" class="prod-box" rx="5"/>
115
115
  <text x="300" y="840" class="node-title" text-anchor="middle">ProductionNode</text>
116
116
  <text x="300" y="860" class="label" text-anchor="middle">(rule: "trading_signal")</text>
117
- <text x="300" y="880" class="detail" text-anchor="middle">Action: if price < threshold,</text>
117
+ <text x="300" y="880" class="detail" text-anchor="middle">Action: if price &lt; threshold,</text>
118
118
  <text x="300" y="892" class="detail" text-anchor="middle">BUY symbol</text>
119
119
 
120
120
  <!-- Legend -->