Closed11

LLMアプリにグラフベースのメモリレイヤーを追加する「Zep」を試す

kun432kun432

公式サイト。クラウドサービスとして提供しているっぽい。

https://www.getzep.com/

AIの基礎となるメモリレイヤー

平凡なものから途方もないものまで、タスクを完了するための知識をエージェントに与えます。

パーソナライズされた正確なAIエージェントを構築
ユーザーとビジネスデータから学習するメモリ

  • Zepにチャットメッセージとビジネスデータのストリームを送信
  • Zepはナレッジグラフ上でインテリジェントにデータを融合します。
  • 1回のAPIコールでエージェントに正確で適切なユーザ情報を提供します。

エージェントの時間的推論を可能に
事実の変化に応じて更新される記憶

  • Zepが新しい情報を取り込むと、古いファクトは無効としてマークされます。
  • Zepはグラフ上の時間的コンテキストを識別して保存
  • ファクトの履歴を保持することで、エージェントはユーザの状態が変化しても推論可能

なるほど、LLMアプリの会話履歴等から記憶すべき項目をナレッジグラフで管理する、というようなものらしい。

料金を見ると一応無料プランがある。

https://www.getzep.com/pricing

  • 1プロジェクト
  • エンドユーザー数無制限
  • 2,000メッセージ/月
  • データ取り込みに制限あり
  • レート制限あり
  • Discordコミュニティサポート

で、ドキュメントを見ると、Community Editionというのがあり、GitHubレポジトリがある。

https://help.getzep.com/ce/quickstart

GitHubレポジトリ

https://github.com/getzep/zep

見た感じセルフホストもできるようなので、少し試してみる。

kun432kun432

一応どんなものかをREADMEから確認

https://github.com/getzep/zep

Zep: AIスタックのための記憶基盤

AIエージェントを継続的に学習させ、パーソナライズされた体験を実現するためのZep

Zepとは?💬

Zepはユーザーのやり取りから継続的に学習することで、AIエージェントの知識を強化し、個別化された体験や精度向上を実現します。

Zepの仕組み

  1. 各ユーザーとのやり取りやエージェントのイベントごとに、チャットメッセージやデータアーティファクトをZepに追加します。
  2. Zepは新しい情報をユーザーの知識グラフにインテリジェントに統合し、必要に応じて既存のコンテキストを更新します。
  3. 次回のやり取りやイベントにおいて関連する事実をZepから取得します。

Zepの時間的知識グラフは、事実に関するコンテキスト情報を保持し、状態変化の推論やデータの出所に関する洞察を提供します。各事実には、valid_at(有効日)とinvalid_at(無効日)が含まれており、エージェントがユーザーの好みや特性、環境の変化を追跡することができます。

Zepの高速性

事実の取得はシンプルで非常に高速です。他のメモリソリューションとは異なり、Zepは事実が関連性を持つようにエージェントを使用することなく、事実やエンティティの要約、その他のアーティファクトを非同期で事前計算します。取得速度は主に埋め込みサービスのパフォーマンスに依存します。

Zepは多様なデータタイプをサポート

Zepには多種多様なデータアーティファクトを追加できます:

  • チャット履歴のメッセージ追加。
  • JSONや非構造化テキストの取り込み(近日公開予定)。
  • Zepはチャットセッション、ユーザー、そしてグループレベルのグラフをサポートしており、特にグループグラフは組織の知識をキャプチャするのに役立ちます。

コード以外のところで気になったところをピックアップ

Python、TypeScript、Go向けのシンプルなAPIとSD

チャット履歴を保持する操作は簡単かつ高速です。

Zepの高レベルAPIは、現在の会話に関連する事実を検索するために、BM25、セマンティック検索、グラフ検索を使用する意見の強いリトリーバルAPIを提供します。検索結果はユーザーのノードからの距離によって再ランキングされ、関連性がさらに向上します。

検索やCRUD用の低レベルAPIも利用可能です。

なぜZepは時間的知識グラフを使うのか?

知識グラフとは、互いに接続された事実のネットワークです。たとえば、「ケンドラはアディダスの靴が好き」という事実は、2つのエンティティ(「ケンドラ」「アディダスの靴」)と関係(「好き」)を持つ「トリプレット」として表現されます。

知識グラフは、エージェントの複雑な世界をモデル化する手段を提供し、RAG(Retrieval-Augmented Generation)で一般的に使用されるセマンティック検索以上に優れた情報検索アプローチを可能にします。知識グラフの多くの構築方法は、状態変化に対する推論が苦手であり、ユーザーが新しい情報を提供したり、ビジネスデータが変化したりすると、事実が時間とともに変わっていくことが避けられません。

一般的に、知識グラフを構築する手法は状態変化にうまく対応しませんが、ZepではGraphitiという独自の時間的知識グラフライブラリを活用し、この課題に対応しています。Graphitiは関係の変化を処理しながら、履歴情報を維持して知識グラフを自律的に構築します。

また、チャット履歴、JSONビジネスデータ、非構造化テキストの取り込みも可能です。

Zepは特定のフレームワークに依存していますか?

Zepはフレームワークに依存しません。LangChain、LangGraph、Chainlit、Microsoft Autogenなど、さまざまなフレームワークと共に利用できます。

Zep Community Editionとは? ⭐️

Zep Community EditionはオープンソースのZepの配布版です。Zep Cloudと共通のAPIを持ち、包括的なドキュメントが提供されています。

Zep Cloudとは? ⚡️

Zep Cloudは、Zep Community Editionを基盤としたマネージドサービスです。Zep Community Editionのメモリ層に加え、以下の機能を提供します:

  • 低遅延・スケーラビリティ・高可用性:Zep Cloudは、数百万のDAUを持つ顧客のニーズに応えるよう設計され、SOC II Type 2認証を受けています。低遅延のメモリ検索とグラフ構築を実現します。
  • ダイアログ分類:チャットダイアログを即座に正確に分類し、ユーザーの意図や感情を理解、セグメント分け、さらにはセマンティックコンテキストに基づくイベントトリガーも可能。
  • 構造化データの抽出:チャットからビジネスデータを迅速に抽出。アシスタントが次に尋ねるべき情報を理解し、タスク完遂をサポート。

なぜZepを長期記憶に使用するのか?

チャット履歴全体をプロンプトに含めるのでは不十分なのか?

LLMのコンテキスト長が増えるにつれて、チャット履歴全体やRAG結果をプロンプトに含めることが魅力的に思えるかもしれませんが、これは時間的推論力の低下、リコールの欠如、幻覚、低速・高コストの推論につながりかねません。

RedisやPostgresなどを使ってチャット履歴を保持しない理由は?

LLMに単にチャット履歴を提供するだけでは、時間的推論が十分にできないことが多いためです。

Zepはエージェントやアシスタントアプリに特化して設計

ユーザー、セッション、チャットメッセージがZepでは主要な抽象概念として扱われており、簡単で柔軟なメモリ管理が可能です。また、消去要求などのプライバシー対応も単一のAPI呼び出しで対応できます。

Zepの言語サポートとエコシステム

ZepはPython、TypeScript、Goをサポートしていますか?

はい、ZepはPythonとTypeScript/JS SDKを提供しており、アシスタントアプリに簡単に統合可能です。また、Zepと共に使用できる人気フレームワークの例も提供されています。

ZepはLangChain、LlamaIndex、Vercel AI、n8n、FlowWiseなどと使用できますか?

はい、Zepのチームおよびコミュニティの協力で、ZepのメモリコンポーネントをLangChainアプリに組み込むなど、さまざまなフレームワークとの統合が可能です。詳細はZepのドキュメントと、お気に入りのフレームワークのドキュメントを参照してください。

Zep Community Edition LLMサービスの依存関係

Zep Community Editionは外部のLLM APIサービスに依存しており、OpenAI互換のLLM APIをサポートしています。AnthropicなどのプロバイダーはLiteLLMのようなプロキシを介して利用できます。LiteLLMには埋め込みサービスの設定も必要です。

Zepはテレメトリを収集しますか?

Zepの使用状況を理解するため、テレメトリデータを収集することが可能です。これは任意であり、以下のようにzep.yamlの設定を変更することで無効化できます。

telemetry:
 disabled: false

個人を特定できる情報やデータは収集せず、オプションで設定するorg_nameを除き、Zepの利用方法について匿名化されたデータのみを収集しています。

以下は今となっては関係なさそうだが、元々はオープンソースエディションみたいなものになっていたのが変わったみたい。

Zep Community Edition(Zep CE)とZep Open Source v0.x(Zep OSS)の違い

Zep Open Sourceは古いバージョンで、メモリの保存とリコールに知識グラフを使用していませんでした。

その他の変更点:

  • Web UIの廃止とSDKの強化
    Zep OSSに存在したWeb UIは廃止され、Python、TypeScript、Go向けのSDKが大幅に強化されました。これにより、開発者は多様なプログラミング言語でZepを統合しやすくなりました。
  • ドキュメントコレクション機能の削除
    Zep CEではドキュメントコレクション機能が削除されています。代わりに、ホスト型またはローカルのベクターデータベースの利用が推奨されています。
  • 多様なLLMサービスのサポート
    Zep CEはOpenAI互換APIを提供する多様なLLMサービスやローカルサーバーをサポートしています。その他のサービスも、LLMプロキシを介して利用可能です。
  • ローカル埋め込みサービスと固有表現抽出器の非同梱化
    Zep CEでは、ローカルの埋め込みサービスや固有表現抽出器が含まれておらず、これによりユーザーは適切なサービスを選択できます。

Zep Open SourceからZep Community Editionへの移行パスはありますか?

Zepには大幅な変更が加えられており、残念ながらZep OSSからZep CEへの移行パスは用意されていません。

Zep OSSは引き続きコンテナリポジトリにて利用可能ですが、今後の機能追加やバグ修正の予定はありません。コードは、このリポジトリのlegacyブランチに残されています。

ライセンスはapache-2.0

kun432kun432

セットアップ

https://help.getzep.com/ce/quickstart

Community Edition の Quick Start Guideに従って進めてみる。色々見てみた感じ、前提として以下が必要になるっぽい。

  • PostgreSQL
  • Graphitiサーバ
    • バックエンドはNeo4J

構成的にはこんな感じになるっぽい

これらは全部Zepのレポジトリにあるdocker-compose.ce.yamlで定義されているので、個別に用意する必要はない。

今回はローカルのMac上のDockerでやる。ではレポジトリをクローン。

git clone https://github.com/getzep/zep && cd zep

zep.yamlファイルを修正する。まずデフォルトはこんな感じ(コメント部分は日本語に訳してある)

zep.yaml
log:
  # debug / info / warn / error / panic / dpanic / fatal から指定。デフォルトは "info"
  level: info
  # ログのフォーマット。"console"だと人間が読める形式のログフォーマットになり、
  # "json"だとJSON形式のログになる。デフォルトは"json"。
  format: json
http:
  # バインドするホスト。デフォルトは 0.0.0.0。
  host: 0.0.0.0
  # バインドするポート。デフォルトは8000。
  port: 8000
  max_request_size: 5242880
postgres:
  user: postgres
  password: postgres
  host: db
  port: 5432
  database: postgres
  schema_name: public
  read_timeout: 30
  write_timeout: 30
  max_open_connections: 10
# 時間を扱うパッケージ、Carbon(github.com/golang-module/carbon)の設定。
# 主に "2 hours ago" のような人間にとって読みやすい相対時間文字列を生成するために使われる。
# Carbonが対応している言語一覧は以下を参照:
# https://github.com/golang-module/carbon?tab=readme-ov-file#i18n
carbon:
  locale: en
graphiti:
   # graphiti サービスのベースURL
  service_url: http://graphiti:8003
# ZepサービスへのAPIリクエストを認証に使用するシークレットを設定(必須)
# シークレットはZepサービスとクライアントの間で厳重に管理する必要がある。
# シークレットは任意の文字列値を指定できる。
# Zepサービスにリクエストするときは、Authorizationヘッダに以下のキーでこのsecretを含める。
api_secret: 
# Zepがどのように使用されているかをよりよく理解するために、遠隔測定データを収集する。
# これはオプションであり、disabledをtrueに設定することで無効にできる。
# 当社はPIIやお客様のデータを収集することはない。Zepの使用方法に関する匿名化されたデータのみを収集する。
telemetry:
  disabled: false
  # 誰がZepを使用しているのかをより良く理解するために、組織名を記入する。
  organization_name:

以下のように設定した。

zep.yaml
log:
  level: info
  format: json
http:
  host: 0.0.0.0
  port: 8000
  max_request_size: 5242880
postgres:
  user: postgres
  password: postgres
  host: db
  port: 5432
  database: postgres
  schema_name: public
  read_timeout: 30
  write_timeout: 30
  max_open_connections: 10
carbon:
  locale: jp
graphiti:
  service_url: http://graphiti:8003
api_secret: zep_secret
telemetry:
  disabled: true
  organization_name:

あと、.envファイルは必須になっているので作成。OpenAI APIキーをセットしておく。

OPENAI_API_KEY="XXXXXXXXXX"

Zepサーバはzepコマンドを使って操作する。といっても、中身は単なるシェルスクリプトで、中でdocker composeしているだけ。

まずイメージをプル。

./zep pull
出力
[+] Pulling 32/24
 ✔ neo4j Pulled                                                                                                    2.0s
 ✔ graphiti Pulled                                                                                                34.2s
 ✔ db Pulled                                                                                                      35.8s
 ✔ zep Pulled                                                                                                     31.5s

起動

./zep up

起動しているコンテナを確認

docker ps -a --format "table {{.ID}}\t{{.Names}}\t{{.Ports}}"
出力
CONTAINER ID   NAMES               PORTS
56ad7dc94a94   zep-ce-zep-1        0.0.0.0:8000->8000/tcp
75bf17e3a91e   zep-ce-graphiti-1   0.0.0.0:8003->8003/tcp
f157108c6424   zep-ce-postgres     0.0.0.0:5432->5432/tcp
23c04b0e8454   zep-ce-neo4j-1      0.0.0.0:7474->7474/tcp, 7473/tcp, 0.0.0.0:7687->7687/tcp
kun432kun432

Zepへのアクセス(Python)

https://help.getzep.com/ce/sdks

ではクライアントからアクセスしてみる。JupyterLabの使い捨てコンテナを起動してそこからアクセスする。

mkdir zep-test && cd zep-test
docker run --rm \
    -p 8888:8888 \
    -u root \
    -e GRANT_SUDO=yes \
    -v .:/home/jovyan/work \
    quay.io/jupyter/minimal-notebook:latest

以降はJupyterLab上での作業。

ZepのPython SDKをインストール。

!pip install zep-python

ノートブックの場合はこれが必要

import nest_asyncio

nest_asyncio.apply()

クライアントの初期化。コンテナからのアクセスになるので、ホストはhost.docker.internalとなる。

import os
from zep_python.client import AsyncZep

API_KEY = "zep_secret"
BASE_URL = "http://host.docker.internal:8000"

zep = AsyncZep(
    api_key=API_KEY,
    base_url=BASE_URL
)

ユーザを追加

import uuid

user_id = uuid.uuid4().hex # ユーザ識別子

new_user = await zep.user.add(
    user_id=user_id,
    email="yamada.taro@example.com",
    first_name="太郎",
    last_name="山田",
    metadata={
        "gender": "male",
        "age": 30,
    },
)

new_user.dict()
出力
{'created_at': '2024-11-14T14:45:26.53665Z',
 'email': 'yamada.taro@example.com',
 'first_name': '太郎',
 'id': 1,
 'last_name': '山田',
 'metadata': {'age': 30, 'gender': 'male'},
 'project_uuid': '00000000-0000-0000-0000-000000000000',
 'updated_at': '2024-11-14T14:45:26.53665Z',
 'user_id': 'd82ad26e76da4571a4991f70cdba2366',
 'uuid': '3ebda8e2-d377-4248-b927-5829d35347d8',
 'deleted_at': None}

チャットセッションを作成

session_id = uuid.uuid4().hex # チャットセッション識別子
session = await zep.memory.add_session(
    session_id=session_id,
    user_id=user_id,
)

session.dict()
出力
{'created_at': '2024-11-14T14:48:34.850304Z',
 'id': 1,
 'project_uuid': '00000000-0000-0000-0000-000000000000',
 'session_id': '713bc5af07034fd9a1f533fd87d1201b',
 'updated_at': '2024-11-14T14:48:34.850304Z',
 'user_id': 'd82ad26e76da4571a4991f70cdba2366',
 'uuid': '25c62cc9-fec8-4108-9eee-8482a892cdfa',
 'deleted_at': None,
 'ended_at': None,
 'metadata': None}

セッションにメモリを追加

from zep_python.types import Message

await zep.memory.add(
    session_id=session_id,
    messages=[
        Message(
            role_type = "user", # ("system", "assistant", "user", "function", "tool") のどれか
            role = "Researcher", # オプション。ユーザーの役割を表すユースケース固有の文字列を指定
            content = "私の趣味は競馬なんですよ。今週末のマイルチャンピオンシップが楽しみです。", # メッセージの中身
        )
    ],
)
出力
SuccessResponse(message='OK')

メモリを読み出す。

memory = await client.memory.get(session_id=session_id)
messages = memory.messages # セッション内のメッセージのリスト (量はmemory.getのオプションlastnパラメータで指定)
relevant_facts = memory.relevant_facts # セッション内の最近のメッセージに関連する事実のリスト

中身を見てみる。まずmessages

for m in messages:
    print(m.role_type, ":", m.content)
出力
user : 私の趣味は競馬なんですよ。今週末のマイルチャンピオンシップが楽しみです。

ここは単なる会話履歴のリストである。

次に、relavant_facts

for fact in relevant_facts:
    print(fact.fact)
The researcher enjoys horse racing as their hobby.
The researcher is looking forward to the Mile Championship event this weekend.

こちらが会話のメッセージから取り出された事実となっている。

検索することもできる。

search_response = await zep.memory.search_sessions(
    user_id=user_id,
    search_scope="facts",
    text="趣味は何?",
)
facts = [r.fact for r in search_response.results]
for fact in facts:
    print(fact.fact)
The researcher enjoys horse racing as their hobby.
kun432kun432

実際にOpenAIクライアントと組み合わせた例は以下のZep Cloudのドキュメントにある。SDKの使い方という点については、Community Editionとの違いはそれほどないのではないだろうか?と思うので、参考になりそう。

https://help.getzep.com/quickstart

Community Editionとクラウドの違いは以下にある。

https://help.getzep.com/ce/ce-vs-cloud

機能 Community Edition Cloud
グラフベースのメモリ Yes Yes
Python、TypeScript、Go向けSDK Yes Yes
Web UI No Yes
複数プロジェクト No Yes
ユーザーグループ向けメモリ No Yes
グラフCRUD操作 No Yes
JSONおよびテキストデータの取り込み No Yes
コミュニティおよびノードのサマリ No Yes
低遅延グラフ構築 No Yes
ダイアログ分類 No Yes
構造化出力 No Yes
ライセンス Apache 2.0 Proprietary

なるほど、クラウドのほうが色々便利そうではある。

kun432kun432

Neo4jを見てみるとこんな感じ。

試してるときにちょっとミスったので、複数エントリになってしまったのかもしれないが、会話のメッセージからグラフが作成されているのがわかる。

kun432kun432

実際にOpenAIクライアントと組み合わせた例は以下のZep Cloudのドキュメントにある。SDKの使い方という点については、Community Editionとの違いはそれほどないのではないだろうか?と思うので、参考になりそう。

ということでやってみる。

OpenAIパッケージをインストール

!pip install openai

OpenAI APIキーをセット

import getpass
import os

os.environ["OPENAI_API_KEY"] = getpass.getpass('OPENAI_API_KEY')

notebookの場合はこれが必要

import nest_asyncio

nest_asyncio.apply()

まずユーザを定義してメモリを作成

from zep_python.client import AsyncZep
import uuid

zep = AsyncZep(
    api_key="zep_secret",
    base_url="http://host.docker.internal:8000",
)

# ユーザ情報
bot_name = "AI"
user_lastname = "山田"
user_firstname = "太郎"
user_name= "taro.yamada"
user_id = user_name + "_" + str(uuid.uuid4())[:4]

await zep.user.add(
    user_id=user_id,
    email=f"{user_name}@example.com",
    first_name=user_firstname,
    last_name=user_lastname,
)

print(f"ユーザ名: {user_name}")
print(f"メールアドレス: {user_name}@example.com")
print(f"ユーザID: {user_id}")
出力
ユーザ名: taro.yamada
メールアドレス: taro.yamada@example.com
ユーザID: taro.yamada_c81b

これでまずノードが作成される。

でチャット。

from typing import List
from openai import AsyncOpenAI
from zep_python.types import Message

client = AsyncOpenAI()


async def get_response(messages: List[dict], model: str = "gpt-4o-mini") -> str:
    try:
        response = await client.chat.completions.create(
            messages=messages,
            model=model
        )
        return response.choices[0].message.content
    except Exception as e:
        return f"エラーが発生しました: {str(e)}"


async def main() -> None:
    print("チャットアプリへようこそ!")
    print("終了するには 'quit' と入力してください。")

    session_id = str(uuid.uuid4())

    print("\n==== セッション情報 ====")
    print(f"ユーザ名: {user_name}")
    print(f"メールアドレス: {user_name}@example.com")
    print(f"ユーザID: {user_id}")
    print(f"セッションID: {session_id}")
    print("====================\n")

    # 会話セッション初期化
    await zep.memory.add_session(
        user_id=user_id,
        session_id=session_id,
    )

    while True:
        user_input = input("User: ")
        if user_input.lower() == 'quit':
            print("Assistant: チャットを終了します。さようなら!")
            break

        # メモリを読み込み
        memory = await zep.memory.get(session_id=session_id)
        memory_str = "\n".join([r.fact for r in memory.relevant_facts[:10]])

        # システムプロンプトにメモリを埋め込む
        system_message = (
            "あなたは親切な日本語のアシスタントです。\n"
            "以下はユーザに関する関連情報です:\n"
            f"{memory_str}"
        )
        
        messages = [
            {
                "role": "system",
                "content": system_message,
            },
            {
                "role": "user",
                "content": user_input,
            },
        ]
        
        assistant_response = await get_response(messages)

        # ユーザの入力とLLMのレスポンスをメモリに追加
        await zep.memory.add(
            session_id=session_id,
            messages=[
                Message(role_type="user", role=user_name, content=user_input)
                Message(role_type="assistant", role=bot_name, content=assistant_response)
            ]
        )
        
        print(f"Assistant: {assistant_response}")

        # 確認用にメモリを再度読み込んで表示
        memory = await zep.memory.get(session_id=session_id)
        memory_str = "\n".join([r.fact for r in memory.relevant_facts[:10]])

        print("\n---- ユーザ関連情報 ----")
        print(memory_str)
        print("-----------------------\n")


asyncio.run(main())

とりあえずわかりやすいように会話履歴は一切保持せずに、Zepから過去の記憶だけを読み出すようにしてみた。もう少しメモリの使い分けをちゃんとしたほうがいいとは思うけど、お試しなので。

実際のチャットはこんな感じになる。

出力
チャットアプリへようこそ!
終了するには 'quit' と入力してください。

==== セッション情報 ====
ユーザ名: taro.yamada
メールアドレス: taro.yamada@example.com
ユーザID: taro.yamada_c81b
セッションID: 03b378a0-ec8f-4a1e-80bc-65aa4c2a74fe
====================

User:  今週末が楽しみ!
Assistant: 楽しみですね!何か特別な予定があるのでしょうか?イベントやアクティビティについてお話ししてくれると嬉しいです。

---- ユーザ関連情報 ----

-----------------------

User:  競馬だよー
Assistant: 競馬についてお話ししましょう!競馬は日本を含む多くの国で人気のあるスポーツですね。レースの種類や馬の育成、騎手の技術など、様々な要素が関わります。どのようなことに興味がありますか?レースの予想や馬の情報、歴史などについてお答えできますよ!

---- ユーザ関連情報 ----

-----------------------

User:  今週末はマイルチャンピオンシップなんだよ
Assistant: そうですね、マイルチャンピオンシップは非常に楽しみなレースです!競馬の中でも特に注目されるレースの一つですよね。出走馬や予想、過去の成績など、何か特に気になることがありますか?

---- ユーザ関連情報 ----
The AI assistant is asking taro.yamada about any special plans or activities for the weekend.
taro.yamada is excited about the end of the week and is engaging with the AI assistant.
-----------------------

User:  やっぱりナミュールかな、去年の勝ち馬だしね。
Assistant: ナミュールですね!昨年の勝ち馬で期待が高まりますね。競馬の調子はいかがですか?他にも注目している馬はいますか?

---- ユーザ関連情報 ----
The AI assistant is asking taro.yamada about any special plans or activities for the weekend.
taro.yamada is excited about the end of the week and is engaging with the AI assistant.
-----------------------

User:  あとはオオバンブルマイかな~、武豊ジョッキー騎乗だしね
Assistant: オオバンブルマイですね!武豊ジョッキーが騎乗するとなると、さらに期待が高まりますね。彼の騎乗技術は素晴らしいですから、いいレースが期待できそうです。今週末のレースでの活躍が楽しみですね!他にも注目している馬はいますか?

---- ユーザ関連情報 ----
The AI assistant is asking taro.yamada about any special plans or activities for the weekend.
User taro.yamada expresses excitement about horse racing.
taro.yamada is excited about the end of the week and is engaging with the AI assistant.
-----------------------

User:  セリフォスにも注目してるよ、こちらは一昨年の勝ち馬だしね。
Assistant: セリフォスに注目しているんですね!一昨年の勝ち馬ということで、実力がある馬ですね。今週末のレースでは、どの馬が勝つか楽しみですね!他にも気になっている馬やレースはありますか?

---- ユーザ関連情報 ----
The AI assistant is asking taro.yamada about any special plans or activities for the weekend.
User taro.yamada expresses excitement about horse racing.
taro.yamada is excited about the end of the week and is engaging with the AI assistant.
User taro.yamada mentions the Mile Championship event happening this weekend.
Horse racing involves jockeys who ride the horses during races.
-----------------------

こんな感じで、一応会話としてはつながっている感じ。

少しの会話でもこんな感じでグラフが作成されているのがわかる。

kun432kun432

まとめ

用途としては以前とりあげたmem0と同じもの。

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

mem0もナレッジグラフに対応しているはずなんだけど前回やったときはうまく動かせなかった。個人的な意見としては、ナレッジグラフはRAGのコンテキストで使うよりもメモリで使うほうが使いやすいと思っているので、Zepでちゃんと動かせて満足。mem0も今なら動くのかもしれない。

とりあえずZepもmem0も、ライブラリとしてみるとそれほど大きな違う感はないのだけど、こちらのほうがやや主要フレームワークとのインテグレーションは多いかなぁ、というところ。mem0はChrome拡張があるのが良い。ライブラリは開発者しか見ないと思うけど、ブラウザ拡張ならユーザとしても使いたくなるところはある。

ドキュメントはどちらも揃ってて良い。あえて不満を述べるならば、Zepのドキュメントのコードブロックは表示領域が狭くていちいちスクロールしないと見づらいところかな。

このスクラップは1ヶ月前にクローズされました