monRem - AI自己進化・記憶制御アーキテクチャ設計書 (v2026.05 rev14)
はじめに
2026-04-22 初稿 → 2026-05-08 改訂(monChat2設計メモと統合) → 2026-05-20 改訂(方向を大きく転換と初期実装仕様書を策定)
記憶部分のコード部分をライブラリとし名前も「monRem」としました。
また、仮のチャット部分を「monRChat_preview」としました。
この設計書は僕がClaudeへ考えを伝え、壁打ちしたのち清書してもらいました。
綺麗にまとまっているのはClaudeのおかげです。
今回は時間かかった・・・
monRem - AI自己進化・記憶制御アーキテクチャ設計書 人格システム設計書 (v2026.05 rev14)
2026-04-22 初稿 → 2026-05-19 rev14(rev13を更新)
統合ペルソナ分割・ログ設計詳細化・debug_log ON/OFF追加。
はじめに
この設計書は「育つ相棒」としてのAIを実現するための、人格システムアーキテクチャをまとめたものです。
ライブラリ名 monRem、チャットアプリ名 monRChat_preview の設計をベースに、汎用アーキテクチャとして構想しています。
この設計の核心は一言で言うとこうです。
ユーザーという人間の解像度を、会話を重ねるごとに上げていく。
趣味・趣向のワードを保存するのではない。
話し方・返事の仕方・思考の深さ・コミュニケーションスタイルそのものを学んでいく。
LLM自身がユーザーモデルを育て、自分のsystem promptを書き換えていく。
記憶とは評価の記録ではなく、その人がどういう人間かの蒸留です。
「すき焼きが好き」ではなく、「すき焼きの話をよくする。割り下はやや甘め派らしい」という粒度。
ユーザーの言葉でできた、その人の像です。
0. 設計思想
作るものの定義
× チャットアプリに記憶機能をつける
○ 人格システムを作る。チャットはその窓口の一つ人格システムとは何か
外部LLM(Gemini・Ollama・LM Studio)をエンジンとして使い、
SQLiteの中に育った記憶・ユーザーモデル・サブプロンプトを纏わせることで、このユーザーだけの人格を作り出すシステム。
外部LLM(エンジン・交換可能)
↓
人格システム(器・プロンプト)
↓
趣向データ・ユーザーモデル・サブプロンプト(SQLiteに蓄積)← ここが本体モデルが変わっても、SQLiteが残れば人格は続く
Geminiが終わってもOllamaに乗り換えられる
本体はクラウドではなくローカルにある
会話相手ではなく人格を作る
会話相手を作る → 賢く答えてくれるAI → どこにでもある
人格を作る → このユーザーとの歴史が刻まれたもの → 世界に一つ趣向データ → 人格が何を知っているか(話題の文脈)
ユーザーモデル → 人格がどういう人間と向き合っているか(人物の文脈)
サブプロンプト → 人格がどう感じて動くか
日記 → 人格とユーザーの歴史これ全部合わさって初めて人格になる。
記憶が難しい本質的な理由
LLMで記憶が難しい本質的な理由は、コンテキスト長でも検索精度でもなく、
**「何を覚えるかを決める主体(性格)がない」**こと。
既存のAI記憶システムはほぼ全て「情報をどう構造化するか」の問題として扱ってきた。
チャンクの区切り方、ベクトル検索の精度、要約の圧縮率。
人間の記憶は違う。同じ出来事でも、人によって全く異なるものが残る。
それは構造の違いではなく、その人の性格・関心・気分による。
ならば、AIにも観察専用の人格を持たせればいい。それが観察ペルソナというアイデア。
記憶は「醸造」である
記憶の蒸留を一度で完結させようとしない。時間と繰り返しが判定基準の一部。
人間 このシステム
━━━━━━━━━━━━━━━━━━━━
日中の体験 → 会話ログ(チャンク)
睡眠・夢 → 日記生成・フラッシュ
記憶の定着 → SQLiteへの蒸留近い記憶(生ログ)は精度高く保持。チャンク単位で管理。
フラッシュを重ねるごとにユーザーモデルが育っていく。
繰り返し選ばれたことそのものが重要度の根拠になる。
人間の記憶も適当。AIの揺らぎや誤記憶も「相棒らしさ」になりうる。
完璧な記憶より、自然な記憶を目指す。
透明性原則
「どんな処理も隠さない」
「ログに残らない処理は存在しない」
「UIで見えない状態は作らない」
「隠さないだけでなく、全部コントロールできる」処理の透明性がUXを担保する。ユーザーが待てる・何をしているか分かる。
全ての動作をUIから調整できる。コードを触らずに挙動を変えられる。
1. アーキテクチャ構造
二層構造
Layer A 人格システム(コア・独立したライブラリ = monRem)
├ ペルソナ管理(複数ペルソナ・完全独立)
├ 趣向データ管理(ペルソナごと)
├ ユーザーモデル管理(ペルソナごと)
├ サブプロンプト管理(ペルソナごと)
├ フラッシュパイプライン(ペルソナごと)
└ 人格DB(SQLite・ペルソナごとに完全分離)
Layer B アプリ群(人格システムを利用する = monRChat_preview)
├ チャットアプリ(マルチペルソナ・エンジン切り替え)
├ 日記閲覧画面
├ 記憶・趣向管理画面
├ 設定画面
└ 処理ログ閲覧画面ペルソナ完全分離の原則
ペルソナごとに全てのデータ・プロンプトが独立する。
これはシステムの核心であり、後から変更しない前提で設計する。
persona_monica/
├ chat_log.db ← モニカとの会話履歴・チャンク
├ diary.db ← モニカとの日記
├ memory.db ← モニカが育てた趣向・ユーザーモデル
└ prompt.json ← モニカ専用の全プロンプト
persona_xxx/
├ chat_log.db
├ diary.db
├ memory.db
└ prompt.json ← xxx専用の全プロンプトペルソナを追加 → フォルダごと自動生成
ペルソナを削除 → フォルダごと削除
ペルソナをエクスポート → フォルダごと持ち出せる
モデルが変わっても、フォルダが残れば人格は続く
ライブラリ構成
monRem/
├ __init__.py
├ db.py ← DB接続・初期化・ペルソナフォルダ管理
├ chunk.py ← チャンク管理・メッセージ保存
├ memory.py ← preferences読み書き・FTS5検索
├ user_model.py ← ユーザーモデル読み書き
├ flash.py ← フラッシュパイプライン全体の司令塔
├ diary.py ← 日記生成・統合・保存
├ persona.py ← 観察ペルソナ三段処理
├ emotion.py ← 感情取得・ふるまいモード
└ prompt.py ← プロンプト組み立て・LLM呼び出し
monRChat_preview/
├ main.py ← FastAPIエントリーポイント(ポート8010)
├ config.json ← エンジン・アプリ設定(全ペルソナ共通)
├ static/
└ templates/
├ index.html ← チャット画面
├ diary.html ← 日記閲覧画面
├ memory.html ← 記憶・趣向管理画面
├ settings.html ← 設定画面
└ logs.html ← 処理ログ閲覧画面
data/
├ personas.db ← ペルソナ一覧・設定(共通)
├ persona_monica/
│ ├ chat_log.db
│ ├ diary.db
│ ├ memory.db
│ └ prompt.json ← モニカ専用プロンプト全部
└ persona_xxx/
├ chat_log.db
├ diary.db
├ memory.db
└ prompt.json ← xxx専用プロンプト全部チャット側の役割はシンプルに保つ。
monRChat_preview:
├ ユーザー入力を受け取る
├ アクティブペルソナのmonRemに投げる
└ 結果を表示するだけ2. 技術スタック
人格システムコア(全部ローカル)
├ Python 3.12
├ SQLite 3(FTS5有効・人格DB)
├ MeCab(日本語分かち書き・キーワード抽出)
└ FastAPI + Jinja2 + Alpine.js
LLMエンジン(交換可能・差し替えるだけ)
├ Gemini API(フラッシュ・精度優先)
├ Ollama(チャット・速度優先)
└ LM Studio(チャット・速度優先)同じパイプライン設計のまま、エンジンだけ差し替えられる。
3. DBファイル構成
役割が異なるデータを同一DBに混在させない。
ペルソナごとに全DBを完全分離する。これはシステムの核心。
personas.db → ペルソナ一覧・設定(全ペルソナ共通・唯一の共有DB)
persona_{name}/
├ chat_log.db → 会話ログ・チャンク(永続保存)
├ diary.db → 日記(永続保存)
├ memory.db → 趣向データ・ユーザーモデル(観察ペルソナのみが書く)
└ prompt.json → このペルソナ専用の全プロンプトタイムスタンプ設計
全データに年月日時分秒のタイムスタンプを必須付与する。
「記憶→日記→会話」を完全に遡れる設計にする。
chat_log.db → 発言ごとに timestamp
chunk → chunk_id + 開始・終了 timestamp
diary.db → date + chunk_ids(どのチャンクから生成したか)
memory.db → source_chunk_ids(根拠チャンクへの参照)
created_at / last_surfaced_atDBスキーマ
personas.db(全ペルソナ共通)
CREATE TABLE personas (
id INTEGER PRIMARY KEY,
name TEXT UNIQUE, -- ペルソナ名(フォルダ名にも使用)
system_prompt TEXT, -- ペルソナ定義プロンプト
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);chat_log.db(ペルソナごと)
CREATE TABLE messages (
id INTEGER PRIMARY KEY,
chunk_id TEXT,
role TEXT, -- 'user' / 'assistant'
content TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE chunks (
id TEXT PRIMARY KEY, -- chunk_20260518_143022
started_at TIMESTAMP,
ended_at TIMESTAMP,
flushed INTEGER DEFAULT 0 -- 0=未処理 1=処理済み
);diary.db(ペルソナごと)
CREATE TABLE diary (
id INTEGER PRIMARY KEY,
period TEXT, -- "2026-05-18 09:00-15:00"
date TEXT, -- "2026-05-18"
chunk_ids TEXT, -- JSON配列 ["chunk_xxx", "chunk_yyy"]
content TEXT, -- 統合日記本文
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);memory.db(ペルソナごと)
-- 趣向データ
CREATE TABLE preferences (
id INTEGER PRIMARY KEY,
category TEXT, -- ゲーム・食事・趣味 など
content TEXT,
intensity TEXT, -- high / medium / low
source_chunk_ids TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_surfaced_at TIMESTAMP
);
-- FTS5仮想テーブル(preferences検索用)
CREATE VIRTUAL TABLE preferences_fts USING fts5(
content,
content='preferences',
content_rowid='id'
);
-- ユーザーモデル
CREATE TABLE user_model (
layer TEXT PRIMARY KEY, -- style / personality / background
content TEXT, -- 自然言語の小文
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- メタ情報
CREATE TABLE meta (
key TEXT PRIMARY KEY,
value TEXT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- flush_count などを管理4. ユーザーモデルの構造
趣向データとユーザーモデルは用途が根本的に異なるため完全に分離する。
二つの違い
趣向データ(好みを覚える):
用途 :話題に応じてFTS5で動的に引き出す
使い方:今の会話と自然に絡める
イメージ:「この人はこれが好き」という引き出し
→ エルデンリングの話題が出たとき自然に乗れる
ユーザーモデル(その人を知る):
用途 :常時システムプロンプトに付随
使い方:AIの反応・応対の基盤になる
イメージ:「この人はこういう人間だ」という理解
→ 落ち込んでいるときの寄り添い方がわかる
→ 価値観に沿った返し方ができる片方は話題の文脈、もう片方は人物の文脈。
二つ合わさって初めて「分かってくれてる」感覚になる。
ユーザーモデルの層構造
スタイル層(しっかり持つ):
口調・口癖・よく使う言い回し
感情の出方・テンションの変化パターン
文の長さや返事の傾向
人物層(しっかり持つ):
価値観・信念 → 何を大事にしているか・譲れないもの
思考パターン → 問題をどう考えるか・結論の出し方
背景層(うっすら持つ):
日常の様子 → 生活リズム・行動の傾向
過去の文脈 → 経験・今に繋がる背景価値観・思考パターンは会話から直接語られることは少ない。
統合ペルソナがスタイル・趣向の観察を受け取った上で推論・解釈して抽出する。
背景層は明示的に構造化するのではなく、会話の積み重ねの中でじわっと滲み出るものとして扱う。
初期値について
ユーザー事前入力は採用しない。
最初は空のユーザーモデルで会話スタートし、フラッシュを重ねて育てていく。
「何も知らない相棒が、だんだん自分を理解していく」体験がそのまま設計になる。
ユーザーモデルの長さ
極端に短くすることを目指さない。ちゃんと通じる内容を構造的に持つことが優先。
冗長な重複だけ避ける。ユーザーモデルが育って長くなっていくのは、理解が深まっている証拠。
ただし常時プロンプトに入るため、コンパクトさも意識する。
5. FTS5動的引き出し
問題
趣向データが育つほどsystem promptが膨らむ。上限を設けると「育つ相棒」と矛盾する。
解決策
SQLiteに無制限蓄積
↓
ユーザー入力からキーワード抽出(MeCabで分かち書き)
↓
FTS5で関連趣向だけ検索・引き出す(BM25スコア上位 config.memory_search_top_k 件)
↓
system promptは常に必要最小限検索対象は観察ペルソナが既に選別した趣向データのみ
ノイズがないのでキーワード検索の精度で十分
embeddingモデル不要・RAG不要・セマンティック検索不要
数万〜数十万件でも実用速度
生ログ検索は不要
蒸留済みデータは既に統合・圧縮されている。毎ターン生ログを引っ張るのは冗長。
毎ターンは蒸留済み趣向データのみをFTS5検索する。生ログはフラッシュ時にのみ使う。
検索精度の補完はペルソナが担う
「鍋にしようかな」
↓ FTS5で「鍋」検索 → ヒットなし
↓ ペルソナが「何の鍋ですか?」と自然に聞き返す
↓ 「すき焼きにしようかな」
↓ FTS5で「すき焼き」検索 → ヒット・記憶が滲み出る「記憶にないことを自然に聞き返す能力」がプロンプト設計の核心。
記憶の滲み出し方
記憶はくっきり前面に出すのではなく、背景として滲み出させる。
× 「すき焼きが好きですよね」と直接言う → 不自然・AIっぽい
○ 鍋の話題になった時に自然と「すき焼きにする?」 → 相棒らしい記憶を「背景知識」として渡し、ペルソナが自然に使いこなすかどうかはプロンプトに依存する。
6. 毎ターン処理フロー
ユーザー入力
↓
chunk.py:chunk_id判定・messages保存
↓
flush_count = 0?(初回フラッシュ未実施チェック)
YES → FTS5スキップ・案内表示
NO → MeCabでキーワード抽出
→ FTS5でpreferencesから関連趣向を取得(top_k件)
↓
emotion.py:Nターン経過?
YES → 感情取得(LLM呼び出し)→ ふるまいモード更新・感情ログ蓄積
NO → 前回の感情をそのまま使用
↓
prompt.py:system prompt組み立て
ペルソナ本体
+ ユーザーモデル(user_modelテーブル・背景知識として)
+ 関連趣向(FTS5結果・話題の文脈として)
+ ふるまいサブプロンプト(最新感情反映済み)
↓
LLM呼び出し(1回のみ)
↓
応答を返す・messagesに保存・通知フィールドに表示LLMへの呼び出しは1ターンに1回のみ。複数回呼び出しは禁止。
7. チャンク設計
基本方針
3分無会話をチャンク区切りのトリガーとする。
ユーザーが自然に離席するタイミング(トイレ・食事・休憩など)で区切られるため、
会話の流れが自然に止まった場所で切れる。話題の途中で文脈が死なない。
| 方法 | 採用しなかった理由 |
|---|---|
| 時間区切り | 会話密度に関わらず機械的に切れる |
| トークン数 | 文の途中で切れる可能性がある |
| ターン数 | 話題の途中で文脈が死ぬ |
| 話題の切れ目 | AIによる判定精度が保証できない |
ターン数の上限は設けない。人間の生活リズムが自然な上限として機能する。
チャンクの命名規則
chunk_20260518_143022 ← 区切り発生時刻8. 観察ペルソナ三本柱
会話を担う表のペルソナとは別の、観察・蒸留だけを担う三本の役割。
ユーザーからは見えない。全てメインペルソナにサブプロンプトをアタッチして切り替える。
表のキャラクターを変えても、観察ペルソナが同じであれば蓄積されたデータは壊れない。
「自分のことを分かってくれている」感覚の連続性は、観察ペルソナが守る。
趣向ペルソナ → 好き・嫌いの軸で観察
スタイルペルソナ → 話し方・感情の軸で観察
統合ペルソナ → 二つをまとめてユーザーモデルへ三本とも固定。ユーザーが増やす設計にはしない。
趣向ペルソナが拾うもの
好き・嫌い
興味・関心の方向
趣味
こだわり・譲れないもの
いまやっていること・取り組んでいること(現在進行形の状態)「いまやっていること」は静的な好みではなく現在進行形の状態として捉える。
スタイルペルソナが拾うもの
口調・口癖・よく使う言い回し
感情の出方・テンションの変化パターン
文の長さや返事の傾向統合ペルソナの役割
趣向・スタイル両方の観察レポートを受け取り、ユーザーモデルに統合・更新する。
生ログは見ない。整理済みの二つの観察を受け取るだけ。
単なるまとめ役ではなく推論・解釈まで担う。三本柱の中で最も頭を使う役割。
受け取るもの:
趣向ペルソナの観察レポート(小文・自然言語)
スタイルペルソナの観察レポート(小文・自然言語)
既存のユーザーモデル(user_modelテーブルの内容)
やること:
二つの観察を統合
価値観・思考パターンを推論・抽出
日常・過去文脈をうっすら拾う
読み物としての統合日記を生成
ユーザーモデルを更新
変化量を自己評価(change_level)重み付けの仕組み
一回だけ出た話題 → 重み低
複数回・複数文脈で出た話題 → 重みが上がっていく
趣向・スタイル両方から捕捉された話題 → 特に重要なシグナル繰り返しフラッシュを重ねる中で自然に重要なものが浮き上がる。
繰り返し選ばれたことそのものが重要度の根拠になる。
9. アウトプットの形式:全ペルソナ自然言語で
全ペルソナのアウトプットは自然言語で統一する。箇条書き・構造化データは使わない。
趣向ペルソナ → 小文(簡潔だが自然言語)
スタイルペルソナ → 小文(簡潔だが自然言語)
統合ペルソナ → しっかりした観察日記(自然言語・読み物として)小文とは
箇条書きより長く、要約より短い。文脈と観察者の解釈が自然に混ざったもの。
箇条書き(NG):
・ゲームが好き
・エルデンリングをやっている
要約(NG):
ゲーム好きでエルデンリングに取り組んでいる。
小文(OK):
今日はエルデンリングの話が多かった。
ボスが倒せないと言いながらも、諦める気配はなかった。「諦める気配はなかった」のような観察と解釈が混ざることで、
統合ペルソナが価値観・思考パターンを推論する材料になる。
ユーザーが後から日記を読み返したとき、あの日の会話が蘇るくらいの温度感が理想。
10. フラッシュパイプライン
フラッシュの思想
手動フラッシュ=ユーザーが夢を見るタイミングを選ぶ。
自動化できないのではなく、意図的な設計選択。
普段はすいすい会話し、すぐ記憶を反映したければすぐフラッシュすればいい。
フラッシュ可能条件
前回フラッシュから flush_min_chunks 以上の未処理チャンクがあること
条件未満 → フラッシュボタンをグレーアウト・案内表示
チャンク数が flush_warning_chunks に達した → フラッシュ推奨の警告表示パイプライン全体フロー
手動フラッシュ実行
↓
① 未処理チャンクを取得(chunksテーブル flushed=0)
↓
② チャンクごとに小さな日記を生成(APIコール×チャンク数)
※ api_call_sleep_seconds でsleep
↓
③ 6時間単位で日記を統合(APIコール×グループ数)
→ diary.dbに保存
↓
④ 趣向ペルソナ・スタイルペルソナを並列実行(APIコール×2)
→ 各小文を生成
↓
⑤ 統合日記を生成(APIコール×1)
→ diary.db(読み物・人間が読む)
↓
⑥ ユーザーモデルを更新(APIコール×1)
→ user_modelテーブル更新
↓
⑦ preferences候補を抽出(APIコール×1)
候補ごとにFTS5で既存を照合
ヒットあり → 既存レコードを更新・強化
ヒットなし → 新規追加
→ preferencesテーブル更新
↓
⑧ change_levelを判定(APIコール×1)
→ change_level / change_summary を出力
↓
⑨ change_levelに応じて対話確認
small → トーストのみ「〇件の趣向が更新されました」
medium → ペルソナが1〜2個確認しに来る
large → ペルソナが複数項目を対話で確認していく
↓
⑩ 処理済みチャンクを flushed=1 に更新
フラッシュログを保存フラッシュは精度優先・時間がかかってもかまわない。
④並列実行を含め、全工程で数分以内を想定。モーダルで進捗を逐一表示する。
日記の二重の役割
| 用途 | 内容 |
|---|---|
| 中間生成物 | 蒸留の入力(AIが使う) |
| 副産物 | 人間が読む振り返り用 |
日記 → 人間が読むもの・物語的・専用UIで全閲覧可能
SQLite → AIが使うもの・system prompt用
6時間単位統合の理由
チャンクの断片日記 → バラバラな文脈
6時間の統合日記 → まとまった流れとして整合した物語
↓
蒸留に投げる単位が「まとまった物語」になるため精度が上がるフラッシュ処理時間
処理時間は50秒〜数分程度を想定。時間がかかっても精度優先でよい。
モーダルで進捗を逐一表示し、ユーザーが安心して待てるようにする。
11. 変化量トリガーの対話確認
思想
設定画面でプロンプトをいじるのは「ツールを操作する」感覚。
対話式にすることで「相棒が自分を理解しようとしている」感覚になる。
相棒が確かめに来る体験を作る。
変化がないのに確認が来ると鬱陶しい。変化があったときだけ来るから相棒らしい。
変化量ごとの挙動
small → 確認なし・静かに反映
「〇件の趣向が更新されました」(トーストのみ)
medium → ペルソナが1〜2個確認しに来る
「最近ストーリー系ゲームの話多いワン。
アクションより物語派になってきた?」
large → ペルソナが複数項目を対話で確認していく
「ご主人様のこと、少し見え方が変わってきたワン。
確認させてほしいワン」確認後の処理
ユーザーが「そうそう」 → そのまま反映
ユーザーが「ちょっと違う」 → 修正してから反映この対話自体がユーザーモデルの精度を上げていく。
12. サブプロンプト方式:ペルソナの役割拡張
観察ペルソナを別人格として管理する必要はない。
状況に応じてペルソナにサブプロンプトをアタッチして役割・振る舞いを切り替える。
通常時:
ペルソナ本体プロンプト → チャット担当
フラッシュ時:
ペルソナ本体プロンプト + 観察専用サブプロンプト → 観察担当
↓
終わったらサブプロンプトは外れるふるまいモード
| モード名 | 用途 |
|---|---|
| 普段の応対 | 通常会話 |
| うれしそう/楽しそうなとき | 感情に合わせた応対 |
| 悲しそうなとき | 感情に合わせた応対 |
| 怒っているとき | 感情に合わせた応対 |
| 真面目モード | 簡潔・論理優先 |
サブプロンプトはペルソナごとの`prompt.json`で管理し、UIから編集可能。
プロンプト自己成長ループ
会話する
↓
フラッシュ
↓
統合ペルソナが日記を読んで判断
「このユーザーにはこの応対が合ってる」
↓
ユーザーモデルが更新される
↓
次の会話でより精度の高い応対ができる最初のプロンプトは完璧でなくていい。育てる前提。余白を残す。
13. 感情取得
感情取得を`emotion.py`に完全統合する。その場限りの機能から育つデータへ。
感情取得(毎Nターン):
直近数ターン → 感情判定 → ふるまいモード切り替え(継続)
+ 感情ログとして蓄積
↓
フラッシュ時にスタイルペルソナが分析
→ 感情パターンとしてユーザーモデルへ14. 透明性:通知・処理ログ設計
通知フィールド(常時表示)
全動作を細かくリアルタイム通知する。チャット画面に常時表示。
「キーワードを抽出しています…」
「記憶を検索しています…(3件ヒット)」
「感情を読み取っています…」
「日記を生成しています… (2/5)」
「記憶を蒸留しています…」
「〇件の趣向が更新されました」フラッシュ中モーダル
長い処理中は何が行われているかを逐一表示する。
■ フラッシュ処理中
[■■■□□□□□□□] 30%
✅ チャンク日記を生成しました(3件)
✅ 日記を統合しました
⏳ 趣向ペルソナ・スタイルペルソナ処理中…処理ログ設計
全工程の入出力を빠짐없이ログDBに保存する。
プロンプトの効果を検証・チューニングするための最重要機能。
ログに記録する内容
毎ターンログ:
├ 入力テキスト
├ 抽出キーワード(MeCab結果)
├ FTS5ヒット結果(件数・内容)
├ 感情取得の入力・出力
├ 組み立てたsystem prompt全文
└ LLMへの入力・出力全文
フラッシュログ:
├ 処理対象チャンクID一覧
├ チャンク日記:入力(チャンク本文)・出力(生成日記)
├ 統合日記:入力(チャンク日記群)・出力(統合日記)
├ 趣向ペルソナ:入力・出力(小文)
├ スタイルペルソナ:入力・出力(小文)
├ 統合日記生成:入力・出力
├ ユーザーモデル更新:入力・出力・更新前後の差分
├ preferences抽出:入力・出力・追加/更新の差分
└ change_level判定:入力・出力debug_log ON/OFF制御
"debug_log": true`true`:全工程の入出力をログDBに保存・logs.htmlで閲覧可能
`false`:ログ保存をスキップ(通常運用時・DB節約)
設定画面からON/OFFを切り替えられる
プロンプト調整時はON・安定運用時はOFFが基本方針
logs.htmlの表示
フラッシュログ一覧(日時・チャンク数・change_level)
↓ 選択
各ステップの入力・出力を展開表示
各ペルソナが何を受け取って何を返したか
DBへの反映前後の差分
毎ターンログ一覧(日時)
↓ 選択
system prompt全文・LLM入出力を表示15. config.json設定項目
config.jsonは全ペルソナ共通の設定。ペルソナ固有のプロンプトは各`persona_{name}/prompt.json`で管理する。
{
"app": {
"port": 8010,
"debug_log": true
},
"engine": {
"type": "gemini",
"active_ollama": 0,
"active_gemini": 0,
"active_lmstudio": 0,
"ollama": [{"name": "Local Gemma3", "endpoint": "http://localhost:11434", "model": "gemma3:4b"}],
"gemini": [{"name": "Gemini Flash Lite", "api_key": "", "model": "gemini-2.5-flash-lite"}],
"lmstudio": [{"name": "LM Studio Local", "endpoint": "http://localhost:1234", "model": ""}]
},
"memory": {
"context_turns": 10,
"summary_batch": 11,
"emotion_interval_turns": 5,
"memory_search_top_k": 3
},
"flash": {
"chunk_interval_minutes": 3,
"diary_merge_hours": 6,
"flush_warning_chunks": 10,
"flush_min_chunks": 1,
"api_call_sleep_seconds": 5
},
"user_model": {
"change_notify": true,
"change_threshold": "medium",
"confirm_required": true
},
"loop_detection": {
"enabled": true,
"window_chars": 300,
"repeat_ratio": 0.9
}
}persona_{name}/prompt.json(ペルソナごと)
{
"persona_base": "ペルソナ定義プロンプト(未定・要設計)",
"subprompt_diary": "日記生成用(未定)",
"subprompt_preference": "趣向ペルソナ用(未定)",
"subprompt_style": "スタイルペルソナ用(未定)",
"subprompt_integrate_diary": "統合日記生成用(未定)",
"subprompt_integrate_usermodel": "ユーザーモデル更新用(未定)",
"subprompt_integrate_preferences": "preferences抽出用(未定)",
"subprompt_integrate_change": "change_level判定用(未定)",
"subprompt_emotion": "感情取得用(未定)",
"behavior_normal": "通常応対(未定)",
"behavior_happy": "楽しそうなとき(未定)",
"behavior_sad": "悲しそうなとき(未定)",
"behavior_angry": "怒っているとき(未定)",
"behavior_serious": "真面目モード(未定)"
}全プロンプトはUIの設定画面から編集可能。
16. 設計上の既知の制約
| 制約 | 対処方針 |
|---|---|
| 初期は記憶が育たない | 宿命。運用期間が必要と割り切る |
| 話題の粒度判定 | AIの文脈理解に委ねる・日記生成プロンプトで補う |
| プロンプト品質への依存 | ここが本領。継続的に育てる対象として扱う |
| 検索精度の限界 | ペルソナの「聞き返す能力」で補完する |
| 記憶を自然に滲み出させる | プロンプト設計次第。仕組みだけでは保証できない |
| フラッシュAPIコール数が多い | 精度優先・時間がかかってもかまわない設計につき許容 |
17. 未解決・今後詰める
高優先度(品質の核):
・ペルソナのベースプロンプト文(最重要・最初は粗くていい)
・各観察ペルソナのサブプロンプト文
・ふるまいサブプロンプト群の具体的な設計
設計レベル:
・change_levelの判定基準(small/medium/largeの定義)
・背景層(日常・過去文脈)の蓄積粒度
構想段階:
・ネガティブ要素(苦手・避けたいもの)の扱い
→ 常時制約層(件数少)とFTS5召喚層に分ける方向が有望18. 実装指針
ペルソナ完全分離を最初から守る — 後から分離すると全部やり直しになる
人格ライブラリから作る — チャットUIは後でいい
日記生成プロンプトと観察ペルソナプロンプトが核 — ここの質が全体の品質を左右する
ログ記録を最初から入れる — 後から検証・チューニングできるように
全データにタイムスタンプ — 後から原因を追えるようにする
最初のプロンプトは粗くていい — 育てる前提。余白を残す
段階的に始める — まずDB骨格+monChat2チャット機能から
この設計は汎用アーキテクチャとして構想しています。
プロンプト設計が本領。長い戦い覚悟で育てていく。
実装・検証を経て随時更新予定。
monRem / monRChat_preview 実装仕様書v2026.05 実装仕様 rev14
このドキュメントは実装の拠り所。設計思想の詳細はmonRem_design_rev14.mdを参照。
プロンプト設計は粗くていい。動かしながら育てる前提。
1. プロジェクト構成
monRem/ ← ライブラリ本体
├ __init__.py
├ db.py ← DB接続・初期化・テーブル作成
├ chunk.py ← チャンク管理・メッセージ保存
├ memory.py ← preferences読み書き・FTS5検索
├ user_model.py ← ユーザーモデル読み書き
├ flash.py ← フラッシュパイプライン司令塔
├ diary.py ← 日記生成・統合・保存
├ persona.py ← サブペルソナ三段処理
├ emotion.py ← 感情取得・ふるまいモード
└ prompt.py ← システムプロンプト組み立て・LLM呼び出し
monRChat_preview/ ← チャットアプリ
├ main.py ← FastAPIエントリーポイント(ポート8010)
├ config.json ← エンジン・アプリ設定(全ペルソナ共通)
├ static/
└ templates/
├ index.html ← チャット画面
├ diary.html ← 日記閲覧画面
├ memory.html ← 記憶・趣向管理画面
├ settings.html ← 設定画面
└ logs.html ← 処理ログ閲覧画面
data/
├ personas.db ← ペルソナ一覧・設定(全ペルソナ共通)
├ persona_{name}/ ← ペルソナごとのフォルダ(自動生成)
│ ├ chat_log.db ← 会話ログ・チャンク
│ ├ diary.db ← 日記
│ ├ memory.db ← 趣向データ・ユーザーモデル
│ └ prompt.json ← このペルソナ専用の全プロンプト
└ persona_{name}/
├ chat_log.db
├ diary.db
├ memory.db
└ prompt.json2. 技術スタック
言語:Python 3.12
フレームワーク:FastAPI
テンプレート:Jinja2
フロントエンド:Alpine.js(ローカル)
DB:SQLite 3(FTS5有効)
形態素解析:MeCab(FTS5検索のキーワード抽出)
ポート:8010
LLMエンジン(交換可能):
├ Gemini API(gemini-2.5-flash-lite)← フラッシュ・精度優先
├ Ollama(httpx直接通信)← チャット・速度優先
└ LM Studio(OpenAI互換API)← チャット・速度優先3. DBスキーマ
personas.db(全ペルソナ共通)
CREATE TABLE personas (
id INTEGER PRIMARY KEY,
name TEXT UNIQUE, -- ペルソナ名(フォルダ名にも使用)
system_prompt TEXT, -- ペルソナ定義プロンプト
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);ペルソナ追加時に `data/persona_{name}/` フォルダを自動生成し、
chat_log.db / diary.db / memory.db を初期化する。
chat_log.db(ペルソナごと)
CREATE TABLE messages (
id INTEGER PRIMARY KEY,
chunk_id TEXT,
role TEXT, -- 'user' / 'assistant'
content TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE chunks (
id TEXT PRIMARY KEY, -- chunk_20260518_143022
started_at TIMESTAMP,
ended_at TIMESTAMP,
flushed INTEGER DEFAULT 0 -- 0=未処理 1=処理済み
);diary.db(ペルソナごと)
CREATE TABLE diary (
id INTEGER PRIMARY KEY,
period TEXT, -- "2026-05-18 09:00-15:00"
date TEXT, -- "2026-05-18"
chunk_ids TEXT, -- JSON配列 ["chunk_xxx", "chunk_yyy"]
content TEXT, -- 統合日記本文
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);memory.db(ペルソナごと)
-- 趣向データ
CREATE TABLE preferences (
id INTEGER PRIMARY KEY,
category TEXT,
content TEXT,
intensity TEXT, -- high / medium / low
source_chunk_ids TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_surfaced_at TIMESTAMP
);
-- FTS5仮想テーブル
CREATE VIRTUAL TABLE preferences_fts USING fts5(
content,
content='preferences',
content_rowid='id'
);
-- ユーザーモデル
CREATE TABLE user_model (
layer TEXT PRIMARY KEY, -- style / personality / background
content TEXT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- メタ情報
CREATE TABLE meta (
key TEXT PRIMARY KEY,
value TEXT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- flush_count などを管理intensityの定義と判定基準
| intensity | 判定基準 |
|-----------|----------|
| high | 明確な好み・こだわりとして読み取れる |
| medium | 繰り返し言及・複数文脈で出てきた |
| low | 曖昧な表現・一度だけの言及 |
※ 判定はAI(統合ペルソナ)が文脈を読んで行う
※ 曖昧な場合はlowで登録する(登録しないより登録を優先)
※ 重複防止が最優先:登録前に必ずFTS5で既存を照合すること
※ この判定基準はsubprompt_integrateに明示すること
4. config.json 設定項目
{
"app": {
"port": 8010,
"debug_log": true
},
"engine": {
"type": "gemini",
"active_ollama": 0,
"active_gemini": 0,
"active_lmstudio": 0,
"ollama": [{
"name": "Local Gemma3",
"endpoint": "http://localhost:11434",
"model": "gemma3:4b",
"timeout": 120,
"params": {
"temperature": 0.8,
"top_p": 0.9,
"top_k": 40,
"max_tokens": 2048,
"repeat_penalty": 1.1
}
}],
"gemini": [{
"name": "Gemini Flash Lite",
"api_key": "",
"model": "gemini-2.5-flash-lite",
"timeout": 60,
"params": {
"temperature": 1.0,
"top_p": 0.95,
"top_k": 40,
"max_output_tokens": 2048
}
}],
"lmstudio": [{
"name": "LM Studio Local",
"endpoint": "http://localhost:1234",
"model": "",
"timeout": 120,
"params": {
"temperature": 0.8,
"top_p": 0.9,
"max_tokens": 2048,
"repeat_penalty": 1.1
}
}]
},
"memory": {
"context_turns": 10,
"summary_batch": 11,
"emotion_interval_turns": 5,
"memory_search_top_k": 3
},
"flash": {
"chunk_interval_minutes": 3,
"diary_merge_hours": 6,
"flush_warning_chunks": 10,
"flush_min_chunks": 1,
"api_call_sleep_seconds": 5
},
"user_model": {
"change_notify": true,
"change_threshold": "medium",
"confirm_required": true
},
"loop_detection": {
"enabled": true,
"window_chars": 300,
"repeat_ratio": 0.9
}
}5. prompt.json構成(ペルソナごと)
各ペルソナフォルダ内の`prompt.json`に全プロンプトを格納する。
ペルソナ間で共有しない。全プロンプトはUIから編集可能。
{
"persona_base": "ペルソナ定義プロンプト(未定・要設計)",
"subprompt_diary": "日記生成用(未定)",
"subprompt_preference": "趣向ペルソナ用(未定)",
"subprompt_style": "スタイルペルソナ用(未定)",
"subprompt_integrate_diary": "統合日記生成用(未定)",
"subprompt_integrate_usermodel": "ユーザーモデル更新用(未定)",
"subprompt_integrate_preferences": "preferences抽出用(未定)",
"subprompt_integrate_change": "change_level判定用(未定)",
"subprompt_emotion": "感情取得用(未定)",
"behavior_normal": "通常応対(未定)",
"behavior_happy": "楽しそうなとき(未定)",
"behavior_sad": "悲しそうなとき(未定)",
"behavior_angry": "怒っているとき(未定)",
"behavior_serious": "真面目モード(未定)"
}6. フラッシュパイプライン
手動フラッシュ実行
↓
① 未処理チャンクを取得(chunksテーブル flushed=0)
↓
② チャンクごとに小さな日記を生成(APIコール×チャンク数)
※ api_call_sleep_seconds でsleep
↓
③ 6時間単位で日記を統合(APIコール×グループ数)
→ diary.dbに保存
↓
④ 趣向サブペルソナ・スタイルサブペルソナを並列実行(APIコール×2)
→ 各小文を生成
↓
⑤ 統合日記を生成(APIコール×1)
→ diary.db(読み物・人間が読む)
↓
⑥ ユーザーモデルを更新(APIコール×1)
→ user_modelテーブル更新
↓
⑦ preferences候補を抽出(APIコール×1)
候補ごとにMeCabで分かち書き
→ FTS5で既存preferencesを検索
ヒットあり → 既存レコードを更新・強化(新規追加禁止)
ヒットなし → 新規追加
→ preferencesテーブル更新
↓
⑧ change_levelを判定(APIコール×1)
→ change_level / change_summary を出力
↓
⑨ change_levelに応じて対話確認
small → トーストのみ
medium → モニカが1〜2個確認
large → モニカが複数項目を対話確認
↓
⑩ 処理済みチャンクを flushed=1 に更新
フラッシュログを保存フラッシュは精度優先・時間がかかってもかまわない。
④並列実行を含め全工程で数分以内を想定。モーダルで進捗を逐一表示する。
※ subprompt_preferencesに以下を明示すること:
「趣向を追加する前に必ずFTS5で既存を確認する」
「ヒットした場合は新規追加禁止・既存レコードを更新する」
7. 毎ターン処理
monChat2のチャット機能をベースに、monRemの記憶・ユーザーモデルを統合する。
ユーザー入力(SSEストリーミング)
↓
chunk.py:chunk_id判定・messages保存
↓
emotion.py:Nターン経過?
YES → 感情取得・ふるまいモード更新
NO → 前回の感情をそのまま使用
↓
memory.py:MeCabでキーワード抽出
→ FTS5でpreferencesから関連趣向を取得(top_k件)
↓
prompt.py:system prompt組み立て(4層構造)
# General Instructions ← 共通プロンプト
# Persona ← ペルソナ定義+ユーザーモデル(背景知識)
# Current Emotional State ← 感情・ふるまいモード
# Past Conversation Summary ← ローリング要約(500文字以内)
+ 関連趣向(FTS5結果・話題の文脈として)
↓
LLM呼び出し(SSEストリーミング・1回のみ)
↓
ループ検出(直近300文字・90%類似でストップ)
↓
応答を返す・messagesに保存
↓
rawターン数 > context_turns(10)?
YES → 最古の1ターンをpendingへ移動
↓
pending >= summary_batch(11)?
YES → バックグラウンドで忘却曲線型ローリング要約を実行
→ past_summaryを更新・pendingを削除LLMへの呼び出しは1ターンに1回のみ。複数回呼び出しは禁止。
SSEシグナル
| シグナル | 説明 |
|---|---|
| `{"token": "..."}` | ストリーミングトークン |
| `{"summarize": true}` | 要約バッチ満杯 |
| `{"tokens": {"prompt": N, "output": N}}` | トークン使用量(Gemini専用) |
| `{"loop_detected": true}` | ループ検出・自動停止 |
| `{"debug_prompt": {...}}` | デバッグ用送信プロンプト(debug_log=true時のみ) |
| `{"error": "..."}` | エラー |
| `[DONE]` | ストリーム終了 |
8. UI画面構成
チャット画面(index.html)
ペルソナ切り替えドロップダウン(切り替え時に履歴・メモリを自動ロード)
エンジン切り替えドロップダウン(Ollama / LM Studio / Gemini 即時切り替え)
チャット入力・SSEストリーミング表示
通知フィールド(全動作を細かくリアルタイム通知)
フラッシュボタン(条件未満はグレーアウト・フラッシュ中はモーダル表示)
ループ検出警告トースト
トークン表示(Gemini専用)
日記閲覧画面(diary.html)
ペルソナ選択
日付一覧・日付を選択して統合日記を閲覧
記憶・趣向管理画面(memory.html)
ペルソナ選択
preferencesテーブルの内容を一覧・編集
user_modelテーブルの内容を層ごとに編集
設定画面(settings.html)
Engineタブ:Gemini / Ollama / LM Studio 各設定・パラメータ
Promptsタブ:prompt.jsonの全プロンプトをUIから編集
Personasタブ:ペルソナCRUD・システムプロンプト設定
Appタブ:config.jsonのその他設定項目
処理ログ閲覧画面(logs.html)
debug_log=trueのときのみ記録・表示
ペルソナ選択
フラッシュログ:各ペルソナの入出力・DB反映前後の差分
毎ターンログ:system prompt全文・LLM入出力
9. 通知・透明性設計
通知フィールド(常時表示)
全動作を細かくリアルタイム通知する。チャット画面に常時表示。
「キーワードを抽出しています…」
「記憶を検索しています…(3件ヒット)」
「感情を読み取っています…」
「日記を生成しています… (2/5)」
「記憶を蒸留しています…」
「〇件の趣向が更新されました」フラッシュ中モーダル
長い処理中は何が行われているかを逐一表示する。
■ フラッシュ処理中
[■■■□□□□□□□] 30%
✅ チャンク日記を生成しました(3件)
✅ 日記を統合しました
⏳ 趣向ペルソナ・スタイルペルソナ処理中…処理ログ設計
全工程の入出力をログDBに保存する。
プロンプトの効果を検証・チューニングするための最重要機能。
ログに記録する内容
毎ターンログ:
├ 入力テキスト
├ 抽出キーワード(MeCab結果)
├ FTS5ヒット結果(件数・内容)
├ 感情取得の入力・出力
├ 組み立てたsystem prompt全文
└ LLMへの入力・出力全文
フラッシュログ:
├ 処理対象チャンクID一覧
├ チャンク日記:入力(チャンク本文)・出力(生成日記)
├ 統合日記:入力(チャンク日記群)・出力(統合日記)
├ 趣向ペルソナ:入力・出力(小文)
├ スタイルペルソナ:入力・出力(小文)
├ 統合日記生成:入力・出力
├ ユーザーモデル更新:入力・出力・更新前後の差分
├ preferences抽出:入力・出力・追加/更新の差分
└ change_level判定:入力・出力debug_log ON/OFF制御
`true`:全工程の入出力をログDBに保存・logs.htmlで閲覧可能
`false`:ログ保存をスキップ(通常運用時・DB節約)
設定画面からON/OFFを切り替えられる
プロンプト調整時はON・安定運用時はOFFが基本方針
logs.htmlの表示
フラッシュログ一覧(日時・チャンク数・change_level)
↓ 選択
各ステップの入力・出力を展開表示
各ペルソナが何を受け取って何を返したか
DBへの反映前後の差分
毎ターンログ一覧(日時)
↓ 選択
system prompt全文・LLM入出力を表示10. 未確定・動かしながら決めるもの
・モニカのベースプロンプト文(最重要・最初は粗くていい)
・各サブペルソナのプロンプト文
・change_levelの判定基準(small/medium/largeの定義)
・対話確認のUIフロー詳細
・MeCabなしの代替キーワード抽出(環境によって検討)11. 実装順序(案)
Step 1 DBとライブラリ骨格
personas.db・ペルソナごとのDB初期化
db.py・chunk.py・memory.py・user_model.py
Step 2 チャット機能(monChat2ベース)
SSEストリーミング・スライディングウィンドウ
ローリング要約・ループ検出・エンジン切り替え
ペルソナ管理・system prompt4層構造
→ ここでペルソナとして普通に会話できる状態
Step 3 monRem統合
FTS5検索・ユーザーモデル組み込み
フラッシュパイプライン(diary.py・persona.py・flash.py)
Step 4 UI拡充
日記閲覧・記憶管理・設定・ログ画面
Step 5 プロンプト育成
動かしながら継続的に改善プロンプト設計が本領。長い戦い覚悟で育てていく。

いいなと思ったら応援しよう!
応援ありがとうございます!