test_selection_graph.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. from __future__ import annotations
  2. """测试候选筛选阶段子图:验证航班筛选、酒店过滤与往返组合排序。"""
  3. import asyncio
  4. from rich.console import Console
  5. from rich.table import Table
  6. from app.graph.selection_builder import (
  7. build_selection_graph,
  8. )
  9. console = Console()
  10. async def main() -> None:
  11. graph = await build_selection_graph()
  12. result = await graph.ainvoke(
  13. {
  14. "user_query": (
  15. "我和妻子计划2026年8月10日"
  16. "从上海去成都,8月13日返回,"
  17. "总预算8000元。"
  18. "酒店每晚不超过600元,"
  19. "评分希望不低于4.5分。"
  20. "机票价格和便利性综合考虑,"
  21. "不限制是否中转。"
  22. ),
  23. "errors": [],
  24. "missing_fields": [],
  25. }
  26. )
  27. console.rule("[bold]候选筛选结果")
  28. console.print(result["final_answer"])
  29. selection = result.get(
  30. "candidate_selection_result"
  31. )
  32. if selection is None:
  33. console.print(
  34. {
  35. "错误": result.get("errors"),
  36. "警告": result.get(
  37. "selection_warnings"
  38. ),
  39. }
  40. )
  41. return
  42. console.print(
  43. {
  44. "旅行类型": selection.flight_type,
  45. "去程查询候选数": len(
  46. selection.outbound_candidates
  47. ),
  48. "往返组合数": len(
  49. selection.round_trip_options
  50. ),
  51. "酒店候选数": len(
  52. selection.hotels
  53. ),
  54. "警告": selection.warnings,
  55. }
  56. )
  57. flight_table = Table(
  58. title="排名靠前的往返组合"
  59. )
  60. flight_table.add_column("排名")
  61. flight_table.add_column("去程航班")
  62. flight_table.add_column("返程航班")
  63. flight_table.add_column("接口报价")
  64. flight_table.add_column("分数")
  65. flight_table.add_column("排序原因")
  66. for index, option in enumerate(
  67. selection.round_trip_options[:5],
  68. start=1,
  69. ):
  70. outbound_numbers = "、".join(
  71. option.outbound.get(
  72. "flight_numbers",
  73. [],
  74. )
  75. )
  76. return_numbers = "、".join(
  77. option.return_flight.get(
  78. "flight_numbers",
  79. [],
  80. )
  81. )
  82. flight_table.add_row(
  83. str(index),
  84. outbound_numbers or "未知",
  85. return_numbers or "未知",
  86. (
  87. f"{option.quoted_price:.0f}"
  88. f" {option.currency}"
  89. if option.quoted_price is not None
  90. else "未知"
  91. ),
  92. f"{option.score:.2f}",
  93. ";".join(option.reasons),
  94. )
  95. console.print(flight_table)
  96. hotel_table = Table(
  97. title="排名靠前的酒店"
  98. )
  99. hotel_table.add_column("排名")
  100. hotel_table.add_column("酒店")
  101. hotel_table.add_column("每晚价格")
  102. hotel_table.add_column("评分")
  103. hotel_table.add_column("分数")
  104. hotel_table.add_column("排序原因")
  105. for index, candidate in enumerate(
  106. selection.hotels[:5],
  107. start=1,
  108. ):
  109. hotel = candidate.hotel
  110. price = hotel.get("price_per_night")
  111. hotel_table.add_row(
  112. str(index),
  113. str(hotel.get("name", "未知")),
  114. (
  115. f"{price} {hotel.get('currency', '')}"
  116. if price is not None
  117. else "未知"
  118. ),
  119. str(
  120. hotel.get(
  121. "overall_rating",
  122. "未知",
  123. )
  124. ),
  125. f"{candidate.score:.2f}",
  126. ";".join(candidate.reasons),
  127. )
  128. console.print(hotel_table)
  129. if __name__ == "__main__":
  130. asyncio.run(main())