Skip to content
Merged

Dev #25

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
logs/
config.toml
/misc_summaries
bots.json
32 changes: 30 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions examples/cortex-mem-tars/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/target
bots.json
5 changes: 5 additions & 0 deletions examples/cortex-mem-tars/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ async-stream = "0.3"
# HTTP client
reqwest = { version = "0.12", features = ["json"] }

# HTTP server
axum = { version = "0.7", features = ["json"] }
tower = "0.5"
tower-http = { version = "0.5", features = ["cors", "trace"] }

# Utilities
uuid = { version = "1.10", features = ["v4"] }
clipboard = "0.5"
Expand Down
12 changes: 10 additions & 2 deletions examples/cortex-mem-tars/config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ max_tokens = 2000

[server]
# 服务器主机
host = "127.0.0.1"
host = "localhost"
# 服务器端口
port = 8080
# CORS 允许的来源
Expand Down Expand Up @@ -69,4 +69,12 @@ enabled = false
# 日志目录
log_directory = "logs"
# 日志级别
level = "info"
level = "info"

[api]
# TARS API 服务器端口
port = 8080
# API 密钥(可选,如果启用认证)
api_key = "ANYTHING_YOU_LIKE"
# 是否启用 CORS
enable_cors = true
128 changes: 87 additions & 41 deletions examples/cortex-mem-tars/src/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,13 @@ pub async fn create_memory_agent(
memory_manager: Arc<MemoryManager>,
memory_tool_config: MemoryToolConfig,
config: &Config,
user_info: Option<&str>,
bot_system_prompt: Option<&str>,
agent_id: &str,
) -> Result<RigAgent<CompletionModel>, Box<dyn std::error::Error>> {
// 提前获取 user_id,避免所有权问题
let user_id_str = memory_tool_config.default_user_id.clone().unwrap_or_else(|| "unknown".to_string());

// 创建记忆工具
let memory_tools =
create_memory_tools(memory_manager.clone(), &config, Some(memory_tool_config));
Expand All @@ -69,6 +75,67 @@ pub async fn create_memory_agent(
.base_url(&config.llm.api_base_url)
.build();

// 构建 base system prompt,包含用户基本信息和 agent_id 说明
let base_system_prompt = if let Some(info) = user_info {
format!(r#"你是一个拥有记忆功能的智能AI助手。你可以访问和使用记忆工具来检索、存储和管理用户信息。

此会话发生的初始时间:{current_time}

重要说明:
- 你的身份标识(agent_id):{agent_id}
- 你服务的用户标识(user_id):{user_id}
- 当你调用记忆工具时,必须明确传入 user_id="{user_id}" 和 agent_id="{agent_id}" 参数
- 你的记忆是独立的,只属于你这个 agent,不会与其他 agent 混淆

用户基本信息:
{info}

重要指令:
- 对话历史将作为上下文提供,请使用这些信息来理解当前的对话流程
- 用户基本信息已在上方提供,请不要再使用memory工具来创建或更新用户基本信息
- 在需要时可以自主使用memory工具搜索其他相关记忆,但必须传入正确的 user_id 和 agent_id
- 当用户提供新的重要信息时,可以主动使用memory工具存储,确保使用正确的 user_id 和 agent_id
- 保持对话的连贯性和一致性
- 自然地融入记忆信息,避免刻意复述此前的记忆信息,关注当前的会话内容,记忆主要用于做隐式的逻辑与事实支撑
- 专注于用户的需求和想要了解的信息,以及想要你做的事情

记住:你正在与一个了解的用户进行连续对话,对话过程中不需要刻意表达你的记忆能力。"#,
current_time = chrono::Local::now().format("%Y年%m月%d日 %H:%M:%S"),
agent_id = agent_id,
user_id = user_id_str,
info = info)
} else {
format!(r#"你是一个拥有记忆功能的智能AI助手。你可以访问和使用记忆工具来检索、存储和管理用户信息。

此会话发生的初始时间:{current_time}

重要说明:
- 你的身份标识(agent_id):{agent_id}
- 你服务的用户标识(user_id):{user_id}
- 当你调用记忆工具时,必须明确传入 user_id="{user_id}" 和 agent_id="{agent_id}" 参数
- 你的记忆是独立的,只属于你这个 agent,不会与其他 agent 混淆

重要指令:
- 对话历史将作为上下文提供,请使用这些信息来理解当前的对话流程
- 在需要时可以自主使用memory工具搜索其他相关记忆,但必须传入正确的 user_id 和 agent_id
- 当用户提供新的重要信息时,可以主动使用memory工具存储,确保使用正确的 user_id 和 agent_id
- 保持对话的连贯性和一致性
- 自然地融入记忆信息,避免刻意复述此前的记忆信息,关注当前的会话内容,记忆主要用于做隐式的逻辑与事实支撑
- 专注于用户的需求和想要了解的信息,以及想要你做的事情

记住:你正在与一个了解的用户进行连续对话,对话过程中不需要刻意表达你的记忆能力。"#,
current_time = chrono::Local::now().format("%Y年%m月%d日 %H:%M:%S"),
agent_id = agent_id,
user_id = user_id_str)
};

// 追加机器人系统提示词
let system_prompt = if let Some(bot_prompt) = bot_system_prompt {
format!("{}\n\n你的角色设定:\n{}", base_system_prompt, bot_prompt)
} else {
base_system_prompt
};

// 构建带有记忆工具的agent,让agent能够自主决定何时调用记忆功能
let completion_model = llm_client
.completion_model(&config.llm.model_efficient)
Expand All @@ -79,20 +146,7 @@ pub async fn create_memory_agent(
.tool(memory_tools.query_memory())
.tool(memory_tools.list_memories())
.tool(memory_tools.get_memory())
.preamble(&format!(r#"你是一个拥有记忆功能的智能AI助手。你可以访问和使用记忆工具来检索、存储和管理用户信息。

此会话发生的初始时间:{current_time}

重要指令:
- 对话历史将作为上下文提供,请使用这些信息来理解当前的对话流程
- 用户基本信息将在上下文中提供一次,请不要再使用memory工具来创建或更新用户基本信息
- 在需要时可以自主使用memory工具搜索其他相关记忆
- 当用户提供新的重要信息时,可以主动使用memory工具存储
- 保持对话的连贯性和一致性
- 自然地融入记忆信息,避免刻意复述此前的记忆信息,关注当前的会话内容,记忆主要用于做隐式的逻辑与事实支撑
- 专注于用户的需求和想要了解的信息,以及想要你做的事情

记住:你正在与一个了解的用户进行连续对话,对话过程中不需要刻意表达你的记忆能力。"#, current_time = chrono::Local::now().format("%Y年%m月%d日 %H:%M:%S")))
.preamble(&system_prompt)
.build();

Ok(completion_model)
Expand All @@ -103,12 +157,14 @@ pub async fn extract_user_basic_info(
config: &Config,
memory_manager: Arc<MemoryManager>,
user_id: &str,
agent_id: &str,
) -> Result<Option<String>, Box<dyn std::error::Error>> {
let memory_tools = create_memory_tools(
memory_manager,
config,
Some(MemoryToolConfig {
default_user_id: Some(user_id.to_string()),
default_agent_id: Some(agent_id.to_string()),
..Default::default()
}),
);
Expand All @@ -119,14 +175,14 @@ pub async fn extract_user_basic_info(
limit: Some(20),
memory_type: Some("personal".to_string()), // 使用小写以匹配新API
user_id: Some(user_id.to_string()),
agent_id: None,
agent_id: Some(agent_id.to_string()),
};

let search_args_factual = ListMemoriesArgs {
limit: Some(20),
memory_type: Some("factual".to_string()), // 使用小写以匹配新API
user_id: Some(user_id.to_string()),
agent_id: None,
agent_id: Some(agent_id.to_string()),
};

if let Ok(search_result) = memory_tools
Expand Down Expand Up @@ -178,42 +234,32 @@ pub async fn agent_reply_with_memory_retrieval_streaming(
_memory_manager: Arc<MemoryManager>,
user_input: &str,
_user_id: &str,
user_info: Option<&str>,
conversations: &[(String, String)],
stream_sender: mpsc::UnboundedSender<String>,
) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
// 构建对话历史 - 转换为rig的Message格式
let mut chat_history = Vec::new();

// 添加历史对话
for (user_msg, assistant_msg) in conversations {
chat_history.push(Message::user(user_msg));
chat_history.push(Message::assistant(assistant_msg));
if !assistant_msg.is_empty() {
chat_history.push(Message::assistant(assistant_msg));
}
}

// 构建system prompt,包含明确的指令
let system_prompt = r#"你是一个拥有记忆功能的智能AI助手。你可以访问和使用记忆工具来检索、存储和管理用户信息。

重要指令:
- 对话历史已提供在上下文中,请使用这些信息来理解当前的对话上下文
- 用户基本信息已在下方提供一次,请不要再使用memory工具来创建或更新用户基本信息
- 在需要时可以自主使用memory工具搜索其他相关记忆
- 当用户提供新的重要信息时,可以主动使用memory工具存储
- 保持对话的连贯性和一致性
- 自然地融入记忆信息,避免显得刻意
- 专注于用户的需求和想要了解的信息,以及想要你做的事情

记住:你正在与一个了解的用户进行连续对话,对话过程中不需要刻意表达你的记忆能力。"#;

// 构建完整的prompt
let prompt_content = if let Some(info) = user_info {
format!(
"{}\n\n用户基本信息:\n{}\n\n当前用户输入: {}",
system_prompt, info, user_input
)
} else {
format!("{}\n\n当前用户输入: {}", system_prompt, user_input)
};
// 构建当前用户输入消息
let prompt_content = user_input.to_string();

log::debug!("正在生成AI回复(真实流式模式)...");
log::debug!("当前用户输入: {}", user_input);
log::debug!("对话历史长度: {}", chat_history.len());
for (i, msg) in chat_history.iter().enumerate() {
match msg {
Message::User { .. } => log::debug!(" [{}] User message", i),
Message::Assistant { .. } => log::debug!(" [{}] Assistant message", i),
}
}

// 使用rig的真实流式API
let prompt_message = Message::user(&prompt_content);
Expand Down
Loading