mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-10-08 03:10:24 +00:00
feat(config): switch to thinking model and enhance memory system
This commit is contained in:
parent
0692023379
commit
c98050dc0d
8 changed files with 868 additions and 657 deletions
|
|
@ -7,55 +7,39 @@ system_prompt: |
|
|||
## Current Time
|
||||
{current_time}
|
||||
|
||||
## 记忆系统
|
||||
You wake up fresh each session. These files provide continuity:
|
||||
- 📝 Daily Notes: `memory/YYYY-MM-DD.md`
|
||||
- 🧠 Long-Term Memory: `MEMORY.md`
|
||||
## Memory
|
||||
You wake up fresh each session. These files are your continuity:
|
||||
- **Daily notes:** `memory/YYYY-MM-DD.md` — raw logs of what happened
|
||||
- **Long-term:** `MEMORY.md` — your curated memories, like a human's long-term memory
|
||||
Capture what matters. Decisions, context, things to remember. Skip the secrets unless asked to keep them.
|
||||
|
||||
### 什么时候更新记忆
|
||||
- 对话中涉及用户的重要信息,包括:用户preferences,用户日程信息等
|
||||
- 对话中用户主动要求你记录一些信息
|
||||
- 你要做的事情的todo
|
||||
### 🧠 MEMORY.md - Your Long-Term Memory
|
||||
- You can **read, edit, and update** MEMORY.md freely in main sessions
|
||||
- Write significant events, thoughts, decisions, opinions, lessons learned
|
||||
- This is your curated memory — the distilled essence, not raw logs
|
||||
- Over time, review your daily files and update MEMORY.md with what's worth keeping
|
||||
|
||||
默认可以记录到 `memory/YYYY-MM-DD.md` 中,除非主动提及 `MEMORY.md`。
|
||||
可以先判断`memory/YYYY-MM-DD.md`是否存在,如果不存在可以直接写入。
|
||||
如果`memory/YYYY-MM-DD.md`文件存在,可以先读取原来的内容,然后通过写入完整的内容,或者编辑替换内容来更新记忆
|
||||
|
||||
### 记忆检索(优先级从高到低)
|
||||
- 可以使用memory_search进行检索,(基于向量和BM25检索),检索memory chunk
|
||||
- 可以使用
|
||||
|
||||
|
||||
### 记忆的检索
|
||||
|
||||
## Memory System
|
||||
You wake up fresh each session. These files provide continuity:
|
||||
|
||||
### 📝 Daily Notes: `memory/YYYY-MM-DD.md`
|
||||
- Raw logs of what happened today
|
||||
- Create `memory/` directory if needed
|
||||
- Write events, conversations, tasks, decisions as they happen
|
||||
- Capture what matters
|
||||
|
||||
### 🧠 Long-Term Memory: `MEMORY.md`
|
||||
- Your curated memories, like a human's long-term memory
|
||||
- The distilled essence, not raw logs
|
||||
- Contains: significant events, thoughts, decisions, opinions, lessons learned
|
||||
- Maintenance: periodically review daily files and promote important context here
|
||||
|
||||
### 🔍 Memory Recall
|
||||
Before answering questions about prior work, decisions, dates, people, preferences, or todos:
|
||||
1. Run `memory_search` on MEMORY.md + memory/*.md
|
||||
2. Use `memory_get` to pull only the needed lines
|
||||
|
||||
### 💾 Write It Down - No "Mental Notes"!
|
||||
- **Memory is limited** — if you want to remember something, WRITE IT TO A FILE
|
||||
### 📝 Write It Down - No "Mental Notes"!
|
||||
- "Mental notes" don't survive session restarts. Files do.
|
||||
- When someone says "remember this" → update `memory/YYYY-MM-DD.md` or MEMORY.md
|
||||
- When you learn a lesson → update `memory/YYYY-MM-DD.md` or MEMORY.md
|
||||
- When you make a mistake → update `memory/YYYY-MM-DD.md` or MEMORY.md, so future-you doesn't repeat it
|
||||
- **IMPORTANT: Always read the file first before writing** — understand what's already there, then append or update
|
||||
- When someone says "remember this" → read then update `memory/YYYY-MM-DD.md` or relevant file
|
||||
- When you learn a lesson → read then update `memory/YYYY-MM-DD.md` or relevant file
|
||||
- When you make a mistake → read then update `memory/YYYY-MM-DD.md` or relevant file
|
||||
- **Text > Brain** 📝
|
||||
|
||||
### 🔍 Recall Tools
|
||||
Before answering questions about prior work, decisions, dates, people, preferences, or todos:
|
||||
1. Run `memory_search` on MEMORY.md + memory/*.md
|
||||
2. If you need to read the Daily Notes `memory/YYYY-MM-DD.md`, you can use the read tool to access it.
|
||||
|
||||
### 🛠️ Other Tools
|
||||
- **bash_tool** — execute shell commands
|
||||
- **ls_tool** — list directory contents
|
||||
- **read_tool** — read file contents
|
||||
- **edit_tool** — modify existing files
|
||||
- **write_tool** — create new files
|
||||
- **execute_code** — run Python code
|
||||
- **dashscope_search** — search the web
|
||||
|
||||
## React Like a Human 😊
|
||||
**React when:**
|
||||
|
|
@ -80,4 +64,72 @@ system_prompt: |
|
|||
[has_previous_summary]{previous_summary}
|
||||
[has_previous_summary]</previous-summary>
|
||||
[has_previous_summary]
|
||||
[has_previous_summary]The above is a summary of our previous conversation. Use it as context to maintain continuity.
|
||||
[has_previous_summary]The above is a summary of our previous conversation. Use it as context to maintain continuity.
|
||||
|
||||
system_prompt_zh: |
|
||||
你是一个名叫 Remy 的个人助手。
|
||||
|
||||
## 工作目录
|
||||
{workspace_dir}
|
||||
|
||||
## 当前时间
|
||||
{current_time}
|
||||
|
||||
## 记忆系统
|
||||
每次会话你都会重新唤醒。这些文件是你保持连续性的关键:
|
||||
- **每日笔记:** `memory/YYYY-MM-DD.md` — 发生的事情的原始记录
|
||||
- **长期记忆:** `MEMORY.md` — 你精心整理的记忆,就像人类的长期记忆一样
|
||||
记录重要的事情。决策、上下文、需要记住的事情。除非被要求保留,否则跳过秘密信息。
|
||||
|
||||
### 🧠 MEMORY.md - 你的长期记忆
|
||||
- 在主会话中,你可以**自由地阅读、编辑和更新** MEMORY.md
|
||||
- 记录重要的事件、想法、决策、观点、经验教训
|
||||
- 这是你精选的记忆 — 提炼的精华,而不是原始日志
|
||||
- 随着时间推移,回顾你的每日文件,并将值得保留的内容更新到 MEMORY.md
|
||||
|
||||
### 📝 写下来 - 不要只在"脑中记住"!
|
||||
- "脑中记住"无法在会话重启后保留。文件可以。
|
||||
- **重要:写入之前务必先读取文件** — 了解已有内容,然后再追加或更新
|
||||
- 当有人说"记住这个" → 先读取再更新 `memory/YYYY-MM-DD.md` 或相关文件
|
||||
- 当你学到经验 → 先读取再更新 `memory/YYYY-MM-DD.md` 或相关文件
|
||||
- 当你犯了错误 → 先读取再更新 `memory/YYYY-MM-DD.md` 或相关文件
|
||||
- **文字 > 大脑** 📝
|
||||
|
||||
### 🔍 检索工具
|
||||
在回答关于过往工作、决策、日期、人员、偏好或待办事项的问题之前:
|
||||
1. 对 MEMORY.md + memory/*.md 运行 `memory_search`
|
||||
2. 如果你需要阅读每日笔记 `memory/YYYY-MM-DD.md`,可以使用读取工具访问它。
|
||||
|
||||
### 🛠️ 其他工具
|
||||
- **bash_tool** — 执行 shell 命令
|
||||
- **ls_tool** — 列出目录内容
|
||||
- **read_tool** — 读取文件内容
|
||||
- **edit_tool** — 修改现有文件
|
||||
- **write_tool** — 创建新文件
|
||||
- **execute_code** — 运行 Python 代码
|
||||
- **dashscope_search** — 网络搜索
|
||||
|
||||
## 像人类一样回应 😊
|
||||
**何时使用表情回应:**
|
||||
- 你欣赏某事但不需要文字回复时(👍, ❤️, 🙌)
|
||||
- 某事让你发笑时(😂, 💀)
|
||||
- 你觉得有趣或发人深省时(🤔, 💡)
|
||||
- 你想要确认但不想打断对话流时
|
||||
- 这是一个简单的是/否或批准的情况(✅, 👀)
|
||||
**原因:** 表情回应是轻量级的社交信号。人类经常使用它们 — 它们表示"我看到了,我认可你"而不会让对话变得混乱。
|
||||
**不要过度使用:** 每条消息最多一个表情回应。选择最合适的一个。
|
||||
|
||||
## 🛡️ 安全规则
|
||||
- 不要在没有询问的情况下运行破坏性命令
|
||||
- 优先使用 `trash` 而不是 `rm`(可恢复比永久删除更好)
|
||||
- 有疑问时,先询问
|
||||
|
||||
## 持续改进
|
||||
这只是一个起点。随着你逐渐发现什么有效,添加你自己的约定、风格和规则。
|
||||
|
||||
[has_previous_summary]## 之前的对话摘要
|
||||
[has_previous_summary]<previous-summary>
|
||||
[has_previous_summary]{previous_summary}
|
||||
[has_previous_summary]</previous-summary>
|
||||
[has_previous_summary]
|
||||
[has_previous_summary]以上是我们之前对话的摘要。使用它作为上下文以保持连续性。
|
||||
|
|
|
|||
|
|
@ -66,18 +66,7 @@ class FsCompactor(BaseOp):
|
|||
]
|
||||
|
||||
async def execute(self) -> str:
|
||||
"""
|
||||
Generate summary for conversation history.
|
||||
|
||||
Expects context to have:
|
||||
- messages_to_summarize: list[Message] (required)
|
||||
- turn_prefix_messages: list[Message] (optional, for split turn)
|
||||
- previous_summary: str (optional, for incremental summarization)
|
||||
|
||||
Returns:
|
||||
str: Generated summary text formatted with compaction_summary_format.
|
||||
Returns empty string if no messages to summarize.
|
||||
"""
|
||||
"""Generate summary for conversation history."""
|
||||
messages_to_summarize = self.context.get("messages_to_summarize", [])
|
||||
turn_prefix_messages = self.context.get("turn_prefix_messages", [])
|
||||
previous_summary = self.context.get("previous_summary", "")
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from loguru import logger
|
|||
from ...core.enumeration import Role
|
||||
from ...core.op import BaseReact
|
||||
from ...core.schema import Message
|
||||
from ...core.utils import format_messages
|
||||
|
||||
|
||||
class FsSummarizer(BaseReact):
|
||||
|
|
@ -26,7 +27,8 @@ class FsSummarizer(BaseReact):
|
|||
Message(
|
||||
role=Role.USER,
|
||||
content=self.prompt_format(
|
||||
"user_message_v2",
|
||||
"user_message_default",
|
||||
conversation=format_messages(messages, add_index=False),
|
||||
date=date_str,
|
||||
memory_dir=self.memory_dir,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -9,31 +9,66 @@ user_message: |
|
|||
Store durable memories now (use {memory_dir}/YYYY-MM-DD.md; create {memory_dir}/ if needed).
|
||||
If nothing to store, reply with [SILENT].
|
||||
|
||||
user_message_v2: |
|
||||
user_message_default: |
|
||||
<conversation>
|
||||
{conversation}
|
||||
</conversation>
|
||||
|
||||
The conversation is about to be compacted. Please extract persistent memories to disk.
|
||||
Current Date: {date}
|
||||
The session is near auto-compaction; capture durable memories to disk.
|
||||
|
||||
Memory storage workflow:
|
||||
1. Check if {memory_dir}/ exists; if not, create it via bash
|
||||
2. Check if {memory_dir}/YYYY-MM-DD.md exists (use actual date)
|
||||
3. If file is NEW: Write memories directly (be concise)
|
||||
4. If file EXISTS:
|
||||
a) Read the existing file content
|
||||
b) Compare conversation history with existing content
|
||||
c) Identify NEW/UPDATED information not yet captured
|
||||
d) Use edit_tool to add/update only the new information (preserve existing content)
|
||||
e) If conversation contains NO new information, skip writing
|
||||
5. If NO valuable information to store: Reply with reason and [SILENT]
|
||||
Execution Flow:
|
||||
1. Determine if the conversation contains information worth storing
|
||||
- If no: Reply with reason + [SILENT]
|
||||
- If yes: Continue to step 2
|
||||
|
||||
IMPORTANT for updates:
|
||||
- Only add information that is NOT already in the file
|
||||
- Preserve all existing entries
|
||||
- Merge duplicate information intelligently
|
||||
- Use edit_tool for surgical updates, not write_tool (which overwrites)
|
||||
2. Check file {memory_dir}/YYYY-MM-DD.md (use actual date)
|
||||
- File doesn't exist: Write new memories directly
|
||||
- File exists:
|
||||
a) Read existing content
|
||||
b) Compare and identify new/updated information
|
||||
c) Prefer edit_tool for precise additions (preserves existing content); write_tool overwrites entire file
|
||||
d) If no new information: Reply with explanation + [SILENT]
|
||||
|
||||
Example of what counts as NEW information:
|
||||
- Existing: "Alice: Software engineer"
|
||||
- Conversation: "Alice loves Python and AI projects"
|
||||
- Action: ADD "Enjoys Python programming and AI project work" to Alice's entry
|
||||
Update Principles:
|
||||
- Only add unrecorded information, preserve all existing content
|
||||
- Intelligently merge duplicate information, retain key details like timestamps
|
||||
|
||||
Store durable memories. Keep entries concise and well-organized.
|
||||
Example (information merging):
|
||||
Existing: "Alice joined Company A as Software Engineer on 2023-01-01"
|
||||
New: "Alice joined Company B as Senior Engineer on 2024-01-01"
|
||||
Result: "Alice joined Company A as Software Engineer on 2023-01-01, moved to Company B as Senior Engineer on 2024-01-01"
|
||||
|
||||
Please store persistent memories, keeping entries concise and well-structured.
|
||||
|
||||
user_message_default_zh: |
|
||||
<conversation>
|
||||
{conversation}
|
||||
</conversation>
|
||||
|
||||
conversation即将压缩,请提取持久性记忆存储至磁盘。
|
||||
当前日期:{date}
|
||||
|
||||
执行流程:
|
||||
1. 判断对话是否包含值得存储的信息
|
||||
- 若无:回复原因 + [SILENT]
|
||||
- 若有:继续步骤 2
|
||||
|
||||
2. 检查文件 {memory_dir}/YYYY-MM-DD.md(使用实际日期)
|
||||
- 文件不存在:直接写入新记忆
|
||||
- 文件已存在:
|
||||
a) 读取现有内容
|
||||
b) 对比识别新增/更新信息
|
||||
c) 优先使用 edit_tool 精准添加新信息(保留已有内容),write_tool 会覆盖整个文件
|
||||
d) 若无新信息:回复说明 + [SILENT]
|
||||
|
||||
更新原则:
|
||||
- 仅添加未记录的信息,保留所有已有内容
|
||||
- 智能合并重复信息,保留时间等关键细节
|
||||
|
||||
示例(信息合并):
|
||||
已有:"Alice 于 2023-01-01 加入 A 公司任软件工程师"
|
||||
新增:"Alice 于 2024-01-01 加入 B 公司任高级工程师"
|
||||
结果:"Alice 于 2023-01-01 加入 A 公司任软件工程师,2024-01-01 转入 B 公司任高级工程师"
|
||||
|
||||
请存储持久性记忆,保持条目简洁、结构清晰。
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ backend: cmd
|
|||
llms:
|
||||
default:
|
||||
backend: openai
|
||||
model_name: qwen3-30b-a3b-instruct-2507
|
||||
# model_name: qwen3-30b-a3b-thinking-2507
|
||||
# model_name: qwen3-30b-a3b-instruct-2507
|
||||
model_name: qwen3-30b-a3b-thinking-2507
|
||||
request_interval: 1
|
||||
# temperature: 0.0001
|
||||
|
||||
|
|
|
|||
106
reme/reme_fs.py
106
reme/reme_fs.py
|
|
@ -13,15 +13,12 @@ from .agent.fs import FsCompactor, FsContextChecker, FsSummarizer
|
|||
from .config import ReMeConfigParser
|
||||
from .core import Application
|
||||
from .core.enumeration import ChunkEnum
|
||||
from .core.op import BaseTool
|
||||
from .core.schema import Message, StreamChunk
|
||||
from .tool.fs import (
|
||||
BashTool,
|
||||
EditTool,
|
||||
FindTool,
|
||||
FsMemoryGet,
|
||||
FsMemorySearch,
|
||||
GrepTool,
|
||||
LsTool,
|
||||
ReadTool,
|
||||
WriteTool,
|
||||
|
|
@ -93,14 +90,19 @@ class ReMeFs(Application):
|
|||
default_file_watcher_config = default_file_watcher_config or {}
|
||||
default_file_watcher_config.update(
|
||||
{
|
||||
"watch_paths": watch_paths or [self.working_dir, str(memory_path)],
|
||||
"watch_paths": watch_paths
|
||||
or [
|
||||
str(working_path / "MEMORY.md"),
|
||||
str(working_path / "memory.md"),
|
||||
str(memory_path),
|
||||
],
|
||||
"suffix_filters": suffix_filters or [".md"],
|
||||
"recursive": recursive,
|
||||
"debounce": debounce,
|
||||
"chunk_tokens": chunk_tokens,
|
||||
"chunk_overlap": chunk_overlap,
|
||||
"scan_on_start": scan_on_start,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
super().__init__(
|
||||
|
|
@ -129,34 +131,14 @@ class ReMeFs(Application):
|
|||
self.hybrid_text_weight: float = hybrid_text_weight
|
||||
self.hybrid_candidate_multiplier: float = hybrid_candidate_multiplier
|
||||
|
||||
# Setup file system tools
|
||||
self.fs_tools: list[BaseTool] = [
|
||||
FsMemorySearch(
|
||||
hybrid_enabled=hybrid_enabled,
|
||||
hybrid_vector_weight=hybrid_vector_weight,
|
||||
hybrid_text_weight=hybrid_text_weight,
|
||||
hybrid_candidate_multiplier=hybrid_candidate_multiplier,
|
||||
),
|
||||
# FsMemoryGet(cwd=self.working_dir),
|
||||
BashTool(cwd=self.working_dir),
|
||||
EditTool(cwd=self.working_dir),
|
||||
FindTool(cwd=self.working_dir),
|
||||
GrepTool(cwd=self.working_dir),
|
||||
LsTool(cwd=self.working_dir),
|
||||
ReadTool(cwd=self.working_dir),
|
||||
WriteTool(cwd=self.working_dir),
|
||||
ExecuteCode(),
|
||||
DashscopeSearch(),
|
||||
]
|
||||
|
||||
# Commands
|
||||
self.commands = [
|
||||
"/new",
|
||||
"/compact",
|
||||
"/exit",
|
||||
"/clear",
|
||||
"/help",
|
||||
]
|
||||
self.commands = {
|
||||
"/new": "Create a new conversation.",
|
||||
"/compact": "Compact messages into a summary.",
|
||||
"/exit": "Exit the application.",
|
||||
"/clear": "Clear the history.",
|
||||
"/help": "Show help.",
|
||||
}
|
||||
|
||||
async def context_check(self, messages: list[Message | dict]) -> dict:
|
||||
"""Check if messages exceed context limits."""
|
||||
|
|
@ -172,18 +154,11 @@ class ReMeFs(Application):
|
|||
messages_to_summarize: list[Message | dict] = None,
|
||||
turn_prefix_messages: list[Message | dict] = None,
|
||||
previous_summary: str = "",
|
||||
language: str = "zh",
|
||||
**kwargs,
|
||||
) -> str:
|
||||
"""Compact messages into a summary.
|
||||
|
||||
Args:
|
||||
messages_to_summarize: Messages to summarize
|
||||
turn_prefix_messages: Messages to prepend to each turn
|
||||
previous_summary: Previous summary to build upon
|
||||
|
||||
Returns:
|
||||
Compaction result from FsCompactor
|
||||
"""
|
||||
compactor = FsCompactor()
|
||||
"""Compact messages into a summary."""
|
||||
compactor = FsCompactor(language=language, **kwargs)
|
||||
return await compactor.call(
|
||||
messages_to_summarize=messages_to_summarize or [],
|
||||
turn_prefix_messages=turn_prefix_messages or [],
|
||||
|
|
@ -191,17 +166,20 @@ class ReMeFs(Application):
|
|||
service_context=self.service_context,
|
||||
)
|
||||
|
||||
async def summary(self, messages: list[Message | dict], date: str):
|
||||
"""Generate a summary of the given messages.
|
||||
|
||||
Args:
|
||||
messages: Messages to summarize
|
||||
date: Date of the conversation
|
||||
|
||||
Returns:
|
||||
Summary of the given messages
|
||||
"""
|
||||
summarizer = FsSummarizer(tools=self.fs_tools, working_dir=self.working_dir)
|
||||
async def summary(self, messages: list[Message | dict], date: str, language: str = "zh", **kwargs):
|
||||
"""Generate a summary of the given messages."""
|
||||
summarizer = FsSummarizer(
|
||||
tools=[
|
||||
BashTool(cwd=self.working_dir),
|
||||
LsTool(cwd=self.working_dir),
|
||||
ReadTool(cwd=self.working_dir),
|
||||
WriteTool(cwd=self.working_dir),
|
||||
EditTool(cwd=self.working_dir),
|
||||
],
|
||||
working_dir=self.working_dir,
|
||||
language=language,
|
||||
**kwargs,
|
||||
)
|
||||
return await summarizer.call(messages=messages, date=date, service_context=self.service_context)
|
||||
|
||||
async def memory_search(self, query: str, max_results: int = 10, min_score: float = 0.3) -> str:
|
||||
|
|
@ -257,11 +235,25 @@ class ReMeFs(Application):
|
|||
result = await checker.call(messages=messages, service_context=self.service_context)
|
||||
return result["needs_compaction"]
|
||||
|
||||
async def chat_with_remy(self, tool_result_max_size: int = 100):
|
||||
async def chat_with_remy(self, tool_result_max_size: int = 100, language: str = "zh", **kwargs):
|
||||
"""Interactive CLI chat with Remy using simple streaming output."""
|
||||
fs_cli = FsCli(
|
||||
working_dir=self.working_dir,
|
||||
tools=self.fs_tools,
|
||||
tools=[
|
||||
FsMemorySearch(
|
||||
hybrid_enabled=self.hybrid_enabled,
|
||||
hybrid_vector_weight=self.hybrid_vector_weight,
|
||||
hybrid_text_weight=self.hybrid_text_weight,
|
||||
hybrid_candidate_multiplier=self.hybrid_candidate_multiplier,
|
||||
),
|
||||
BashTool(cwd=self.working_dir),
|
||||
LsTool(cwd=self.working_dir),
|
||||
ReadTool(cwd=self.working_dir),
|
||||
EditTool(cwd=self.working_dir),
|
||||
WriteTool(cwd=self.working_dir),
|
||||
ExecuteCode(),
|
||||
DashscopeSearch(),
|
||||
],
|
||||
context_window_tokens=self.context_window_tokens,
|
||||
reserve_tokens=self.reserve_tokens,
|
||||
keep_recent_tokens=self.keep_recent_tokens,
|
||||
|
|
@ -270,6 +262,8 @@ class ReMeFs(Application):
|
|||
hybrid_text_weight=self.hybrid_text_weight,
|
||||
hybrid_candidate_multiplier=self.hybrid_candidate_multiplier,
|
||||
tool_result_max_size=tool_result_max_size,
|
||||
language=language,
|
||||
**kwargs,
|
||||
)
|
||||
session = PromptSession()
|
||||
|
||||
|
|
|
|||
|
|
@ -34,20 +34,20 @@ def create_long_conversation() -> list[Message]:
|
|||
messages = [
|
||||
Message(
|
||||
role=Role.USER,
|
||||
content="I need help building a complete web application with authentication, database, and API endpoints.",
|
||||
content="我需要帮助构建一个完整的Web应用程序,包含身份验证、数据库和API端点。",
|
||||
),
|
||||
Message(
|
||||
role=Role.ASSISTANT,
|
||||
content="""I'll help you build a complete web application. Here's what we'll do:
|
||||
content="""我会帮你构建一个完整的Web应用程序。我们将执行以下步骤:
|
||||
|
||||
1. Set up the project structure
|
||||
2. Implement authentication system
|
||||
3. Design and create database schema
|
||||
4. Build API endpoints
|
||||
5. Add frontend components
|
||||
6. Test and deploy
|
||||
1. 设置项目结构
|
||||
2. 实现身份验证系统
|
||||
3. 设计和创建数据库架构
|
||||
4. 构建API端点
|
||||
5. 添加前端组件
|
||||
6. 测试和部署
|
||||
|
||||
Let me start with the project structure...""",
|
||||
让我从项目结构开始...""",
|
||||
),
|
||||
]
|
||||
|
||||
|
|
@ -55,24 +55,24 @@ Let me start with the project structure...""",
|
|||
messages.append(
|
||||
Message(
|
||||
role=Role.USER,
|
||||
content=f"What about step {i + 1}? Can you provide more details?",
|
||||
content=f"第{i + 1}步怎么做?能提供更多细节吗?",
|
||||
),
|
||||
)
|
||||
messages.append(
|
||||
Message(
|
||||
role=Role.ASSISTANT,
|
||||
content=f"""For step {i + 1}, here's a detailed explanation:
|
||||
content=f"""对于第{i + 1}步,这里是详细的解释:
|
||||
|
||||
First, we need to consider the architecture. """
|
||||
+ "This is important context. " * 50
|
||||
首先,我们需要考虑架构设计。"""
|
||||
+ "这是重要的上下文信息。" * 50
|
||||
+ """
|
||||
|
||||
Then we implement the following:
|
||||
- Component A
|
||||
- Component B
|
||||
- Component C
|
||||
然后我们实现以下内容:
|
||||
- 组件A
|
||||
- 组件B
|
||||
- 组件C
|
||||
|
||||
Let me show you the code for this part..."""
|
||||
让我向你展示这部分的代码..."""
|
||||
+ "\n\ncode_example = 'example'" * 20,
|
||||
),
|
||||
)
|
||||
|
|
@ -85,471 +85,454 @@ def create_realistic_personal_conversation() -> list[Message]:
|
|||
messages = [
|
||||
Message(
|
||||
role=Role.USER,
|
||||
content="Hi! I'm planning a trip to Japan next month. I need help organizing my itinerary.",
|
||||
content="你好!我计划下个月去中国旅行。我需要帮助安排行程。",
|
||||
),
|
||||
Message(
|
||||
role=Role.ASSISTANT,
|
||||
content="""\
|
||||
Great! I'd be happy to help you plan your Japan trip. To give you the best recommendations, could you tell me:
|
||||
1. Which cities are you planning to visit?
|
||||
2. How many days will you be there?
|
||||
3. What are your interests (food, culture, nature, shopping, etc.)?
|
||||
4. What's your approximate budget per day?""",
|
||||
太好了!我很乐意帮你规划中国之旅。为了给你最好的建议,请告诉我:
|
||||
1. 你计划去哪些城市?
|
||||
2. 你会在那里待几天?
|
||||
3. 你的兴趣是什么(美食、文化、自然景观、购物等)?
|
||||
4. 你每天的预算大约是多少?""",
|
||||
),
|
||||
Message(
|
||||
role=Role.USER,
|
||||
content="""\
|
||||
I'm planning to visit Tokyo, Kyoto, and Osaka. The trip will be 14 days, from March 15th to March 29th.
|
||||
My name is Sarah Chen, and I'm 28 years old. I'm traveling with my boyfriend David Miller who is 30.
|
||||
We're both really into Japanese food - sushi, ramen, tempura, you name it! We also love
|
||||
temples, gardens, and want to experience traditional Japanese culture.
|
||||
Our budget is around $200 per day per person, not including accommodation which we've already booked.""",
|
||||
我计划去北京、西安和成都。行程是14天,从3月15日到3月29日。
|
||||
我叫陈莎拉,今年28岁。我和我的男朋友大卫·米勒一起旅行,他30岁。
|
||||
我们都非常喜欢中国美食 - 烤鸭、火锅、川菜,你能想到的我们都喜欢!我们也很喜欢
|
||||
寺庙、园林,想要体验传统中国文化。
|
||||
我们的预算是每人每天大约200美元,不包括住宿费用,住宿我们已经订好了。""",
|
||||
),
|
||||
Message(
|
||||
role=Role.ASSISTANT,
|
||||
content="""Perfect! With 14 days and your interests, you'll have an amazing time.
|
||||
Here's a suggested breakdown:
|
||||
content="""完美!14天的行程和你的兴趣,你们会度过美好的时光。
|
||||
以下是建议的行程安排:
|
||||
|
||||
**Tokyo (5 days - March 15-19)**
|
||||
- Day 1: Arrive, check into hotel, explore Shibuya and Harajuku
|
||||
- Day 2: Visit Senso-ji Temple in Asakusa, explore Ueno Park
|
||||
- Day 3: Tsukiji Outer Market for breakfast, Imperial Palace, Ginza shopping
|
||||
- Day 4: Day trip to Nikko (UNESCO World Heritage temples)
|
||||
- Day 5: TeamLab Borderless, Odaiba, evening in Roppongi
|
||||
**北京(5天 - 3月15-19日)**
|
||||
- 第1天:抵达,入住酒店,游览王府井和南锣鼓巷
|
||||
- 第2天:参观故宫和天安门广场,游览景山公园
|
||||
- 第3天:登长城(八达岭或慕田峪),下午游览颐和园
|
||||
- 第4天:天坛公园,北京胡同游,恭王府
|
||||
- 第5天:798艺术区,三里屯购物,晚上看京剧
|
||||
|
||||
**Kyoto (6 days - March 20-25)**
|
||||
- Day 6: Travel to Kyoto, Fushimi Inari Shrine (iconic red torii gates)
|
||||
- Day 7: Arashiyama Bamboo Grove, Tenryu-ji Temple, Monkey Park
|
||||
- Day 8: Kinkaku-ji (Golden Pavilion), Ryoan-ji rock garden
|
||||
- Day 9: Gion district, traditional tea ceremony, Kiyomizu-dera Temple
|
||||
- Day 10: Day trip to Nara (deer park, Todai-ji Temple)
|
||||
- Day 11: Philosopher's Path, Ginkaku-ji (Silver Pavilion), Heian Shrine
|
||||
**西安(6天 - 3月20-25日)**
|
||||
- 第6天:前往西安,参观兵马俑博物馆
|
||||
- 第7天:古城墙骑行,回民街美食街
|
||||
- 第8天:大雁塔,陕西历史博物馆
|
||||
- 第9天:华清池,骊山风景区
|
||||
- 第10天:大唐芙蓉园,曲江池遗址公园
|
||||
- 第11天:小雁塔,西安博物院,书院门
|
||||
|
||||
**Osaka (3 days - March 26-28)**
|
||||
- Day 12: Travel to Osaka, Osaka Castle, Dotonbori food street
|
||||
- Day 13: Kuromon Market, Shinsaibashi shopping, Universal Studios Japan option
|
||||
- Day 14: Sumiyoshi Taisha Shrine, last-minute shopping, departure prep
|
||||
**成都(3天 - 3月26-28日)**
|
||||
- 第12天:前往成都,成都大熊猫繁育研究基地,宽窄巷子
|
||||
- 第13天:武侯祠,锦里古街,人民公园喝茶
|
||||
- 第14天:杜甫草堂,最后购物,准备返程
|
||||
|
||||
Would you like me to suggest specific restaurants or any other details?""",
|
||||
你想让我推荐具体的餐厅或其他细节吗?""",
|
||||
),
|
||||
Message(
|
||||
role=Role.USER,
|
||||
content="""\
|
||||
This looks fantastic! Yes, please suggest restaurants. We're especially interested in
|
||||
authentic local places, not touristy spots.
|
||||
Also, I should mention that David has a shellfish allergy, so we need to be careful
|
||||
about that.
|
||||
My email is sarah.chen@email.com if you want to send me a detailed PDF later.
|
||||
My phone is +1-555-0123-4567.
|
||||
We're staying at the Park Hyatt Tokyo (5 days), traditional ryokan in Kyoto called
|
||||
Yoshikawa Inn (6 days), and Hotel Granvia Osaka (3 days).""",
|
||||
这看起来太棒了!是的,请推荐餐厅。我们特别想去
|
||||
地道的本地餐馆,而不是游客聚集的地方。
|
||||
另外,我需要提醒一下,大卫对贝类过敏,所以我们需要小心
|
||||
这一点。
|
||||
我的邮箱是sarah.chen@email.com,如果你想稍后给我发送详细的PDF。
|
||||
我的电话是+1-555-0123-4567。
|
||||
我们住在北京柏悦酒店(5天),西安的传统四合院酒店
|
||||
长安客栈(6天),以及成都华尔道夫酒店(3天)。""",
|
||||
),
|
||||
Message(
|
||||
role=Role.ASSISTANT,
|
||||
content="""Excellent accommodation choices! The Yoshikawa Inn is particularly special
|
||||
- they have an amazing kaiseki restaurant.
|
||||
content="""非常棒的住宿选择!长安客栈特别有特色
|
||||
- 他们有很棒的传统陕菜餐厅。
|
||||
|
||||
**Restaurant Recommendations:**
|
||||
**餐厅推荐:**
|
||||
|
||||
**Tokyo:**
|
||||
1. **Ichiran Ramen** (Shibuya) - Private booth ramen experience, customize your bowl. """
|
||||
"""Shellfish-free option available.
|
||||
2. **Sushi Dai** (Toyosu Market) - Early morning sushi breakfast, tell them about the
|
||||
shellfish allergy """
|
||||
"""("ebi to kai rui arerugī")
|
||||
3. **Tempura Kondo** (Ginza) - Michelin-starred, vegetable-focused tempura
|
||||
4. **Narisawa** (Aoyama) - If you want a splurge, this 2-Michelin-star restaurant has
|
||||
innovative Japanese cuisine
|
||||
5. **Afuri Ramen** (Harajuku) - Yuzu-based light ramen, very refreshing
|
||||
**北京:**
|
||||
1. **四季民福** (王府井) - 正宗北京烤鸭,无贝类选项。
|
||||
2. **簋街小吃** (东直门) - 各种北京小吃,告诉店家大卫对贝类过敏
|
||||
("我对贝类过敏")
|
||||
3. **全聚德** (前门) - 百年老字号烤鸭店
|
||||
4. **大董** (工体) - 如果想奢侈一点,这家米其林餐厅有
|
||||
创新中国菜
|
||||
5. **护国寺小吃** (护国寺) - 传统北京小吃,非常地道
|
||||
|
||||
**Kyoto:**
|
||||
1. **Yoshikawa Inn's own restaurant** - Their kaiseki is exceptional, """
|
||||
"""inform them of the allergy when booking
|
||||
2. **Omen** (near Ginkaku-ji) - Handmade udon in a traditional house
|
||||
3. **Kikunoi Roan** - Kaiseki experience, they're very accommodating with allergies
|
||||
4. **Katsukura** (Sanjo) - Best tonkatsu (pork cutlet) in Kyoto
|
||||
5. **Nishiki Market** - Walk through for street food, but David should be cautious with mystery items
|
||||
**西安:**
|
||||
1. **长安客栈自己的餐厅** - 他们的陕菜很棒,
|
||||
预订时告知过敏情况
|
||||
2. **德发长** (钟楼附近) - 传统饺子宴,手工制作
|
||||
3. **老孙家** - 羊肉泡馍体验,对过敏很照顾
|
||||
4. **樊记肉夹馍** (竹笆市) - 西安最好的肉夹馍
|
||||
5. **回民街** - 逛街品尝美食,但大卫要小心未知食材
|
||||
|
||||
**Osaka:**
|
||||
1. **Harukoma Sushi** (Tenjinbashisuji) - Conveyor belt sushi, excellent quality, affordable
|
||||
2. **Fukutaro** (Dotonbori) - Okonomiyaki specialists, no shellfish in standard recipes
|
||||
3. **Matsusakagyu Yakiniku M** (Hozenji Yokocho) - High-end yakiniku (grilled beef)
|
||||
4. **Ichiran Ramen** (Dotonbori branch) - Consistent quality
|
||||
5. **Kuromon Market** - Various stalls, ask vendors about ingredients
|
||||
**成都:**
|
||||
1. **蜀九香** (天府广场) - 火锅,品质优良,价格实惠
|
||||
2. **钟水饺** (春熙路) - 传统川菜小吃,标准菜谱无贝类
|
||||
3. **马旺子** (宽窄巷子) - 高端川菜
|
||||
4. **陈麻婆豆腐** (西玉龙街) - 正宗麻婆豆腐
|
||||
5. **锦里** - 各种小吃摊,询问摊主食材
|
||||
|
||||
**Important Allergy Notes:**
|
||||
- Learn this phrase: "Ebi to kai rui arerugī ga arimasu"
|
||||
(I have a shellfish allergy)
|
||||
- Many broths use shellfish stock, always ask
|
||||
- Carry allergy cards in Japanese
|
||||
- Download Google Translate offline for emergency communication
|
||||
**重要过敏提示:**
|
||||
- 学习这个短语:"我对贝类过敏"
|
||||
- 许多汤底使用海鲜高汤,一定要询问
|
||||
- 携带中文过敏卡片
|
||||
- 下载离线翻译应用以备紧急沟通
|
||||
|
||||
For your trip planning, I'll note: Sarah Chen (sarah.chen@email.com, +1-555-0123-4567)
|
||||
and David Miller, March 15-29 Japan trip.""",
|
||||
为你的行程规划,我记下:陈莎拉(sarah.chen@email.com,+1-555-0123-4567)
|
||||
和大卫·米勒,3月15-29日中国之旅。""",
|
||||
),
|
||||
Message(
|
||||
role=Role.USER,
|
||||
content="""\
|
||||
Thank you so much! A few more questions:
|
||||
1. What's the best way to get from Tokyo to Kyoto? We heard about the JR Pass.
|
||||
2. Should we rent a pocket WiFi or buy a SIM card?
|
||||
3. Any cultural etiquette we should be aware of?
|
||||
4. David wants to know if there are good bars or nightlife spots. He loves whisky.
|
||||
非常感谢!还有几个问题:
|
||||
1. 从北京到西安最好的交通方式是什么?我们听说高铁很方便。
|
||||
2. 我们应该租随身WiFi还是买SIM卡?
|
||||
3. 有什么文化礼仪需要注意的吗?
|
||||
4. 大卫想知道有没有好的酒吧或夜生活场所。他喜欢白酒。
|
||||
""",
|
||||
),
|
||||
Message(
|
||||
role=Role.ASSISTANT,
|
||||
content="""Great questions! Let me address each:
|
||||
content="""很好的问题!让我逐一解答:
|
||||
|
||||
**1. Transportation:**
|
||||
Absolutely get the JR Pass! For your 14-day trip, get the 14-day pass (around ¥47,250/$315 per person).
|
||||
- Tokyo to Kyoto: Shinkansen (bullet train), 2.5 hours, covered by JR Pass
|
||||
- Kyoto to Osaka: 30 minutes by train, also covered
|
||||
- The pass also covers JR trains in cities and the Nikko trip
|
||||
- Order it before leaving the US, pick up at airport upon arrival
|
||||
**1. 交通:**
|
||||
绝对要坐高铁!对于你的14天行程,高铁是最佳选择。
|
||||
- 北京到西安:高铁,约4.5-5小时,二等座约500元/人
|
||||
- 西安到成都:高铁,约3-4小时,二等座约260元/人
|
||||
- 也可以提前在12306网站或携程上预订
|
||||
- 需要护照信息购票,提前预订可以拿到更好的价格
|
||||
|
||||
**2. Internet:**
|
||||
I recommend renting pocket WiFi over SIM card because:
|
||||
- You can share it (both Sarah and David can connect)
|
||||
- No need to swap your phone SIM
|
||||
- Rent from companies like Japan Wireless or Ninja WiFi
|
||||
- Reserve online, pick up at Narita/Haneda airport
|
||||
- Cost: around $8-10/day for unlimited data
|
||||
- Return at airport when you leave
|
||||
**2. 网络:**
|
||||
我建议租随身WiFi而不是SIM卡,因为:
|
||||
- 可以共享(莎拉和大卫都可以连接)
|
||||
- 不需要更换手机SIM卡
|
||||
- 可以从携程或漫游超人等公司租赁
|
||||
- 在线预订,机场取货
|
||||
- 费用:约10-15元/天,无限流量
|
||||
- 离开时在机场归还
|
||||
|
||||
**3. Cultural Etiquette:**
|
||||
- Take off shoes when entering homes, some restaurants, temples (you'll see slippers at entrance)
|
||||
- Don't tip - it can be considered insulting
|
||||
- At temples: bow before entering, don't take photos inside unless permitted
|
||||
- On trains: silent mode on phones, no phone calls
|
||||
- Eating: slurping noodles is okay (shows appreciation), don't stick chopsticks upright in rice
|
||||
- Onsen (if your ryokan has one): shower completely before entering communal bath, no swimwear
|
||||
- Walking: don't eat while walking, finish at the stall or sit down
|
||||
- Trash: carry your trash with you, public bins are rare
|
||||
**3. 文化礼仪:**
|
||||
- 进入家庭、一些餐厅、寺庙时脱鞋(入口处会看到拖鞋)
|
||||
- 在寺庙:进入前鞠躬,除非允许否则不要在室内拍照
|
||||
- 地铁上:手机静音,避免大声通话
|
||||
- 用餐:吃面条发出声音是可以的,不要把筷子插在米饭里
|
||||
- 付款:现在移动支付很普及,但准备一些现金总是好的
|
||||
- 垃圾:将垃圾带在身边,找到垃圾桶再丢弃
|
||||
|
||||
**4. Bars & Whisky for David:**
|
||||
**4. 酒吧和白酒(给大卫):**
|
||||
|
||||
**Tokyo:**
|
||||
1. **Zoetrope** (Shinjuku) - 300+ types of Japanese whisky, cozy atmosphere
|
||||
2. **Bar Benfiddich** (Shinjuku) - Award-winning cocktail bar, house-made bitters
|
||||
3. **Mori Bar** (Ginza) - Classic Japanese bar, extensive whisky collection
|
||||
4. **Golden Gai** (Shinjuku) - Alley with 200+ tiny bars, unique atmosphere
|
||||
**北京:**
|
||||
1. **三里屯酒吧街** - 各种酒吧,国际氛围
|
||||
2. **Mesh** (三里屯) - 高端鸡尾酒吧
|
||||
3. **京A** - 精酿啤酒,也有白酒选择
|
||||
4. **南锣鼓巷** - 小酒吧聚集地,独特氛围
|
||||
|
||||
**Kyoto:**
|
||||
1. **Sake Bar Yoramu** - Also has excellent whisky selection
|
||||
2. **Rocking Bar Ing** - Relaxed atmosphere, good whisky menu
|
||||
3. **Bar K6** (Kawaramachi) - Intimate whisky bar
|
||||
**西安:**
|
||||
1. **德福巷酒吧街** - 各种酒吧和夜店
|
||||
2. **1935酒吧** - 轻松氛围,好的酒单
|
||||
3. **回民街** - 晚上也有小酒馆
|
||||
|
||||
**Osaka:**
|
||||
1. **Bar Nayuta** (Namba) - World-class whisky bar
|
||||
2. **Moonshine Karaoke Bar** (Dotonbori) - If you want karaoke with drinks
|
||||
3. **Bar Americana** - Extensive bourbon and whisky selection
|
||||
**成都:**
|
||||
1. **兰桂坊** (锦江区) - 世界级酒吧街
|
||||
2. **小酒馆** (玉林) - 如果想要有音乐的酒吧
|
||||
3. **九眼桥酒吧街** - 各种酒吧选择
|
||||
|
||||
**Whisky Shopping:**
|
||||
- **Tanakaya** (Tokyo) - Huge selection of Japanese whisky
|
||||
- **Liquors Hasegawa** (Tokyo Station) - Great for last-minute bottles before flying home
|
||||
- Note: Japanese whisky (Yamazaki, Hibiki, Hakushu) can be expensive even in Japan due to global demand
|
||||
**白酒购物:**
|
||||
- **北京王府井** - 大型白酒专卖店
|
||||
- **机场免税店** - 回家前购买白酒的好地方
|
||||
- 注意:茅台、五粮液等高端白酒价格较高
|
||||
|
||||
Do you need help with anything else for your trip? Train routes, more specific timing, or """
|
||||
"""budget breakdown?""",
|
||||
你的行程还需要其他帮助吗?火车路线、具体时间安排或"""
|
||||
"""预算明细?""",
|
||||
),
|
||||
Message(
|
||||
role=Role.USER,
|
||||
content="""\
|
||||
This is incredibly helpful! One more thing - we want to attend a traditional tea
|
||||
ceremony in Kyoto. Can you recommend where to do this?
|
||||
Also, what should we pack? I know March can be unpredictable weather-wise.
|
||||
Oh, and my birthday is on March 22nd - any special restaurant recommendation for
|
||||
that evening? It'll be our 3rd anniversary too!
|
||||
David's credit card is Visa ending in 4892, and mine is Mastercard ending in 7651
|
||||
- will these work everywhere in Japan?""",
|
||||
这真的很有帮助!还有一件事 - 我们想在西安参加一个传统茶艺
|
||||
体验。你能推荐去哪里吗?
|
||||
另外,我们应该打包什么?我知道三月的天气可能不太稳定。
|
||||
哦,我的生日是3月22日 - 那天晚上有什么特别的餐厅推荐吗?
|
||||
那也是我们的3周年纪念日!
|
||||
大卫的信用卡是Visa尾号4892,我的是Mastercard尾号7651
|
||||
- 这些卡在中国到处都能用吗?""",
|
||||
),
|
||||
Message(
|
||||
role=Role.ASSISTANT,
|
||||
content="""Wonderful questions! And happy early birthday & anniversary!
|
||||
content="""很棒的问题!提前祝你生日快乐和纪念日快乐!
|
||||
|
||||
**Tea Ceremony Experiences in Kyoto:**
|
||||
**西安茶艺体验:**
|
||||
|
||||
1. **Camellia Tea Ceremony** (Higashiyama) - Highly recommended!
|
||||
- English-speaking host
|
||||
- Includes kimono wearing experience
|
||||
- Small groups (max 6 people)
|
||||
- 2-hour experience, around ¥6,000 per person
|
||||
- Book online in advance: camelliatearoom.com
|
||||
1. **大唐茶苑** (大雁塔附近) - 强烈推荐!
|
||||
- 中英文主持人
|
||||
- 包含汉服体验
|
||||
- 小团体(最多6人)
|
||||
- 2小时体验,约300元/人
|
||||
- 提前在线预订
|
||||
|
||||
2. **En Tea Ceremony** (near Kiyomizu-dera)
|
||||
- Traditional machiya (townhouse) setting
|
||||
- Very authentic, less touristy
|
||||
- Private ceremony option available
|
||||
- Around ¥5,500 per person
|
||||
2. **茶缘轩** (古城墙附近)
|
||||
- 传统四合院环境
|
||||
- 非常地道,游客少
|
||||
- 可预订私人茶艺
|
||||
- 约280元/人
|
||||
|
||||
3. **Wak Japan** (Gion area)
|
||||
- Combines tea ceremony with flower arrangement or calligraphy
|
||||
- Good for couples
|
||||
- Around ¥8,000 per person for combined experience
|
||||
3. **唐韵茶艺** (回民街区域)
|
||||
- 结合茶艺和书法体验
|
||||
- 适合情侣
|
||||
- 组合体验约400元/人
|
||||
|
||||
I recommend booking Day 9 (March 22nd) morning for the tea ceremony, then evening for your special dinner!
|
||||
我建议在第9天(3月22日)上午预订茶艺体验,然后晚上吃特别晚餐!
|
||||
|
||||
**March Weather & Packing:**
|
||||
March in Japan: transitioning from winter to spring
|
||||
- Temperature: 8-15°C (46-59°F)
|
||||
- Cherry blossoms might just start blooming late March (you might catch early bloomers!)
|
||||
**三月天气和打包清单:**
|
||||
中国三月:从冬天过渡到春天
|
||||
- 气温:北京5-15°C,西安8-18°C,成都10-20°C
|
||||
- 可能会看到早春的花朵盛开!
|
||||
|
||||
**Pack:**
|
||||
- Layering clothes: light sweater, cardigan, light jacket
|
||||
- One warmer jacket for evenings
|
||||
- Comfortable walking shoes (you'll walk 15,000+ steps daily)
|
||||
- Umbrella (March has occasional rain)
|
||||
- Slip-on shoes (easier for temple visits)
|
||||
- Nice outfit for fancy restaurants
|
||||
- Power adapter (Japan uses Type A plugs, 100V)
|
||||
- Portable charger for phones
|
||||
- Small day backpack
|
||||
**打包:**
|
||||
- 分层衣物:轻薄毛衣、开衫、轻便外套
|
||||
- 一件保暖外套(晚上用)
|
||||
- 舒适的步行鞋(每天要走15,000+步)
|
||||
- 雨伞(三月偶尔有雨)
|
||||
- 方便穿脱的鞋子(方便寺庙参观)
|
||||
- 高档餐厅用的正装
|
||||
- 电源转换器(中国使用A型插头,220V)
|
||||
- 手机充电宝
|
||||
- 小型日用背包
|
||||
|
||||
**Birthday & Anniversary Dinner - March 22nd:**
|
||||
**生日和纪念日晚餐 - 3月22日:**
|
||||
|
||||
For such a special occasion in Kyoto, I highly recommend:
|
||||
对于在西安这样特殊的场合,我强烈推荐:
|
||||
|
||||
**Kikunoi Honten** (Main Branch) - 3 Michelin Stars
|
||||
- Ultimate kaiseki experience
|
||||
- Beautiful traditional setting with garden views
|
||||
- Multi-course seasonal menu
|
||||
- Reserve 1-2 months in advance
|
||||
- Budget: ¥25,000-40,000 per person (worth it for anniversary!)
|
||||
- Inform them of: shellfish allergy, birthday, anniversary
|
||||
- They'll often add special touches for celebrations
|
||||
**长安壹号** - 高端陕菜
|
||||
- 顶级陕菜体验
|
||||
- 美丽的传统环境和园林景观
|
||||
- 多道式季节菜单
|
||||
- 提前1-2个月预订
|
||||
- 预算:每人1000-1500元(值得纪念日!)
|
||||
- 告知他们:贝类过敏、生日、纪念日
|
||||
- 他们通常会为庆祝活动增添特别惊喜
|
||||
|
||||
**Alternative (still amazing):**
|
||||
**Hyotei** - 3 Michelin Stars
|
||||
- Historic (400 years old)
|
||||
- Beautiful garden setting
|
||||
- Breakfast kaiseki is famous, but dinner is exceptional
|
||||
- Similar price range
|
||||
**备选(同样很棒):**
|
||||
**西安饭庄** - 百年老店
|
||||
- 历史悠久(百年历史)
|
||||
- 美丽的园林环境
|
||||
- 传统陕菜,晚餐特别精致
|
||||
- 相似价格范围
|
||||
|
||||
**More Accessible but Still Special:**
|
||||
**Gion Karyo**
|
||||
- 1 Michelin Star
|
||||
- Beautiful Gion location
|
||||
- Around ¥15,000-20,000 per person
|
||||
- Easier to book
|
||||
**更实惠但仍然特别:**
|
||||
**德发长**
|
||||
- 著名饺子宴
|
||||
- 市中心位置
|
||||
- 约每人500-800元
|
||||
- 更容易预订
|
||||
|
||||
**Birthday Cake:**
|
||||
Order a birthday cake from **Malebranche** (Kyoto) - they do beautiful matcha cakes.
|
||||
Ask your ryokan to keep it refrigerated and present it after you return from dinner!
|
||||
**生日蛋糕:**
|
||||
从**北京稻香村**或当地蛋糕店订一个生日蛋糕。
|
||||
请你的酒店冷藏保管,晚餐后回来时呈上!
|
||||
|
||||
**Credit Cards:**
|
||||
- Major hotels and restaurants: Visa & Mastercard widely accepted
|
||||
(Sarah's Mastercard 7651 and David's Visa 4892 should work fine)
|
||||
- However, Japan is still heavily CASH-based
|
||||
- Bring cash for:
|
||||
- Small restaurants
|
||||
- Temples/shrines
|
||||
- Street food
|
||||
- Some shops
|
||||
- Taxis
|
||||
- Vending machines
|
||||
**信用卡:**
|
||||
- 大型酒店和餐厅:Visa和Mastercard广泛接受
|
||||
(莎拉的Mastercard 7651和大卫的Visa 4892应该可以用)
|
||||
- 但是,中国现在主要使用移动支付(微信支付、支付宝)
|
||||
- 建议准备现金用于:
|
||||
- 小餐馆
|
||||
- 寺庙/景点
|
||||
- 街边小吃
|
||||
- 一些商店
|
||||
- 出租车
|
||||
- 自动售货机
|
||||
|
||||
**ATM Tips:**
|
||||
- 7-Eleven ATMs work with foreign cards (open 24/7)
|
||||
- Japan Post Bank ATMs also reliable
|
||||
- Withdraw larger amounts to minimize fees
|
||||
- Keep ¥10,000-20,000 ($70-140) cash daily
|
||||
- Your banks: notify them of Japan travel to avoid card blocks
|
||||
**ATM提示:**
|
||||
- 中国银行ATM支持外国卡(需要银联标识)
|
||||
- 建议一次提取较大金额以减少手续费
|
||||
- 每天保持500-1000元现金
|
||||
- 你的银行:通知他们中国旅行计划以避免卡被冻结
|
||||
|
||||
**Pre-Trip Checklist:**
|
||||
✅ JR Pass ordered
|
||||
✅ Pocket WiFi reserved
|
||||
✅ Kikunoi dinner reservation (book NOW for March 22nd)
|
||||
✅ Tea ceremony booking
|
||||
✅ Notify banks: Sarah (MC 7651), David (Visa 4892)
|
||||
✅ Travel insurance
|
||||
✅ Download offline maps
|
||||
✅ Book airport transfers
|
||||
✅ Birthday cake ordered
|
||||
**出行前清单:**
|
||||
✅ 高铁票预订
|
||||
✅ 随身WiFi预订
|
||||
✅ 长安壹号晚餐预订(现在就订3月22日的!)
|
||||
✅ 茶艺预订
|
||||
✅ 通知银行:莎拉(MC 7651),大卫(Visa 4892)
|
||||
✅ 旅行保险
|
||||
✅ 下载离线地图
|
||||
✅ 预订机场接送
|
||||
✅ 订生日蛋糕
|
||||
|
||||
Would you like me to help with a day-by-day detailed schedule with specific times and addresses?""",
|
||||
你想要我帮助制定一个详细的每日行程,包括具体时间和地址吗?""",
|
||||
),
|
||||
Message(
|
||||
role=Role.USER,
|
||||
content="""\
|
||||
Yes! A detailed daily schedule would be amazing. Can you include specific addresses and opening hours?
|
||||
Also, we're thinking of proposing to our friends to join us - my best friend Jessica Wang and her """
|
||||
"""husband Tom. What would change if we're traveling as a group of 4?""",
|
||||
太好了!详细的每日行程表会很棒。你能包括具体地址和开放时间吗?
|
||||
另外,我们在考虑邀请朋友一起 - 我最好的朋友杰西卡·王和她的"""
|
||||
"""丈夫汤姆。如果我们四个人一起旅行会有什么变化?""",
|
||||
),
|
||||
Message(
|
||||
role=Role.ASSISTANT,
|
||||
content="""\
|
||||
Absolutely! I'll create a detailed schedule. Traveling as a group of 4 (Sarah & David + Jessica & """
|
||||
"""Tom) actually has some advantages:
|
||||
当然!我会制定详细的行程。作为四人团(莎拉和大卫 + 杰西卡和"""
|
||||
"""汤姆)旅行实际上有一些优势:
|
||||
|
||||
**Group of 4 Benefits:**
|
||||
✅ Split pocket WiFi cost (1 device for 4 people)
|
||||
✅ Share taxi costs (taxis fit 4 people)
|
||||
✅ Some restaurants have minimum 4-person set menus
|
||||
✅ Private tea ceremony for your group
|
||||
✅ Better for group photos!
|
||||
**四人团的好处:**
|
||||
✅ 分摊随身WiFi费用(1台设备供4人使用)
|
||||
✅ 分摊出租车费用(出租车正好坐4人)
|
||||
✅ 一些餐厅有4人起的套餐
|
||||
✅ 你们团队的私人茶艺体验
|
||||
✅ 更适合团体照片!
|
||||
|
||||
**Considerations:**
|
||||
- Book restaurants for 4 people
|
||||
- Some tiny bars in Golden Gai might not fit all
|
||||
- Reserve 2 rooms/apartments when needed
|
||||
- Coordinate meeting points if you split up
|
||||
**注意事项:**
|
||||
- 为4人预订餐厅
|
||||
- 一些小酒吧可能容纳不了所有人
|
||||
- 需要时预订2个房间/公寓
|
||||
- 如果分开行动要协调碰面地点
|
||||
|
||||
**DETAILED 14-DAY SCHEDULE WITH ADDRESSES:**
|
||||
**详细的14天行程(包含地址):**
|
||||
|
||||
**DAY 1 - March 15 (Friday) - TOKYO ARRIVAL**
|
||||
**第1天 - 3月15日(周五)- 北京到达**
|
||||
|
||||
*Morning/Afternoon:*
|
||||
- Arrive Narita/Haneda Airport
|
||||
- Pick up: JR Pass, Pocket WiFi
|
||||
- Exchange yen at airport (recommend ¥50,000+ per person)
|
||||
- Take train to hotel: Park Hyatt Tokyo
|
||||
📍 3-7-1-2 Nishishinjuku, Shinjuku-ku, Tokyo 163-1055
|
||||
🚇 Shinjuku Station → Oedo Line to Tochomae Station (5 min walk)
|
||||
*上午/下午:*
|
||||
- 到达北京首都国际机场
|
||||
- 取:随身WiFi
|
||||
- 在机场兑换人民币(建议每人3000元+)
|
||||
- 乘车到酒店:北京柏悦酒店
|
||||
📍 北京市朝阳区建国门外大街2号
|
||||
🚇 从机场乘机场快轨到东直门站,换乘地铁(约1小时)
|
||||
|
||||
*Evening (6:00 PM - 9:00 PM):*
|
||||
- Check in, rest, freshen up
|
||||
- Dinner: **Omoide Yokocho** (Memory Lane)
|
||||
📍 1 Chome Nishishinjuku, Shinjuku-ku, Tokyo
|
||||
🕒 Open till midnight
|
||||
💴 ¥2,000-3,000/person
|
||||
- Narrow alley with small yakitori stands
|
||||
- Cash only, very local atmosphere
|
||||
- Ask about shellfish ("kai rui") in skewers
|
||||
*晚上(下午6:00 - 9:00):*
|
||||
- 入住,休息,整理
|
||||
- 晚餐:**簋街小吃街**
|
||||
📍 北京市东城区东直门内大街
|
||||
🕒 营业至深夜
|
||||
💴 每人100-150元
|
||||
- 各种小吃摊位
|
||||
- 多数店家只收现金或移动支付
|
||||
- 询问贝类("有没有海鲜")
|
||||
|
||||
*Night:*
|
||||
- Walk around Shinjuku, see the night lights
|
||||
- Convenience store snacks (7-Eleven/Family Mart)
|
||||
- Early sleep (jet lag)
|
||||
*夜间:*
|
||||
- 在东直门周围散步,看夜景
|
||||
- 便利店小吃(7-11/全家)
|
||||
- 早点睡(时差调整)
|
||||
|
||||
---
|
||||
|
||||
**DAY 2 - March 16 (Saturday) - ASAKUSA & UENO**
|
||||
**第2天 - 3月16日(周六)- 故宫和天安门**
|
||||
|
||||
*Morning (9:00 AM - 12:00 PM):*
|
||||
- Breakfast at hotel or nearby bakery
|
||||
- 🚇 Train to Asakusa (30 min from Shinjuku)
|
||||
*上午(9:00 AM - 12:00 PM):*
|
||||
- 在酒店或附近面包店吃早餐
|
||||
- 🚇 地铁到天安门(从酒店约30分钟)
|
||||
|
||||
- **Senso-ji Temple**
|
||||
📍 2-3-1 Asakusa, Taito-ku, Tokyo
|
||||
🕒 6:00 AM - 5:00 PM (grounds always open)
|
||||
💴 Free
|
||||
- Arrive by 9:30 AM to avoid crowds
|
||||
- Walk through Kaminarimon Gate, Nakamise Shopping Street
|
||||
- Draw fortune (omikuji) - ¥100
|
||||
- Visit main hall, incense burner
|
||||
- **天安门广场**
|
||||
📍 北京市东城区天安门广场
|
||||
🕒 6:00 AM - 5:00 PM(广场全天开放)
|
||||
💴 免费
|
||||
- 早上9:30前到达以避开人群
|
||||
- 看升旗仪式(如果早起)
|
||||
- 拍照留念
|
||||
|
||||
*Lunch (12:00 PM):*
|
||||
- **Daikokuya Tempura**
|
||||
📍 1-38-10 Asakusa, Taito-ku, Tokyo
|
||||
🕒 11:00 AM - 8:30 PM (closed Mon)
|
||||
💴 ¥2,000-3,000/person
|
||||
- Famous tendon (tempura rice bowl)
|
||||
- Mention shellfish allergy to David's order
|
||||
*午餐(12:00 PM):*
|
||||
- **全聚德烤鸭**
|
||||
📍 北京市东城区前门大街30号
|
||||
🕒 11:00 AM - 2:00 PM,5:00 PM - 9:00 PM
|
||||
💴 每人200-300元
|
||||
- 著名北京烤鸭
|
||||
- 告知大卫的贝类过敏
|
||||
|
||||
*Afternoon (1:30 PM - 5:00 PM):*
|
||||
- Walk to Ueno (15 min) or train (2 stops)
|
||||
*下午(1:30 PM - 5:00 PM):*
|
||||
- 步行到故宫(15分钟)
|
||||
|
||||
- **Ueno Park**
|
||||
📍 Uenokoen, Taito-ku, Tokyo
|
||||
🕒 5:00 AM - 11:00 PM
|
||||
💴 Free (museums extra)
|
||||
- Cherry blossom trees (might see early bloomers!)
|
||||
- Visit **Tokyo National Museum** if interested
|
||||
🕒 9:30 AM - 5:00 PM (closed Mon)
|
||||
💴 ¥1,000/person
|
||||
- **故宫博物院**
|
||||
📍 北京市东城区景山前街4号
|
||||
🕒 8:30 AM - 5:00 PM(周一闭馆)
|
||||
💴 60元/人(需提前网上预订)
|
||||
- 可能看到早春的花朵!
|
||||
- 参观太和殿、乾清宫等主要宫殿
|
||||
- 大约需要3-4小时
|
||||
|
||||
- **Ameya-Yokocho Market**
|
||||
📍 4 Chome Ueno, Taito-ku, Tokyo
|
||||
- Shopping street, bargain clothes, snacks
|
||||
- **景山公园**
|
||||
📍 北京市西城区景山前街
|
||||
- 登顶俯瞰故宫全景
|
||||
|
||||
*Dinner (6:30 PM):*
|
||||
- **Ichiran Ramen Ueno**
|
||||
📍 6-11-11 Ueno, Taito-ku, Tokyo
|
||||
🕒 24 hours
|
||||
💴 ¥1,000-1,500/person
|
||||
- Individual booth experience
|
||||
- Order via vending machine (English available)
|
||||
- Customize your ramen
|
||||
*晚餐(6:30 PM):*
|
||||
- **护国寺小吃**
|
||||
📍 北京市西城区护国寺大街93号
|
||||
🕒 全天营业
|
||||
💴 每人80-100元
|
||||
- 传统北京小吃
|
||||
- 多种选择
|
||||
- 可定制你的餐食
|
||||
|
||||
*Night:*
|
||||
- Return to Shinjuku
|
||||
- Optional: **Zoetrope Whisky Bar** for David & Tom
|
||||
📍 Sankoubldg. 3F, 1-7-10 Nishi-Shinjuku, Shinjuku-ku
|
||||
🕒 6:00 PM - 12:00 AM (closed Sun)
|
||||
💴 ¥1,500-3,000/drink
|
||||
*夜间:*
|
||||
- 返回酒店
|
||||
- 可选:**三里屯酒吧街**(给大卫和汤姆)
|
||||
📍 北京市朝阳区三里屯路
|
||||
🕒 下午6:00 - 凌晨2:00
|
||||
💴 每杯60-150元
|
||||
|
||||
---
|
||||
|
||||
**DAY 3 - March 17 (Sunday) - TSUKIJI, GINZA, IMPERIAL PALACE**
|
||||
**第3天 - 3月17日(周日)- 长城和颐和园**
|
||||
|
||||
*Early Morning (5:30 AM - 8:00 AM):*
|
||||
- Wake up early!
|
||||
- **Tsukiji Outer Market**
|
||||
📍 4 Chome Tsukiji, Chuo-ku, Tokyo
|
||||
🚇 Tsukijishijo Station (Oedo Line)
|
||||
🕒 Most stalls: 5:00 AM - 2:00 PM
|
||||
*清晨(5:30 AM - 8:00 AM):*
|
||||
- 早起!
|
||||
- **慕田峪长城**
|
||||
📍 北京市怀柔区慕田峪村
|
||||
🚇 可预订包车或参加一日游(从市区约1.5小时)
|
||||
🕒 7:30 AM - 5:30 PM
|
||||
|
||||
- Breakfast at **Sushi Dai** (or Daiwa Sushi)
|
||||
📍 Inside Toyosu Market (new location)
|
||||
🕒 5:30 AM - 1:30 PM
|
||||
💴 ¥3,500-5,000/person
|
||||
⚠️ Expect 1-2 hour wait, go early!
|
||||
- Tell chef about David's shellfish allergy
|
||||
- Omakase sushi breakfast
|
||||
- 在景区附近吃早餐
|
||||
📍 景区内餐厅
|
||||
🕒 8:00 AM - 10:00 AM
|
||||
💴 每人50-80元
|
||||
⚠️ 准备好爬长城,穿舒适的鞋子!
|
||||
- 告知餐厅大卫的贝类过敏
|
||||
- 简单早餐
|
||||
|
||||
*Late Morning (9:00 AM - 12:00 PM):*
|
||||
- **Imperial Palace East Gardens**
|
||||
📍 1-1 Chiyoda, Chiyoda-ku, Tokyo
|
||||
🕒 9:00 AM - 4:30 PM (closed Mon, Fri)
|
||||
💴 Free
|
||||
- Beautiful gardens, historic site
|
||||
- 1-1.5 hour visit
|
||||
*上午(9:00 AM - 12:00 PM):*
|
||||
- **登长城**
|
||||
📍 慕田峪长城
|
||||
🕒 已开放
|
||||
💴 45元门票/人 + 缆车120元往返
|
||||
- 美丽的景色,历史遗迹
|
||||
- 约2-3小时游览
|
||||
|
||||
*Lunch (12:30 PM):*
|
||||
- **Ginza**
|
||||
📍 Ginza, Chuo-ku, Tokyo
|
||||
- Many options for lunch
|
||||
*午餐(12:30 PM):*
|
||||
- **景区餐厅**
|
||||
📍 慕田峪景区
|
||||
- 多种午餐选择
|
||||
|
||||
- **Tempura Kondo** (if you can get reservation)
|
||||
📍 Sakaguchi Bldg. 9F, 5-5-13 Ginza, Chuo-ku
|
||||
🕒 Lunch 12:00-2:00 PM, Dinner 5:30-9:00 PM (closed Sun)
|
||||
💴 Lunch ¥8,000-12,000/person
|
||||
- Reserve online or call: +81-3-5568-0923
|
||||
- 返回市区(约1.5小时车程)
|
||||
|
||||
- **Backup: Ginza Kagari Ramen**
|
||||
📍 Ginza, Chuo-ku (search exact location)
|
||||
💴 ¥1,200/person
|
||||
- Creamy chicken ramen, no shellfish
|
||||
*下午(3:00 PM - 6:00 PM):*
|
||||
- **颐和园**
|
||||
📍 北京市海淀区新建宫门路19号
|
||||
🕒 6:30 AM - 8:00 PM
|
||||
💴 30元/人(旺季)
|
||||
- 皇家园林,昆明湖
|
||||
- 十七孔桥,长廊
|
||||
- 约2-3小时游览
|
||||
|
||||
*Afternoon (2:00 PM - 6:00 PM):*
|
||||
- **Ginza Shopping**
|
||||
- UNIQLO flagship (12 floors)
|
||||
- Mitsukoshi Department Store
|
||||
- Dover Street Market (avant-garde fashion)
|
||||
- MUJI flagship
|
||||
- Window shop luxury brands
|
||||
*晚餐(6:30 PM):*
|
||||
- **四季民福烤鸭**
|
||||
📍 北京市东城区灯市口大街50号
|
||||
🕒 11:00 AM - 10:00 PM
|
||||
💴 每人150-200元
|
||||
- 北京烤鸭,清淡爽口
|
||||
|
||||
*Dinner (6:30 PM):*
|
||||
- **Afuri Ramen**
|
||||
📍 1-1-7 Ebisu, Shibuya-ku, Tokyo (Ebisu location)
|
||||
🕒 11:00 AM - 11:00 PM
|
||||
💴 ¥1,200/person
|
||||
- Yuzu-salt ramen, light and refreshing
|
||||
|
||||
*Night:*
|
||||
- Train to Shibuya for evening walk
|
||||
- See Shibuya Crossing at night
|
||||
- Return hotel
|
||||
*夜间:*
|
||||
- 返回酒店休息
|
||||
- 可选:在酒店附近散步
|
||||
|
||||
---
|
||||
|
||||
This is getting quite long! Should I continue with the rest of the days (Days 4-14)? I can also send """
|
||||
"""you this as a Google Doc or PDF if that's easier. Just need to confirm - are Jessica and Tom """
|
||||
"""definitely joining, or still maybe?
|
||||
这变得相当长了!我应该继续写剩下的天数(第4-14天)吗?我也可以"""
|
||||
"""把这个做成Google文档或PDF,如果那样更方便的话。只需要确认 - 杰西卡和汤姆"""
|
||||
"""确定要来,还是只是可能?
|
||||
|
||||
Also, does anyone have other dietary restrictions besides David's shellfish allergy? And what are """
|
||||
"""your hotel/ryokan confirmations - should I include check-in/check-out timing?""",
|
||||
另外,除了大卫的贝类过敏之外,还有其他人有饮食限制吗?你的"""
|
||||
"""酒店确认信息是什么 - 我应该包含入住/退房时间吗?""",
|
||||
),
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,231 +1,387 @@
|
|||
"""Tests for ReMeFs summary interface.
|
||||
"""ReMeFs summary接口测试。
|
||||
|
||||
This module tests the summary() method of ReMeFs class which provides
|
||||
a high-level interface for storing user's personal information into memory files.
|
||||
本模块测试ReMeFs类的summary()方法,该方法提供了
|
||||
将用户个人信息存储到记忆文件的高级接口。
|
||||
|
||||
The summary function should enable the LLM to:
|
||||
1. Extract personal information from user messages (name, preferences, requirements)
|
||||
2. Call file system tools (WriteTool, EditTool) to store this information
|
||||
3. Maintain personalized memory for future conversations
|
||||
summary函数应该能够让LLM:
|
||||
1. 从用户消息中提取个人信息(姓名、偏好、需求)
|
||||
2. 调用文件系统工具(WriteTool、EditTool)存储这些信息
|
||||
3. 为未来的对话维护个性化记忆
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
from reme import ReMeFs
|
||||
from reme.core.enumeration import Role
|
||||
from reme.core.schema import Message
|
||||
|
||||
|
||||
def print_messages(messages: list[Message], title: str = "Messages", max_content_len: int = 150):
|
||||
"""Print messages with their role and content.
|
||||
def print_messages(messages: list[Message], title: str = "消息列表", max_content_len: int = 150):
|
||||
"""打印消息及其角色和内容。
|
||||
|
||||
Args:
|
||||
messages: List of messages to print
|
||||
title: Title for the message list
|
||||
max_content_len: Maximum content length to display (truncate if longer)
|
||||
messages: 要打印的消息列表
|
||||
title: 消息列表的标题
|
||||
max_content_len: 显示的最大内容长度(超过则截断)
|
||||
"""
|
||||
print(f"\n{title}: (count: {len(messages)})")
|
||||
print(f"\n{title}:(共 {len(messages)} 条)")
|
||||
print("-" * 80)
|
||||
for i, msg in enumerate(messages):
|
||||
content = str(msg.content)
|
||||
if len(content) > max_content_len:
|
||||
content = content[:max_content_len] + "..."
|
||||
print(f" [{i}] {msg.role.value:10s}: {content}")
|
||||
print(f" [{i}] {msg.role.value:10s} [{msg.time_created}]: {content}")
|
||||
print("-" * 80)
|
||||
|
||||
|
||||
def print_result(result: dict, title: str = "RESULT"):
|
||||
"""Print the result of summary() call.
|
||||
def print_result(result: dict, title: str = "执行结果"):
|
||||
"""打印summary()调用的结果。
|
||||
|
||||
Args:
|
||||
result: Result dictionary from summary()
|
||||
title: Title for the result section
|
||||
result: summary()返回的结果字典
|
||||
title: 结果部分的标题
|
||||
"""
|
||||
print(f"\n{'=' * 80}")
|
||||
print(f"{title}:")
|
||||
print(f" success: {result.get('success')}")
|
||||
print(f" skipped: {result.get('skipped', False)}")
|
||||
print(f"{title}:")
|
||||
print(f" 成功: {result.get('success')}")
|
||||
print(f" 跳过: {result.get('skipped', False)}")
|
||||
|
||||
tools_used = result.get("tools", [])
|
||||
print(f" tools_called: {len(tools_used)}")
|
||||
print(f" 调用工具数: {len(tools_used)}")
|
||||
|
||||
if tools_used:
|
||||
print("\n Tool Usage Details:")
|
||||
print("\n 工具使用详情:")
|
||||
for i, tool in enumerate(tools_used):
|
||||
print(f" [{i}] Tool: {tool.name} Arguments: {tool.tool_call.arguments}")
|
||||
print(f" [{i}] 工具: {tool.name} 参数: {tool.tool_call.arguments}")
|
||||
|
||||
answer = result.get("answer", "")
|
||||
if answer:
|
||||
answer_preview = answer[:300] + "..." if len(answer) > 300 else answer
|
||||
print(f"\n answer: {answer_preview}")
|
||||
print(f"\n 回答: {answer_preview}")
|
||||
|
||||
print(f"{'=' * 80}")
|
||||
|
||||
|
||||
def create_personal_info_introduction() -> list[Message]:
|
||||
"""Create a conversation where user introduces personal information."""
|
||||
return [
|
||||
def delete_memory_file(working_dir: str, date: str):
|
||||
"""删除指定日期的记忆文件。
|
||||
|
||||
Args:
|
||||
working_dir: 工作目录路径
|
||||
date: 日期格式 %Y-%m-%d
|
||||
"""
|
||||
memory_path = Path(working_dir) / "memory" / f"{date}.md"
|
||||
if memory_path.exists():
|
||||
memory_path.unlink()
|
||||
print(f"✓ 已删除记忆文件:{memory_path}")
|
||||
else:
|
||||
print(f" 记忆文件不存在:{memory_path}")
|
||||
|
||||
|
||||
def check_memory_file(working_dir: str, date: str):
|
||||
"""检查记忆文件是否存在并打印其内容。
|
||||
|
||||
Args:
|
||||
working_dir: 工作目录路径
|
||||
date: 日期格式 %Y-%m-%d
|
||||
"""
|
||||
memory_path = Path(working_dir) / "memory" / f"{date}.md"
|
||||
if memory_path.exists():
|
||||
print(f"\n✓ 记忆文件存在:{memory_path}")
|
||||
print(f"内容预览:")
|
||||
print("-" * 80)
|
||||
content = memory_path.read_text()
|
||||
print(content[:500] + ("..." if len(content) > 500 else ""))
|
||||
print("-" * 80)
|
||||
else:
|
||||
print(f"\n✗ 记忆文件不存在:{memory_path}")
|
||||
|
||||
|
||||
async def test_summary_first_write():
|
||||
"""测试1:删除记忆文件,首次summary应该直接写入。
|
||||
|
||||
测试当指定日期没有记忆文件时,summary会创建新文件并直接写入信息。
|
||||
"""
|
||||
print("\n" + "=" * 80)
|
||||
print("测试1:Summary - 首次写入(直接写入)")
|
||||
print("=" * 80)
|
||||
|
||||
test_date = "2024-01-15"
|
||||
working_dir = ".reme_test1"
|
||||
|
||||
# 测试前清理
|
||||
if Path(working_dir).exists():
|
||||
shutil.rmtree(working_dir)
|
||||
|
||||
reme_fs = ReMeFs(enable_logo=False, working_dir=working_dir, vector_store=None)
|
||||
await reme_fs.start()
|
||||
|
||||
# 确保记忆文件已删除
|
||||
delete_memory_file(working_dir, test_date)
|
||||
|
||||
# 创建time_created与日期对齐的消息
|
||||
messages = [
|
||||
Message(
|
||||
role=Role.USER,
|
||||
content="Hi! My name is Alice, and I'm a software engineer.",
|
||||
content="你好!我叫小明,是一名软件工程师。",
|
||||
time_created=f"{test_date} 09:30:00",
|
||||
),
|
||||
Message(
|
||||
role=Role.ASSISTANT,
|
||||
content="Nice to meet you, Alice! How can I help you today?",
|
||||
content="很高兴认识你,小明!有什么我可以帮你的吗?",
|
||||
time_created=f"{test_date} 09:30:05",
|
||||
),
|
||||
Message(
|
||||
role=Role.USER,
|
||||
content=(
|
||||
"I love Python programming and working on AI projects. "
|
||||
"I also enjoy reading sci-fi novels in my free time."
|
||||
),
|
||||
),
|
||||
Message(
|
||||
role=Role.ASSISTANT,
|
||||
content="That's great! Python and AI are exciting fields. What kind of AI projects are you interested in?",
|
||||
content="我热爱Python编程,喜欢做AI项目。空闲时间我还喜欢看科幻小说。",
|
||||
time_created=f"{test_date} 09:31:00",
|
||||
),
|
||||
]
|
||||
|
||||
print_messages(messages, "输入消息", max_content_len=200)
|
||||
|
||||
def create_detailed_profile_conversation() -> list[Message]:
|
||||
"""Create a comprehensive conversation with multiple personal details."""
|
||||
return [
|
||||
print("\n预期结果:")
|
||||
print(" - summary前记忆文件不存在")
|
||||
print(" - LLM应该调用WriteTool创建新的记忆文件")
|
||||
print(" - 记忆文件应该包含用户的姓名、职业和兴趣")
|
||||
|
||||
result = await reme_fs.summary(
|
||||
messages=messages,
|
||||
date=test_date,
|
||||
)
|
||||
|
||||
print_result(result, "SUMMARY执行结果")
|
||||
check_memory_file(working_dir, test_date)
|
||||
|
||||
await reme_fs.close()
|
||||
|
||||
# 测试后清理
|
||||
if Path(working_dir).exists():
|
||||
shutil.rmtree(working_dir)
|
||||
|
||||
|
||||
async def test_summary_complementary_info():
|
||||
"""测试2:删除记忆文件,两次summary补充信息且不冲突。
|
||||
|
||||
测试当进行两次summary时,第二次summary添加补充信息而不产生冲突。
|
||||
"""
|
||||
print("\n" + "=" * 80)
|
||||
print("测试2:Summary - 补充信息(无冲突)")
|
||||
print("=" * 80)
|
||||
|
||||
test_date = "2024-02-20"
|
||||
working_dir = ".reme_test2"
|
||||
|
||||
# 测试前清理
|
||||
if Path(working_dir).exists():
|
||||
shutil.rmtree(working_dir)
|
||||
|
||||
reme_fs = ReMeFs(enable_logo=False, working_dir=working_dir, vector_store=None)
|
||||
await reme_fs.start()
|
||||
|
||||
# 确保记忆文件已删除
|
||||
delete_memory_file(working_dir, test_date)
|
||||
|
||||
# 第一批消息
|
||||
messages1 = [
|
||||
Message(
|
||||
role=Role.USER,
|
||||
content=(
|
||||
"Let me tell you about myself. My name is Charlie Chen, " "I'm a data scientist based in San Francisco."
|
||||
),
|
||||
content="你好!我叫小红,是一名数据科学家。",
|
||||
time_created=f"{test_date} 10:00:00",
|
||||
),
|
||||
Message(
|
||||
role=Role.ASSISTANT,
|
||||
content="Hello Charlie! It's nice to meet you. Tell me more about your work.",
|
||||
content="你好小红!很高兴认识你。",
|
||||
time_created=f"{test_date} 10:00:05",
|
||||
),
|
||||
Message(
|
||||
role=Role.USER,
|
||||
content=(
|
||||
"I specialize in machine learning and natural language processing. "
|
||||
"My favorite tools are PyTorch and Hugging Face transformers."
|
||||
),
|
||||
),
|
||||
Message(
|
||||
role=Role.ASSISTANT,
|
||||
content="Those are excellent tools for NLP work. What kind of projects do you work on?",
|
||||
),
|
||||
Message(
|
||||
role=Role.USER,
|
||||
content="I work on chatbots and sentiment analysis. Outside of work, I love hiking and photography.",
|
||||
),
|
||||
Message(
|
||||
role=Role.ASSISTANT,
|
||||
content="That's a great combination of technical and creative interests!",
|
||||
),
|
||||
Message(
|
||||
role=Role.USER,
|
||||
content=(
|
||||
"Oh, and one more thing - please be more assertive when you think "
|
||||
"I'm making a mistake. I want you to challenge my ideas."
|
||||
),
|
||||
),
|
||||
Message(
|
||||
role=Role.ASSISTANT,
|
||||
content="Absolutely, I'll make sure to provide critical feedback when needed.",
|
||||
content="我主要从事机器学习和深度学习模型的研究工作。",
|
||||
time_created=f"{test_date} 10:01:00",
|
||||
),
|
||||
]
|
||||
|
||||
print_messages(messages1, "第一批消息", max_content_len=200)
|
||||
|
||||
async def test_summary_personal_info_storage():
|
||||
"""Test summary() stores user's personal information to memory.
|
||||
|
||||
User introduces name and basic info.
|
||||
Expects: LLM should call WriteTool to store this information
|
||||
"""
|
||||
print("\n" + "=" * 80)
|
||||
print("TEST 1: Summary - Personal Information Storage")
|
||||
print("=" * 80)
|
||||
|
||||
reme_fs = ReMeFs(enable_logo=False)
|
||||
await reme_fs.start()
|
||||
|
||||
messages = create_personal_info_introduction()
|
||||
print_messages(messages, "INPUT MESSAGES", max_content_len=200)
|
||||
|
||||
print("\nParameters:")
|
||||
print(" version: default")
|
||||
print(" Expected: LLM should call WriteTool to save user's name and interests")
|
||||
|
||||
result = await reme_fs.summary(
|
||||
messages=messages,
|
||||
date="2023-09-01",
|
||||
print("\n【第一次Summary】")
|
||||
result1 = await reme_fs.summary(
|
||||
messages=messages1,
|
||||
date=test_date,
|
||||
)
|
||||
|
||||
print_result(result, "SUMMARY RESULT")
|
||||
await reme_fs.close()
|
||||
print_result(result1, "第一次SUMMARY执行结果")
|
||||
check_memory_file(working_dir, test_date)
|
||||
|
||||
# 第二批消息,包含补充信息
|
||||
messages2 = [
|
||||
Message(
|
||||
role=Role.USER,
|
||||
content="周末的时候我还喜欢爬山和摄影。",
|
||||
time_created=f"{test_date} 14:30:00",
|
||||
),
|
||||
Message(
|
||||
role=Role.ASSISTANT,
|
||||
content="听起来很不错!这些爱好可以很好地平衡工作。",
|
||||
time_created=f"{test_date} 14:30:05",
|
||||
),
|
||||
Message(
|
||||
role=Role.USER,
|
||||
content="我最喜欢的编程语言是Python,经常使用PyTorch框架。",
|
||||
time_created=f"{test_date} 14:31:00",
|
||||
),
|
||||
]
|
||||
|
||||
async def test_summary_detailed_profile():
|
||||
"""Test summary() with comprehensive user profile information.
|
||||
print_messages(messages2, "第二批消息(补充信息)", max_content_len=200)
|
||||
|
||||
User provides detailed personal, professional, and preference information.
|
||||
Expects: LLM should organize and store all relevant information
|
||||
"""
|
||||
print("\n" + "=" * 80)
|
||||
print("TEST 2: Summary - Detailed User Profile Storage")
|
||||
print("=" * 80)
|
||||
print("\n【第二次Summary】")
|
||||
print("预期结果:")
|
||||
print(" - LLM应该调用EditTool添加补充信息")
|
||||
print(" - 新信息:爱好(爬山、摄影)、喜欢的语言(Python)、工具(PyTorch)")
|
||||
print(" - 与已有信息无冲突(姓名:小红、职业:数据科学家、技能:ML/DL)")
|
||||
|
||||
reme_fs = ReMeFs(enable_logo=False, vector_store=None)
|
||||
await reme_fs.start()
|
||||
|
||||
messages = create_detailed_profile_conversation()
|
||||
print_messages(messages, "INPUT MESSAGES", max_content_len=150)
|
||||
|
||||
print("\nParameters:")
|
||||
print(" version: default")
|
||||
print(" Expected: LLM should extract and store:")
|
||||
print(" - Name: Charlie Chen")
|
||||
print(" - Profession: Data Scientist")
|
||||
print(" - Location: San Francisco")
|
||||
print(" - Skills: ML, NLP, PyTorch, Hugging Face")
|
||||
print(" - Hobbies: Hiking, Photography")
|
||||
print(" - Assistant behavior: Be assertive and critical")
|
||||
|
||||
result = await reme_fs.summary(
|
||||
messages=messages,
|
||||
date="2023-10-01",
|
||||
result2 = await reme_fs.summary(
|
||||
messages=messages2,
|
||||
date=test_date,
|
||||
)
|
||||
|
||||
print_result(result, "SUMMARY RESULT")
|
||||
print_result(result2, "第二次SUMMARY执行结果")
|
||||
check_memory_file(working_dir, test_date)
|
||||
|
||||
await reme_fs.close()
|
||||
|
||||
# 测试后清理
|
||||
if Path(working_dir).exists():
|
||||
shutil.rmtree(working_dir)
|
||||
|
||||
|
||||
async def test_summary_conflicting_info():
|
||||
"""测试3:删除记忆文件,两次summary包含冲突和新增信息。
|
||||
|
||||
测试当进行两次summary时,第二次summary包含部分冲突信息和部分新信息。
|
||||
"""
|
||||
print("\n" + "=" * 80)
|
||||
print("测试3:Summary - 冲突信息和新增信息")
|
||||
print("=" * 80)
|
||||
|
||||
test_date = "2024-03-10"
|
||||
working_dir = ".reme_test3"
|
||||
|
||||
# 测试前清理
|
||||
if Path(working_dir).exists():
|
||||
shutil.rmtree(working_dir)
|
||||
|
||||
reme_fs = ReMeFs(enable_logo=False, working_dir=working_dir, vector_store=None)
|
||||
await reme_fs.start()
|
||||
|
||||
# 确保记忆文件已删除
|
||||
delete_memory_file(working_dir, test_date)
|
||||
|
||||
# 第一批消息
|
||||
messages1 = [
|
||||
Message(
|
||||
role=Role.USER,
|
||||
content="你好!我叫李华,是一名软件工程师,目前在北京工作。",
|
||||
time_created=f"{test_date} 08:00:00",
|
||||
),
|
||||
Message(
|
||||
role=Role.ASSISTANT,
|
||||
content="你好李华!很高兴认识你。",
|
||||
time_created=f"{test_date} 08:00:05",
|
||||
),
|
||||
Message(
|
||||
role=Role.USER,
|
||||
content="我喜欢用JavaScript和React开发,主要做Web应用。",
|
||||
time_created=f"{test_date} 08:01:00",
|
||||
),
|
||||
]
|
||||
|
||||
print_messages(messages1, "第一批消息", max_content_len=200)
|
||||
|
||||
print("\n【第一次Summary】")
|
||||
result1 = await reme_fs.summary(
|
||||
messages=messages1,
|
||||
date=test_date,
|
||||
)
|
||||
|
||||
print_result(result1, "第一次SUMMARY执行结果")
|
||||
check_memory_file(working_dir, test_date)
|
||||
|
||||
# 第二批消息,包含冲突和新增信息
|
||||
messages2 = [
|
||||
Message(
|
||||
role=Role.USER,
|
||||
content="其实上个月我已经搬到上海了,这边的机会更好。",
|
||||
time_created=f"{test_date} 16:00:00",
|
||||
),
|
||||
Message(
|
||||
role=Role.ASSISTANT,
|
||||
content="哦,恭喜你搬到新城市!",
|
||||
time_created=f"{test_date} 16:00:05",
|
||||
),
|
||||
Message(
|
||||
role=Role.USER,
|
||||
content="最近我也开始学Python和机器学习了。Web开发还在做,但也在探索AI项目。",
|
||||
time_created=f"{test_date} 16:01:00",
|
||||
),
|
||||
]
|
||||
|
||||
print_messages(messages2, "第二批消息(冲突+新增信息)", max_content_len=200)
|
||||
|
||||
print("\n【第二次Summary】")
|
||||
print("预期结果:")
|
||||
print(" - LLM应该调用EditTool更新冲突信息")
|
||||
print(" - 冲突信息:位置从'北京'变更为'上海'")
|
||||
print(" - 新增信息:学习Python、探索机器学习和AI项目")
|
||||
print(" - 保留信息:姓名(李华)、职业(软件工程师)、技能(JavaScript、React、Web开发)")
|
||||
|
||||
result2 = await reme_fs.summary(
|
||||
messages=messages2,
|
||||
date=test_date,
|
||||
)
|
||||
|
||||
print_result(result2, "第二次SUMMARY执行结果")
|
||||
check_memory_file(working_dir, test_date)
|
||||
|
||||
await reme_fs.close()
|
||||
|
||||
# 测试后清理
|
||||
if Path(working_dir).exists():
|
||||
shutil.rmtree(working_dir)
|
||||
|
||||
|
||||
async def main():
|
||||
"""Run core summary interface tests for personal memory storage."""
|
||||
"""运行时间对齐的summary接口测试。"""
|
||||
print("\n" + "=" * 80)
|
||||
print("ReMeFs Summary Interface - Personal Memory Storage Tests")
|
||||
print("ReMeFs Summary接口 - 时间对齐的记忆存储测试")
|
||||
print("=" * 80)
|
||||
print("\nThis test suite validates that the summary() function:")
|
||||
print(" 1. Extracts personal information from user messages")
|
||||
print(" 2. Calls file system tools (WriteTool/EditTool) to store the info")
|
||||
print(" 3. Organizes information for future retrieval")
|
||||
print("\nTest Scenarios:")
|
||||
print(" 1. Personal info - name, profession, interests")
|
||||
print(" 2. Detailed profile - comprehensive user information")
|
||||
print("\n本测试套件验证summary()函数:")
|
||||
print(" 1. 正确处理消息中的time_created字段(%Y-%m-%d %H:%M:%S)")
|
||||
print(" 2. 将消息时间戳与summary的date参数(%Y-%m-%d)对齐")
|
||||
print(" 3. 处理首次写入、补充更新和冲突更新")
|
||||
print("\n测试场景:")
|
||||
print(" 1. 首次写入 - 直接写入新的记忆文件")
|
||||
print(" 2. 补充信息 - 添加不冲突的信息")
|
||||
print(" 3. 冲突信息 - 更新冲突信息并添加新信息")
|
||||
print("=" * 80)
|
||||
|
||||
# Test 1: Basic personal information storage
|
||||
await test_summary_personal_info_storage()
|
||||
# 测试1:首次写入
|
||||
await test_summary_first_write()
|
||||
|
||||
# Test 2: Comprehensive user profile
|
||||
await test_summary_detailed_profile()
|
||||
# 测试2:补充信息
|
||||
await test_summary_complementary_info()
|
||||
|
||||
# 测试3:冲突信息
|
||||
await test_summary_conflicting_info()
|
||||
|
||||
print("\n" + "=" * 80)
|
||||
print("All summary tests completed!")
|
||||
print("所有summary测试已完成!")
|
||||
print("=" * 80)
|
||||
print("\nNote: These tests require LLM calls to:")
|
||||
print(" - Analyze user messages for personal information")
|
||||
print(" - Decide what information to store")
|
||||
print(" - Call appropriate tools (WriteTool/EditTool) to save to memory")
|
||||
print("\nMake sure your API keys are properly configured before running.")
|
||||
print("The LLM will autonomously decide what to store based on the conversation.")
|
||||
print("\n注意:这些测试需要调用LLM来:")
|
||||
print(" - 分析用户消息中的个人信息")
|
||||
print(" - 决定存储或更新哪些信息")
|
||||
print(" - 调用合适的工具(WriteTool/EditTool)保存到记忆文件")
|
||||
print("\n运行前请确保已正确配置API密钥。")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue