| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513 |
- # core/main_agent.py
- """主 Agent - 协调层,负责意图识别和路由"""
- from hello_agents import SimpleAgent, HelloAgentsLLM
- from core.file_manager import FileManager
- class MainAgent(SimpleAgent):
- """
- 系统协调者,负责意图识别和路由
- 职责:
- - 接收用户输入
- - 识别用户意图(create/add/vibe/summary/help/exit)
- - 路由到相应的子 Agent 或处理器
- - 管理基本命令(help, list, exit)
- """
- # 意图关键词映射(按优先级排序,更具体的在前)
- INTENT_KEYWORDS = {
- "create": [
- "/create",
- "创建计划",
- "制定学习路径",
- "我想学",
- "我想学习",
- "学习计划", # 移除单独的"学习"以避免冲突
- ],
- "add": ["/add", "添加笔记", "记录知识", "添加知识"],
- "vibe": ["/vibe", "开始学习", "互动学习", "练习", "考察"],
- "summary": ["/summary", "学习进度", "总结", "评估"],
- "help": ["/help", "帮助", "help"],
- "list": ["/list", "列出所有", "所有领域", "列表"],
- "exit": ["/exit", "退出", "quit", "exit"],
- }
- def __init__(self, llm: HelloAgentsLLM, file_manager: FileManager, streaming: bool = None):
- """
- 初始化主 Agent
- Args:
- llm: HelloAgentsLLM 实例
- file_manager: FileManager 实例
- streaming: 是否启用流式输出(None = 自动检测)
- """
- system_prompt = """
- 你是 LearningAgent 学习助手的主界面。
- 支持的功能:
- 1. 创建学习计划 (/create, "我想学习")
- 2. 添加知识笔记 (/add, "添加笔记")
- 3. 开始互动学习 (/vibe, "开始学习")
- 4. 查看学习总结 (/summary, "总结")
- 5. 显示帮助 (/help, "帮助")
- 6. 列出所有领域 (/list)
- 7. 退出程序 (/exit, "退出")
- 识别用户意图后,调用相应的功能。
- 如果意图模糊,询问用户确认。
- """
- self.llm = llm
- self.file_manager = file_manager
- # 添加流式输出支持
- from utils.streaming import should_stream
- self.streaming = should_stream(streaming)
- # 会话状态管理
- self.active_session = None # {"domain": str, "mode": str, "round": int}
- # 使用父类初始化
- super().__init__("MainAgent", llm, system_prompt)
- def _identify_intent(self, user_input: str) -> str:
- """
- 识别用户意图
- Args:
- user_input: 用户输入
- Returns:
- 意图类型(create/add/vibe/summary/help/list/exit/unknown)
- """
- user_input_lower = user_input.lower().strip()
- # 检查每个意图的关键词
- for intent, keywords in self.INTENT_KEYWORDS.items():
- for keyword in keywords:
- if keyword.lower() in user_input_lower:
- return intent
- return "unknown"
- def process_command(self, user_input: str) -> str:
- """
- 处理用户命令
- Args:
- user_input: 用户输入
- Returns:
- 处理结果
- """
- # 检查是否有活跃的 vibe 会话
- if self.active_session is not None:
- # 检查是否要退出会话
- if self._is_exit_command(user_input):
- return self._end_vibe_session()
- # 否则继续对话
- return self._continue_vibe_session(user_input)
- # 正常命令处理
- intent = self._identify_intent(user_input)
- if intent == "create":
- return self._route_to_create_plan(user_input)
- elif intent == "add":
- return self._route_to_add_knowledge(user_input)
- elif intent == "vibe":
- return self._route_to_vibe_learning(user_input)
- elif intent == "summary":
- return self._route_to_summary(user_input)
- elif intent == "help":
- return self._show_help()
- elif intent == "list":
- return self._list_domains()
- elif intent == "exit":
- return "EXIT"
- elif intent == "unknown":
- return "❓ 未识别的命令。输入 /help 查看帮助。"
- def _route_to_create_plan(self, input_data: str) -> str:
- """
- 路由到 CreatePlanAgent
- Args:
- input_data: 用户输入
- Returns:
- 执行结果
- """
- from agents.create_plan_agent import CreatePlanAgent
- try:
- # 去掉命令前缀,只保留参数部分
- # 支持: "/create 数学", "创建计划 数学", "我想学习数学"
- clean_input = input_data
- # 去掉 /create 前缀
- for prefix in ["/create", "/CREATE"]:
- if clean_input.startswith(prefix):
- clean_input = clean_input[len(prefix) :].strip()
- break
- # 如果是自然语言形式,保留原样
- # 例如: "我想学习数学", "创建一个学习计划"
- if not clean_input or clean_input == input_data:
- clean_input = input_data
- agent = CreatePlanAgent(self.llm, streaming=self.streaming)
- return agent.run(clean_input)
- except Exception as e:
- return f"❌ 创建学习计划失败:{e}"
- def _route_to_add_knowledge(self, input_data: str) -> str:
- """
- 路由到 AddKnowledgeProcessor
- Args:
- input_data: 用户输入
- Returns:
- 执行结果
- """
- from processors.add_knowledge import AddKnowledgeProcessor
- try:
- # 去掉命令前缀,只保留参数部分
- # 支持: "/add 数学 算法", "添加笔记", "记录知识"
- clean_input = input_data
- # 去掉 /add 前缀
- for prefix in ["/add", "/ADD"]:
- if clean_input.startswith(prefix):
- clean_input = clean_input[len(prefix) :].strip()
- break
- # 如果是自然语言形式,需要询问用户输入内容和领域
- if not clean_input or clean_input == input_data or len(clean_input) < 10:
- return self._ask_for_knowledge_input()
- # 解析输入(格式:领域 内容)
- # 例如: "机器学习 决策树算法"
- parts = clean_input.split(maxsplit=1)
- if len(parts) == 2:
- domain, content = parts
- domain = domain.strip()
- content = content.strip()
- else:
- # 无法解析,询问用户
- return self._ask_for_knowledge_input()
- processor = AddKnowledgeProcessor(self.llm, self.file_manager)
- return processor.add(domain, content)
- except Exception as e:
- return f"❌ 添加知识失败:{e}"
- def _ask_for_knowledge_input(self) -> str:
- """
- 询问用户知识内容和领域
- Returns:
- 提示信息
- """
- return """📝 添加知识笔记
- 请按以下格式输入:
- > /add <领域> <知识内容>
- 例如:
- > /add 机器学习 # 决策树算法简介
- 或者直接输入内容(会询问领域):
- > /add 决策树是一种监督学习算法,用于分类和回归...
- 💡 提示:长内容可以先在编辑器中准备好,然后一次性粘贴。
- """
- def _route_to_vibe_learning(self, input_data: str) -> str:
- """
- 路由到 VibeLearningAgent
- Args:
- input_data: 用户输入
- Returns:
- 执行结果
- """
- from agents.vibe_learning_agent import VibeLearningAgent
- try:
- # 去掉命令前缀,只保留参数部分
- # 支持: "/vibe Python", "/vibe Python --mode quiz"
- clean_input = input_data
- # 去掉 /vibe 前缀
- for prefix in ["/vibe", "/VIBE", "/Vibe"]:
- if clean_input.startswith(prefix):
- clean_input = clean_input[len(prefix):].strip()
- break
- # 如果是自然语言形式,询问用户
- if not clean_input or len(clean_input.split()) < 1:
- return self._ask_for_vibe_input()
- # 解析输入
- # 格式: <领域> [--mode <mode>]
- parts = clean_input.split()
- domain = parts[0].strip()
- # 检查是否有模式选项
- mode = "free" # 默认模式
- if "--mode" in parts:
- mode_idx = parts.index("--mode")
- if mode_idx + 1 < len(parts):
- mode = parts[mode_idx + 1].strip().lower()
- if mode not in ["free", "quiz"]:
- return "❌ 无效的模式。请使用 --mode free 或 --mode quiz"
- # 启动学习会话
- agent = VibeLearningAgent(self.llm, self.file_manager,
- streaming=self.streaming)
- result = agent.start_session(domain, mode=mode)
- # 设置活跃会话
- self.active_session = {
- "domain": domain,
- "mode": mode,
- "round": 1,
- "agent": agent,
- "streaming": self.streaming # 保存 streaming 设置
- }
- return result
- except Exception as e:
- return f"❌ 启动互动学习失败:{e}"
- def _route_to_summary(self, input_data: str) -> str:
- """
- 路由到 SummaryAgent
- Args:
- input_data: 用户输入
- Returns:
- 执行结果
- """
- from agents.summary_agent import SummaryAgent
- try:
- # 去掉命令前缀,只保留参数部分
- # 支持: "/summary Python", "总结学习进度"
- clean_input = input_data
- # 去掉 /summary 前缀
- for prefix in ["/summary", "/SUMMARY", "/Summary"]:
- if clean_input.startswith(prefix):
- clean_input = clean_input[len(prefix):].strip()
- break
- # 如果是自然语言形式,询问用户
- if not clean_input or len(clean_input.split()) < 1:
- return self._ask_for_summary_input()
- # 解析输入
- # 格式: <领域>
- domain = clean_input.split()[0].strip()
- # 生成学习总结
- agent = SummaryAgent(self.llm, self.file_manager,
- streaming=self.streaming)
- return agent.run(domain)
- except Exception as e:
- return f"❌ 生成学习总结失败:{e}"
- def _is_exit_command(self, user_input: str) -> bool:
- """
- 检查是否是退出命令
- Args:
- user_input: 用户输入
- Returns:
- 是否是退出命令
- """
- exit_keywords = ["/exit", "exit", "退出", "quit", "/quit", "结束", "完成"]
- return user_input.strip().lower() in exit_keywords
- def _end_vibe_session(self) -> str:
- """
- 结束 vibe 会话
- Returns:
- 结束消息
- """
- domain = self.active_session["domain"]
- mode = self.active_session["mode"]
- rounds = self.active_session["round"]
- # 清除会话状态
- self.active_session = None
- return f"""✅ 会话已结束
- 📁 领域: {domain}
- 📝 模式: {mode}
- 💬 对话轮数: {rounds}
- 💡 上下文已保存。输入 /help 查看可用命令。
- """
- def _continue_vibe_session(self, user_input: str) -> str:
- """
- 继续 vibe 会话
- Args:
- user_input: 用户回答
- Returns:
- 反馈和下一个问题
- """
- try:
- agent = self.active_session["agent"]
- domain = self.active_session["domain"]
- mode = self.active_session["mode"]
- # 确保 streaming 设置一致
- agent.streaming = self.active_session.get("streaming", agent.streaming)
- # 继续对话(获取反馈和下一个问题)
- result = agent.continue_session(domain, user_input, mode)
- # 增加轮次计数
- self.active_session["round"] += 1
- return result
- except Exception as e:
- # 发生错误时清除会话状态
- self.active_session = None
- return f"❌ 对话过程中发生错误:{e}\n\n会话已结束。"
- def _ask_for_vibe_input(self) -> str:
- """
- 询问用户互动学习参数
- Returns:
- 提示信息
- """
- return """📝 互动学习
- 请按以下格式输入:
- > /vibe <领域> [--mode <模式>]
- 例如:
- > /vibe Python
- > /vibe Python --mode quiz
- 模式说明:
- - free: 自由对话模式(默认)
- - quiz: 测验模式
- 💡 提示:需要先使用 /create 创建学习计划。
- """
- def _ask_for_summary_input(self) -> str:
- """
- 询问用户学习总结参数
- Returns:
- 提示信息
- """
- return """📝 学习进度总结
- 请按以下格式输入:
- > /summary <领域>
- 例如:
- > /summary Python
- > /summary 机器学习
- 💡 提示:需要先使用 /create 创建学习计划。
- """
- def _show_help(self) -> str:
- """显示帮助信息"""
- return """
- # 🤖 LearningAgent 帮助
- ## 命令列表
- ### 创建学习计划
- - `/create <领域>` - 创建学习计划
- 例:`/create 数学`
- 例:`/create https://github.com/user/project`
- 例:`/create ~/paper.pdf`
- - 自然语言:`我想学习数学`
- ### 添加知识笔记 ✨ 新功能
- - `/add <领域> <内容>` - 添加知识笔记
- 例:`/add 机器学习 # 决策树算法简介`
- 例:`/add Python 这是一个列表推导式的例子...`
- - 文件输入:`/add ~/notes.md`
- - 自然语言:`添加笔记` `记录知识`
- ### 开始互动学习 ✨ 新功能
- - `/vibe <领域>` - 开始互动学习
- 例:`/vibe Python`
- 例:`/vibe Python --mode quiz`
- - 模式说明:
- - `free`: 自由对话模式(默认)
- - `quiz`: 测验模式
- - 退出会话:输入"退出"、"exit"或"/exit"即可随时结束
- - 自然语言:`开始学习数学` `练习一下`
- ### 查看学习总结 ✨ 新功能
- - `/summary <领域>` - 查看学习总结
- 例:`/summary Python`
- 例:`/summary 机器学习`
- - 自然语言:`总结学习进度` `评估我的水平`
- ### 其他命令
- - `/list` - 列出所有学习领域
- - `/help` - 显示帮助
- - `/exit` 或 `exit` - 退出程序
- ## 提示
- - 支持命令前缀(如 `/create`)和自然语言(如"我想学习")
- - 添加知识时,内容会自动分析、分类并打标签
- - 随时输入 `/help` 查看帮助
- """
- def _list_domains(self) -> str:
- """列出所有学习领域"""
- domains = self.file_manager.list_domains()
- if not domains:
- return "📭 还没有创建任何学习领域。\n使用 `/create` 创建第一个学习计划。"
- domain_list = "\n".join([f"- {domain}" for domain in domains])
- return f"# 📚 学习领域\n\n{domain_list}\n\n共 {len(domains)} 个领域"
- def list_domains(self) -> list:
- """
- 获取所有领域列表
- Returns:
- 领域名称列表
- """
- return self.file_manager.list_domains()
|