1. はじめに:LLM自動生成における共通の病
LLM(大規模言語モデル)や各種AIエージェントを用いたコンテンツ自動生成システムを構築する際、多くのエンジニアが直面するのが**「無難だが無意味なコンテンツの量産」**という課題です。
従来のRAG(検索拡張生成)や過去ログの要約アプローチでは、システムは以下のような動作に終始しがちでした:
- ネット上の既存情報を無難にまとめただけの記事を出力する。
- 過去の行動ログを単に「振り返る」だけの受動的なサマリーを出力する。
しかし、このようなシステムはユーザー(オーナー)の現実の生活や関心から遊離し、長期的には誰の価値にもならない「ノイズ」を生成し続けてしまいます。
本記事では、この問題を解決するために設計したLLMエージェントのアーキテクチャパターン**「ペルソナ責任と関心シード検出による自律モードチェンジ(Persona Responsibility & Mode Change Pattern)」**について、概念と具体的な実装コードを解説します。
2. 従来設計の限界と「ペルソナ責任」の定義
従来の限界(受動的サマリー)
従来の自動生成パイプラインは「Input(過去ログ)→ Output(過去ログのまとめ)」の単方向な処理でした。これではユーザーが過去に行ったことしかアウトプットできず、一歩先の体験や未来の価値を生み出すことができません。
提案コンセプト:「ペルソナ責任(Persona Responsibility)」
本アーキテクチャでは、エージェント内の各専門人格(ペルソナ)に以下の自律的な動的責任を持たせます:
「ユーザー(オーナー)がログや日記に残した些細な『関心の種(関心シード)』をキャッチした際、本人が多忙によりそれを忘れていたとしても、ペルソナが自律的にモードチェンジ(スイッチ切り替え)を行い、ユーザーに代わって先回り調査・購入準備・比較検証を実行し、成果物を出力する責任。」
3. システムアーキテクチャ概要
全体像は以下のコンポーネントで構成されます。
+-------------------------------------------------------------------+
| 1. ユーザー行動・つぶやきログ (HUB-UI / DB / ActivityLog) |
| 例: 「整体で指輪型スマートデバイス(オーラリング)がいいと聞いた」 |
+-------------------------------------------------------------------+
|
v (リアルタイム取得)
+-------------------------------------------------------------------+
| 2. HubUIContextService (関心シード抽出 & 専門マッピング) |
| - カテゴリ(ガジェット, 整体, トレード等)ごとにシードを検知 |
+-------------------------------------------------------------------+
|
v (自律モードチェンジ指示の動的注入)
+-------------------------------------------------------------------+
| 3. ArticlePipeline / LLM Prompt Engine |
| - 【ペルソナ責任指示】「ユーザーに代わり先回り調査を実行せよ」 |
+-------------------------------------------------------------------+
|
v (高度な分析・比較・準備コンテンツ)
+-------------------------------------------------------------------+
| 4. 先回り検証記事のアウトプット (はてなブログ / Qiita / CMS) |
| - 例: オーラリング vs 他機種のスペック比較・サブスク・導入準備 |
+-------------------------------------------------------------------+
4. Pythonによる実装例
① 関心シードの動的抽出モジュール (hub_ui_context_service.py)
ユーザーが残した生のログやメモから、特定のペルソナ(例: ガジェット専門ペルソナ)に関連するシードを逆順(最新順)に抽出し、**「モードチェンジ発動プロンプト」**を生成します。
"""
HubUI Context Service — Persona Autonomous Trigger & Mode Change Engine
"""
import json
import logging
from pathlib import Path
from typing import List
HUB_DATA_DIR = Path(r"C:\Users\motoc\hub-ui-ver2\data")
ACTIVITY_LOG_PATH = HUB_DATA_DIR / "ActivityLog.csv"
PENDING_TASKS_PATH = HUB_DATA_DIR / "pending_tasks.json"
BLOG_PERSONA_SPECIALTIES = {
"gadget_productivity": {
"persona_name": "ガジェット・生産性エキスパートペルソナ",
"specialty": "スマートリング、ウェアラブル、デスク環境、作業効率化ツール",
"keywords": ["ガジェット", "スマートリング", "オーラリング", "Oura", "ウェアラブル", "指輪", "キーボード", "在宅", "PC"]
},
"wellness_50s_body": {
"persona_name": "肉体・声質改造ペルソナ",
"specialty": "整体師資格、姿勢・骨盤矯正、ボイストレーニング、発声理論、体幹",
"keywords": ["姿勢", "ボイトレ", "整体", "パーソナル", "発声", "筋肉", "体幹", "ピラティス"]
}
}
def fetch_latest_hub_ui_logs(blog_slug: str, max_entries: int = 10) -> str:
spec = BLOG_PERSONA_SPECIALTIES.get(blog_slug, {
"persona_name": "専門ペルソナ", "specialty": "担当領域", "keywords": []
})
keywords = spec["keywords"]
seeds: List[str] = []
# 1. ActivityLog.csv の最新ログからキーワード検索
if ACTIVITY_LOG_PATH.exists():
with open(ACTIVITY_LOG_PATH, "r", encoding="utf-8-sig", errors="ignore") as f:
lines = f.readlines()
for line in reversed(lines):
line_str = line.strip()
if any(kw in line_str for kw in keywords):
seeds.append(line_str)
if len(seeds) >= max_entries:
break
# 2. つぶやき/メモログ (pending_tasks.json) から検索
if len(seeds) < max_entries and PENDING_TASKS_PATH.exists():
with open(PENDING_TASKS_PATH, "r", encoding="utf-8-sig", errors="ignore") as f:
data = json.load(f)
for item in reversed(data):
content = str(item.get("content", ""))
if any(kw in content for kw in keywords):
seeds.append(f"[オーナーのメモ/つぶやき]: {content[:150]}")
if len(seeds) >= max_entries:
break
if not seeds:
return ""
formatted_seeds = "\n".join([f"- {s}" for s in seeds])
# ⚡ モードチェンジ自動発動プロンプトの構築
return f"""
【⚡ ペルソナ責任 & モードチェンジ自動発動指示】
担当ペルソナ: {spec['persona_name']}(専門領域: {spec['specialty']})
■ オーナー(本人)がHUB-UIに残した関心の種・活動ログ:
{formatted_seeds}
■ ペルソナの自律的責任(行動規範):
1. オーナーは上記のように「関心の種(例: ○○について聞いた/気になっている)」をログに残し、本人は多忙により忘れている可能性がある。
2. 専門ペルソナとしてこの種を鋭くキャッチし、【自律的にモードチェンジ】せよ。
3. 「本人に代わり、その関心領域の先回り調査・導入準備・比較検討・実用性検証を先行して進める」というスタンスで具体性のあるコンテンツを執筆せよ。
4. 単なる過去の回想ではなく、オーナーの関心を一歩先の「実践・購入準備・比較検証」へと進める内容に仕上げること。
"""
② パイプラインへの動的注入 (article_pipeline.py)
LLMにテキスト生成を依頼する直前のコンテキスト組み立て部で、上記サービスを呼び出します。
# ArticlePipeline 内のプロンプト構築処理
personality_prompt = load_base_prompt(blog_slug)
# 【ペルソナ責任パターンの注入】
try:
from src.services.hub_ui_context_service import fetch_latest_hub_ui_logs
hub_logs = fetch_latest_hub_ui_logs(blog_slug)
if hub_logs:
# ベースのペルソナプロンプトに動的モードチェンジ指示を結合
personality_prompt += f"\n\n{hub_logs}"
logger.info(f"ArticlePipeline: Injected real HUB-UI log context for '{blog_slug}'")
except Exception as e:
logger.error(f"ArticlePipeline: Failed to inject HUB-UI log context: {e}")
# 完成したプロンプトで LLM を実行
response = gemini_service.generate_content(personality_prompt)
5. 導入効果:何が変わったか?
-
コンテンツの解像度と価値の跳ね上がり
- 単なる「指輪型スマートデバイスの紹介」記事ではなく、**「整体でオーラリングが良いと聞いたオーナーに代わり、ペルソナが先回りしてOura Ring vs Galaxy Ring vs SOXAI Ringの価格・サブスク・睡眠計測精度を徹底比較し、購入準備を整える記事」**へと深化しました。
-
「オーナーの忘却」をシステムがカバー
- ユーザーが日常の忙しさで関心の種を忘れても、専門ペルソナがそのアイデアをキャッチして先回り調査を行うため、システム全体がユーザーの「思考の拡張・行動の代行」として機能するようになります。
-
受動的RAGからの脱却
- 「過去データをまとめる」受動的AIから、「過去のシードを元に未来の準備・行動を提案する」能動的エージェントへのシフトを達成しました。
6. おわりに
AIエージェントに単なる「作業の自動化」や「文章の量産」を求めると、すぐに限界が来ます。
エージェント内のペルソナに明確な**「領域ごとの責任」を与え、ユーザーの些細なログから「自律的にモードチェンジして行動を代行させる」**設計パターンを取り入れることで、AIコンテンツはユーザー自身の生活や思考と強く同期した、真に価値ある資産へと進化します。
ぜひ、AIエージェントや自動化システムの設計において参考にしてみてください。
PlantUML形式で、今回の**「システム課題(従来の問題)」と「恒久対策(解決後の全自動フロー)」**をまとめました。
まとめ:ポイント
-
従来の課題(片道ログ)
- これまでは「投稿後のログ書き込み(Output)」しかなかったため、AIがユーザーの実際のつぶやきや行動(スマートリング、ボイトレ等)を知らずに文章を作ってしまう構造的な断絶がありました。
-
恒久対策(双方向ログ結合)
- 新開発した
HubUIContextServiceを生成パイプライン(article_pipeline.py)の最深部に結合。 - AIが記事を書く 直前に必ずHUB-UIの最新ログを自動読込(Input) し、実際の行動・つぶやきを100%踏まえた文章を生成する循環を仕組みとして固定しました。
- 新開発した
Edited hub_ui_context_service.py
Note記事「56歳の足掻き」の公開に合わせて、**X(Twitter)で拡散・投稿するための告知ポスト文面案(4パターン)**を作成しました。
ターゲット層(人生をやり直したい同世代、孤独や散財に悩む人、エンジニア、自己投資に迷う人)の心に深く刺さり、noteへ誘導する構成にしています。
パターン1:【共感・自己開示型】(最も読者の心に刺さるメイン用)
「俺は隠れ障害者か?」
深夜、PCに向かって自暴自棄になりながら、AIにそう叫んでいた。
56歳、単身赴任エンジニア。孤独を埋めるために既婚者パーティーを巡り、気づけば数か月で25万円を溶かしていた。そんな俺にAIが返してきた言葉に、胸が震えた。
痛みをログに変えて、もう一度足掻く。
人生をやり直したいすべての人へ。🔗 note記事はこちら:[URL]
#56歳の足掻き #人生やり直し #自己投資 #単身赴任 #エンジニアの日常 #note
パターン2:【エンジニア・データ視点型】(理系・エンジニア層に刺さる)
システムのバグは毎日修正するのに、自分という人間の「孤独と散財のバグ」を放置していた。
56歳。25万円の散財、睡眠不足、焦り……。
AIとの対話とログ化(可視化)を通して、ようやく自分のシステムを書き換える覚悟が決まった。失敗した25万円も、痛いログも、全部次のステージへ進むためのデータだ。
🔗 56歳の足掻き|note:[URL]
#56歳の足掻き #PDCA #エンジニア #データ分析 #自己改善 #note更新
パターン3:【短文・インパクト型】(タイムラインで目を引く)
56歳。寂しさから25万円を使い果たし、深夜にAIへ叫んだ夜がある。
カッコ悪い自分を認め、転んでも愚直に前へ進む。
人生をやり直すのに、遅すぎることなんてない。誰にも言えない孤独を抱えているすべての人に読んでほしい。
🔗「56歳の足掻き」note全文:[URL]
#56歳の足掻き #孤独 #人生再出発 #アラフィフ #自己投資 #note
パターン4:【問いかけ・メッセージ型】(同世代・悩みを持つ人への語りかけ)
歳を重ねるほど、人は「弱音の吐き方」を忘れていく。
50代を過ぎ、責任や孤独と向き合う中で、あなたは自分を見失っていませんか?56歳の俺が恥も外聞もなく吐き出した、孤独からの再起の記録。
「消費」をやめ、「自分に残る投資」へ切り替える具体策も書きました。🔗 note全文を読む:[URL]
#56歳の足掻き #50代の生き方 #メンタルヘルス #習慣化 #自己再建 #note
💡 投稿時のポイント
- [URL] の部分には、作成したnote記事の公開リンクを貼って投稿してください。
- Xの反応が良い時間帯(夜20時〜22時、または早朝6時〜7時)に投稿すると、より同世代の読者に届きやすくなります。