resource_agent.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. from __future__ import annotations
  2. """资源查询Agent:调用航班/酒店MCP工具搜索外部资源,指导工具调用顺序并汇总结果为结构化数据。"""
  3. from typing import Any
  4. from langchain.agents import create_agent
  5. from langchain_core.language_models.chat_models import (
  6. BaseChatModel,
  7. )
  8. from langchain_core.tools import BaseTool
  9. from app.llm import get_chat_model
  10. from app.schemas.travel_request import TravelRequest
  11. # RESOURCE_AGENT_PROMPT:资源查询的系统提示词,定义航班与酒店搜索的工具调用策略。
  12. RESOURCE_AGENT_PROMPT = """
  13. 你是旅行系统中的资源查询Agent。
  14. 你只负责查询机场、航班和酒店,不负责生成最终行程。
  15. 必须按照以下顺序执行:
  16. 1. 使用search_airports查询出发城市的机场。
  17. 2. 使用search_airports查询目的城市的机场。
  18. 3. 优先选择type为city且城市名称最匹配的结果。
  19. 4. 提取该城市包含的全部机场代码。
  20. 5. 使用search_flights查询真实航班。
  21. 6. 使用search_hotels查询目的城市的真实酒店。
  22. 7. 当前阶段不要调用search_return_flights。
  23. 8. 用户没有提出限制时,不得自行限制:
  24. - 是否直飞;
  25. - 起飞或落地时间;
  26. - 机场;
  27. - 机票最高价格;
  28. - 酒店星级和评分。
  29. 9. 不得编造机场代码、价格、航班或酒店。
  30. 10. 工具查询结束后,只输出简短的查询完成摘要。
  31. 航班查询规则:
  32. - 用户有返程日期时,flight_type使用round_trip。
  33. - departure_airports使用出发城市全部有效机场代码。
  34. - arrival_airports使用目的城市全部有效机场代码。
  35. - 日期必须使用YYYY-MM-DD。
  36. - 成人和儿童数量严格使用用户需求。
  37. - 默认币种使用CNY。
  38. 酒店查询规则:
  39. - query使用“目的城市 + 酒店”。
  40. - 入住日期使用出发日期。
  41. - 退房日期使用返程日期。
  42. - 不要自行增加价格、评分或星级限制。
  43. """
  44. class ResourceSearchAgent:
  45. """查询机场、航班和酒店的专业Agent。"""
  46. def __init__(
  47. self,
  48. tools: list[BaseTool],
  49. model: BaseChatModel | None = None,
  50. ) -> None:
  51. """初始化资源查询Agent,支持注入自定义模型和MCP工具集。"""
  52. allowed_names = {
  53. "search_airports",
  54. "search_flights",
  55. "search_hotels",
  56. }
  57. selected_tools = [
  58. tool
  59. for tool in tools
  60. if tool.name in allowed_names
  61. ]
  62. loaded_names = {
  63. tool.name
  64. for tool in selected_tools
  65. }
  66. missing_names = (
  67. allowed_names
  68. - loaded_names
  69. )
  70. if missing_names:
  71. raise RuntimeError(
  72. "资源查询Agent缺少工具:"
  73. + "、".join(
  74. sorted(missing_names)
  75. )
  76. )
  77. self._agent = create_agent(
  78. model=model or get_chat_model(),
  79. tools=selected_tools,
  80. system_prompt=RESOURCE_AGENT_PROMPT,
  81. )
  82. async def search(
  83. self,
  84. request: TravelRequest,
  85. ) -> dict[str, Any]:
  86. """根据结构化旅行需求调用真实工具。"""
  87. request_json = request.model_dump_json(
  88. indent=2,
  89. )
  90. user_message = f"""
  91. 请根据下面的结构化旅行需求执行真实资源查询:
  92. {request_json}
  93. 必须实际调用机场、航班和酒店工具。
  94. 不要只根据已有知识直接回答。
  95. """
  96. return await self._agent.ainvoke(
  97. {
  98. "messages": [
  99. {
  100. "role": "user",
  101. "content": user_message,
  102. }
  103. ]
  104. }
  105. )