shell.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. """
  2. 重构后的 CustomReactAgent 的交互式命令行客户端
  3. """
  4. import asyncio
  5. import logging
  6. import sys
  7. import os
  8. # 动态地将项目根目录添加到 sys.path,以支持跨模块导入
  9. # 这使得脚本更加健壮,无论从哪里执行
  10. PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
  11. sys.path.insert(0, PROJECT_ROOT)
  12. # 从新模块导入 Agent 和配置 (使用相对导入)
  13. from .agent import CustomReactAgent
  14. from . import config
  15. # 配置日志
  16. logging.basicConfig(level=config.LOG_LEVEL, format=config.LOG_FORMAT)
  17. logger = logging.getLogger(__name__)
  18. class CustomAgentShell:
  19. """新 Agent 的交互式 Shell 客户端"""
  20. def __init__(self, agent: CustomReactAgent):
  21. """私有构造函数,请使用 create() 类方法。"""
  22. self.agent = agent
  23. self.user_id: str = config.DEFAULT_USER_ID
  24. self.thread_id: str | None = None
  25. @classmethod
  26. async def create(cls):
  27. """异步工厂方法,创建 Shell 实例。"""
  28. agent = await CustomReactAgent.create()
  29. return cls(agent)
  30. async def close(self):
  31. """关闭 Agent 资源。"""
  32. if self.agent:
  33. await self.agent.close()
  34. async def start(self):
  35. """启动 Shell 界面。"""
  36. print("\n🚀 Custom React Agent Shell (StateGraph Version)")
  37. print("=" * 50)
  38. # 获取用户ID
  39. user_input = input(f"请输入您的用户ID (默认: {self.user_id}): ").strip()
  40. if user_input:
  41. self.user_id = user_input
  42. print(f"👤 当前用户: {self.user_id}")
  43. # 这里可以加入显示历史会话的逻辑
  44. print("\n💬 开始对话 (输入 'exit' 或 'quit' 退出)")
  45. print("-" * 50)
  46. await self._chat_loop()
  47. async def _chat_loop(self):
  48. """主要的聊天循环。"""
  49. while True:
  50. user_input = input(f"👤 [{self.user_id[:8]}]> ").strip()
  51. if not user_input:
  52. continue
  53. if user_input.lower() in ['quit', 'exit']:
  54. raise KeyboardInterrupt # 优雅退出
  55. if user_input.lower() == 'new':
  56. self.thread_id = None
  57. print("🆕 已开始新会话。")
  58. continue
  59. if user_input.lower() == 'history':
  60. await self._show_current_history()
  61. continue
  62. # 正常对话
  63. print("🤖 Agent 正在思考...")
  64. result = await self.agent.chat(user_input, self.user_id, self.thread_id)
  65. if result.get("success"):
  66. print(f"🤖 Agent: {result.get('answer')}")
  67. # 更新 thread_id 以便在同一会话中继续
  68. self.thread_id = result.get("thread_id")
  69. else:
  70. print(f"❌ 发生错误: {result.get('error')}")
  71. async def _show_current_history(self):
  72. """显示当前会话的历史记录。"""
  73. if not self.thread_id:
  74. print("当前没有活跃的会话。请先开始对话。")
  75. return
  76. print(f"\n--- 对话历史: {self.thread_id} ---")
  77. history = await self.agent.get_conversation_history(self.thread_id)
  78. if not history:
  79. print("无法获取历史或历史为空。")
  80. return
  81. for msg in history:
  82. print(f"[{msg['type']}] {msg['content']}")
  83. print("--- 历史结束 ---")
  84. async def main():
  85. """主函数入口"""
  86. shell = None
  87. try:
  88. shell = await CustomAgentShell.create()
  89. await shell.start()
  90. except KeyboardInterrupt:
  91. logger.info("\n👋 检测到退出指令,正在清理资源...")
  92. except Exception as e:
  93. logger.error(f"❌ 程序发生严重错误: {e}", exc_info=True)
  94. finally:
  95. if shell:
  96. await shell.close()
  97. print("✅ 程序已成功关闭。")
  98. if __name__ == "__main__":
  99. try:
  100. asyncio.run(main())
  101. except KeyboardInterrupt:
  102. # 这个捕获是为了处理在 main 之外的 Ctrl+C
  103. print("\n👋 程序被强制退出。")