30
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【圏論×AI×認知科学】村上春樹「ノルウェーの森」をLLMで形式化してみた

Last updated at Posted at 2025-12-06

はじめに

みなさん、こんにちは。@10long です。

TRIAL&RetailAI Advent Calendar 2025 の 7 日目の記事です。

昨日は @mrsd さんの『🚀 リバースプロキシの良さと必然性を再確認する』でした。インフラ構成は頻繁に触るものではないため、しばらく経つとすぐ忘れてしまいますね。特にリバースプロキシの必然性や「通信の主体」の概念は重要なので、この記事を定期的に見返して理解を定着させたいと思います。実践的でわかりやすくまとまっているので、備忘録として最適ですね!良記事ですので、ぜひご覧ください。

突然ですが、村上春樹の『ノルウェーの森』を読んだことはありますよね?もし、読んだことなければ、この記事が契機となって手に取ってもらえたら幸いです。

読んだことがある方なら、一度はこんな「もしも」を考えたことがあるかもしれません。

「もしキズキが死ななかったら、直子は救われたのだろうか?」

実はこの問い、数学的に検証できるんです。

本記事では、認知科学の自由エネルギー原理、AIエージェントのBDIアーキテクチャ、そして圏論を組み合わせて、小説の構造を形式化するフレームワークを提案します。

しのごの言わず、さっそく始めていきましょう!

Gemini_Generated_Image_xdgoaqxdgoaqxdgo.png

目次

  1. このフレームワークで何ができるのか
  2. 第1章:直子はなぜ救われなかったのか?——自由エネルギー原理で読み解く
  3. 第2章:キャラクターを「プログラム」として記述する——BDIアーキテクチャ
  4. 第3章:すべてを繋ぐ数学——圏論入門
  5. 第4章:実装編——Emacs org-modeで動かしてみよう
  6. 第5章:反実仮想分析を実行する
  7. 第6章:数学的付録
  8. まとめ
  9. 参考文献

このフレームワークで何ができるのか

本記事で紹介するフレームワークを使うと、以下のことが実現できます:

できること 使う理論 実用例
🔍 反実仮想分析 圏論(射の置換) 「あの時、違う選択をしていたら?」をシミュレーション
キャラクター検証 バリデーションルール 新キャラクターが物語世界に「合うか」を判定
🧠 心理的整合性チェック FEP + BDI キャラクターの行動が心理学的に妥当かを評価
📊 物語構造の可視化 圏(対象と射) 関係性のグラフ化

「え、そんなことできるの?」と思われるかもしれませんね。

それでは、一つずつ見ていきましょう!


第1章:直子はなぜ救われなかったのか?——自由エネルギー原理で読み解く

1.1 自由エネルギー原理(FEP)とは?

まず、最も重要な理論から説明します。

自由エネルギー原理は、神経科学者カール・フリストンが提唱した理論で、一言で言えば:

「生き物は、予測と現実のズレ(驚き)を最小化するように行動する」

という原理です。

例えば、あなたが暗い部屋でドアを開けたとき、「明るい廊下がある」と予測していたのに真っ暗だったら「驚き」ますよね。この驚き(サプライズ)を減らそうとするのが、脳の基本的な働きだという考え方です。

数式で書くと:

F = \underbrace{-\ln p(o)}_{\text{驚き}} + \underbrace{D_{KL}[q(s) \| p(s|o)]}_{\text{信念と現実のズレ}}

難しく見えますが、要するにこういうことです:

記号 意味 日常的な例
$o$ 観測(実際に見たもの) 「直子が泣いている」
$q(s)$ 信念(こうだと思っている) 「直子は元気なはず」
$p(s|o)$ 現実(本当はこう) 「直子は深く傷ついている」
$F$ 自由エネルギー ズレの大きさ

1.2 直子の悲劇を数式で理解する

では、これを『ノルウェーの森』の直子に当てはめてみましょう。

直子の内面モデル:

直子の生成モデル(世界の捉え方):
├── 信念: 「自分は壊れている」「性的な親密さは不可能」
├── 観測: 「ワタナベは優しい」「でも体が反応しない」
└── 予測誤差: 【巨大】← ここが問題!

直子は、キズキの死というトラウマによって、解消できない予測誤差を抱え続けているといえます。

普通なら、人は2つの方法で予測誤差を減らします:

  1. 信念を更新する(考え方を変える)
  2. 行動で環境を変える(状況を変える)

しかし、直子の場合:

\text{予測誤差} = o - g(\mu) \neq 0 \quad \text{(常にゼロにならない)}

どんなに阿美寮で治療を受けても、レイコから熱心に支えられても、信念(「自分は壊れている」)が更新されない

そして最終的に、予測誤差を完全にゼロにする唯一の方法——自ら???——を選んでしまいます。

これは、FEPの観点から見ると、局所最適解に陥った最適化の失敗と解釈できるのではないでしょうか。

1.3 緑はなぜ「生きられる」のか?

対照的に、緑について見てみましょう。

緑の生成モデル:

緑の生成モデル:
├── 信念: 「喪失は人生の一部」「私は私として生きる」
├── 観測: 「父は死んだ」「でも世界は続く」
└── 予測誤差: 【統合済み】← 喪失を「データ」として処理

緑も立て続けに両親を亡くすという喪失を経験しています。しかし、彼女の生成モデルは喪失を壊滅的なエラーではなく、統合されたデータとして扱います。

F_{\text{緑}} < F_{\text{直子}}

だから緑は柔軟に生きられる。これが「強さ」の正体です。


第2章:キャラクターを「プログラム」として記述する——BDIアーキテクチャ

2.1 BDIとは?

次に、キャラクターの心をプログラムとして記述する方法を紹介します。

BDI(Belief-Desire-Intention)アーキテクチャは、AIエージェントの設計手法で、人間の意思決定を3つの要素でモデル化します:

要素 英語 意味 例(直子の場合)
B Belief 信念(世界をどう認識しているか) 「自分は壊れている」
D Desire 欲求(何を望んでいるか) 「普通になりたい」「ワタナベを愛したい」
I Intention 意図(実際に何をするか) 「阿美寮で治療する」→ 失敗 → ???

2.2 『ノルウェーの森』の主要キャラクターをBDIで記述

さてさて、ここからが面白いところです。主要キャラクターをJavaScriptのオブジェクトとして記述してみましょう:

// ============================================
// 型定義
// ============================================

/** 信念の種類 */
type Belief = 
  // 直子の信念
  | "self_is_damaged"        // 自分は壊れている
  | "death_surrounds_me"     // 死が自分を取り囲んでいる
  | "intimacy_impossible"    // 親密さは不可能
  // ワタナベの信念
  | "naoko_needs_me"         // 直子は自分を必要としている
  | "midori_offers_life"     // 緑は生を提供してくれる
  | "cannot_choose"          // 選べない
  // 緑の信念
  | "loss_is_integrated"     // 喪失は統合されている
  | "life_continues"         // 人生は続く
  | "self_is_whole";         // 自分は全体である

/** 欲求の種類 */
type Desire = 
  | "be_normal"              // 普通でありたい
  | "love_watanabe"          // ワタナベを愛したい
  | "escape_pain"            // 苦しみから逃れたい
  | "save_naoko"             // 直子を救いたい
  | "embrace_life"           // 生を受け入れたい
  | "avoid_pain"             // 痛みを避けたい
  | "live_fully"             // 全力で生きたい
  | "maintain_autonomy";     // 自律性を保ちたい

/** 計画の種類 */
type Plan = 
  | "heal_at_hostel"         // 療養所で治療する
  | "oscillate"              // 振動し続ける
  | "active_engagement"      // 積極的な関与
  | "???";                   // 未定義

/** 意図の状態 */
type IntentionStatus = 
  | "active"                 // 実行中
  | "successful"             // 成功
  | "failed"                 // 失敗
  | "unresolved";            // 未解決

/** 意図 */
interface Intention {
  plan: Plan;
  status: IntentionStatus;
}

/** BDIエージェント */
interface BDIAgent {
  name: string;
  beliefs: Belief[];
  desires: Desire[];
  intentions: Intention[];
}

// ============================================
// キャラクター定義
// ============================================

/** 直子のBDIモデル */
const Naoko: BDIAgent = {
  name: "直子",
  beliefs: [
    "self_is_damaged",       // 自分は壊れている
    "death_surrounds_me",    // 死が自分を取り囲んでいる
    "intimacy_impossible"    // 親密さは不可能
  ],
  desires: [
    "be_normal",             // 普通でありたい
    "love_watanabe",         // ワタナベを愛したい
    "escape_pain"            // 苦しみから逃れたい
  ],
  intentions: [
    { plan: "heal_at_hostel", status: "failed" },
    { plan: "???", status: "unresolved" }  // ← 意図が形成できない
  ]
};

/** ワタナベのBDIモデル */
const Watanabe: BDIAgent = {
  name: "ワタナベ",
  beliefs: [
    "naoko_needs_me",        // 直子は自分を必要としている
    "midori_offers_life",    // 緑は生を提供してくれる
    "cannot_choose"          // 選べない
  ],
  desires: [
    "save_naoko",            // 直子を救いたい
    "embrace_life",          // 生を受け入れたい
    "avoid_pain"             // 痛みを避けたい
  ],
  intentions: [
    { plan: "oscillate", status: "active" }  // 振動し続ける
  ]
};

/** 緑のBDIモデル */
const Midori: BDIAgent = {
  name: "小林緑",
  beliefs: [
    "loss_is_integrated",    // 喪失は統合されている
    "life_continues",        // 人生は続く
    "self_is_whole"          // 自分は全体である
  ],
  desires: [
    "live_fully",            // 全力で生きたい
    "love_watanabe",         // ワタナベを愛したい
    "maintain_autonomy"      // 自律性を保ちたい
  ],
  intentions: [
    { plan: "active_engagement", status: "successful" }
  ]
};

// ============================================
// ユーティリティ関数
// ============================================

/** 意図形成が可能かチェック */
function canFormIntention(agent: BDIAgent): boolean {
  // 未解決の意図があるかチェック
  return !agent.intentions.some(i => i.status === "unresolved");
}

/** 自由エネルギー(心理的安定性)を簡易計算 */
function calculateFreeEnergy(agent: BDIAgent): number {
  const failedIntentions = agent.intentions.filter(i => i.status === "failed").length;
  const unresolvedIntentions = agent.intentions.filter(i => i.status === "unresolved").length;
  
  // 失敗・未解決の意図が多いほど自由エネルギーが高い(不安定)
  return failedIntentions * 0.3 + unresolvedIntentions * 0.5;
}

// ============================================
// 実行例
// ============================================

const characters: BDIAgent[] = [Naoko, Watanabe, Midori];

characters.forEach(char => {
  console.log(`\n【${char.name}】`);
  console.log(`  意図形成可能: ${canFormIntention(char)}`);
  console.log(`  自由エネルギー: ${calculateFreeEnergy(char).toFixed(2)}`);
});

// 出力:
// 【直子】
//   意図形成可能: false
//   自由エネルギー: 0.80
//
// 【ワタナベ】
//   意図形成可能: true
//   自由エネルギー: 0.00
//
// 【小林緑】
//   意図形成可能: true
//   自由エネルギー: 0.00

2.3 悲劇の構造:信念が意図を殺す

ここで重要なのは、直子の悲劇はBDI構造から必然的に導かれるということです。

直子の欲求: "be_normal" (普通でありたい)
     ↓
計画生成: どうすれば普通になれる?
     ↓
信念チェック: 「自分は壊れている」が TRUE
     ↓
結果: すべての計画が【文脈条件を満たさない】
     ↓
意図形成: 【失敗】

これは「著者がそう書いたから」ではなく、信念構造から論理的に導かれる帰結なのです。

なかなか面白いですよね?


第3章:すべてを繋ぐ数学——圏論入門

3.1 なぜ圏論が必要なのか?

ここまでで、FEP(心の理論)とBDI(プログラム)を見てきました。

でも、疑問が残ります:

  • FEPとBDIはどう関係するの?
  • 複数のキャラクターの関係はどう表現するの?
  • 「もしも」の分析はどう数学的に行うの?

これらを統一的に扱うために、圏論を使います。

3.2 圏論の超入門

圏論は「構造の構造」を研究する数学です。怖がらなくて大丈夫です。基本は2つだけ:

圏 = { 対象(Objects), 射(Morphisms) }
概念 数学的な意味 『ノルウェーの森』での解釈
対象 点、もの キャラクター、場所、状態
矢印、変換 関係性、因果、影響

例えば、キャラクター間の関係を圏として表現すると:

3.3 ベイズレンズ:双方向の因果を表現する

圏論の強力な道具の一つがベイズレンズです。

通常の関数は一方向ですが、レンズは双方向の関係を表現できます:

\text{レンズ}(c, c^\dagger): \begin{cases}
c: X \to Y & \text{(順方向:原因→結果)} \\
c^\dagger: Y \to X & \text{(逆方向:結果→原因の推論)}
\end{cases}

これにより:

  • 順方向: 「キズキの死 → 直子のトラウマ」
  • 逆方向: 「直子のトラウマから、何が原因だったかを推論」

という双方向の分析が可能になります。

3.4 反実仮想分析の数学的基礎

「もしキズキが死ななかったら」という反実仮想は、圏論では射の置換として表現できます:

元の物語世界(圏 NW):
  キズキ ──[死]──→ 直子の崩壊 ──→ 悲劇

反実仮想世界(圏 NW'):
  キズキ ──[生存]──→ ??? ──→ ???

射を置き換えたとき、関手(圏から圏への変換)を通じて、物語全体がどう変わるかを追跡できます。


第4章:実装編——Emacs org-modeで動かしてみよう

4.1 システム全体像

ぼくは普段、Emacsのorg-mode内で作業することが多いので、org-modeベースの実装を考えてみました。

4.2 Org-modeでのキャラクター定義

実際にorg-modeで使えるフォーマットを定義します:

* Characters
:PROPERTIES:
:CATEGORY: NorwegianWood
:END:

** 直子
:PROPERTIES:
:ENTITY_TYPE: Character
:CUSTOM_ID: naoko
:END:

*** Beliefs(信念)
- damaged_self: 0.95
- death_surrounds: 0.90
- intimacy_impossible: 0.85

*** Desires(欲求)
- be_normal: 0.90
- love_toru: 0.80
- escape_pain: 0.95

*** Intentions(意図)
- heal_at_hostel: FAILED
- find_peace: UNRESOLVED

*** Generative Model(生成モデル)
:PROPERTIES:
:PRECISION_TRAUMA: 0.95
:PRECISION_POSITIVE: 0.10
:FREE_ENERGY: HIGH
:END:

4.3 バリデーションルールの定義

3段階の診断レベルを定義します。LSPの診断レベルと同じ考え方ですね:

// バリデーションレベル
enum DiagnosticLevel {
  ERROR = 1,      // 構造的に不可能(物語が成立しない)
  WARNING = 2,    // 心理的に不自然(説明が必要)
  INFO = 3        // 推奨(より良くできる)
}

// バリデーションルール
const validationRules = [
  {
    id: "BDI-001",
    level: ERROR,
    check: (char) => char.beliefs.length > 0,
    message: "キャラクターには最低1つの信念が必要です"
  },
  {
    id: "BDI-002", 
    level: WARNING,
    check: (char) => !hasContradictoryBeliefs(char),
    message: "矛盾する信念があります(意図的でない場合は修正を)"
  },
  {
    id: "FEP-001",
    level: WARNING,
    check: (char) => char.freeEnergy !== "EXTREME",
    message: "自由エネルギーが極端に高い状態が続くと破綻します"
  },
  {
    id: "NW-001",
    level: INFO,
    check: (char) => hasConnectionToDeath(char),
    message: "『ノルウェーの森』のキャラクターは死との関係性を持つことが多いです"
  }
];

4.4 新キャラクターのバリデーション例

新しいキャラクターを追加して、検証してみましょう:

新キャラクター:佐藤

Gemini_Generated_Image_8zgx5x8zgx5x8zgx.png

早稲田大学商学部2年生、19歳。横浜出身の中流家庭育ち。テニスサークル所属で、合コン・バイト・飲み会が日常。身近な人の死や大きな挫折を経験したことがなく、「人生は楽しんだもん勝ち」がモットー。友人は広く浅く100人以上いるが、誰とも深い関係を築いたことがない。哲学的な話や死について考えることを避け、「難しく考えない」をポリシーとする。悪人ではないが、ワタナベや直子の内面世界を理解することは構造的に不可能な存在。

** 新キャラクター案:佐藤
:PROPERTIES:
:ENTITY_TYPE: Character
:CUSTOM_ID: sato
:END:

*** Beliefs
- life_is_simple: 0.90
- no_past_trauma: 0.95
- relationships_are_easy: 0.85

*** Desires  
- have_fun: 0.90
- avoid_complexity: 0.80

*** Intentions
- enjoy_college_life: ACTIVE

バリデーション結果:

⚠ WARNING [NW-001]: 死との関係性が定義されていません
  → 『ノルウェーの森』の世界観では、キャラクターは何らかの
    形で死や喪失と向き合っています。
    
⚠ WARNING [NW-002]: 内面の傷が見当たりません
  → このキャラクターは物語のトーンと合わない可能性があります。
  
ℹ INFO [NW-003]: 推奨
  → 「過去のトラウマ」または「喪失の経験」を追加すると、
    より物語世界に馴染みます。

いい感じですね!このキャラクターは『ノルウェーの森』の世界観には合わないと判定されました。


第5章:反実仮想分析を実行する

5.1 分岐点(Critical Morphisms)の定義

物語の重要な分岐点を定義します:

// 物語の分岐点を定義
const criticalMorphisms = [
  {
    id: "CM-001",
    description: "キズキの死",
    original: { from: "kizuki", to: "death", type: "suicide" },
    counterfactual: { from: "kizuki", to: "survival", type: "lives" }
  },
  {
    id: "CM-002", 
    description: "直子の選択",
    original: { from: "naoko", to: "ami_hostel", type: "retreat" },
    counterfactual: { from: "naoko", to: "tokyo_with_toru", type: "confront" }
  },
  {
    id: "CM-003",
    description: "ワタナベの決断",
    original: { from: "watanabe", to: "oscillation", type: "indecision" },
    counterfactual: { from: "watanabe", to: "commitment", type: "choose_one" }
  }
];

5.2 反実仮想分析のプロンプト

LLMに以下のプロンプトを与えて分析を実行します:

# 反実仮想分析プロンプト

## 現在の物語構造
{
  "morphism": "キズキ → 死",
  "effects": [
    "直子: trauma_level = 0.95",
    "ワタナベ: guilt_level = 0.60",
    "物語: death_theme_activated = true"
  ]
}

## 変更する射
キズキ → 死  を  キズキ → 生存  に変更

## 分析してください
1. 直接的影響:直子、ワタナベへの即時的な変化
2. 伝播効果:物語全体のトーンやテーマへの影響
3. 整合性評価:変更後の物語は『ノルウェーの森』として成立するか?
4. 自由エネルギー変化:各キャラクターの心理的安定性の変化

5.3 分析結果の例

さてさて、分析結果が気になるところですよね。出力した結果がこちらです!

反実仮想分析結果:
  scenario: "キズキが生存した場合"
  
  直接的影響:
    直子:
      - trauma_level: 0.95 → 0.30
      - belief_damaged_self: 0.95 → 0.40
      - relationship_with_kizuki: "継続"
    ワタナベ:
      - guilt_level: 0.60 → 0.10
      - relationship_with_naoko: "友人のまま"
      
  伝播効果:
    - 阿美寮のエピソード: 【消失】
    - レイコとの関係: 【発生しない】
    - 緑との恋愛: 【早期に発展】
    - 死のテーマ: 【大幅に弱体化】
    
  整合性評価:
    score: 0.25  # 低い
    reason: >
      『ノルウェーの森』の核心テーマ「喪失と再生」が
      成立しなくなる。物語は「普通の青春小説」になり、
      村上春樹作品としてのアイデンティティを失う。
      
  結論: >
    キズキの死は物語の【必然的な射】であり、
    これを変更すると圏NWは本質的に異なる圏NW'になる。
    (同型ではなく、別の物語)

なるほど、キズキの死は物語にとって必然的な射であることが分析から導かれました!


第6章:数学的付録

興味のある方向けに、数学的な詳細を記載しておきます。

6.1 自由エネルギーの完全な定式化

F = \underbrace{D_{KL}[q(s) \| p(s)]}_{\text{複雑性コスト}} - \underbrace{\mathbb{E}_{q(s)}[\ln p(o|s)]}_{\text{精度(期待対数尤度)}}

6.2 期待自由エネルギー(方策選択)

G(\pi) = \underbrace{D_{KL}[q(o|\pi) \| p(o)]}_{\text{リスク(目標からの乖離)}} + \underbrace{\mathbb{E}_{q(o|\pi)}[H[q(s|o, \pi)]]}_{\text{曖昧性(不確実性)}}

6.3 ベイズレンズの合成

(c_1, c_1^\dagger) \circ (c_2, c_2^\dagger) = (c_1 \circ c_2, c_2^\dagger \circ c_1^\dagger)

これにより、階層的な因果モデルを構築・反転できます。


まとめ

いかがでしたでしょうか?

本記事では、認知科学の自由エネルギー原理、AIエージェントのBDIアーキテクチャ、そして圏論を組み合わせて、『ノルウェーの森』を形式化するフレームワークを提案しました。

できるようになったこと

できること 使う理論 実用例
キャラクターの心理を形式化 FEP + BDI 心理的整合性の自動チェック
「もしも」の分析 圏論(射の置換) 物語の分岐点シミュレーション
新キャラクターの検証 バリデーションルール 世界観との整合性チェック
物語構造の可視化 圏(対象と射) 関係性のグラフ化

今後の展望

  • 他の村上作品への自然変換: 『1Q84』『ねじまき鳥クロニクル』
  • 創作支援ツール: 小説家向けの整合性チェッカー
  • ゲームAI: より深みのあるNPCの設計

直子や緑が、このフレームワークを見たらなんて言うんだろうなぁ。面白がるのか、はたまた「そんな単純じゃないのよ」と言うのか。やれやれ。

この先ちゃんとした物語分析AIエージェントが登場したら、ぜひインタビューしてみたいものですね。

ここまでお読みいただき、ありがとうございました!


さいごに

この記事が面白いと思ったら、いいねをお願いします!🙏

昨年書いた「leanによる定理証明AIエージェント遊戯」のマルチエージェントと今回の内容を組み合わせてみても面白そうです。

質問やフィードバックはコメント欄へどうぞ。実装の詳細やorg-modeの設定ファイルなど、続編も検討中です。

明日のアドベントカレンダーは、@mossan_hoshi さんの『AIコーディングの極意 ~AIは〇〇と思え~』です。お楽しみに!

また、RetailAIとTRIALではエンジニアを募集しています。
興味がある方はご連絡ください!


参考文献

  1. Free Energy Principle - Wikipedia
  2. Friston, K. (2010). "The free-energy principle: a unified brain theory?" Nature Reviews Neuroscience
  3. Rao, A. S., & Georgeff, M. P. (1995). "BDI Agents: From Theory to Practice"
  4. Smithe, T. S. C. (2021). "Compositional Active Inference"
  5. 村上春樹 (1987). 『ノルウェーの森』講談社
  6. Lean 4 - 定理証明支援系
  7. OpenAI Swarm - マルチエージェントフレームワーク
30
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?