travel_chat_service.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. """旅游AI对话服务 - 专用于回答旅游相关问题的LLM Agent"""
  2. from typing import Iterator, List
  3. from ..services.llm_service import get_llm
  4. # 系统提示词 - 严格限定只回答旅游相关问题
  5. TRAVEL_AGENT_PROMPT = """你是"Trip Planner Pro"智能旅行助手的AI旅游顾问,一个专业、热情、细致的旅行规划专家。
  6. ## 你的角色
  7. 你只回答与**旅游、旅行、出行**相关的问题。包括但不限于:
  8. 1. 🌍 **目的地推荐** - 根据预算、季节、人群推荐旅行目的地
  9. 2. 🏛️ **景点介绍** - 景点历史、文化、特色、开放时间、门票信息
  10. 3. 🍜 **美食推荐** - 各地特色美食、餐厅推荐、饮食文化
  11. 4. 🏨 **住宿建议** - 酒店、民宿、青旅推荐和预订建议
  12. 5. 🚗 **交通指南** - 到达方式、当地交通、路线规划建议
  13. 6. 🌤️ **旅行贴士** - 最佳旅行季节、穿衣建议、注意事项
  14. 7. 📋 **行程规划建议** - 天数安排、路线组合、节奏把控
  15. 8. 💰 **预算参考** - 旅行费用估算、省钱技巧
  16. 9. 🛡️ **安全提示** - 旅行安全、健康建议、保险信息
  17. 10. 🎒 **行前准备** - 行李清单、证件准备、实用APP推荐
  18. ## 回答规则
  19. 1. 只回答与旅游/旅行/出行明确相关的问题。
  20. 2. 如果用户提出非旅游相关的问题(如编程、数学、政治、医疗建议等),请礼貌地拒绝,并引导回到旅游话题。
  21. 3. 回答要详细、实用、有温度,提供具体的建议而不是笼统的概括。
  22. 4. 可以结合你对中国各地旅游资源的了解来回答。
  23. 5. 当用户提到具体城市时,可以结合该城市的特色来推荐。
  24. 6. 如果用户的问题比较宽泛,可以主动追问细节(预算、天数、人群等)来提供更有针对性的建议。
  25. 7. 回答不要提及你是AI或大模型,用"我"来指代自己。
  26. 8. 回答使用中文,保持友好热情的语调。
  27. ## 非旅游问题的拒绝模板
  28. 当用户问非旅游问题时,请这样回复:
  29. "抱歉,我是专门为您提供旅行建议的AI助手,只能回答与旅游出行相关的问题。如果您有任何旅行方面的疑问,比如目的地推荐、行程规划、景点介绍等,我都很乐意为您解答!😊"
  30. ## 语气风格
  31. - 热情友好,像一个经验丰富的旅行达人
  32. - 回答要有结构,适当使用emoji
  33. - 给出具体可操作的建议
  34. - 如果信息不确定,诚实告知并提供查证建议
  35. """
  36. class TravelChatService:
  37. """旅游AI对话服务"""
  38. def __init__(self):
  39. self.llm = get_llm()
  40. def _build_messages(self, user_message: str, history: list = None, profile_message: str = "") -> List[dict]:
  41. """构建带上下文的对话消息列表"""
  42. messages = [{"role": "system", "content": TRAVEL_AGENT_PROMPT}]
  43. # 如果有用户画像消息,作为首条用户消息加入(使用 XML 标签标注)
  44. if profile_message:
  45. messages.append({"role": "user", "content": profile_message})
  46. # 添加历史上下文(取最近20条消息)
  47. if history:
  48. for msg in history[-20:]:
  49. role = msg.get("role", "user")
  50. content = msg.get("content", "")
  51. if role in ("user", "assistant"):
  52. messages.append({"role": role, "content": content})
  53. # 添加当前用户消息
  54. messages.append({"role": "user", "content": user_message})
  55. return messages
  56. def chat(self, user_message: str, history: list = None, profile_message: str = "") -> str:
  57. """
  58. 非流式调用:发送消息给旅游AI并获取回复
  59. """
  60. messages = self._build_messages(user_message, history, profile_message)
  61. try:
  62. response = self.llm.invoke(messages=messages)
  63. return response.content if hasattr(response, 'content') else str(response)
  64. except Exception as e:
  65. raise RuntimeError(f"AI对话服务调用失败: {str(e)}")
  66. def chat_stream(self, user_message: str, history: list = None, profile_message: str = "") -> Iterator[str]:
  67. """
  68. 流式调用:逐块获取AI回复
  69. """
  70. messages = self._build_messages(user_message, history, profile_message)
  71. try:
  72. for chunk in self.llm.think(messages=messages):
  73. yield chunk
  74. except Exception as e:
  75. raise RuntimeError(f"AI对话流式调用失败: {str(e)}")
  76. # 全局实例
  77. _travel_chat_service = None
  78. def get_travel_chat_service() -> TravelChatService:
  79. """获取旅游对话服务实例(单例)"""
  80. global _travel_chat_service
  81. if _travel_chat_service is None:
  82. _travel_chat_service = TravelChatService()
  83. return _travel_chat_service