main_prompts.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. # 这是提示模板库,不是项目启动入口。主入口在 api_server.py。
  2. # 内部顺序:1 调用静态方法创建模板 → 2 用 prompt | llm 组装调用链
  3. # → 3 调用方 invoke/astream 填入变量并请求模型 → 4 调用方解析或转发模型输出。
  4. # 下面方法按场景选用,不会从第一个方法到最后一个方法依次执行。
  5. # 提示词正文参与模型输入,本次仅补充代码说明,不改动正文。
  6. """
  7. 需求:定义SmartBizTrip项目中使用的各种提示模板,用于不同场景的对话处理
  8. 什么是 Prompt Template(提示模板)?
  9. 提示模板是一种可复用的文本模板,其中包含固定内容和可变变量(用 {变量名} 表示)。
  10. 例如:模板 "你好,{name}!" 在填入 name="张三" 后会变成 "你好,张三!"。
  11. 为什么使用模板类管理?
  12. 1. 集中管理:所有 prompt 定义在同一个文件中,方便查找和修改
  13. 2. 可复用:同一个模板可以被多处调用
  14. 3. 参数化:通过变量注入不同上下文,避免字符串拼接
  15. 本项目中的 Prompt 分类:
  16. 1. 意图识别类:intent_prompt —— 识别用户想做什么
  17. 2. 结果总结类:summarize_weather_prompt、summarize_ticket_prompt —— 将原始数据转化为友好回复
  18. 3. 内容生成类:city_service_prompt —— 直接生成城市接待建议
  19. 4. 任务规划类:planning_prompt —— 判断任务复杂度并生成执行计划(Planning + ReAct 架构)
  20. 5. ReAct推理类:react_prompt、react_summary_prompt —— 逐步推理和最终汇总
  21. """
  22. from langchain_core.prompts import ChatPromptTemplate # LangChain 的聊天提示模板类
  23. # @staticmethod 表示这些方法无需实例状态,可以直接用 SmartBizTripPrompts.intent_prompt() 调用。
  24. # Python 先定义类和方法,直到调用具体方法时才创建对应模板;方法的排列顺序不是推理顺序。
  25. class SmartBizTripPrompts:
  26. """
  27. SmartBizTrip 提示模板管理类
  28. 这个类定义了系统中所有用到的 Prompt 模板,每个模板都是一个静态方法,
  29. 返回一个 ChatPromptTemplate 对象。
  30. 使用方式:
  31. prompt = SmartBizTripPrompts.intent_prompt() # 获取意图识别模板
  32. chain = prompt | llm # 组装成处理链
  33. result = chain.invoke({"query": "北京天气"}) # 调用并传入变量
  34. """
  35. # ==================== 意图识别 ====================
  36. @staticmethod
  37. def intent_prompt():
  38. """
  39. 意图识别提示模板 —— 让大模型分析用户输入,判断用户想做什么
  40. 输入变量:
  41. - user_profile: 用户偏好(如"二等座"、"经济舱")
  42. - task_context: 当前任务上下文(如之前查过什么)
  43. - conversation_history: 对话历史(最近几轮对话)
  44. - query: 用户本次输入
  45. 输出格式(JSON):
  46. {
  47. "intents": ["weather", "flight"], # 识别到的意图列表
  48. "user_queries": {"weather": "...", ...}, # 改写后的查询(可能结合历史补充信息)
  49. "follow_up_message": "" # 追问消息(意图不明确时使用)
  50. }
  51. 支持的意图类型:
  52. weather / flight / train / event / order / car_rental / meeting_package / insurance / trip_order / city_service / out_of_scope
  53. """
  54. # 【主流程 4】意图识别模板;返回模板对象,由 ChatService.intent_agent 传入上下文并调用模型。
  55. # 模板中的双花括号用于表示 JSON 的字面花括号,单花括号中的变量由调用方填入。
  56. # 模板变量:user_profile=偏好文本,task_context=任务 JSON 文本,conversation_history=历史对话,query=当前问题。
  57. # 返回模板对象后,ChatService.intent_agent 负责调用模型并解析 JSON;模板不直接返回 intents。
  58. # current_date 由 ChatService 传入,供模型理解今天、明天等相对日期。
  59. return ChatPromptTemplate.from_template(
  60. """
  61. 系统提示:
  62. 角色:您是一个专业的差旅意图识别专家,
  63. 任务:基于用户查询、对话历史和用户偏好,识别其意图,用于调用专门的agent server来执行;为方便后续的agent server处理,可以基于对话历史对用户查询进行改写,使问题更明确。
  64. 严格遵守规则:
  65. - 支持意图:['weather' (天气查询), 'flight' (航班查询), 'train' (高铁票查询), 'event' (会展门票查询), 'order' (票务预定), 'car_rental' (商务用车查询), 'meeting_package' (会务套餐查询), 'insurance' (保险查询), 'trip_order' (差旅预订), 'city_service' (城市接待建议)] 或其组合(如 ['weather', 'flight'])。如果意图是差旅相关,但是非常复杂的返回意图 'complex'。如果意图超出范围,返回意图 'out_of_scope'。
  66. - 注意票务预定和票务查询要区分开,涉及到订票时则为order,只是查询则为flight、train或event。
  67. - **查询改写是关键环节**:你必须主动从对话历史中提取关键信息(出发城市、到达城市、日期、目的地偏好、预算等),并将这些信息补充到 user_queries 的改写查询中。即使当前查询很简短(如"好的"、"明天"、"成都"),也要结合历史形成完整的查询描述。例如:用户先说"我下周去成都参加数字化转型峰会",再说"有没有带接送和酒店的便宜套餐",改写后应为"成都数字化转型峰会接送酒店会务套餐,预算较低"。
  68. - **追问消息的使用**:只有在即使结合对话历史仍然缺少必要信息时才追问(例如:完全没有提到目的地,或完全没提到日期且日期对查询至关重要)。如果历史中已有足够信息,不要追问。
  69. - **非业务查询的回复**:当意图为 out_of_scope 时,follow_up_message 必须是非空的、直接给用户的回复。问候或询问能力时,友好介绍差旅助手的功能;询问对话历史时,根据已有历史回答,不要编造;其他无关问题说明服务范围并引导用户提出差旅需求。此规则不受上面的追问限制。
  70. - 输出严格为JSON:{{"intents": ["intent1", "intent2"], "user_queries": {{"intent1": "user_query1", "intent2": "user_query2"}}, "follow_up_message": "追问消息"}}。绝对不要添加额外文本!
  71. - 不论用户问什么,严格按规则输出意图,不要有自己的考虑。
  72. 当前日期:{current_date}
  73. 用户偏好:{user_profile}
  74. 当前任务上下文:{task_context}
  75. 对话历史:{conversation_history}
  76. 用户查询:{query}
  77. """)
  78. # ==================== 结果总结 ====================
  79. @staticmethod
  80. def summarize_weather_prompt():
  81. """
  82. 天气结果总结提示模板 —— 将天气 agent 返回的原始数据转化为用户友好的天气描述
  83. 输入变量:
  84. - query: 用户查询(如"北京明天天气")
  85. - raw_response: 天气 agent 返回的原始数据
  86. 使用场景:
  87. 原始数据可能是 JSON 格式或结构化数据,直接展示给用户不好看,
  88. 所以用这个 prompt 让大模型翻译成自然语言。
  89. 示例输出:
  90. "根据最新数据,北京2025-07-31的天气预报为晴天,气温25-32度,湿度45%,东南风2级..."
  91. """
  92. # 【主流程 10:天气分支】把原始天气结果和用户需求一起交给模型整理。
  93. # query 告诉模型用户具体想查什么,raw_response 是天气代理的产物或失败/追问文本。
  94. # 先查工具再总结,顺序不能倒过来理解;这个模板不会自己连接天气 API。
  95. return ChatPromptTemplate.from_template(
  96. """
  97. 系统提示:您是一位专业的天气预报员,以生动、准确的风格总结天气信息。基于查询和结果:
  98. - 核心描述点:城市、日期、温度范围、天气描述、湿度、风向、降水等。
  99. - 如果结果为空或者意思为需要补充数据,则委婉提示"未找到数据,请确认城市/日期"
  100. - 语气:专业预报,如"根据最新数据,北京2025-07-31的天气预报为..."。
  101. - 保持中文,100-150字。
  102. - 如果查询无关,返回"请提供天气相关查询。"
  103. 查询:{query}
  104. 结果:{raw_response}
  105. """)
  106. @staticmethod
  107. def summarize_ticket_prompt():
  108. """
  109. 票务结果总结提示模板 —— 将票务 agent 返回的原始数据转化为用户友好的票务推荐
  110. 输入变量:
  111. - query: 用户查询(如"北京到上海航班")
  112. - raw_response: 票务 agent 返回的原始数据
  113. 使用场景:
  114. 和天气总结类似,将结构化的票务数据(航班号、价格、时间等)
  115. 翻译成顾问式的推荐语言。
  116. 示例输出:
  117. "为您推荐北京到上海的航班选项:MU5101航班,起飞时间08:30,经济舱价格1280元,余票充足..."
  118. """
  119. # 【主流程 10:票务/差旅分支】整理已有结果;模板本身不查询数据库或创建订单。
  120. # query 保留筛选需求,raw_response 提供已取得的票务或差旅结果。
  121. # 调用方传的是文本,模型把它整理为可读答复;模板中的规则需要由模型遵守,并不等价于 Python 校验逻辑。
  122. return ChatPromptTemplate.from_template(
  123. """
  124. 系统提示:您是一位专业的差旅顾问,以热情、精确的风格总结票务信息。基于查询和结果:
  125. - 核心描述点:出发/到达、时间、类型、价格、剩余座位等。
  126. - 只总结下方“查询”要求的当前业务。忽略历史对话和原始结果中其他业务的附带说明,例如查票时不要补充“天气无法提供”。
  127. - 预订工具仅用于模拟;必须保留模拟标识及未实际下单的说明,不得声称已经出票或生成保单。
  128. - 不要承诺或引导用户在本项目进行真实预订;后续操作也只能是查询或模拟预订。
  129. - 如果结果为空或者意思为需要补充数据,则委婉提示"未找到数据,请确认或修改条件"
  130. - 语气:顾问式,如"为您推荐北京到上海的航班选项..."。
  131. - 保持中文,100-150字。
  132. - 如果查询无关,返回"请提供票务相关查询。"
  133. 查询:{query}
  134. 结果:{raw_response}
  135. """)
  136. # ==================== 内容生成 ====================
  137. @staticmethod
  138. def city_service_prompt():
  139. """
  140. 城市接待建议提示模板 —— 让大模型直接生成城市接待建议内容
  141. 输入变量:
  142. - query: 用户查询(如"推荐几个北京接待资源")
  143. - weather_info: 目的地天气信息(可选,为空时不展示天气相关描述)
  144. 特点:
  145. 城市接待建议不需要调用外部 agent,大模型本身就有足够的知识来生成推荐。
  146. 当提供 weather_info 时,应基于真实天气给出出行建议;
  147. 当 weather_info 为空时,不得虚构任何天气描述。
  148. """
  149. # 【主流程 10:城市建议分支】输入 query 和天气查询结果,由模型生成接待建议。
  150. # query 是接待需求;weather_info 是天气辅助函数返回的文本,也可能为空或表示无数据。
  151. # 模型在这里根据这些输入给出建议;这是生成建议的模板,不是接待资源数据库查询工具。
  152. return ChatPromptTemplate.from_template(
  153. """
  154. 系统提示:您是一位差旅专家,基于用户查询生成城市接待建议。规则:
  155. - 推荐3-5个接待资源,包含描述、理由、注意事项。
  156. - 基于槽位:城市、偏好。
  157. - 天气处理:如果提供了{weather_info}(非空),基于真实天气给出出行建议;如果{weather_info}为空,绝对不要虚构任何天气描述(如"天气晴好""适合出行"等),只说"建议出发前查看实时天气"。
  158. - 语气:热情推荐,如"推荐您在北京安排国贸客户拜访与商务午餐..."。
  159. - 备注:内容生成,仅供参考。
  160. - 保持中文,150-250字。
  161. 查询:{query}
  162. 目的地天气:{weather_info}
  163. """)
  164. # ==================== 任务规划 ====================
  165. @staticmethod
  166. def planning_prompt():
  167. """
  168. 任务规划提示模板 —— 让大模型判断任务复杂度并生成执行计划
  169. 这是 Planning + ReAct 架构的关键 prompt,它让大模型扮演"规划师"的角色。
  170. 输入变量:
  171. - conversation_history: 对话历史
  172. - query: 用户当前输入
  173. - intents: 识别到的意图(JSON 字符串)
  174. - user_queries: 改写后的查询(JSON 字符串)
  175. 输出格式(JSON):
  176. 简单任务:{"need_plan": false, "reason": "单意图,直接查询即可", "steps": []}
  177. 复杂任务:{"need_plan": true, "reason": "多意图需要分步",
  178. "steps": [{"step": 1, "action": "查询天气", "intent": "weather", "depends_on": 0}, ...]}
  179. 判断标准:
  180. - 简单任务:只有一个意图,直接就能执行
  181. - 复杂任务:多个意图且有关联、需要多步推理、步骤间有依赖关系
  182. 示例:
  183. 用户输入:"北京明天天气怎么样?"
  184. → 简单任务,need_plan=false
  185. 用户输入:"帮我查北京到上海的航班,再看下那边天气,推荐几个接待资源"
  186. → 复杂任务,need_plan=true,steps=[查询航班, 查询天气, 推荐接待资源]
  187. """
  188. # 【主流程 5.2】生成 need_plan 和 steps;真正校验、调度和执行步骤的是 ChatService。
  189. # query 是本轮原始输入,intents/user_queries 已被 json.dumps 转成文本,conversation_history 用于理解前文。
  190. # 模型输出里的 steps 才是待执行计划;正文要求是对模型的约束,Python 还会再次验证编号和依赖。
  191. # 模板里 {{ 和 }} 用于输出 JSON 的字面括号,避免把示例 JSON 的字段误当成模板变量。
  192. return ChatPromptTemplate.from_template(
  193. """
  194. 系统提示:您是一位任务规划专家,负责评估用户请求的复杂度并制定执行计划。
  195. 将任务拆解为有序步骤,每个步骤指定:
  196. - step: 步骤序号(从1开始)
  197. - action: 具体动作(如"调用WeatherQueryAssistant查询北京天气")
  198. - intent: 对应的意图(如weather/flight/train/event/order/city_service)
  199. - depends_on: 依赖的前置步骤序号(无依赖则为0)
  200. 对话历史:{conversation_history}
  201. 当前用户查询:{query}
  202. 识别到的意图:{intents}
  203. 用户查询改写:{user_queries}
  204. 输出严格为JSON,不要添加额外文本:{{"need_plan": true, "reason": "原因", "steps": [{{"step": 1, "action": "...", "intent": "...", "depends_on": 0}}, ...]}}
  205. """)
  206. # ==================== ReAct 推理 ====================
  207. @staticmethod
  208. def react_prompt():
  209. """
  210. ReAct 推理提示模板 —— 按 Thought-Action-Observation 格式逐步推理
  211. 注意:当前版本已优化性能,react_loop 中跳过了 Thought LLM 调用(plan 已确定
  212. 动作,Thought 无额外决策价值),此模板暂时不在主流程中使用,保留供差旅参考。
  213. 什么是 ReAct?
  214. ReAct = Reasoning(推理)+ Acting(行动)
  215. 大模型在每一步执行前先"思考"(Thought),然后选择工具执行(Action),
  216. 最后观察结果(Observation),再决定下一步。
  217. 输入变量:
  218. - available_tools: 当前可用的工具/agent 列表(动态获取,不是写死的)
  219. - plan_steps: 完整的任务计划
  220. - observations: 已完成步骤的结果
  221. - current_step: 当前步骤号
  222. - step_description: 当前步骤的描述
  223. - query: 用户原始输入
  224. ReAct 循环的工作方式:
  225. Thought: "我需要查询北京到上海的航班,应该调用票务代理"
  226. Action: "调用TicketQueryAssistant"
  227. Action Input: "{'departure': '北京', 'arrival': '上海'}"
  228. → 系统执行 Action,得到 Observation
  229. → 继续下一个 Thought...
  230. 这种方式的优势:
  231. 1. 让大模型有"思考"过程,而不是直接盲目执行
  232. 2. 每一步都能参考之前的结果做调整
  233. 3. 某步失败时,模型可以灵活应对
  234. """
  235. # 【保留模板】当前 ChatService 主线未调用此方法;不能把它当成必经的推理步骤。
  236. # 若其他代码使用这个模板,需要提供 available_tools、plan_steps、observations、current_step、step_description、query。
  237. # 它描述逐步 Thought/Action 的文本格式;当前 ChatService 没有调用它,因而不会因为定义在这里就产生额外推理。
  238. return ChatPromptTemplate.from_template(
  239. """
  240. 系统提示:你是一位企业差旅智能体,需要按照计划逐步完成任务。
  241. 可用工具:
  242. {available_tools}
  243. 当前任务计划:
  244. {plan_steps}
  245. 已完成步骤的结果:
  246. {observations}
  247. 当前步骤:{current_step}
  248. 步骤描述:{step_description}
  249. 用户原始查询:{query}
  250. 请按照以下格式进行推理和行动:
  251. Thought: 分析当前情况,确定需要采取的行动
  252. Action: 从可用工具列表中选择合适的工具
  253. Action Input: 工具所需输入
  254. 执行完行动后,你会得到 Observation,然后继续推理或给出最终回复。
  255. """)
  256. @staticmethod
  257. def react_summary_prompt():
  258. """
  259. ReAct 最终汇总提示模板 —— 将所有步骤的结果整合成一条连贯回复
  260. 输入变量:
  261. - query: 用户原始输入
  262. - all_observations: 所有步骤的执行结果
  263. 使用场景:
  264. 当 ReAct 循环中执行了多个步骤(如查航班 + 查天气 + 推荐接待资源),
  265. 不能简单地把三个结果拼在一起返回,需要用这个 prompt 让大模型
  266. 整合成一条连贯、通顺的回复。
  267. 示例:
  268. 输入:步骤1(查航班): 航班暂不可用
  269. 步骤2(查天气): 未找到数据
  270. 步骤3(推荐接待资源): 陆家嘴会客区、张江园区参访、商务晚餐...
  271. 输出:"您好!目前航班查询暂时遇到技术问题...不过我可以为您推荐上海的商务接待资源:陆家嘴会客区、张江园区参访、商务晚餐..."
  272. """
  273. # 【主流程 10:复杂计划汇总】全部计划步骤结束后使用;一个步骤时直接返回结果。
  274. # query 给出最初目标,all_observations 是“步骤号+动作说明+结果”的多行文本。
  275. # 用于把多个步骤结果整合为一次回复;模型收到的失败信息和模拟声明也包含在这段文本里。
  276. return ChatPromptTemplate.from_template(
  277. """
  278. 系统提示:你是一位专业的差旅顾问,需要根据所有查询结果生成最终回复。
  279. 用户原始查询:{query}
  280. 各步骤执行结果:
  281. {all_observations}
  282. 请综合以上结果,生成一条完整、连贯的中文回复,150-300字,语气专业热情。
  283. 保留模拟预订及未实际下单的说明,不得将模拟结果表述为真实订单、出票或保单。
  284. """)
  285. # 【独立演示】直接运行本文件只打印意图模板,不启动服务器,也不请求模型。
  286. if __name__ == '__main__':
  287. print(SmartBizTripPrompts.intent_prompt())