Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 21 additions & 11 deletions assets/cli_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 <id> 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 <id> Set user id 设置用户ID ユーザIDを設定
:profile / :pf Show user profile 显示用户已有的配置信息 ユーザのプロファイルを表示
:flush / :fl Flush buffer 刷新缓冲区 バッファをフラッシュ
"""
_ALL_COMMAND_NAMES = [
"help",
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion docs/site/references/local_config.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
13 changes: 13 additions & 0 deletions src/server/api/memobase_server/controllers/modal/chat/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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,
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@
"detect_interest": zh_detect_interest,
"infer_plot": zh_infer_plot,
},
"ja": {},
}
6 changes: 3 additions & 3 deletions src/server/api/memobase_server/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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]
Expand Down
2 changes: 2 additions & 0 deletions src/server/api/memobase_server/prompts/__init__.py
Original file line number Diff line number Diff line change
@@ -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,
)
14 changes: 14 additions & 0 deletions src/server/api/memobase_server/prompts/chat_context_pack.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
163 changes: 163 additions & 0 deletions src/server/api/memobase_server/prompts/ja_extract_profile.py
Original file line number Diff line number Diff line change
@@ -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"""<example>
<input>{p[0]}</input>
<output>
{pack_profiles_into_string(p[1])}
</output>
</example>
"""
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()))
Loading