route_builder.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. from __future__ import annotations
  2. """路线评估阶段子图:组合候选航班与酒店,调用高德距离工具评估酒店位置与景点路线。"""
  3. from langchain_core.tools import BaseTool
  4. from langgraph.graph import (
  5. END,
  6. START,
  7. StateGraph,
  8. )
  9. from app.agents.map_agent import (
  10. MapResearchAgent,
  11. )
  12. from app.agents.requirement_agent import (
  13. RequirementAgent,
  14. )
  15. from app.agents.resource_agent import (
  16. ResourceSearchAgent,
  17. )
  18. from app.graph.map_nodes import (
  19. make_map_research_node,
  20. )
  21. from app.graph.nodes import (
  22. clarification_node,
  23. error_node,
  24. make_parse_request_node,
  25. route_after_parse,
  26. )
  27. from app.graph.resource_nodes import (
  28. make_resource_search_node,
  29. )
  30. from app.graph.route_nodes import (
  31. make_route_evaluation_node,
  32. route_after_map_research,
  33. route_after_selection,
  34. )
  35. from app.graph.selection_nodes import (
  36. make_candidate_selection_node,
  37. route_after_resources_to_selection,
  38. )
  39. from app.graph.state import TravelState
  40. from app.mcp_client import load_mcp_tools
  41. def find_tool(
  42. tools: list[BaseTool],
  43. *names: str,
  44. ) -> BaseTool | None:
  45. """按名称在MCP工具列表中查找目标工具,未找到返回None。"""
  46. for name in names:
  47. for tool in tools:
  48. if tool.name == name:
  49. return tool
  50. return None
  51. async def build_route_graph():
  52. """构建从需求解析到酒店路线评估的完整流程。"""
  53. tool_bundle = await load_mcp_tools()
  54. return_flight_tool = find_tool(
  55. tool_bundle.travel_tools,
  56. "search_return_flights",
  57. )
  58. distance_tool = find_tool(
  59. tool_bundle.amap_tools,
  60. "maps_distance",
  61. )
  62. around_search_tool = find_tool(
  63. tool_bundle.amap_tools,
  64. "maps_around_search",
  65. "maps_search_around",
  66. )
  67. if return_flight_tool is None:
  68. raise RuntimeError(
  69. "没有加载search_return_flights工具。"
  70. )
  71. if distance_tool is None:
  72. raise RuntimeError(
  73. "没有加载maps_distance工具。"
  74. )
  75. requirement_agent = RequirementAgent()
  76. resource_agent = ResourceSearchAgent(
  77. tools=tool_bundle.travel_tools,
  78. )
  79. map_agent = MapResearchAgent(
  80. tools=tool_bundle.amap_tools,
  81. )
  82. builder = StateGraph(TravelState)
  83. builder.add_node(
  84. "parse_request",
  85. make_parse_request_node(
  86. requirement_agent
  87. ),
  88. )
  89. builder.add_node(
  90. "clarify",
  91. clarification_node,
  92. )
  93. builder.add_node(
  94. "error",
  95. error_node,
  96. )
  97. builder.add_node(
  98. "search_resources",
  99. make_resource_search_node(
  100. resource_agent
  101. ),
  102. )
  103. builder.add_node(
  104. "select_candidates",
  105. make_candidate_selection_node(
  106. return_flight_tool,
  107. max_outbound_queries=2,
  108. max_returns_per_outbound=5,
  109. ),
  110. )
  111. builder.add_node(
  112. "research_destination",
  113. make_map_research_node(
  114. map_agent
  115. ),
  116. )
  117. builder.add_node(
  118. "evaluate_routes",
  119. make_route_evaluation_node(
  120. distance_tool=distance_tool,
  121. around_search_tool=(
  122. around_search_tool
  123. ),
  124. max_hotels=3,
  125. max_attractions=4,
  126. ),
  127. )
  128. builder.add_edge(
  129. START,
  130. "parse_request",
  131. )
  132. builder.add_conditional_edges(
  133. "parse_request",
  134. route_after_parse,
  135. {
  136. "clarify": "clarify",
  137. "error": "error",
  138. "ready": "search_resources",
  139. },
  140. )
  141. builder.add_edge("clarify", END)
  142. builder.add_edge("error", END)
  143. builder.add_conditional_edges(
  144. "search_resources",
  145. route_after_resources_to_selection,
  146. {
  147. "select_candidates": (
  148. "select_candidates"
  149. ),
  150. "end": END,
  151. },
  152. )
  153. builder.add_conditional_edges(
  154. "select_candidates",
  155. route_after_selection,
  156. {
  157. "research_destination": (
  158. "research_destination"
  159. ),
  160. "end": END,
  161. },
  162. )
  163. builder.add_conditional_edges(
  164. "research_destination",
  165. route_after_map_research,
  166. {
  167. "evaluate_routes": (
  168. "evaluate_routes"
  169. ),
  170. "end": END,
  171. },
  172. )
  173. builder.add_edge(
  174. "evaluate_routes",
  175. END,
  176. )
  177. return builder.compile()