mcpField
M
memory-literary-analysis

memory-literary-analysis

github.com/basicmachines-co生産性4.4k1.2k 回インストール今日更新
リポジトリ

memory-literary-analysis について

文学作品全体をBasic Memoryの構造化された知識グラフにまとめるスキルです。章ごとの分析、エンティティの補足、検証、統合を支援し、学習・教育・研究に使えます。

主な機能

  1. 01章ごとのノート作成
  2. 02文学エンティティのスキーマ定義
  3. 03人物とテーマのノート連携
  4. 04グラフの検証と統合分析
  5. 05範囲を指定した原文読み込み

活用シーン

  1. 01小説の学習資料の作成
  2. 02戯曲を通じたテーマの追跡
  3. 03精読研究の情報整理

MCP 対応

Basic Memory MCPのbm_read、bm_find、bm_search、bm_listでノートやメタデータを読み取り、bm_writeとbm_editで書き込みます。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/basicmachines-co/basic-memory --skill memory-literary-analysis

インストール後、Claude Code で/memory-literary-analysisを入力して読み込まれたか確認します。

詳細

文学作品のメモリー分析

文学作品全体を、構造化されたナレッジグラフに変換します。登場人物、テーマ、章、場所、象徴、文学的技法を相互につながるノートにまとめ、検索、検証、たどり歩きができるようにします。

使用する場面

  • 小説、戯曲、詩、ノンフィクション作品を最初から最後まで分析する
  • 文学作品の教材や学習用リソースを作成する
  • 読書会向けのナレッジベースを作成する
  • 構造化された精読を必要とする研究プロジェクト
  • Basic Memory の大規模な負荷テスト(ノート約200件以上、関係1000件以上)

パイプラインの概要

Phase 0: Setup         → project, schemas, directory structure
Phase 1: Seed          → stub notes for known major entities
Phase 2: Process       → chapter-by-chapter notes in batches
Phase 3: Cross-ref     → enrich arcs, add parallels, write analysis
Phase 4: Validate      → schema checks, drift detection, consistency
Phase 5: Explore       → traverse the graph, write synthesis notes

ツール

書き込みには bm_write と bm_edit を使います。MCP での読み取りには bm_read、bm_find、 bm_search、bm_list を使います。CLI では POSIX の動詞をそのまま使用します。以下の表とシェルの例は CLI コマンドです(各動詞の前に bm を付けてください)。

必要な操作使用するもの代わりに使用しないもの
長いノートの一部分cat <note> --section Observationsノート全体を読む
原文の一部の行範囲cat <source>.txt --lines 4200-4890本全体をコンテキストに読み込む
フロントマターに一致するノートfind --meta status=activeフィールドを確認するためにノートを読む
多数のノートにまたがるフィールドfind --meta ... --fields pov,settingノートごとに1回ずつ読み取る
対象が保存されている場所ls、tree、find --name '*.md'すべてを一覧表示する

100章以上にわたる作業で重要なルールは、次の2つです。

  • フィールドを確認するためにノートを読まないでください。 そのために --meta の述語と --fields による投影があります。1回の呼び出しで、ノートごとに読み取りを繰り返す場合に必要な処理を実行できます。
  • ファイルの一部分を取得するために、ファイル全体をコンテキストに読み込まないでください。 セクションと行範囲の指定は、出力を切り取ります。ノート全体を取得してから、返す前に短くします。節約できるのはコンテキストであり、I/O ではありません。長い章や原文全体を扱う場合に必要なトークンは、ファイル全体ではなく該当部分の分だけです。

クエリを書く前に知っておきたい、注意すべき点が2つあります。1つ目は気付かないうちに失敗するため、警告のない短い回答と終了コード0だけが返されます。監査できていると思い込んでいたグラフから学ぶのではなく、ここで覚えておきましょう。

  • find はページ分割され、デフォルトのページサイズは10です。 回答が「全N章」となるクエリでは、--page-size 200(最大値)を指定する必要があります。網羅性の確認を参照してください。
  • --name と --meta は併用できません。 メタデータ検索ではファイル名の glob を指定できません。位置引数のパスで --meta クエリの範囲を絞り込んでください。例:find /characters --meta 'note_type=character'。そのパスは、ノートのパーマリンクではなく、ノートがインデックス化されているファイルパスに対し、ディレクトリ境界に沿って照合されます。ノートにフロントマターで permalink: が設定されるか、移動されると、パーマリンクはファイルパスを反映しなくなるためです。したがって、/characters は characters/ 配下(characters/major/ を含む)に保存されたものをすべて対象とし、characters-cut/ は対象にしません。

MCP では、部分取得に bm_read(identifier="<note>", section="Observations") または start_line/end_line を使います。メタデータの取得には bm_find(filters={"type": "chapter"}, fields=["chapter_number", "pov"])、一致する行の検索には bm_search(query="doubloon", lines=2) を使います。bm_list はフォルダーを1階層分だけ一覧表示し、bm_find は再帰的に検索します。 MCP の list/find/search のページサイズは最大100です。has_more を使って、すべてのページを取得してください。 以下の CLI のページサイズとフラグは、MCP のパラメーターとは別です。

フェーズ0:セットアップ

プロジェクトを作成する
bm_project(command="create", name="<work-name>", path="~/basic-memory/<work-name>")

作品のタイトルを kebab-case にしたスラッグを使用してください(例:great-gatsby、hamlet、beloved)。

スキーマを定義する

6つのスキーマノートを schema/ に書き込みます。各スキーマでは、エンティティタイプのフィールド、観察カテゴリ、関係タイプを定義します。作品に合うようフィールドを調整してください。以下のスキーマは出発点であり、厳格なテンプレートではありません。

Character スキーマ
bm_write(
  title="Character",
  directory="schema",
  note_type="schema",
  metadata={
    "entity": "Character",
    "version": 1,
    "schema": {
      "role(enum)": "[protagonist, antagonist, supporting, minor], character's narrative role",
      "description": "string, brief character description",
      "first_appearance?": "string, chapter or scene of first appearance",
      "status?(enum)": "[alive, dead, unknown, transformed], character status at end of work"
    },
    "settings": {"validation": "warn"}
  },
  content="""# Character

Schema for character entity notes.

## Observations
- [convention] Major characters in characters/major/, minor in characters/minor/
- [convention] Observation categories: trait, motivation, arc, quote, appearance, relationship, symbolism, fate
- [convention] Relations: appears_in, contrasts_with, allied_with, commands, symbolizes, associated_with"""
)

必要に応じて作品固有のフィールドを追加します。たとえば、軍事小説では rank、一族を描く物語では house、ファンタジーでは species などです。

Theme スキーマ
bm_write(
  title="Theme",
  directory="schema",
  note_type="schema",
  metadata={
    "entity": "Theme",
    "version": 1,
    "schema": {
      "description": "string, what this theme explores",
      "prevalence(enum)": "[major, minor], how central to the work",
      "first_introduced?": "string, where theme first appears"
    },
    "settings": {"validation": "warn"}
  },
  content="""# Theme

Schema for thematic analysis notes.

## Observations
- [convention] Observation categories: definition, manifestation, evolution, counterpoint, quote, interpretation
- [convention] Relations: embodied_by, contrasts_with, reinforced_by, explored_in, expressed_through"""
)
Chapter スキーマ
bm_write(
  title="Chapter",
  directory="schema",
  note_type="schema",
  metadata={
    "entity": "Chapter",
    "version": 1,
    "schema": {
      "chapter_number": "integer, sequential chapter number",
      "pov?": "string, point-of-view character or narrator mode",
      "setting?": "string, primary location",
      "narrative_mode?(enum)": "[dramatic, expository, reflective, epistolary, mixed], chapter's primary mode"
    },
    "settings": {"validation": "warn"}
  },
  content="""# Chapter

Schema for chapter-level analysis notes.

## Observations
- [convention] Chapters stored in chapters/ directory
- [convention] Observation categories: summary, event, tone, technique, quote, significance, foreshadowing
- [convention] Relations: features, set_in, explores, contains, employs, follows, precedes, parallels"""
)
Location スキーマ
bm_write(
  title="Location",
  directory="schema",
  note_type="schema",
  metadata={
    "entity": "Location",
    "version": 1,
    "schema": {
      "description": "string, what this place is",
      "location_type(enum)": "[city, building, landscape, body_of_water, region, fictional, vehicle], type of place",
      "real_or_fictional(enum)": "[real, fictional, both], whether the place exists"
    },
    "settings": {"validation": "warn"}
  },
  content="""# Location

Schema for location and setting notes.

## Observations
- [convention] Observation categories: description, atmosphere, symbolism, significance, geography
- [convention] Relations: setting_for, associated_with, symbolizes, contains, part_of"""
)
Symbol スキーマ
bm_write(
  title="Symbol",
  directory="schema",
  note_type="schema",
  metadata={
    "entity": "Symbol",
    "version": 1,
    "schema": {
      "description": "string, what the symbol is literally",
      "symbol_type(enum)": "[object, animal, color, action, natural_phenomenon, body_part], category of symbol",
      "primary_meaning": "string, most common interpretation"
    },
    "settings": {"validation": "warn"}
  },
  content="""# Symbol

Schema for symbolic element notes.

## Observations
- [convention] Observation categories: meaning, appearance, ambiguity, interpretation, quote, evolution
- [convention] Relations: represents, associated_with, appears_in, contrasts_with, located_at"""
)
LiteraryDevice スキーマ
bm_write(
  title="LiteraryDevice",
  directory="schema",
  note_type="schema",
  metadata={
    "entity": "LiteraryDevice",
    "version": 1,
    "schema": {
      "description": "string, what the device is",
      "device_type(enum)": "[rhetorical, structural, figurative, narrative, dramatic], category",
      "frequency(enum)": "[pervasive, frequent, occasional, rare], how often used"
    },
    "settings": {"validation": "warn"}
  },
  content="""# LiteraryDevice

Schema for literary technique and device notes.

## Observations
- [convention] Observation categories: definition, usage, effect, example, significance
- [convention] Relations: used_in, characterizes, expresses, related_to"""
)
ディレクトリ構成
<project>/
  <work>.txt         # the source text, verbatim (see Phase 2)
  schema/            # 6 schema definitions
  chapters/          # one note per chapter/section + prologue/epilogue
  characters/
    major/           # protagonist, antagonist, key supporting
    minor/           # named characters with limited roles
  themes/            # thematic analysis notes
  locations/         # settings and places
  symbols/           # symbolic elements
  literary-devices/  # techniques and devices
  analysis/          # cross-cutting synthesis
  tasks/             # processing tracker

フェーズ 1:主要エンティティの登録

章の処理を始める前に、主要エンティティのスタブノートを作成し、最初から [[wiki-links]] が解決されるようにします。

登場人物(主要)

主要な登場人物ごとに、既知のメタデータを含むスタブを作成します。

bm_write(
  title="<Character Name>",
  directory="characters/major",
  note_type="Character",
  tags=["character", "major", "<role>"],
  metadata={"role": "<role>", "description": "<brief description>"},
  content="""# <Character Name>

## Observations
- [role] <Character's role in the work>
- [appearance] <Key physical description>

## Relations
- associated_with [[<Related Character>]]
- appears_in [[<Key Location>]]"""
)
登録チェックリスト

読み始める前に、作品の主要エンティティを特定します。最初のインベントリとして、以下が適しています。

種類一般的な数含めるもの
登場人物(主要)8-20主人公、敵役、重要な脇役
テーマ5-12作品が掘り下げる中心的な問題
場所4-10主な舞台、象徴的に重要な場所
シンボル4-10多層的な意味を持つ、繰り返し登場する物、イメージ、モチーフ

スタブは完全である必要はありません。[[wiki-link]] のリンク先となり、章の処理中に内容が充実していきます。

フェーズ 2:章の処理

原文の準備

全文を入手し、章やセクションの境界を特定します。パブリックドメインの作品には、Project Gutenberg が適しています。著作権で保護された作品の場合は、物理的な複製またはライセンスを受けたデジタル版を使用します。

原文はプロジェクトディレクトリ内に .txt として置き、一度だけインデックス化します。 bm cat が解決するのはファイルシステムのパスではなく、ノート識別子です。プロジェクトのインデックスが認識したファイルにしかアクセスできません。一度インデックスを作成すると、原文にエンティティ行が追加され、その後は行範囲を指定して取得できます。

cp ~/Downloads/moby-dick.txt ~/basic-memory/moby-dick/moby-dick.txt
bm reindex --search -p moby-dick          # one pass; the .txt becomes readable

この方法が適している理由となる制約が 2 つあります。どちらも守る価値があります。

  • .txt のままにし、.md に変換しないでください。 Basic Memory は Markdown ノートにフロントマターを挿入するため、そのブロックの分だけすべての行番号がずれます。その結果、元のファイルを基に作成したオフセットマップが気づかないうちに誤ったものになります。.txt はそのまま保存されるため、行番号はディスク上のファイルと 1 対 1 で一致します。
  • プロジェクト内に置いてください。 ディスク上の別の場所にある原文はエンティティではないため、bm cat は Error: Entity not found を返します。どうしてもプロジェクト外に置く必要がある場合は、原文に対する BM のコマンドを使わず、通常のシェルを使います(sed -n '4200,4890p' <path>)。ノートには引き続き BM のコマンドを使い、原文の取得に限ってシェルを使います。

次に、処理を始める前に章のオフセットマップを一度作成します。 テキストをスキャンして章の見出しを探し、各章の行範囲を記録します。その後は本全体をコンテキストに読み込み直すのではなく、範囲を指定して各章を読みます。

grep -n '^CHAPTER ' ~/basic-memory/moby-dick/moby-dick.txt   # heading -> line number
bm cat moby-dick/moby-dick.txt --lines 4200-4890 --plain   # one chapter, not the whole text

ここでの grep -n は、ファイルシステム上のパスに対してシェルの grep を実行します(ファイルが必要なマップ作成手順です)。その後、bm cat に ノート識別子 を渡すと、指定した範囲だけが lines 4200-4890 of N というフッターとともに返されます。正しいプロジェクトに振り分けられるよう、<work>/<work>.txt のようにプロジェクト名を含めた識別子を指定します。grep パターンを書く前に見出しの形式を手早く確認するには、bm head moby-dick/moby-dick.txt -n 40 を使います。

後続のバッチや、コンテキストの圧縮後に再開した実行でマップを再発見する必要がないよう、マップをプロジェクト内(ノートまたは小さな JSON ファイル)に保存します。長い作品では、これはパイプライン内で最大のコンテキスト節約につながります。

バッチ処理の戦略

深さと進捗のバランスを取るため、1 バッチあたり約 10 章を処理します。物語の展開やテーマに沿ってグループ化します。

バッチ一般的な内容
1冒頭:舞台設定、登場人物の紹介、世界観の構築
2-3展開:対立の確立、関係性の発展
4-6中盤:複雑化、転換点、テーマの掘り下げ
7-8クライマックスへの接近:緊張の高まり、発覚、危機
最終クライマックス、解決、エピローグ

章の長さと密度に応じてバッチサイズを調整します。短くアクション中心の章は大きなグループにまとめられますが、長く哲学的な内容の濃い章は、より小さなバッチに分ける必要がある場合があります。

章ごとのワークフロー

各章について:

1. 章を注意深く読みます。 ファイル全体ではなく、オフセットマップに記載された章の行範囲を読みます (bm cat <source>.txt --lines <start>-<end>)。実際のテキストを読みます — 記憶や要約をもとに作業してはいけません。テキスト上の根拠こそがすべてです。

2. 章のノートを作成します:

bm_write(
  title="Chapter <N> - <Title>",
  directory="chapters",
  note_type="Chapter",
  tags=["chapter", "<arc-phase>"],
  metadata={
    "chapter_number": <N>,
    "pov": "<narrator or POV character>",
    "setting": "<primary location>",
    "narrative_mode": "<mode>"
  },
  content="""# Chapter <N> - <Title>

## Observations
- [summary] <1-2 sentence synopsis>
- [event] <Key plot events>
- [tone] <Emotional and stylistic atmosphere>
- [technique] <Notable narrative techniques>
- [quote] "<Significant passage>"
- [significance] <Why this chapter matters to the whole>
- [foreshadowing] <Hints at future events>

## Relations
- features [[<Character>]]
- set_in [[<Location>]]
- explores [[<Theme>]]
- contains [[<Symbol>]]
- employs [[<Literary Device>]]
- follows [[Chapter <N-1> - <Previous Title>]]
- precedes [[Chapter <N+1> - <Next Title>]]"""
)

3. 関連するエンティティを充実させます:

bm_edit(
  identifier="characters/major/<character-slug>",
  command="insert_after_section",
  section="Observations",
  content="""- [arc] Ch.<N>: <What happens to this character>
- [quote] "<Attributed quote>" (Ch.<N>)"""
)

append と prepend はセクションを指定せず、それぞれノートの末尾と本文の先頭に書き込みます。見出しの下に行を配置するには、insert_after_section/insert_before_section を使います。見出しが見つからない場合は、エラーが明確に表示されます。

4. 進捗を記録します。 memory-tasks スキルを使って、コンテキスト圧縮後も保持される処理タスクを作成します。

章ごとに記録する内容
カテゴリ確認する内容
[summary]章の概要を1~2文で記述
[event]物語の重要な出来事(行動、真相の判明、登場)
[tone]感情面および文体上の雰囲気
[technique]物語上の工夫(視点の切り替え、構成上の実験、ジャンルの融合)
[quote]印象的、またはテーマ上重要な一節
[significance]この章が全体にとって重要な理由
[foreshadowing]今後の出来事を示唆する手がかり
章ごとのエンティティの充実化

各章の処理中に、関連するエンティティに所見を追加します:

  • 登場人物:[arc] の局面、新たに判明した [trait]、引用の [quote] 属性
  • テーマ:この章での [manifestation]、[evolution] の変化
  • 象徴:文脈を添えた [appearance]、新たな [interpretation] の視点
  • 場所:描写された [atmosphere]、場面における [significance]
  • 文学的技法:この章からの [example]
散文と解釈の追加

構造化された所見を記録した後、主要なエンティティのノートに解釈的な散文を追加することを検討します。bm_edit(command="insert_before_section", section="Observations", content=...) を使い、Observations セクションの前に批評的エッセイを2~4段落挿入します。この散文では、次の点に留意してください:

  • 登場人物、テーマ、象徴について、単に説明するのではなく、ひとつの解釈を論じます
  • そのエンティティを作品全体の関心事や文学的伝統と結び付けます
  • 主観的な意見は、「私の読みでは……」「私は……と考えます」のように明確に示します
  • 章番号を示して引用したテキスト上の根拠に基づいて主張します

この散文によって、構造化された所見だけでは捉えられない解釈の豊かさが加わります。

フェーズ 3:相互参照

すべての章の処理が終わったら:

充実化が必要な箇所を見つける

内容が薄い箇所を判断するために、すべてのノートを読み直してはいけません。クエリで探します:

bm find --meta 'note_type=chapter' --fields chapter_number,pov,setting --page-size 200
bm find --meta 'note_type=character' --fields role,status --page-size 200   # who is still a stub
bm find --meta 'chapter_number>100' --fields pov --page-size 200           # late-book POV drift

ノートで一度も設定されていないフィールドは、空欄(--json では null)として返されるため、空欄のある行が作業キューになります。これにより、「グラフを監査する」作業は、すべてのノートを読むのではなく、質問ごとに1回の呼び出しで済みます。

bm_write は note_type をスネークケースに変換して保存します(Chapter は chapter として保存されます)。また、--meta の型述語も同じ方法で正規化されるため、どちらの表記でも一致します。--page-size 200 は飾りではありません。これを指定しない場合、最初の10行しか返されず、作業キューは10項目に見えてしまいます。

登場人物のアーク

主要な登場人物ごとに、作品全体を通じた軌跡をまとめた [arc] の所見を作成します。

テーマの展開

テーマごとに、導入から解決に至るまでの展開をたどる [evolution] の所見を追加します。

章同士の対比

構成上類似する章(たとえば、鏡像のような場面、繰り返される舞台、テーマ上の呼応)の間に、parallels と contrasts_with の関係を追加します。

分析ノート

analysis/ に統合的な分析ノートを作成します:

bm_write(
  title="Narrative Structure",
  directory="analysis",
  note_type="note",
  tags=["analysis", "structure"],
  content="""# Narrative Structure

Analysis of the work's narrative architecture.

## Observations
- [structure] <Overall arc description>
- [technique] <Key narrative strategies>
...

## Relations
- analyzes [[<Protagonist>]]
- analyzes [[<Key Character>]]
- explores [[<Central Theme>]]
..."""
)

推奨される分析ノート:

  • 物語構成 — 全体的な構成とペース配分
  • 作品概要 — 作品全体の統合的な概観(概要、主張、後世への影響)
  • 批評的評価 — 歴史的および現代的な解釈
新たに現れた登場要素を見つける

章の処理中に、新たな脇役、場所、シンボルが現れます。3章以上に登場するもの、またはテーマ上重要なものについては、ノートを作成してください。

フェーズ 4:検証

スキーマの検証
# Validate each entity type
bm_schema(command="validate", note_type="Character")
bm_schema(command="validate", note_type="Theme")
bm_schema(command="validate", note_type="Chapter")
bm_schema(command="validate", note_type="Location")
bm_schema(command="validate", note_type="Symbol")
bm_schema(command="validate", note_type="LiteraryDevice")
ドリフトの検出
bm_schema(command="diff", note_type="Character")
# ... for each type

見つかった問題を修正します。よくある修正方法:

  • 必須の観察カテゴリが不足している → bm_edit で追加する
  • 許可された値の範囲外にある列挙値 → メタデータを修正する
  • スキーマにないフィールドがノートにある → 正当なものであれば、スキーマにオプションとして追加する
網羅性の確認

スキーマ検証で確認できるのは、ノートの形式が正しいことです。以下の確認で、グラフが完全かどうかを確かめます:

bm find --meta 'note_type=chapter' --fields chapter_number --page-size 200   # every chapter present?
bm find --meta 'note_type=chapter' --fields pov,setting --page-size 200      # missing context?
bm find /characters --meta 'note_type=character' --fields role --page-size 200  # inventory vs. seed list

ページ分割される検索は、網羅性の確認にはなりません。 bm find のデフォルトは --page-size 10 なので、最初のクエリをサイズ指定なしで実行すると、138章の作品でも 「10章ある」ことしか確認できません。ページサイズの上限は200です。それを超える場合は --page 2、--page 3、…と繰り返してください。--meta クエリの範囲は、--name ではなく 位置引数のパス(/characters)で指定します。2つのオプションは同時に使えません。 メタデータ検索にはファイル名の glob がないためです。位置引数のパスは、ノートが インデックス化されているファイルパスをディレクトリの境界で照合して範囲を限定します。 そのため、/characters では characters/major/ahab.md は対象になりますが、 characters-cut/ は対象になりません。パーマリンクとの照合ではないため、ノートに 独自の permalink: が設定されていても、そのノートはファイルの保存場所で見つかります。

件数は、表示されている行数ではなくフッターで確認します。ターミナルでは、各 find の結果に page 1 • total 138 と表示され、ページ分割によって回答が途切れている場合は • more available (--page) が追加されます。この末尾が表示されるなら、見えている行数に かかわらず、確認は不合格です。フッターが表示されるのは TTY の場合だけです。 --plain を指定せずにパイプへ出力すると JSON になり、total と has_more が含まれます。 --plain を指定した場合、行は stdout に出力され、続きがあることを知らせるメッセージは stderr に出力されます。スクリプトでは、--json の has_more を確認してください。

件数だけでは検出できない連番の欠落を確認するには、--json から total、 total_is_exact、has_more を取得します:

expected=138; page=1; rows='[]'
while :; do
  resp=$(bm find --meta 'note_type=chapter' --fields chapter_number \
           --page-size 200 --page $page --project <work> --json)
  rows=$(jq -n --argjson acc "$rows" --argjson r "$resp" \
           '$acc + [$r.results[] | {title, n: .fields.chapter_number}]')
  [ "$(jq -r '.has_more' <<<"$resp")" = "true" ] || break
  page=$((page + 1))
done
jq -n --argjson rows "$rows" --argjson expected "$expected" '
  ([$rows[] | select(.n != null and (.n | tostring | test("^[0-9]+$"))) | .n | tonumber]) as $n
  | { total:        ($rows | length),
      unnumbered:   [$rows[] | select(.n == null or (.n | tostring | test("^[0-9]+$") | not))
                             | .title],
      missing:      ([range(1; $expected + 1)] - $n),
      duplicates:   ($n | group_by(.) | map(select(length > 1) | .[0])),
      out_of_range: ($n | map(select(. < 1 or . > $expected)) | unique) }'

このループは形式だけのものではありません。--page-size の上限は200なので、章が 200を超える作品の全件を1回の呼び出しで一覧にはできません。また、これを --page 2 で再実行すると、数値は累積されず置き換えられます。そのため、全件がそろった コーパスでも、1~200章が欠落していると報告されます。has_more が false になるまで ページをたどり、全ページの和集合を確認してください。

--fields は YAML のネイティブ型を返すため、chapter_number: 63 は 63 として返されます。 それでも tostring | test(...) | tonumber のガードを使えば、手入力された "63" や "12a" などの値を検出できます。

作品の実際の章数を $expected として渡してください。見つかった最大番号から範囲を 導き出すと、不完全なグラフでも検査を通過してしまいます。1~137の番号が付いた138行に 重複が1つある場合、最大値から範囲を導き出す検査では missing: [] と報告されますが、 実際には章が1つ欠落しています。重複によって件数が合い、欠落した末尾の章によって目標値が ずれてしまうためです。すべてのページを結合した状態で、unnumbered: []、missing: []、 duplicates: []、および out_of_range: [] がそろった場合にのみ、検査に合格します。

unnumbered も飾りではありません。chapter_number が付いていない chapter ノートは null として返され、それをそのまま tonumber に渡すと、パイプライン全体が null cannot be parsed as a number というエラーで中断します。つまり、まさに検出すべき 不正な一覧で検査がクラッシュします。先に振り分けることで、該当する行を名前付きで示せます。

out_of_range は仮定上の問題ではありません。プロローグやエピローグを chapter として 入力すると、番号は0または $expected + 1 になります。このキーがなければ、想定される番号が すべてそろい、重複もないため、報告上は問題がないように見えます。しかし、一覧には番号の 範囲に含まれないノートが残っています。冒頭や末尾の付録は別のノートタイプにするか、 $expected を意図的に広げてください。

バッチの途中にできる欠落は、処理で最もよく起きる失敗で、目視では最も見落としやすいものです。 章番号の重複は2番目によく起きる失敗で、最初の欠落を隠してしまいます。

関係の一貫性

双方向の関係を抜き取り確認します。Chapter X に features [[Character]] がある場合、Character に Chapter X を参照する観察記録があるでしょうか。欠けている部分を修正します。

孤立ノートもこの確認の対象です。インバウンドまたはアウトバウンドの関係がないノートは、本当に孤立しているか、グラフにまだリンクされていないかのどちらかです。

bm orphans          # entities with no relations in the graph

orphans は関係のないノートを見つけます。[[target]] が何も解決しないケース、つまりエンティティ名のスペルミスや改名は見つけません。章のリンクは bm tool build-context memory://chapters/<slug> --depth 1 --project <work> --json で確認し、to_entity_id が null の関係を探します。スペルを修正するかエイリアスを追加してから、章を再実行します。

グラフの品質を決めるのはノートの数ではなく、関係の 密度 です。孤立ノートを残したままノートを追加するパスでは、グラフの質が下がります。

フェーズ 5:グラフを探索する

グラフが完成したら、章ごとの処理では見つけられなかった内容を見つけるために、グラフをたどります。

bm grep "features [[" --page-size 200 --project <work> --json   # every chapter's cast, one call
bm find --meta 'note_type=theme' --fields prevalence --page-size 200  # thematic weight
bm grep "doubloon" --page-size 100 --project <work>                # every mention of a symbol

「最も多くの章を共有している登場人物は誰か」という問いには、1 行目と各行の content から features [[...]] をローカルで解析するだけで答えられます。章ごとに読み込むのではなく、1 回の呼び出しで済みます。ここでは bm tool build-context 'memory://characters/major/*' を使わないでください。ワイルドカードのコンテキストは、すべてのプライマリにわたる関連行を 100 件までに制限し、インデックス化された観察ごとにプライマリ行を 1 件返します。そのため、登場人物を列挙することも、ウェブをたどることもできません。別の問いについては、以下で説明するように、単一のノートに対する build-context が適切なツールです。build-context は URL を位置引数として受け取ります。--url オプションはありません。

grep はキーワードに一致する結果を 10 件返します。ページが静かに切り詰められるため、「これはどこに出てくるのか」という問いに対し、40 章に登場する記号でも 10 件しか返ってきません。記号を追跡する場合は --page-size を増やしてください。探している意味の変化はたいてい後半の出現箇所にあり、デフォルトのページでは抜け落ちてしまいます。--semantic は代わりに意味に基づいて順位付けするため、「これは何についてか」には答えられますが、「この単語はどこに出てくるか」には答えられません。

grep の行について、もう 2 点あります。一致判定では大文字と小文字が区別されず、ノート単位で結果が返されます。ヒットは行ではなくノートであり、-n もコンテキスト表示もありません。また、各行の content は 4000 文字までに制限されています。切り詰められた行には content_truncated: true が付きます。そのため、長いノートの末尾(最終章の follows/precedes 関係など)が、grep を使ったスキャンから欠落することがあります。解析にノートの末尾が必要な場合は、そのノートを cat してください。

--page-size は上限を引き上げますが、上限そのものをなくすわけではありません。長い作品では、記号が 100 件を超えることもあります。最終ページがいっぱいかどうかを確認し、いっぱいであれば --page 2、--page 3 と、結果がいっぱいでなくなるまでページを進めてください。記号検索が切り詰められると、ページ指定のない find と同じように、気付かれないまま失敗します。もっともらしい回答が返り、終了コードは 0 で、末尾が欠落していることを示すものはありません。

また、grep が検索するのは ソースではなく、自分のノートです。<work>.txt はエンティティとしてインデックス化されていますが、その本文は検索可能なテキストに含まれないため、ノートに一度も書き写していない箇所は見つかりません。ソース内にだけ存在する単語を検索すると、total: 0 が返ります。つまり、「硬貨について自分はどこに書いたか」には答えられますが、「本のどこに硬貨が出てくるか」には答えられません。後者を調べるにはファイル自体を検索し、章のオフセットマップを使って該当箇所を章に対応付けてください。

グラフをたどることで、二次的な問いに答えられます。最も多くの章を共有している登場人物、最終幕で収束するテーマ、記号の意味が変化する箇所などです。見つけた内容は analysis/ ノートとして記録してください。こうした統合分析こそ、グラフを構築した成果です。

他のジャンルへの適用

このパイプラインは、あらゆる文学作品に使用できます。ジャンルに合わせてスキーマを調整してください。

ジャンルスキーマの調整
小説基本スキーマはそのまま使用できます。必要に応じて、ジャンル固有のフィールドを Character に追加します
戯曲Act と Scene のスキーマを追加します。Character に speaking_lines フィールドを追加します
詩集Chapter を Poem に置き換え、form、meter、rhyme_scheme フィールドを追加します
ノンフィクションChapter を Section に置き換え、Argument と Evidence のスキーマを追加します
短編集narrator、setting、word_count を備えた Story スキーマを追加します
叙事詩/神話Deity と Prophecy のスキーマを追加します。Location に mythological_significance を追加します
回顧録Character スキーマに relationship_to_narrator を追加し、Memory スキーマを追加します
規模に関するガイダンス
作品の長さバッチサイズ推定ノート数
中編小説(約40K語)5~10章約50~80
小説(約80K語)8~12章約100~150
長編小説(約200K語以上)10~15章約200~300
シリーズ(複数巻)一度に1巻1巻あたり約200以上

関連スキル

  • memory-schema — スキーマの作成、検証、ドリフトの検出
  • memory-tasks — コンテキストの圧縮をまたいで章の処理状況を追跡
  • memory-notes — ノートの作成パターン、観察のカテゴリ、wikiリンク
  • memory-ingest — 外部入力を構造化されたエンティティに加工
  • memory-metadata-search — frontmatter フィールドによるノートの検索
  • memory-lifecycle — 完了した分析フェーズのアーカイブ

ガイドライン

  • 処理前にシードを作成します。 まずエンティティのスタブを作成しておくと、章の処理中に wikiリンクをすぐに解決できます。
  • 無理のない単位に分けます。 一度に約10章を処理すると、深さと勢いのバランスが取れます。Task ノートで進捗を追跡してください。
  • 原文を読みます。 記憶や要約に頼らないでください。ノートを作成する前に、各バッチの実際のテキストを読み(または読み直し)ます。テキストによる根拠がすべてです。
  • 範囲を絞って読みます。 原文をプロジェクト内に .txt として保存し、一度だけインデックスを作成し、章のオフセットマップも一度だけ作成してから、行範囲を指定して章を読み、セクションごとにノートを読みます。長い作品では、ファイル全体がコンテキストに読み込まれることが、パイプラインで最も回避しやすいコストです。
  • スキャンせず、クエリします。 どのノートにフィールドがあるかを調べるときは、--meta の述語と --fields の射影を使って検索します。frontmatter を確認するためにノートを読むのは、このパイプラインを大規模に運用する際の誤りです。クエリで気づきにくい落とし穴の一つとして、find は --page-size を指定しないと10行を返します。
  • 観察がインデックスになります。 ナレッジグラフの価値は、分類された観察から生まれます。カテゴリは多めに設定し、内容を具体的にしてください。
  • 関連がウェブを形作ります。 各章から、登場人物、テーマ、場所、表現技法へリンクを張ります。各エンティティからも、登場する章へリンクを張ります。
  • 段階的に充実させます。 エンティティのノートは、章を処理するごとに内容が豊かになります。最初から完璧な登場人物ノートを作ろうとせず、作業に合わせて追記してください。
  • 深みを加えるために文章を書きます。 構造化データを整えた後、主要なノートに解釈的なエッセイを追加します。文章は、観察だけでは捉えられない主張、ニュアンス、意見、語り口を記録します。
  • 定期的に検証します。 最後だけでなく、各バッチの処理後に bm_schema(command="validate") を実行します。早い段階でドリフトを検出してください。
  • 引用を惜しみなく使います。 文学分析はテキストによる根拠に基づきます。重要な引用は、章の出典を付けて [quote] の観察として含めてください。
  • 見直して改訂します。 すべての章の処理を終えたら、外部の視点からグラフ全体を見直します。内容の薄いノート、欠けているつながり、網羅性の不足を探してください。最初の作業が最後になることはありません。
  • 分析は最後に行います。 analysis/ 内の統合ノートは、全章を処理し、全体像が把握できてから作成してください。

コメント