Closed10

AIアプリ向けプロファイルベースの長期メモリ「Memobase」を試す②

kun432kun432

以前に試したMemobase

https://zenn.dev/kun432/scraps/f87a4f1d40c3ba

もう少し色々触ってみる、ということでPart2。今回は気になったところだけをピックアップして見ていく。

なお、以前試したのはもう半年前ぐらいなので、中身はそれなりに変わっていると思われる。最新のドキュメントを参照されたし。

kun432kun432

Context()

登録したプロファイルやイベントをまるっと取得してプロンプト的な文字列で返す context() メソッドにはいくつかオプションがある。ドキュメントだとこのあたり。

https://docs.memobase.io/features/context

とりあえずこんな感じでMemobaseに登録したとする。会話文はGPT-5に作ってもらった。

from memobase import MemoBaseClient
from memobase import ChatBlob

mb_client = MemoBaseClient(
    project_url="http://<サーバのIPアドレス>:8019",
    api_key="secret",
)

uid = mb_client.add_user({"user_id": "test"})
u = mb_client.get_user(uid)

sample_messages = [
    {
      "role": "user",
      "content": "はじめまして。私は太郎といいます。普段はエンジニアをやっています。よろしくお願いします。"
    },
    {
      "role": "assistant",
      "content": "太郎さん、はじめまして。こちらこそよろしくお願いします。ご機嫌いかがですか?"
    },
    {
      "role": "user",
      "content": "明日は、競馬観戦に行く予定なんだよね。楽しみ〜。お天気はどうかな?"
    },
    {
      "role": "assistant",
      "content": "すごく楽しそうですね!明日の阪神競馬場の天気は晴れで、最高の観戦日和になると思いますよ!"
    },
    {
      "role": "user",
      "content": "それはうれしい!自宅が西宮市なので、阪神競馬場はアクセスが楽なんだよね。"
    },
    {
      "role": "assistant",
      "content": "西宮市にお住まいなんですね。明日はどなたと行かれる予定ですか?日差しが強いときは日焼け止めや帽子があると快適ですよ。"
    },
    {
      "role": "user",
      "content": "妹の花子と行く予定。彼女、競馬は初めてでワクワクしてるみたい。"
    },
    {
      "role": "assistant",
      "content": "花子さんと初観戦、良いですね!混雑もあるので、待ち合わせ場所を先に決めておくと安心です。ところで太郎さんはお仕事はエンジニアとのことですが、どんな分野がメインですか?"
    },
    {
      "role": "user",
      "content": "今年の4月に転職して、いまは大阪のフィンテック企業でバックエンドをやってる。GoとPythonが多いかな。"
    },
    {
      "role": "assistant",
      "content": "4月にご転職、おめでとうございます!通勤は毎日ですか?それともリモート併用でしょうか。"
    },
    {
      "role": "user",
      "content": "火木がリモートで、他の平日は大阪オフィス。西宮からだと電車一本で行ける日もあって助かってるよ。"
    },
    {
      "role": "assistant",
      "content": "ほどよいハイブリッド勤務なんですね。お住まいではペットなどはいらっしゃいますか?在宅の日の相棒がいると気分転換になりますよね。"
    },
    {
      "role": "user",
      "content": "去年の5月に柴犬を迎えたよ。名前は「むぎ」。散歩が日課。"
    },
    {
      "role": "assistant",
      "content": "「むぎ」ちゃん、かわいい名前!散歩は朝派ですか?それとも夜派?運動習慣にもつながって良いですね。"
    },
    {
      "role": "user",
      "content": "朝6時半に起きて、出社日の前に軽く20分歩くのがルーティン。最近はランもしてて、8月に10kmで49分30秒の自己ベスト出せた。"
    },
    {
      "role": "assistant",
      "content": "自己ベスト更新おめでとうございます!来年以降、大会に出るご予定はありますか?(例えば大阪マラソンなど)"
    },
    {
      "role": "user",
      "content": "大阪マラソンは2026年にフルで出たいと思ってる。来年はハーフを目標にするつもり。"
    },
    {
      "role": "assistant",
      "content": "長期計画がしっかりしていて素敵です。食事や体調面で気をつけていることはありますか?アレルギーや苦手な食べ物なども、覚えておきます。"
    },
    {
      "role": "user",
      "content": "甲殻類アレルギーで、特にエビは避けてる。あと18時以降のコーヒーは眠りに影響出るから控えるようにしてるよ。代わりにカモミールティー。"
    },
    {
      "role": "assistant",
      "content": "了解です。エビNG、夜はコーヒー控えめでカモミールですね。スポーツ以外の趣味はありますか?例えばプロ野球観戦など。"
    },
    {
      "role": "user",
      "content": "阪神タイガースのファン。先月、父と甲子園に行ってきた。実は両親は2023年に福岡へ引っ越したから、関西に来たタイミングで観戦したんだ。"
    },
    {
      "role": "assistant",
      "content": "ご両親は2023年に福岡へ転居されたんですね。家族での観戦は良い思い出になりますね。近々の予定で、旅行や記念日などはありますか?覚えておきます。"
    },
    {
      "role": "user",
      "content": "来月、彼女の美咲と北海道へ3泊4日の旅行。10月12日が付き合って2周年で、小樽でちょっと特別なディナーを予約した。"
    },
    {
      "role": "assistant",
      "content": "10月12日が2周年、そして北海道(札幌・小樽)へ3泊4日ですね。素敵!ご希望なら当日近くにディナーのリマインドや、甲殻類を避けられるお店候補の提案もできます。"
    }
]

u.insert(ChatBlob(messages=sample_messages))
u.flush()

普通に context() を実行するとこんな感じ。

print(u.context())
出力
---
# Memory
Unless the user has relevant queries, do not actively mention those memories in the conversation.
## User Current Profile:
- basic_info::name: 太郎
- work::title: backend engineer
- work::company: fintech company in Osaka
- work::working_industry: fintech
- work::work_skills: Go, Python
- contact_info::city: 西宮市
- life_event::adoption: adopted a Shiba Inu named むぎ in May 2024
- schedule::plan: going to horse racing with sister 花子 on 2025/09/29
- schedule::walk: walks for 20 minutes before work at 6:30 AM
- event::personal_best: achieved a personal best of 49 minutes and 30 seconds for a 10km run in August 2025
- schedule::marathon: plans to participate in the Osaka Marathon in 2026 and aims to run a half marathon next year
- health::allergy: has a shellfish allergy, particularly avoiding shrimp
- health::caffeine: avoids coffee after 6 PM, opts for chamomile tea instead
- interest::sports_team: fan of 阪神タイガース
- event::family: went to Koshien with father last month
- life_event::parents_move: parents moved to 福岡 in 2023
- schedule::trip: plans a 3-night, 4-day trip to 北海道 with girlfriend 美咲 in October 2025
- schedule::anniversary: 2nd anniversary with 美咲 is on October 12, 2025, with a special dinner reserved in 小樽

## Past Events:
- User's alias is 太郎. // info
- 太郎 is an engineer. // info
- 太郎 lives in 西宮市. // info
- 太郎 plans to go to the horse racing tomorrow with his sister, 花子. [mention 2025/09/28, plan in 2025/09/29] // schedule
- 太郎 started a new job in April 2025 at a fintech company in Osaka, working as a backend engineer. // info
- 太郎 primarily uses Go and Python in his work. // info
- 太郎 works remotely on Tuesdays and Thursdays, and commutes to the Osaka office on other weekdays. // info
- 太郎 adopted a Shiba Inu named むぎ in May 2024. // life_event
- 太郎 walks for 20 minutes before work at 6:30 AM. // schedule
- 太郎 achieved a personal best of 49 minutes and 30 seconds for a 10km run in August 2025. // event
- 太郎 plans to participate in the Osaka Marathon in 2026 and aims to run a half marathon next year. // schedule
- 太郎 has a shellfish allergy, particularly avoiding shrimp. // info
- 太郎 avoids coffee after 6 PM to prevent sleep disturbances, opting for chamomile tea instead. // info
- 太郎 is a fan of the 阪神タイガース. // interest
- 太郎 went to Koshien with his father last month. // event
- 太郎's parents moved to 福岡 in 2023. // life_event
- 太郎 plans a 3-night, 4-day trip to 北海道 with his girlfriend 美咲 next month. [mention 2025/09/28, trip in October 2025]
---

これを元に色々なパラメータを試していく。


クエリを元により関連性のあるイベントを取得する場合、chatsパラメータにメッセージのリストを渡す。

recent_chats = [
    {"role": "user", "content": "趣味は?"}
]

print(u.context(chats=recent_chats))
出力
---
# Memory
Unless the user has relevant queries, do not actively mention those memories in the conversation.
## User Current Profile:
- basic_info::name: 太郎
- work::title: backend engineer
- work::company: fintech company in Osaka
- work::working_industry: fintech
- work::work_skills: Go, Python
- contact_info::city: 西宮市
- life_event::adoption: adopted a Shiba Inu named むぎ in May 2024
- schedule::plan: going to horse racing with sister 花子 on 2025/09/29
- schedule::walk: walks for 20 minutes before work at 6:30 AM
- event::personal_best: achieved a personal best of 49 minutes and 30 seconds for a 10km run in August 2025
- schedule::marathon: plans to participate in the Osaka Marathon in 2026 and aims to run a half marathon next year
- health::allergy: has a shellfish allergy, particularly avoiding shrimp
- health::caffeine: avoids coffee after 6 PM, opts for chamomile tea instead
- interest::sports_team: fan of 阪神タイガース
- event::family: went to Koshien with father last month
- life_event::parents_move: parents moved to 福岡 in 2023
- schedule::trip: plans a 3-night, 4-day trip to 北海道 with girlfriend 美咲 in October 2025
- schedule::anniversary: 2nd anniversary with 美咲 is on October 12, 2025, with a special dinner reserved in 小樽

## Past Events:
- 太郎 is a fan of the 阪神タイガース. // interest
- User's alias is 太郎. // info
- 太郎 plans to go to the horse racing tomorrow with his sister, 花子. [mention 2025/09/28, plan in 2025/09/29] // schedule
- 太郎 plans a 3-night, 4-day trip to 北海道 with his girlfriend 美咲 next month. [mention 2025/09/28, trip in October 2025]
- 太郎 has a shellfish allergy, particularly avoiding shrimp. // info
- 太郎 went to Koshien with his father last month. // event
- 太郎 primarily uses Go and Python in his work. // info
- 太郎's 2nd anniversary with 美咲 is on October 12, 2025, and he has reserved a special dinner in 小樽. [mention 2025/09/28, anniversary on 2025/10/12] // schedule
---

プロフィール部分は殆ど同じだけど、イベント部分がやや少なくなっているように思える。おそらく「趣味」というところでセマンティック検索した結果になるのだろう。


出力されるコンテキストの量を調整できる。ドキュメントではmax_tokensパラメータとあるが、指定すると怒られる。どうやら max_token_size が正解みたい。

print(u.context(max_token_size=200))

プロフィールもイベントもだいぶコンパクトな感じになった。

出力
---
# Memory
Unless the user has relevant queries, do not actively mention those memories in the conversation.
## User Current Profile:
- basic_info::name: 太郎
- work::title: backend engineer
- work::company: fintech company in Osaka
- work::working_industry: fintech
- work::work_skills: Go, Python
- contact_info::city: 西宮市
- life_event::adoption: adopted a Shiba Inu named むぎ in May 2024
- schedule::plan: going to horse racing with sister 花子 on 2025/09/29
- schedule::walk: walks for 20 minutes before work at 6:30 AM

## Past Events:
- User's alias is 太郎. // info
- 太郎 is an engineer. // info
- 太郎 lives in 西宮市. // info
- 太郎 plans to go to the horse racing tomorrow with his sister, 花子. [mention 2025/09/28, plan in 2025/09/29] // schedule
---

少し増やしてみる。

print(u.context(max_token_size=400))
出力
---
# Memory
Unless the user has relevant queries, do not actively mention those memories in the conversation.
## User Current Profile:
- basic_info::name: 太郎
- work::title: backend engineer
- work::company: fintech company in Osaka
- work::working_industry: fintech
- work::work_skills: Go, Python
- contact_info::city: 西宮市
- life_event::adoption: adopted a Shiba Inu named むぎ in May 2024
- schedule::plan: going to horse racing with sister 花子 on 2025/09/29
- schedule::walk: walks for 20 minutes before work at 6:30 AM
- event::personal_best: achieved a personal best of 49 minutes and 30 seconds for a 10km run in August 2025
- schedule::marathon: plans to participate in the Osaka Marathon in 2026 and aims to run a half marathon next year
- health::allergy: has a shellfish allergy, particularly avoiding shrimp
- health::caffeine: avoids coffee after 6 PM, opts for chamomile tea instead
- interest::sports_team: fan of 阪神タイガース
- event::family: went to Koshien with father last month
- life_event::parents_move: parents moved to 福岡 in 2023

## Past Events:
- User's alias is 太郎. // info
- 太郎 is an engineer. // info
- 太郎 lives in 西宮市. // info
- 太郎 plans to go to the horse racing tomorrow with his sister, 花子. [mention 2025/09/28, plan in 2025/09/29] // schedule
- 太郎 started a new job in April 2025 at a fintech company in Osaka, working as a backend engineer. // info
- 太郎 primarily uses Go and Python in his work. // info
- 太郎 works remo

何かしらルールベース的に出力するものを選定しているような気がするんだけど、このバランスはどうやって調整しているんだろう?なんとなくプロフィールもイベントも先頭から読んでいるような感じには見える。

あと、注意書きには以下とある(日本語に訳している)

注記: max_tokens の制限はプロファイルおよびイベントの内容に適用され、最終的なフォーマット済み文字列には適用されません。大規模なカスタムプロンプトテンプレートを使用する場合、最終的な出力は依然として制限を超える可能性があります。

んー、まあ最終的な出力に制限かけてぶつ切りになるというわけではなさそう。


その他にもいくつかパラメータがあるが、クライアントSDKは結局のところ、APIを叩いているだけなので、APIリファレンスを見るのがわかりやすそう。

プロフィール・イベントの両方をまるっと「コンテキスト」として取得するのは以下のAPI。なお、プロフィールだけ、イベントだけ、っていう取得方法もある。

https://docs.memobase.io/api-reference/prompt/get_context

SDK引数(APIパラメータが異なる場合は / で下段に記載) デフォルト 説明
max_token_size integer 1000 返却されるコンテキストの最大トークン数。
prefer_topics string[] なし フィルタ時に優先的に保持したいトピック。更新時刻の順より優先。
only_topics string[] なし 指定したトピックのプロフィールのみ返す。未指定の場合は全て。
max_subtopic_size integer なし 同一トピックに含めるサブトピック数の上限。
topic_limits /
topic_limits_json
dict /
string(JSON)
なし トピック別サブトピック上限の個別指定。max_subtopic_size を上書き。
profile_event_ratio number 0.6 コンテキスト内のプロフィールとイベントの配分比率。
require_event_summary boolean false イベント要約を必須にするか。
chats /
chats_str
OpenAI互換メッセージ配列 / string(JSON配列) なし 直近チャットを渡して関連イベント検索を有効化。レイテンシとコスト増あり。
event_similarity_threshold number 0.2 イベント類似度のしきい値。
customize_context_prompt string なし コンテキストのテンプレート文字列。{profile_section} / {event_section}が利用可能。
full_profile_and_only_search_event boolean true falseでプロフィールとイベント双方を検索。trueはイベントのみ検索。
fill_window_with_events boolean false 残りトークン枠をイベントで埋める。
time_range_in_days(APIのみ) integer 180 Context API一般仕様に存在。主にイベント検索系で使用)。

例えば、いくつか使ってみるとこんな感じ。

custom_context_prompt = """\
=== 長期メモリ ===
---- ユーザのプロフィール ----
{profile_section}
---- ユーザの過去のイベント ----
{event_section}
"""

print(u.context(
    max_token_size=500,
    profile_event_ratio=0.9,
    customize_context_prompt=custom_context_prompt
))
出力
=== 長期メモリ ===
---- ユーザのプロフィール ----
- basic_info::name: 太郎
- work::title: backend engineer
- work::company: fintech company in Osaka
- work::working_industry: fintech
- work::work_skills: Go, Python
- contact_info::city: 西宮市
- life_event::adoption: adopted a Shiba Inu named むぎ in May 2024
- schedule::plan: going to horse racing with sister 花子 on 2025/09/29
- schedule::walk: walks for 20 minutes before work at 6:30 AM
- event::personal_best: achieved a personal best of 49 minutes and 30 seconds for a 10km run in August 2025
- schedule::marathon: plans to participate in the Osaka Marathon in 2026 and aims to run a half marathon next year
- health::allergy: has a shellfish allergy, particularly avoiding shrimp
- health::caffeine: avoids coffee after 6 PM, opts for chamomile tea instead
- interest::sports_team: fan of 阪神タイガース
- event::family: went to Koshien with father last month
- life_event::parents_move: parents moved to 福岡 in 2023
- schedule::trip: plans a 3-night, 4-day trip to 北海道 with girlfriend 美咲 in October 2025
- schedule::anniversary: 2nd anniversary with 美咲 is on October 12, 2025, with a special dinner reserved in 小樽
---- ユーザの過去のイベント ----
- User's alias is 太郎. // info
- 太郎 is an engineer. // info
- 太郎 lives in 西宮市. // info

profile_event_ratioを使えば、プロファイルだけ、イベントだけ、っていうこともできる。ただ、time_range_in_daysも使えればイベント検索の時間幅も絞れて便利そうなんだけど、SDKのcontext()メソッドには実行されていないんだよね、漏れてるだけかな?

Issueあげといた。

https://github.com/memodb-io/memobase/issues/136

kun432kun432

メッセージ登録時のオプション

https://docs.memobase.io/features/message

会話履歴を登録するだけでMemobaseは長期メモリを生成してくれるが、ここでもいくつかオブションがある。


カスタムタイムスタンプ

Memobaseにおけるメモリには2つのタイムスタンプがある。

  • 外部タイムスタンプ: DBにメモリが「保存・更新された際の日時」
  • 内部タイムスタンプ: メモリの内容に含まれる「出来事が起きた時刻」(誕生日、旅行日、など)

デフォルトだと(メモリの内容にタイムスタンプ的な情報が含まれていなければ)「保存・更新したタイムスタンプ=そのメモリやイベントが発生したタイムスタンプ」として認識される。

各メッセージにcreated_at を付与すると、このタイムスタンプを明示的に指定することができる。

from memobase import MemoBaseClient, ChatBlob

client = MemoBaseClient(
    project_url="http://localhost:8019",
    api_key="secret",
)

uid = client.add_user()
u = client.get_user(uid)

u.insert(
    ChatBlob(
        messages=[
            {
                "role": "user",
                "content": "はじめまして。私は太郎といいます。普段はエンジニアをやっています。よろしくお願いします。",
                "created_at": "2025/01/04"
            },
            {
                "role": "assistant",
                "content": "太郎さん、はじめまして。こちらこそよろしくお願いします。ご機嫌いかがですか?",
                "created_at": "2025/01/04"
            },
            {
                "role": "user",
                "content": "明日は、競馬観戦に行く予定なんだよね。楽しみ〜。お天気はどうかな?",
                "created_at": "2025/01/04"
            },
            {
                "role": "assistant",
                "content": "すごく楽しそうですね!明日の阪神競馬場の天気は晴れで、最高の観戦日和になると思いますよ!",
                "created_at": "2025/01/04"
            }
        ]
    )
)

u.flush()
print(u.context())
print()
出力
---
# Memory
Unless the user has relevant queries, do not actively mention those memories in the conversation.
## User Current Profile:
- basic_info::name: 太郎
- work::title: engineer [mention 2025/01/04]
- life_event::schedule: plans to go to a horse racing event on 2025/01/05 [mention 2025/01/04]

## Past Events:
- User's alias is 太郎. // info
- 太郎 is an engineer. [mention 2025/01/04] // info
- 太郎 plans to go to a horse racing event tomorrow. [mention 2025/01/04, plan in 2025/01/05] // schedule
---

なお、日付は特にフォーマットを意識しなくても良い。ここはLLMでよしなに解釈される様子。


キャラクターエイリアス

通常、会話履歴のロールは userassistant になるが、ロールプレイとか複数の登場人物がいる時などはaliasで各メッセージに名前をつけることができる。

from memobase import MemoBaseClient, ChatBlob

client = MemoBaseClient(
    project_url="http://rtx4090.local:8019",
    api_key="secret",
)

uid = client.add_user()
print("UID:", uid)
print()

u = client.get_user(uid)

u.insert(
    ChatBlob(
        messages=[
            {
                "role": "user",
                "content": "はじめまして。普段はエンジニアをやっています。よろしくお願いします。",
                "alias": "太郎"
            },
            {
                "role": "assistant",
                "content": "はじめまして。AIアシスタントとしてあなたのお手伝いをしますよ。よろしくお願いします。ご機嫌いかがですか?",
                "alias": "花子"
            },
            {
                "role": "user",
                "content": "明日は、競馬観戦に行く予定なんだよね。楽しみ〜。お天気はどうかな?",
                "alias": "太郎"
            },
            {
                "role": "assistant",
                "content": "すごく楽しそうですね!明日の阪神競馬場の天気は晴れで、最高の観戦日和になると思いますよ!",
                "alias": "花子"
            }
        ]
    )
)

u.flush()
print(u.context())
print()
出力
---
# Memory
Unless the user has relevant queries, do not actively mention those memories in the conversation.
## User Current Profile:
- work::title: 太郎 is an engineer [mention 2025/09/29]
- life_event::schedule: 太郎 plans to go to a horse racing event on 2025/09/30 [mention 2025/09/29]

## Past Events:
- 太郎 is an engineer. [mention 2025/09/29] // info
- 太郎 plans to go to a horse racing event tomorrow. [mention 2025/09/29, plan in 2025/09/30] // schedule
---

assistant の方はメモリに含まれなかったけど、userのほうはalias からユーザ名を取得できているのがわかる。

kun432kun432

プロファイルの検索

context()メソッドを使えば、プロファイル・イベントまるっと取れて便利で、chatsパラメータで会話を渡せばセマンティックな検索もできるのは上の方で書いた通り。

ただし、より細かく検索を制御したい場合は、プロファイル・イベントそれぞれのAPIを使うのが良さそう。プロファイルは基本的にprofile()メソッドで取得でき、パラメータでフィルタリング・検索をカスタマイズできる。

カスタマイズ方法は2つ

  1. ルールベースで絞り込み
  2. セマンティックに絞り込み検索

1. ルールベースで絞り込み

https://docs.memobase.io/features/profile/profile_filter

まず、ルールベースでのフィルタリング。

context()メソッドのサンプルを流用してまずメモリを作成。

from memobase import MemoBaseClient
from memobase import ChatBlob

mb_client = MemoBaseClient(
    project_url="http://<サーバのIPアドレス>:8019",
    api_key="secret",
)

uid = mb_client.add_user({"user_id": "test"})
u = mb_client.get_user(uid)

sample_messages = [
    {
      "role": "user",
      "content": "はじめまして。私は太郎といいます。普段はエンジニアをやっています。よろしくお願いします。"
    },
    {
      "role": "assistant",
      "content": "太郎さん、はじめまして。こちらこそよろしくお願いします。ご機嫌いかがですか?"
    },
    {
      "role": "user",
      "content": "明日は、競馬観戦に行く予定なんだよね。楽しみ〜。お天気はどうかな?"
    },
    {
      "role": "assistant",
      "content": "すごく楽しそうですね!明日の阪神競馬場の天気は晴れで、最高の観戦日和になると思いますよ!"
    },
    {
      "role": "user",
      "content": "それはうれしい!自宅が西宮市なので、阪神競馬場はアクセスが楽なんだよね。"
    },
    {
      "role": "assistant",
      "content": "西宮市にお住まいなんですね。明日はどなたと行かれる予定ですか?日差しが強いときは日焼け止めや帽子があると快適ですよ。"
    },
    {
      "role": "user",
      "content": "妹の花子と行く予定。彼女、競馬は初めてでワクワクしてるみたい。"
    },
    {
      "role": "assistant",
      "content": "花子さんと初観戦、良いですね!混雑もあるので、待ち合わせ場所を先に決めておくと安心です。ところで太郎さんはお仕事はエンジニアとのことですが、どんな分野がメインですか?"
    },
    {
      "role": "user",
      "content": "今年の4月に転職して、いまは大阪のフィンテック企業でバックエンドをやってる。GoとPythonが多いかな。"
    },
    {
      "role": "assistant",
      "content": "4月にご転職、おめでとうございます!通勤は毎日ですか?それともリモート併用でしょうか。"
    },
    {
      "role": "user",
      "content": "火木がリモートで、他の平日は大阪オフィス。西宮からだと電車一本で行ける日もあって助かってるよ。"
    },
    {
      "role": "assistant",
      "content": "ほどよいハイブリッド勤務なんですね。お住まいではペットなどはいらっしゃいますか?在宅の日の相棒がいると気分転換になりますよね。"
    },
    {
      "role": "user",
      "content": "去年の5月に柴犬を迎えたよ。名前は「むぎ」。散歩が日課。"
    },
    {
      "role": "assistant",
      "content": "「むぎ」ちゃん、かわいい名前!散歩は朝派ですか?それとも夜派?運動習慣にもつながって良いですね。"
    },
    {
      "role": "user",
      "content": "朝6時半に起きて、出社日の前に軽く20分歩くのがルーティン。最近はランもしてて、8月に10kmで49分30秒の自己ベスト出せた。"
    },
    {
      "role": "assistant",
      "content": "自己ベスト更新おめでとうございます!来年以降、大会に出るご予定はありますか?(例えば大阪マラソンなど)"
    },
    {
      "role": "user",
      "content": "大阪マラソンは2026年にフルで出たいと思ってる。来年はハーフを目標にするつもり。"
    },
    {
      "role": "assistant",
      "content": "長期計画がしっかりしていて素敵です。食事や体調面で気をつけていることはありますか?アレルギーや苦手な食べ物なども、覚えておきます。"
    },
    {
      "role": "user",
      "content": "甲殻類アレルギーで、特にエビは避けてる。あと18時以降のコーヒーは眠りに影響出るから控えるようにしてるよ。代わりにカモミールティー。"
    },
    {
      "role": "assistant",
      "content": "了解です。エビNG、夜はコーヒー控えめでカモミールですね。スポーツ以外の趣味はありますか?例えばプロ野球観戦など。"
    },
    {
      "role": "user",
      "content": "阪神タイガースのファン。先月、父と甲子園に行ってきた。実は両親は2023年に福岡へ引っ越したから、関西に来たタイミングで観戦したんだ。"
    },
    {
      "role": "assistant",
      "content": "ご両親は2023年に福岡へ転居されたんですね。家族での観戦は良い思い出になりますね。近々の予定で、旅行や記念日などはありますか?覚えておきます。"
    },
    {
      "role": "user",
      "content": "来月、彼女の美咲と北海道へ3泊4日の旅行。10月12日が付き合って2周年で、小樽でちょっと特別なディナーを予約した。"
    },
    {
      "role": "assistant",
      "content": "10月12日が2周年、そして北海道(札幌・小樽)へ3泊4日ですね。素敵!ご希望なら当日近くにディナーのリマインドや、甲殻類を避けられるお店候補の提案もできます。"
    }
]

u.insert(ChatBlob(messages=sample_messages))
u.flush()
print(u.context())
出力
---
# Memory
Unless the user has relevant queries, do not actively mention those memories in the conversation.
## User Current Profile:
- basic_info::name: 太郎
- work::title: backend engineer
- work::company: fintech company in Osaka
- work::working_industry: fintech
- contact_info::city: 西宮市
- life_event::adoption: adopted a Shiba Inu named むぎ in May 2024 [mention 2025/09/29, adopt dog in 2024/05]
- life_event::trip: going on a 3-night, 4-day trip to 北海道 with his girlfriend, 美咲, next month [mention 2025/09/29, trip in 2025/10]
- life_event::anniversary: 2nd anniversary with 美咲 on October 12, 2025, with a special dinner reservation in 小樽 [mention 2025/09/29, anniversary on 2025/10/12]
- schedule::plan: plans to go to the horse racing tomorrow with his sister, 花子 [mention 2025/09/29, plan in 2025/09/30]; plans to participate in the 大阪マラソン in 2026 and aims for a half marathon next year [mention 2025/09/29, plan in 2026 and 2025]
- psychological::health: has a shellfish allergy, particularly avoiding shrimp; avoids coffee after 6 PM to prevent sleep disturbances and drinks chamomile tea instead
- interest::sports: fan of 阪神タイガース and went to Koshien with his father last month [mention 2025/09/29, go to Koshien in 2025/08]
- demographics::parents_moved: parents moved to 福岡 in 2023 [mention 2025/09/29, move in 2023]
- psychological::fitness: walks for 20 minutes every morning before going to work; achieved a personal best of 49 minutes and 30 seconds for a 10km run in August 2025 [mention 2025/09/29, achieve best in 2025/08]

## Past Events:
- User's alias is 太郎. // info
- 太郎 is an engineer. // info
- 太郎 lives in 西宮市. // info
- 太郎 plans to go to the horse racing tomorrow with his sister, 花子. [mention 2025/09/29, plan in 2025/09/30] // schedule
- 太郎 started a new job in April 2025 at a fintech company in Osaka, working as a backend engineer. [mention 2025/09/29, start job in 2025/04] // info
- 太郎 works remotely on 火曜日 and 木曜日, and goes to the office on other weekdays. // info
- 太郎 adopted a Shiba Inu named むぎ in May 2024. [mention 2025/09/29, adopt dog in 2024/05] // info
- 太郎 walks for 20 minutes every morning before going to work. // info
- 太郎 achieved a personal best of 49 minutes and 30 seconds for a 10km run in August 2025. [mention 2025/09/29, achieve best in 2025/08] // info
- 太郎 plans to participate in the 大阪マラソン in 2026 and aims for a half marathon next year. [mention 2025/09/29, plan in 2026 and 2025] // schedule
- 太郎 has a shellfish allergy, particularly avoiding shrimp. // info
- 太郎 avoids coffee after 6 PM to prevent sleep disturbances and drinks chamomile tea instead. // info
- 太郎 is a fan of 阪神タイガース and went to Koshien with his father last month. [mention 2025/09/29, go to Koshien in 2025/08] // info
---

プロファイルの取得はprofile()メソッドで行う。need_jsonで辞書で返ってくる(おそらくAPIパラメータ的にはJSONで返すがクライアントは辞書になるってことみたい。)。

u.profile(
    need_json=True
)

フィルタ条件なしだとこんな感じ。

出力
{'basic_info': {'name': {'id': UUID('d81bb2da-f214-4a23-a55d-c51d90c2f752'),
   'content': '太郎',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))}},
 'work': {'title': {'id': UUID('5a53f288-f866-4435-aa4e-a1d7c535d7cc'),
   'content': 'backend engineer',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))},
  'company': {'id': UUID('57dd1d72-d061-49f0-b983-64637a8c6c3a'),
   'content': 'fintech company in Osaka',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))},
  'working_industry': {'id': UUID('f1bf98b9-d42a-4e26-8e1d-7915219ac7f4'),
   'content': 'fintech',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))}},
 'contact_info': {'city': {'id': UUID('8bf79534-f8a0-4b49-a17b-2ae4f30d2f4e'),
   'content': '西宮市',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))}},
 'life_event': {'adoption': {'id': UUID('35c13d30-8afe-4d42-984f-34a2b4b27dbc'),
   'content': 'adopted a Shiba Inu named むぎ in May 2024 [mention 2025/09/29, adopt dog in 2024/05]',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))},
  'trip': {'id': UUID('39b79e98-a7fc-4d9b-887d-be99e5b64479'),
   'content': 'going on a 3-night, 4-day trip to 北海道 with his girlfriend, 美咲, next month [mention 2025/09/29, trip in 2025/10]',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))},
  'anniversary': {'id': UUID('959eaa7a-cea9-47c1-9470-46c99b33bd88'),
   'content': '2nd anniversary with 美咲 on October 12, 2025, with a special dinner reservation in 小樽 [mention 2025/09/29, anniversary on 2025/10/12]',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))}},
 'schedule': {'plan': {'id': UUID('d1951754-ef35-4f63-8c46-6a5f8eb1ee15'),
   'content': 'plans to go to the horse racing tomorrow with his sister, 花子 [mention 2025/09/29, plan in 2025/09/30]; plans to participate in the 大阪マラソン in 2026 and aims for a half marathon next year [mention 2025/09/29, plan in 2026 and 2025]',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))}},
 'psychological': {'health': {'id': UUID('1a271efe-4081-4212-b485-91bd264d3a4b'),
   'content': 'has a shellfish allergy, particularly avoiding shrimp; avoids coffee after 6 PM to prevent sleep disturbances and drinks chamomile tea instead',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))},
  'fitness': {'id': UUID('15d1d491-4cfe-4201-9392-b1fb83bfcfb6'),
   'content': 'walks for 20 minutes every morning before going to work; achieved a personal best of 49 minutes and 30 seconds for a 10km run in August 2025 [mention 2025/09/29, achieve best in 2025/08]',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))}},
 'interest': {'sports': {'id': UUID('949a47bc-4dfa-4e9f-9895-33f299127241'),
   'content': 'fan of 阪神タイガース and went to Koshien with his father last month [mention 2025/09/29, go to Koshien in 2025/08]',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))}},
 'demographics': {'parents_moved': {'id': UUID('93b47dec-8b36-4eb1-b250-6128d32dc093'),
   'content': 'parents moved to 福岡 in 2023 [mention 2025/09/29, move in 2023]',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))}}}

まずルールベース。ルールベースで使えるパラメータは以下。

  • max_token_size: プロフィール全体のサイズ上限を設定
  • prefer_topics: 特定トピックの優先度を上げる
  • only_topics: 指定トピックのみ抽出

max_token_sizeでトークンサイズでフィルタ

u.profile(
    need_json=True,
    max_token_size=100
)

上の例の前半部分だけがピックされたような感じに見える。

出力
{'basic_info': {'name': {'id': UUID('d81bb2da-f214-4a23-a55d-c51d90c2f752'),
   'content': '太郎',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))}},
 'work': {'title': {'id': UUID('5a53f288-f866-4435-aa4e-a1d7c535d7cc'),
   'content': 'backend engineer',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))},
  'company': {'id': UUID('57dd1d72-d061-49f0-b983-64637a8c6c3a'),
   'content': 'fintech company in Osaka',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))},
  'working_industry': {'id': UUID('f1bf98b9-d42a-4e26-8e1d-7915219ac7f4'),
   'content': 'fintech',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))}},
 'contact_info': {'city': {'id': UUID('8bf79534-f8a0-4b49-a17b-2ae4f30d2f4e'),
   'content': '西宮市',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))}},
 'life_event': {'adoption': {'id': UUID('35c13d30-8afe-4d42-984f-34a2b4b27dbc'),
   'content': 'adopted a Shiba Inu named むぎ in May 2024 [mention 2025/09/29, adopt dog in 2024/05]',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))}}}

prefer_topics で 特定トピックの優先度を上げる。ここでは life_eventworkの優先度を上げてみる。

u.profile(
    need_json=True,
    prefer_topics=["life_event","work"],
)

順番が変わっているのがわかる。

出力
{'life_event': {'adoption': {'id': UUID('35c13d30-8afe-4d42-984f-34a2b4b27dbc'),
   'content': 'adopted a Shiba Inu named むぎ in May 2024 [mention 2025/09/29, adopt dog in 2024/05]',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))},
  'trip': {'id': UUID('39b79e98-a7fc-4d9b-887d-be99e5b64479'),
   'content': 'going on a 3-night, 4-day trip to 北海道 with his girlfriend, 美咲, next month [mention 2025/09/29, trip in 2025/10]',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))},
  'anniversary': {'id': UUID('959eaa7a-cea9-47c1-9470-46c99b33bd88'),
   'content': '2nd anniversary with 美咲 on October 12, 2025, with a special dinner reservation in 小樽 [mention 2025/09/29, anniversary on 2025/10/12]',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))}},
 'work': {'title': {'id': UUID('5a53f288-f866-4435-aa4e-a1d7c535d7cc'),
   'content': 'backend engineer',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))},
  'company': {'id': UUID('57dd1d72-d061-49f0-b983-64637a8c6c3a'),
   'content': 'fintech company in Osaka',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))},
  'working_industry': {'id': UUID('f1bf98b9-d42a-4e26-8e1d-7915219ac7f4'),
   'content': 'fintech',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))}},
 'basic_info': {'name': {'id': UUID('d81bb2da-f214-4a23-a55d-c51d90c2f752'),
   'content': '太郎',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))}},
 'contact_info': {'city': {'id': UUID('8bf79534-f8a0-4b49-a17b-2ae4f30d2f4e'),
   'content': '西宮市',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))}},
 'schedule': {'plan': {'id': UUID('d1951754-ef35-4f63-8c46-6a5f8eb1ee15'),
   'content': 'plans to go to the horse racing tomorrow with his sister, 花子 [mention 2025/09/29, plan in 2025/09/30]; plans to participate in the 大阪マラソン in 2026 and aims for a half marathon next year [mention 2025/09/29, plan in 2026 and 2025]',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))}},
 'psychological': {'health': {'id': UUID('1a271efe-4081-4212-b485-91bd264d3a4b'),
   'content': 'has a shellfish allergy, particularly avoiding shrimp; avoids coffee after 6 PM to prevent sleep disturbances and drinks chamomile tea instead',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))},
  'fitness': {'id': UUID('15d1d491-4cfe-4201-9392-b1fb83bfcfb6'),
   'content': 'walks for 20 minutes every morning before going to work; achieved a personal best of 49 minutes and 30 seconds for a 10km run in August 2025 [mention 2025/09/29, achieve best in 2025/08]',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))}},
 'interest': {'sports': {'id': UUID('949a47bc-4dfa-4e9f-9895-33f299127241'),
   'content': 'fan of 阪神タイガース and went to Koshien with his father last month [mention 2025/09/29, go to Koshien in 2025/08]',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))}},
 'demographics': {'parents_moved': {'id': UUID('93b47dec-8b36-4eb1-b250-6128d32dc093'),
   'content': 'parents moved to 福岡 in 2023 [mention 2025/09/29, move in 2023]',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))}}}

only_topicsで特定のトピックだけに限定。life_eventだけに限定してみた。

u.profile(
    need_json=True,
    only_topics=["life_event"],
)
出力
{'life_event': {'adoption': {'id': UUID('35c13d30-8afe-4d42-984f-34a2b4b27dbc'),
   'content': 'adopted a Shiba Inu named むぎ in May 2024 [mention 2025/09/29, adopt dog in 2024/05]',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))},
  'trip': {'id': UUID('39b79e98-a7fc-4d9b-887d-be99e5b64479'),
   'content': 'going on a 3-night, 4-day trip to 北海道 with his girlfriend, 美咲, next month [mention 2025/09/29, trip in 2025/10]',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))},
  'anniversary': {'id': UUID('959eaa7a-cea9-47c1-9470-46c99b33bd88'),
   'content': '2nd anniversary with 美咲 on October 12, 2025, with a special dinner reservation in 小樽 [mention 2025/09/29, anniversary on 2025/10/12]',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))}}}
kun432kun432

2. セマンティックに検索

https://docs.memobase.io/features/profile/profile_search

次にセマンティックなプロファイルの検索を行う。context() メソッドと同じように、chatsでメッセージを渡す。

u.profile(
    chats=[{"role": "user", "content": "転職しようかなぁ"}],
    need_json=True
)
出力
{'work': {'company': {'id': UUID('57dd1d72-d061-49f0-b983-64637a8c6c3a'),
   'content': 'fintech company in Osaka',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))},
  'title': {'id': UUID('5a53f288-f866-4435-aa4e-a1d7c535d7cc'),
   'content': 'backend engineer',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))},
  'working_industry': {'id': UUID('f1bf98b9-d42a-4e26-8e1d-7915219ac7f4'),
   'content': 'fintech',
   'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC)),
   'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 329092, tzinfo=TzInfo(UTC))}}}

転職に関係しそうなものだけがフィルタされている。ただ、このセマンティックなフィルタはレスポンスに少し時間がかかる。ここはドキュメントにも記載がある。

重要な考慮事項

  • レイテンシー: プロフィール検索は強力ですが、計算負荷の高い操作です。ユーザーのプロフィールサイズに応じて、応答時間に2~5秒を追加する場合があります。慎重にご利用ください。
  • コスト: プロフィール検索のたびにMemobaseトークンを消費します(1回の呼び出しあたり約100~1000トークン)。これによりご利用料金に影響が生じます。

後者のコストについてはMemobaseクラウドを使用する場合の説明だけど、セルフホストでもOpenAIモデルを使ったりしている場合は当然ながらコスト・レイテンシーは同様に発生する。


詳細はProfile APIのパラメータを見ると良い。

https://docs.memobase.io/api-reference/profiles/profile

kun432kun432

イベントの検索

https://docs.memobase.io/features/event/event_search

Memobaseは会話履歴から「イベント」を抽出し保存する。以下のような挙動になる。

  • 「イベント」に「タグ」も生成・付与して保存する(設定で有効化されている場合)
  • 取得時は、デフォルトだとイベントが時系列で返される

タグについては後述するとして、ここではイベントの検索を試してみる。なお、イベントをシンプルに取得するだけならば、event()メソッドを使う。

1つ前のサンプルコードを実行した状態から、そのまま続けて試してみる。

u.event()
出力
[
    UserEventData(
        id=UUID('2c7c4fd5-8937-4d27-9fdf-761717baf1bc'),
        event_data=EventData(
            profile_delta=[
            ProfileDelta(content='太郎', attributes={'topic': 'basic_info', 'sub_topic': 'name'}),
            ProfileDelta(content='backend engineer', attributes={'topic': 'work', 'sub_topic': 'title'}),
            ProfileDelta(content='fintech company in Osaka', attributes={'topic': 'work', 'sub_topic': 'company'}),
            ProfileDelta(content='fintech', attributes={'topic': 'work', 'sub_topic': 'working_industry'}),
            ProfileDelta(content='西宮市', attributes={'topic': 'contact_info', 'sub_topic': 'city'}),
            ProfileDelta(content='adopted a Shiba Inu named むぎ in May 2024 [mention 2025/09/29, adopt dog in 2024/05]', attributes={'topic': 'life_event', 'sub_topic': 'adoption'}),
            ProfileDelta(content='going on a 3-night, 4-day trip to 北海道 with his girlfriend, 美咲, next month [mention 2025/09/29, trip in 2025/10]', attributes={'topic': 'life_event', 'sub_topic': 'trip'}),
            ProfileDelta(content='2nd anniversary with 美咲 on October 12, 2025, with a special dinner reservation in 小樽 [mention 2025/09/29, anniversary on 2025/10/12]', attributes={'topic': 'life_event', 'sub_topic': 'anniversary'}),
            ProfileDelta(content='plans to go to the horse racing tomorrow with his sister, 花子 [mention 2025/09/29, plan in 2025/09/30]; plans to participate in the 大阪マラソン in 2026 and aims for a half marathon next year [mention 2025/09/29, plan in 2026 and 2025]', attributes={'topic': 'schedule', 'sub_topic': 'plan'}),
            ProfileDelta(content='has a shellfish allergy, particularly avoiding shrimp; avoids coffee after 6 PM to prevent sleep disturbances and drinks chamomile tea instead', attributes={'topic': 'psychological', 'sub_topic': 'health'}),
            ProfileDelta(content='fan of 阪神タイガース and went to Koshien with his father last month [mention 2025/09/29, go to Koshien in 2025/08]', attributes={'topic': 'interest', 'sub_topic': 'sports'}),
            ProfileDelta(content='parents moved to 福岡 in 2023 [mention 2025/09/29, move in 2023]', attributes={'topic': 'demographics', 'sub_topic': 'parents_moved'}),
            ProfileDelta(content='walks for 20 minutes every morning before going to work; achieved a personal best of 49 minutes and 30 seconds for a 10km run in August 2025 [mention 2025/09/29, achieve best in 2025/08]', attributes={'topic': 'psychological', 'sub_topic': 'fitness'})
        ],
        event_tip="```\n- User's alias is 太郎. // info\n- 太郎 is an engineer. // info\n- 太郎 lives in 西宮市. // info\n- 太郎 plans to go to the horse racing tomorrow with his sister, 花子. [mention 2025/09/29, plan in 2025/09/30] // schedule\n- 太郎 started a new job in April 2025 at a fintech company in Osaka, working as a backend engineer. [mention 2025/09/29, start job in 2025/04] // info\n- 太郎 works remotely on 火曜日 and 木曜日, and goes to the office on other weekdays. // info\n- 太郎 adopted a Shiba Inu named むぎ in May 2024. [mention 2025/09/29, adopt dog in 2024/05] // info\n- 太郎 walks for 20 minutes every morning before going to work. // info\n- 太郎 achieved a personal best of 49 minutes and 30 seconds for a 10km run in August 2025. [mention 2025/09/29, achieve best in 2025/08] // info\n- 太郎 plans to participate in the 大阪マラソン in 2026 and aims for a half marathon next year. [mention 2025/09/29, plan in 2026 and 2025] // schedule\n- 太郎 has a shellfish allergy, particularly avoiding shrimp. // info\n- 太郎 avoids coffee after 6 PM to prevent sleep disturbances and drinks chamomile tea instead. // info\n- 太郎 is a fan of 阪神タイガース and went to Koshien with his father last month. [mention 2025/09/29, go to Koshien in 2025/08] // info\n- 太郎's parents moved to 福岡 in 2023. [mention 2025/09/29, move in 2023] // info\n- 太郎 is going on a 3-night, 4-day trip to 北海道 with his girlfriend, 美咲, next month. [mention 2025/09/29, trip in 2025/10] \n- 太郎 and 美咲's 2nd anniversary is on October 12, 2025, and they have a special dinner reservation in 小樽. [mention 2025/09/29, anniversary on 2025/10/12] // schedule\n```",
event_tags=None
        ),
        created_at=datetime.datetime(2025, 9, 29, 9, 20, 28, 280967, tzinfo=TzInfo(UTC)),
        updated_at=datetime.datetime(2025, 9, 29, 9, 20, 28, 280967, tzinfo=TzInfo(UTC)),
        similarity=None
    )
]

イベントだけでなく、それに関連するプロファイル的なものも取得されているように思う。

シンプルに文字列として取り出したいならこんな感じで取り出せそう。

for ev in u.event():
    print(ev.event_data.event_tip)
出力
```
- User's alias is 太郎. // info
- 太郎 is an engineer. // info
- 太郎 lives in 西宮市. // info
- 太郎 plans to go to the horse racing tomorrow with his sister, 花子. [mention 2025/09/29, plan in 2025/09/30] // schedule
- 太郎 started a new job in April 2025 at a fintech company in Osaka, working as a backend engineer. [mention 2025/09/29, start job in 2025/04] // info
- 太郎 works remotely on 火曜日 and 木曜日, and goes to the office on other weekdays. // info
- 太郎 adopted a Shiba Inu named むぎ in May 2024. [mention 2025/09/29, adopt dog in 2024/05] // info
- 太郎 walks for 20 minutes every morning before going to work. // info
- 太郎 achieved a personal best of 49 minutes and 30 seconds for a 10km run in August 2025. [mention 2025/09/29, achieve best in 2025/08] // info
- 太郎 plans to participate in the 大阪マラソン in 2026 and aims for a half marathon next year. [mention 2025/09/29, plan in 2026 and 2025] // schedule
- 太郎 has a shellfish allergy, particularly avoiding shrimp. // info
- 太郎 avoids coffee after 6 PM to prevent sleep disturbances and drinks chamomile tea instead. // info
- 太郎 is a fan of 阪神タイガース and went to Koshien with his father last month. [mention 2025/09/29, go to Koshien in 2025/08] // info
- 太郎's parents moved to 福岡 in 2023. [mention 2025/09/29, move in 2023] // info
- 太郎 is going on a 3-night, 4-day trip to 北海道 with his girlfriend, 美咲, next month. [mention 2025/09/29, trip in 2025/10] 
- 太郎 and 美咲's 2nd anniversary is on October 12, 2025, and they have a special dinner reservation in 小樽. [mention 2025/09/29, anniversary on 2025/10/12] // schedule
```

でここから検索を行って必要なイベントだけを取り出す。検索方法は2つある。

  1. セマンティック検索
  2. Gist 検索

1. セマンティック検索

これはcontextprofileと同様に、会話履歴を渡すことでセマンティックに検索を行うというもの。セマンティック検索は search_event()メソッドを使う。シンプルにクエリを投げるだけみたいだけど・・・

u.search_event("マラソンについて")
出力
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[147], line 1
----> 1 u.search_event("マラソンについて")

File /opt/conda/lib/python3.12/site-packages/memobase/core/entry.py:297, in User.search_event(self, query, topk, similarity_threshold, time_range_in_days)
    291 params = f"?query={query}&topk={topk}&similarity_threshold={similarity_threshold}&time_range_in_days={time_range_in_days}"
    292 r = unpack_response(
    293     self.project_client.client.get(
    294         f"/users/event/search/{self.user_id}{params}"
    295     )
    296 )
--> 297 return [UserEventData.model_validate(e) for e in r.data["events"]]

KeyError: 'events'

うーん・・・バグかなぁ・・・とりあえず後で調べるとして、セマンティック検索では与えられたクエリを元に

  • ユーザーがその話題について話したイベント
  • 自動で付与された関連するタグイベント
  • 関連するプロフィールスロットを更新したイベント

が検索されるらしい。

2. Gist検索

gist検索とはなんぞや?となるけども、そもそもMemobaseにおけるイベントは「ある期間に起きた複数の情報のまとまり」を指す。で、event_gist はそれを構成する、単一の事実・予定・リマインダーなどを切り出した最小単位となるらしい。ここが一つ上のsearch_event()との違いみたい。

なるほど?わかったようなわからないような。実際に試してみる。gist検索はsearch_event_gist()メソッドを使う。

for eg in u.search_event_gist("マラソンについて"):
    display(eg.model_dump())
出力
{'id': UUID('3b56cafe-4fb0-4a91-8921-3485e75d5ada'),
 'gist_data': {'content': '- 太郎 plans to participate in the 大阪マラソン in 2026 and aims for a half marathon next year. [mention 2025/09/29, plan in 2026 and 2025] // schedule'},
 'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 280967, tzinfo=TzInfo(UTC)),
 'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 280967, tzinfo=TzInfo(UTC)),
 'similarity': 0.4922547936439514}
{'id': UUID('ea4dd2bb-198f-4971-8268-d110e292684e'),
 'gist_data': {'content': '- 太郎 achieved a personal best of 49 minutes and 30 seconds for a 10km run in August 2025. [mention 2025/09/29, achieve best in 2025/08] // info'},
 'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 280967, tzinfo=TzInfo(UTC)),
 'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 280967, tzinfo=TzInfo(UTC)),
 'similarity': 0.3350836434910516}
{'id': UUID('b9e96457-1384-4a91-9f49-3cd55ebba6c5'),
 'gist_data': {'content': '- 太郎 plans to go to the horse racing tomorrow with his sister, 花子. [mention 2025/09/29, plan in 2025/09/30] // schedule'},
 'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 280967, tzinfo=TzInfo(UTC)),
 'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 280967, tzinfo=TzInfo(UTC)),
 'similarity': 0.2615939222816279}
{'id': UUID('92470ae1-207d-4487-a7e0-1660e5f11dad'),
 'gist_data': {'content': '- 太郎 walks for 20 minutes every morning before going to work. // info'},
 'created_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 280967, tzinfo=TzInfo(UTC)),
 'updated_at': datetime.datetime(2025, 9, 29, 9, 20, 28, 280967, tzinfo=TzInfo(UTC)),
 'similarity': 0.2096834009697084}

こちらは検索できた。それっぽいものが並んでいるので、こちらもセマンティックではある。


とりあえず前者はエラーになってしまったので、違いが見えにくい感があるのだけど、とりあえず使い分けとしては、

  • セマンティック検索: 大まかなイベントをざっくり検索する
  • Gist 検索: イベント中の特定の事実を検索する

というような使い方になるのだろう。イベントは通常時系列でコンテキストが連続的・複合的になると思うので、俯瞰的に検索したいなら前者、ピンポイントなら後者って感じかなぁ。まあセマンティック検索がうまく動いてくれないのでなんともかんともなんだけども。注意すべきなのはおそらくこれもベクトル検索を行っているので、当然レイテンシーは発生すると思う。体感的にはプロファイルの検索ほど時間がかかるという感じはしなかったけど。

なお、それぞれのメソッドのパラメータは以下。topktime_range_in_days を使えば、コンテキストサイズに合わせて、絞り込むことができる。

https://github.com/memodb-io/memobase/blob/5efecc420f1628d350616ac19389da268fa72cc1/src/client/memobase/core/entry.py#L284-L290

https://github.com/memodb-io/memobase/blob/5efecc420f1628d350616ac19389da268fa72cc1/src/client/memobase/core/entry.py#L299-L305

APIリファレンスも

https://docs.memobase.io/api-reference/events/search_events

https://docs.memobase.io/api-reference/events/search_event_gists

kun432kun432

イベントタグ

https://docs.memobase.io/features/event/event_tag

上にも書いたけどタグについて。

Memobaseは会話履歴から「イベント」を抽出し保存する。以下のような挙動になる。

  • 「イベント」に「タグ」も生成・付与して保存する。

例えば以下のようなタグを付与できるらしい。

  • 感情(happy / frustrated
  • 人生のゴール(buying_a_house / learning_a_skill
  • 関係性(new_friend / family_mention

イベントタグの設定

イベントタグはデフォルトでは記録されないため、api/config.yamlでこれを設定する必要がある。サンプルに従って以下を追加、サーバを再起動した。

api/config.yaml
(snip)
event_tags:
  - name: "emotion"
    description: "Records the user's current emotional state."
  - name: "romance"
    description: "Tracks any mention of romantic relationships or feelings."

で、おそらくこれは新しく会話履歴が追加してメモリが生成される際に付与されることになると思うので、新しいユーザを作成して試す。

from memobase import MemoBaseClient
from memobase import ChatBlob

mb_client = MemoBaseClient(
    project_url="http://<サーバのIPアドレス>:8019",
    api_key="secret",
)

uid = mb_client.add_user({"user_id": "test"})
u = mb_client.get_user(uid)

sample_messages = [
    {
      "role": "user",
      "content": "はじめまして。私は太郎といいます。普段はエンジニアをやっています。よろしくお願いします。"
    },
    {
      "role": "assistant",
      "content": "太郎さん、はじめまして。こちらこそよろしくお願いします。ご機嫌いかがですか?"
    },
    {
      "role": "user",
      "content": "明日は、競馬観戦に行く予定なんだよね。楽しみ〜。お天気はどうかな?"
    },
    {
      "role": "assistant",
      "content": "すごく楽しそうですね!明日の阪神競馬場の天気は晴れで、最高の観戦日和になると思いますよ!"
    },
    {
      "role": "user",
      "content": "それはうれしい!自宅が西宮市なので、阪神競馬場はアクセスが楽なんだよね。"
    },
    {
      "role": "assistant",
      "content": "西宮市にお住まいなんですね。明日はどなたと行かれる予定ですか?日差しが強いときは日焼け止めや帽子があると快適ですよ。"
    },
    {
      "role": "user",
      "content": "妹の花子と行く予定。彼女、競馬は初めてでワクワクしてるみたい。"
    },
    {
      "role": "assistant",
      "content": "花子さんと初観戦、良いですね!混雑もあるので、待ち合わせ場所を先に決めておくと安心です。ところで太郎さんはお仕事はエンジニアとのことですが、どんな分野がメインですか?"
    },
    {
      "role": "user",
      "content": "今年の4月に転職して、いまは大阪のフィンテック企業でバックエンドをやってる。GoとPythonが多いかな。"
    },
    {
      "role": "assistant",
      "content": "4月にご転職、おめでとうございます!通勤は毎日ですか?それともリモート併用でしょうか。"
    },
    {
      "role": "user",
      "content": "火木がリモートで、他の平日は大阪オフィス。西宮からだと電車一本で行ける日もあって助かってるよ。"
    },
    {
      "role": "assistant",
      "content": "ほどよいハイブリッド勤務なんですね。お住まいではペットなどはいらっしゃいますか?在宅の日の相棒がいると気分転換になりますよね。"
    },
    {
      "role": "user",
      "content": "去年の5月に柴犬を迎えたよ。名前は「むぎ」。散歩が日課。"
    },
    {
      "role": "assistant",
      "content": "「むぎ」ちゃん、かわいい名前!散歩は朝派ですか?それとも夜派?運動習慣にもつながって良いですね。"
    },
    {
      "role": "user",
      "content": "朝6時半に起きて、出社日の前に軽く20分歩くのがルーティン。最近はランもしてて、8月に10kmで49分30秒の自己ベスト出せた。"
    },
    {
      "role": "assistant",
      "content": "自己ベスト更新おめでとうございます!来年以降、大会に出るご予定はありますか?(例えば大阪マラソンなど)"
    },
    {
      "role": "user",
      "content": "大阪マラソンは2026年にフルで出たいと思ってる。来年はハーフを目標にするつもり。"
    },
    {
      "role": "assistant",
      "content": "長期計画がしっかりしていて素敵です。食事や体調面で気をつけていることはありますか?アレルギーや苦手な食べ物なども、覚えておきます。"
    },
    {
      "role": "user",
      "content": "甲殻類アレルギーで、特にエビは避けてる。あと18時以降のコーヒーは眠りに影響出るから控えるようにしてるよ。代わりにカモミールティー。"
    },
    {
      "role": "assistant",
      "content": "了解です。エビNG、夜はコーヒー控えめでカモミールですね。スポーツ以外の趣味はありますか?例えばプロ野球観戦など。"
    },
    {
      "role": "user",
      "content": "阪神タイガースのファン。先月、父と甲子園に行ってきた。実は両親は2023年に福岡へ引っ越したから、関西に来たタイミングで観戦したんだ。"
    },
    {
      "role": "assistant",
      "content": "ご両親は2023年に福岡へ転居されたんですね。家族での観戦は良い思い出になりますね。近々の予定で、旅行や記念日などはありますか?覚えておきます。"
    },
    {
      "role": "user",
      "content": "来月、彼女の美咲と北海道へ3泊4日の旅行。10月12日が付き合って2周年で、小樽でちょっと特別なディナーを予約した。"
    },
    {
      "role": "assistant",
      "content": "10月12日が2周年、そして北海道(札幌・小樽)へ3泊4日ですね。素敵!ご希望なら当日近くにディナーのリマインドや、甲殻類を避けられるお店候補の提案もできます。"
    }
]

u.insert(ChatBlob(messages=sample_messages))
u.flush()

イベントタグを取得してみる。

events = user.event()
for event in events:
    print(event.event_data.event_tags)
出力
[EventTag(tag='romance', value='太郎 and 美咲')]

上のサンプルだと上記だけがタグ付けされた。

タグでイベントを検索することもできる。

events = u.search_event_by_tags(tags=["emotion", "romance"])
print(events)
出力
[]

んー、出てこない。romanceだけで検索してみたら出てきた。and検索っぽい動きに見える。

events = u.search_event_by_tags(tags=["romance"])
print(events[0].event_data.event_tags)
print(events[0].event_data.event_tip)
出力
[EventTag(tag='romance', value='太郎 and 美咲')]
```
- User's alias is 太郎. // info
- 太郎 is an engineer. // info
- 太郎 lives in 西宮市. // info
- 太郎 plans to go to the horse racing tomorrow with his sister, 花子. [mention 2025/09/30, plan in 2025/10/01] // schedule
- 太郎 started a new job in April 2025 at a fintech company in Osaka, working as a backend engineer. [mention 2025/09/30, start job in 2025/04] // info
- 太郎 works remotely on 火曜日 and 木曜日, and commutes to the Osaka office on other weekdays. // info
- 太郎 adopted a Shiba Inu named むぎ in May 2024. [mention 2025/09/30, adopt dog in 2024/05] // info
- 太郎 has a routine of walking for 20 minutes before work at 6:30 AM. // info
- 太郎 achieved a personal best of 49 minutes and 30 seconds for a 10km run in August 2025. [mention 2025/09/30, achieve best in 2025/08] // info
- 太郎 plans to participate in the 大阪マラソン in 2026 and aims for a half marathon next year. [mention 2025/09/30, plan in 2026] // schedule
- 太郎 has a shellfish allergy, specifically avoiding shrimp. // info
- 太郎 avoids coffee after 6 PM to prevent sleep issues and drinks chamomile tea instead. // info
- 太郎 is a fan of 阪神タイガース and went to Koshien with his father last month. [mention 2025/09/30, go to Koshien in 2025/08] // info
- 太郎's parents moved to 福岡 in 2023. [mention 2025/09/30, move in 2023] // info
- 太郎 is planning a 3-night, 4-day trip to 北海道 with his girlfriend, 美咲, next month. [mention 2025/09/30, plan trip in 2025/10] // schedule
- 太郎 and 美咲's 2nd anniversary is on October 12, 2025, and he has reserved a special dinner in 小樽. [mention 2025/09/30, anniversary on 2025/10/12] // schedule

んー、自分が想像したのは以下だけが取得できるイメージだったんだけど、「イベント」は個々のevent gistの集合で、タグはgistに付与されるのではなく、gistが含まれるイベントに付与されてるように見える。なので、まるっと取得されちゃう感じ。ピンポイントにsearch_event_gist_by_tags があればいいんだけどそれもない。

- 太郎 is planning a 3-night, 4-day trip to 北海道 with his girlfriend, 美咲, next month. [mention 2025/09/30, plan trip in 2025/10] // schedule
- 太郎 and 美咲's 2nd anniversary is on October 12, 2025, and he has reserved a special dinner in 小樽. [mention 2025/09/30, anniversary on 2025/10/12] // schedule

context()でもタグは出力されないので、いまいちタグの有用性が見えない。ただ、セマンティックなイベント検索などで使われているとあるので、ないよりはあったほうがいい、という感じかなぁ・・・

kun432kun432

event gistとeventの違いは上にも書いてるけど、

gist検索とはなんぞや?となるけども、そもそもMemobaseにおけるイベントは「ある期間に起きた複数の情報のまとまり」を指す。で、event_gist はそれを構成する、単一の事実・予定・リマインダーなどを切り出した最小単位となるらしい。

自分の認識だとこう。

  • event gist: 登録した会話履歴から生成される個々のイベントメモリ
  • event: 複数のevent gistを「あるタイミング」で「まとめて」登録

なので、乱暴に言ってしまうと、登録した会話履歴内の個々のメッセージがevent gistで、それらを含むセッションがeventに概念的には近いような気がする。

となると、ここまでのサンプルコードでは、複数のメッセージからなる会話履歴を1回のinsert()で登録していたけども、これを別々にinsert()するとデータの持ち方などが変わってくるのではないか?という疑問が出てくる。

例えばこんな感じ

sample_messages = [
    ...
]

for m in sample_messages:
    u.insert(ChatBlob(messages=[m]))

u.flush()
print(u.context())
出力
---
# Memory
Unless the user has relevant queries, do not actively mention those memories in the conversation.
## User Current Profile:
- life_event::trip: user plans a 3-night, 4-day trip to Hokkaido with his girlfriend 美咲 [mention 2025/09/30, trip in 2025/10]
- life_event::dinner: user reserved a special dinner in Otaru for the anniversary [mention 2025/09/30, dinner in 2025/10/12]
- life_event::anniversary: user has a 2nd anniversary with his girlfriend 美咲 on October 12 [mention 2025/09/30, anniversary in 2025/10/12]
- schedule::morning_routine: user has a routine of waking up at 6:30 AM and walking for 20 minutes before work on workdays. [mention 2025/09/30]
- life_event::dog_adoption: user welcomed a Shiba Inu named "Mugi" in May 2024. [mention 2025/09/30, welcome dog in 2024/05]
- life_event::personal_achievement: user achieved a personal best of 49 minutes and 30 seconds for a 10km run in August 2025. [mention 2025/09/30, achieve in 2025/08]
- schedule::marathon_participation: user plans to participate in the Osaka Marathon in 2026 and aims for a half marathon next year. [mention 2025/09/30, plan in 2026]
- health::allergy: user has a shellfish allergy, particularly avoiding shrimp. [mention 2025/09/30]
- health::caffeine_intake: user avoids coffee after 6 PM to prevent sleep disturbances and drinks chamomile tea instead. [mention 2025/09/30]
- interest::sports_team: user is a fan of the Hanshin Tigers and went to Koshien with their father last month. [mention 2025/09/30, go to Koshien in 2025/08]
- life_event::parents_moved: user’s parents moved to Fukuoka in 2023. [mention 2025/09/30]
- work::company: fintech company
- work::work_skills: Go, Python
- contact_info::city: 西宮市
- life_event::job_start: 2025/04
- life_event::plan: going to horse racing on 2025/10/01 with sister 花子
- work::title: backend engineer
- basic_info::name: 太郎
- work::working_industry: fintech

## Past Events:
- user plans a 3-night, 4-day trip to Hokkaido with his girlfriend 美咲. [mention 2025/09/30, trip in 2025/10] // schedule
- user has a 2nd anniversary with his girlfriend 美咲 on October 12. [mention 2025/09/30, anniversary in 2025/10/12] // event
- user reserved a special dinner in Otaru for the anniversary. [mention 2025/09/30, dinner in 2025/10/12] // event
- user welcomed a Shiba Inu named "Mugi" in May 2024. [mention 2025/09/30, welcome dog in 2024/05] // event
- user has a routine of waking up at 6:30 AM and walking for 20 minutes before work on workdays. [mention 2025/09/30] // schedule
- user achieved a personal best of 49 minutes and 30 seconds for a 10km run in August 2025. [mention 2025/09/30, achieve in 2025/08] // event
- user plans to participate in the Osaka Marathon in 2026 and aims for a half marathon next year. [mention 2025/09/30, plan in 2026] // schedule
- user has a shellfish allergy, particularly avoiding shrimp. [mention 2025/09/30] // info
- user avoids coffee after 6 PM to prevent sleep disturbances and drinks chamomile tea instead. [mention 2025/09/30] // info
- user is a fan of the Hanshin Tigers and went to Koshien with their father last month. [mention 2025/09/30, go to Koshien in 2025/08] // event
---

タグを見てみる。

for event in u.event():
    print(event.event_data.event_tags)

タグされたイベントが増えている。

出力
[EventTag(tag='romance', value='trip to Hokkaido with girlfriend 美咲'), EventTag(tag='romance', value='2nd anniversary with girlfriend 美咲'), EventTag(tag='romance', value='special dinner in Otaru for anniversary')]
[]
[]

少なくともイベントは3つになっているのがわかる。

for event in u.event():
    print(event.event_data.event_tip)
出力
```
- user plans a 3-night, 4-day trip to Hokkaido with his girlfriend 美咲. [mention 2025/09/30, trip in 2025/10] // schedule
- user has a 2nd anniversary with his girlfriend 美咲 on October 12. [mention 2025/09/30, anniversary in 2025/10/12] // event
- user reserved a special dinner in Otaru for the anniversary. [mention 2025/09/30, dinner in 2025/10/12] // event
```
```
- user welcomed a Shiba Inu named "Mugi" in May 2024. [mention 2025/09/30, welcome dog in 2024/05] // event
- user has a routine of waking up at 6:30 AM and walking for 20 minutes before work on workdays. [mention 2025/09/30] // schedule
- user achieved a personal best of 49 minutes and 30 seconds for a 10km run in August 2025. [mention 2025/09/30, achieve in 2025/08] // event
- user plans to participate in the Osaka Marathon in 2026 and aims for a half marathon next year. [mention 2025/09/30, plan in 2026] // schedule
- user has a shellfish allergy, particularly avoiding shrimp. [mention 2025/09/30] // info
- user avoids coffee after 6 PM to prevent sleep disturbances and drinks chamomile tea instead. [mention 2025/09/30] // info
- user is a fan of the Hanshin Tigers and went to Koshien with their father last month. [mention 2025/09/30, go to Koshien in 2025/08] // event
- user’s parents moved to Fukuoka in 2023. [mention 2025/09/30] // info
```
```
- User's alias is 太郎. // info
- 太郎 is an engineer. // info
- 太郎 lives in 西宮市. // info
- 太郎 plans to go to the horse racing tomorrow. [mention 2025/09/30, plan in 2025/10/01] // schedule
- 太郎 is going to the horse racing with his sister, 花子. // event
- 太郎 started a new job in April 2025 at a fintech company in Osaka as a backend engineer. [mention 2025/09/30, start job in 2025/04] // info
- 太郎 primarily uses Go and Python in his work. // info
- 太郎 works remotely on Tuesdays and Thursdays, and goes to the Osaka office on other weekdays. // info
```

個々のメッセージごとに登録して、個々のメッセージからメモリを抽出するとなると、会話の連続性が失われるのでコンテキストが欠落するのではないか?という気がするのだけど、どうだろう?今回のサンプルだとそういったことは起きていないような気がする。

おそらくだけど、以下が絡んでいるのではなかろうか?

https://docs.memobase.io/practices/tips#flushing-the-memory-buffer

メモリバッファのフラッシュ

Memobaseはユーザー操作を収集するためにバッファを使用します。フラッシュ操作はこのバッファを処理し、長期記憶を更新します。フラッシュは次の場合に自動的に実行されます:

  • バッファが特定のサイズを超えたとき
  • バッファが設定期間アイドル状態だったとき

flush APIを使用して手動でトリガーすることも可能です。ユーザーセッションまたは会話の終了時にflush を呼び出すことがベストプラクティスです。

Memobaseに登録した会話履歴はすぐにメモリが生成されるわけではなく一旦バッファに溜まる。そして、上記の条件を満たした場合にメモリが生成される。手動でflush()メソッドを実行する以外のトリガーはapi/config.yamlだとおそらく以下。

  • buffer_flush_interval: バッファがアイドルになってからフラッシュされるまでのアイドル期間(だと思う)デフォルトは3600(1時間)。
  • max_chat_blob_buffer_token_size: バッファがフラッシュされるまでの最大トークンサイズ。デフォルトは1024トークン。

おそらくだけども、

  • 1回のinsert() でそれなりのボリュームを投げると、max_chat_blob_buffer_token_size をまあ超えるので、そのタイミングで同時にflush() も行われて、マルっと全てのデータを元にメモリが生成される。なので、イベントが1つになる場合がある。
  • 逆に、上のように各メッセージごとに細切れで投げていくと、max_chat_blob_buffer_token_sizeを超えたタイミングで都度都度flushが行われるので、そのタイミングでバッファに入っていたものからメモリが生成される。なので、イベントが複数に分かれる場合がある。

ってことなのではないだろうか?

自分の推測が正しければ、

  • サーバ側でflushされるタイミングもイベントの単位になんらか影響しているのかもしれない。
  • 細切れで送るたびに処理されるわけではないので、ある程度の会話の連続性は保持されそう。

と考えられるかな。

kun432kun432

概ね知りたいことは一通り試せたかな。

最初に試したときにも思ったことだけど、長期メモリの実装は概ね複雑になりがち、と思っていて、特に最近はナレッジベースを使った実装が増えていて、より複雑になりがちなのでは?という風に考えている。

Memobaseはあらかじめ定義しておいた項目について抽出・検索を行うというもので、ナレッジグラフに比べれば思想的には古い感もあるが、スタートラインの考え方としてはシンプルでわかりやすいと思う。

基本的にメモリを長期でもたせるということは、運用も長期で行うということになるし、そのためにはノウハウも保つ必要がある。このあたりは個人なりチームなりのスキル・学習レベルなどに応じて選択するのが良いのではないかと思う。Memobaseの考え方はシンプルなので、とりあえず長期メモリをまず始めてみる、というケースでは良いと思うし、物足りなくなったらナレッジベースなりに変えればいいかなと(それはそれで大変そうではあるが)

kun432kun432

個人的にはこれを自分の手で実装はしたくないなという感があって、OSSなプロジェクトなり、マネージドサービスを色々調べている。

マネージドならAgentCoreのように入出力だけ見てあとはお任せが良いかな。コストはちょっと高い気がしなくもないけど、運用コスト+(抽出時)LLMコストも含まれてると思うので、トータルで見るとそんな高くはない気もする。(セルフホストとか自前での運用コストはまあだいたい軽視されがちだしね)

https://aws.amazon.com/jp/bedrock/agentcore/pricing/

このスクラップは2日前にクローズされました