019
behavioral AI

Blackboard

Reference Wikipedia ↗
Blackboard — class diagram
Plate 019 class diagram

The Blackboard pattern provides a central repository (the Blackboard) of information that multiple, independent Knowledge Sources can access, modify, and react to. A Controller selects which Knowledge Sources are relevant at any given time based on the current state of the Blackboard, and applies their expertise to solve a complex problem. This allows for a flexible and extensible system when the problem-solving strategy isn’t known in advance or changes frequently.

It’s particularly useful in domains like artificial intelligence, speech recognition, and expert systems where a variety of specialized components need to collaborate to achieve a single goal. The Blackboard decouples the problem-solving logic from the individual knowledge sources, making it easier to add, remove, or modify components without disrupting the entire system.

Usage

The Blackboard pattern is commonly used in:

  • AI and Expert Systems: For tasks like image recognition, natural language processing, and automated reasoning, where different sources of knowledge (e.g., edge detection, grammar rules, inference engines) contribute to a final solution.
  • Speech Recognition Systems: Different modules for acoustic modeling, phoneme recognition, and language processing contribute to recognizing spoken words.
  • Complex Data Processing Pipelines: Where multiple stages of data transformation and analysis need to be applied reactively depending on the data’s contents.
  • Robotics: Coordinating actions of different robot components based on sensor input and environmental conditions.
  • Game AI: Managing the behavior of multiple game entities, allowing them to react and interact with each other in complex ways.

Examples

  • GraalVM’s Truffle Framework: Truffle uses a Blackboard pattern to represent the abstract syntax tree (AST) of code being executed. Different language implementations (knowledge sources) contribute to analyzing and optimizing this AST, and a central interpreter (controller) manages the execution. The AST effectively is the Blackboard.
  • OpenCV: OpenCV’s image processing pipeline utilizes a Blackboard approach, although not explicitly named as such. An image (the Blackboard) is passed through a series of filters and algorithms (Knowledge Sources). The output of one filter becomes the input for the next, with a central process orchestrating the pipeline and determining which algorithms to apply based on the image data. For example, object detection might involve edge detection, then shape analysis, then feature matching – each a knowledge source contributing to the overall “understanding” of the image.

Specimens

15 implementations
Specimen 019.01 Dart View specimen ↗

The Blackboard pattern is a computational architecture for solving problems that involve multiple, independent knowledge sources. A shared data structure, the “blackboard,” holds the problem state. Knowledge sources (independent modules) observe the blackboard and, when their conditions are met, execute to modify the state. This allows for flexible problem-solving without tight coupling between components.

This Dart implementation uses a simple Blackboard class to hold the problem state (a String in this case). KnowledgeSource classes have a condition function to check if they can act, and an action function to modify the blackboard. A BlackboardSystem orchestrates the process, repeatedly applying applicable knowledge sources until a solution is reached. The use of functions as arguments and the separation of condition/action logic are idiomatic for Dart’s functional and OOP capabilities, providing a clean and testable structure.

// blackboard.dart
class Blackboard {
  String state = '';
}

abstract class KnowledgeSource {
  bool condition(Blackboard blackboard);
  void action(Blackboard blackboard);
}

class Initializer implements KnowledgeSource {
  @override
  bool condition(Blackboard blackboard) => blackboard.state.isEmpty;

  @override
  void action(Blackboard blackboard) {
    blackboard.state = 'initial data';
    print('Initializer applied: blackboard.state = ${blackboard.state}');
  }
}

class Processor1 implements KnowledgeSource {
  @override
  bool condition(Blackboard blackboard) => blackboard.state == 'initial data';

  @override
  void action(Blackboard blackboard) {
    blackboard.state = 'processed by 1';
    print('Processor 1 applied: blackboard.state = ${blackboard.state}');
  }
}

class Processor2 implements KnowledgeSource {
  @override
  bool condition(Blackboard blackboard) => blackboard.state == 'processed by 1';

  @override
  void action(Blackboard blackboard) {
    blackboard.state = 'processed by 2';
    print('Processor 2 applied: blackboard.state = ${blackboard.state}');
  }
}

class BlackboardSystem {
  final Blackboard blackboard;
  final List<KnowledgeSource> knowledgeSources;

  BlackboardSystem(this.blackboard, this.knowledgeSources);

  void run() {
    while (true) {
      bool applied = false;
      for (var source in knowledgeSources) {
        if (source.condition(blackboard)) {
          source.action(blackboard);
          applied = true;
          break; // Apply only one source per cycle
        }
      }
      if (!applied) {
        print('No sources applied, system halted.');
        break;
      }
      if (blackboard.state == 'processed by 2') {
        print('Final State: ${blackboard.state}');
        break;
      }
    }
  }
}

void main() {
  var blackboard = Blackboard();
  var system = BlackboardSystem(blackboard, [
    Initializer(),
    Processor1(),
    Processor2(),
  ]);

  system.run();
}