今日のゴール
- Blackboardパターンの概念を理解する
- 複数エージェントによる協調問題解決を学ぶ
- AIシステムへの応用を理解する
Blackboardパターンとは
黒板(Blackboard)に複数の専門家が書き込み、協調して問題を解決するパターンです。
| 要素 | 役割 |
|---|---|
| Blackboard | 共有知識ベース(タプルスペース) |
| Knowledge Sources | 専門知識を持つエージェント |
| Control | 処理の調整(イベント駆動) |
タプルスペースとの相性
| 特性 | Blackboardでの活用 |
|---|---|
| 共有メモリ | 知識の共有 |
| パターンマッチ | 関連知識の検索 |
| イベント通知 | 知識追加時のトリガー |
| 疎結合 | エージェント間の独立性 |
基本構造
知識の表現
# 事実(確定した情報)
toTuple(strVal("fact"), strVal("category"), strVal("key"), strVal("value"))
# 仮説(推測)
toTuple(strVal("hypothesis"), strVal("name"), floatVal(confidence))
# 解決状態
toTuple(strVal("problem"), intVal(id), strVal("level"), strVal("data"))
基本操作
# 事実を追加
proc assertFact(category, key, value: string) {.async.} =
await blackboard.writeAsync(toTuple(
strVal("fact"), strVal(category), strVal(key), strVal(value)
))
# 事実を照会
proc queryFacts(category: string): Future[seq[Tuple]] {.async.} =
return await blackboard.readAllAsync(
toPattern(strVal("fact"), strVal(category), nilValue(), nilValue())
)
# 仮説を追加
proc addHypothesis(name: string, confidence: float) {.async.} =
await blackboard.writeAsync(toTuple(
strVal("hypothesis"), strVal(name), floatVal(confidence)
))
実践例: 診断システム
複数の知識源が協調して診断を行う例:
# 知識源1: 症状分析
proc symptomAnalyzer() {.async.} =
let symptoms = await queryFacts("symptom")
for s in symptoms:
case s[2].strVal
of "fever": await addHypothesis("infection", 0.6)
of "cough": await addHypothesis("respiratory", 0.5)
else: discard
# 知識源2: 組み合わせ分析
proc combinationAnalyzer() {.async.} =
let hypotheses = await blackboard.readAllAsync(hypothesisPattern)
if hasHypothesis("infection") and hasHypothesis("respiratory"):
await addHypothesis("flu", 0.8) # より高い確信度で診断
ポイント:
- 各エージェントは自分の専門領域だけを担当
- 知識が蓄積されるにつれて、より精度の高い結論に到達
漸進的問題解決
段階的に解決レベルを上げるパターン:
type SolutionLevel = enum
slRaw, slParsed, slAnalyzed, slSolved
proc levelUp(problemId: int, fromLevel, toLevel: SolutionLevel, data: string) {.async.} =
# 古いレベルを削除
discard await bb.tryTakeAsync(
toPattern(strVal("problem"), intVal(problemId), strVal($fromLevel), nilValue())
)
# 新しいレベルを追加
await bb.writeAsync(toTuple(
strVal("problem"), intVal(problemId), strVal($toLevel), strVal(data)
))
ポイント:
- 各段階の処理は独立したエージェント
- 失敗しても前のレベルに戻れる
- 並列処理が可能(異なる問題を同時に処理)
AI推論エンジン
ルールベースの推論をタプルスペースで実装:
type Rule = tuple[
conditions: seq[(string, string)], # 条件リスト
conclusion: (string, string) # 結論
]
let rules: seq[Rule] = @[
(conditions: @[("has", "feathers"), ("can", "fly")],
conclusion: ("is", "bird")),
(conditions: @[("is", "bird"), ("size", "small")],
conclusion: ("is", "sparrow")),
]
proc infer() {.async.} =
var changed = true
while changed:
changed = false
for rule in rules:
if await allConditionsMet(rule.conditions):
if not await hasFact(rule.conclusion):
await assertFact(rule.conclusion)
changed = true # 新しい事実が追加されたので再度チェック
推論の流れ:
- 初期事実:
has=feathers,can=fly,size=small - ルール1適用 →
is=birdを導出 - ルール2適用 →
is=sparrowを導出
ユースケース
| 分野 | 応用例 |
|---|---|
| 医療診断 | 症状から病気を推論 |
| 音声認識 | 音素→単語→文の段階的認識 |
| 画像解析 | エッジ→形状→物体の認識 |
| 計画立案 | 部分解の統合 |
| ゲームAI | 状況分析と戦略決定 |
まとめ
学んだこと
| トピック | ポイント |
|---|---|
| Blackboard | 共有知識ベースによる協調 |
| Knowledge Sources | 専門エージェントが知識を書き込み |
| 漸進的解決 | 段階的に解のレベルを上げる |
| 推論 | ルールベースの知識導出 |
いつ使うか
| 場面 | 推奨 |
|---|---|
| 複雑な診断 | Blackboard |
| 段階的変換 | 漸進的解決 |
| ルールベースAI | 推論エンジン |
| 単純なデータ処理 | Producer-Consumer |
演習問題
問題15-1: マルチエージェント診断
3つ以上の知識源を持つ診断システムを実装してください。
- 症状分析エージェント
- 患者属性分析エージェント(年齢、既往歴など)
- 環境分析エージェント(季節、流行など)
ヒント
- 各エージェントは独立した
asyncプロシージャ - 事実は
("fact", category, key, value)形式で統一 - 各エージェントは自分の担当カテゴリだけを読む
- 最終診断は全仮説の確信度を総合して決定
問題15-2: 優先度付き推論
確信度の高い仮説から優先的に処理する推論エンジンを実装してください。
ヒント
- 仮説を確信度でソート
- 閾値(例: 0.7)以上の仮説のみを次の推論に使用
-
readAllAsync→ ソート → 処理の順で実装 - 確信度の低い仮説は後回しにして、高い仮説で推論を進める
問題15-3: イベント駆動Blackboard
新しい事実が追加されたときに自動的に推論を実行するイベント駆動型システムを実装してください。
ヒント
-
evWriteイベントを購読 - 新しい事実が追加されたら関連する知識源をトリガー
-
subscribeAsyncでパターン("fact", *, *, *)を監視 - イベント受信時に
asyncCheckで推論を非同期実行