diff --git a/assets/cli_demo.py b/assets/cli_demo.py index e1f94ed..ed65e91 100644 --- a/assets/cli_demo.py +++ b/assets/cli_demo.py @@ -27,19 +27,27 @@ 4. **Avoid Repetition**: Provide new information or in-depth content, avoiding repetition of what the user already knows. 5. **Flexible Adjustment**: Dynamically adjust your response style based on user feedback and the context of the conversation.''' +SYS_PROMPT_JA = '''あなたは、パーソナライズされた、親しみやすく、役立つサービスを提供することを目的とした知的なアシスタントです。以下の原則を遵守してください: +1. **パーソナライゼーション**:ユーザーの興味や背景(例: ${user_interests})に基づいて提案を行ってください。ただし、プライバシー侵害は避けてください。 +2. **友好的な対話**: 忍耐強く温かい口調を保ち、ユーザーが尊重されサポートされていると感じられるようにしてください。 +3. **関連性のある提案**: 適切なタイミングで、ユーザーの興味に関連する有益な提案や話題(例: 食物、旅行)を共有してください。 +4. **繰り返しの回避**:ユーザーが既に知っている情報の繰り返しを避け、新しい情報や深い内容を提供してください。 +5. **柔軟な調整**:ユーザーのフィードバックや会話の文脈に基づき、応答スタイルを動的に調整してください。''' + _WELCOME_MSG = """Welcome to Memobase, a user profile-based memory system. Type text to chat, :h for help. -(欢迎使用 Memobase,基于用户档案的记忆系统。输入内容开始对话,:h 获取帮助。)""" +(欢迎使用 Memobase,基于用户档案的记忆系统。输入内容开始对话,:h 获取帮助。) +(ユーザープロファイルベースのメモリシステム、Memobaseへようこそ。テキストを入力してチャット、:h でヘルプを表示。)""" _HELP_MSG = """\ Commands: - :help / :h Show this help message 显示帮助信息 - :exit / :quit / :q Exit the demo 退出Demo - :clear / :cl Clear screen 清屏 - :clear-history / :clh Clear history 清除对话历史 - :history / :his Show history 显示对话历史 - :user Show user id 显示用户ID - :user Set user id 设置用户ID - :profile / :pf Show user profile 显示用户已有的配置信息 - :flush / :fl Flush buffer 刷新缓冲区 + :help / :h Show this help message 显示帮助信息 ヘルプを表示 + :exit / :quit / :q Exit the demo 退出Demo デモを終了 + :clear / :cl Clear screen 清屏 ターミナルをクリア + :clear-history / :clh Clear history 清除对话历史 ヒストリーを表示 + :history / :his Show history 显示对话历史 ヒストリーを表示 + :user Show user id 显示用户ID ユーザIDを表示 + :user Set user id 设置用户ID ユーザIDを設定 + :profile / :pf Show user profile 显示用户已有的配置信息 ユーザのプロファイルを表示 + :flush / :fl Flush buffer 刷新缓冲区 バッファをフラッシュ """ _ALL_COMMAND_NAMES = [ "help", @@ -107,6 +115,8 @@ def _get_args(): print("You should set OPENAI_API_KEY environment variable or pass --openai-api-key argument.") if args.language == 'zh': args.sys_prompt = SYS_PROMPT_ZH + elif args.language == 'ja': + args.sys_prompt = SYS_PROMPT_JA else: args.sys_prompt = SYS_PROMPT_EN return args @@ -232,7 +242,7 @@ def _launch_demo(args): base_url=args.openai_base_url, ) mb_client = MemoBaseClient( - args.memobase_endpoint, + project_url=args.memobase_endpoint, api_key=args.memobase_token ) client = openai_memory(client, mb_client) diff --git a/docs/site/references/local_config.mdx b/docs/site/references/local_config.mdx index 60d4a3a..75cb4b8 100644 --- a/docs/site/references/local_config.mdx +++ b/docs/site/references/local_config.mdx @@ -62,7 +62,7 @@ minimum_chats_token_size_for_event_summary: 256 - `use_timezone`: string, default to `null`. Options include `"UTC"`, `"America/New_York"`, `"Europe/London"`, `"Asia/Tokyo"`, and `"Asia/Shanghai"`. If not set, the system's local timezone is used. ### LLM Configuration -- `language`: string, default to `"en"`, available options `{"en", "zh"}`. The prompt language of Memobase. +- `language`: string, default to `"en"`, available options `{"en", "zh", "ja"}`. The prompt language of Memobase. - `llm_style`: string, default to `"openai"`, available options `{"openai", "doubao_cache"}`. The LLM provider style. - `llm_base_url`: string, default to `null`. The base URL of any OpenAI-Compatible API. - `llm_api_key`: string, required. Your LLM API key. diff --git a/src/server/api/memobase_server/controllers/modal/chat/types.py b/src/server/api/memobase_server/controllers/modal/chat/types.py index 0e306ac..275d0c2 100644 --- a/src/server/api/memobase_server/controllers/modal/chat/types.py +++ b/src/server/api/memobase_server/controllers/modal/chat/types.py @@ -11,6 +11,11 @@ zh_merge_profile, zh_summary_entry_chats, zh_merge_profile_yolo, + ja_user_profile_topics, + ja_extract_profile, + ja_merge_profile, + ja_summary_entry_chats, + ja_merge_profile_yolo, ) from ....models.response import ProfileData @@ -52,4 +57,12 @@ "merge_yolo": zh_merge_profile_yolo, "organize": organize_profile, }, + "ja": { + "entry_summary": ja_summary_entry_chats, + "profile": ja_user_profile_topics, + "extract": ja_extract_profile, + "merge": ja_merge_profile, + "merge_yolo": ja_merge_profile_yolo, + "organize": organize_profile, + }, } diff --git a/src/server/api/memobase_server/controllers/modal/roleplay/types.py b/src/server/api/memobase_server/controllers/modal/roleplay/types.py index 7205d68..fdfb4eb 100644 --- a/src/server/api/memobase_server/controllers/modal/roleplay/types.py +++ b/src/server/api/memobase_server/controllers/modal/roleplay/types.py @@ -13,4 +13,5 @@ "detect_interest": zh_detect_interest, "infer_plot": zh_infer_plot, }, + "ja": {}, } diff --git a/src/server/api/memobase_server/env.py b/src/server/api/memobase_server/env.py index 2e57e4c..9cceda6 100644 --- a/src/server/api/memobase_server/env.py +++ b/src/server/api/memobase_server/env.py @@ -91,7 +91,7 @@ class Config: cache_user_profiles_ttl: int = 60 * 20 # 20 minutes # LLM - language: Literal["en", "zh"] = "en" + language: Literal["en", "zh", "ja"] = "en" llm_style: Literal["openai", "doubao_cache"] = "openai" llm_base_url: str = None llm_api_key: str = None @@ -227,7 +227,7 @@ def timezone(self) -> timezone: @dataclass class ProfileConfig: - language: Literal["en", "zh"] = None + language: Literal["en", "zh", "ja"] = None profile_strict_mode: bool | None = None profile_validate_mode: bool | None = None additional_user_profiles: list[dict] = field(default_factory=list) @@ -237,7 +237,7 @@ class ProfileConfig: event_tags: list[dict] = None def __post_init__(self): - if self.language not in ["en", "zh"]: + if self.language not in ["en", "zh", "ja"]: self.language = None if self.additional_user_profiles: [UserProfileTopic(**up) for up in self.additional_user_profiles] diff --git a/src/server/api/memobase_server/prompts/__init__.py b/src/server/api/memobase_server/prompts/__init__.py index db36553..59274e7 100644 --- a/src/server/api/memobase_server/prompts/__init__.py +++ b/src/server/api/memobase_server/prompts/__init__.py @@ -1,8 +1,10 @@ from . import ( extract_profile, zh_extract_profile, + ja_extract_profile, merge_profile, zh_merge_profile, + ja_merge_profile, organize_profile, summary_profile, ) diff --git a/src/server/api/memobase_server/prompts/chat_context_pack.py b/src/server/api/memobase_server/prompts/chat_context_pack.py index 9e116cc..0926de1 100644 --- a/src/server/api/memobase_server/prompts/chat_context_pack.py +++ b/src/server/api/memobase_server/prompts/chat_context_pack.py @@ -29,7 +29,21 @@ def zh_context_prompt(profile_section: str, event_section: str) -> str: """ +def ja_context_prompt(profile_section: str, event_section: str) -> str: + return f"""--- +# メモリ +ユーザーから関連する質問がない限り、これらのメモリについて会話の中で積極的に言及しないでください。 +## ユーザーの現在のプロフィール: +{profile_section} + +## 過去のイベント: +{event_section} +--- +""" + + CONTEXT_PROMPT_PACK: dict[str, ContextPromptFunc] = { "en": en_context_prompt, "zh": zh_context_prompt, + "ja": ja_context_prompt, } diff --git a/src/server/api/memobase_server/prompts/ja_extract_profile.py b/src/server/api/memobase_server/prompts/ja_extract_profile.py new file mode 100644 index 0000000..d48ae6c --- /dev/null +++ b/src/server/api/memobase_server/prompts/ja_extract_profile.py @@ -0,0 +1,163 @@ +from . import ja_user_profile_topics +from ..models.response import AIUserProfiles +from ..env import CONFIG, LOG +from .utils import pack_profiles_into_string + +ADD_KWARGS = { + "prompt_id": "ja_extract_profile", +} + +EXAMPLES = [ + ( + """- ユーザーがアシスタントに挨拶した。 +""", + AIUserProfiles(**{"facts": []}), + ), + ( + """ +- ユーザーの好きな映画は『インセプション』と『インターステラー』 [2025/01/01に言及] +- ユーザーの一番好きな映画は『TENET テネット』 [2025/01/02に言及] +""", + AIUserProfiles( + **{ + "facts": [ + { + "topic": "興味", + "sub_topic": "映画", + "memo": "『インセプション』、『インターステラー』[2025/01/01に言及];一番好きな映画は『TENET テネット』[2025/01/02に言及]", + }, + { + "topic": "興味", + "sub_topic": "映画監督", + "memo": "ユーザーはクリスチャン・ノーラン監督の大ファンだと思われる", + }, + ] + } + ), + ), +] + +DEFAULT_JOB = """あなたはプロの心理学者です。 +あなたの仕事は、ユーザーのメモを詳細に読み、構造化された形式でユーザーの重要なプロフィールを抽出することです。 +次に、関連する重要な事実、ユーザーの好みを抽出します。これらの情報は、ユーザーの状態を評価するのに役立ちます。 +あなたは、明示された情報を抽出するだけでなく、会話中に暗示されている情報も推測する必要があります。 +これらの情報を記録する際は、ユーザーが入力した言語と同じ言語を使用してください。 +""" + +FACT_RETRIEVAL_PROMPT = """{system_prompt} + +## フォーマット +### 入力 +#### トピックガイドライン +あなたには、収集・抽出に集中すべき、ユーザーに関連するトピックとサブトピックの一覧が与えられます。 +ユーザーに関連しないトピックについては、情報の混乱を招く可能性があるため、収集しないでください。 +例えば、メモに他人の職位が言及されている場合、"仕事{tab}職位"というトピックを生成しないでください。 +必要なら、新しいトピック/サブトピックを作成しても構いません。ただしユーザーが禁止していない場合に限ります。 + +#### 既存のトピック +あなたには、ユーザーとアシスタント間ですでに共有されている、トピックとサブトピックの一覧が与えられます。 +会話の中でそれらが再度言及された場合は、同じものを使うことを検討してください。 + +#### メモ +あなたには、ユーザーに関する情報・出来事・好みなどを含む、Markdown形式のメモが与えられます。 +このメモは、ユーザーとアシスタントの会話から要約されたものです。 + +### 出力 +#### 思考 +あなたは、メモにはどのようなトピックやサブトピックが言及されているか?また、そのメモからどのような示唆を推測できるか?を考える必要があります。 +#### プロフィール + +あなたの思考プロセスの後、あなたは、メモから事実と好みを抽出し、順序付きリストに整理する必要があります: +- TOPIC{tab}SUB_TOPIC{tab}MEMO +例: +- 基本情報{tab}氏名{tab}メリンダ +- 仕事{tab}役職{tab}ソフトウェアエンジニア + +各行は1つの事実または好みを表し、以下を含みます: +1. TOPIC: 大分類(例: 基本情報、仕事、興味 など) +2. SUB_TOPIC: 詳細分類(例: 氏名、役職、映画 など) +3. MEMO: 「ユーザー」に関する抽出結果。メモに時間情報が含まれる場合は[...]で表記。 +これらは `{tab}` で区切り、各行は "- " で始めて`\n`で改行区切りにしてください。 + +最終出力テンプレート: +``` +POSSIBLE TOPICS THINKING +--- +- TOPIC{tab}SUB_TOPIC{tab}MEMO +- ... +``` + +## 抽出例 +いくつかの例を示します: +{examples} +上記のMarkdown形式のリストフォーマットで事実と好みを返してください。 +実際の値を持つ属性のみを抽出してください。ユーザーが値を提供しない場合は抽出しないでください。 +あなたは、まず最初に思考して、そして、メモから事実と好みを抽出する必要があります。 + +#### トピック指針 +以下はあなたが収集・抽出に集中すべきトピックとサブトピックの一覧です: +{topic_examples} + +以下を念頭に置いてください: +- もしユーザが日時に関連する情報を発言している場合は、データから具体的な日付を推定してください。 +- 日付は可能な限り具体的な日付を使用してください。「今日」「昨日」のような相対表現は使用しないでください。 +- 以下の会話に関連する情報が見つからなければ、空のリストを返しても構いません。 +- フォーマットと抽出例セクションに記載されている形式でレスポンスを返すことを厳守してください。 +- 明示的に発言された内容だけでなく、会話から暗示されることを推測してください。 +- 同一トピック/サブトピックに属する内容は1要素にまとめ、重複を避けてください。 +- メモに含まれる日時は2つの種類があります。1つはメモが「言及された日時」、もう1つはメモ内の「イベントが発生した日時」です。どちらも重要です。混同しないようにしてください。日時情報を正確に抽出し、関連するメモの後に時間表記を[...]を使用して記述する必要があります。 +- 実際の値を持つ属性のみを抽出してください。ユーザーが値を提供していない場合は、抽出しないでください。 + +では、あなたのタスクを遂行してください。 +以下はユーザーとアシスタントの会話です。会話から関連する事実と好みを抽出・推測して、上記のリスト形式で返してください。 +ユーザーが入力した言語を検出し、同じ言語で記録してください。 +""" + + +def pack_input(already_input, chat_strs, strict_mode: bool = False): + header = "" + if strict_mode: + header = "#### トピックガイドラインに記載されていないトピック/サブトピックを抽出しないでください。そうしないと、あなたの回答は無効になります!" + return f"""{header} +#### 既存のトピック +関連するトピック/サブトピックを抽出する場合は、以下のトピック/サブトピックの命名規則を検討してください: +{already_input} + +#### メモ +メモに記載されていないトピック/サブトピックに関する情報は一切出力しないでください。 +{chat_strs} +""" + + +def get_default_profiles() -> str: + return ja_user_profile_topics.get_prompt() + + +def get_prompt(topic_examples: str) -> str: + sys_prompt = CONFIG.system_prompt or DEFAULT_JOB + examples = "\n\n".join( + [ + f""" +{p[0]} + +{pack_profiles_into_string(p[1])} + + +""" + for p in EXAMPLES + ] + ) + return FACT_RETRIEVAL_PROMPT.format( + system_prompt=sys_prompt, + examples=examples, + tab=CONFIG.llm_tab_separator, + topic_examples=topic_examples, + ) + + +def get_kwargs() -> dict: + return ADD_KWARGS + + +if __name__ == "__main__": + print(get_prompt(get_default_profiles())) diff --git a/src/server/api/memobase_server/prompts/ja_merge_profile.py b/src/server/api/memobase_server/prompts/ja_merge_profile.py new file mode 100644 index 0000000..d6214ab --- /dev/null +++ b/src/server/api/memobase_server/prompts/ja_merge_profile.py @@ -0,0 +1,105 @@ +from datetime import datetime +from .utils import pack_merge_action_into_string +from ..env import CONFIG + +ADD_KWARGS = {"prompt_id": "ja_merge_profile"} + +MERGE_FACTS_PROMPT = """ +あなたは、ユーザーのメモをメンテナンスする責務を負っています。 +あなたの仕事は、新しい補足情報を現在のメモとどのように統合するか?を判断することです。 +あなたは、新しい補足情報を直接追加すべきか?新しい情報で既存の情報を更新すべきか?、または統合を放棄すべきか?を判断する必要があります。 +ユーザーは、メモのトピック/サブトピックを提供します。またトピックの説明や、特定の更新要件も提供する可能性があります。 + +あなたの出力アクションは以下の通りです: +1. 直接追加: 補足情報が新たな情報を提供する場合は、直接追加してください。現在のメモが空の場合、補足情報を直接追加してください。 +2. メモの更新: 補足情報が現在のメモと矛盾する場合、または現在の情報をより適切に反映するために現在のメモを修正する必要がある場合は、メモを更新してください。 +3. 統合の放棄: 補足情報に価値がない場合、情報が現在のメモで完全に網羅されている場合、または現在のメモのコンテンツ要件を満たさない場合は、統合を放棄してください。 + +## 思考 +出力アクションを行う前に、以下について思考してください: +1. 補足情報がメモのトピック説明を満たしているか? + 1.1. 要件を満たしていない場合は、補足情報を修正してメモの要件を満たす内容にできるかどうかを判断し、修正した補足情報を処理してください + 1.2. 補足情報を修正できない場合は、統合を破棄してください。 +3. 補足情報が現在のメモの要件を満たしている場合は、上記の説明に従って出力アクションを判断してください +4. メモの更新を選択した場合、現在のメモに簡略化または削除できる部分がないかも同時に検討してください。 + +追加事項: +1. 現在のメモが空の場合、思考ステップ1を行い、補足情報が要件を満たしていれば、直接追加してください。 +2. 更新要件が空でない場合、ユーザーの更新要件を参照した上で、思考してください。 + +## 出力アクション +### 直接追加 +``` +- APPEND{tab}APPEND +``` +直接追加を選択した場合、内容を繰り返さずに`APPEND`ワードを直接出力してください。 +### メモの更新 +``` +- UPDATE{tab}[UPDATED_MEMO] +``` +メモの更新を選択した場合、メモを更新して`[UPDATED_MEMO]`の中に書き直してください。 +### 統合の放棄 +``` +- ABORT{tab}ABORT +``` +統合の放棄を選択した場合、内容を繰り返さずに`ABORT`ワードを直接出力してください。 + +## 出力テンプレート +上記の指示に従って、以下のフォーマットで出力してください: + +THOUGHT +--- +ACTION + +上記のうち: +- `THOUGHT` はあなたの思考プロセスです +- `ACTION` はあなたの出力アクションです +例: +```example +補足情報には、ユーザーの現在の学習目標が期末試験の準備であることが記載されており、現在のトピック説明はユーザーの学習目標を記録しているため、これは要件を満たしています。現在のトピック説明はユーザーの学習目標を記録しているため、これは要件を満たしています。同時に、現在のメモには中間試験の準備に関する記録もあり、これは中間試験が既に終了していることを示唆しています。したがって、補足情報を単純に追加するのではなく、現在のメモを更新する必要があります。 +メモの他の部分は保持したまま、該当箇所のみを更新する必要があります。 +--- +- UPDATE{tab}...現在Duolingoで日本語を独学中、日本語能力試験2級合格を目指している[2025/05/05に言及]; 期末試験の準備中[2025/06/01に言及]; +``` + +以下の指示に従ってください: +- 正しい出力形式を厳守してください。 +- 最終的なメモは5文を超えないようにしてください。常に簡潔にまとめ、メモの要点を出力してください。 +- 入力で言及されていない内容を捏造しないでください。 +- 旧メモと新メモの両方の日時アノテーションを保持してください。(例: XXX[2025/05/05に言及、2022年に発生])。 +- 更新する場合は、最終メモが簡潔であり、冗長な情報を含んでいないことを確認してください(例: 「ユーザーは悲しい;ユーザーの気分は悲しい」==「ユーザーは悲しい」)。 + +以上が全ての内容です。では、作業を実行してください。 +""" + + +def get_input( + topic, subtopic, old_memo, new_memo, update_instruction=None, topic_description=None +): + today = datetime.now().astimezone(CONFIG.timezone).strftime("%Y-%m-%d") + return f"""今日は {today} です。 +## メモの更新指示 +{update_instruction or "[empty]"} +### メモのトピックの説明 +{topic_description or "[empty]"} +## メモのトピック +{topic}, {subtopic} +## 現在のメモ +{old_memo or "[empty]"} +## 補足情報 +{new_memo} +""" + + +def get_prompt() -> str: + return MERGE_FACTS_PROMPT.format( + tab=CONFIG.llm_tab_separator, + ) + + +def get_kwargs() -> dict: + return ADD_KWARGS + + +if __name__ == "__main__": + print(get_prompt()) diff --git a/src/server/api/memobase_server/prompts/ja_merge_profile_yolo.py b/src/server/api/memobase_server/prompts/ja_merge_profile_yolo.py new file mode 100644 index 0000000..11439ae --- /dev/null +++ b/src/server/api/memobase_server/prompts/ja_merge_profile_yolo.py @@ -0,0 +1,145 @@ +from .utils import pack_merge_action_into_string +from ..env import CONFIG + +ADD_KWARGS = {"prompt_id": "ja_merge_profile_yolo"} + +MERGE_FACTS_PROMPT = """あなたは、ユーザーのメモをメンテナンスする責務を負っています。 +あなたの仕事は、新しい補足情報を現在のメモとどのように統合するか?を判断することです。 +ユーザーは一連のメモトピック/サブトピック、トピックの説明、および具体的な更新要件(空の場合あり)を提供します。 +各補足情報について、新しい情報を直接追加するか、現在の対応するメモを更新するか、または破棄するかを判断する必要があります。 + +## 入力フォーマット +``` +{{ + "memo_id": "1", + "new_info": "", + "current_memo": "", + "topic": "", + "subtopic": "", + "topic_description": "", + "update_instruction": "", +}} +{{ + "memo_id": "2", + ... +}} +... +``` +各メモを評価する際には、それがトピックと更新内容の説明に沿っているかどうかを考慮する必要があります。 + +あなたの出力アクションは以下の通りです: +1. 直接追加: 補足情報が新たな知見を提供する場合は、直接追加してください。現在のメモが空の場合、補足情報を直接追加してください。 +2. メモの更新: 補足情報が現在のメモと矛盾する場合、または現在の情報をより適切に反映するために現在のメモを修正する必要がある場合は、メモを更新してください。 +3. 統合の放棄: 補足情報に価値がない場合、情報が現在のメモで完全に網羅されている場合、または現在のメモのコンテンツ要件を満たさない場合は、統合を放棄してください。 + +## 思考 +出力アクションを行う前に、以下について思考してください: +1. 補足情報がメモのトピック説明に沿っているか? + 1.1. 要件に沿っている場合は、補足情報を修正してメモの要件を満たす内容にできるかどうかを判断し、修正した補足情報を処理してください + 1.2. トピックの説明を満たすように補足情報を修正できない場合は、統合を破棄してください。 +3. 補足情報が現在のメモの要件を満たしている場合は、上記の説明に従って出力アクションを判断してください +4. メモの更新を選択した場合、現在のメモに簡略化または削除できる部分がないかも同時に検討してください。 + +追加事項: +1. 現在のメモが空の場合、思考ステップ1を行い、補足情報が要件を満たしていれば、直接追加してください。 +2. 更新要件が空でない場合、ユーザーの更新要件を参照した上で、思考してください。 + +## 出力アクション +### 直接追加 +``` +N. APPEND{tab}APPEND +``` +直接追加を選択した場合、シンプルに`APPEND`ワードを出力してください。コンテンツを繰り返す必要はありません。 +### メモの更新 +``` +N. UPDATE{tab}[UPDATED_MEMO] +``` +メモの更新を選択した場合、メモを更新して`[UPDATED_MEMO]`の中に完全に更新された現在のメモを書き直してください。 +### 統合の放棄 +``` +N. ABORT{tab}ABORT +``` +統合の放棄を選択した場合、シンプルに`ABORT`ワードを出力してください。コンテンツを繰り返す必要はありません。 + +## 出力フォーマット +上記の指示に従って、以下のフォーマットで出力してください: + +THOUGHT +--- +1. ACTION{tab}... +2. ACTION{tab}... +... + +上記のうち: +- `THOUGHT` はあなたの思考プロセスです +- 思考とActionの間は `---` で区切られます +- `N. ACTION{tab}...` はN番目の補足情報(memo_id=N)に対する出力アクションです + +## 例 +### 入力例 +{{ + "memo_id": "1", + "new_info": "期末試験の準備中[2025/06/01に言及]", + "current_memo": "中間試験の準備中[2025/04/01に言及]", + "topic": "学歴", + "subtopic": "試験目標", + "update_instruction": "目標を更新する際、古くなってしまった目標や矛盾した目標がないかを確認して、必要なら削除してください", +}} +{{ + "memo_id": "2", + "new_info": "Duolingoで日本語を独学中", + "current_memo": "", + "topic": "学歴", + "subtopic": "使用ソフトウェア", +}} +{{ + "memo_id": "3", + "new_info": "ユーザーは鍋料理を食べるのが好き", + "current_memo": "", + "topic": "興味", + "subtopic": "運動", +}} + +### 出力例 +``` +補足情報には、ユーザーの現在の学習目標が期末試験の準備であることが記載されており、これはユーザーの学習目標を記録するというトピックの説明と一致しています。しかし、期末試験と中間試験が重複しているため、中間試験の目標を削除し、それを期末試験に更新する必要があります。 +さらに、ユーザーは言語学習にDuolingoを利用していると述べており、ソフトウェア使用の要件を満たしています。メモID 2の現在のメモが空欄であるため、直接追加できます。 +鍋料理が好きという事実は興味・スポーツには該当せず、この情報から潜在的な興味を導き出すことはできないため、統合を破棄する。 +--- +1. UPDATE{tab}期末試験の準備中[2025/06/01に言及]; +2. APPEND{tab}APPEND +3. ABORT{tab}ABORT +``` + +## 要件 +以下の指示に従ってください: +- 正しい出力形式を厳守してください。 +- 更新されたメモは5文を超えないようにしてください。常に簡潔にまとめ、メモの要点を出力してください。 +- 入力で言及されていない内容を捏造しないでください。 +- 旧メモと新メモの両方の日時アノテーションを保持してください。(例: XXX[2025/05/05に言及、2022年に発生])。 +- 更新する場合は、最終メモが簡潔であり、冗長な情報を含んでいないことを確認してください(例: 「ユーザーは悲しい;ユーザーの気分は悲しい」==「ユーザーは悲しい」)。 + +以上が全ての内容です。では、作業を実行してください。 +""" + + +def get_input( + memos_list: list[dict], +): + return f""" +{memos_list} +""" + + +def get_prompt() -> str: + return MERGE_FACTS_PROMPT.format( + tab=CONFIG.llm_tab_separator, + ) + + +def get_kwargs() -> dict: + return ADD_KWARGS + + +if __name__ == "__main__": + print(get_prompt()) diff --git a/src/server/api/memobase_server/prompts/ja_summary_entry_chats.py b/src/server/api/memobase_server/prompts/ja_summary_entry_chats.py new file mode 100644 index 0000000..9883b95 --- /dev/null +++ b/src/server/api/memobase_server/prompts/ja_summary_entry_chats.py @@ -0,0 +1,95 @@ +from ..env import CONFIG + +ADD_KWARGS = { + "prompt_id": "ja_summary_entry_chats", +} +SUMMARY_PROMPT = """あなたは、チャットから個人情報、スケジュール、イベントを記録する専門家です。 +あなたには、ユーザーとアシスタント間で行われたチャットが与えられます。 + +## 要件 +- すべての可能なユーザー情報、スケジュール、イベントをリストアップする必要があります +- {additional_requirements} +- ユーザーのイベント/スケジュールに特定の言及日時またはイベント発生日時が含まれている場合、メッセージ内の日時情報を変換して、記録の後の[TIME]内にメッセージ内のイベント日付情報を変換してください。例えば: + 入力: `[2024/04/30] user: 昨日、新車を買ったんだ!` + 出力: `ユーザは新車を購入した。[2024/04/30に言及、2024/04/29に車購入]。` + 入力: `[2024/04/30] user: 4年前に車を買ったんだ!` + 出力: `ユーザは車を購入した。[2024/04/30に言及、2020年に車購入]。` + 説明: 正確な日付はわからず、年のみが判明している。したがって2024-4=2020となる。または[2024/04/30の4年前]と記録することも可能。 + 入力: `[2024/04/30] user: 先週、新車を買ったんだ!` + 出力: `ユーザは新車を購入した。[2024/04/30に言及、2024/04/30の1週前に車購入]。` + 説明: 正確な日付がわからないため。 + 入力: `[...] user: 先週、新車を買ったんだ!` + 出力: `ユーザは新車を購入した。` + 説明: 正確な日付がわからないため、日付を含めない。 + +### 重要情報 +以下は、チャットから記録すべきトピック/サブトピックです。 + +{topics} + +以下は、チャットから記録すべき重要な属性です。 + +{attributes} + + +## 入力フォーマット +### すでに記録済み +過去の記録結果のリストを受け取ります。既に記録されている情報に関連する可能性のある関連情報も記録してください。 +過去の結果はプロファイル形式で整理されています: +- TOPIC{separator}SUBTOPIC{separator}CONTENT... // maybe truncated + +### 入力されるチャット +ユーザーとアシスタントの会話を受け取ります。会話の形式は以下の通りです: +- [TIME] NAME: MESSAGE +ここでは、NAMEはALIAS(ROLE)または単にROLEです。ALIASが利用可能な場合は、ユーザー/アシスタントを指すのにALIASを使用してください。 +MESSAGEは会話の内容です。 +TIMEはメッセージが発生した時刻です。必要に応じて、メッセージ内の日付情報をTIMEに基づいて変換してください。 + +## 出力フォーマット +- LOGGING[TIME INFO] // TYPE +記録結果をMarkdownの箇条書き形式で出力してください。 +例えば: +``` +- Jackが子供たちの絵を描いた。[2023/01/23に言及] // event +- ユーザーの別名はJack、アシスタントはMelinda。 // info +- Jackは自身の職業がMemobaseのソフトウェアエンジニアだと述べた。[2023/01/23に言及] // info +- Jackはジムに行く予定。[2023/1/23に言及、2023/01/24にジムに行く予定] // schedule +... +``` +記録には必ず具体的な言及日時を、可能であればイベント発生日時も追加してください。 +記録は純粋かつ簡潔に保ってください。日時情報は全て[TIME INFO]ブロック内に移動させてください。 + +## コンテンツ要求 +- すべての可能なユーザー情報、スケジュール、イベントをリストアップする必要があります +- {additional_requirements} + +では、作業を実行してください。 +""" + + +def pack_input(already_logged_str: str, chat_strs: str): + return f"""### 既に記録済み +{already_logged_str} + +### 入力されるチャット +{chat_strs} +""" + + +def get_prompt( + topic_examples: str, attribute_examples: str, additional_requirements: str = "" +) -> str: + return SUMMARY_PROMPT.format( + topics=topic_examples, + attributes=attribute_examples, + additional_requirements=additional_requirements, + separator=CONFIG.llm_tab_separator, + ) + + +def get_kwargs() -> dict: + return ADD_KWARGS + + +if __name__ == "__main__": + print(get_prompt()) diff --git a/src/server/api/memobase_server/prompts/ja_user_profile_topics.py b/src/server/api/memobase_server/prompts/ja_user_profile_topics.py new file mode 100644 index 0000000..ac00f34 --- /dev/null +++ b/src/server/api/memobase_server/prompts/ja_user_profile_topics.py @@ -0,0 +1,108 @@ +from ..env import CONFIG, LOG +from .profile_init_utils import ( + UserProfileTopic, + formate_profile_topic, + modify_default_user_profile, +) + + +CANDIDATE_PROFILE_TOPICS: list[UserProfileTopic] = [ + UserProfileTopic( + "基本情報", + sub_topics=[ + "氏名", + { + "name": "年齢", + "description": "整数", + }, + "性別", + "生年月日", + "国籍", + "民族", + "使用言語", + ], + ), + UserProfileTopic( + "連絡先", + sub_topics=[ + "電子メール", + "電話", + "市区町村", + "都道府県", + "国", + ], + ), + UserProfileTopic( + "学歴", + sub_topics=[ + "出身校", + "学位", + "専攻", + "卒業年", + ], + ), + UserProfileTopic( + "人口統計", + sub_topics=[ + "婚姻状況", + "子供の数", + "世帯収入", + ], + ), + UserProfileTopic( + "仕事", + sub_topics=[ + "会社", + "役職", + "業界", + "以前のプロジェクト", + "スキル", + ], + ), + UserProfileTopic( + "興味", + sub_topics=[ + "本", + "映画", + "音楽", + "料理", + "運動", + ], + ), + UserProfileTopic( + "ライフスタイル", + sub_topics=[ + {"name": "食事の好み", "description": "例: ベジタリアン、ヴィーガン"}, + "運動習慣", + "健康状態", + "睡眠パターン", + "喫煙", + "飲酒", + ], + ), + UserProfileTopic( + "心理的特性", + sub_topics=["性格", "価値観", "信念", "動機", "目標"], + ), + UserProfileTopic( + "ライフイベント", + sub_topics=["結婚", "引っ越し", "退職"], + ), +] + + +CANDIDATE_PROFILE_TOPICS = modify_default_user_profile(CANDIDATE_PROFILE_TOPICS) + + +def get_prompt(profiles: list[UserProfileTopic] = CANDIDATE_PROFILE_TOPICS): + return "\n".join([formate_profile_topic(up) for up in profiles]) + "\n..." + + +if CONFIG.language == "ja": + LOG.info(f"User profiles: \n{get_prompt()}") + +if __name__ == "__main__": + from .profile_init_utils import export_user_profile_to_yaml + + # print(get_prompt()) + print(export_user_profile_to_yaml(CANDIDATE_PROFILE_TOPICS))